callbacks-registry.ts 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. const v8Util = process.electronBinding('v8_util');
  2. export class CallbacksRegistry {
  3. private nextId: number = 0
  4. private callbacks: Record<number, Function> = {}
  5. add (callback: Function) {
  6. // The callback is already added.
  7. let id = v8Util.getHiddenValue<number>(callback, 'callbackId');
  8. if (id != null) return id;
  9. id = this.nextId += 1;
  10. // Capture the location of the function and put it in the ID string,
  11. // so that release errors can be tracked down easily.
  12. const regexp = /at (.*)/gi;
  13. const stackString = (new Error()).stack;
  14. if (!stackString) return;
  15. let filenameAndLine;
  16. let match;
  17. while ((match = regexp.exec(stackString)) !== null) {
  18. const location = match[1];
  19. if (location.includes('(native)')) continue;
  20. if (location.includes('(<anonymous>)')) continue;
  21. if (location.includes('electron/js2c')) continue;
  22. const ref = /([^/^)]*)\)?$/gi.exec(location);
  23. if (ref) filenameAndLine = ref![1];
  24. break;
  25. }
  26. this.callbacks[id] = callback;
  27. v8Util.setHiddenValue(callback, 'callbackId', id);
  28. v8Util.setHiddenValue(callback, 'location', filenameAndLine);
  29. return id;
  30. }
  31. get (id: number) {
  32. return this.callbacks[id] || function () {};
  33. }
  34. apply (id: number, ...args: any[]) {
  35. return this.get(id).apply(global, ...args);
  36. }
  37. remove (id: number) {
  38. const callback = this.callbacks[id];
  39. if (callback) {
  40. v8Util.deleteHiddenValue(callback, 'callbackId');
  41. delete this.callbacks[id];
  42. }
  43. }
  44. }