@jsenv/test 1.2.4 → 1.3.1
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/controllable_child_process.mjs +73 -66
- package/dist/controllable_worker_thread.mjs +62 -49
- package/dist/importmap_node_loader.mjs +286 -185
- package/dist/js/execute_using_dynamic_import.js +398 -283
- package/dist/js/v8_coverage.js +91 -98
- package/dist/jsenv_test.js +2605 -1904
- package/dist/no_experimental_warnings.cjs +4 -1
- package/package.json +5 -5
- package/src/coverage/empty_coverage_factory.js +1 -1
- package/src/execution/execute_steps.js +1 -1
- package/src/runtime_node/profiler_v8_coverage.js +1 -1
|
@@ -4,48 +4,56 @@ import { PerformanceObserver, performance } from "node:perf_hooks";
|
|
|
4
4
|
|
|
5
5
|
// https://developer.mozilla.org/en-US/docs/Glossary/Primitive
|
|
6
6
|
|
|
7
|
-
const isComposite = value => {
|
|
7
|
+
const isComposite = (value) => {
|
|
8
8
|
if (value === null) {
|
|
9
|
-
return false
|
|
9
|
+
return false
|
|
10
10
|
}
|
|
11
|
+
|
|
11
12
|
const type = typeof value;
|
|
12
13
|
if (type === "object") {
|
|
13
|
-
return true
|
|
14
|
+
return true
|
|
14
15
|
}
|
|
16
|
+
|
|
15
17
|
if (type === "function") {
|
|
16
|
-
return true
|
|
18
|
+
return true
|
|
17
19
|
}
|
|
18
|
-
|
|
20
|
+
|
|
21
|
+
return false
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
|
|
22
25
|
const compositeWellKnownMap = new WeakMap();
|
|
23
26
|
const primitiveWellKnownMap = new Map();
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
+
|
|
28
|
+
const getCompositeGlobalPath = (value) => compositeWellKnownMap.get(value);
|
|
29
|
+
|
|
30
|
+
const getPrimitiveGlobalPath = (value) => primitiveWellKnownMap.get(value);
|
|
31
|
+
|
|
32
|
+
const visitGlobalObject = (value) => {
|
|
27
33
|
const visitValue = (value, path) => {
|
|
28
34
|
if (isComposite(value)) {
|
|
29
35
|
// prevent infinite recursion
|
|
30
36
|
if (compositeWellKnownMap.has(value)) {
|
|
31
|
-
return
|
|
37
|
+
return
|
|
32
38
|
}
|
|
33
39
|
compositeWellKnownMap.set(value, path);
|
|
34
|
-
|
|
40
|
+
|
|
41
|
+
const visitProperty = (property) => {
|
|
35
42
|
let descriptor;
|
|
36
43
|
try {
|
|
37
44
|
descriptor = Object.getOwnPropertyDescriptor(value, property);
|
|
38
45
|
} catch (e) {
|
|
39
46
|
if (e.name === "SecurityError") {
|
|
40
|
-
return
|
|
47
|
+
return
|
|
41
48
|
}
|
|
42
|
-
throw e
|
|
49
|
+
throw e
|
|
43
50
|
}
|
|
51
|
+
|
|
44
52
|
if (!descriptor) {
|
|
45
53
|
// it's apparently possible to have getOwnPropertyNames returning
|
|
46
54
|
// a property that later returns a null descriptor
|
|
47
55
|
// for instance window.showModalDialog in webkit 13.0
|
|
48
|
-
return
|
|
56
|
+
return
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
// do not trigger getter/setter
|
|
@@ -54,15 +62,20 @@ const visitGlobalObject = value => {
|
|
|
54
62
|
visitValue(propertyValue, [...path, property]);
|
|
55
63
|
}
|
|
56
64
|
};
|
|
57
|
-
|
|
58
|
-
Object.
|
|
65
|
+
|
|
66
|
+
Object.getOwnPropertyNames(value).forEach((name) => visitProperty(name));
|
|
67
|
+
Object.getOwnPropertySymbols(value).forEach((symbol) => visitProperty(symbol));
|
|
59
68
|
}
|
|
69
|
+
|
|
60
70
|
primitiveWellKnownMap.set(value, path);
|
|
61
|
-
return
|
|
71
|
+
return
|
|
62
72
|
};
|
|
73
|
+
|
|
63
74
|
visitValue(value, []);
|
|
64
75
|
};
|
|
76
|
+
|
|
65
77
|
if (typeof window === "object") visitGlobalObject(window);
|
|
78
|
+
|
|
66
79
|
if (typeof global === "object") visitGlobalObject(global);
|
|
67
80
|
|
|
68
81
|
/**
|
|
@@ -70,172 +83,196 @@ if (typeof global === "object") visitGlobalObject(global);
|
|
|
70
83
|
*
|
|
71
84
|
*/
|
|
72
85
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
prototypeStrict,
|
|
76
|
-
ignoreSymbols
|
|
77
|
-
}) => {
|
|
86
|
+
|
|
87
|
+
const decompose = (mainValue, { functionAllowed, prototypeStrict, ignoreSymbols }) => {
|
|
78
88
|
const valueMap = {};
|
|
79
89
|
const recipeArray = [];
|
|
90
|
+
|
|
80
91
|
const valueToIdentifier = (value, path = []) => {
|
|
81
92
|
if (!isComposite(value)) {
|
|
82
93
|
const existingIdentifier = identifierForPrimitive(value);
|
|
83
94
|
if (existingIdentifier !== undefined) {
|
|
84
|
-
return existingIdentifier
|
|
95
|
+
return existingIdentifier
|
|
85
96
|
}
|
|
86
97
|
const identifier = identifierForNewValue(value);
|
|
87
98
|
recipeArray[identifier] = primitiveToRecipe(value);
|
|
88
|
-
return identifier
|
|
99
|
+
return identifier
|
|
89
100
|
}
|
|
101
|
+
|
|
90
102
|
if (typeof Promise === "function" && value instanceof Promise) {
|
|
91
|
-
throw new Error(createPromiseAreNotSupportedMessage({
|
|
92
|
-
path
|
|
93
|
-
}));
|
|
103
|
+
throw new Error(createPromiseAreNotSupportedMessage({ path }))
|
|
94
104
|
}
|
|
95
105
|
if (typeof WeakSet === "function" && value instanceof WeakSet) {
|
|
96
|
-
throw new Error(createWeakSetAreNotSupportedMessage({
|
|
97
|
-
path
|
|
98
|
-
}));
|
|
106
|
+
throw new Error(createWeakSetAreNotSupportedMessage({ path }))
|
|
99
107
|
}
|
|
100
108
|
if (typeof WeakMap === "function" && value instanceof WeakMap) {
|
|
101
|
-
throw new Error(createWeakMapAreNotSupportedMessage({
|
|
102
|
-
path
|
|
103
|
-
}));
|
|
109
|
+
throw new Error(createWeakMapAreNotSupportedMessage({ path }))
|
|
104
110
|
}
|
|
105
111
|
if (typeof value === "function" && !functionAllowed) {
|
|
106
|
-
throw new Error(createForbiddenFunctionMessage({
|
|
107
|
-
path
|
|
108
|
-
}));
|
|
112
|
+
throw new Error(createForbiddenFunctionMessage({ path }))
|
|
109
113
|
}
|
|
114
|
+
|
|
110
115
|
const existingIdentifier = identifierForComposite(value);
|
|
111
116
|
if (existingIdentifier !== undefined) {
|
|
112
|
-
return existingIdentifier
|
|
117
|
+
return existingIdentifier
|
|
113
118
|
}
|
|
114
119
|
const identifier = identifierForNewValue(value);
|
|
120
|
+
|
|
115
121
|
const compositeGlobalPath = getCompositeGlobalPath(value);
|
|
116
122
|
if (compositeGlobalPath) {
|
|
117
123
|
recipeArray[identifier] = createGlobalReferenceRecipe(compositeGlobalPath);
|
|
118
|
-
return identifier
|
|
124
|
+
return identifier
|
|
119
125
|
}
|
|
126
|
+
|
|
120
127
|
const propertyDescriptionArray = [];
|
|
121
|
-
Object.getOwnPropertyNames(value).forEach(propertyName => {
|
|
128
|
+
Object.getOwnPropertyNames(value).forEach((propertyName) => {
|
|
122
129
|
const propertyDescriptor = Object.getOwnPropertyDescriptor(value, propertyName);
|
|
123
130
|
const propertyNameIdentifier = valueToIdentifier(propertyName, [...path, propertyName]);
|
|
124
131
|
const propertyDescription = computePropertyDescription(propertyDescriptor, propertyName, path);
|
|
125
|
-
propertyDescriptionArray.push({
|
|
126
|
-
propertyNameIdentifier,
|
|
127
|
-
propertyDescription
|
|
128
|
-
});
|
|
132
|
+
propertyDescriptionArray.push({ propertyNameIdentifier, propertyDescription });
|
|
129
133
|
});
|
|
134
|
+
|
|
130
135
|
const symbolDescriptionArray = [];
|
|
131
136
|
if (!ignoreSymbols) {
|
|
132
|
-
Object.getOwnPropertySymbols(value).forEach(symbol => {
|
|
137
|
+
Object.getOwnPropertySymbols(value).forEach((symbol) => {
|
|
133
138
|
const propertyDescriptor = Object.getOwnPropertyDescriptor(value, symbol);
|
|
134
139
|
const symbolIdentifier = valueToIdentifier(symbol, [...path, `[${symbol.toString()}]`]);
|
|
135
140
|
const propertyDescription = computePropertyDescription(propertyDescriptor, symbol, path);
|
|
136
|
-
symbolDescriptionArray.push({
|
|
137
|
-
symbolIdentifier,
|
|
138
|
-
propertyDescription
|
|
139
|
-
});
|
|
141
|
+
symbolDescriptionArray.push({ symbolIdentifier, propertyDescription });
|
|
140
142
|
});
|
|
141
143
|
}
|
|
144
|
+
|
|
142
145
|
const methodDescriptionArray = computeMethodDescriptionArray(value, path);
|
|
146
|
+
|
|
143
147
|
const extensible = Object.isExtensible(value);
|
|
148
|
+
|
|
144
149
|
recipeArray[identifier] = createCompositeRecipe({
|
|
145
150
|
propertyDescriptionArray,
|
|
146
151
|
symbolDescriptionArray,
|
|
147
152
|
methodDescriptionArray,
|
|
148
|
-
extensible
|
|
153
|
+
extensible,
|
|
149
154
|
});
|
|
150
|
-
return identifier
|
|
155
|
+
return identifier
|
|
151
156
|
};
|
|
157
|
+
|
|
152
158
|
const computePropertyDescription = (propertyDescriptor, propertyNameOrSymbol, path) => {
|
|
153
159
|
if (propertyDescriptor.set && !functionAllowed) {
|
|
154
|
-
throw new Error(createForbiddenPropertySetterMessage({
|
|
155
|
-
path,
|
|
156
|
-
propertyNameOrSymbol
|
|
157
|
-
}));
|
|
160
|
+
throw new Error(createForbiddenPropertySetterMessage({ path, propertyNameOrSymbol }))
|
|
158
161
|
}
|
|
159
162
|
if (propertyDescriptor.get && !functionAllowed) {
|
|
160
|
-
throw new Error(createForbiddenPropertyGetterMessage({
|
|
161
|
-
path,
|
|
162
|
-
propertyNameOrSymbol
|
|
163
|
-
}));
|
|
163
|
+
throw new Error(createForbiddenPropertyGetterMessage({ path, propertyNameOrSymbol }))
|
|
164
164
|
}
|
|
165
|
+
|
|
165
166
|
return {
|
|
166
167
|
configurable: propertyDescriptor.configurable,
|
|
167
168
|
writable: propertyDescriptor.writable,
|
|
168
169
|
enumerable: propertyDescriptor.enumerable,
|
|
169
|
-
getIdentifier:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
+
}
|
|
173
195
|
};
|
|
196
|
+
|
|
174
197
|
const computeMethodDescriptionArray = (value, path) => {
|
|
175
198
|
const methodDescriptionArray = [];
|
|
199
|
+
|
|
176
200
|
if (typeof Set === "function" && value instanceof Set) {
|
|
177
201
|
const callArray = [];
|
|
178
202
|
value.forEach((entryValue, index) => {
|
|
179
|
-
const entryValueIdentifier = valueToIdentifier(entryValue, [
|
|
203
|
+
const entryValueIdentifier = valueToIdentifier(entryValue, [
|
|
204
|
+
...path,
|
|
205
|
+
`[[SetEntryValue]]`,
|
|
206
|
+
index,
|
|
207
|
+
]);
|
|
180
208
|
callArray.push([entryValueIdentifier]);
|
|
181
209
|
});
|
|
182
|
-
methodDescriptionArray.push({
|
|
183
|
-
methodNameIdentifier: valueToIdentifier("add"),
|
|
184
|
-
callArray
|
|
185
|
-
});
|
|
210
|
+
methodDescriptionArray.push({ methodNameIdentifier: valueToIdentifier("add"), callArray });
|
|
186
211
|
}
|
|
212
|
+
|
|
187
213
|
if (typeof Map === "function" && value instanceof Map) {
|
|
188
214
|
const callArray = [];
|
|
189
215
|
value.forEach((entryValue, entryKey) => {
|
|
190
|
-
const entryKeyIdentifier = valueToIdentifier(entryKey, [
|
|
191
|
-
|
|
216
|
+
const entryKeyIdentifier = valueToIdentifier(entryKey, [
|
|
217
|
+
...path,
|
|
218
|
+
"[[MapEntryKey]]",
|
|
219
|
+
entryKey,
|
|
220
|
+
]);
|
|
221
|
+
const entryValueIdentifier = valueToIdentifier(entryValue, [
|
|
222
|
+
...path,
|
|
223
|
+
"[[MapEntryValue]]",
|
|
224
|
+
entryValue,
|
|
225
|
+
]);
|
|
192
226
|
callArray.push([entryKeyIdentifier, entryValueIdentifier]);
|
|
193
227
|
});
|
|
194
|
-
methodDescriptionArray.push({
|
|
195
|
-
methodNameIdentifier: valueToIdentifier("set"),
|
|
196
|
-
callArray
|
|
197
|
-
});
|
|
228
|
+
methodDescriptionArray.push({ methodNameIdentifier: valueToIdentifier("set"), callArray });
|
|
198
229
|
}
|
|
199
|
-
|
|
230
|
+
|
|
231
|
+
return methodDescriptionArray
|
|
200
232
|
};
|
|
201
|
-
|
|
202
|
-
|
|
233
|
+
|
|
234
|
+
const identifierForPrimitive = (value) => {
|
|
235
|
+
return Object.keys(valueMap).find((existingIdentifier) => {
|
|
203
236
|
const existingValue = valueMap[existingIdentifier];
|
|
204
|
-
if (Object.is(value, existingValue)) return true
|
|
205
|
-
return value === existingValue
|
|
206
|
-
})
|
|
237
|
+
if (Object.is(value, existingValue)) return true
|
|
238
|
+
return value === existingValue
|
|
239
|
+
})
|
|
207
240
|
};
|
|
208
|
-
|
|
209
|
-
|
|
241
|
+
|
|
242
|
+
const identifierForComposite = (value) => {
|
|
243
|
+
return Object.keys(valueMap).find((existingIdentifier) => {
|
|
210
244
|
const existingValue = valueMap[existingIdentifier];
|
|
211
|
-
return value === existingValue
|
|
212
|
-
})
|
|
245
|
+
return value === existingValue
|
|
246
|
+
})
|
|
213
247
|
};
|
|
214
|
-
|
|
248
|
+
|
|
249
|
+
const identifierForNewValue = (value) => {
|
|
215
250
|
const identifier = nextIdentifier();
|
|
216
251
|
valueMap[identifier] = value;
|
|
217
|
-
return identifier
|
|
252
|
+
return identifier
|
|
218
253
|
};
|
|
254
|
+
|
|
219
255
|
let currentIdentifier = -1;
|
|
220
256
|
const nextIdentifier = () => {
|
|
221
257
|
const identifier = String(parseInt(currentIdentifier) + 1);
|
|
222
258
|
currentIdentifier = identifier;
|
|
223
|
-
return identifier
|
|
259
|
+
return identifier
|
|
224
260
|
};
|
|
261
|
+
|
|
225
262
|
const mainIdentifier = valueToIdentifier(mainValue);
|
|
226
263
|
|
|
227
264
|
// prototype, important to keep after the whole structure was visited
|
|
228
265
|
// so that we discover if any prototype is part of the value
|
|
229
|
-
const prototypeValueToIdentifier = prototypeValue => {
|
|
266
|
+
const prototypeValueToIdentifier = (prototypeValue) => {
|
|
230
267
|
// prototype is null
|
|
231
268
|
if (prototypeValue === null) {
|
|
232
|
-
return valueToIdentifier(prototypeValue)
|
|
269
|
+
return valueToIdentifier(prototypeValue)
|
|
233
270
|
}
|
|
234
271
|
|
|
235
272
|
// prototype found somewhere already
|
|
236
273
|
const prototypeExistingIdentifier = identifierForComposite(prototypeValue);
|
|
237
274
|
if (prototypeExistingIdentifier !== undefined) {
|
|
238
|
-
return prototypeExistingIdentifier
|
|
275
|
+
return prototypeExistingIdentifier
|
|
239
276
|
}
|
|
240
277
|
|
|
241
278
|
// mark prototype as visited
|
|
@@ -245,56 +282,63 @@ const decompose = (mainValue, {
|
|
|
245
282
|
const prototypeGlobalPath = getCompositeGlobalPath(prototypeValue);
|
|
246
283
|
if (prototypeGlobalPath) {
|
|
247
284
|
recipeArray[prototypeIdentifier] = createGlobalReferenceRecipe(prototypeGlobalPath);
|
|
248
|
-
return prototypeIdentifier
|
|
285
|
+
return prototypeIdentifier
|
|
249
286
|
}
|
|
250
287
|
|
|
251
288
|
// otherwise prototype is unknown
|
|
252
289
|
if (prototypeStrict) {
|
|
253
|
-
throw new Error(createUnknownPrototypeMessage({
|
|
254
|
-
prototypeValue
|
|
255
|
-
}));
|
|
290
|
+
throw new Error(createUnknownPrototypeMessage({ prototypeValue }))
|
|
256
291
|
}
|
|
257
|
-
|
|
292
|
+
|
|
293
|
+
return prototypeValueToIdentifier(Object.getPrototypeOf(prototypeValue))
|
|
258
294
|
};
|
|
259
295
|
const identifierForValueOf = (value, path = []) => {
|
|
260
296
|
if (value instanceof Array) {
|
|
261
|
-
return valueToIdentifier(value.length, [...path, "length"])
|
|
297
|
+
return valueToIdentifier(value.length, [...path, "length"])
|
|
262
298
|
}
|
|
299
|
+
|
|
263
300
|
if ("valueOf" in value === false) {
|
|
264
|
-
return undefined
|
|
301
|
+
return undefined
|
|
265
302
|
}
|
|
303
|
+
|
|
266
304
|
if (typeof value.valueOf !== "function") {
|
|
267
|
-
return undefined
|
|
305
|
+
return undefined
|
|
268
306
|
}
|
|
307
|
+
|
|
269
308
|
const valueOfReturnValue = value.valueOf();
|
|
270
309
|
if (!isComposite(valueOfReturnValue)) {
|
|
271
|
-
return valueToIdentifier(valueOfReturnValue, [...path, "valueOf()"])
|
|
310
|
+
return valueToIdentifier(valueOfReturnValue, [...path, "valueOf()"])
|
|
272
311
|
}
|
|
312
|
+
|
|
273
313
|
if (valueOfReturnValue === value) {
|
|
274
|
-
return undefined
|
|
314
|
+
return undefined
|
|
275
315
|
}
|
|
276
|
-
|
|
316
|
+
|
|
317
|
+
throw new Error(createUnexpectedValueOfReturnValueMessage())
|
|
277
318
|
};
|
|
319
|
+
|
|
278
320
|
recipeArray.slice().forEach((recipe, index) => {
|
|
279
321
|
if (recipe.type === "composite") {
|
|
280
322
|
const value = valueMap[index];
|
|
323
|
+
|
|
281
324
|
if (typeof value === "function") {
|
|
282
325
|
const valueOfIdentifier = nextIdentifier();
|
|
283
326
|
recipeArray[valueOfIdentifier] = {
|
|
284
327
|
type: "primitive",
|
|
285
|
-
value
|
|
328
|
+
value,
|
|
286
329
|
};
|
|
287
330
|
recipe.valueOfIdentifier = valueOfIdentifier;
|
|
288
|
-
return
|
|
331
|
+
return
|
|
289
332
|
}
|
|
333
|
+
|
|
290
334
|
if (value instanceof RegExp) {
|
|
291
335
|
const valueOfIdentifier = nextIdentifier();
|
|
292
336
|
recipeArray[valueOfIdentifier] = {
|
|
293
337
|
type: "primitive",
|
|
294
|
-
value
|
|
338
|
+
value,
|
|
295
339
|
};
|
|
296
340
|
recipe.valueOfIdentifier = valueOfIdentifier;
|
|
297
|
-
return
|
|
341
|
+
return
|
|
298
342
|
}
|
|
299
343
|
|
|
300
344
|
// valueOf, mandatory to uneval new Date(10) for instance.
|
|
@@ -303,57 +347,65 @@ const decompose = (mainValue, {
|
|
|
303
347
|
recipe.prototypeIdentifier = prototypeValueToIdentifier(prototypeValue);
|
|
304
348
|
}
|
|
305
349
|
});
|
|
350
|
+
|
|
306
351
|
return {
|
|
307
352
|
recipeArray,
|
|
308
353
|
mainIdentifier,
|
|
309
|
-
valueMap
|
|
310
|
-
}
|
|
354
|
+
valueMap,
|
|
355
|
+
}
|
|
311
356
|
};
|
|
312
|
-
|
|
357
|
+
|
|
358
|
+
const primitiveToRecipe = (value) => {
|
|
313
359
|
if (typeof value === "symbol") {
|
|
314
|
-
return symbolToRecipe(value)
|
|
360
|
+
return symbolToRecipe(value)
|
|
315
361
|
}
|
|
316
|
-
|
|
362
|
+
|
|
363
|
+
return createPimitiveRecipe(value)
|
|
317
364
|
};
|
|
318
|
-
|
|
365
|
+
|
|
366
|
+
const symbolToRecipe = (symbol) => {
|
|
319
367
|
const globalSymbolKey = Symbol.keyFor(symbol);
|
|
320
368
|
if (globalSymbolKey !== undefined) {
|
|
321
|
-
return createGlobalSymbolRecipe(globalSymbolKey)
|
|
369
|
+
return createGlobalSymbolRecipe(globalSymbolKey)
|
|
322
370
|
}
|
|
371
|
+
|
|
323
372
|
const symbolGlobalPath = getPrimitiveGlobalPath(symbol);
|
|
324
373
|
if (!symbolGlobalPath) {
|
|
325
|
-
throw new Error(createUnknownSymbolMessage({
|
|
326
|
-
symbol
|
|
327
|
-
}));
|
|
374
|
+
throw new Error(createUnknownSymbolMessage({ symbol }))
|
|
328
375
|
}
|
|
329
|
-
|
|
376
|
+
|
|
377
|
+
return createGlobalReferenceRecipe(symbolGlobalPath)
|
|
330
378
|
};
|
|
331
|
-
|
|
379
|
+
|
|
380
|
+
const createPimitiveRecipe = (value) => {
|
|
332
381
|
return {
|
|
333
382
|
type: "primitive",
|
|
334
|
-
value
|
|
335
|
-
}
|
|
383
|
+
value,
|
|
384
|
+
}
|
|
336
385
|
};
|
|
337
|
-
|
|
386
|
+
|
|
387
|
+
const createGlobalReferenceRecipe = (path) => {
|
|
338
388
|
const recipe = {
|
|
339
389
|
type: "global-reference",
|
|
340
|
-
path
|
|
390
|
+
path,
|
|
341
391
|
};
|
|
342
|
-
return recipe
|
|
392
|
+
return recipe
|
|
343
393
|
};
|
|
344
|
-
|
|
394
|
+
|
|
395
|
+
const createGlobalSymbolRecipe = (key) => {
|
|
345
396
|
return {
|
|
346
397
|
type: "global-symbol",
|
|
347
|
-
key
|
|
348
|
-
}
|
|
398
|
+
key,
|
|
399
|
+
}
|
|
349
400
|
};
|
|
401
|
+
|
|
350
402
|
const createCompositeRecipe = ({
|
|
351
403
|
prototypeIdentifier,
|
|
352
404
|
valueOfIdentifier,
|
|
353
405
|
propertyDescriptionArray,
|
|
354
406
|
symbolDescriptionArray,
|
|
355
407
|
methodDescriptionArray,
|
|
356
|
-
extensible
|
|
408
|
+
extensible,
|
|
357
409
|
}) => {
|
|
358
410
|
return {
|
|
359
411
|
type: "composite",
|
|
@@ -362,132 +414,152 @@ const createCompositeRecipe = ({
|
|
|
362
414
|
propertyDescriptionArray,
|
|
363
415
|
symbolDescriptionArray,
|
|
364
416
|
methodDescriptionArray,
|
|
365
|
-
extensible
|
|
366
|
-
}
|
|
417
|
+
extensible,
|
|
418
|
+
}
|
|
367
419
|
};
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}) => {
|
|
420
|
+
|
|
421
|
+
const createPromiseAreNotSupportedMessage = ({ path }) => {
|
|
371
422
|
if (path.length === 0) {
|
|
372
|
-
return `promise are not supported
|
|
423
|
+
return `promise are not supported.`
|
|
373
424
|
}
|
|
425
|
+
|
|
374
426
|
return `promise are not supported.
|
|
375
|
-
promise found at: ${path.join("")}
|
|
427
|
+
promise found at: ${path.join("")}`
|
|
376
428
|
};
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}) => {
|
|
429
|
+
|
|
430
|
+
const createWeakSetAreNotSupportedMessage = ({ path }) => {
|
|
380
431
|
if (path.length === 0) {
|
|
381
|
-
return `weakSet are not supported
|
|
432
|
+
return `weakSet are not supported.`
|
|
382
433
|
}
|
|
434
|
+
|
|
383
435
|
return `weakSet are not supported.
|
|
384
|
-
weakSet found at: ${path.join("")}
|
|
436
|
+
weakSet found at: ${path.join("")}`
|
|
385
437
|
};
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}) => {
|
|
438
|
+
|
|
439
|
+
const createWeakMapAreNotSupportedMessage = ({ path }) => {
|
|
389
440
|
if (path.length === 0) {
|
|
390
|
-
return `weakMap are not supported
|
|
441
|
+
return `weakMap are not supported.`
|
|
391
442
|
}
|
|
443
|
+
|
|
392
444
|
return `weakMap are not supported.
|
|
393
|
-
weakMap found at: ${path.join("")}
|
|
445
|
+
weakMap found at: ${path.join("")}`
|
|
394
446
|
};
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}) => {
|
|
447
|
+
|
|
448
|
+
const createForbiddenFunctionMessage = ({ path }) => {
|
|
398
449
|
if (path.length === 0) {
|
|
399
|
-
return `function are not allowed
|
|
450
|
+
return `function are not allowed.`
|
|
400
451
|
}
|
|
452
|
+
|
|
401
453
|
return `function are not allowed.
|
|
402
|
-
function found at: ${path.join("")}
|
|
454
|
+
function found at: ${path.join("")}`
|
|
403
455
|
};
|
|
456
|
+
|
|
404
457
|
const createForbiddenPropertyGetterMessage = ({
|
|
405
458
|
path,
|
|
406
|
-
propertyNameOrSymbol
|
|
459
|
+
propertyNameOrSymbol,
|
|
407
460
|
}) => `property getter are not allowed.
|
|
408
461
|
getter found on property: ${String(propertyNameOrSymbol)}
|
|
409
462
|
at: ${path.join("")}`;
|
|
463
|
+
|
|
410
464
|
const createForbiddenPropertySetterMessage = ({
|
|
411
465
|
path,
|
|
412
|
-
propertyNameOrSymbol
|
|
466
|
+
propertyNameOrSymbol,
|
|
413
467
|
}) => `property setter are not allowed.
|
|
414
468
|
setter found on property: ${String(propertyNameOrSymbol)}
|
|
415
469
|
at: ${path.join("")}`;
|
|
416
|
-
|
|
470
|
+
|
|
471
|
+
const createUnexpectedValueOfReturnValueMessage = () =>
|
|
472
|
+
`valueOf() must return a primitive of the object itself.`;
|
|
473
|
+
|
|
417
474
|
const createUnknownSymbolMessage = ({
|
|
418
|
-
symbol
|
|
475
|
+
symbol,
|
|
419
476
|
}) => `symbol must be global, like Symbol.iterator, or created using Symbol.for().
|
|
420
477
|
symbol: ${symbol.toString()}`;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
478
|
+
|
|
479
|
+
const createUnknownPrototypeMessage = ({ prototypeValue }) =>
|
|
480
|
+
`prototype must be global, like Object.prototype, or somewhere in the value.
|
|
424
481
|
prototype constructor name: ${prototypeValue.constructor.name}`;
|
|
425
482
|
|
|
426
483
|
// be carefull because this function is mutating recipe objects inside the recipeArray.
|
|
427
484
|
// this is not an issue because each recipe object is not accessible from the outside
|
|
428
485
|
// when used internally by uneval
|
|
429
|
-
const sortRecipe = recipeArray => {
|
|
486
|
+
const sortRecipe = (recipeArray) => {
|
|
430
487
|
const findInRecipePrototypeChain = (recipe, callback) => {
|
|
431
488
|
let currentRecipe = recipe;
|
|
432
489
|
// eslint-disable-next-line no-constant-condition
|
|
433
490
|
while (true) {
|
|
434
491
|
if (currentRecipe.type !== "composite") {
|
|
435
|
-
break
|
|
492
|
+
break
|
|
436
493
|
}
|
|
494
|
+
|
|
437
495
|
const prototypeIdentifier = currentRecipe.prototypeIdentifier;
|
|
438
496
|
if (prototypeIdentifier === undefined) {
|
|
439
|
-
break
|
|
497
|
+
break
|
|
440
498
|
}
|
|
499
|
+
|
|
441
500
|
currentRecipe = recipeArray[prototypeIdentifier];
|
|
501
|
+
|
|
442
502
|
if (callback(currentRecipe, prototypeIdentifier)) {
|
|
443
|
-
return prototypeIdentifier
|
|
503
|
+
return prototypeIdentifier
|
|
444
504
|
}
|
|
445
505
|
}
|
|
446
|
-
return undefined
|
|
506
|
+
return undefined
|
|
447
507
|
};
|
|
508
|
+
|
|
448
509
|
const recipeArrayOrdered = recipeArray.slice();
|
|
449
510
|
recipeArrayOrdered.sort((leftRecipe, rightRecipe) => {
|
|
450
511
|
const leftType = leftRecipe.type;
|
|
451
512
|
const rightType = rightRecipe.type;
|
|
513
|
+
|
|
452
514
|
if (leftType === "composite" && rightType === "composite") {
|
|
453
|
-
const rightRecipeIsInLeftRecipePrototypeChain = findInRecipePrototypeChain(
|
|
515
|
+
const rightRecipeIsInLeftRecipePrototypeChain = findInRecipePrototypeChain(
|
|
516
|
+
leftRecipe,
|
|
517
|
+
(recipeCandidate) => recipeCandidate === rightRecipe,
|
|
518
|
+
);
|
|
454
519
|
// if left recipe requires right recipe, left must be after right
|
|
455
520
|
if (rightRecipeIsInLeftRecipePrototypeChain) {
|
|
456
|
-
return 1
|
|
521
|
+
return 1
|
|
457
522
|
}
|
|
458
|
-
|
|
523
|
+
|
|
524
|
+
const leftRecipeIsInRightRecipePrototypeChain = findInRecipePrototypeChain(
|
|
525
|
+
rightRecipe,
|
|
526
|
+
(recipeCandidate) => recipeCandidate === leftRecipe,
|
|
527
|
+
);
|
|
459
528
|
// if right recipe requires left recipe, right must be after left
|
|
460
529
|
if (leftRecipeIsInRightRecipePrototypeChain) {
|
|
461
|
-
return -1
|
|
530
|
+
return -1
|
|
462
531
|
}
|
|
463
532
|
}
|
|
533
|
+
|
|
464
534
|
if (leftType !== rightType) {
|
|
465
535
|
// if left is a composite, left must be after right
|
|
466
536
|
if (leftType === "composite") {
|
|
467
|
-
return 1
|
|
537
|
+
return 1
|
|
468
538
|
}
|
|
469
539
|
|
|
470
540
|
// if right is a composite, right must be after left
|
|
471
541
|
if (rightType === "composite") {
|
|
472
|
-
return -1
|
|
542
|
+
return -1
|
|
473
543
|
}
|
|
474
544
|
}
|
|
545
|
+
|
|
475
546
|
const leftIndex = recipeArray.indexOf(leftRecipe);
|
|
476
547
|
const rightIndex = recipeArray.indexOf(rightRecipe);
|
|
477
548
|
// left was before right, don't change that
|
|
478
549
|
if (leftIndex < rightIndex) {
|
|
479
|
-
return -1
|
|
550
|
+
return -1
|
|
480
551
|
}
|
|
481
552
|
|
|
482
553
|
// right was after left, don't change that
|
|
483
|
-
return 1
|
|
554
|
+
return 1
|
|
484
555
|
});
|
|
485
|
-
|
|
556
|
+
|
|
557
|
+
return recipeArrayOrdered
|
|
486
558
|
};
|
|
487
559
|
|
|
488
560
|
// https://github.com/joliss/js-string-escape/blob/master/index.js
|
|
489
561
|
// http://javascript.crockford.com/remedial.html
|
|
490
|
-
const escapeString = value => {
|
|
562
|
+
const escapeString = (value) => {
|
|
491
563
|
const string = String(value);
|
|
492
564
|
let i = 0;
|
|
493
565
|
const j = string.length;
|
|
@@ -511,24 +583,20 @@ const escapeString = value => {
|
|
|
511
583
|
escapedString += escapedChar;
|
|
512
584
|
i++;
|
|
513
585
|
}
|
|
514
|
-
return escapedString
|
|
586
|
+
return escapedString
|
|
515
587
|
};
|
|
516
588
|
|
|
517
|
-
const uneval = (
|
|
518
|
-
|
|
519
|
-
prototypeStrict = false,
|
|
520
|
-
|
|
521
|
-
} =
|
|
522
|
-
const {
|
|
523
|
-
recipeArray,
|
|
524
|
-
mainIdentifier,
|
|
525
|
-
valueMap
|
|
526
|
-
} = decompose(value, {
|
|
589
|
+
const uneval = (
|
|
590
|
+
value,
|
|
591
|
+
{ functionAllowed = false, prototypeStrict = false, ignoreSymbols = false } = {},
|
|
592
|
+
) => {
|
|
593
|
+
const { recipeArray, mainIdentifier, valueMap } = decompose(value, {
|
|
527
594
|
functionAllowed,
|
|
528
595
|
prototypeStrict,
|
|
529
|
-
ignoreSymbols
|
|
596
|
+
ignoreSymbols,
|
|
530
597
|
});
|
|
531
598
|
const recipeArraySorted = sortRecipe(recipeArray);
|
|
599
|
+
|
|
532
600
|
let source = `(function () {
|
|
533
601
|
var globalObject
|
|
534
602
|
try {
|
|
@@ -543,116 +611,139 @@ function safeDefineProperty(object, propertyNameOrSymbol, descriptor) {
|
|
|
543
611
|
Object.defineProperty(object, propertyNameOrSymbol, descriptor)
|
|
544
612
|
};
|
|
545
613
|
`;
|
|
614
|
+
|
|
546
615
|
const variableNameMap = {};
|
|
547
616
|
recipeArray.forEach((recipe, index) => {
|
|
548
617
|
const indexSorted = recipeArraySorted.indexOf(recipe);
|
|
549
618
|
variableNameMap[index] = `_${indexSorted}`;
|
|
550
619
|
});
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (recipe.type === "
|
|
556
|
-
return
|
|
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)
|
|
557
628
|
};
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}) => {
|
|
629
|
+
|
|
630
|
+
const primitiveRecipeToSetupSource = ({ value }) => {
|
|
561
631
|
const type = typeof value;
|
|
632
|
+
|
|
562
633
|
if (type === "string") {
|
|
563
|
-
return `"${escapeString(value)}"
|
|
634
|
+
return `"${escapeString(value)}";`
|
|
564
635
|
}
|
|
636
|
+
|
|
565
637
|
if (type === "bigint") {
|
|
566
|
-
return `${value.toString()}n
|
|
638
|
+
return `${value.toString()}n`
|
|
567
639
|
}
|
|
640
|
+
|
|
568
641
|
if (Object.is(value, -0)) {
|
|
569
|
-
return "-0;"
|
|
642
|
+
return "-0;"
|
|
570
643
|
}
|
|
571
|
-
|
|
644
|
+
|
|
645
|
+
return `${String(value)};`
|
|
572
646
|
};
|
|
573
|
-
|
|
574
|
-
|
|
647
|
+
|
|
648
|
+
const globalSymbolRecipeToSetupSource = (recipe) => {
|
|
649
|
+
return `Symbol.for("${escapeString(recipe.key)}");`
|
|
575
650
|
};
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
651
|
+
|
|
652
|
+
const globalReferenceRecipeToSetupSource = (recipe) => {
|
|
653
|
+
const pathSource = recipe.path.map((part) => `["${escapeString(part)}"]`).join("");
|
|
654
|
+
return `globalObject${pathSource};`
|
|
579
655
|
};
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
valueOfIdentifier
|
|
583
|
-
}) => {
|
|
656
|
+
|
|
657
|
+
const compositeRecipeToSetupSource = ({ prototypeIdentifier, valueOfIdentifier }) => {
|
|
584
658
|
if (prototypeIdentifier === undefined) {
|
|
585
|
-
return identifierToVariableName(valueOfIdentifier)
|
|
659
|
+
return identifierToVariableName(valueOfIdentifier)
|
|
586
660
|
}
|
|
661
|
+
|
|
587
662
|
const prototypeValue = valueMap[prototypeIdentifier];
|
|
588
663
|
if (prototypeValue === null) {
|
|
589
|
-
return `Object.create(null)
|
|
664
|
+
return `Object.create(null);`
|
|
590
665
|
}
|
|
666
|
+
|
|
591
667
|
const prototypeConstructor = prototypeValue.constructor;
|
|
592
668
|
if (prototypeConstructor === Object) {
|
|
593
|
-
return `Object.create(${identifierToVariableName(prototypeIdentifier)})
|
|
669
|
+
return `Object.create(${identifierToVariableName(prototypeIdentifier)});`
|
|
594
670
|
}
|
|
671
|
+
|
|
595
672
|
if (valueOfIdentifier === undefined) {
|
|
596
|
-
return `new ${prototypeConstructor.name}()
|
|
673
|
+
return `new ${prototypeConstructor.name}();`
|
|
597
674
|
}
|
|
675
|
+
|
|
598
676
|
if (prototypeConstructor.name === "BigInt") {
|
|
599
|
-
return `Object(${identifierToVariableName(valueOfIdentifier)})
|
|
677
|
+
return `Object(${identifierToVariableName(valueOfIdentifier)})`
|
|
600
678
|
}
|
|
601
|
-
|
|
679
|
+
|
|
680
|
+
return `new ${prototypeConstructor.name}(${identifierToVariableName(valueOfIdentifier)});`
|
|
602
681
|
};
|
|
603
|
-
|
|
682
|
+
|
|
683
|
+
recipeArraySorted.forEach((recipe) => {
|
|
604
684
|
const recipeVariableName = identifierToVariableName(recipeArray.indexOf(recipe));
|
|
605
685
|
source += `var ${recipeVariableName} = ${recipeToSetupSource(recipe)}
|
|
606
686
|
`;
|
|
607
687
|
});
|
|
688
|
+
|
|
608
689
|
const recipeToMutateSource = (recipe, recipeVariableName) => {
|
|
609
690
|
if (recipe.type === "composite") {
|
|
610
|
-
return compositeRecipeToMutateSource(recipe, recipeVariableName)
|
|
691
|
+
return compositeRecipeToMutateSource(recipe, recipeVariableName)
|
|
611
692
|
}
|
|
612
|
-
return
|
|
693
|
+
return ``
|
|
613
694
|
};
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
symbolDescriptionArray,
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}, recipeVariableName) => {
|
|
695
|
+
|
|
696
|
+
const compositeRecipeToMutateSource = (
|
|
697
|
+
{ propertyDescriptionArray, symbolDescriptionArray, methodDescriptionArray, extensible },
|
|
698
|
+
recipeVariableName,
|
|
699
|
+
) => {
|
|
620
700
|
let mutateSource = ``;
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
701
|
+
|
|
702
|
+
propertyDescriptionArray.forEach(({ propertyNameIdentifier, propertyDescription }) => {
|
|
703
|
+
mutateSource += generateDefinePropertySource(
|
|
704
|
+
recipeVariableName,
|
|
705
|
+
propertyNameIdentifier,
|
|
706
|
+
propertyDescription,
|
|
707
|
+
);
|
|
626
708
|
});
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
709
|
+
|
|
710
|
+
symbolDescriptionArray.forEach(({ symbolIdentifier, propertyDescription }) => {
|
|
711
|
+
mutateSource += generateDefinePropertySource(
|
|
712
|
+
recipeVariableName,
|
|
713
|
+
symbolIdentifier,
|
|
714
|
+
propertyDescription,
|
|
715
|
+
);
|
|
632
716
|
});
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
callArray
|
|
636
|
-
}) => {
|
|
717
|
+
|
|
718
|
+
methodDescriptionArray.forEach(({ methodNameIdentifier, callArray }) => {
|
|
637
719
|
mutateSource += generateMethodCallSource(recipeVariableName, methodNameIdentifier, callArray);
|
|
638
720
|
});
|
|
721
|
+
|
|
639
722
|
if (!extensible) {
|
|
640
723
|
mutateSource += generatePreventExtensionSource(recipeVariableName);
|
|
641
724
|
}
|
|
642
|
-
|
|
725
|
+
|
|
726
|
+
return mutateSource
|
|
643
727
|
};
|
|
644
|
-
|
|
728
|
+
|
|
729
|
+
const generateDefinePropertySource = (
|
|
730
|
+
recipeVariableName,
|
|
731
|
+
propertyNameOrSymbolIdentifier,
|
|
732
|
+
propertyDescription,
|
|
733
|
+
) => {
|
|
645
734
|
const propertyOrSymbolVariableName = identifierToVariableName(propertyNameOrSymbolIdentifier);
|
|
735
|
+
|
|
646
736
|
const propertyDescriptorSource = generatePropertyDescriptorSource(propertyDescription);
|
|
647
|
-
return `safeDefineProperty(${recipeVariableName}, ${propertyOrSymbolVariableName}, ${propertyDescriptorSource})
|
|
737
|
+
return `safeDefineProperty(${recipeVariableName}, ${propertyOrSymbolVariableName}, ${propertyDescriptorSource});`
|
|
648
738
|
};
|
|
739
|
+
|
|
649
740
|
const generatePropertyDescriptorSource = ({
|
|
650
741
|
configurable,
|
|
651
742
|
writable,
|
|
652
743
|
enumerable,
|
|
653
744
|
getIdentifier,
|
|
654
745
|
setIdentifier,
|
|
655
|
-
valueIdentifier
|
|
746
|
+
valueIdentifier,
|
|
656
747
|
}) => {
|
|
657
748
|
if (valueIdentifier === undefined) {
|
|
658
749
|
return `{
|
|
@@ -660,33 +751,46 @@ function safeDefineProperty(object, propertyNameOrSymbol, descriptor) {
|
|
|
660
751
|
enumerable: ${enumerable},
|
|
661
752
|
get: ${getIdentifier === undefined ? undefined : identifierToVariableName(getIdentifier)},
|
|
662
753
|
set: ${setIdentifier === undefined ? undefined : identifierToVariableName(setIdentifier)},
|
|
663
|
-
}
|
|
754
|
+
}`
|
|
664
755
|
}
|
|
756
|
+
|
|
665
757
|
return `{
|
|
666
758
|
configurable: ${configurable},
|
|
667
759
|
writable: ${writable},
|
|
668
760
|
enumerable: ${enumerable},
|
|
669
761
|
value: ${valueIdentifier === undefined ? undefined : identifierToVariableName(valueIdentifier)}
|
|
670
|
-
}
|
|
762
|
+
}`
|
|
671
763
|
};
|
|
764
|
+
|
|
672
765
|
const generateMethodCallSource = (recipeVariableName, methodNameIdentifier, callArray) => {
|
|
673
766
|
let methodCallSource = ``;
|
|
767
|
+
|
|
674
768
|
const methodVariableName = identifierToVariableName(methodNameIdentifier);
|
|
675
|
-
callArray.forEach(argumentIdentifiers => {
|
|
676
|
-
const argumentVariableNames = argumentIdentifiers.map(argumentIdentifier =>
|
|
677
|
-
|
|
769
|
+
callArray.forEach((argumentIdentifiers) => {
|
|
770
|
+
const argumentVariableNames = argumentIdentifiers.map((argumentIdentifier) =>
|
|
771
|
+
identifierToVariableName(argumentIdentifier),
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
methodCallSource += `${recipeVariableName}[${methodVariableName}](${argumentVariableNames.join(
|
|
775
|
+
",",
|
|
776
|
+
)});`;
|
|
678
777
|
});
|
|
679
|
-
|
|
778
|
+
|
|
779
|
+
return methodCallSource
|
|
680
780
|
};
|
|
681
|
-
|
|
682
|
-
|
|
781
|
+
|
|
782
|
+
const generatePreventExtensionSource = (recipeVariableName) => {
|
|
783
|
+
return `Object.preventExtensions(${recipeVariableName});`
|
|
683
784
|
};
|
|
684
|
-
|
|
785
|
+
|
|
786
|
+
recipeArraySorted.forEach((recipe) => {
|
|
685
787
|
const recipeVariableName = identifierToVariableName(recipeArray.indexOf(recipe));
|
|
686
788
|
source += `${recipeToMutateSource(recipe, recipeVariableName)}`;
|
|
687
789
|
});
|
|
790
|
+
|
|
688
791
|
source += `return ${identifierToVariableName(mainIdentifier)}; })()`;
|
|
689
|
-
|
|
792
|
+
|
|
793
|
+
return source
|
|
690
794
|
};
|
|
691
795
|
|
|
692
796
|
/*
|
|
@@ -695,7 +799,7 @@ function safeDefineProperty(object, propertyNameOrSymbol, descriptor) {
|
|
|
695
799
|
* So the best solution remains NODE_V8_COVERAGE
|
|
696
800
|
* This profiler strategy remains useful when:
|
|
697
801
|
* - As fallback when NODE_V8_COVERAGE is not configured
|
|
698
|
-
* - If explicitely enabled with coverageMethodForNodeJs:
|
|
802
|
+
* - If explicitely enabled with coverageMethodForNodeJs: "Profiler"
|
|
699
803
|
* - Used by jsenv during automated tests about coverage
|
|
700
804
|
* - Anyone prefering this approach over NODE_V8_COVERAGE and assuming
|
|
701
805
|
* it will not fork subprocess or don't care if coverage is missed for this code
|
|
@@ -704,9 +808,10 @@ function safeDefineProperty(object, propertyNameOrSymbol, descriptor) {
|
|
|
704
808
|
* - https://vanilla.aslushnikov.com/?Profiler.startPreciseCoverage
|
|
705
809
|
*/
|
|
706
810
|
|
|
811
|
+
|
|
707
812
|
const startJsCoverage = async ({
|
|
708
813
|
callCount = true,
|
|
709
|
-
detailed = true
|
|
814
|
+
detailed = true,
|
|
710
815
|
} = {}) => {
|
|
711
816
|
const session = new Session();
|
|
712
817
|
session.connect();
|
|
@@ -722,70 +827,76 @@ const startJsCoverage = async ({
|
|
|
722
827
|
});
|
|
723
828
|
return promise;
|
|
724
829
|
};
|
|
830
|
+
|
|
725
831
|
await postSession("Profiler.enable");
|
|
726
|
-
await postSession("Profiler.startPreciseCoverage", {
|
|
727
|
-
|
|
728
|
-
detailed
|
|
729
|
-
});
|
|
832
|
+
await postSession("Profiler.startPreciseCoverage", { callCount, detailed });
|
|
833
|
+
|
|
730
834
|
const takeJsCoverage = async () => {
|
|
731
835
|
const coverage = await postSession("Profiler.takePreciseCoverage");
|
|
732
836
|
return coverage;
|
|
733
837
|
};
|
|
838
|
+
|
|
734
839
|
const stopJsCoverage = async () => {
|
|
735
840
|
const coverage = await takeJsCoverage();
|
|
736
841
|
await postSession("Profiler.stopPreciseCoverage");
|
|
737
842
|
await postSession("Profiler.disable");
|
|
738
843
|
return coverage;
|
|
739
844
|
};
|
|
845
|
+
|
|
740
846
|
return {
|
|
741
847
|
takeJsCoverage,
|
|
742
|
-
stopJsCoverage
|
|
848
|
+
stopJsCoverage,
|
|
743
849
|
};
|
|
744
850
|
};
|
|
745
851
|
|
|
746
852
|
const startObservingPerformances = () => {
|
|
747
853
|
const measureEntries = [];
|
|
748
854
|
// https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html
|
|
749
|
-
const perfObserver = new PerformanceObserver(
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
855
|
+
const perfObserver = new PerformanceObserver(
|
|
856
|
+
(
|
|
857
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#perf_hooks_class_performanceobserverentrylist
|
|
858
|
+
list,
|
|
859
|
+
) => {
|
|
860
|
+
const perfMeasureEntries = list.getEntriesByType("measure");
|
|
861
|
+
measureEntries.push(...perfMeasureEntries);
|
|
862
|
+
},
|
|
863
|
+
);
|
|
755
864
|
perfObserver.observe({
|
|
756
|
-
entryTypes: ["measure"]
|
|
865
|
+
entryTypes: ["measure"],
|
|
757
866
|
});
|
|
758
867
|
return async () => {
|
|
759
868
|
// wait for node to call the performance observer
|
|
760
|
-
await new Promise(resolve => {
|
|
869
|
+
await new Promise((resolve) => {
|
|
761
870
|
setTimeout(resolve);
|
|
762
871
|
});
|
|
763
872
|
performance.clearMarks();
|
|
764
873
|
perfObserver.disconnect();
|
|
765
874
|
return {
|
|
766
875
|
...readNodePerformance(),
|
|
767
|
-
measures: measuresFromMeasureEntries(measureEntries)
|
|
876
|
+
measures: measuresFromMeasureEntries(measureEntries),
|
|
768
877
|
};
|
|
769
878
|
};
|
|
770
879
|
};
|
|
880
|
+
|
|
771
881
|
const readNodePerformance = () => {
|
|
772
882
|
const nodePerformance = {
|
|
773
883
|
nodeTiming: asPlainObject(performance.nodeTiming),
|
|
774
884
|
timeOrigin: performance.timeOrigin,
|
|
775
|
-
eventLoopUtilization: performance.eventLoopUtilization()
|
|
885
|
+
eventLoopUtilization: performance.eventLoopUtilization(),
|
|
776
886
|
};
|
|
777
887
|
return nodePerformance;
|
|
778
888
|
};
|
|
779
889
|
|
|
780
890
|
// remove getters that cannot be stringified
|
|
781
|
-
const asPlainObject = objectWithGetters => {
|
|
891
|
+
const asPlainObject = (objectWithGetters) => {
|
|
782
892
|
const objectWithoutGetters = {};
|
|
783
|
-
Object.keys(objectWithGetters).forEach(key => {
|
|
893
|
+
Object.keys(objectWithGetters).forEach((key) => {
|
|
784
894
|
objectWithoutGetters[key] = objectWithGetters[key];
|
|
785
895
|
});
|
|
786
896
|
return objectWithoutGetters;
|
|
787
897
|
};
|
|
788
|
-
|
|
898
|
+
|
|
899
|
+
const measuresFromMeasureEntries = (measureEntries) => {
|
|
789
900
|
const measures = {};
|
|
790
901
|
// Sort to ensure measures order is predictable
|
|
791
902
|
// It seems to be already predictable on Node 16+ but
|
|
@@ -793,11 +904,14 @@ const measuresFromMeasureEntries = measureEntries => {
|
|
|
793
904
|
measureEntries.sort((a, b) => {
|
|
794
905
|
return a.startTime - b.startTime;
|
|
795
906
|
});
|
|
796
|
-
measureEntries.forEach(
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
907
|
+
measureEntries.forEach(
|
|
908
|
+
(
|
|
909
|
+
// https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#perf_hooks_class_performanceentry
|
|
910
|
+
perfMeasureEntry,
|
|
911
|
+
) => {
|
|
912
|
+
measures[perfMeasureEntry.name] = perfMeasureEntry.duration;
|
|
913
|
+
},
|
|
914
|
+
);
|
|
801
915
|
return measures;
|
|
802
916
|
};
|
|
803
917
|
|
|
@@ -808,24 +922,23 @@ const executeUsingDynamicImport = async ({
|
|
|
808
922
|
coverageEnabled,
|
|
809
923
|
coverageConfig,
|
|
810
924
|
coverageMethodForNodeJs,
|
|
811
|
-
coverageFileUrl
|
|
925
|
+
coverageFileUrl,
|
|
812
926
|
}) => {
|
|
813
927
|
const result = {};
|
|
814
928
|
const afterImportCallbacks = [];
|
|
815
929
|
if (coverageEnabled && coverageMethodForNodeJs === "Profiler") {
|
|
816
|
-
const {
|
|
817
|
-
|
|
818
|
-
} = await import("./v8_coverage.js").then(n => n.v8_coverage);
|
|
819
|
-
const {
|
|
820
|
-
stopJsCoverage
|
|
821
|
-
} = await startJsCoverage();
|
|
930
|
+
const { filterV8Coverage } = await import("./v8_coverage.js").then(n => n.v8_coverage);
|
|
931
|
+
const { stopJsCoverage } = await startJsCoverage();
|
|
822
932
|
afterImportCallbacks.push(async () => {
|
|
823
933
|
const coverage = await stopJsCoverage();
|
|
824
934
|
const coverageLight = await filterV8Coverage(coverage, {
|
|
825
935
|
rootDirectoryUrl,
|
|
826
|
-
coverageConfig
|
|
936
|
+
coverageConfig,
|
|
827
937
|
});
|
|
828
|
-
writeFileSync(
|
|
938
|
+
writeFileSync(
|
|
939
|
+
new URL(coverageFileUrl),
|
|
940
|
+
JSON.stringify(coverageLight, null, " "),
|
|
941
|
+
);
|
|
829
942
|
});
|
|
830
943
|
}
|
|
831
944
|
if (collectPerformance) {
|
|
@@ -837,10 +950,12 @@ const executeUsingDynamicImport = async ({
|
|
|
837
950
|
}
|
|
838
951
|
const namespace = await import(fileUrl);
|
|
839
952
|
const namespaceResolved = {};
|
|
840
|
-
await Promise.all(
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
953
|
+
await Promise.all(
|
|
954
|
+
Object.keys(namespace).map(async (key) => {
|
|
955
|
+
const value = await namespace[key];
|
|
956
|
+
namespaceResolved[key] = value;
|
|
957
|
+
}),
|
|
958
|
+
);
|
|
844
959
|
result.namespace = namespaceResolved;
|
|
845
960
|
await afterImportCallbacks.reduce(async (previous, afterImportCallback) => {
|
|
846
961
|
await previous;
|