opencv4nodejs icon indicating copy to clipboard operation
opencv4nodejs copied to clipboard

Library throws strings instead of error objects

Open SimonSchick opened this issue 2 years ago • 3 comments

This is pretty bad because it makes it impossible to trace errors.

I think it would be a relatively easy fix here https://github.com/UrielCh/opencv4nodejs/blob/master/cc/native-node-utils/TryCatch.h#L32 unfortunately my CPP game is terrible and I've never worked on any CPP node modules.

image

SimonSchick avatar Aug 08 '23 00:08 SimonSchick

Repro: new (require('@u4/opencv4nodejs').Mat)().getDataAsArray()

SimonSchick avatar Aug 08 '23 00:08 SimonSchick

Thank you for pointing out the file that needs correction. I'm currently facing homelessness, so I'll address that once I've resolved this pressing issue.

UrielCh avatar Oct 12 '23 07:10 UrielCh

Hey, I hope you're doing better, I'm interested in contributing, would you be able to provide some pointers on how to resolve this issue? Right now our workaround looks like this:

// Manually patch all opencv.* opencv.Mat/Contour.prototype.* methods to not throw strings

import * as cv from 'opencv4nodejs';

export class OpenCVWrappedError extends Error {
  public override name = 'OpenCVWrappedError';
  public constructor(
    message: string,
    public readonly functionName: string
  ) {
    super(`${functionName}: ${message}`);
  }
}

const patchNoThrowString = (obj: Record<string, unknown>) => {
  for (const [key, value] of Object.entries(obj)) {
    // ignore what is likely classes
    if (
      typeof value !== 'function' ||
      key[0]!.toUpperCase() === key[0] ||
      !!Object.keys(value.prototype as object).length
    ) {
      continue;
    }

    // eslint-disable-next-line unicorn/prefer-ternary
    if (key.endsWith('Async')) {
      // we specifically use a different name, `jest` in it's infinite wisdom resets js modules but NOT native modules
      // which leads to opencv loading multiple times but not it's bindings
      // further it relies on `.prototype.constructor.name` being present thus this function cannot be async...
      // eslint-disable-next-line @typescript-eslint/promise-function-async
      obj[key] = function asyncProxy(...args: unknown[]) {
        // eslint-disable-next-line @typescript-eslint/return-await
        return (value as (...args: unknown[]) => Promise<unknown>)
          .call(this, ...args)
          .catch((error: unknown) => {
            if (typeof error === 'string') {
              throw new OpenCVWrappedError(error, key);
            }
            throw error;
          });
      };
    } else {
      obj[key] = function proxy(...args: unknown[]): unknown {
        try {
          return value.call(this, ...args) as unknown;
        } catch (error) {
          if (typeof error === 'string') {
            throw new OpenCVWrappedError(error, key);
          }
          throw error;
        }
      };
    }
  }
};

// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
patchNoThrowString(require('opencv4nodejs') as Record<string, unknown>);
patchNoThrowString(cv.Mat.prototype as unknown as Record<string, unknown>);
patchNoThrowString(cv.Contour.prototype as unknown as Record<string, unknown>);

SimonSchick avatar Sep 27 '24 23:09 SimonSchick