@pulumi/pulumi 3.163.0-alpha.xdc88586 → 3.163.0-alpha.xe7c1e4f

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.
Files changed (70) hide show
  1. package/automation/cmd.js +92 -111
  2. package/automation/cmd.js.map +1 -1
  3. package/automation/localWorkspace.js +383 -476
  4. package/automation/localWorkspace.js.map +1 -1
  5. package/automation/remoteWorkspace.js +38 -55
  6. package/automation/remoteWorkspace.js.map +1 -1
  7. package/automation/server.js +6 -16
  8. package/automation/server.js.map +1 -1
  9. package/automation/stack.js +537 -608
  10. package/automation/stack.js.map +1 -1
  11. package/cmd/dynamic-provider/index.js +204 -235
  12. package/cmd/dynamic-provider/index.js.map +1 -1
  13. package/cmd/run/error.js +3 -3
  14. package/cmd/run/error.js.map +1 -1
  15. package/cmd/run/run.js +322 -336
  16. package/cmd/run/run.js.map +1 -1
  17. package/cmd/run/tracing.js +1 -2
  18. package/cmd/run/tracing.js.map +1 -1
  19. package/cmd/run-plugin/run.js +13 -17
  20. package/cmd/run-plugin/run.js.map +1 -1
  21. package/cmd/run-policy-pack/run.js +12 -16
  22. package/cmd/run-policy-pack/run.js.map +1 -1
  23. package/output.js +61 -78
  24. package/output.js.map +1 -1
  25. package/package.json +1 -1
  26. package/provider/experimental/analyzer.js +43 -46
  27. package/provider/experimental/analyzer.js.map +1 -1
  28. package/provider/experimental/provider.js +22 -36
  29. package/provider/experimental/provider.js.map +1 -1
  30. package/provider/server.js +359 -397
  31. package/provider/server.js.map +1 -1
  32. package/resource.js +29 -41
  33. package/resource.js.map +1 -1
  34. package/runtime/asyncIterableUtil.js +6 -17
  35. package/runtime/asyncIterableUtil.js.map +1 -1
  36. package/runtime/callbacks.js +254 -269
  37. package/runtime/callbacks.js.map +1 -1
  38. package/runtime/closure/codePaths.js +117 -135
  39. package/runtime/closure/codePaths.js.map +1 -1
  40. package/runtime/closure/createClosure.js +807 -871
  41. package/runtime/closure/createClosure.js.map +1 -1
  42. package/runtime/closure/parseFunction.js +2 -3
  43. package/runtime/closure/parseFunction.js.map +1 -1
  44. package/runtime/closure/serializeClosure.js +17 -30
  45. package/runtime/closure/serializeClosure.js.map +1 -1
  46. package/runtime/closure/v8.js +166 -190
  47. package/runtime/closure/v8.js.map +1 -1
  48. package/runtime/closure/v8Hooks.js +19 -34
  49. package/runtime/closure/v8Hooks.js.map +1 -1
  50. package/runtime/dependsOn.js +61 -80
  51. package/runtime/dependsOn.js.map +1 -1
  52. package/runtime/invoke.js +155 -170
  53. package/runtime/invoke.js.map +1 -1
  54. package/runtime/mocks.js +77 -96
  55. package/runtime/mocks.js.map +1 -1
  56. package/runtime/resource.js +238 -252
  57. package/runtime/resource.js.map +1 -1
  58. package/runtime/rpc.js +193 -215
  59. package/runtime/rpc.js.map +1 -1
  60. package/runtime/settings.js +70 -87
  61. package/runtime/settings.js.map +1 -1
  62. package/runtime/stack.js +111 -131
  63. package/runtime/stack.js.map +1 -1
  64. package/stackReference.js +39 -58
  65. package/stackReference.js.map +1 -1
  66. package/tsutils.js +1 -2
  67. package/tsutils.js.map +1 -1
  68. package/utils.js +2 -3
  69. package/utils.js.map +1 -1
  70. 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
