@jsenv/test 1.6.9 → 1.7.0
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/dist/js/execute_using_dynamic_import.js +118 -771
- package/dist/jsenv_test.js +409 -99
- package/dist/{controllable_child_process.mjs → node_child_process_controlled.mjs} +6 -13
- package/dist/{controllable_worker_thread.mjs → node_worker_thread_controlled.mjs} +6 -13
- package/package.json +3 -3
- package/src/execution/exception.js +137 -0
- package/src/execution/execute_steps.js +8 -13
- package/src/execution/execute_test_plan.js +132 -6
- package/src/execution/github_annotation_from_error.js +132 -0
- package/src/execution/logs_file_execution.js +134 -62
- package/src/runtime_browsers/using_playwright.js +2 -8
- package/src/runtime_node/node_child_process.js +5 -6
- package/src/runtime_node/{controllable_child_process.mjs → node_child_process_controlled.mjs} +6 -14
- package/src/runtime_node/node_worker_thread.js +4 -5
- package/src/runtime_node/{controllable_worker_thread.mjs → node_worker_thread_controlled.mjs} +6 -13
- package/src/execution/logs_file_execution.test.mjs +0 -41
|
@@ -2,795 +2,142 @@ import { writeFileSync } from "node:fs";
|
|
|
2
2
|
import { Session } from "node:inspector";
|
|
3
3
|
import { PerformanceObserver, performance } from "node:perf_hooks";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return false
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const type = typeof value;
|
|
13
|
-
if (type === "object") {
|
|
14
|
-
return true
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (type === "function") {
|
|
18
|
-
return true
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return false
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
|
|
25
|
-
const compositeWellKnownMap = new WeakMap();
|
|
26
|
-
const primitiveWellKnownMap = new Map();
|
|
27
|
-
|
|
28
|
-
const getCompositeGlobalPath = (value) => compositeWellKnownMap.get(value);
|
|
29
|
-
|
|
30
|
-
const getPrimitiveGlobalPath = (value) => primitiveWellKnownMap.get(value);
|
|
31
|
-
|
|
32
|
-
const visitGlobalObject = (value) => {
|
|
33
|
-
const visitValue = (value, path) => {
|
|
34
|
-
if (isComposite(value)) {
|
|
35
|
-
// prevent infinite recursion
|
|
36
|
-
if (compositeWellKnownMap.has(value)) {
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
compositeWellKnownMap.set(value, path);
|
|
40
|
-
|
|
41
|
-
const visitProperty = (property) => {
|
|
42
|
-
let descriptor;
|
|
43
|
-
try {
|
|
44
|
-
descriptor = Object.getOwnPropertyDescriptor(value, property);
|
|
45
|
-
} catch (e) {
|
|
46
|
-
if (e.name === "SecurityError") {
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
throw e
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!descriptor) {
|
|
53
|
-
// it's apparently possible to have getOwnPropertyNames returning
|
|
54
|
-
// a property that later returns a null descriptor
|
|
55
|
-
// for instance window.showModalDialog in webkit 13.0
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// do not trigger getter/setter
|
|
60
|
-
if ("value" in descriptor) {
|
|
61
|
-
const propertyValue = descriptor.value;
|
|
62
|
-
visitValue(propertyValue, [...path, property]);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
Object.getOwnPropertyNames(value).forEach((name) => visitProperty(name));
|
|
67
|
-
Object.getOwnPropertySymbols(value).forEach((symbol) => visitProperty(symbol));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
primitiveWellKnownMap.set(value, path);
|
|
71
|
-
return
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
visitValue(value, []);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
if (typeof window === "object") visitGlobalObject(window);
|
|
78
|
-
|
|
79
|
-
if (typeof global === "object") visitGlobalObject(global);
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* transforms a javascript value into an object describing it.
|
|
5
|
+
/*
|
|
6
|
+
* Exception are objects used to wrap a value that is thrown
|
|
7
|
+
* Usually they wrap error but the value can be anything as
|
|
8
|
+
* throw "toto" can be used in JS
|
|
83
9
|
*
|
|
10
|
+
* - provide a common API to interact with value that can be thrown
|
|
11
|
+
* - enrich usual errors with a bit more information (line, column)
|
|
12
|
+
* - normalize error properties
|
|
13
|
+
* - exception.stackTrace: only the stack trace as string (no error.name or error.message)
|
|
14
|
+
* - error.stack: error.name + error.message + error.stackTrace
|
|
15
|
+
*
|
|
16
|
+
* It is used mostly internally by jsenv but can also be found
|
|
17
|
+
* value returned by "executeTestPlan" public export (for failed executions)
|
|
18
|
+
* and value returned by "execute" public export (when execution fails)
|
|
19
|
+
*
|
|
20
|
+
* This file is responsible to wrap error hapenning in Node.js runtime
|
|
21
|
+
* The browser part can be found in "supervisor.js"
|
|
84
22
|
*/
|
|
85
23
|
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return existingIdentifier
|
|
96
|
-
}
|
|
97
|
-
const identifier = identifierForNewValue(value);
|
|
98
|
-
recipeArray[identifier] = primitiveToRecipe(value);
|
|
99
|
-
return identifier
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (typeof Promise === "function" && value instanceof Promise) {
|
|
103
|
-
throw new Error(createPromiseAreNotSupportedMessage({ path }))
|
|
104
|
-
}
|
|
105
|
-
if (typeof WeakSet === "function" && value instanceof WeakSet) {
|
|
106
|
-
throw new Error(createWeakSetAreNotSupportedMessage({ path }))
|
|
107
|
-
}
|
|
108
|
-
if (typeof WeakMap === "function" && value instanceof WeakMap) {
|
|
109
|
-
throw new Error(createWeakMapAreNotSupportedMessage({ path }))
|
|
110
|
-
}
|
|
111
|
-
if (typeof value === "function" && !functionAllowed) {
|
|
112
|
-
throw new Error(createForbiddenFunctionMessage({ path }))
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const existingIdentifier = identifierForComposite(value);
|
|
116
|
-
if (existingIdentifier !== undefined) {
|
|
117
|
-
return existingIdentifier
|
|
118
|
-
}
|
|
119
|
-
const identifier = identifierForNewValue(value);
|
|
120
|
-
|
|
121
|
-
const compositeGlobalPath = getCompositeGlobalPath(value);
|
|
122
|
-
if (compositeGlobalPath) {
|
|
123
|
-
recipeArray[identifier] = createGlobalReferenceRecipe(compositeGlobalPath);
|
|
124
|
-
return identifier
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const propertyDescriptionArray = [];
|
|
128
|
-
Object.getOwnPropertyNames(value).forEach((propertyName) => {
|
|
129
|
-
const propertyDescriptor = Object.getOwnPropertyDescriptor(value, propertyName);
|
|
130
|
-
const propertyNameIdentifier = valueToIdentifier(propertyName, [...path, propertyName]);
|
|
131
|
-
const propertyDescription = computePropertyDescription(propertyDescriptor, propertyName, path);
|
|
132
|
-
propertyDescriptionArray.push({ propertyNameIdentifier, propertyDescription });
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
const symbolDescriptionArray = [];
|
|
136
|
-
if (!ignoreSymbols) {
|
|
137
|
-
Object.getOwnPropertySymbols(value).forEach((symbol) => {
|
|
138
|
-
const propertyDescriptor = Object.getOwnPropertyDescriptor(value, symbol);
|
|
139
|
-
const symbolIdentifier = valueToIdentifier(symbol, [...path, `[${symbol.toString()}]`]);
|
|
140
|
-
const propertyDescription = computePropertyDescription(propertyDescriptor, symbol, path);
|
|
141
|
-
symbolDescriptionArray.push({ symbolIdentifier, propertyDescription });
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const methodDescriptionArray = computeMethodDescriptionArray(value, path);
|
|
146
|
-
|
|
147
|
-
const extensible = Object.isExtensible(value);
|
|
148
|
-
|
|
149
|
-
recipeArray[identifier] = createCompositeRecipe({
|
|
150
|
-
propertyDescriptionArray,
|
|
151
|
-
symbolDescriptionArray,
|
|
152
|
-
methodDescriptionArray,
|
|
153
|
-
extensible,
|
|
154
|
-
});
|
|
155
|
-
return identifier
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const computePropertyDescription = (propertyDescriptor, propertyNameOrSymbol, path) => {
|
|
159
|
-
if (propertyDescriptor.set && !functionAllowed) {
|
|
160
|
-
throw new Error(createForbiddenPropertySetterMessage({ path, propertyNameOrSymbol }))
|
|
161
|
-
}
|
|
162
|
-
if (propertyDescriptor.get && !functionAllowed) {
|
|
163
|
-
throw new Error(createForbiddenPropertyGetterMessage({ path, propertyNameOrSymbol }))
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
configurable: propertyDescriptor.configurable,
|
|
168
|
-
writable: propertyDescriptor.writable,
|
|
169
|
-
enumerable: propertyDescriptor.enumerable,
|
|
170
|
-
getIdentifier:
|
|
171
|
-
"get" in propertyDescriptor
|
|
172
|
-
? valueToIdentifier(propertyDescriptor.get, [
|
|
173
|
-
...path,
|
|
174
|
-
String(propertyNameOrSymbol),
|
|
175
|
-
"[[descriptor:get]]",
|
|
176
|
-
])
|
|
177
|
-
: undefined,
|
|
178
|
-
setIdentifier:
|
|
179
|
-
"set" in propertyDescriptor
|
|
180
|
-
? valueToIdentifier(propertyDescriptor.set, [
|
|
181
|
-
...path,
|
|
182
|
-
String(propertyNameOrSymbol),
|
|
183
|
-
"[[descriptor:set]]",
|
|
184
|
-
])
|
|
185
|
-
: undefined,
|
|
186
|
-
valueIdentifier:
|
|
187
|
-
"value" in propertyDescriptor
|
|
188
|
-
? valueToIdentifier(propertyDescriptor.value, [
|
|
189
|
-
...path,
|
|
190
|
-
String(propertyNameOrSymbol),
|
|
191
|
-
"[[descriptor:value]]",
|
|
192
|
-
])
|
|
193
|
-
: undefined,
|
|
194
|
-
}
|
|
24
|
+
const createException = (reason) => {
|
|
25
|
+
const exception = {
|
|
26
|
+
isException: true,
|
|
27
|
+
isError: false,
|
|
28
|
+
name: "",
|
|
29
|
+
message: "",
|
|
30
|
+
stack: "",
|
|
31
|
+
stackTrace: "",
|
|
32
|
+
site: null,
|
|
195
33
|
};
|
|
196
34
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (typeof Set === "function" && value instanceof Set) {
|
|
201
|
-
const callArray = [];
|
|
202
|
-
value.forEach((entryValue, index) => {
|
|
203
|
-
const entryValueIdentifier = valueToIdentifier(entryValue, [
|
|
204
|
-
...path,
|
|
205
|
-
`[[SetEntryValue]]`,
|
|
206
|
-
index,
|
|
207
|
-
]);
|
|
208
|
-
callArray.push([entryValueIdentifier]);
|
|
209
|
-
});
|
|
210
|
-
methodDescriptionArray.push({ methodNameIdentifier: valueToIdentifier("add"), callArray });
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (typeof Map === "function" && value instanceof Map) {
|
|
214
|
-
const callArray = [];
|
|
215
|
-
value.forEach((entryValue, entryKey) => {
|
|
216
|
-
const entryKeyIdentifier = valueToIdentifier(entryKey, [
|
|
217
|
-
...path,
|
|
218
|
-
"[[MapEntryKey]]",
|
|
219
|
-
entryKey,
|
|
220
|
-
]);
|
|
221
|
-
const entryValueIdentifier = valueToIdentifier(entryValue, [
|
|
222
|
-
...path,
|
|
223
|
-
"[[MapEntryValue]]",
|
|
224
|
-
entryValue,
|
|
225
|
-
]);
|
|
226
|
-
callArray.push([entryKeyIdentifier, entryValueIdentifier]);
|
|
227
|
-
});
|
|
228
|
-
methodDescriptionArray.push({ methodNameIdentifier: valueToIdentifier("set"), callArray });
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return methodDescriptionArray
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
const identifierForPrimitive = (value) => {
|
|
235
|
-
return Object.keys(valueMap).find((existingIdentifier) => {
|
|
236
|
-
const existingValue = valueMap[existingIdentifier];
|
|
237
|
-
if (Object.is(value, existingValue)) return true
|
|
238
|
-
return value === existingValue
|
|
239
|
-
})
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const identifierForComposite = (value) => {
|
|
243
|
-
return Object.keys(valueMap).find((existingIdentifier) => {
|
|
244
|
-
const existingValue = valueMap[existingIdentifier];
|
|
245
|
-
return value === existingValue
|
|
246
|
-
})
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
const identifierForNewValue = (value) => {
|
|
250
|
-
const identifier = nextIdentifier();
|
|
251
|
-
valueMap[identifier] = value;
|
|
252
|
-
return identifier
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
let currentIdentifier = -1;
|
|
256
|
-
const nextIdentifier = () => {
|
|
257
|
-
const identifier = String(parseInt(currentIdentifier) + 1);
|
|
258
|
-
currentIdentifier = identifier;
|
|
259
|
-
return identifier
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const mainIdentifier = valueToIdentifier(mainValue);
|
|
263
|
-
|
|
264
|
-
// prototype, important to keep after the whole structure was visited
|
|
265
|
-
// so that we discover if any prototype is part of the value
|
|
266
|
-
const prototypeValueToIdentifier = (prototypeValue) => {
|
|
267
|
-
// prototype is null
|
|
268
|
-
if (prototypeValue === null) {
|
|
269
|
-
return valueToIdentifier(prototypeValue)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// prototype found somewhere already
|
|
273
|
-
const prototypeExistingIdentifier = identifierForComposite(prototypeValue);
|
|
274
|
-
if (prototypeExistingIdentifier !== undefined) {
|
|
275
|
-
return prototypeExistingIdentifier
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// mark prototype as visited
|
|
279
|
-
const prototypeIdentifier = identifierForNewValue(prototypeValue);
|
|
280
|
-
|
|
281
|
-
// prototype is a global reference ?
|
|
282
|
-
const prototypeGlobalPath = getCompositeGlobalPath(prototypeValue);
|
|
283
|
-
if (prototypeGlobalPath) {
|
|
284
|
-
recipeArray[prototypeIdentifier] = createGlobalReferenceRecipe(prototypeGlobalPath);
|
|
285
|
-
return prototypeIdentifier
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// otherwise prototype is unknown
|
|
289
|
-
if (prototypeStrict) {
|
|
290
|
-
throw new Error(createUnknownPrototypeMessage({ prototypeValue }))
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return prototypeValueToIdentifier(Object.getPrototypeOf(prototypeValue))
|
|
294
|
-
};
|
|
295
|
-
const identifierForValueOf = (value, path = []) => {
|
|
296
|
-
if (value instanceof Array) {
|
|
297
|
-
return valueToIdentifier(value.length, [...path, "length"])
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if ("valueOf" in value === false) {
|
|
301
|
-
return undefined
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (typeof value.valueOf !== "function") {
|
|
305
|
-
return undefined
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const valueOfReturnValue = value.valueOf();
|
|
309
|
-
if (!isComposite(valueOfReturnValue)) {
|
|
310
|
-
return valueToIdentifier(valueOfReturnValue, [...path, "valueOf()"])
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (valueOfReturnValue === value) {
|
|
314
|
-
return undefined
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
throw new Error(createUnexpectedValueOfReturnValueMessage())
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
recipeArray.slice().forEach((recipe, index) => {
|
|
321
|
-
if (recipe.type === "composite") {
|
|
322
|
-
const value = valueMap[index];
|
|
323
|
-
|
|
324
|
-
if (typeof value === "function") {
|
|
325
|
-
const valueOfIdentifier = nextIdentifier();
|
|
326
|
-
recipeArray[valueOfIdentifier] = {
|
|
327
|
-
type: "primitive",
|
|
328
|
-
value,
|
|
329
|
-
};
|
|
330
|
-
recipe.valueOfIdentifier = valueOfIdentifier;
|
|
331
|
-
return
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (value instanceof RegExp) {
|
|
335
|
-
const valueOfIdentifier = nextIdentifier();
|
|
336
|
-
recipeArray[valueOfIdentifier] = {
|
|
337
|
-
type: "primitive",
|
|
338
|
-
value,
|
|
339
|
-
};
|
|
340
|
-
recipe.valueOfIdentifier = valueOfIdentifier;
|
|
341
|
-
return
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// valueOf, mandatory to uneval new Date(10) for instance.
|
|
345
|
-
recipe.valueOfIdentifier = identifierForValueOf(value);
|
|
346
|
-
const prototypeValue = Object.getPrototypeOf(value);
|
|
347
|
-
recipe.prototypeIdentifier = prototypeValueToIdentifier(prototypeValue);
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
return {
|
|
352
|
-
recipeArray,
|
|
353
|
-
mainIdentifier,
|
|
354
|
-
valueMap,
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
const primitiveToRecipe = (value) => {
|
|
359
|
-
if (typeof value === "symbol") {
|
|
360
|
-
return symbolToRecipe(value)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return createPimitiveRecipe(value)
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
const symbolToRecipe = (symbol) => {
|
|
367
|
-
const globalSymbolKey = Symbol.keyFor(symbol);
|
|
368
|
-
if (globalSymbolKey !== undefined) {
|
|
369
|
-
return createGlobalSymbolRecipe(globalSymbolKey)
|
|
35
|
+
if (reason === undefined) {
|
|
36
|
+
exception.message = "undefined";
|
|
37
|
+
return exception;
|
|
370
38
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
throw new Error(createUnknownSymbolMessage({ symbol }))
|
|
39
|
+
if (reason === undefined) {
|
|
40
|
+
exception.message = "undefined";
|
|
41
|
+
return exception;
|
|
375
42
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const createPimitiveRecipe = (value) => {
|
|
381
|
-
return {
|
|
382
|
-
type: "primitive",
|
|
383
|
-
value,
|
|
43
|
+
if (typeof reason === "string") {
|
|
44
|
+
exception.message = reason;
|
|
45
|
+
return exception;
|
|
384
46
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
key,
|
|
47
|
+
if (reason instanceof Error) {
|
|
48
|
+
exception.isError = true;
|
|
49
|
+
exception.name = reason.name;
|
|
50
|
+
exception.message = reason.message;
|
|
51
|
+
const stackInfo = getStackInfo(reason);
|
|
52
|
+
if (stackInfo) {
|
|
53
|
+
const { stackTrace, stackObject } = stackInfo;
|
|
54
|
+
exception.stackTrace = stackTrace;
|
|
55
|
+
const [firstCallSite] = stackObject;
|
|
56
|
+
writePropertiesFromCallSite(exception, firstCallSite);
|
|
57
|
+
}
|
|
58
|
+
exception.stack = reason.stack;
|
|
59
|
+
return exception;
|
|
399
60
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
propertyDescriptionArray,
|
|
406
|
-
symbolDescriptionArray,
|
|
407
|
-
methodDescriptionArray,
|
|
408
|
-
extensible,
|
|
409
|
-
}) => {
|
|
410
|
-
return {
|
|
411
|
-
type: "composite",
|
|
412
|
-
prototypeIdentifier,
|
|
413
|
-
valueOfIdentifier,
|
|
414
|
-
propertyDescriptionArray,
|
|
415
|
-
symbolDescriptionArray,
|
|
416
|
-
methodDescriptionArray,
|
|
417
|
-
extensible,
|
|
61
|
+
if (typeof reason === "object") {
|
|
62
|
+
exception.code = reason.code;
|
|
63
|
+
exception.message = reason.message;
|
|
64
|
+
exception.stack = reason.stack;
|
|
65
|
+
return exception;
|
|
418
66
|
}
|
|
67
|
+
exception.message = JSON.stringify(reason);
|
|
68
|
+
return exception;
|
|
419
69
|
};
|
|
420
70
|
|
|
421
|
-
const
|
|
422
|
-
if (
|
|
423
|
-
|
|
71
|
+
const getStackInfo = (reason) => {
|
|
72
|
+
if (reason instanceof Error) {
|
|
73
|
+
const { prepareStackTrace } = Error;
|
|
74
|
+
let stackTrace;
|
|
75
|
+
let stackObject;
|
|
76
|
+
Error.prepareStackTrace = (e, secondArg) => {
|
|
77
|
+
Error.prepareStackTrace = prepareStackTrace;
|
|
78
|
+
stackObject = secondArg;
|
|
79
|
+
stackTrace = secondArg.map((callSite) => ` at ${callSite}`).join("\n");
|
|
80
|
+
const name = e.name || "Error";
|
|
81
|
+
const message = e.message || "";
|
|
82
|
+
let stack = `${name}: ${message}`;
|
|
83
|
+
if (stackTrace) {
|
|
84
|
+
stack += `\n${stackTrace}`;
|
|
85
|
+
}
|
|
86
|
+
return stack;
|
|
87
|
+
};
|
|
88
|
+
// eslint-disable-next-line no-unused-expressions
|
|
89
|
+
reason.stack;
|
|
90
|
+
if (stackTrace === undefined) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
stackTrace,
|
|
95
|
+
stackObject,
|
|
96
|
+
};
|
|
424
97
|
}
|
|
425
|
-
|
|
426
|
-
return `promise are not supported.
|
|
427
|
-
promise found at: ${path.join("")}`
|
|
98
|
+
return null;
|
|
428
99
|
};
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
if (
|
|
432
|
-
|
|
100
|
+
const writePropertiesFromCallSite = (exception, callSite) => {
|
|
101
|
+
const source = callSite.getFileName() || callSite.getScriptNameOrSourceURL();
|
|
102
|
+
if (source) {
|
|
103
|
+
const line = callSite.getLineNumber();
|
|
104
|
+
const column = callSite.getColumnNumber() - 1;
|
|
105
|
+
exception.site = {
|
|
106
|
+
url: source,
|
|
107
|
+
line,
|
|
108
|
+
column,
|
|
109
|
+
};
|
|
110
|
+
return;
|
|
433
111
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (path.length === 0) {
|
|
441
|
-
return `weakMap are not supported.`
|
|
112
|
+
// Code called using eval() needs special handling
|
|
113
|
+
if (callSite.isEval()) {
|
|
114
|
+
const origin = callSite.getEvalOrigin();
|
|
115
|
+
if (origin) {
|
|
116
|
+
writePropertiesFromEvalOrigin(exception, origin);
|
|
117
|
+
}
|
|
442
118
|
}
|
|
443
|
-
|
|
444
|
-
return `weakMap are not supported.
|
|
445
|
-
weakMap found at: ${path.join("")}`
|
|
446
119
|
};
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
120
|
+
const writePropertiesFromEvalOrigin = (exception, origin) => {
|
|
121
|
+
// Most eval() calls are in this format
|
|
122
|
+
const topLevelEvalMatch = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(
|
|
123
|
+
origin,
|
|
124
|
+
);
|
|
125
|
+
if (topLevelEvalMatch) {
|
|
126
|
+
const source = topLevelEvalMatch[2];
|
|
127
|
+
const line = Number(topLevelEvalMatch[3]);
|
|
128
|
+
const column = topLevelEvalMatch[4] - 1;
|
|
129
|
+
exception.site = {
|
|
130
|
+
url: source,
|
|
131
|
+
line,
|
|
132
|
+
column,
|
|
133
|
+
};
|
|
134
|
+
return;
|
|
451
135
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const createForbiddenPropertyGetterMessage = ({
|
|
458
|
-
path,
|
|
459
|
-
propertyNameOrSymbol,
|
|
460
|
-
}) => `property getter are not allowed.
|
|
461
|
-
getter found on property: ${String(propertyNameOrSymbol)}
|
|
462
|
-
at: ${path.join("")}`;
|
|
463
|
-
|
|
464
|
-
const createForbiddenPropertySetterMessage = ({
|
|
465
|
-
path,
|
|
466
|
-
propertyNameOrSymbol,
|
|
467
|
-
}) => `property setter are not allowed.
|
|
468
|
-
setter found on property: ${String(propertyNameOrSymbol)}
|
|
469
|
-
at: ${path.join("")}`;
|
|
470
|
-
|
|
471
|
-
const createUnexpectedValueOfReturnValueMessage = () =>
|
|
472
|
-
`valueOf() must return a primitive of the object itself.`;
|
|
473
|
-
|
|
474
|
-
const createUnknownSymbolMessage = ({
|
|
475
|
-
symbol,
|
|
476
|
-
}) => `symbol must be global, like Symbol.iterator, or created using Symbol.for().
|
|
477
|
-
symbol: ${symbol.toString()}`;
|
|
478
|
-
|
|
479
|
-
const createUnknownPrototypeMessage = ({ prototypeValue }) =>
|
|
480
|
-
`prototype must be global, like Object.prototype, or somewhere in the value.
|
|
481
|
-
prototype constructor name: ${prototypeValue.constructor.name}`;
|
|
482
|
-
|
|
483
|
-
// be carefull because this function is mutating recipe objects inside the recipeArray.
|
|
484
|
-
// this is not an issue because each recipe object is not accessible from the outside
|
|
485
|
-
// when used internally by uneval
|
|
486
|
-
const sortRecipe = (recipeArray) => {
|
|
487
|
-
const findInRecipePrototypeChain = (recipe, callback) => {
|
|
488
|
-
let currentRecipe = recipe;
|
|
489
|
-
// eslint-disable-next-line no-constant-condition
|
|
490
|
-
while (true) {
|
|
491
|
-
if (currentRecipe.type !== "composite") {
|
|
492
|
-
break
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
const prototypeIdentifier = currentRecipe.prototypeIdentifier;
|
|
496
|
-
if (prototypeIdentifier === undefined) {
|
|
497
|
-
break
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
currentRecipe = recipeArray[prototypeIdentifier];
|
|
501
|
-
|
|
502
|
-
if (callback(currentRecipe, prototypeIdentifier)) {
|
|
503
|
-
return prototypeIdentifier
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
return undefined
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
const recipeArrayOrdered = recipeArray.slice();
|
|
510
|
-
recipeArrayOrdered.sort((leftRecipe, rightRecipe) => {
|
|
511
|
-
const leftType = leftRecipe.type;
|
|
512
|
-
const rightType = rightRecipe.type;
|
|
513
|
-
|
|
514
|
-
if (leftType === "composite" && rightType === "composite") {
|
|
515
|
-
const rightRecipeIsInLeftRecipePrototypeChain = findInRecipePrototypeChain(
|
|
516
|
-
leftRecipe,
|
|
517
|
-
(recipeCandidate) => recipeCandidate === rightRecipe,
|
|
518
|
-
);
|
|
519
|
-
// if left recipe requires right recipe, left must be after right
|
|
520
|
-
if (rightRecipeIsInLeftRecipePrototypeChain) {
|
|
521
|
-
return 1
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
const leftRecipeIsInRightRecipePrototypeChain = findInRecipePrototypeChain(
|
|
525
|
-
rightRecipe,
|
|
526
|
-
(recipeCandidate) => recipeCandidate === leftRecipe,
|
|
527
|
-
);
|
|
528
|
-
// if right recipe requires left recipe, right must be after left
|
|
529
|
-
if (leftRecipeIsInRightRecipePrototypeChain) {
|
|
530
|
-
return -1
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
if (leftType !== rightType) {
|
|
535
|
-
// if left is a composite, left must be after right
|
|
536
|
-
if (leftType === "composite") {
|
|
537
|
-
return 1
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// if right is a composite, right must be after left
|
|
541
|
-
if (rightType === "composite") {
|
|
542
|
-
return -1
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
const leftIndex = recipeArray.indexOf(leftRecipe);
|
|
547
|
-
const rightIndex = recipeArray.indexOf(rightRecipe);
|
|
548
|
-
// left was before right, don't change that
|
|
549
|
-
if (leftIndex < rightIndex) {
|
|
550
|
-
return -1
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// right was after left, don't change that
|
|
554
|
-
return 1
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
return recipeArrayOrdered
|
|
558
|
-
};
|
|
559
|
-
|
|
560
|
-
// https://github.com/joliss/js-string-escape/blob/master/index.js
|
|
561
|
-
// http://javascript.crockford.com/remedial.html
|
|
562
|
-
const escapeString = (value) => {
|
|
563
|
-
const string = String(value);
|
|
564
|
-
let i = 0;
|
|
565
|
-
const j = string.length;
|
|
566
|
-
var escapedString = "";
|
|
567
|
-
while (i < j) {
|
|
568
|
-
const char = string[i];
|
|
569
|
-
let escapedChar;
|
|
570
|
-
if (char === '"' || char === "'" || char === "\\") {
|
|
571
|
-
escapedChar = `\\${char}`;
|
|
572
|
-
} else if (char === "\n") {
|
|
573
|
-
escapedChar = "\\n";
|
|
574
|
-
} else if (char === "\r") {
|
|
575
|
-
escapedChar = "\\r";
|
|
576
|
-
} else if (char === "\u2028") {
|
|
577
|
-
escapedChar = "\\u2028";
|
|
578
|
-
} else if (char === "\u2029") {
|
|
579
|
-
escapedChar = "\\u2029";
|
|
580
|
-
} else {
|
|
581
|
-
escapedChar = char;
|
|
582
|
-
}
|
|
583
|
-
escapedString += escapedChar;
|
|
584
|
-
i++;
|
|
136
|
+
// Parse nested eval() calls using recursion
|
|
137
|
+
const nestedEvalMatch = /^eval at ([^(]+) \((.+)\)$/.exec(origin);
|
|
138
|
+
if (nestedEvalMatch) {
|
|
139
|
+
writePropertiesFromEvalOrigin(exception, nestedEvalMatch[2]);
|
|
585
140
|
}
|
|
586
|
-
return escapedString
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
const uneval = (
|
|
590
|
-
value,
|
|
591
|
-
{ functionAllowed = false, prototypeStrict = false, ignoreSymbols = false } = {},
|
|
592
|
-
) => {
|
|
593
|
-
const { recipeArray, mainIdentifier, valueMap } = decompose(value, {
|
|
594
|
-
functionAllowed,
|
|
595
|
-
prototypeStrict,
|
|
596
|
-
ignoreSymbols,
|
|
597
|
-
});
|
|
598
|
-
const recipeArraySorted = sortRecipe(recipeArray);
|
|
599
|
-
|
|
600
|
-
let source = `(function () {
|
|
601
|
-
var globalObject
|
|
602
|
-
try {
|
|
603
|
-
globalObject = Function('return this')() || (42, eval)('this');
|
|
604
|
-
} catch(e) {
|
|
605
|
-
globalObject = window;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function safeDefineProperty(object, propertyNameOrSymbol, descriptor) {
|
|
609
|
-
var currentDescriptor = Object.getOwnPropertyDescriptor(object, propertyNameOrSymbol);
|
|
610
|
-
if (currentDescriptor && !currentDescriptor.configurable) return
|
|
611
|
-
Object.defineProperty(object, propertyNameOrSymbol, descriptor)
|
|
612
|
-
};
|
|
613
|
-
`;
|
|
614
|
-
|
|
615
|
-
const variableNameMap = {};
|
|
616
|
-
recipeArray.forEach((recipe, index) => {
|
|
617
|
-
const indexSorted = recipeArraySorted.indexOf(recipe);
|
|
618
|
-
variableNameMap[index] = `_${indexSorted}`;
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
const identifierToVariableName = (identifier) => variableNameMap[identifier];
|
|
622
|
-
|
|
623
|
-
const recipeToSetupSource = (recipe) => {
|
|
624
|
-
if (recipe.type === "primitive") return primitiveRecipeToSetupSource(recipe)
|
|
625
|
-
if (recipe.type === "global-symbol") return globalSymbolRecipeToSetupSource(recipe)
|
|
626
|
-
if (recipe.type === "global-reference") return globalReferenceRecipeToSetupSource(recipe)
|
|
627
|
-
return compositeRecipeToSetupSource(recipe)
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
const primitiveRecipeToSetupSource = ({ value }) => {
|
|
631
|
-
const type = typeof value;
|
|
632
|
-
|
|
633
|
-
if (type === "string") {
|
|
634
|
-
return `"${escapeString(value)}";`
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (type === "bigint") {
|
|
638
|
-
return `${value.toString()}n`
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
if (Object.is(value, -0)) {
|
|
642
|
-
return "-0;"
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
return `${String(value)};`
|
|
646
|
-
};
|
|
647
|
-
|
|
648
|
-
const globalSymbolRecipeToSetupSource = (recipe) => {
|
|
649
|
-
return `Symbol.for("${escapeString(recipe.key)}");`
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
const globalReferenceRecipeToSetupSource = (recipe) => {
|
|
653
|
-
const pathSource = recipe.path.map((part) => `["${escapeString(part)}"]`).join("");
|
|
654
|
-
return `globalObject${pathSource};`
|
|
655
|
-
};
|
|
656
|
-
|
|
657
|
-
const compositeRecipeToSetupSource = ({ prototypeIdentifier, valueOfIdentifier }) => {
|
|
658
|
-
if (prototypeIdentifier === undefined) {
|
|
659
|
-
return identifierToVariableName(valueOfIdentifier)
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const prototypeValue = valueMap[prototypeIdentifier];
|
|
663
|
-
if (prototypeValue === null) {
|
|
664
|
-
return `Object.create(null);`
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
const prototypeConstructor = prototypeValue.constructor;
|
|
668
|
-
if (prototypeConstructor === Object) {
|
|
669
|
-
return `Object.create(${identifierToVariableName(prototypeIdentifier)});`
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
if (valueOfIdentifier === undefined) {
|
|
673
|
-
return `new ${prototypeConstructor.name}();`
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
if (prototypeConstructor.name === "BigInt") {
|
|
677
|
-
return `Object(${identifierToVariableName(valueOfIdentifier)})`
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
return `new ${prototypeConstructor.name}(${identifierToVariableName(valueOfIdentifier)});`
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
recipeArraySorted.forEach((recipe) => {
|
|
684
|
-
const recipeVariableName = identifierToVariableName(recipeArray.indexOf(recipe));
|
|
685
|
-
source += `var ${recipeVariableName} = ${recipeToSetupSource(recipe)}
|
|
686
|
-
`;
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
const recipeToMutateSource = (recipe, recipeVariableName) => {
|
|
690
|
-
if (recipe.type === "composite") {
|
|
691
|
-
return compositeRecipeToMutateSource(recipe, recipeVariableName)
|
|
692
|
-
}
|
|
693
|
-
return ``
|
|
694
|
-
};
|
|
695
|
-
|
|
696
|
-
const compositeRecipeToMutateSource = (
|
|
697
|
-
{ propertyDescriptionArray, symbolDescriptionArray, methodDescriptionArray, extensible },
|
|
698
|
-
recipeVariableName,
|
|
699
|
-
) => {
|
|
700
|
-
let mutateSource = ``;
|
|
701
|
-
|
|
702
|
-
propertyDescriptionArray.forEach(({ propertyNameIdentifier, propertyDescription }) => {
|
|
703
|
-
mutateSource += generateDefinePropertySource(
|
|
704
|
-
recipeVariableName,
|
|
705
|
-
propertyNameIdentifier,
|
|
706
|
-
propertyDescription,
|
|
707
|
-
);
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
symbolDescriptionArray.forEach(({ symbolIdentifier, propertyDescription }) => {
|
|
711
|
-
mutateSource += generateDefinePropertySource(
|
|
712
|
-
recipeVariableName,
|
|
713
|
-
symbolIdentifier,
|
|
714
|
-
propertyDescription,
|
|
715
|
-
);
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
methodDescriptionArray.forEach(({ methodNameIdentifier, callArray }) => {
|
|
719
|
-
mutateSource += generateMethodCallSource(recipeVariableName, methodNameIdentifier, callArray);
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
if (!extensible) {
|
|
723
|
-
mutateSource += generatePreventExtensionSource(recipeVariableName);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
return mutateSource
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
const generateDefinePropertySource = (
|
|
730
|
-
recipeVariableName,
|
|
731
|
-
propertyNameOrSymbolIdentifier,
|
|
732
|
-
propertyDescription,
|
|
733
|
-
) => {
|
|
734
|
-
const propertyOrSymbolVariableName = identifierToVariableName(propertyNameOrSymbolIdentifier);
|
|
735
|
-
|
|
736
|
-
const propertyDescriptorSource = generatePropertyDescriptorSource(propertyDescription);
|
|
737
|
-
return `safeDefineProperty(${recipeVariableName}, ${propertyOrSymbolVariableName}, ${propertyDescriptorSource});`
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
const generatePropertyDescriptorSource = ({
|
|
741
|
-
configurable,
|
|
742
|
-
writable,
|
|
743
|
-
enumerable,
|
|
744
|
-
getIdentifier,
|
|
745
|
-
setIdentifier,
|
|
746
|
-
valueIdentifier,
|
|
747
|
-
}) => {
|
|
748
|
-
if (valueIdentifier === undefined) {
|
|
749
|
-
return `{
|
|
750
|
-
configurable: ${configurable},
|
|
751
|
-
enumerable: ${enumerable},
|
|
752
|
-
get: ${getIdentifier === undefined ? undefined : identifierToVariableName(getIdentifier)},
|
|
753
|
-
set: ${setIdentifier === undefined ? undefined : identifierToVariableName(setIdentifier)},
|
|
754
|
-
}`
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
return `{
|
|
758
|
-
configurable: ${configurable},
|
|
759
|
-
writable: ${writable},
|
|
760
|
-
enumerable: ${enumerable},
|
|
761
|
-
value: ${valueIdentifier === undefined ? undefined : identifierToVariableName(valueIdentifier)}
|
|
762
|
-
}`
|
|
763
|
-
};
|
|
764
|
-
|
|
765
|
-
const generateMethodCallSource = (recipeVariableName, methodNameIdentifier, callArray) => {
|
|
766
|
-
let methodCallSource = ``;
|
|
767
|
-
|
|
768
|
-
const methodVariableName = identifierToVariableName(methodNameIdentifier);
|
|
769
|
-
callArray.forEach((argumentIdentifiers) => {
|
|
770
|
-
const argumentVariableNames = argumentIdentifiers.map((argumentIdentifier) =>
|
|
771
|
-
identifierToVariableName(argumentIdentifier),
|
|
772
|
-
);
|
|
773
|
-
|
|
774
|
-
methodCallSource += `${recipeVariableName}[${methodVariableName}](${argumentVariableNames.join(
|
|
775
|
-
",",
|
|
776
|
-
)});`;
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
return methodCallSource
|
|
780
|
-
};
|
|
781
|
-
|
|
782
|
-
const generatePreventExtensionSource = (recipeVariableName) => {
|
|
783
|
-
return `Object.preventExtensions(${recipeVariableName});`
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
recipeArraySorted.forEach((recipe) => {
|
|
787
|
-
const recipeVariableName = identifierToVariableName(recipeArray.indexOf(recipe));
|
|
788
|
-
source += `${recipeToMutateSource(recipe, recipeVariableName)}`;
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
source += `return ${identifierToVariableName(mainIdentifier)}; })()`;
|
|
792
|
-
|
|
793
|
-
return source
|
|
794
141
|
};
|
|
795
142
|
|
|
796
143
|
/*
|
|
@@ -964,4 +311,4 @@ const executeUsingDynamicImport = async ({
|
|
|
964
311
|
return result;
|
|
965
312
|
};
|
|
966
313
|
|
|
967
|
-
export {
|
|
314
|
+
export { createException, executeUsingDynamicImport };
|