dcp-client 4.2.7 → 4.2.10
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.
- package/LICENSE.md +1 -1
- package/README.md +9 -7
- package/dist/dcp-client-bundle.js +44 -45
- package/generated/sandbox-definitions.json +1 -1
- package/index.js +64 -27
- package/libexec/evaluator-node.js +2 -2
- package/libexec/sandbox/access-lists.js +2 -14
- package/libexec/sandbox/bootstrap.js +6 -5
- package/libexec/sandbox/bravojs-env.js +8 -13
- package/libexec/sandbox/bravojs-init.js +2 -2
- package/libexec/sandbox/calculate-capabilities.js +2 -37
- package/libexec/sandbox/event-loop-virtualization.js +28 -8
- package/libexec/sandbox/gpu-timers.js +69 -0
- package/libexec/sandbox/native-event-loop.js +17 -15
- package/libexec/sandbox/sa-ww-simulation.js +2 -1
- package/package.json +5 -5
|
@@ -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","
|
|
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","gpu-timers","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","webgpu-worker-environment","gpu-timers","access-lists","bravojs-init","bravojs/bravo.js","bravojs-env","calculate-capabilities","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","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
|
|
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 = []
|
|
@@ -95,6 +96,7 @@ const bundleSandbox = {
|
|
|
95
96
|
setImmediate, clearImmediate,
|
|
96
97
|
crypto: { getRandomValues: require('polyfill-crypto.getrandomvalues') },
|
|
97
98
|
dcpConfig: {
|
|
99
|
+
build: 'bootstrap',
|
|
98
100
|
bundleConfig: true,
|
|
99
101
|
scheduler: {},
|
|
100
102
|
bank: {
|
|
@@ -107,6 +109,30 @@ const bundleSandbox = {
|
|
|
107
109
|
},
|
|
108
110
|
};
|
|
109
111
|
|
|
112
|
+
function runSandboxedCode(sandbox, code, options)
|
|
113
|
+
{
|
|
114
|
+
if (process.env.DCP_CLIENT_LEGACY_CONTEXT_SANDBOXING)
|
|
115
|
+
{
|
|
116
|
+
const script = new vm.Script(code, options);
|
|
117
|
+
return script.runInNewContext(vm.createContext(sandbox), options);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (typeof runSandboxedCode.extraGlobalProps === 'undefined')
|
|
121
|
+
runSandboxedCode.extraGlobalProps = {};
|
|
122
|
+
|
|
123
|
+
for (let prop in sandbox)
|
|
124
|
+
{
|
|
125
|
+
if (!globalThis.hasOwnProperty(prop) || runSandboxedCode.extraGlobalProps.hasOwnProperty(prop))
|
|
126
|
+
{
|
|
127
|
+
globalThis[prop] = sandbox[prop];
|
|
128
|
+
runSandboxedCode.extraGlobalProps[prop] = true; /* Memoize global prop list mutation to allow re-mutation */
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const script = new vm.Script(code, options);
|
|
133
|
+
return script.runInThisContext(options);
|
|
134
|
+
}
|
|
135
|
+
|
|
110
136
|
/** Evaluate a file in a sandbox without polluting the global object.
|
|
111
137
|
* @param filename {string} The name of the file to evaluate, relative to
|
|
112
138
|
* @param sandbox {object} A sandbox object, used for injecting 'global' symbols as needed
|
|
@@ -114,7 +140,6 @@ const bundleSandbox = {
|
|
|
114
140
|
*/
|
|
115
141
|
function evalScriptInSandbox(filename, sandbox, olFlag) {
|
|
116
142
|
var code
|
|
117
|
-
var context = createContext(sandbox)
|
|
118
143
|
try {
|
|
119
144
|
code = fs.readFileSync(path.resolve(distDir, filename), 'utf-8')
|
|
120
145
|
if (olFlag)
|
|
@@ -126,7 +151,8 @@ function evalScriptInSandbox(filename, sandbox, olFlag) {
|
|
|
126
151
|
throw e
|
|
127
152
|
}
|
|
128
153
|
|
|
129
|
-
|
|
154
|
+
|
|
155
|
+
return runSandboxedCode(sandbox, code, { filename, lineNumber: 0 });
|
|
130
156
|
}
|
|
131
157
|
|
|
132
158
|
/** Evaluate code in a secure sandbox; in this case, the code is the configuration
|
|
@@ -134,18 +160,12 @@ function evalScriptInSandbox(filename, sandbox, olFlag) {
|
|
|
134
160
|
* during config file processing.
|
|
135
161
|
*
|
|
136
162
|
* @param code {string} The code to eval
|
|
137
|
-
* @param
|
|
138
|
-
* that will act like the context's global object
|
|
163
|
+
* @param sandbox {object} A sandbox object, used for injecting 'global' symbols as needed
|
|
139
164
|
* @param filename {string} The name of the file we're evaluating for stack-
|
|
140
165
|
* trace purposes.
|
|
141
166
|
*/
|
|
142
|
-
function evalStringInSandbox(
|
|
143
|
-
|
|
144
|
-
sandbox,
|
|
145
|
-
filename = '(dcp-client$$evalStringInSandbox)',
|
|
146
|
-
) {
|
|
147
|
-
const context = createContext(sandbox);
|
|
148
|
-
|
|
167
|
+
function evalStringInSandbox(code, sandbox, filename = '(dcp-client$$evalStringInSandbox)')
|
|
168
|
+
{
|
|
149
169
|
/**
|
|
150
170
|
* Remove comments and then decide if this config file contains an IIFE. If
|
|
151
171
|
* not, we need to wrap it as an IIFE to avoid "SyntaxError: Illegal return
|
|
@@ -167,7 +187,7 @@ function evalStringInSandbox(
|
|
|
167
187
|
* printed.
|
|
168
188
|
*/
|
|
169
189
|
try {
|
|
170
|
-
result =
|
|
190
|
+
result = runSandboxedCode(sandbox, code, {
|
|
171
191
|
filename,
|
|
172
192
|
lineOffset,
|
|
173
193
|
/**
|
|
@@ -301,7 +321,7 @@ function addConfig (existing, neo) {
|
|
|
301
321
|
existing[prop] = new (existing[prop].constructor)(neo[prop]);
|
|
302
322
|
continue;
|
|
303
323
|
}
|
|
304
|
-
if (typeof neo[prop] === 'object' && !Array.isArray(neo[prop]) && ['Function','Object'].includes(neo[prop].constructor.name)) {
|
|
324
|
+
if (typeof neo[prop] === 'object' && neo[prop] !== null && !Array.isArray(neo[prop]) && ['Function','Object'].includes(neo[prop].constructor.name)) {
|
|
305
325
|
if (typeof existing[prop] === 'undefined') {
|
|
306
326
|
existing[prop] = {}
|
|
307
327
|
}
|
|
@@ -447,7 +467,11 @@ function addConfigFile(existing /*, file path components ... */) {
|
|
|
447
467
|
* becomes the neo config.
|
|
448
468
|
*/
|
|
449
469
|
async function addConfigRKey(existing, hive, keyTail) {
|
|
450
|
-
var neo
|
|
470
|
+
var neo;
|
|
471
|
+
// make sure RKey calls do not execute the windows registry calls on non-windows platforms
|
|
472
|
+
if (os.platform() !== 'win32')
|
|
473
|
+
return;
|
|
474
|
+
neo = await require('./windows-registry').getObject(hive, keyTail);
|
|
451
475
|
debugging() && console.debug(` * Loading configuration from ${hive} ${keyTail}`);
|
|
452
476
|
if (neo)
|
|
453
477
|
addConfig(existing, neo);
|
|
@@ -466,11 +490,10 @@ function addConfigEnviron(existing, prefix) {
|
|
|
466
490
|
continue
|
|
467
491
|
}
|
|
468
492
|
if (process.env[v][0] === '{') {
|
|
469
|
-
// FIXME(bryan-hoang): fixCase is not defined.
|
|
470
493
|
let prop = fixCase(v.slice(prefix.length))
|
|
471
494
|
if (typeof neo[prop] !== 'object') {
|
|
472
495
|
neo[prop] = {}
|
|
473
|
-
addConfig(neo[prop],
|
|
496
|
+
addConfig(neo[prop], eval(`"use strict"; (${process.env[v]})`));
|
|
474
497
|
} else {
|
|
475
498
|
if (typeof neo[prop] === "object") {
|
|
476
499
|
throw new Error("Cannot override configuration property " + prop + " with a string (is an object)")
|
|
@@ -483,6 +506,18 @@ function addConfigEnviron(existing, prefix) {
|
|
|
483
506
|
addConfig(existing, neo);
|
|
484
507
|
}
|
|
485
508
|
|
|
509
|
+
/** Turn UGLY_STRING into uglyString */
|
|
510
|
+
function fixCase(ugly)
|
|
511
|
+
{
|
|
512
|
+
var fixed = ugly.toLowerCase();
|
|
513
|
+
var idx;
|
|
514
|
+
|
|
515
|
+
while ((idx = fixed.indexOf('_')) !== -1)
|
|
516
|
+
fixed = fixed.slice(0, idx) + fixed[idx + 1].toUpperCase() + fixed.slice(idx + 2);
|
|
517
|
+
|
|
518
|
+
return fixed;
|
|
519
|
+
}
|
|
520
|
+
|
|
486
521
|
/**
|
|
487
522
|
* Tasks which are run in the early phases of initialization
|
|
488
523
|
* - plumb in global.XMLHttpRequest which lives forever -- that way KeepAlive etc works.
|
|
@@ -575,7 +610,7 @@ function initTail(aggrConfig, finalBundleCode, finalBundleURL) {
|
|
|
575
610
|
require('dcp/dcp-url').patchup(aggrConfig);
|
|
576
611
|
|
|
577
612
|
/* 3 */
|
|
578
|
-
if (
|
|
613
|
+
if (hadOriginalDcpConfig) {
|
|
579
614
|
/* dcpConfig was defined before dcp-client was initialized: assume dev knows what he/she is doing */
|
|
580
615
|
debugging() && console.debug('Dropping bundle dcp-config in favour of global dcpConfig')
|
|
581
616
|
Object.assign(require('dcp/dcp-config'), global.dcpConfig);
|
|
@@ -587,6 +622,8 @@ function initTail(aggrConfig, finalBundleCode, finalBundleURL) {
|
|
|
587
622
|
bundleSandbox.dcpConfig = defaultConfig;
|
|
588
623
|
}
|
|
589
624
|
|
|
625
|
+
bundleSandbox.dcpConfig.build = require('dcp/build').config.build;
|
|
626
|
+
|
|
590
627
|
Object.defineProperty(exports, 'distDir', {
|
|
591
628
|
value: function dcpClient$$distDir$getter() {
|
|
592
629
|
return distDir;
|
|
@@ -638,6 +675,7 @@ function initTail(aggrConfig, finalBundleCode, finalBundleURL) {
|
|
|
638
675
|
ret = makeInitReturnObject();
|
|
639
676
|
if (bundle.postInitTailHook) /* for use by auto-update future backwards compat */
|
|
640
677
|
ret = bundle.postInitTailHook(ret, aggrConfig, bundle, finalBundleLabel, bundleSandbox, injectModule);
|
|
678
|
+
dcpConfig.build = bundleSandbox.dcpConfig.build = require('dcp/build').config.build;
|
|
641
679
|
return ret;
|
|
642
680
|
}
|
|
643
681
|
|
|
@@ -759,13 +797,13 @@ exports.createAggregateConfig = async function dcpClient$$createAggregateConfig(
|
|
|
759
797
|
let config = localConfig;
|
|
760
798
|
|
|
761
799
|
/* This follows spec doc line-by-line */
|
|
762
|
-
|
|
763
|
-
|
|
800
|
+
await addConfigRKey(config, 'HKLM', 'dcp-client/dcp-config');
|
|
801
|
+
addConfigFile(config, etc, 'dcp/dcp-client/dcp-config.js');
|
|
764
802
|
programName && await addConfigRKey(config, 'HKLM', `dcp-client/${programName}/dcp-config`);
|
|
765
|
-
programName &&
|
|
766
|
-
|
|
767
|
-
programName &&
|
|
768
|
-
|
|
803
|
+
programName && addConfigFile(config, etc, `dcp/dcp-client/${programName}/dcp-config.js`);
|
|
804
|
+
addConfigFile(config, home, '.dcp/dcp-client/dcp-config.js');
|
|
805
|
+
programName && addConfigFile(config, home, `.dcp/dcp-client/${programName}/dcp-config.js`);
|
|
806
|
+
await addConfigRKey(config, 'HKCU', 'dcp-client/dcp-config');
|
|
769
807
|
programName && await addConfigRKey(config, 'HKCU', `dcp-client/${programName}/dcp-config`);
|
|
770
808
|
|
|
771
809
|
// Sort out polymorphic arguments: 'passed-in configuration'.
|
|
@@ -787,9 +825,8 @@ exports.createAggregateConfig = async function dcpClient$$createAggregateConfig(
|
|
|
787
825
|
if (initArgv[2])
|
|
788
826
|
addConfig(localConfig.bundle, { location: new URL(initArgv[2])});
|
|
789
827
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
await addConfigRKey(localConfig, 'HKLM', 'override/dcp-config');
|
|
828
|
+
addConfigEnviron(localConfig, 'DCP_CONFIG_');
|
|
829
|
+
addConfigFile(localConfig, etc, 'dcp/override/dcp-config.js');
|
|
793
830
|
addConfig(aggrConfig, localConfig);
|
|
794
831
|
|
|
795
832
|
/**
|
|
@@ -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: (
|
|
69
|
+
die: () => { this.destroy(); },
|
|
70
70
|
writeln: (string) => { this.writeln(string) },
|
|
71
71
|
onreadln: (handler) => { this.onreadlnHandler = handler },
|
|
72
72
|
setTimeout,
|
|
@@ -366,7 +366,7 @@ function main() {
|
|
|
366
366
|
setupErrorHandlers();
|
|
367
367
|
|
|
368
368
|
const argv = require('yargs')
|
|
369
|
-
.usage('Node Evaluator - Copyright (c) 2020-
|
|
369
|
+
.usage('Node Evaluator - Copyright (c) 2020-2022 Distributive, Ltd. All Rights Reserved.'
|
|
370
370
|
+ 'Usage: dcp-evaluator [options] [<file.js> <file.js> ...]')
|
|
371
371
|
.options({
|
|
372
372
|
port: {
|
|
@@ -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
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file worker/evaluator-lib/bootstrap.js
|
|
3
|
-
* Copyright (c) 2018,
|
|
3
|
+
* Copyright (c) 2018-2022, Distributive, Ltd. All Rights Reserved.
|
|
4
4
|
*
|
|
5
5
|
* Final evaluator bootstrap code for defining functions to be used in the work function.
|
|
6
6
|
*
|
|
@@ -131,15 +131,16 @@ self.wrapScriptLoading({ scriptName: 'bootstrap', finalScript: true }, function
|
|
|
131
131
|
return true;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
function workerBootstrap$work$emit(
|
|
135
|
-
if (typeof
|
|
136
|
-
throw new Error(`Event name passed to work.emit must be a string, not ${
|
|
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
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* This file extends BravoJS, creating a CommonJS Modules/2.0
|
|
3
3
|
* environment for WebWorkers and similar environments.
|
|
4
4
|
*
|
|
5
|
-
* Copyright (c) 2018,
|
|
5
|
+
* Copyright (c) 2018-2022, Distributive, Ltd. All Rights Reserved.
|
|
6
6
|
* Wes Garland, wes@kingsds.network
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -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
|
-
|
|
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
|
|
236
|
-
const
|
|
237
|
-
|
|
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 */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file worker/evaluator-lib/bravojs-init.js
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2020-2022, Distributive, Ltd. All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* This file sets up the environment for
|
|
5
|
+
* This file sets up the environment for BravoJS to load properly.
|
|
6
6
|
*
|
|
7
7
|
* @author Ryan Rossiter, ryan@kingsds.network
|
|
8
8
|
* @date Sept 2020
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file worker/evaluator-lib/calculate-capabilities.js
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2020-2022, Distributive. All Rights Reserved.
|
|
4
4
|
*
|
|
5
5
|
* This file provides a message handler for handling capabilities requests.
|
|
6
6
|
*
|
|
@@ -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.
|
|
@@ -20,6 +20,7 @@ self.wrapScriptLoading({ scriptName: 'event-loop-virtualization' }, function eve
|
|
|
20
20
|
{
|
|
21
21
|
(function privateScope(realSetTimeout, realSetInterval, realSetImmediate, realClearTimeout, realClearInterval, realClearImmediate) {
|
|
22
22
|
let totalCPUTime = 0;
|
|
23
|
+
let startTime;
|
|
23
24
|
const events = [];
|
|
24
25
|
events.serial = 0;
|
|
25
26
|
|
|
@@ -32,8 +33,9 @@ self.wrapScriptLoading({ scriptName: 'event-loop-virtualization' }, function eve
|
|
|
32
33
|
serviceEvents.timeout = null;
|
|
33
34
|
serviceEvents.nextTimeout = null;
|
|
34
35
|
serviceEvents.servicing = true;
|
|
36
|
+
serviceEvents.sliceIsFinished = false;
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
startTime = performance.now();
|
|
37
39
|
let now = Date.now();
|
|
38
40
|
|
|
39
41
|
sortEvents();
|
|
@@ -58,17 +60,31 @@ self.wrapScriptLoading({ scriptName: 'event-loop-virtualization' }, function eve
|
|
|
58
60
|
function endOfRealEventCycle()
|
|
59
61
|
{
|
|
60
62
|
serviceEvents.servicing = false;
|
|
61
|
-
|
|
62
|
-
totalCPUTime += endTime - startTime;
|
|
63
|
-
|
|
64
|
-
// Set timeout to rerun this function if there are events remaining that just can't be used yet
|
|
65
|
-
if (events.length > 0)
|
|
63
|
+
if (!serviceEvents.sliceIsFinished)
|
|
66
64
|
{
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
const endTime = performance.now();
|
|
66
|
+
totalCPUTime += endTime - startTime;
|
|
67
|
+
|
|
68
|
+
// Set timeout to rerun this function if there are events remaining that just can't be used yet
|
|
69
|
+
if (events.length > 0)
|
|
70
|
+
{
|
|
71
|
+
serviceEvents.nextTimeout = events[0].when
|
|
72
|
+
serviceEvents.timeout = realSetTimeout(serviceEvents, events[0].when - Date.now());
|
|
73
|
+
}
|
|
69
74
|
}
|
|
70
75
|
}
|
|
71
76
|
}
|
|
77
|
+
protectedStorage.markCPUTimeAsDone = function markCPUTimeAsDone()
|
|
78
|
+
{
|
|
79
|
+
const endTime = performance.now();
|
|
80
|
+
totalCPUTime += endTime - startTime;
|
|
81
|
+
serviceEvents.sliceIsFinished = true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protectedStorage.subtractWebGLTimeFromCPUTime = function subtractCPUTime(time)
|
|
85
|
+
{
|
|
86
|
+
totalCPUTime -= time;
|
|
87
|
+
}
|
|
72
88
|
|
|
73
89
|
/** Execute callback after at least timeout ms.
|
|
74
90
|
*
|
|
@@ -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,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file gpu-timers.js
|
|
3
|
+
* Copyright (c) 2022, Distributive, Ltd.
|
|
4
|
+
* All Rights Reserved. Licensed under the terms of the MIT License.
|
|
5
|
+
*
|
|
6
|
+
* This file adds wrappers for webGL and webGPU functions so we can measure their GPU time
|
|
7
|
+
*
|
|
8
|
+
* @author Ryan Saweczko, ryansaweczko@kingsds.network
|
|
9
|
+
* @date July 2022
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* global WebGPUWindow GPU */
|
|
13
|
+
// @ts-nocheck
|
|
14
|
+
|
|
15
|
+
self.wrapScriptLoading({ scriptName: 'gpu-timers' }, function gpuTimers$fn(protectedStorage, ring2PostMessage)
|
|
16
|
+
{
|
|
17
|
+
/* Default if we don't have webGL */
|
|
18
|
+
protectedStorage.getAndResetWebGLTimer = function defaultTimer()
|
|
19
|
+
{
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (self.OffscreenCanvas && new OffscreenCanvas(1,1))
|
|
24
|
+
{
|
|
25
|
+
let time = 0;
|
|
26
|
+
function getAndResetWebGLTimer()
|
|
27
|
+
{
|
|
28
|
+
const tmp = time;
|
|
29
|
+
time = 0;
|
|
30
|
+
return tmp;
|
|
31
|
+
}
|
|
32
|
+
protectedStorage.getAndResetWebGLTimer = getAndResetWebGLTimer;
|
|
33
|
+
|
|
34
|
+
/* Factory to wrap a function from a context with a timer */
|
|
35
|
+
function timeWebGLFactory(context, prop)
|
|
36
|
+
{
|
|
37
|
+
let originalFn = context[prop].bind(context);
|
|
38
|
+
|
|
39
|
+
context[prop] = function wrappedWebGLFunction(...args)
|
|
40
|
+
{
|
|
41
|
+
var returnValue;
|
|
42
|
+
const start = performance.now();
|
|
43
|
+
try
|
|
44
|
+
{
|
|
45
|
+
returnValue = originalFn(...args);
|
|
46
|
+
time += performance.now() - start;
|
|
47
|
+
}
|
|
48
|
+
catch(e)
|
|
49
|
+
{
|
|
50
|
+
time += performance.now() - start;
|
|
51
|
+
throw e;
|
|
52
|
+
}
|
|
53
|
+
return returnValue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Update all functions on the OffscreenCanvas getContext prototype to have timers */
|
|
58
|
+
let oldGetContext = OffscreenCanvas.prototype.getContext;
|
|
59
|
+
OffscreenCanvas.prototype.getContext = function(type, options)
|
|
60
|
+
{
|
|
61
|
+
let context = oldGetContext.bind(this)(type, options);
|
|
62
|
+
for (let key of Object.getOwnPropertyNames(context.__proto__))
|
|
63
|
+
if (typeof context[key] === 'function')
|
|
64
|
+
timeWebGLFactory(context, key);
|
|
65
|
+
return context;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
});
|
|
@@ -51,27 +51,29 @@ self.wrapScriptLoading({ scriptName: 'native-event-loop' }, function nativeEvent
|
|
|
51
51
|
function fireTimerCallbacks()
|
|
52
52
|
{
|
|
53
53
|
sortTimers();
|
|
54
|
-
let timer;
|
|
55
|
-
|
|
54
|
+
let timer = timers.shift();
|
|
55
|
+
let now = Date.now();
|
|
56
|
+
if (!timer)
|
|
57
|
+
throw new Error('Logic error: trying to run timer when no timer exists') /* should be impossible */
|
|
58
|
+
if (timer.when > now) /* should be impossible, but at least we can handle this */
|
|
56
59
|
{
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
timer.fn.apply(null, timer.args);
|
|
60
|
+
timers.unshift(timer);
|
|
61
|
+
nextTimer(timers[0].when);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
timer.fn.apply(null, timer.args);
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
sortTimers();
|
|
66
|
+
if (timer.recur)
|
|
67
|
+
{
|
|
68
|
+
timer.when = Date.now() + timer.recur;
|
|
69
|
+
timers.push(timer);
|
|
71
70
|
}
|
|
71
|
+
|
|
72
|
+
sortTimers();
|
|
72
73
|
if (timers.length)
|
|
73
74
|
nextTimer(timers[0].when);
|
|
74
75
|
}
|
|
76
|
+
|
|
75
77
|
ontimer(fireTimerCallbacks);
|
|
76
78
|
|
|
77
79
|
/** Execute callback after at least timeout ms.
|
|
@@ -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 */
|