dcp-client 4.2.6 → 4.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"browser":["deny-node","kvin/kvin.js","script-load-wrapper","wrap-event-listeners","event-loop-virtualization","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"node":["kvin/kvin.js","sa-ww-simulation","script-load-wrapper","wrap-event-listeners","event-loop-virtualization","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"native":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","event-loop-virtualization","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"webGpuNative":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","event-loop-virtualization","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","webgpu-worker-environment.js","bootstrap"],"nodeTesting":["kvin/kvin.js","sa-ww-simulation","script-load-wrapper","wrap-event-listeners","event-loop-virtualization","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap","testing.js"],"testing":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","event-loop-virtualization","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap","testing.js"]}
1
+ {"browser":["deny-node","kvin/kvin.js","script-load-wrapper","wrap-event-listeners","event-loop-virtualization","gpu-timers","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"node":["kvin/kvin.js","sa-ww-simulation","script-load-wrapper","wrap-event-listeners","event-loop-virtualization","gpu-timers","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"native":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","event-loop-virtualization","webgpu-worker-environment","gpu-timers","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap"],"webGpuNative":["deny-node","kvin/kvin.js","bootstrap"],"nodeTesting":["kvin/kvin.js","sa-ww-simulation","script-load-wrapper","wrap-event-listeners","event-loop-virtualization","gpu-timers","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap","testing.js"],"testing":["deny-node","kvin/kvin.js","sa-ww-simulation","script-load-wrapper","native-event-loop","wrap-event-listeners","event-loop-virtualization","webgpu-worker-environment","gpu-timers","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","bootstrap","testing.js"]}
package/index.js CHANGED
@@ -32,10 +32,11 @@ const process = require('process');
32
32
  const kvin = require('kvin');
33
33
  const moduleSystem = require('module');
34
34
  const { spawnSync } = require('child_process');
35
- const { createContext, runInContext } = require('vm');
35
+ const vm = require('vm');
36
36
 
37
37
  exports.debug = false;
38
38
  let initInvoked = false; /* flag to help us detect use of Compute API before init */
39
+ let hadOriginalDcpConfig = globalThis.hasOwnProperty('dcpConfig'); /* flag to determine if the user set their own dcpConfig global variable before init */
39
40
 
40
41
  function debugging(what = 'dcp-client') {
41
42
  const debugSyms = []
@@ -107,6 +108,30 @@ const bundleSandbox = {
107
108
  },
108
109
  };
109
110
 
111
+ function runSandboxedCode(sandbox, code, options)
112
+ {
113
+ if (process.env.DCP_CLIENT_LEGACY_CONTEXT_SANDBOXING)
114
+ {
115
+ const script = new vm.Script(code, options);
116
+ return script.runInNewContext(vm.createContext(sandbox), options);
117
+ }
118
+
119
+ if (typeof runSandboxedCode.extraGlobalProps === 'undefined')
120
+ runSandboxedCode.extraGlobalProps = {};
121
+
122
+ for (let prop in sandbox)
123
+ {
124
+ if (!globalThis.hasOwnProperty(prop) || runSandboxedCode.extraGlobalProps.hasOwnProperty(prop))
125
+ {
126
+ globalThis[prop] = sandbox[prop];
127
+ runSandboxedCode.extraGlobalProps[prop] = true; /* Memoize global prop list mutation to allow re-mutation */
128
+ }
129
+ }
130
+
131
+ const script = new vm.Script(code, options);
132
+ return script.runInThisContext(options);
133
+ }
134
+
110
135
  /** Evaluate a file in a sandbox without polluting the global object.
111
136
  * @param filename {string} The name of the file to evaluate, relative to
112
137
  * @param sandbox {object} A sandbox object, used for injecting 'global' symbols as needed
@@ -114,7 +139,6 @@ const bundleSandbox = {
114
139
  */
115
140
  function evalScriptInSandbox(filename, sandbox, olFlag) {
116
141
  var code
117
- var context = createContext(sandbox)
118
142
  try {
119
143
  code = fs.readFileSync(path.resolve(distDir, filename), 'utf-8')
120
144
  if (olFlag)
@@ -126,7 +150,8 @@ function evalScriptInSandbox(filename, sandbox, olFlag) {
126
150
  throw e
127
151
  }
128
152
 
129
- return runInContext(code, context, filename, 0);
153
+
154
+ return runSandboxedCode(sandbox, code, { filename, lineNumber: 0 });
130
155
  }
131
156
 
132
157
  /** Evaluate code in a secure sandbox; in this case, the code is the configuration
@@ -134,18 +159,12 @@ function evalScriptInSandbox(filename, sandbox, olFlag) {
134
159
  * during config file processing.
135
160
  *
136
161
  * @param code {string} The code to eval
137
- * @param context {object} An object that has been initialized as a context
138
- * that will act like the context's global object
162
+ * @param sandbox {object} A sandbox object, used for injecting 'global' symbols as needed
139
163
  * @param filename {string} The name of the file we're evaluating for stack-
140
164
  * trace purposes.
141
165
  */
142
- function evalStringInSandbox(
143
- code,
144
- sandbox,
145
- filename = '(dcp-client$$evalStringInSandbox)',
146
- ) {
147
- const context = createContext(sandbox);
148
-
166
+ function evalStringInSandbox(code, sandbox, filename = '(dcp-client$$evalStringInSandbox)')
167
+ {
149
168
  /**
150
169
  * Remove comments and then decide if this config file contains an IIFE. If
151
170
  * not, we need to wrap it as an IIFE to avoid "SyntaxError: Illegal return
@@ -167,7 +186,7 @@ function evalStringInSandbox(
167
186
  * printed.
168
187
  */
169
188
  try {
170
- result = runInContext(code, context, {
189
+ result = runSandboxedCode(sandbox, code, {
171
190
  filename,
172
191
  lineOffset,
173
192
  /**
@@ -301,7 +320,7 @@ function addConfig (existing, neo) {
301
320
  existing[prop] = new (existing[prop].constructor)(neo[prop]);
302
321
  continue;
303
322
  }
304
- if (typeof neo[prop] === 'object' && !Array.isArray(neo[prop]) && ['Function','Object'].includes(neo[prop].constructor.name)) {
323
+ if (typeof neo[prop] === 'object' && neo[prop] !== null && !Array.isArray(neo[prop]) && ['Function','Object'].includes(neo[prop].constructor.name)) {
305
324
  if (typeof existing[prop] === 'undefined') {
306
325
  existing[prop] = {}
307
326
  }
@@ -575,7 +594,7 @@ function initTail(aggrConfig, finalBundleCode, finalBundleURL) {
575
594
  require('dcp/dcp-url').patchup(aggrConfig);
576
595
 
577
596
  /* 3 */
578
- if (global.dcpConfig) {
597
+ if (hadOriginalDcpConfig) {
579
598
  /* dcpConfig was defined before dcp-client was initialized: assume dev knows what he/she is doing */
580
599
  debugging() && console.debug('Dropping bundle dcp-config in favour of global dcpConfig')
581
600
  Object.assign(require('dcp/dcp-config'), global.dcpConfig);
@@ -66,7 +66,7 @@ exports.Evaluator = function Evaluator(inputStream, outputStream, files) {
66
66
  /* Add properties to sandbox global scope */
67
67
  Object.assign(this.sandboxGlobal, {
68
68
  self: this.sandboxGlobal,
69
- die: (exitCode) => { this.destroy(); process.exit(exitCode); },
69
+ die: () => { this.destroy(); },
70
70
  writeln: (string) => { this.writeln(string) },
71
71
  onreadln: (handler) => { this.onreadlnHandler = handler },
72
72
  setTimeout,
@@ -23,6 +23,8 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
23
23
  'AsyncFunction',
24
24
  'Atomics',
25
25
  'BigInt',
26
+ 'BigInt64Array',
27
+ 'BigUint64Array',
26
28
  'Boolean',
27
29
  'Blob',
28
30
  'bravojs',
@@ -45,7 +47,6 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
45
47
  'Float32Array',
46
48
  'Float64Array',
47
49
  'Function',
48
- 'getWebGLTimer',
49
50
  'Headers',
50
51
  'Infinity',
51
52
  'Int16Array',
@@ -108,7 +109,6 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
108
109
  'WeakSet',
109
110
  'WebAssembly',
110
111
  'WebGL2RenderingContext',
111
- 'webGLOffset',
112
112
  'WebGLTexture',
113
113
  'WorkerGlobalScope',
114
114
  // Our own Webgpu symbols
@@ -123,18 +123,6 @@ self.wrapScriptLoading({ scriptName: 'access-lists', ringTransition: true }, fun
123
123
  'flushLastLog'
124
124
  ]);
125
125
 
126
- // webGL timer getter to be overwritten in calculate-capabilities
127
- self.getWebGLTimer = (function monkeypatchWebGL() {
128
- let timer = 0;
129
- function getTimer() {
130
- return timer;
131
- }
132
- return getTimer;
133
- })();
134
-
135
- // Offset time for webGL when sandbox is re-used
136
- self.webGLOffset = 0;
137
-
138
126
  // Origin time for performance polyfill
139
127
  const pt0 = new Date().getTime();
140
128
 
@@ -131,15 +131,16 @@ self.wrapScriptLoading({ scriptName: 'bootstrap', finalScript: true }, function
131
131
  return true;
132
132
  }
133
133
 
134
- function workerBootstrap$work$emit(eventName, value) {
135
- if (typeof eventName !== 'string') {
136
- throw new Error(`Event name passed to work.emit must be a string, not ${eventName}.`);
134
+ function workerBootstrap$work$emit(customEvent, value) {
135
+ if (typeof customEvent !== 'string') {
136
+ throw new Error(`Event name passed to work.emit must be a string, not ${customEvent}.`);
137
137
  }
138
138
 
139
139
  postMessage({
140
140
  request: 'emitEvent',
141
141
  payload: {
142
- eventName,
142
+ eventName: 'custom',
143
+ customEvent,
143
144
  data: value,
144
145
  },
145
146
  });
@@ -184,12 +184,9 @@ self.wrapScriptLoading({ scriptName: 'bravojs-env', ringTransition: true }, func
184
184
  function reportRejectedGPUandTotal (t0) {
185
185
  try
186
186
  {
187
- const webGLTimer = getWebGLTimer;
188
- const offset = webGLOffset;
189
187
  const total = performance.now() - t0;
190
-
191
- let webGL = webGLTimer() - offset;
192
- self.webGLOffset = offset + webGL;
188
+ const webGL = protectedStorage.getAndResetWebGLTimer();
189
+ protectedStorage.subtractWebGLTimeFromCPUTime(webGL);
193
190
  ring3PostMessage({ request: 'measurement', total, webGL });
194
191
  }
195
192
  catch (error)
@@ -232,12 +229,9 @@ self.wrapScriptLoading({ scriptName: 'bravojs-env', ringTransition: true }, func
232
229
  {
233
230
  try
234
231
  {
235
- const webGLTimer = getWebGLTimer;
236
- const offset = webGLOffset;
237
- const total = performance.now() - t0;
238
-
239
- let webGL = webGLTimer() - offset;
240
- self.webGLOffset = offset + webGL;
232
+ const total = performance.now() - t0 + 1; /* +1 to ensure we never have "0 second slices" */
233
+ const webGL = protectedStorage.getAndResetWebGLTimer();
234
+ protectedStorage.subtractWebGLTimeFromCPUTime(webGL); /* Because webGL is sync but doesn't use CPU */
241
235
  ring3PostMessage({ request: 'measurement', total, webGL });
242
236
  ring3PostMessage({ request: 'complete', result });
243
237
  }
@@ -277,6 +271,7 @@ self.wrapScriptLoading({ scriptName: 'bravojs-env', ringTransition: true }, func
277
271
  */
278
272
  try { await tryFlushMicroTaskQueue(); } catch(e) {};
279
273
  try { flushLastLog(); } catch(e) {};
274
+ try { protectedStorage.markCPUTimeAsDone(); } catch(e) {};
280
275
 
281
276
  if (rejection)
282
277
  errorCallback(rejection);
@@ -301,6 +296,6 @@ self.wrapScriptLoading({ scriptName: 'bravojs-env', ringTransition: true }, func
301
296
  * 1. shorten stack
302
297
  * 2. initialize the event loop measurement code
303
298
  */
304
- setTimeout(() => runWorkFunction_inner(datum, (result) => reportResult(t0, result), (rejection) => reportError(t0, rejection)));
299
+ protectedStorage.setTimeout(() => runWorkFunction_inner(datum, (result) => reportResult(t0, result), (rejection) => reportError(t0, rejection)));
305
300
  }
306
301
  }); /* end of fn */
@@ -91,47 +91,12 @@ self.wrapScriptLoading({ scriptName: 'calculate-capabilities' }, function calcul
91
91
 
92
92
  // Destroy the WebGL context, from https://www.khronos.org/registry/webgl/extensions/WEBGL_lose_context/
93
93
  // "This is the recommended mechanism for applications to programmatically halt their use of the WebGL API."
94
- gl.getExtension('WEBGL_lose_context').loseContext();
94
+ // gl.getExtension('WEBGL_lose_context').loseContext();
95
95
 
96
96
  bigTexture4096 = textureSize >= 4096;
97
97
  bigTexture8192 = textureSize >= 8192;
98
98
  bigTexture16384 = textureSize >= 16384;
99
99
  bigTexture32768 = textureSize >= 32768;
100
-
101
- // Monkeypatch webGL *after* capability check to verify getContext exists AND not charge every job for GPU checking
102
- getWebGLTimer = (function monkeypatchWebGL() {
103
- let timer = 0;
104
- function getTimer() {
105
- return timer;
106
- }
107
- let oldGetContext = OffscreenCanvas.prototype.getContext;
108
- OffscreenCanvas.prototype.getContext = function(type, options){
109
- let context = oldGetContext(type, options);
110
- for (let key of Object.getOwnPropertyNames(context.__proto__)){
111
- if (typeof context[key] === 'function'){
112
- let func = context[key].bind(context);
113
- context[key] = (...args) => {
114
- let startTimer = performance.now();
115
- // Try using .then (assuming async) and falling back to synchronous
116
- try{
117
- ret = func(...args).then((ret)=>{
118
- let timeRes = performance.now() - startTimer;
119
- timer += timeRes;
120
- return ret;
121
- });
122
- }catch(err){
123
- ret = func(...args);
124
- let timeRes = performance.now() - startTimer;
125
- timer += timeRes;
126
- }
127
- return ret;
128
- };
129
- }
130
- };
131
- return context;
132
- };
133
- return getTimer;
134
- })();
135
100
  }catch(err){
136
101
  //it is possible on some machines, that offscreenCanvas is available but canvas.getContext('webgl' or 'webgl2') results in null
137
102
  //Any error in this using an extensions should likely result in specifications for that capability being set to false.
@@ -32,6 +32,7 @@ self.wrapScriptLoading({ scriptName: 'event-loop-virtualization' }, function eve
32
32
  serviceEvents.timeout = null;
33
33
  serviceEvents.nextTimeout = null;
34
34
  serviceEvents.servicing = true;
35
+ serviceEvents.sliceIsFinished = false;
35
36
 
36
37
  const startTime = performance.now();
37
38
  let now = Date.now();
@@ -58,15 +59,30 @@ self.wrapScriptLoading({ scriptName: 'event-loop-virtualization' }, function eve
58
59
  function endOfRealEventCycle()
59
60
  {
60
61
  serviceEvents.servicing = false;
62
+ if (!serviceEvents.sliceIsFinished)
63
+ {
64
+ const endTime = performance.now();
65
+ totalCPUTime += endTime - startTime;
66
+
67
+ // Set timeout to rerun this function if there are events remaining that just can't be used yet
68
+ if (events.length > 0)
69
+ {
70
+ serviceEvents.nextTimeout = events[0].when
71
+ serviceEvents.timeout = realSetTimeout(serviceEvents, events[0].when - Date.now());
72
+ }
73
+ }
74
+ }
75
+
76
+ protectedStorage.markCPUTimeAsDone = function markCPUTimeAsDone()
77
+ {
61
78
  const endTime = performance.now();
62
79
  totalCPUTime += endTime - startTime;
80
+ serviceEvents.sliceIsFinished = true;
81
+ }
63
82
 
64
- // Set timeout to rerun this function if there are events remaining that just can't be used yet
65
- if (events.length > 0)
66
- {
67
- serviceEvents.nextTimeout = events[0].when
68
- serviceEvents.timeout = realSetTimeout(serviceEvents, events[0].when - Date.now());
69
- }
83
+ protectedStorage.subtractWebGLTimeFromCPUTime = function subtractCPUTime(time)
84
+ {
85
+ totalCPUTime -= time;
70
86
  }
71
87
  }
72
88
 
@@ -124,6 +140,9 @@ self.wrapScriptLoading({ scriptName: 'event-loop-virtualization' }, function eve
124
140
  return timer;
125
141
  }
126
142
 
143
+ /** Ensure our trampoline setTimeout in bravojs-env will have the proper setTimeout, don't allow clients to see or overwrite to prevent measuring time */
144
+ protectedStorage.setTimeout = setTimeout;
145
+
127
146
  /** Remove a timeout from the list of pending timeouts, regardless of its current
128
147
  * status.
129
148
  *
@@ -215,6 +234,7 @@ self.wrapScriptLoading({ scriptName: 'event-loop-virtualization' }, function eve
215
234
  serviceEvents.timeout = null;
216
235
  serviceEvents.nextTimeout = null;
217
236
  serviceEvents.servicing = false;
237
+ serviceEvents.sliceIsFinished = false;
218
238
  }
219
239
 
220
240
  addEventListener('message', async (event) => {
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @file gpu-timers.js
3
+ * Copyright (c) 2018, Kings Distributed Systems, Ltd. All Rights Reserved. @todo: is this correct lol?
4
+ *
5
+ * This file adds wrappers for webGL and webGPU functions so we can measure their GPU time
6
+ *
7
+ * @author Ryan Saweczko, ryansaweczko@kingsds.network
8
+ * @date July 2022
9
+ */
10
+
11
+ /* global WebGPUWindow GPU */
12
+ // @ts-nocheck
13
+
14
+ self.wrapScriptLoading({ scriptName: 'gpu-timers' }, function gpuTimers$fn(protectedStorage, ring2PostMessage)
15
+ {
16
+ /* Default if we don't have webGL */
17
+ protectedStorage.getAndResetWebGLTimer = function defaultTimer()
18
+ {
19
+ return 0;
20
+ }
21
+
22
+ if (self.OffscreenCanvas && new OffscreenCanvas(1,1))
23
+ {
24
+ let time = 0;
25
+ function getAndResetWebGLTimer()
26
+ {
27
+ const tmp = time;
28
+ time = 0;
29
+ return tmp;
30
+ }
31
+ protectedStorage.getAndResetWebGLTimer = getAndResetWebGLTimer;
32
+
33
+ /* Factory to wrap a function from a context with a timer */
34
+ function timeWebGLFactory(context, prop)
35
+ {
36
+ let originalFn = context[prop].bind(context);
37
+
38
+ context[prop] = function wrappedWebGLFunction(...args)
39
+ {
40
+ var returnValue;
41
+ const start = performance.now();
42
+ try
43
+ {
44
+ returnValue = originalFn(...args);
45
+ time += performance.now() - start;
46
+ }
47
+ catch(e)
48
+ {
49
+ time += performance.now() - start;
50
+ throw e;
51
+ }
52
+ return returnValue;
53
+ }
54
+ }
55
+
56
+ /* Update all functions on the OffscreenCanvas getContext prototype to have timers */
57
+ let oldGetContext = OffscreenCanvas.prototype.getContext;
58
+ OffscreenCanvas.prototype.getContext = function(type, options)
59
+ {
60
+ let context = oldGetContext.bind(this)(type, options);
61
+ for (let key of Object.getOwnPropertyNames(context.__proto__))
62
+ if (typeof context[key] === 'function')
63
+ timeWebGLFactory(context, key);
64
+ return context;
65
+ };
66
+ }
67
+
68
+ });
@@ -135,6 +135,7 @@ try {
135
135
  serialize = newSerializer.serialize
136
136
  deserialize = newSerializer.deserialize
137
137
  outMsg = { type: 'nop', success: true }
138
+ send(outMsg);
138
139
  break
139
140
  case 'workerMessage':
140
141
  // if (inMsg.message.request === 'main') {
@@ -145,6 +146,7 @@ try {
145
146
  // }
146
147
  emitEvent('message', {data: inMsg.message})
147
148
  outMsg.success = true
149
+ send(outMsg)
148
150
  break
149
151
  case 'die':
150
152
  writeln('DIE: ' + Date())
@@ -157,7 +159,6 @@ try {
157
159
  outMsg.success = false
158
160
  outMsg.exception = { name: e.name, message: e.message, fileName: e.fileName, lineNumber: e.lineNumber, stack: e.stack }
159
161
  outMsg.e = e
160
- } finally {
161
162
  send(outMsg)
162
163
  }
163
164
  }) /* receiveLine */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dcp-client",
3
- "version": "4.2.6",
3
+ "version": "4.2.9",
4
4
  "description": "Core libraries for accessing DCP network",
5
5
  "keywords": [
6
6
  "dcp"
@@ -52,8 +52,8 @@
52
52
  "devDependencies": {
53
53
  "@kingsds/eslint-config": "1.0.1",
54
54
  "eslint": "7.30.0",
55
- "peter": "^2.2.0",
56
- "express": "^4.17.1"
55
+ "express": "^4.17.1",
56
+ "peter": "^2.3.2"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "dcp-worker": "^3.0.0"