- return __awaiter(this, void 0, void 0, function* () {
75
- // Initialize our Context object. It is effectively used to keep track of the work we're doing
76
- // as well as to keep track of the graph as we're walking it so we don't infinitely recurse.
77
- const context = {
78
- cache: new Map(),
79
- classInstanceMemberToSuperEntry: new Map(),
80
- classStaticMemberToSuperEntry: new Map(),
81
- frames: [],
82
- simpleFunctions: [],
83
- logResource,
84
- containsSecrets: false,
85
- };
86
- // Pre-populate our context's cache with global well-known values. These are values for things
87
- // like global.Number, or Function.prototype. Actually trying to serialize/deserialize these
88
- // would be a bad idea as that would mean once deserialized the objects wouldn't point to the
89
- // well known globals that were expected. Furthermore, most of these are almost certain to fail
90
- // to serialize due to hitting things like native-builtins.
91
- yield addEntriesForWellKnownGlobalObjectsAsync();
92
- // Make sure this func is in the cache itself as we may hit it again while recursing.
93
- const entry = {};
94
- context.cache.set(func, entry);
95
- entry.function = yield analyzeFunctionInfoAsync(func, context, serialize);
96
- return {
97
- func: entry.function,
98
- containsSecrets: context.containsSecrets,
99
- };
100
- function addEntriesForWellKnownGlobalObjectsAsync() {
101
- return __awaiter(this, void 0, void 0, function* () {
102
- const seenGlobalObjects = new Set();
103
- // Front load these guys so we prefer emitting code that references them directly,
104
- // instead of in unexpected ways. i.e. we'd prefer to have Number.prototype vs
105
- // Object.getPrototypeOf(Infinity) (even though they're the same thing.)
106
- yield addGlobalInfoAsync("Object");
107
- yield addGlobalInfoAsync("Function");
108
- yield addGlobalInfoAsync("Array");
109
- yield addGlobalInfoAsync("Number");
110
- yield addGlobalInfoAsync("String");
111
- for (let current = global; current; current = Object.getPrototypeOf(current)) {
112
- for (const key of Object.getOwnPropertyNames(current)) {
113
- // "GLOBAL" and "root" are deprecated and give warnings if you try to access them. So
114
- // just skip them.
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
- // Add information so that we can properly serialize over generators/iterators.
121
- yield addGeneratorEntriesAsync();
122
- yield addEntriesAsync(Symbol.iterator, "Symbol.iterator");
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
- function addEntriesAsync(val, emitExpr, recurse = false) {
125
- return __awaiter(this, void 0, void 0, function* () {
126
- if (val === undefined || val === null) {
127
- return;
128
- }
129
- // No need to add values twice. Ths can happen as we walk the global namespace and
130
- // sometimes run into multiple names aliasing to the same value.
131
- if (seenGlobalObjects.has(val)) {
132
- return;
133
- }
134
- seenGlobalObjects.add(val);
135
- context.cache.set(val, { expr: emitExpr });
136
- // For global objects, we want to recurse into them to find all
137
- // their properties and add entries for them. This allows us to
138
- // recognize these builtins when they have been aliased, for
139
- // example if we have `const isArray = Array.isArray` and capture
140
- // `isArray` in the function, we want to emit the expression
141
- // `Array.isArray` for the captured value.
142
- if (recurse) {
143
- for (const propName of Object.getOwnPropertyNames(val)) {
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
- return __awaiter(this, void 0, void 0, function* () {
202
- // logInfo = logInfo || func.name === "addHandler";
203
- const { file, line, column } = yield v8.getFunctionLocationAsync(func);
204
- let functionString = func.toString();
205
- const frame = { functionLocation: { func, file, line, column, functionString, isArrowFunction: false } };
206
- context.frames.push(frame);
207
- const result = yield serializeWorkerAsync();
208
- context.frames.pop();
209
- if (isSimple(result)) {
210
- const existingSimpleFunction = findSimpleFunction(result);
211
- if (existingSimpleFunction) {
212
- return existingSimpleFunction;
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 result;
217
- function isSimple(info) {
218
- return info.capturedValues.size === 0 && info.env.size === 0 && !info.proto;
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
- function findSimpleFunction(info) {
221
- for (const other of context.simpleFunctions) {
222
- if (other.code === info.code && other.usesNonLexicalThis === info.usesNonLexicalThis) {
223
- return other;
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
- return undefined;
242
+ catch (err) {
243
+ throwSerializationError(func, context, err.message);
244
+ }
227
245
  }
228
- function serializeWorkerAsync() {
229
- return __awaiter(this, void 0, void 0, function* () {
230
- const funcEntry = context.cache.get(func);
231
- if (!funcEntry) {
232
- throw new Error("Entry for this this function was not created by caller");
233
- }
234
- const capturedValues = new Map();
235
- // If the function is a native function, it is most likely the result of `Function.bind`.
236
- // We get the target function via the debugger API, along with the boundThis and boundArgs
237
- // values and create a new function using the target function's text representation and
238
- // rebind it. The boundThis and boundArgs values are manually captured.
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
- // TODO: This does not handle multiple binds correctly.
241
- // For example (function () { ... }).bind("a").bind("b") will fail to serialize correctly.
242
- if (parseFunctionModule.isNativeFunction(functionString)) {
243
- try {
244
- const { targetFunctionText, boundThisValue, boundArgsValues } = yield v8.getBoundFunction(func);
245
- const boundThis = "__pulumi_bound_this";
246
- const boundArgs = boundArgsValues.map((_, i) => `__pulumi_bound_arg_${i}`).join(", ");
247
- const boundArgsString = boundArgs.length > 0 ? `, ${boundArgs}` : "";
248
- functionString =
249
- `function (...args) {\n` +
250
- ` return (\n` +
251
- `${targetFunctionText}\n` +
252
- ` ).bind(${boundThis}${boundArgsString})(...args);\n` +
253
- `}`;
254
- const serializedName = yield getOrCreateNameEntryAsync("__pulumi_bound_this", undefined, context, serialize, logInfo);
255
- const serializedValue = yield getOrCreateEntryAsync(boundThisValue, undefined, context, serialize, logInfo);
256
- capturedValues.set(serializedName, { entry: serializedValue });
257
- for (const [i, boundArg] of boundArgsValues.entries()) {
258
- const name = yield getOrCreateNameEntryAsync(`__pulumi_bound_arg_${i}`, undefined, context, serialize, logInfo);
259
- const value = yield getOrCreateEntryAsync(boundArg, undefined, context, serialize, logInfo);
260
- capturedValues.set(name, { entry: value });
261
- }
262
- }
263
- catch (err) {
264
- throwSerializationError(func, context, err.message);
265
- }
266
- }
267
- // First, convert the js func object to a reasonable stringified version that we can operate on.
268
- // Importantly, this function helps massage all the different forms that V8 can produce to
269
- // either a "function (...) { ... }" form, or a "(...) => ..." form. In other words, all
270
- // 'funky' functions (like classes and whatnot) will be transformed to reasonable forms we can
271
- // process down the pipeline.
272
- const pf = require("./parseFunction");
273
- const [error, parsedFunction] = pf.parseFunction(functionString);
274
- if (error) {
275
- throwSerializationError(func, context, error);
276
- }
277
- const funcExprWithName = parsedFunction.funcExprWithName;
278
- const functionDeclarationName = parsedFunction.functionDeclarationName;
279
- frame.functionLocation.isArrowFunction = parsedFunction.isArrowFunction;
280
- yield processCapturedVariablesAsync(parsedFunction.capturedVariables.required, /*throwOnFailure:*/ true);
281
- yield processCapturedVariablesAsync(parsedFunction.capturedVariables.optional, /*throwOnFailure:*/ false);
282
- const functionInfo = {
283
- code: parsedFunction.funcExprWithoutName,
284
- capturedValues: capturedValues,
285
- env: new Map(),
286
- usesNonLexicalThis: parsedFunction.usesNonLexicalThis,
287
- name: functionDeclarationName,
288
- paramCount: func.length,
289
- };
290
- const proto = Object.getPrototypeOf(func);
291
- const isAsyncFunction = yield computeIsAsyncFunction(func);
292
- // Ensure that the prototype of this function is properly serialized as well. We only need to do
293
- // this for functions with a custom prototype (like a derived class constructor, or a function
294
- // that a user has explicit set the prototype for). Normal functions will pick up
295
- // Function.prototype by default, so we don't need to do anything for them.
296
- if (proto !== Function.prototype && !isAsyncFunction && !isDerivedNoCaptureConstructor(func)) {
297
- const protoEntry = yield getOrCreateEntryAsync(proto, undefined, context, serialize, logInfo);
298
- functionInfo.proto = protoEntry;
299
- if (functionString.startsWith("class ")) {
300
- // This was a class (which is effectively synonymous with a constructor-function).
301
- // We also know that it's a derived class because of the `proto !==
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
- // If this was a named function (literally, only a named function-expr or function-decl), then
346
- // place an entry in the environment that maps from this function name to the serialized
347
- // function we're creating. This ensures that recursive functions will call the right method.
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
- return functionInfo;
360
- function processCapturedVariablesAsync(capturedVariables, throwOnFailure) {
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
- function processCapturedVariableAsync(capturedVariables, name, value) {
404
- return __awaiter(this, void 0, void 0, function* () {
405
- const properties = capturedVariables.get(name);
406
- const serializedName = yield getOrCreateNameEntryAsync(name, undefined, context, serialize, logInfo);
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
- function processDerivedClassConstructorAsync(protoEntry) {
415
- return __awaiter(this, void 0, void 0, function* () {
416
- // Map from derived class' constructor and members, to the entry for the base class (i.e.
417
- // the base class' constructor function). We'll use this when serializing out those members
418
- // to rewrite any usages of 'super' appropriately.
419
- // We're processing the derived class constructor itself. Just map it directly to the base
420
- // class function.
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
- for (const descriptor of yield getOwnPropertyDescriptors(func.prototype)) {
432
- // instance method.
433
- const classProp = yield getOwnPropertyAsync(func.prototype, descriptor);
434
- addIfFunction(classProp, /*isStatic*/ false);
370
+ else {
371
+ context.frames.push({ capturedVariableName: name });
435
372
  }
436
- return;
437
- function addIfFunction(prop, isStatic) {
438
- if (prop instanceof Function) {
439
- const set = isStatic ? context.classStaticMemberToSuperEntry : context.classInstanceMemberToSuperEntry;
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
- return __awaiter(this, void 0, void 0, function* () {
449
- // Note, i can't think of a better way to determine this. This is particularly hard because we
450
- // can't even necessary refer to async function objects here as this code is rewritten by TS,
451
- // converting all async functions to non async functions.
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
- return __awaiter(this, void 0, void 0, function* () {
573
- // The initial value of prototype on any newly-created Function instance is a new instance of
574
- // Object, but with the own-property 'constructor' set to point back to the new function.
575
- if (prototypeProp && prototypeProp.constructor === func) {
576
- const descriptors = yield getOwnPropertyDescriptors(prototypeProp);
577
- return descriptors.length === 1 && descriptors[0].name === "constructor";
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
- return __awaiter(this, void 0, void 0, function* () {
584
- const propertyInfo = { hasValue: descriptor.value !== undefined };
585
- propertyInfo.configurable = descriptor.configurable;
586
- propertyInfo.enumerable = descriptor.enumerable;
587
- propertyInfo.writable = descriptor.writable;
588
- if (descriptor.get) {
589
- propertyInfo.get = yield getOrCreateEntryAsync(descriptor.get, undefined, context, serialize, logInfo);
590
- }
591
- if (descriptor.set) {
592
- propertyInfo.set = yield getOrCreateEntryAsync(descriptor.set, undefined, context, serialize, logInfo);
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
- return __awaiter(this, void 0, void 0, function* () {
608
- // Check if this is a special number that we cannot json serialize. Instead, we'll just inject
609
- // the code necessary to represent the number on the other side. Note: we have to do this
610
- // before we do *anything* else. This is because these special numbers don't even work in maps
611
- // properly. So, if we lookup the value in a map, we may get the cached value for something
612
- // else *entirely*. For example, 0 and -0 will map to the same entry.
613
- if (typeof obj === "number") {
614
- if (Object.is(obj, -0)) {
615
- return { expr: "-0" };
616
- }
617
- if (Object.is(obj, NaN)) {
618
- return { expr: "NaN" };
619
- }
620
- if (Object.is(obj, Infinity)) {
621
- return { expr: "Infinity" };
622
- }
623
- if (Object.is(obj, -Infinity)) {
624
- return { expr: "-Infinity" };
625
- }
626
- // Not special, just use normal json serialization.
627
- return { json: obj };
628
- }
629
- // See if we have a cache hit. If yes, use the object as-is.
630
- let entry = context.cache.get(obj);
631
- if (entry) {
632
- // Even though we've already serialized out this object, it might be the case
633
- // that we serialized out a different set of properties than the current set
634
- // we're being asked to serialize. So we have to make sure that all these props
635
- // are actually serialized.
636
- if (entry.object) {
637
- yield serializeObjectAsync();
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
- function doNotCapture() {
674
- if (!serialize(obj)) {
675
- // caller explicitly does not want us to capture this value.
676
- return true;
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
- if (utils_1.hasTrueBooleanMember(obj, "doNotCapture")) {
679
- // object has set itself as something that should not be captured.
680
- return true;
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
- if (obj instanceof Function && isDerivedNoCaptureConstructor(obj)) {
683
- // this was a constructor that derived from something that should not be captured.
684
- return true;
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
- function dispatchAnyAsync() {
689
- return __awaiter(this, void 0, void 0, function* () {
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
- function serializeObjectAsync() {
757
- return __awaiter(this, void 0, void 0, function* () {
758
- // Serialize the set of property names asked for. If we discover that any of them
759
- // use this/super, then go and reserialize all the properties.
760
- const serializeAll = yield serializeObjectWorkerAsync(capturedObjectProperties || []);
761
- if (serializeAll) {
762
- yield serializeObjectWorkerAsync([]);
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
- // Returns 'true' if the caller (serializeObjectAsync) should call this again, but without any
767
- // property filtering.
768
- function serializeObjectWorkerAsync(localCapturedPropertyChains) {
769
- return __awaiter(this, void 0, void 0, function* () {
770
- entry.object = entry.object || { env: new Map() };
771
- if (localCapturedPropertyChains.length === 0) {
772
- yield serializeAllObjectPropertiesAsync(entry.object);
773
- return false;
774
- }
775
- else {
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
- // Serializes out all the properties of this object. Used when we can't prove that
781
- // only a subset of properties are used on this object.
782
- function serializeAllObjectPropertiesAsync(object) {
783
- return __awaiter(this, void 0, void 0, function* () {
784
- // we wanted to capture everything (including the prototype chain)
785
- const descriptors = yield getOwnPropertyDescriptors(obj);
786
- for (const descriptor of descriptors) {
787
- const keyEntry = yield getOrCreateEntryAsync(getNameOrSymbol(descriptor), undefined, context, serialize, logInfo);
788
- // We're about to recurse inside this object. In order to prevent infinite loops, put a
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
- // Serializes out only the subset of properties of this object that we have seen used
822
- // and have recorded in localCapturedPropertyChains
823
- function serializeSomeObjectPropertiesAsync(object, localCapturedPropertyChains) {
824
- return __awaiter(this, void 0, void 0, function* () {
825
- // validate our invariants.
826
- for (const chain of localCapturedPropertyChains) {
827
- if (chain.infos.length === 0) {
828
- throw new Error("Expected a non-empty chain.");
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
- // we only want to capture a subset of properties. We can do this as long those
832
- // properties don't somehow end up involving referencing "this" in an 'invoked'
833
- // capacity (in which case we need to completely realize the object.
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
- // this is slightly tricky as it's not obvious if a property is a getter/setter
836
- // and this is implicitly invoked just by access it.
837
- // Find the list of property names *directly* accessed off this object.
838
- const propChainFirstNames = new Set(localCapturedPropertyChains.map((chain) => chain.infos[0].name));
839
- // Now process each top level property name accessed off of this object in turn. For
840
- // example, if we say "foo.bar.baz", "foo.bar.quux", "foo.ztesch", this would "bar" and
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
- return false;
903
- });
904
- }
905
- function propInfoUsesNonLexicalThis(capturedInfos, propertyInfo, valEntry) {
906
- if (capturedInfos.some((info) => info.invoked)) {
907
- // If the property was invoked, then we have to check if that property ends
908
- // up using this/super. if so, then we actually have to serialize out this
909
- // object entirely.
910
- if (usesNonLexicalThis(valEntry)) {
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
- // if we're accessing a getter/setter, and that getter/setter uses
915
- // `this`, then we need to serialize out this object entirely.
916
- if (usesNonLexicalThis(propertyInfo ? propertyInfo.get : undefined) ||
917
- usesNonLexicalThis(propertyInfo ? propertyInfo.set : undefined)) {
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
- function getPropertyInfoAsync(on, key) {
923
- return __awaiter(this, void 0, void 0, function* () {
924
- for (let current = on; current; current = Object.getPrototypeOf(current)) {
925
- const desc = Object.getOwnPropertyDescriptor(current, key);
926
- if (desc) {
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
- function usesNonLexicalThis(localEntry) {
936
- var _a;
937
- return (_a = localEntry === null || localEntry === void 0 ? void 0 : localEntry.function) === null || _a === void 0 ? void 0 : _a.usesNonLexicalThis;
938
- }
939
- function captureModuleAsync(normalizedModuleName) {
940
- return __awaiter(this, void 0, void 0, function* () {
941
- // Splitting on "/" is safe to do as this module name is already in a normalized form.
942
- const moduleParts = normalizedModuleName.split("/");
943
- const nodeModulesSegment = "node_modules";
944
- const nodeModulesSegmentIndex = moduleParts.findIndex((v) => v === nodeModulesSegment);
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
- function createOutputEntryAsync(output) {
987
- return __awaiter(this, void 0, void 0, function* () {
988
- // We have an Output<T>. This is effectively just a wrapped value 'V' at deployment-time.
989
- // We want to effectively generate a value post serialization effectively equivalent to `new
990
- // SerializedOutput(V)`. It is tempting to want to just do the following:
991
- //
992
- // const val = await output.promise();
993
- // return await getOrCreateEntryAsync(new SerializedOutput(val), undefined, context, serialize, logInfo);
994
- //
995
- // That seems like it would work. We're instantiating a SerializedOutput that will point at
996
- // the underlying 'val' instance, and we're then serializing that entire object to be
997
- // rehydrated on the other side.
998
- //
999
- // However, there's a subtlety here that we need to avoid. Specifically, in a world where
1000
- // we are never actually looking at real values, but are instead looking at 'Mirrors' of
1001
- // values, we never want to serialize something that actually points at a Mirror (like the
1002
- // SerializedOutput instance would). The reason for this is that if we then go to serialize
1003
- // the SerializedOutput, our Inspector APIs will hit the Mirror value and then get a Mirror
1004
- // for *that* Mirror. I.e. a Mirror<Mirror>. This is not what we want and will cause us to
1005
- // generate code that actually produces a Mirror object at cloud-runtime time instead of
1006
- // producing the real value.
1007
- //
1008
- // To avoid this, do something tricky. We first create an 'empty' SerializedObject. i.e.
1009
- //
1010
- // new SerializedOutput(undefined)
1011
- //
1012
- // We then serialize that instance (which we know must be an 'Object-Entry'). We then
1013
- // serialize out 'V', getting back the Entry for it. We then manually jam in that Entry
1014
- // into the Object-Entry for the SerializedOutput instance.
1015
- // First get the underlying value of the out, and create the environment entry for it.
1016
- const val = yield output.promise();
1017
- const valEntry = yield getOrCreateEntryAsync(val, undefined, context, serialize, logInfo);
1018
- // Now, create an empty-serialized output and create an environment entry for it. It
1019
- // will have a property 'value' that points to an Entry for 'undefined'.
1020
- const initializedEmptyOutput = new SerializedOutput(undefined);
1021
- const emptyOutputEntry = yield getOrCreateEntryAsync(initializedEmptyOutput, undefined, context, serialize, logInfo);
1022
- // validate that we created the right sort of entry. It should be an Object-Entry with
1023
- // a single property called 'value' in it.
1024
- if (!emptyOutputEntry.object) {
1025
- throw new Error("Did not get an 'object' in the entry for a serialized output");
1026
- }
1027
- const envEntries = [...emptyOutputEntry.object.env.entries()];
1028
- if (envEntries.length !== 1) {
1029
- throw new Error("Expected SerializedOutput object to only have one property: " + envEntries.length);
1030
- }
1031
- const [envEntry] = envEntries[0];
1032
- if (envEntry.json !== "value") {
1033
- throw new Error("Expected SerializedOutput object sole property to be called 'value': " + envEntry.json);
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
- return __awaiter(this, void 0, void 0, function* () {
1064
- // These modules are built-in to Node.js, and are available via `require(...)`
1065
- // but are not stored in the `require.cache`. They are guaranteed to be
1066
- // available at the unqualified names listed below.
1067
- const excludes = [
1068
- "punycode",
1069
- "sys",
1070
- "wasi",
1071
- ];
1072
- const builtInModuleNames = node_module_1.builtinModules.filter((name) => !name.startsWith("_") && !excludes.includes(name));
1073
- const map = new Map();
1074
- for (const name of builtInModuleNames) {
1075
- map.set(require(name), name);
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
- return __awaiter(this, void 0, void 0, function* () {
1094
- // First, check the built-in modules
1095
- const modules = yield getBuiltInModules();
1096
- const key = modules.get(obj);
1097
- if (key) {
1098
- return key;
1099
- }
1100
- // Next, check the Node module require cache, which will store cached values
1101
- // of all non-built-in Node modules loaded by the program so far. _Note_: We
1102
- // don't pre-compute this because the require cache will get populated
1103
- // dynamically during execution.
1104
- for (const path of Object.keys(require.cache)) {
1105
- const c = require.cache[path];
1106
- if (c !== undefined && c.exports === obj) {
1107
- // Rewrite the path to be a local module reference relative to the current working
1108
- // directory.
1109
- const modPath = upath.relative(process.cwd(), path);
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
- // Else, return that no global name is available for this object.
1114
- return undefined;
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 = Object.assign({}, descriptor);
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
- return __awaiter(this, void 0, void 0, function* () {
1132
- const result = [];
1133
- for (const name of Object.getOwnPropertyNames(obj)) {
1134
- if (name === "__proto__") {
1135
- // don't return prototypes here. If someone wants one, they should call
1136
- // Object.getPrototypeOf. Note: this is the standard behavior of
1137
- // Object.getOwnPropertyNames. However, the Inspector API returns these, and we want to
1138
- // filter them out.
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
- for (const symbol of Object.getOwnPropertySymbols(obj)) {
1148
- const descriptor = Object.getOwnPropertyDescriptor(obj, symbol);
1149
- if (!descriptor) {
1150
- throw new Error(`Could not get descriptor for symbol ${symbol.toString()} on: ${JSON.stringify(obj)}`);
1151
- }
1152
- result.push(createClosurePropertyDescriptor(symbol, descriptor));
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
- return result;
1155
- });
1093
+ result.push(createClosurePropertyDescriptor(symbol, descriptor));
1094
+ }
1095
+ return result;
1156
1096
  }
1157
- function getOwnPropertyAsync(obj, descriptor) {
1158
- return __awaiter(this, void 0, void 0, function* () {
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 __awaiter(this, void 0, void 0, function* () {
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) {