@pulumi/pulumi 3.163.0-alpha.xdc88586 → 3.163.0-alpha.xffc5a7c
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/automation/cmd.js +92 -111
- package/automation/cmd.js.map +1 -1
- package/automation/localWorkspace.js +383 -476
- package/automation/localWorkspace.js.map +1 -1
- package/automation/remoteWorkspace.js +38 -55
- package/automation/remoteWorkspace.js.map +1 -1
- package/automation/server.js +6 -16
- package/automation/server.js.map +1 -1
- package/automation/stack.js +537 -608
- package/automation/stack.js.map +1 -1
- package/cmd/dynamic-provider/index.js +204 -235
- package/cmd/dynamic-provider/index.js.map +1 -1
- package/cmd/run/error.js +3 -3
- package/cmd/run/error.js.map +1 -1
- package/cmd/run/run.js +322 -336
- package/cmd/run/run.js.map +1 -1
- package/cmd/run/tracing.js +1 -2
- package/cmd/run/tracing.js.map +1 -1
- package/cmd/run-plugin/run.js +13 -17
- package/cmd/run-plugin/run.js.map +1 -1
- package/cmd/run-policy-pack/run.js +12 -16
- package/cmd/run-policy-pack/run.js.map +1 -1
- package/output.js +61 -78
- package/output.js.map +1 -1
- package/package.json +1 -1
- package/provider/experimental/analyzer.js +43 -46
- package/provider/experimental/analyzer.js.map +1 -1
- package/provider/experimental/provider.js +22 -36
- package/provider/experimental/provider.js.map +1 -1
- package/provider/server.js +359 -397
- package/provider/server.js.map +1 -1
- package/resource.js +29 -41
- package/resource.js.map +1 -1
- package/runtime/asyncIterableUtil.js +6 -17
- package/runtime/asyncIterableUtil.js.map +1 -1
- package/runtime/callbacks.js +254 -269
- package/runtime/callbacks.js.map +1 -1
- package/runtime/closure/codePaths.js +117 -135
- package/runtime/closure/codePaths.js.map +1 -1
- package/runtime/closure/createClosure.js +807 -871
- package/runtime/closure/createClosure.js.map +1 -1
- package/runtime/closure/parseFunction.js +2 -3
- package/runtime/closure/parseFunction.js.map +1 -1
- package/runtime/closure/serializeClosure.js +17 -30
- package/runtime/closure/serializeClosure.js.map +1 -1
- package/runtime/closure/v8.js +166 -190
- package/runtime/closure/v8.js.map +1 -1
- package/runtime/closure/v8Hooks.js +19 -34
- package/runtime/closure/v8Hooks.js.map +1 -1
- package/runtime/dependsOn.js +61 -80
- package/runtime/dependsOn.js.map +1 -1
- package/runtime/invoke.js +155 -170
- package/runtime/invoke.js.map +1 -1
- package/runtime/mocks.js +77 -96
- package/runtime/mocks.js.map +1 -1
- package/runtime/resource.js +238 -252
- package/runtime/resource.js.map +1 -1
- package/runtime/rpc.js +193 -215
- package/runtime/rpc.js.map +1 -1
- package/runtime/settings.js +70 -87
- package/runtime/settings.js.map +1 -1
- package/runtime/stack.js +111 -131
- package/runtime/stack.js.map +1 -1
- package/stackReference.js +39 -58
- package/stackReference.js.map +1 -1
- package/tsutils.js +1 -2
- package/tsutils.js.map +1 -1
- package/utils.js +2 -3
- package/utils.js.map +1 -1
- package/version.js +1 -1
|
@@ -12,15 +12,6 @@
|
|
|
12
12
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
// See the License for the specific language governing permissions and
|
|
14
14
|
// limitations under the License.
|
|
15
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
16
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
17
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
18
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
19
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
20
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
21
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
22
|
-
});
|
|
23
|
-
};
|
|
24
15
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
25
16
|
if (mod && mod.__esModule) return mod;
|
|
26
17
|
var result = {};
|
|
@@ -70,124 +61,114 @@ class SerializedOutput {
|
|
|
70
61
|
*
|
|
71
62
|
* @internal
|
|
72
63
|
*/
|
|
73
|
-
function createClosureInfoAsync(func, serialize, logResource) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (key !== "GLOBAL" && key !== "root") {
|
|
116
|
-
yield addGlobalInfoAsync(key);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
64
|
+
async function createClosureInfoAsync(func, serialize, logResource) {
|
|
65
|
+
// Initialize our Context object. It is effectively used to keep track of the work we're doing
|
|
66
|
+
// as well as to keep track of the graph as we're walking it so we don't infinitely recurse.
|
|
67
|
+
const context = {
|
|
68
|
+
cache: new Map(),
|
|
69
|
+
classInstanceMemberToSuperEntry: new Map(),
|
|
70
|
+
classStaticMemberToSuperEntry: new Map(),
|
|
71
|
+
frames: [],
|
|
72
|
+
simpleFunctions: [],
|
|
73
|
+
logResource,
|
|
74
|
+
containsSecrets: false,
|
|
75
|
+
};
|
|
76
|
+
// Pre-populate our context's cache with global well-known values. These are values for things
|
|
77
|
+
// like global.Number, or Function.prototype. Actually trying to serialize/deserialize these
|
|
78
|
+
// would be a bad idea as that would mean once deserialized the objects wouldn't point to the
|
|
79
|
+
// well known globals that were expected. Furthermore, most of these are almost certain to fail
|
|
80
|
+
// to serialize due to hitting things like native-builtins.
|
|
81
|
+
await addEntriesForWellKnownGlobalObjectsAsync();
|
|
82
|
+
// Make sure this func is in the cache itself as we may hit it again while recursing.
|
|
83
|
+
const entry = {};
|
|
84
|
+
context.cache.set(func, entry);
|
|
85
|
+
entry.function = await analyzeFunctionInfoAsync(func, context, serialize);
|
|
86
|
+
return {
|
|
87
|
+
func: entry.function,
|
|
88
|
+
containsSecrets: context.containsSecrets,
|
|
89
|
+
};
|
|
90
|
+
async function addEntriesForWellKnownGlobalObjectsAsync() {
|
|
91
|
+
const seenGlobalObjects = new Set();
|
|
92
|
+
// Front load these guys so we prefer emitting code that references them directly,
|
|
93
|
+
// instead of in unexpected ways. i.e. we'd prefer to have Number.prototype vs
|
|
94
|
+
// Object.getPrototypeOf(Infinity) (even though they're the same thing.)
|
|
95
|
+
await addGlobalInfoAsync("Object");
|
|
96
|
+
await addGlobalInfoAsync("Function");
|
|
97
|
+
await addGlobalInfoAsync("Array");
|
|
98
|
+
await addGlobalInfoAsync("Number");
|
|
99
|
+
await addGlobalInfoAsync("String");
|
|
100
|
+
for (let current = global; current; current = Object.getPrototypeOf(current)) {
|
|
101
|
+
for (const key of Object.getOwnPropertyNames(current)) {
|
|
102
|
+
// "GLOBAL" and "root" are deprecated and give warnings if you try to access them. So
|
|
103
|
+
// just skip them.
|
|
104
|
+
if (key !== "GLOBAL" && key !== "root") {
|
|
105
|
+
await addGlobalInfoAsync(key);
|
|
119
106
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Add information so that we can properly serialize over generators/iterators.
|
|
110
|
+
await addGeneratorEntriesAsync();
|
|
111
|
+
await addEntriesAsync(Symbol.iterator, "Symbol.iterator");
|
|
112
|
+
return;
|
|
113
|
+
async function addEntriesAsync(val, emitExpr, recurse = false) {
|
|
114
|
+
if (val === undefined || val === null) {
|
|
123
115
|
return;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const desc = Object.getOwnPropertyDescriptor(val, propName);
|
|
145
|
-
if ((desc === null || desc === void 0 ? void 0 : desc.value) && typeof desc.value === "function") {
|
|
146
|
-
addEntriesAsync(desc === null || desc === void 0 ? void 0 : desc.value, `${emitExpr}.${propName}`, recurse);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
function addGlobalInfoAsync(key) {
|
|
153
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
-
const globalObj = global[key];
|
|
155
|
-
const text = utils.isLegalMemberName(key) ? `global.${key}` : `global["${key}"]`;
|
|
156
|
-
if (globalObj !== undefined && globalObj !== null) {
|
|
157
|
-
yield addEntriesAsync(globalObj, text, /* recurse */ true);
|
|
158
|
-
yield addEntriesAsync(Object.getPrototypeOf(globalObj), `Object.getPrototypeOf(${text})`,
|
|
159
|
-
/* recurse */ true);
|
|
160
|
-
yield addEntriesAsync(globalObj.prototype, `${text}.prototype`, /* recurse */ true);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
// A generator function ('f') has ends up creating two interesting objects in the js
|
|
165
|
-
// environment:
|
|
166
|
-
//
|
|
167
|
-
// 1. the generator function itself ('f'). This generator function has an __proto__ that is
|
|
168
|
-
// shared will all other generator functions.
|
|
169
|
-
//
|
|
170
|
-
// 2. a property 'prototype' on 'f'. This property's __proto__ will be shared will all other
|
|
171
|
-
// 'prototype' properties of other generator functions.
|
|
172
|
-
//
|
|
173
|
-
// So, to properly serialize a generator, we stash these special objects away so that we can
|
|
174
|
-
// refer to the well known instance on the other side when we desirialize. Otherwise, if we
|
|
175
|
-
// actually tried to deserialize the instances/prototypes we have we would end up failing when
|
|
176
|
-
// we hit native functions.
|
|
177
|
-
//
|
|
178
|
-
// see http://www.ecma-international.org/ecma-262/6.0/#sec-generatorfunction-objects and
|
|
179
|
-
// http://www.ecma-international.org/ecma-262/6.0/figure-2.png
|
|
180
|
-
function addGeneratorEntriesAsync() {
|
|
181
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
-
// eslint-disable-next-line no-empty,no-empty-function,@typescript-eslint/no-empty-function
|
|
183
|
-
const emptyGenerator = function* () { };
|
|
184
|
-
yield addEntriesAsync(Object.getPrototypeOf(emptyGenerator), "Object.getPrototypeOf(function*(){})");
|
|
185
|
-
yield addEntriesAsync(Object.getPrototypeOf(emptyGenerator.prototype), "Object.getPrototypeOf((function*(){}).prototype)");
|
|
186
|
-
});
|
|
116
|
+
}
|
|
117
|
+
// No need to add values twice. Ths can happen as we walk the global namespace and
|
|
118
|
+
// sometimes run into multiple names aliasing to the same value.
|
|
119
|
+
if (seenGlobalObjects.has(val)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
seenGlobalObjects.add(val);
|
|
123
|
+
context.cache.set(val, { expr: emitExpr });
|
|
124
|
+
// For global objects, we want to recurse into them to find all
|
|
125
|
+
// their properties and add entries for them. This allows us to
|
|
126
|
+
// recognize these builtins when they have been aliased, for
|
|
127
|
+
// example if we have `const isArray = Array.isArray` and capture
|
|
128
|
+
// `isArray` in the function, we want to emit the expression
|
|
129
|
+
// `Array.isArray` for the captured value.
|
|
130
|
+
if (recurse) {
|
|
131
|
+
for (const propName of Object.getOwnPropertyNames(val)) {
|
|
132
|
+
const desc = Object.getOwnPropertyDescriptor(val, propName);
|
|
133
|
+
if (desc?.value && typeof desc.value === "function") {
|
|
134
|
+
addEntriesAsync(desc?.value, `${emitExpr}.${propName}`, recurse);
|
|
135
|
+
}
|
|
187
136
|
}
|
|
188
|
-
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function addGlobalInfoAsync(key) {
|
|
140
|
+
const globalObj = global[key];
|
|
141
|
+
const text = utils.isLegalMemberName(key) ? `global.${key}` : `global["${key}"]`;
|
|
142
|
+
if (globalObj !== undefined && globalObj !== null) {
|
|
143
|
+
await addEntriesAsync(globalObj, text, /* recurse */ true);
|
|
144
|
+
await addEntriesAsync(Object.getPrototypeOf(globalObj), `Object.getPrototypeOf(${text})`,
|
|
145
|
+
/* recurse */ true);
|
|
146
|
+
await addEntriesAsync(globalObj.prototype, `${text}.prototype`, /* recurse */ true);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// A generator function ('f') has ends up creating two interesting objects in the js
|
|
150
|
+
// environment:
|
|
151
|
+
//
|
|
152
|
+
// 1. the generator function itself ('f'). This generator function has an __proto__ that is
|
|
153
|
+
// shared will all other generator functions.
|
|
154
|
+
//
|
|
155
|
+
// 2. a property 'prototype' on 'f'. This property's __proto__ will be shared will all other
|
|
156
|
+
// 'prototype' properties of other generator functions.
|
|
157
|
+
//
|
|
158
|
+
// So, to properly serialize a generator, we stash these special objects away so that we can
|
|
159
|
+
// refer to the well known instance on the other side when we desirialize. Otherwise, if we
|
|
160
|
+
// actually tried to deserialize the instances/prototypes we have we would end up failing when
|
|
161
|
+
// we hit native functions.
|
|
162
|
+
//
|
|
163
|
+
// see http://www.ecma-international.org/ecma-262/6.0/#sec-generatorfunction-objects and
|
|
164
|
+
// http://www.ecma-international.org/ecma-262/6.0/figure-2.png
|
|
165
|
+
async function addGeneratorEntriesAsync() {
|
|
166
|
+
// eslint-disable-next-line no-empty,no-empty-function,@typescript-eslint/no-empty-function
|
|
167
|
+
const emptyGenerator = function* () { };
|
|
168
|
+
await addEntriesAsync(Object.getPrototypeOf(emptyGenerator), "Object.getPrototypeOf(function*(){})");
|
|
169
|
+
await addEntriesAsync(Object.getPrototypeOf(emptyGenerator.prototype), "Object.getPrototypeOf((function*(){}).prototype)");
|
|
189
170
|
}
|
|
190
|
-
}
|
|
171
|
+
}
|
|
191
172
|
}
|
|
192
173
|
exports.createClosureInfoAsync = createClosureInfoAsync;
|
|
193
174
|
// This function ends up capturing many external modules that cannot themselves be serialized.
|
|
@@ -197,260 +178,248 @@ createClosureInfoAsync.doNotCapture = true;
|
|
|
197
178
|
* Does the work to create an asynchronous dataflow graph that resolves to a
|
|
198
179
|
* final {@link FunctionInfo}.
|
|
199
180
|
*/
|
|
200
|
-
function analyzeFunctionInfoAsync(func, context, serialize, logInfo) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
181
|
+
async function analyzeFunctionInfoAsync(func, context, serialize, logInfo) {
|
|
182
|
+
// logInfo = logInfo || func.name === "addHandler";
|
|
183
|
+
const { file, line, column } = await v8.getFunctionLocationAsync(func);
|
|
184
|
+
let functionString = func.toString();
|
|
185
|
+
const frame = { functionLocation: { func, file, line, column, functionString, isArrowFunction: false } };
|
|
186
|
+
context.frames.push(frame);
|
|
187
|
+
const result = await serializeWorkerAsync();
|
|
188
|
+
context.frames.pop();
|
|
189
|
+
if (isSimple(result)) {
|
|
190
|
+
const existingSimpleFunction = findSimpleFunction(result);
|
|
191
|
+
if (existingSimpleFunction) {
|
|
192
|
+
return existingSimpleFunction;
|
|
193
|
+
}
|
|
194
|
+
context.simpleFunctions.push(result);
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
function isSimple(info) {
|
|
198
|
+
return info.capturedValues.size === 0 && info.env.size === 0 && !info.proto;
|
|
199
|
+
}
|
|
200
|
+
function findSimpleFunction(info) {
|
|
201
|
+
for (const other of context.simpleFunctions) {
|
|
202
|
+
if (other.code === info.code && other.usesNonLexicalThis === info.usesNonLexicalThis) {
|
|
203
|
+
return other;
|
|
213
204
|
}
|
|
214
|
-
context.simpleFunctions.push(result);
|
|
215
205
|
}
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
async function serializeWorkerAsync() {
|
|
209
|
+
const funcEntry = context.cache.get(func);
|
|
210
|
+
if (!funcEntry) {
|
|
211
|
+
throw new Error("Entry for this this function was not created by caller");
|
|
219
212
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
213
|
+
const capturedValues = new Map();
|
|
214
|
+
// If the function is a native function, it is most likely the result of `Function.bind`.
|
|
215
|
+
// We get the target function via the debugger API, along with the boundThis and boundArgs
|
|
216
|
+
// values and create a new function using the target function's text representation and
|
|
217
|
+
// rebind it. The boundThis and boundArgs values are manually captured.
|
|
218
|
+
//
|
|
219
|
+
// TODO: This does not handle multiple binds correctly.
|
|
220
|
+
// For example (function () { ... }).bind("a").bind("b") will fail to serialize correctly.
|
|
221
|
+
if (parseFunctionModule.isNativeFunction(functionString)) {
|
|
222
|
+
try {
|
|
223
|
+
const { targetFunctionText, boundThisValue, boundArgsValues } = await v8.getBoundFunction(func);
|
|
224
|
+
const boundThis = "__pulumi_bound_this";
|
|
225
|
+
const boundArgs = boundArgsValues.map((_, i) => `__pulumi_bound_arg_${i}`).join(", ");
|
|
226
|
+
const boundArgsString = boundArgs.length > 0 ? `, ${boundArgs}` : "";
|
|
227
|
+
functionString =
|
|
228
|
+
`function (...args) {\n` +
|
|
229
|
+
` return (\n` +
|
|
230
|
+
`${targetFunctionText}\n` +
|
|
231
|
+
` ).bind(${boundThis}${boundArgsString})(...args);\n` +
|
|
232
|
+
`}`;
|
|
233
|
+
const serializedName = await getOrCreateNameEntryAsync("__pulumi_bound_this", undefined, context, serialize, logInfo);
|
|
234
|
+
const serializedValue = await getOrCreateEntryAsync(boundThisValue, undefined, context, serialize, logInfo);
|
|
235
|
+
capturedValues.set(serializedName, { entry: serializedValue });
|
|
236
|
+
for (const [i, boundArg] of boundArgsValues.entries()) {
|
|
237
|
+
const name = await getOrCreateNameEntryAsync(`__pulumi_bound_arg_${i}`, undefined, context, serialize, logInfo);
|
|
238
|
+
const value = await getOrCreateEntryAsync(boundArg, undefined, context, serialize, logInfo);
|
|
239
|
+
capturedValues.set(name, { entry: value });
|
|
224
240
|
}
|
|
225
241
|
}
|
|
226
|
-
|
|
242
|
+
catch (err) {
|
|
243
|
+
throwSerializationError(func, context, err.message);
|
|
244
|
+
}
|
|
227
245
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
246
|
+
// First, convert the js func object to a reasonable stringified version that we can operate on.
|
|
247
|
+
// Importantly, this function helps massage all the different forms that V8 can produce to
|
|
248
|
+
// either a "function (...) { ... }" form, or a "(...) => ..." form. In other words, all
|
|
249
|
+
// 'funky' functions (like classes and whatnot) will be transformed to reasonable forms we can
|
|
250
|
+
// process down the pipeline.
|
|
251
|
+
const pf = require("./parseFunction");
|
|
252
|
+
const [error, parsedFunction] = pf.parseFunction(functionString);
|
|
253
|
+
if (error) {
|
|
254
|
+
throwSerializationError(func, context, error);
|
|
255
|
+
}
|
|
256
|
+
const funcExprWithName = parsedFunction.funcExprWithName;
|
|
257
|
+
const functionDeclarationName = parsedFunction.functionDeclarationName;
|
|
258
|
+
frame.functionLocation.isArrowFunction = parsedFunction.isArrowFunction;
|
|
259
|
+
await processCapturedVariablesAsync(parsedFunction.capturedVariables.required, /*throwOnFailure:*/ true);
|
|
260
|
+
await processCapturedVariablesAsync(parsedFunction.capturedVariables.optional, /*throwOnFailure:*/ false);
|
|
261
|
+
const functionInfo = {
|
|
262
|
+
code: parsedFunction.funcExprWithoutName,
|
|
263
|
+
capturedValues: capturedValues,
|
|
264
|
+
env: new Map(),
|
|
265
|
+
usesNonLexicalThis: parsedFunction.usesNonLexicalThis,
|
|
266
|
+
name: functionDeclarationName,
|
|
267
|
+
paramCount: func.length,
|
|
268
|
+
};
|
|
269
|
+
const proto = Object.getPrototypeOf(func);
|
|
270
|
+
const isAsyncFunction = await computeIsAsyncFunction(func);
|
|
271
|
+
// Ensure that the prototype of this function is properly serialized as well. We only need to do
|
|
272
|
+
// this for functions with a custom prototype (like a derived class constructor, or a function
|
|
273
|
+
// that a user has explicit set the prototype for). Normal functions will pick up
|
|
274
|
+
// Function.prototype by default, so we don't need to do anything for them.
|
|
275
|
+
if (proto !== Function.prototype && !isAsyncFunction && !isDerivedNoCaptureConstructor(func)) {
|
|
276
|
+
const protoEntry = await getOrCreateEntryAsync(proto, undefined, context, serialize, logInfo);
|
|
277
|
+
functionInfo.proto = protoEntry;
|
|
278
|
+
if (functionString.startsWith("class ")) {
|
|
279
|
+
// This was a class (which is effectively synonymous with a constructor-function).
|
|
280
|
+
// We also know that it's a derived class because of the `proto !==
|
|
281
|
+
// Function.prototype` check above. (The prototype of a non-derived class points at
|
|
282
|
+
// Function.prototype).
|
|
239
283
|
//
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
if
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
// Function.prototype` check above. (The prototype of a non-derived class points at
|
|
303
|
-
// Function.prototype).
|
|
304
|
-
//
|
|
305
|
-
// they're a bit trickier to serialize than just a straight function. Specifically,
|
|
306
|
-
// we have to keep track of the inheritance relationship between classes. That way
|
|
307
|
-
// if any of the class members references 'super' we'll be able to rewrite it
|
|
308
|
-
// accordingly (since we emit classes as Functions)
|
|
309
|
-
yield processDerivedClassConstructorAsync(protoEntry);
|
|
310
|
-
// Because this was was class constructor function, rewrite any 'super' references
|
|
311
|
-
// in it do its derived type if it has one.
|
|
312
|
-
functionInfo.code = rewriteSuper_1.rewriteSuperReferences(funcExprWithName, /*isStatic*/ false);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
// capture any properties placed on the function itself. Don't bother with
|
|
316
|
-
// "length/name" as those are not things we can actually change.
|
|
317
|
-
for (const descriptor of yield getOwnPropertyDescriptors(func)) {
|
|
318
|
-
if (descriptor.name === "length" || descriptor.name === "name") {
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
const funcProp = yield getOwnPropertyAsync(func, descriptor);
|
|
322
|
-
// We don't need to emit code to serialize this function's .prototype object
|
|
323
|
-
// unless that .prototype object was actually changed.
|
|
324
|
-
//
|
|
325
|
-
// In other words, in general, we will not emit the prototype for a normal
|
|
326
|
-
// 'function foo() {}' declaration. but we will emit the prototype for the
|
|
327
|
-
// constructor function of a class.
|
|
328
|
-
if (descriptor.name === "prototype" && (yield isDefaultFunctionPrototypeAsync(func, funcProp))) {
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
const keyEntry = yield getOrCreateEntryAsync(getNameOrSymbol(descriptor), undefined, context, serialize, logInfo);
|
|
332
|
-
const valEntry = yield getOrCreateEntryAsync(funcProp, undefined, context, serialize, logInfo);
|
|
333
|
-
const propertyInfo = yield createPropertyInfoAsync(descriptor, context, serialize, logInfo);
|
|
334
|
-
functionInfo.env.set(keyEntry, { info: propertyInfo, entry: valEntry });
|
|
335
|
-
}
|
|
336
|
-
const superEntry = context.classInstanceMemberToSuperEntry.get(func) || context.classStaticMemberToSuperEntry.get(func);
|
|
337
|
-
if (superEntry) {
|
|
338
|
-
// this was a class constructor or method. We need to put a special __super
|
|
339
|
-
// entry into scope, and then rewrite any calls to super() to refer to it.
|
|
340
|
-
capturedValues.set(yield getOrCreateNameEntryAsync("__super", undefined, context, serialize, logInfo), {
|
|
341
|
-
entry: superEntry,
|
|
342
|
-
});
|
|
343
|
-
functionInfo.code = rewriteSuper_1.rewriteSuperReferences(funcExprWithName, context.classStaticMemberToSuperEntry.has(func));
|
|
284
|
+
// they're a bit trickier to serialize than just a straight function. Specifically,
|
|
285
|
+
// we have to keep track of the inheritance relationship between classes. That way
|
|
286
|
+
// if any of the class members references 'super' we'll be able to rewrite it
|
|
287
|
+
// accordingly (since we emit classes as Functions)
|
|
288
|
+
await processDerivedClassConstructorAsync(protoEntry);
|
|
289
|
+
// Because this was was class constructor function, rewrite any 'super' references
|
|
290
|
+
// in it do its derived type if it has one.
|
|
291
|
+
functionInfo.code = rewriteSuper_1.rewriteSuperReferences(funcExprWithName, /*isStatic*/ false);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// capture any properties placed on the function itself. Don't bother with
|
|
295
|
+
// "length/name" as those are not things we can actually change.
|
|
296
|
+
for (const descriptor of await getOwnPropertyDescriptors(func)) {
|
|
297
|
+
if (descriptor.name === "length" || descriptor.name === "name") {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const funcProp = await getOwnPropertyAsync(func, descriptor);
|
|
301
|
+
// We don't need to emit code to serialize this function's .prototype object
|
|
302
|
+
// unless that .prototype object was actually changed.
|
|
303
|
+
//
|
|
304
|
+
// In other words, in general, we will not emit the prototype for a normal
|
|
305
|
+
// 'function foo() {}' declaration. but we will emit the prototype for the
|
|
306
|
+
// constructor function of a class.
|
|
307
|
+
if (descriptor.name === "prototype" && (await isDefaultFunctionPrototypeAsync(func, funcProp))) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const keyEntry = await getOrCreateEntryAsync(getNameOrSymbol(descriptor), undefined, context, serialize, logInfo);
|
|
311
|
+
const valEntry = await getOrCreateEntryAsync(funcProp, undefined, context, serialize, logInfo);
|
|
312
|
+
const propertyInfo = await createPropertyInfoAsync(descriptor, context, serialize, logInfo);
|
|
313
|
+
functionInfo.env.set(keyEntry, { info: propertyInfo, entry: valEntry });
|
|
314
|
+
}
|
|
315
|
+
const superEntry = context.classInstanceMemberToSuperEntry.get(func) || context.classStaticMemberToSuperEntry.get(func);
|
|
316
|
+
if (superEntry) {
|
|
317
|
+
// this was a class constructor or method. We need to put a special __super
|
|
318
|
+
// entry into scope, and then rewrite any calls to super() to refer to it.
|
|
319
|
+
capturedValues.set(await getOrCreateNameEntryAsync("__super", undefined, context, serialize, logInfo), {
|
|
320
|
+
entry: superEntry,
|
|
321
|
+
});
|
|
322
|
+
functionInfo.code = rewriteSuper_1.rewriteSuperReferences(funcExprWithName, context.classStaticMemberToSuperEntry.has(func));
|
|
323
|
+
}
|
|
324
|
+
// If this was a named function (literally, only a named function-expr or function-decl), then
|
|
325
|
+
// place an entry in the environment that maps from this function name to the serialized
|
|
326
|
+
// function we're creating. This ensures that recursive functions will call the right method.
|
|
327
|
+
// i.e if we have "function f() { f(); }" this will get rewritten to:
|
|
328
|
+
//
|
|
329
|
+
// function __f() {
|
|
330
|
+
// with ({ f: __f }) {
|
|
331
|
+
// return function () { f(); }
|
|
332
|
+
//
|
|
333
|
+
// i.e. the inner call to "f();" will actually call the *outer* __f function, and not
|
|
334
|
+
// itself.
|
|
335
|
+
if (functionDeclarationName !== undefined) {
|
|
336
|
+
capturedValues.set(await getOrCreateNameEntryAsync(functionDeclarationName, undefined, context, serialize, logInfo), { entry: funcEntry });
|
|
337
|
+
}
|
|
338
|
+
return functionInfo;
|
|
339
|
+
async function processCapturedVariablesAsync(capturedVariables, throwOnFailure) {
|
|
340
|
+
for (const name of capturedVariables.keys()) {
|
|
341
|
+
// We have a special case for __pulumi_bound_this and __pulumi_bound_arg_X.
|
|
342
|
+
// We manually inject these variables into the closure environment of a
|
|
343
|
+
// function when we rewrite bound functions.
|
|
344
|
+
if (name.startsWith("__pulumi_bound_")) {
|
|
345
|
+
continue;
|
|
344
346
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
// i.e if we have "function f() { f(); }" this will get rewritten to:
|
|
349
|
-
//
|
|
350
|
-
// function __f() {
|
|
351
|
-
// with ({ f: __f }) {
|
|
352
|
-
// return function () { f(); }
|
|
353
|
-
//
|
|
354
|
-
// i.e. the inner call to "f();" will actually call the *outer* __f function, and not
|
|
355
|
-
// itself.
|
|
356
|
-
if (functionDeclarationName !== undefined) {
|
|
357
|
-
capturedValues.set(yield getOrCreateNameEntryAsync(functionDeclarationName, undefined, context, serialize, logInfo), { entry: funcEntry });
|
|
347
|
+
let value;
|
|
348
|
+
try {
|
|
349
|
+
value = await v8.lookupCapturedVariableValueAsync(func, name, throwOnFailure);
|
|
358
350
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
362
|
-
for (const name of capturedVariables.keys()) {
|
|
363
|
-
// We have a special case for __pulumi_bound_this and __pulumi_bound_arg_X.
|
|
364
|
-
// We manually inject these variables into the closure environment of a
|
|
365
|
-
// function when we rewrite bound functions.
|
|
366
|
-
if (name.startsWith("__pulumi_bound_")) {
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
let value;
|
|
370
|
-
try {
|
|
371
|
-
value = yield v8.lookupCapturedVariableValueAsync(func, name, throwOnFailure);
|
|
372
|
-
}
|
|
373
|
-
catch (err) {
|
|
374
|
-
throwSerializationError(func, context, err.message);
|
|
375
|
-
}
|
|
376
|
-
const moduleName = yield findNormalizedModuleNameAsync(value);
|
|
377
|
-
const frameLength = context.frames.length;
|
|
378
|
-
if (moduleName) {
|
|
379
|
-
context.frames.push({ capturedModule: { name: moduleName, value: value } });
|
|
380
|
-
}
|
|
381
|
-
else if (value instanceof Function) {
|
|
382
|
-
// Only bother pushing on context frame if the name of the variable
|
|
383
|
-
// we captured is different from the name of the function. If the
|
|
384
|
-
// names are the same, this is a direct reference, and we don't have
|
|
385
|
-
// to list both the name of the capture and of the function. if they
|
|
386
|
-
// are different, it's an indirect reference, and the name should be
|
|
387
|
-
// included for clarity.
|
|
388
|
-
if (name !== value.name) {
|
|
389
|
-
context.frames.push({ capturedFunctionName: name });
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
context.frames.push({ capturedVariableName: name });
|
|
394
|
-
}
|
|
395
|
-
yield processCapturedVariableAsync(capturedVariables, name, value);
|
|
396
|
-
// Only if we pushed a frame on should we pop it off.
|
|
397
|
-
if (context.frames.length !== frameLength) {
|
|
398
|
-
context.frames.pop();
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
});
|
|
351
|
+
catch (err) {
|
|
352
|
+
throwSerializationError(func, context, err.message);
|
|
402
353
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
// try to only serialize out the properties that were used by the user's code.
|
|
408
|
-
const serializedValue = yield getOrCreateEntryAsync(value, properties, context, serialize, logInfo);
|
|
409
|
-
capturedValues.set(serializedName, { entry: serializedValue });
|
|
410
|
-
});
|
|
354
|
+
const moduleName = await findNormalizedModuleNameAsync(value);
|
|
355
|
+
const frameLength = context.frames.length;
|
|
356
|
+
if (moduleName) {
|
|
357
|
+
context.frames.push({ capturedModule: { name: moduleName, value: value } });
|
|
411
358
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
context.classInstanceMemberToSuperEntry.set(func, protoEntry);
|
|
422
|
-
// Also, make sure our methods can also find this entry so they too can refer to
|
|
423
|
-
// 'super'.
|
|
424
|
-
for (const descriptor of yield getOwnPropertyDescriptors(func)) {
|
|
425
|
-
if (descriptor.name !== "length" && descriptor.name !== "name" && descriptor.name !== "prototype") {
|
|
426
|
-
// static method.
|
|
427
|
-
const classProp = yield getOwnPropertyAsync(func, descriptor);
|
|
428
|
-
addIfFunction(classProp, /*isStatic*/ true);
|
|
359
|
+
else if (value instanceof Function) {
|
|
360
|
+
// Only bother pushing on context frame if the name of the variable
|
|
361
|
+
// we captured is different from the name of the function. If the
|
|
362
|
+
// names are the same, this is a direct reference, and we don't have
|
|
363
|
+
// to list both the name of the capture and of the function. if they
|
|
364
|
+
// are different, it's an indirect reference, and the name should be
|
|
365
|
+
// included for clarity.
|
|
366
|
+
if (name !== value.name) {
|
|
367
|
+
context.frames.push({ capturedFunctionName: name });
|
|
429
368
|
}
|
|
430
369
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const classProp = yield getOwnPropertyAsync(func.prototype, descriptor);
|
|
434
|
-
addIfFunction(classProp, /*isStatic*/ false);
|
|
370
|
+
else {
|
|
371
|
+
context.frames.push({ capturedVariableName: name });
|
|
435
372
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
set.set(prop, protoEntry);
|
|
441
|
-
}
|
|
373
|
+
await processCapturedVariableAsync(capturedVariables, name, value);
|
|
374
|
+
// Only if we pushed a frame on should we pop it off.
|
|
375
|
+
if (context.frames.length !== frameLength) {
|
|
376
|
+
context.frames.pop();
|
|
442
377
|
}
|
|
443
|
-
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
async function processCapturedVariableAsync(capturedVariables, name, value) {
|
|
381
|
+
const properties = capturedVariables.get(name);
|
|
382
|
+
const serializedName = await getOrCreateNameEntryAsync(name, undefined, context, serialize, logInfo);
|
|
383
|
+
// try to only serialize out the properties that were used by the user's code.
|
|
384
|
+
const serializedValue = await getOrCreateEntryAsync(value, properties, context, serialize, logInfo);
|
|
385
|
+
capturedValues.set(serializedName, { entry: serializedValue });
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function processDerivedClassConstructorAsync(protoEntry) {
|
|
389
|
+
// Map from derived class' constructor and members, to the entry for the base class (i.e.
|
|
390
|
+
// the base class' constructor function). We'll use this when serializing out those members
|
|
391
|
+
// to rewrite any usages of 'super' appropriately.
|
|
392
|
+
// We're processing the derived class constructor itself. Just map it directly to the base
|
|
393
|
+
// class function.
|
|
394
|
+
context.classInstanceMemberToSuperEntry.set(func, protoEntry);
|
|
395
|
+
// Also, make sure our methods can also find this entry so they too can refer to
|
|
396
|
+
// 'super'.
|
|
397
|
+
for (const descriptor of await getOwnPropertyDescriptors(func)) {
|
|
398
|
+
if (descriptor.name !== "length" && descriptor.name !== "name" && descriptor.name !== "prototype") {
|
|
399
|
+
// static method.
|
|
400
|
+
const classProp = await getOwnPropertyAsync(func, descriptor);
|
|
401
|
+
addIfFunction(classProp, /*isStatic*/ true);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const descriptor of await getOwnPropertyDescriptors(func.prototype)) {
|
|
405
|
+
// instance method.
|
|
406
|
+
const classProp = await getOwnPropertyAsync(func.prototype, descriptor);
|
|
407
|
+
addIfFunction(classProp, /*isStatic*/ false);
|
|
408
|
+
}
|
|
409
|
+
return;
|
|
410
|
+
function addIfFunction(prop, isStatic) {
|
|
411
|
+
if (prop instanceof Function) {
|
|
412
|
+
const set = isStatic ? context.classStaticMemberToSuperEntry : context.classInstanceMemberToSuperEntry;
|
|
413
|
+
set.set(prop, protoEntry);
|
|
414
|
+
}
|
|
444
415
|
}
|
|
445
|
-
}
|
|
416
|
+
}
|
|
446
417
|
}
|
|
447
|
-
function computeIsAsyncFunction(func) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
return func.constructor && func.constructor.name === "AsyncFunction";
|
|
453
|
-
});
|
|
418
|
+
async function computeIsAsyncFunction(func) {
|
|
419
|
+
// Note, i can't think of a better way to determine this. This is particularly hard because we
|
|
420
|
+
// can't even necessary refer to async function objects here as this code is rewritten by TS,
|
|
421
|
+
// converting all async functions to non async functions.
|
|
422
|
+
return func.constructor && func.constructor.name === "AsyncFunction";
|
|
454
423
|
}
|
|
455
424
|
function throwSerializationError(func, context, info) {
|
|
456
425
|
let message = "";
|
|
@@ -568,31 +537,27 @@ function getFunctionName(loc) {
|
|
|
568
537
|
}
|
|
569
538
|
return "<anonymous>";
|
|
570
539
|
}
|
|
571
|
-
function isDefaultFunctionPrototypeAsync(func, prototypeProp) {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
return false;
|
|
580
|
-
});
|
|
540
|
+
async function isDefaultFunctionPrototypeAsync(func, prototypeProp) {
|
|
541
|
+
// The initial value of prototype on any newly-created Function instance is a new instance of
|
|
542
|
+
// Object, but with the own-property 'constructor' set to point back to the new function.
|
|
543
|
+
if (prototypeProp && prototypeProp.constructor === func) {
|
|
544
|
+
const descriptors = await getOwnPropertyDescriptors(prototypeProp);
|
|
545
|
+
return descriptors.length === 1 && descriptors[0].name === "constructor";
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
581
548
|
}
|
|
582
|
-
function createPropertyInfoAsync(descriptor, context, serialize, logInfo) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
return propertyInfo;
|
|
595
|
-
});
|
|
549
|
+
async function createPropertyInfoAsync(descriptor, context, serialize, logInfo) {
|
|
550
|
+
const propertyInfo = { hasValue: descriptor.value !== undefined };
|
|
551
|
+
propertyInfo.configurable = descriptor.configurable;
|
|
552
|
+
propertyInfo.enumerable = descriptor.enumerable;
|
|
553
|
+
propertyInfo.writable = descriptor.writable;
|
|
554
|
+
if (descriptor.get) {
|
|
555
|
+
propertyInfo.get = await getOrCreateEntryAsync(descriptor.get, undefined, context, serialize, logInfo);
|
|
556
|
+
}
|
|
557
|
+
if (descriptor.set) {
|
|
558
|
+
propertyInfo.set = await getOrCreateEntryAsync(descriptor.set, undefined, context, serialize, logInfo);
|
|
559
|
+
}
|
|
560
|
+
return propertyInfo;
|
|
596
561
|
}
|
|
597
562
|
function getOrCreateNameEntryAsync(name, capturedObjectProperties, context, serialize, logInfo) {
|
|
598
563
|
return getOrCreateEntryAsync(name, capturedObjectProperties, context, serialize, logInfo);
|
|
@@ -603,442 +568,423 @@ function getOrCreateNameEntryAsync(name, capturedObjectProperties, context, seri
|
|
|
603
568
|
* serialize out those specific properties. If `propNames` is not provided, or
|
|
604
569
|
* is empty, serialize out all properties.
|
|
605
570
|
*/
|
|
606
|
-
function getOrCreateEntryAsync(obj, capturedObjectProperties, context, serialize, logInfo) {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
}
|
|
639
|
-
return entry;
|
|
640
|
-
}
|
|
641
|
-
if (obj instanceof Function && utils_1.hasTrueBooleanMember(obj, "doNotCapture")) {
|
|
642
|
-
// If we get a function we're not supposed to capture, then actually just serialize
|
|
643
|
-
// out a function that will throw at runtime so the user can understand the problem
|
|
644
|
-
// better.
|
|
645
|
-
const funcName = obj.name || "anonymous";
|
|
646
|
-
const funcCode = getTrimmedFunctionCode(obj);
|
|
647
|
-
const message = `Function '${funcName}' cannot be called at runtime. ` +
|
|
648
|
-
`It can only be used at deployment time.\n\n${funcCode}`;
|
|
649
|
-
const errorFunc = () => {
|
|
650
|
-
throw new Error(message);
|
|
651
|
-
};
|
|
652
|
-
obj = errorFunc;
|
|
653
|
-
}
|
|
654
|
-
if (obj instanceof Function && utils_1.hasFunctionMember(obj, "captureReplacement")) {
|
|
655
|
-
// If we've defined a replacement function, then use that instead.
|
|
656
|
-
// This is best used in the case we'd serialize something with undefined runtime
|
|
657
|
-
// behavior. For example, we don't want to serialize out a function that will
|
|
658
|
-
// reference unset environment variables etc, so we provide an alternate function
|
|
659
|
-
// that will be called at runtime instead.
|
|
660
|
-
const funcToSerialize = obj.captureReplacement();
|
|
661
|
-
if (!(funcToSerialize instanceof Function)) {
|
|
662
|
-
throw new Error("captureReplacement must return a function");
|
|
663
|
-
}
|
|
664
|
-
obj = funcToSerialize;
|
|
665
|
-
}
|
|
666
|
-
// We may be processing recursive objects. Because of that, we preemptively put a placeholder
|
|
667
|
-
// entry in the cache. That way, if we encounter this obj again while recursing we can just
|
|
668
|
-
// return that placeholder.
|
|
669
|
-
entry = {};
|
|
670
|
-
context.cache.set(obj, entry);
|
|
671
|
-
yield dispatchAnyAsync();
|
|
571
|
+
async function getOrCreateEntryAsync(obj, capturedObjectProperties, context, serialize, logInfo) {
|
|
572
|
+
// Check if this is a special number that we cannot json serialize. Instead, we'll just inject
|
|
573
|
+
// the code necessary to represent the number on the other side. Note: we have to do this
|
|
574
|
+
// before we do *anything* else. This is because these special numbers don't even work in maps
|
|
575
|
+
// properly. So, if we lookup the value in a map, we may get the cached value for something
|
|
576
|
+
// else *entirely*. For example, 0 and -0 will map to the same entry.
|
|
577
|
+
if (typeof obj === "number") {
|
|
578
|
+
if (Object.is(obj, -0)) {
|
|
579
|
+
return { expr: "-0" };
|
|
580
|
+
}
|
|
581
|
+
if (Object.is(obj, NaN)) {
|
|
582
|
+
return { expr: "NaN" };
|
|
583
|
+
}
|
|
584
|
+
if (Object.is(obj, Infinity)) {
|
|
585
|
+
return { expr: "Infinity" };
|
|
586
|
+
}
|
|
587
|
+
if (Object.is(obj, -Infinity)) {
|
|
588
|
+
return { expr: "-Infinity" };
|
|
589
|
+
}
|
|
590
|
+
// Not special, just use normal json serialization.
|
|
591
|
+
return { json: obj };
|
|
592
|
+
}
|
|
593
|
+
// See if we have a cache hit. If yes, use the object as-is.
|
|
594
|
+
let entry = context.cache.get(obj);
|
|
595
|
+
if (entry) {
|
|
596
|
+
// Even though we've already serialized out this object, it might be the case
|
|
597
|
+
// that we serialized out a different set of properties than the current set
|
|
598
|
+
// we're being asked to serialize. So we have to make sure that all these props
|
|
599
|
+
// are actually serialized.
|
|
600
|
+
if (entry.object) {
|
|
601
|
+
await serializeObjectAsync();
|
|
602
|
+
}
|
|
672
603
|
return entry;
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
604
|
+
}
|
|
605
|
+
if (obj instanceof Function && utils_1.hasTrueBooleanMember(obj, "doNotCapture")) {
|
|
606
|
+
// If we get a function we're not supposed to capture, then actually just serialize
|
|
607
|
+
// out a function that will throw at runtime so the user can understand the problem
|
|
608
|
+
// better.
|
|
609
|
+
const funcName = obj.name || "anonymous";
|
|
610
|
+
const funcCode = getTrimmedFunctionCode(obj);
|
|
611
|
+
const message = `Function '${funcName}' cannot be called at runtime. ` +
|
|
612
|
+
`It can only be used at deployment time.\n\n${funcCode}`;
|
|
613
|
+
const errorFunc = () => {
|
|
614
|
+
throw new Error(message);
|
|
615
|
+
};
|
|
616
|
+
obj = errorFunc;
|
|
617
|
+
}
|
|
618
|
+
if (obj instanceof Function && utils_1.hasFunctionMember(obj, "captureReplacement")) {
|
|
619
|
+
// If we've defined a replacement function, then use that instead.
|
|
620
|
+
// This is best used in the case we'd serialize something with undefined runtime
|
|
621
|
+
// behavior. For example, we don't want to serialize out a function that will
|
|
622
|
+
// reference unset environment variables etc, so we provide an alternate function
|
|
623
|
+
// that will be called at runtime instead.
|
|
624
|
+
const funcToSerialize = obj.captureReplacement();
|
|
625
|
+
if (!(funcToSerialize instanceof Function)) {
|
|
626
|
+
throw new Error("captureReplacement must return a function");
|
|
627
|
+
}
|
|
628
|
+
obj = funcToSerialize;
|
|
629
|
+
}
|
|
630
|
+
// We may be processing recursive objects. Because of that, we preemptively put a placeholder
|
|
631
|
+
// entry in the cache. That way, if we encounter this obj again while recursing we can just
|
|
632
|
+
// return that placeholder.
|
|
633
|
+
entry = {};
|
|
634
|
+
context.cache.set(obj, entry);
|
|
635
|
+
await dispatchAnyAsync();
|
|
636
|
+
return entry;
|
|
637
|
+
function doNotCapture() {
|
|
638
|
+
if (!serialize(obj)) {
|
|
639
|
+
// caller explicitly does not want us to capture this value.
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
if (utils_1.hasTrueBooleanMember(obj, "doNotCapture")) {
|
|
643
|
+
// object has set itself as something that should not be captured.
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
if (obj instanceof Function && isDerivedNoCaptureConstructor(obj)) {
|
|
647
|
+
// this was a constructor that derived from something that should not be captured.
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
async function dispatchAnyAsync() {
|
|
653
|
+
const typeofObj = typeof obj;
|
|
654
|
+
if (doNotCapture()) {
|
|
655
|
+
// We do not want to capture this object. Explicit set .json to undefined so
|
|
656
|
+
// that we will see that the property is set and we will simply roundtrip this
|
|
657
|
+
// as the 'undefined value.
|
|
658
|
+
entry.json = undefined;
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
if (obj === undefined || obj === null || typeofObj === "boolean" || typeofObj === "string") {
|
|
662
|
+
// Serialize primitives as-is.
|
|
663
|
+
entry.json = obj;
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
else if (typeofObj === "bigint") {
|
|
667
|
+
entry.expr = `${obj}n`;
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
else if (obj instanceof RegExp) {
|
|
671
|
+
entry.regexp = obj;
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
const normalizedModuleName = await findNormalizedModuleNameAsync(obj);
|
|
675
|
+
if (normalizedModuleName) {
|
|
676
|
+
await captureModuleAsync(normalizedModuleName);
|
|
677
|
+
}
|
|
678
|
+
else if (obj instanceof Function) {
|
|
679
|
+
// Serialize functions recursively, and store them in a closure property.
|
|
680
|
+
entry.function = await analyzeFunctionInfoAsync(obj, context, serialize, logInfo);
|
|
681
|
+
}
|
|
682
|
+
else if (output_1.Output.isInstance(obj)) {
|
|
683
|
+
if (await output_1.isSecretOutput(obj)) {
|
|
684
|
+
context.containsSecrets = true;
|
|
677
685
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
686
|
+
entry.output = await createOutputEntryAsync(obj);
|
|
687
|
+
}
|
|
688
|
+
else if (obj instanceof Promise) {
|
|
689
|
+
const val = await obj;
|
|
690
|
+
entry.promise = await getOrCreateEntryAsync(val, undefined, context, serialize, logInfo);
|
|
691
|
+
}
|
|
692
|
+
else if (obj instanceof Array) {
|
|
693
|
+
// Recursively serialize elements of an array. Note: we use getOwnPropertyNames as the
|
|
694
|
+
// array may be sparse and we want to properly respect that when serializing.
|
|
695
|
+
entry.array = [];
|
|
696
|
+
for (const descriptor of await getOwnPropertyDescriptors(obj)) {
|
|
697
|
+
if (descriptor.name !== undefined && descriptor.name !== "length") {
|
|
698
|
+
entry.array[descriptor.name] = await getOrCreateEntryAsync(await getOwnPropertyAsync(obj, descriptor), undefined, context, serialize, logInfo);
|
|
699
|
+
}
|
|
681
700
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
701
|
+
// TODO(cyrusn): It feels weird that we're not examining any other descriptors of an
|
|
702
|
+
// array. For example, if someone put on a property with a symbolic name, we'd lose
|
|
703
|
+
// that here. Unlikely, but something we may need to handle in the future.
|
|
704
|
+
}
|
|
705
|
+
else if (Object.prototype.toString.call(obj) === "[object Arguments]") {
|
|
706
|
+
// From: https://stackoverflow.com/questions/7656280/how-do-i-check-whether-an-object-is-an-arguments-object-in-javascript
|
|
707
|
+
entry.array = [];
|
|
708
|
+
for (const elem of obj) {
|
|
709
|
+
entry.array.push(await getOrCreateEntryAsync(elem, undefined, context, serialize, logInfo));
|
|
685
710
|
}
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
// For all other objects, serialize out the properties we've been asked to serialize
|
|
714
|
+
// out.
|
|
715
|
+
await serializeObjectAsync();
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
async function serializeObjectAsync() {
|
|
719
|
+
// Serialize the set of property names asked for. If we discover that any of them
|
|
720
|
+
// use this/super, then go and reserialize all the properties.
|
|
721
|
+
const serializeAll = await serializeObjectWorkerAsync(capturedObjectProperties || []);
|
|
722
|
+
if (serializeAll) {
|
|
723
|
+
await serializeObjectWorkerAsync([]);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
// Returns 'true' if the caller (serializeObjectAsync) should call this again, but without any
|
|
727
|
+
// property filtering.
|
|
728
|
+
async function serializeObjectWorkerAsync(localCapturedPropertyChains) {
|
|
729
|
+
entry.object = entry.object || { env: new Map() };
|
|
730
|
+
if (localCapturedPropertyChains.length === 0) {
|
|
731
|
+
await serializeAllObjectPropertiesAsync(entry.object);
|
|
686
732
|
return false;
|
|
687
733
|
}
|
|
688
|
-
|
|
689
|
-
return
|
|
690
|
-
const typeofObj = typeof obj;
|
|
691
|
-
if (doNotCapture()) {
|
|
692
|
-
// We do not want to capture this object. Explicit set .json to undefined so
|
|
693
|
-
// that we will see that the property is set and we will simply roundtrip this
|
|
694
|
-
// as the 'undefined value.
|
|
695
|
-
entry.json = undefined;
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
if (obj === undefined || obj === null || typeofObj === "boolean" || typeofObj === "string") {
|
|
699
|
-
// Serialize primitives as-is.
|
|
700
|
-
entry.json = obj;
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
else if (typeofObj === "bigint") {
|
|
704
|
-
entry.expr = `${obj}n`;
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
else if (obj instanceof RegExp) {
|
|
708
|
-
entry.regexp = obj;
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
const normalizedModuleName = yield findNormalizedModuleNameAsync(obj);
|
|
712
|
-
if (normalizedModuleName) {
|
|
713
|
-
yield captureModuleAsync(normalizedModuleName);
|
|
714
|
-
}
|
|
715
|
-
else if (obj instanceof Function) {
|
|
716
|
-
// Serialize functions recursively, and store them in a closure property.
|
|
717
|
-
entry.function = yield analyzeFunctionInfoAsync(obj, context, serialize, logInfo);
|
|
718
|
-
}
|
|
719
|
-
else if (output_1.Output.isInstance(obj)) {
|
|
720
|
-
if (yield output_1.isSecretOutput(obj)) {
|
|
721
|
-
context.containsSecrets = true;
|
|
722
|
-
}
|
|
723
|
-
entry.output = yield createOutputEntryAsync(obj);
|
|
724
|
-
}
|
|
725
|
-
else if (obj instanceof Promise) {
|
|
726
|
-
const val = yield obj;
|
|
727
|
-
entry.promise = yield getOrCreateEntryAsync(val, undefined, context, serialize, logInfo);
|
|
728
|
-
}
|
|
729
|
-
else if (obj instanceof Array) {
|
|
730
|
-
// Recursively serialize elements of an array. Note: we use getOwnPropertyNames as the
|
|
731
|
-
// array may be sparse and we want to properly respect that when serializing.
|
|
732
|
-
entry.array = [];
|
|
733
|
-
for (const descriptor of yield getOwnPropertyDescriptors(obj)) {
|
|
734
|
-
if (descriptor.name !== undefined && descriptor.name !== "length") {
|
|
735
|
-
entry.array[descriptor.name] = yield getOrCreateEntryAsync(yield getOwnPropertyAsync(obj, descriptor), undefined, context, serialize, logInfo);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
// TODO(cyrusn): It feels weird that we're not examining any other descriptors of an
|
|
739
|
-
// array. For example, if someone put on a property with a symbolic name, we'd lose
|
|
740
|
-
// that here. Unlikely, but something we may need to handle in the future.
|
|
741
|
-
}
|
|
742
|
-
else if (Object.prototype.toString.call(obj) === "[object Arguments]") {
|
|
743
|
-
// From: https://stackoverflow.com/questions/7656280/how-do-i-check-whether-an-object-is-an-arguments-object-in-javascript
|
|
744
|
-
entry.array = [];
|
|
745
|
-
for (const elem of obj) {
|
|
746
|
-
entry.array.push(yield getOrCreateEntryAsync(elem, undefined, context, serialize, logInfo));
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
// For all other objects, serialize out the properties we've been asked to serialize
|
|
751
|
-
// out.
|
|
752
|
-
yield serializeObjectAsync();
|
|
753
|
-
}
|
|
754
|
-
});
|
|
734
|
+
else {
|
|
735
|
+
return await serializeSomeObjectPropertiesAsync(entry.object, localCapturedPropertyChains);
|
|
755
736
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
737
|
+
}
|
|
738
|
+
// Serializes out all the properties of this object. Used when we can't prove that
|
|
739
|
+
// only a subset of properties are used on this object.
|
|
740
|
+
async function serializeAllObjectPropertiesAsync(object) {
|
|
741
|
+
// we wanted to capture everything (including the prototype chain)
|
|
742
|
+
const descriptors = await getOwnPropertyDescriptors(obj);
|
|
743
|
+
for (const descriptor of descriptors) {
|
|
744
|
+
const keyEntry = await getOrCreateEntryAsync(getNameOrSymbol(descriptor), undefined, context, serialize, logInfo);
|
|
745
|
+
// We're about to recurse inside this object. In order to prevent infinite loops, put a
|
|
746
|
+
// dummy entry in the environment map. That way, if we hit this object again while
|
|
747
|
+
// recursing we won't try to generate this property.
|
|
748
|
+
//
|
|
749
|
+
// Note: we only stop recursing if we hit exactly our sentinel key (i.e. we're self
|
|
750
|
+
// recursive). We *do* want to recurse through the object again if we see it through
|
|
751
|
+
// non-recursive paths. That's because we might be hitting this object through one
|
|
752
|
+
// prop-name-path, but we created it the first time through another prop-name path.
|
|
753
|
+
//
|
|
754
|
+
// By processing the object again, we will add the different members we need.
|
|
755
|
+
if (object.env.has(keyEntry) && object.env.get(keyEntry) === undefined) {
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
object.env.set(keyEntry, undefined);
|
|
759
|
+
const propertyInfo = await createPropertyInfoAsync(descriptor, context, serialize, logInfo);
|
|
760
|
+
const prop = await getOwnPropertyAsync(obj, descriptor);
|
|
761
|
+
const valEntry = await getOrCreateEntryAsync(prop, undefined, context, serialize, logInfo);
|
|
762
|
+
// Now, replace the dummy entry with the actual one we want.
|
|
763
|
+
object.env.set(keyEntry, { info: propertyInfo, entry: valEntry });
|
|
765
764
|
}
|
|
766
|
-
//
|
|
767
|
-
//
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
return yield serializeSomeObjectPropertiesAsync(entry.object, localCapturedPropertyChains);
|
|
777
|
-
}
|
|
778
|
-
});
|
|
765
|
+
// If the object's __proto__ is not Object.prototype, then we have to capture what it
|
|
766
|
+
// actually is. On the other end, we'll use Object.create(deserializedProto) to set
|
|
767
|
+
// things up properly.
|
|
768
|
+
//
|
|
769
|
+
// We don't need to capture the prototype if the user is not capturing `this` either.
|
|
770
|
+
if (!object.proto) {
|
|
771
|
+
const proto = Object.getPrototypeOf(obj);
|
|
772
|
+
if (proto !== Object.prototype) {
|
|
773
|
+
object.proto = await getOrCreateEntryAsync(proto, undefined, context, serialize, logInfo);
|
|
774
|
+
}
|
|
779
775
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
// dummy entry in the environment map. That way, if we hit this object again while
|
|
790
|
-
// recursing we won't try to generate this property.
|
|
791
|
-
//
|
|
792
|
-
// Note: we only stop recursing if we hit exactly our sentinel key (i.e. we're self
|
|
793
|
-
// recursive). We *do* want to recurse through the object again if we see it through
|
|
794
|
-
// non-recursive paths. That's because we might be hitting this object through one
|
|
795
|
-
// prop-name-path, but we created it the first time through another prop-name path.
|
|
796
|
-
//
|
|
797
|
-
// By processing the object again, we will add the different members we need.
|
|
798
|
-
if (object.env.has(keyEntry) && object.env.get(keyEntry) === undefined) {
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
object.env.set(keyEntry, undefined);
|
|
802
|
-
const propertyInfo = yield createPropertyInfoAsync(descriptor, context, serialize, logInfo);
|
|
803
|
-
const prop = yield getOwnPropertyAsync(obj, descriptor);
|
|
804
|
-
const valEntry = yield getOrCreateEntryAsync(prop, undefined, context, serialize, logInfo);
|
|
805
|
-
// Now, replace the dummy entry with the actual one we want.
|
|
806
|
-
object.env.set(keyEntry, { info: propertyInfo, entry: valEntry });
|
|
807
|
-
}
|
|
808
|
-
// If the object's __proto__ is not Object.prototype, then we have to capture what it
|
|
809
|
-
// actually is. On the other end, we'll use Object.create(deserializedProto) to set
|
|
810
|
-
// things up properly.
|
|
811
|
-
//
|
|
812
|
-
// We don't need to capture the prototype if the user is not capturing `this` either.
|
|
813
|
-
if (!object.proto) {
|
|
814
|
-
const proto = Object.getPrototypeOf(obj);
|
|
815
|
-
if (proto !== Object.prototype) {
|
|
816
|
-
object.proto = yield getOrCreateEntryAsync(proto, undefined, context, serialize, logInfo);
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
});
|
|
776
|
+
}
|
|
777
|
+
// Serializes out only the subset of properties of this object that we have seen used
|
|
778
|
+
// and have recorded in localCapturedPropertyChains
|
|
779
|
+
async function serializeSomeObjectPropertiesAsync(object, localCapturedPropertyChains) {
|
|
780
|
+
// validate our invariants.
|
|
781
|
+
for (const chain of localCapturedPropertyChains) {
|
|
782
|
+
if (chain.infos.length === 0) {
|
|
783
|
+
throw new Error("Expected a non-empty chain.");
|
|
784
|
+
}
|
|
820
785
|
}
|
|
821
|
-
//
|
|
822
|
-
//
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
786
|
+
// we only want to capture a subset of properties. We can do this as long those
|
|
787
|
+
// properties don't somehow end up involving referencing "this" in an 'invoked'
|
|
788
|
+
// capacity (in which case we need to completely realize the object.
|
|
789
|
+
//
|
|
790
|
+
// this is slightly tricky as it's not obvious if a property is a getter/setter
|
|
791
|
+
// and this is implicitly invoked just by access it.
|
|
792
|
+
// Find the list of property names *directly* accessed off this object.
|
|
793
|
+
const propChainFirstNames = new Set(localCapturedPropertyChains.map((chain) => chain.infos[0].name));
|
|
794
|
+
// Now process each top level property name accessed off of this object in turn. For
|
|
795
|
+
// example, if we say "foo.bar.baz", "foo.bar.quux", "foo.ztesch", this would "bar" and
|
|
796
|
+
// "ztesch".
|
|
797
|
+
for (const propName of propChainFirstNames) {
|
|
798
|
+
// Get the named chains starting with this prop name. In the above example, if
|
|
799
|
+
// this was "bar", then we would get "[bar, baz]" and [bar, quux].
|
|
800
|
+
const propChains = localCapturedPropertyChains.filter((chain) => chain.infos[0].name === propName);
|
|
801
|
+
// Now, make an entry just for this name.
|
|
802
|
+
const keyEntry = await getOrCreateNameEntryAsync(propName, undefined, context, serialize, logInfo);
|
|
803
|
+
// We're about to recurse inside this object. In order to prevent infinite loops, put a
|
|
804
|
+
// dummy entry in the environment map. That way, if we hit this object again while
|
|
805
|
+
// recursing we won't try to generate this property.
|
|
806
|
+
//
|
|
807
|
+
// Note: we only stop recursing if we hit exactly our sentinel key (i.e. we're self
|
|
808
|
+
// recursive). We *do* want to recurse through the object again if we see it through
|
|
809
|
+
// non-recursive paths. That's because we might be hitting this object through one
|
|
810
|
+
// prop-name-path, but we created it the first time through another prop-name path.
|
|
811
|
+
//
|
|
812
|
+
// By processing the object again, we will add the different members we need.
|
|
813
|
+
if (object.env.has(keyEntry) && object.env.get(keyEntry) === undefined) {
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
object.env.set(keyEntry, undefined);
|
|
817
|
+
const objPropValue = await getPropertyAsync(obj, propName);
|
|
818
|
+
const propertyInfo = await getPropertyInfoAsync(obj, propName);
|
|
819
|
+
if (!propertyInfo) {
|
|
820
|
+
if (objPropValue !== undefined) {
|
|
821
|
+
throw new Error("Could not find property info for real property on object: " + propName);
|
|
830
822
|
}
|
|
831
|
-
//
|
|
832
|
-
//
|
|
833
|
-
//
|
|
823
|
+
// User code referenced a property not actually on the object at all.
|
|
824
|
+
// So to properly represent that, we don't place any information about
|
|
825
|
+
// this property on the object.
|
|
826
|
+
object.env.delete(keyEntry);
|
|
827
|
+
}
|
|
828
|
+
else {
|
|
829
|
+
// Determine what chained property names we're accessing off of this sub-property.
|
|
830
|
+
// if we have no sub property name chain, then indicate that with an empty array
|
|
831
|
+
// so that we capture the entire object.
|
|
834
832
|
//
|
|
835
|
-
//
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
// "ztesch".
|
|
842
|
-
for (const propName of propChainFirstNames) {
|
|
843
|
-
// Get the named chains starting with this prop name. In the above example, if
|
|
844
|
-
// this was "bar", then we would get "[bar, baz]" and [bar, quux].
|
|
845
|
-
const propChains = localCapturedPropertyChains.filter((chain) => chain.infos[0].name === propName);
|
|
846
|
-
// Now, make an entry just for this name.
|
|
847
|
-
const keyEntry = yield getOrCreateNameEntryAsync(propName, undefined, context, serialize, logInfo);
|
|
848
|
-
// We're about to recurse inside this object. In order to prevent infinite loops, put a
|
|
849
|
-
// dummy entry in the environment map. That way, if we hit this object again while
|
|
850
|
-
// recursing we won't try to generate this property.
|
|
851
|
-
//
|
|
852
|
-
// Note: we only stop recursing if we hit exactly our sentinel key (i.e. we're self
|
|
853
|
-
// recursive). We *do* want to recurse through the object again if we see it through
|
|
854
|
-
// non-recursive paths. That's because we might be hitting this object through one
|
|
855
|
-
// prop-name-path, but we created it the first time through another prop-name path.
|
|
856
|
-
//
|
|
857
|
-
// By processing the object again, we will add the different members we need.
|
|
858
|
-
if (object.env.has(keyEntry) && object.env.get(keyEntry) === undefined) {
|
|
859
|
-
continue;
|
|
860
|
-
}
|
|
861
|
-
object.env.set(keyEntry, undefined);
|
|
862
|
-
const objPropValue = yield getPropertyAsync(obj, propName);
|
|
863
|
-
const propertyInfo = yield getPropertyInfoAsync(obj, propName);
|
|
864
|
-
if (!propertyInfo) {
|
|
865
|
-
if (objPropValue !== undefined) {
|
|
866
|
-
throw new Error("Could not find property info for real property on object: " + propName);
|
|
867
|
-
}
|
|
868
|
-
// User code referenced a property not actually on the object at all.
|
|
869
|
-
// So to properly represent that, we don't place any information about
|
|
870
|
-
// this property on the object.
|
|
871
|
-
object.env.delete(keyEntry);
|
|
872
|
-
}
|
|
873
|
-
else {
|
|
874
|
-
// Determine what chained property names we're accessing off of this sub-property.
|
|
875
|
-
// if we have no sub property name chain, then indicate that with an empty array
|
|
876
|
-
// so that we capture the entire object.
|
|
877
|
-
//
|
|
878
|
-
// i.e.: if we started with a.b.c.d, and we've finally gotten to the point where
|
|
879
|
-
// we're serializing out the 'd' property, then we need to serialize it out fully
|
|
880
|
-
// since there are no more accesses off of it.
|
|
881
|
-
let nestedPropChains = propChains.map((chain) => ({ infos: chain.infos.slice(1) }));
|
|
882
|
-
if (nestedPropChains.some((chain) => chain.infos.length === 0)) {
|
|
883
|
-
nestedPropChains = [];
|
|
884
|
-
}
|
|
885
|
-
// Note: objPropValue can be undefined here. That's the case where the
|
|
886
|
-
// object does have the property, but the property is just set to the
|
|
887
|
-
// undefined value.
|
|
888
|
-
const valEntry = yield getOrCreateEntryAsync(objPropValue, nestedPropChains, context, serialize, logInfo);
|
|
889
|
-
const infos = propChains.map((chain) => chain.infos[0]);
|
|
890
|
-
if (propInfoUsesNonLexicalThis(infos, propertyInfo, valEntry)) {
|
|
891
|
-
// the referenced function captured `this`. Have to serialize out
|
|
892
|
-
// this entire object. Undo the work we did to just serialize out a
|
|
893
|
-
// few properties.
|
|
894
|
-
object.env.clear();
|
|
895
|
-
// Signal our caller to serialize the entire object.
|
|
896
|
-
return true;
|
|
897
|
-
}
|
|
898
|
-
// Now, replace the dummy entry with the actual one we want.
|
|
899
|
-
object.env.set(keyEntry, { info: propertyInfo, entry: valEntry });
|
|
900
|
-
}
|
|
833
|
+
// i.e.: if we started with a.b.c.d, and we've finally gotten to the point where
|
|
834
|
+
// we're serializing out the 'd' property, then we need to serialize it out fully
|
|
835
|
+
// since there are no more accesses off of it.
|
|
836
|
+
let nestedPropChains = propChains.map((chain) => ({ infos: chain.infos.slice(1) }));
|
|
837
|
+
if (nestedPropChains.some((chain) => chain.infos.length === 0)) {
|
|
838
|
+
nestedPropChains = [];
|
|
901
839
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
840
|
+
// Note: objPropValue can be undefined here. That's the case where the
|
|
841
|
+
// object does have the property, but the property is just set to the
|
|
842
|
+
// undefined value.
|
|
843
|
+
const valEntry = await getOrCreateEntryAsync(objPropValue, nestedPropChains, context, serialize, logInfo);
|
|
844
|
+
const infos = propChains.map((chain) => chain.infos[0]);
|
|
845
|
+
if (propInfoUsesNonLexicalThis(infos, propertyInfo, valEntry)) {
|
|
846
|
+
// the referenced function captured `this`. Have to serialize out
|
|
847
|
+
// this entire object. Undo the work we did to just serialize out a
|
|
848
|
+
// few properties.
|
|
849
|
+
object.env.clear();
|
|
850
|
+
// Signal our caller to serialize the entire object.
|
|
911
851
|
return true;
|
|
912
852
|
}
|
|
853
|
+
// Now, replace the dummy entry with the actual one we want.
|
|
854
|
+
object.env.set(keyEntry, { info: propertyInfo, entry: valEntry });
|
|
913
855
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
856
|
+
}
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
function propInfoUsesNonLexicalThis(capturedInfos, propertyInfo, valEntry) {
|
|
860
|
+
if (capturedInfos.some((info) => info.invoked)) {
|
|
861
|
+
// If the property was invoked, then we have to check if that property ends
|
|
862
|
+
// up using this/super. if so, then we actually have to serialize out this
|
|
863
|
+
// object entirely.
|
|
864
|
+
if (usesNonLexicalThis(valEntry)) {
|
|
918
865
|
return true;
|
|
919
866
|
}
|
|
920
|
-
return false;
|
|
921
867
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
const closurePropDescriptor = createClosurePropertyDescriptor(key, desc);
|
|
928
|
-
const propertyInfo = yield createPropertyInfoAsync(closurePropDescriptor, context, serialize, logInfo);
|
|
929
|
-
return propertyInfo;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
return undefined;
|
|
933
|
-
});
|
|
868
|
+
// if we're accessing a getter/setter, and that getter/setter uses
|
|
869
|
+
// `this`, then we need to serialize out this object entirely.
|
|
870
|
+
if (usesNonLexicalThis(propertyInfo ? propertyInfo.get : undefined) ||
|
|
871
|
+
usesNonLexicalThis(propertyInfo ? propertyInfo.set : undefined)) {
|
|
872
|
+
return true;
|
|
934
873
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
const isInNodeModules = nodeModulesSegmentIndex >= 0;
|
|
946
|
-
const isLocalModule = normalizedModuleName.startsWith(".") && !isInNodeModules;
|
|
947
|
-
if (utils_1.hasTrueBooleanMember(obj, "deploymentOnlyModule") || isLocalModule) {
|
|
948
|
-
// Try to serialize deployment-time and local-modules by-value.
|
|
949
|
-
//
|
|
950
|
-
// A deployment-only modules can't ever be successfully 'required' on the 'inside'. But
|
|
951
|
-
// parts of it may be serializable on the inside (i.e. pulumi.Config). So just try to
|
|
952
|
-
// capture this as a value. If it fails, we will give the user a good message.
|
|
953
|
-
// Otherwise, it may succeed if the user is only using a small part of the API that is
|
|
954
|
-
// serializable (like pulumi.Config)
|
|
955
|
-
//
|
|
956
|
-
// Or this is a reference to a local module (i.e. starts with '.', but isn't in
|
|
957
|
-
// /node_modules/ somewhere). Always capture the local module as a value. We do this
|
|
958
|
-
// because capturing as a reference (i.e. 'require(...)') has the following problems:
|
|
959
|
-
//
|
|
960
|
-
// 1. 'require(...)' will not work at run-time, because the user's code will not be
|
|
961
|
-
// serialized in a way that can actually be require'd (i.e. it is not ) serialized
|
|
962
|
-
// into any sort of appropriate file/folder structure for those 'require's to work.
|
|
963
|
-
//
|
|
964
|
-
// 2. if we stop here and capture as a reference, then we won't actually see and walk
|
|
965
|
-
// the code that exists in those local modules (direct or transitive). So we won't
|
|
966
|
-
// actually generate the serialized code for the functions or values in that module.
|
|
967
|
-
// This will also lead to code that simply will not work at run-time.
|
|
968
|
-
yield serializeObjectAsync();
|
|
969
|
-
}
|
|
970
|
-
else {
|
|
971
|
-
// If the path goes into node_modules, strip off the node_modules part. This will help
|
|
972
|
-
// ensure that lookup of those modules will work on the cloud-side even if the module
|
|
973
|
-
// isn't in a relative node_modules directory. For example, this happens with aws-sdk.
|
|
974
|
-
// It ends up actually being in /var/runtime/node_modules inside aws lambda.
|
|
975
|
-
//
|
|
976
|
-
// This also helps ensure that modules that are 'yarn link'ed are found properly. The
|
|
977
|
-
// module path we have may be on some non-local path due to the linking, however this
|
|
978
|
-
// will ensure that the module-name we load is a simple path that can be found off the
|
|
979
|
-
// node_modules that we actually upload with our serialized functions.
|
|
980
|
-
entry.module = isInNodeModules
|
|
981
|
-
? package_1.getModuleFromPath(upath.join(...moduleParts.slice(nodeModulesSegmentIndex + 1)))
|
|
982
|
-
: normalizedModuleName;
|
|
983
|
-
}
|
|
984
|
-
});
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
async function getPropertyInfoAsync(on, key) {
|
|
877
|
+
for (let current = on; current; current = Object.getPrototypeOf(current)) {
|
|
878
|
+
const desc = Object.getOwnPropertyDescriptor(current, key);
|
|
879
|
+
if (desc) {
|
|
880
|
+
const closurePropDescriptor = createClosurePropertyDescriptor(key, desc);
|
|
881
|
+
const propertyInfo = await createPropertyInfoAsync(closurePropDescriptor, context, serialize, logInfo);
|
|
882
|
+
return propertyInfo;
|
|
883
|
+
}
|
|
985
884
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
}
|
|
1035
|
-
// Everything looked good. Replace the `"value" -> undefined-Entry` mapping in this entry
|
|
1036
|
-
// with `"value" -> V-Entry`
|
|
1037
|
-
emptyOutputEntry.object.env.set(envEntry, { entry: valEntry });
|
|
1038
|
-
return emptyOutputEntry;
|
|
1039
|
-
});
|
|
885
|
+
return undefined;
|
|
886
|
+
}
|
|
887
|
+
function usesNonLexicalThis(localEntry) {
|
|
888
|
+
return localEntry?.function?.usesNonLexicalThis;
|
|
889
|
+
}
|
|
890
|
+
async function captureModuleAsync(normalizedModuleName) {
|
|
891
|
+
// Splitting on "/" is safe to do as this module name is already in a normalized form.
|
|
892
|
+
const moduleParts = normalizedModuleName.split("/");
|
|
893
|
+
const nodeModulesSegment = "node_modules";
|
|
894
|
+
const nodeModulesSegmentIndex = moduleParts.findIndex((v) => v === nodeModulesSegment);
|
|
895
|
+
const isInNodeModules = nodeModulesSegmentIndex >= 0;
|
|
896
|
+
const isLocalModule = normalizedModuleName.startsWith(".") && !isInNodeModules;
|
|
897
|
+
if (utils_1.hasTrueBooleanMember(obj, "deploymentOnlyModule") || isLocalModule) {
|
|
898
|
+
// Try to serialize deployment-time and local-modules by-value.
|
|
899
|
+
//
|
|
900
|
+
// A deployment-only modules can't ever be successfully 'required' on the 'inside'. But
|
|
901
|
+
// parts of it may be serializable on the inside (i.e. pulumi.Config). So just try to
|
|
902
|
+
// capture this as a value. If it fails, we will give the user a good message.
|
|
903
|
+
// Otherwise, it may succeed if the user is only using a small part of the API that is
|
|
904
|
+
// serializable (like pulumi.Config)
|
|
905
|
+
//
|
|
906
|
+
// Or this is a reference to a local module (i.e. starts with '.', but isn't in
|
|
907
|
+
// /node_modules/ somewhere). Always capture the local module as a value. We do this
|
|
908
|
+
// because capturing as a reference (i.e. 'require(...)') has the following problems:
|
|
909
|
+
//
|
|
910
|
+
// 1. 'require(...)' will not work at run-time, because the user's code will not be
|
|
911
|
+
// serialized in a way that can actually be require'd (i.e. it is not ) serialized
|
|
912
|
+
// into any sort of appropriate file/folder structure for those 'require's to work.
|
|
913
|
+
//
|
|
914
|
+
// 2. if we stop here and capture as a reference, then we won't actually see and walk
|
|
915
|
+
// the code that exists in those local modules (direct or transitive). So we won't
|
|
916
|
+
// actually generate the serialized code for the functions or values in that module.
|
|
917
|
+
// This will also lead to code that simply will not work at run-time.
|
|
918
|
+
await serializeObjectAsync();
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
// If the path goes into node_modules, strip off the node_modules part. This will help
|
|
922
|
+
// ensure that lookup of those modules will work on the cloud-side even if the module
|
|
923
|
+
// isn't in a relative node_modules directory. For example, this happens with aws-sdk.
|
|
924
|
+
// It ends up actually being in /var/runtime/node_modules inside aws lambda.
|
|
925
|
+
//
|
|
926
|
+
// This also helps ensure that modules that are 'yarn link'ed are found properly. The
|
|
927
|
+
// module path we have may be on some non-local path due to the linking, however this
|
|
928
|
+
// will ensure that the module-name we load is a simple path that can be found off the
|
|
929
|
+
// node_modules that we actually upload with our serialized functions.
|
|
930
|
+
entry.module = isInNodeModules
|
|
931
|
+
? package_1.getModuleFromPath(upath.join(...moduleParts.slice(nodeModulesSegmentIndex + 1)))
|
|
932
|
+
: normalizedModuleName;
|
|
1040
933
|
}
|
|
1041
|
-
}
|
|
934
|
+
}
|
|
935
|
+
async function createOutputEntryAsync(output) {
|
|
936
|
+
// We have an Output<T>. This is effectively just a wrapped value 'V' at deployment-time.
|
|
937
|
+
// We want to effectively generate a value post serialization effectively equivalent to `new
|
|
938
|
+
// SerializedOutput(V)`. It is tempting to want to just do the following:
|
|
939
|
+
//
|
|
940
|
+
// const val = await output.promise();
|
|
941
|
+
// return await getOrCreateEntryAsync(new SerializedOutput(val), undefined, context, serialize, logInfo);
|
|
942
|
+
//
|
|
943
|
+
// That seems like it would work. We're instantiating a SerializedOutput that will point at
|
|
944
|
+
// the underlying 'val' instance, and we're then serializing that entire object to be
|
|
945
|
+
// rehydrated on the other side.
|
|
946
|
+
//
|
|
947
|
+
// However, there's a subtlety here that we need to avoid. Specifically, in a world where
|
|
948
|
+
// we are never actually looking at real values, but are instead looking at 'Mirrors' of
|
|
949
|
+
// values, we never want to serialize something that actually points at a Mirror (like the
|
|
950
|
+
// SerializedOutput instance would). The reason for this is that if we then go to serialize
|
|
951
|
+
// the SerializedOutput, our Inspector APIs will hit the Mirror value and then get a Mirror
|
|
952
|
+
// for *that* Mirror. I.e. a Mirror<Mirror>. This is not what we want and will cause us to
|
|
953
|
+
// generate code that actually produces a Mirror object at cloud-runtime time instead of
|
|
954
|
+
// producing the real value.
|
|
955
|
+
//
|
|
956
|
+
// To avoid this, do something tricky. We first create an 'empty' SerializedObject. i.e.
|
|
957
|
+
//
|
|
958
|
+
// new SerializedOutput(undefined)
|
|
959
|
+
//
|
|
960
|
+
// We then serialize that instance (which we know must be an 'Object-Entry'). We then
|
|
961
|
+
// serialize out 'V', getting back the Entry for it. We then manually jam in that Entry
|
|
962
|
+
// into the Object-Entry for the SerializedOutput instance.
|
|
963
|
+
// First get the underlying value of the out, and create the environment entry for it.
|
|
964
|
+
const val = await output.promise();
|
|
965
|
+
const valEntry = await getOrCreateEntryAsync(val, undefined, context, serialize, logInfo);
|
|
966
|
+
// Now, create an empty-serialized output and create an environment entry for it. It
|
|
967
|
+
// will have a property 'value' that points to an Entry for 'undefined'.
|
|
968
|
+
const initializedEmptyOutput = new SerializedOutput(undefined);
|
|
969
|
+
const emptyOutputEntry = await getOrCreateEntryAsync(initializedEmptyOutput, undefined, context, serialize, logInfo);
|
|
970
|
+
// validate that we created the right sort of entry. It should be an Object-Entry with
|
|
971
|
+
// a single property called 'value' in it.
|
|
972
|
+
if (!emptyOutputEntry.object) {
|
|
973
|
+
throw new Error("Did not get an 'object' in the entry for a serialized output");
|
|
974
|
+
}
|
|
975
|
+
const envEntries = [...emptyOutputEntry.object.env.entries()];
|
|
976
|
+
if (envEntries.length !== 1) {
|
|
977
|
+
throw new Error("Expected SerializedOutput object to only have one property: " + envEntries.length);
|
|
978
|
+
}
|
|
979
|
+
const [envEntry] = envEntries[0];
|
|
980
|
+
if (envEntry.json !== "value") {
|
|
981
|
+
throw new Error("Expected SerializedOutput object sole property to be called 'value': " + envEntry.json);
|
|
982
|
+
}
|
|
983
|
+
// Everything looked good. Replace the `"value" -> undefined-Entry` mapping in this entry
|
|
984
|
+
// with `"value" -> V-Entry`
|
|
985
|
+
emptyOutputEntry.object.env.set(envEntry, { entry: valEntry });
|
|
986
|
+
return emptyOutputEntry;
|
|
987
|
+
}
|
|
1042
988
|
}
|
|
1043
989
|
/**
|
|
1044
990
|
* Returns true if this is a constructor derived from a `noCapture` constructor.
|
|
@@ -1059,23 +1005,21 @@ function getBuiltInModules() {
|
|
|
1059
1005
|
builtInModules = computeBuiltInModules();
|
|
1060
1006
|
}
|
|
1061
1007
|
return builtInModules;
|
|
1062
|
-
function computeBuiltInModules() {
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
return map;
|
|
1078
|
-
});
|
|
1008
|
+
async function computeBuiltInModules() {
|
|
1009
|
+
// These modules are built-in to Node.js, and are available via `require(...)`
|
|
1010
|
+
// but are not stored in the `require.cache`. They are guaranteed to be
|
|
1011
|
+
// available at the unqualified names listed below.
|
|
1012
|
+
const excludes = [
|
|
1013
|
+
"punycode",
|
|
1014
|
+
"sys",
|
|
1015
|
+
"wasi",
|
|
1016
|
+
];
|
|
1017
|
+
const builtInModuleNames = node_module_1.builtinModules.filter((name) => !name.startsWith("_") && !excludes.includes(name));
|
|
1018
|
+
const map = new Map();
|
|
1019
|
+
for (const name of builtInModuleNames) {
|
|
1020
|
+
map.set(require(name), name);
|
|
1021
|
+
}
|
|
1022
|
+
return map;
|
|
1079
1023
|
}
|
|
1080
1024
|
}
|
|
1081
1025
|
/**
|
|
@@ -1089,36 +1033,34 @@ function getBuiltInModules() {
|
|
|
1089
1033
|
* This function will also always return modules in a normalized form (i.e. all path components will
|
|
1090
1034
|
* be `/`).
|
|
1091
1035
|
*/
|
|
1092
|
-
function findNormalizedModuleNameAsync(obj) {
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
return "./" + modPath;
|
|
1111
|
-
}
|
|
1036
|
+
async function findNormalizedModuleNameAsync(obj) {
|
|
1037
|
+
// First, check the built-in modules
|
|
1038
|
+
const modules = await getBuiltInModules();
|
|
1039
|
+
const key = modules.get(obj);
|
|
1040
|
+
if (key) {
|
|
1041
|
+
return key;
|
|
1042
|
+
}
|
|
1043
|
+
// Next, check the Node module require cache, which will store cached values
|
|
1044
|
+
// of all non-built-in Node modules loaded by the program so far. _Note_: We
|
|
1045
|
+
// don't pre-compute this because the require cache will get populated
|
|
1046
|
+
// dynamically during execution.
|
|
1047
|
+
for (const path of Object.keys(require.cache)) {
|
|
1048
|
+
const c = require.cache[path];
|
|
1049
|
+
if (c !== undefined && c.exports === obj) {
|
|
1050
|
+
// Rewrite the path to be a local module reference relative to the current working
|
|
1051
|
+
// directory.
|
|
1052
|
+
const modPath = upath.relative(process.cwd(), path);
|
|
1053
|
+
return "./" + modPath;
|
|
1112
1054
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1055
|
+
}
|
|
1056
|
+
// Else, return that no global name is available for this object.
|
|
1057
|
+
return undefined;
|
|
1116
1058
|
}
|
|
1117
1059
|
function createClosurePropertyDescriptor(nameOrSymbol, descriptor) {
|
|
1118
1060
|
if (nameOrSymbol === undefined) {
|
|
1119
1061
|
throw new Error("Was not given a name or symbol");
|
|
1120
1062
|
}
|
|
1121
|
-
const copy =
|
|
1063
|
+
const copy = { ...descriptor };
|
|
1122
1064
|
if (typeof nameOrSymbol === "string") {
|
|
1123
1065
|
copy.name = nameOrSymbol;
|
|
1124
1066
|
}
|
|
@@ -1127,42 +1069,36 @@ function createClosurePropertyDescriptor(nameOrSymbol, descriptor) {
|
|
|
1127
1069
|
}
|
|
1128
1070
|
return copy;
|
|
1129
1071
|
}
|
|
1130
|
-
function getOwnPropertyDescriptors(obj) {
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
continue;
|
|
1140
|
-
}
|
|
1141
|
-
const descriptor = Object.getOwnPropertyDescriptor(obj, name);
|
|
1142
|
-
if (!descriptor) {
|
|
1143
|
-
throw new Error(`Could not get descriptor for ${name} on: ${JSON.stringify(obj)}`);
|
|
1144
|
-
}
|
|
1145
|
-
result.push(createClosurePropertyDescriptor(name, descriptor));
|
|
1072
|
+
async function getOwnPropertyDescriptors(obj) {
|
|
1073
|
+
const result = [];
|
|
1074
|
+
for (const name of Object.getOwnPropertyNames(obj)) {
|
|
1075
|
+
if (name === "__proto__") {
|
|
1076
|
+
// don't return prototypes here. If someone wants one, they should call
|
|
1077
|
+
// Object.getPrototypeOf. Note: this is the standard behavior of
|
|
1078
|
+
// Object.getOwnPropertyNames. However, the Inspector API returns these, and we want to
|
|
1079
|
+
// filter them out.
|
|
1080
|
+
continue;
|
|
1146
1081
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1082
|
+
const descriptor = Object.getOwnPropertyDescriptor(obj, name);
|
|
1083
|
+
if (!descriptor) {
|
|
1084
|
+
throw new Error(`Could not get descriptor for ${name} on: ${JSON.stringify(obj)}`);
|
|
1085
|
+
}
|
|
1086
|
+
result.push(createClosurePropertyDescriptor(name, descriptor));
|
|
1087
|
+
}
|
|
1088
|
+
for (const symbol of Object.getOwnPropertySymbols(obj)) {
|
|
1089
|
+
const descriptor = Object.getOwnPropertyDescriptor(obj, symbol);
|
|
1090
|
+
if (!descriptor) {
|
|
1091
|
+
throw new Error(`Could not get descriptor for symbol ${symbol.toString()} on: ${JSON.stringify(obj)}`);
|
|
1153
1092
|
}
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1093
|
+
result.push(createClosurePropertyDescriptor(symbol, descriptor));
|
|
1094
|
+
}
|
|
1095
|
+
return result;
|
|
1156
1096
|
}
|
|
1157
|
-
function getOwnPropertyAsync(obj, descriptor) {
|
|
1158
|
-
return
|
|
1159
|
-
return descriptor.get || descriptor.set ? undefined : obj[getNameOrSymbol(descriptor)];
|
|
1160
|
-
});
|
|
1097
|
+
async function getOwnPropertyAsync(obj, descriptor) {
|
|
1098
|
+
return descriptor.get || descriptor.set ? undefined : obj[getNameOrSymbol(descriptor)];
|
|
1161
1099
|
}
|
|
1162
|
-
function getPropertyAsync(obj, name) {
|
|
1163
|
-
return
|
|
1164
|
-
return obj[name];
|
|
1165
|
-
});
|
|
1100
|
+
async function getPropertyAsync(obj, name) {
|
|
1101
|
+
return obj[name];
|
|
1166
1102
|
}
|
|
1167
1103
|
function getNameOrSymbol(descriptor) {
|
|
1168
1104
|
if (descriptor.symbol === undefined && descriptor.name === undefined) {
|