UNPKG

33.8 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.createOperationDescriptor = createOperationDescriptor;
7exports.attemptToMergeEquivalentObjectAssigns = attemptToMergeEquivalentObjectAssigns;
8exports.Generator = exports.TemporalObjectAssignEntry = exports.TemporalOperationEntry = exports.GeneratorEntry = void 0;
9
10var _index = require("../values/index.js");
11
12var _errors = require("../errors.js");
13
14var _index2 = require("../domains/index.js");
15
16var _invariant = _interopRequireDefault(require("../invariant.js"));
17
18var _completions = require("../completions.js");
19
20var _singletons = require("../singletons.js");
21
22var _PreludeGenerator = require("./PreludeGenerator.js");
23
24function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
26function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
27
28function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
29
30function createOperationDescriptor(type, data = {}) {
31 return {
32 data,
33 type
34 };
35}
36
37class GeneratorEntry {
38 constructor(realm) {
39 // We increment the index of every TemporalOperationEntry created.
40 // This should match up as a form of timeline value due to the tree-like
41 // structure we use to create entries during evaluation. For example,
42 // if all AST nodes in a BlockStatement resulted in a temporal operation
43 // for each AST node, then each would have a sequential index as to its
44 // position of how it was evaluated in the BlockSstatement.
45 this.index = realm.temporalEntryCounter++;
46 }
47
48 visit(callbacks, containingGenerator) {
49 (0, _invariant.default)(false, "GeneratorEntry is an abstract base class");
50 }
51
52 serialize(context) {
53 (0, _invariant.default)(false, "GeneratorEntry is an abstract base class");
54 }
55
56 getDependencies() {
57 (0, _invariant.default)(false, "GeneratorEntry is an abstract base class");
58 }
59
60 notEqualToAndDoesNotHappenBefore(entry) {
61 return this.index > entry.index;
62 }
63
64 notEqualToAndDoesNotHappenAfter(entry) {
65 return this.index < entry.index;
66 }
67
68}
69
70exports.GeneratorEntry = GeneratorEntry;
71
72class TemporalOperationEntry extends GeneratorEntry {
73 constructor(realm, args) {
74 super(realm);
75 Object.assign(this, args);
76
77 if (this.mutatesOnly !== undefined) {
78 (0, _invariant.default)(!this.isPure);
79
80 for (let arg of this.mutatesOnly) {
81 (0, _invariant.default)(this.args.includes(arg));
82 }
83 }
84
85 (0, _invariant.default)(this.operationDescriptor !== undefined);
86 }
87
88 toDisplayJson(depth) {
89 if (depth <= 0) return `TemporalOperation${this.index}`;
90
91 let obj = _objectSpread({
92 type: "TemporalOperation"
93 }, this);
94
95 delete obj.operationDescriptor;
96 return _singletons.Utils.verboseToDisplayJson(obj, depth);
97 }
98
99 visit(callbacks, containingGenerator) {
100 let omit = this.isPure && this.declared && callbacks.canOmit(this.declared);
101
102 if (!omit && this.declared && this.mutatesOnly !== undefined) {
103 omit = true;
104
105 for (let arg of this.mutatesOnly) {
106 if (!callbacks.canOmit(arg)) {
107 omit = false;
108 }
109 }
110 }
111
112 if (omit) {
113 callbacks.recordDelayedEntry(containingGenerator, this);
114 return false;
115 } else {
116 if (this.declared) callbacks.recordDeclaration(this.declared);
117
118 for (let i = 0, n = this.args.length; i < n; i++) this.args[i] = callbacks.visitEquivalentValue(this.args[i]);
119
120 if (this.dependencies) for (let dependency of this.dependencies) callbacks.visitGenerator(dependency, containingGenerator);
121 return true;
122 }
123 }
124
125 serialize(context) {
126 let omit = this.isPure && this.declared && context.canOmit(this.declared);
127
128 if (!omit && this.declared && this.mutatesOnly !== undefined) {
129 omit = true;
130
131 for (let arg of this.mutatesOnly) {
132 if (!context.canOmit(arg)) {
133 omit = false;
134 }
135 }
136 }
137
138 if (!omit) {
139 let nodes = this.args.map((boundArg, i) => context.serializeValue(boundArg));
140 let valuesToProcess = new Set();
141 let declaredId = this.declared !== undefined ? this.declared.intrinsicName : undefined;
142 let node = context.serializeOperationDescriptor(this.operationDescriptor, nodes, context, valuesToProcess, declaredId);
143
144 if (node.type === "BlockStatement") {
145 let block = node;
146 let statements = block.body;
147 if (statements.length === 0) return;
148
149 if (statements.length === 1) {
150 node = statements[0];
151 }
152 }
153
154 let declared = this.declared;
155
156 if (declared !== undefined && context.options.debugScopes) {
157 context.emit(context.serializeDebugScopeComment(declared));
158 }
159
160 context.emit(node);
161 context.processValues(valuesToProcess);
162 if (this.declared !== undefined) context.declare(this.declared);
163 }
164 }
165
166 getDependencies() {
167 return this.dependencies;
168 }
169
170}
171
172exports.TemporalOperationEntry = TemporalOperationEntry;
173
174class TemporalObjectAssignEntry extends TemporalOperationEntry {
175 visit(callbacks, containingGenerator) {
176 let declared = this.declared;
177
178 if (!(declared instanceof _index.AbstractObjectValue || declared instanceof _index.ObjectValue)) {
179 return false;
180 }
181
182 let realm = declared.$Realm; // The only optimization we attempt to do to Object.assign for now is merging of multiple entries
183 // into a new generator entry.
184
185 let result = attemptToMergeEquivalentObjectAssigns(realm, callbacks, this);
186
187 if (result instanceof TemporalObjectAssignEntry) {
188 let nextResult = result;
189
190 while (nextResult instanceof TemporalObjectAssignEntry) {
191 nextResult = attemptToMergeEquivalentObjectAssigns(realm, callbacks, result); // If we get back a TemporalObjectAssignEntry, then we have successfully merged a single
192 // Object.assign, but we may be able to merge more. So repeat the process.
193
194 if (nextResult instanceof TemporalObjectAssignEntry) {
195 result = nextResult;
196 }
197 } // We have an optimized temporal entry, so replace the current temporal
198 // entry and visit that entry instead.
199
200
201 this.args = result.args;
202 } else if (result === "POSSIBLE_OPTIMIZATION") {
203 callbacks.recordDelayedEntry(containingGenerator, this);
204 return false;
205 }
206
207 return super.visit(callbacks, containingGenerator);
208 }
209
210}
211
212exports.TemporalObjectAssignEntry = TemporalObjectAssignEntry;
213
214class ModifiedPropertyEntry extends GeneratorEntry {
215 constructor(realm, args) {
216 super(realm);
217 Object.assign(this, args);
218 }
219
220 toDisplayString() {
221 let propertyKey = this.propertyBinding.key;
222 let propertyKeyString = propertyKey instanceof _index.Value ? propertyKey.toDisplayString() : propertyKey;
223 (0, _invariant.default)(propertyKeyString !== undefined);
224 return `[ModifiedProperty ${propertyKeyString}]`;
225 }
226
227 serialize(context) {
228 let desc = this.propertyBinding.descriptor;
229 (0, _invariant.default)(desc === this.newDescriptor);
230 context.emitPropertyModification(this.propertyBinding);
231 }
232
233 visit(context, containingGenerator) {
234 (0, _invariant.default)(containingGenerator === this.containingGenerator, "This entry requires effects to be applied and may not be moved");
235 let desc = this.propertyBinding.descriptor;
236 (0, _invariant.default)(desc === this.newDescriptor);
237 context.visitModifiedProperty(this.propertyBinding);
238 return true;
239 }
240
241 getDependencies() {
242 return undefined;
243 }
244
245}
246
247class ModifiedBindingEntry extends GeneratorEntry {
248 constructor(realm, args) {
249 super(realm);
250 Object.assign(this, args);
251 }
252
253 toDisplayString() {
254 return `[ModifiedBinding ${this.modifiedBinding.name}]`;
255 }
256
257 serialize(context) {
258 context.emitBindingModification(this.modifiedBinding);
259 }
260
261 visit(context, containingGenerator) {
262 (0, _invariant.default)(containingGenerator === this.containingGenerator, "This entry requires effects to be applied and may not be moved");
263 context.visitModifiedBinding(this.modifiedBinding);
264 return true;
265 }
266
267 getDependencies() {
268 return undefined;
269 }
270
271}
272
273class ReturnValueEntry extends GeneratorEntry {
274 constructor(realm, generator, returnValue) {
275 super(realm);
276 this.returnValue = returnValue.promoteEmptyToUndefined();
277 this.containingGenerator = generator;
278 }
279
280 toDisplayString() {
281 return `[Return ${this.returnValue.toDisplayString()}]`;
282 }
283
284 visit(context, containingGenerator) {
285 (0, _invariant.default)(containingGenerator === this.containingGenerator, "This entry requires effects to be applied and may not be moved");
286 this.returnValue = context.visitEquivalentValue(this.returnValue);
287 return true;
288 }
289
290 serialize(context) {
291 context.emit(context.serializeReturnValue(this.returnValue));
292 }
293
294 getDependencies() {
295 return undefined;
296 }
297
298}
299
300class BindingAssignmentEntry extends GeneratorEntry {
301 constructor(realm, binding, value) {
302 super(realm);
303 this.binding = binding;
304 this.value = value;
305 }
306
307 toDisplayString() {
308 return `[BindingAssignment ${this.binding.name} = ${this.value.toDisplayString()}]`;
309 }
310
311 serialize(context) {
312 context.emit(context.serializeBindingAssignment(this.binding, this.value));
313 }
314
315 visit(context, containingGenerator) {
316 this.value = context.visitBindingAssignment(this.binding, this.value);
317 return true;
318 }
319
320 getDependencies() {
321 return undefined;
322 }
323
324}
325
326class Generator {
327 constructor(realm, name, pathConditions, effects) {
328 (0, _invariant.default)(realm.useAbstractInterpretation);
329 let realmPreludeGenerator = realm.preludeGenerator;
330 (0, _invariant.default)(realmPreludeGenerator);
331 this.preludeGenerator = realmPreludeGenerator;
332 this.realm = realm;
333 this._entries = [];
334 this.id = realm.nextGeneratorId++;
335 this._name = name;
336 this.effectsToApply = effects;
337 this.pathConditions = pathConditions;
338 }
339
340 toDisplayString() {
341 return _singletons.Utils.jsonToDisplayString(this, 2);
342 }
343
344 toDisplayJson(depth) {
345 if (depth <= 0) return `Generator${this.id}-${this._name}`;
346 return _singletons.Utils.verboseToDisplayJson(this, depth);
347 }
348
349 static _generatorOfEffects(realm, name, environmentRecordIdAfterGlobalCode, effects) {
350 let {
351 result,
352 generator,
353 modifiedBindings,
354 modifiedProperties,
355 createdObjects
356 } = effects;
357 let output = new Generator(realm, name, generator.pathConditions, effects);
358 output.appendGenerator(generator, generator._name);
359
360 for (let propertyBinding of modifiedProperties.keys()) {
361 let object = propertyBinding.object;
362 if (createdObjects.has(object)) continue; // Created Object's binding
363
364 if (_index.ObjectValue.refuseSerializationOnPropertyBinding(propertyBinding)) continue; // modification to internal state
365 // modifications to intrinsic objects are tracked in the generator
366
367 if (object.isIntrinsic()) continue;
368 output.emitPropertyModification(propertyBinding);
369 }
370
371 for (let modifiedBinding of modifiedBindings.keys()) {
372 // TODO #2430: Instead of looking at the environment ids, keep instead track of a createdEnvironmentRecords set,
373 // and only consider bindings here from environment records that already existed, or even better,
374 // ensure upstream that only such bindings are ever added to the modified-bindings set.
375 if (modifiedBinding.environment.id >= environmentRecordIdAfterGlobalCode) continue;
376 output.emitBindingModification(modifiedBinding);
377 }
378
379 if (result instanceof _index.UndefinedValue) return output;
380
381 if (result instanceof _completions.SimpleNormalCompletion) {
382 output.emitReturnValue(result.value);
383 } else if (result instanceof _completions.ThrowCompletion) {
384 output.emitThrow(result.value);
385 } else if (result instanceof _completions.JoinedNormalAndAbruptCompletions) {
386 let selector = c => c instanceof _completions.ThrowCompletion && c.value !== realm.intrinsics.__bottomValue && !(c.value instanceof _index.EmptyValue);
387
388 output.emitConditionalThrow(_singletons.Join.joinValuesOfSelectedCompletions(selector, result));
389 output.emitReturnValue(result.value);
390 } else {
391 (0, _invariant.default)(false);
392 }
393
394 return output;
395 } // Make sure to to fixup
396 // how to apply things around sets of things
397
398
399 static fromEffects(effects, realm, name, environmentRecordIdAfterGlobalCode = 0) {
400 return realm.withEffectsAppliedInGlobalEnv(this._generatorOfEffects.bind(this, realm, name, environmentRecordIdAfterGlobalCode), effects);
401 }
402
403 emitPropertyModification(propertyBinding) {
404 (0, _invariant.default)(this.effectsToApply !== undefined);
405 let desc = propertyBinding.descriptor;
406
407 if (desc !== undefined) {
408 let value = desc.value;
409
410 if (value instanceof _index.AbstractValue) {
411 if (value.kind === "conditional") {
412 let [c, x, y] = value.args;
413
414 if (c instanceof _index.AbstractValue && c.kind === "template for property name condition") {
415 let ydesc = Object.assign({}, desc, {
416 value: y
417 });
418 let yprop = Object.assign({}, propertyBinding, {
419 descriptor: ydesc
420 });
421 this.emitPropertyModification(yprop);
422 let xdesc = Object.assign({}, desc, {
423 value: x
424 });
425 let key = c.args[0];
426 (0, _invariant.default)(key instanceof _index.AbstractValue);
427 let xprop = Object.assign({}, propertyBinding, {
428 key,
429 descriptor: xdesc
430 });
431 this.emitPropertyModification(xprop);
432 return;
433 }
434 } else if (value.kind === "template for prototype member expression") {
435 return;
436 }
437 }
438 }
439
440 this._entries.push(new ModifiedPropertyEntry(this.realm, {
441 propertyBinding,
442 newDescriptor: desc,
443 containingGenerator: this
444 }));
445 }
446
447 emitBindingModification(modifiedBinding) {
448 (0, _invariant.default)(this.effectsToApply !== undefined);
449
450 this._entries.push(new ModifiedBindingEntry(this.realm, {
451 modifiedBinding,
452 containingGenerator: this
453 }));
454 }
455
456 emitReturnValue(result) {
457 this._entries.push(new ReturnValueEntry(this.realm, this, result));
458 }
459
460 getName() {
461 return `${this._name}(#${this.id})`;
462 }
463
464 empty() {
465 return this._entries.length === 0;
466 }
467
468 emitGlobalDeclaration(key, value) {
469 this.preludeGenerator.declaredGlobals.add(key);
470 if (!(value instanceof _index.UndefinedValue)) this.emitGlobalAssignment(key, value);
471 }
472
473 emitGlobalAssignment(key, value) {
474 this._addEntry({
475 args: [value, new _index.StringValue(this.realm, key)],
476 operationDescriptor: createOperationDescriptor("GLOBAL_ASSIGNMENT")
477 });
478 }
479
480 emitConcreteModel(key, value) {
481 this._addEntry({
482 args: [(0, _singletons.concretize)(this.realm, value), new _index.StringValue(this.realm, key)],
483 operationDescriptor: createOperationDescriptor("CONCRETE_MODEL")
484 });
485 }
486
487 emitGlobalDelete(key) {
488 this._addEntry({
489 args: [new _index.StringValue(this.realm, key)],
490 operationDescriptor: createOperationDescriptor("GLOBAL_DELETE")
491 });
492 }
493
494 emitBindingAssignment(binding, value) {
495 this._entries.push(new BindingAssignmentEntry(this.realm, binding, value));
496 }
497
498 emitPropertyAssignment(object, key, value) {
499 if (object instanceof _index.ObjectValue && object.refuseSerialization) {
500 return;
501 }
502
503 if (typeof key === "string") {
504 key = new _index.StringValue(this.realm, key);
505 }
506
507 this._addEntry({
508 args: [object, value, key],
509 operationDescriptor: createOperationDescriptor("EMIT_PROPERTY_ASSIGNMENT", {
510 value
511 })
512 });
513 }
514
515 emitDefineProperty(object, key, desc, isDescChanged = true) {
516 if (object.refuseSerialization) return;
517
518 if (desc.enumerable && desc.configurable && desc.writable && desc.value && !isDescChanged) {
519 let descValue = desc.value;
520 (0, _invariant.default)(descValue instanceof _index.Value);
521 this.emitPropertyAssignment(object, key, descValue);
522 } else {
523 desc = Object.assign({}, desc);
524 let descValue = desc.value || object.$Realm.intrinsics.undefined;
525 (0, _invariant.default)(descValue instanceof _index.Value);
526
527 this._addEntry({
528 args: [new _index.StringValue(this.realm, key), object, descValue, desc.get || object.$Realm.intrinsics.undefined, desc.set || object.$Realm.intrinsics.undefined],
529 operationDescriptor: createOperationDescriptor("DEFINE_PROPERTY", {
530 object,
531 desc
532 })
533 });
534 }
535 }
536
537 emitPropertyDelete(object, key) {
538 if (object.refuseSerialization) return;
539
540 this._addEntry({
541 args: [object, new _index.StringValue(this.realm, key)],
542 operationDescriptor: createOperationDescriptor("PROPERTY_DELETE")
543 });
544 }
545
546 emitCall(callFunctionRef, args) {
547 this._addEntry({
548 args,
549 operationDescriptor: createOperationDescriptor("EMIT_CALL", {
550 callFunctionRef
551 })
552 });
553 }
554
555 emitConsoleLog(method, args) {
556 this._addEntry({
557 args: [new _index.StringValue(this.realm, method), ...args.map(v => typeof v === "string" ? new _index.StringValue(this.realm, v) : v)],
558 operationDescriptor: createOperationDescriptor("CONSOLE_LOG")
559 });
560 } // test must be a temporal value, which means that it must have a defined intrinsicName
561
562
563 emitDoWhileStatement(test, body) {
564 this._addEntry({
565 args: [],
566 operationDescriptor: createOperationDescriptor("DO_WHILE", {
567 generator: body,
568 value: test
569 }),
570 dependencies: [body]
571 });
572 }
573
574 emitConditionalThrow(value) {
575 if (value instanceof _index.EmptyValue) return;
576
577 this._issueThrowCompilerDiagnostic(value);
578
579 this._addEntry({
580 args: [value],
581 operationDescriptor: createOperationDescriptor("CONDITIONAL_THROW", {
582 value
583 })
584 });
585 }
586
587 _issueThrowCompilerDiagnostic(value) {
588 let message = "Program may terminate with exception";
589
590 if (value instanceof _index.ObjectValue) {
591 let object = value;
592 let objectMessage = this.realm.evaluateWithUndo(() => object._SafeGetDataPropertyValue("message"));
593 if (objectMessage instanceof _index.StringValue) message += `: ${objectMessage.value}`;
594 const objectStack = this.realm.evaluateWithUndo(() => object._SafeGetDataPropertyValue("stack"));
595 if (objectStack instanceof _index.StringValue) message += `
596 ${objectStack.value}`;
597 }
598
599 const diagnostic = new _errors.CompilerDiagnostic(message, value.expressionLocation, "PP0023", "Warning");
600 this.realm.handleError(diagnostic);
601 }
602
603 emitThrow(value) {
604 this._issueThrowCompilerDiagnostic(value);
605
606 this.emitStatement([value], createOperationDescriptor("THROW"));
607 } // Checks the full set of possible concrete values as well as typeof
608 // for any AbstractValues
609 // e.g: (obj.property !== undefined && typeof obj.property !== "object")
610 // NB: if the type of the AbstractValue is top, skips the invariant
611
612
613 emitFullInvariant(object, key, value) {
614 if (object.refuseSerialization) return;
615
616 if (value instanceof _index.AbstractValue) {
617 let isTop = false;
618 let concreteComparisons = [];
619 let typeComparisons = new Set();
620
621 function populateComparisonsLists(absValue) {
622 if (absValue.kind === "abstractConcreteUnion") {
623 // recurse
624 for (let nestedValue of absValue.args) if (nestedValue instanceof _index.ConcreteValue) {
625 concreteComparisons.push(nestedValue);
626 } else {
627 (0, _invariant.default)(nestedValue instanceof _index.AbstractValue);
628 populateComparisonsLists(nestedValue);
629 }
630 } else if (absValue.getType() === _index.Value) {
631 isTop = true;
632 } else {
633 typeComparisons.add(absValue.getType());
634 }
635 }
636
637 populateComparisonsLists(value); // No point in doing the invariant if we don't know the type
638 // of one of the nested abstract values
639
640 if (isTop) {
641 return;
642 } else {
643 this._emitInvariant([new _index.StringValue(this.realm, key), value, value], createOperationDescriptor("FULL_INVARIANT_ABSTRACT", {
644 concreteComparisons,
645 typeComparisons
646 }), createOperationDescriptor("INVARIANT_APPEND"));
647 }
648 } else if (value instanceof _index.FunctionValue) {
649 // We do a special case for functions,
650 // as we like to use concrete functions in the model to model abstract behaviors.
651 // These concrete functions do not have the right identity.
652 this._emitInvariant([new _index.StringValue(this.realm, key), object, value, object], createOperationDescriptor("FULL_INVARIANT_FUNCTION"), createOperationDescriptor("INVARIANT_APPEND"));
653 } else {
654 this._emitInvariant([new _index.StringValue(this.realm, key), object, value, object], createOperationDescriptor("FULL_INVARIANT"), createOperationDescriptor("INVARIANT_APPEND"));
655 }
656 }
657
658 emitPropertyInvariant(object, key, state) {
659 if (object.refuseSerialization) return;
660
661 this._emitInvariant([new _index.StringValue(this.realm, key), object, object], createOperationDescriptor("PROPERTY_INVARIANT", {
662 state
663 }), createOperationDescriptor("INVARIANT_APPEND"));
664 }
665
666 _emitInvariant(args, violationConditionOperationDescriptor, appendLastToInvariantOperationDescriptor) {
667 (0, _invariant.default)(this.realm.invariantLevel > 0);
668 let invariantOperationDescriptor = createOperationDescriptor("INVARIANT", {
669 appendLastToInvariantOperationDescriptor,
670 violationConditionOperationDescriptor
671 });
672
673 this._addEntry({
674 args,
675 operationDescriptor: invariantOperationDescriptor
676 });
677 }
678
679 emitCallAndCaptureResult(types, values, callFunctionRef, args, kind) {
680 return this.deriveAbstract(types, values, args, createOperationDescriptor("EMIT_CALL_AND_CAPTURE_RESULT", {
681 callFunctionRef
682 }), {
683 kind
684 });
685 }
686
687 emitStatement(args, operationDescriptor) {
688 (0, _invariant.default)(typeof operationDescriptor !== "function");
689
690 this._addEntry({
691 args,
692 operationDescriptor
693 });
694 }
695
696 emitVoidExpression(types, values, args, operationDescriptor) {
697 this._addEntry({
698 args,
699 operationDescriptor
700 });
701
702 return this.realm.intrinsics.undefined;
703 }
704
705 emitForInStatement(o, lh, sourceObject, targetObject, boundName) {
706 this._addEntry({
707 // duplicate args to ensure refcount > 1
708 args: [o, targetObject, sourceObject, targetObject, sourceObject],
709 operationDescriptor: createOperationDescriptor("FOR_IN", {
710 boundName,
711 lh
712 })
713 });
714 }
715
716 deriveConcreteObject(buildValue, args, operationDescriptor, optionalArgs) {
717 let id = this.preludeGenerator.nameGenerator.generate("derived");
718 let value = buildValue(id);
719 value.intrinsicNameGenerated = true;
720 value._isScopedTemplate = true; // because this object doesn't exist ahead of time, and the visitor would otherwise declare it in the common scope
721
722 (0, _invariant.default)(value.intrinsicName === id);
723
724 this._addDerivedEntry({
725 isPure: optionalArgs ? optionalArgs.isPure : undefined,
726 declared: value,
727 args,
728 operationDescriptor
729 });
730
731 return value;
732 }
733
734 deriveAbstract(types, values, args, operationDescriptor, optionalArgs) {
735 let id = this.preludeGenerator.nameGenerator.generate("derived");
736 let options = {};
737 if (optionalArgs && optionalArgs.kind !== undefined) options.kind = optionalArgs.kind;
738 if (optionalArgs && optionalArgs.shape !== undefined) options.shape = optionalArgs.shape;
739 let Constructor = _index.Value.isTypeCompatibleWith(types.getType(), _index.ObjectValue) ? _index.AbstractObjectValue : _index.AbstractValue;
740 let res = new Constructor(this.realm, types, values, 1735003607742176 + this.realm.derivedIds.size, [], createOperationDescriptor("IDENTIFIER", {
741 id
742 }), options);
743 res.intrinsicName = id;
744
745 this._addDerivedEntry({
746 isPure: optionalArgs ? optionalArgs.isPure : undefined,
747 declared: res,
748 args,
749 operationDescriptor,
750 mutatesOnly: optionalArgs ? optionalArgs.mutatesOnly : undefined
751 });
752
753 let type = types.getType();
754 if (optionalArgs && optionalArgs.skipInvariant) return res;
755 let typeofString;
756 if (type instanceof _index.FunctionValue) typeofString = "function";else if (type === _index.UndefinedValue) (0, _invariant.default)(false);else if (type === _index.NullValue) (0, _invariant.default)(false);else if (type === _index.StringValue) typeofString = "string";else if (type === _index.BooleanValue) typeofString = "boolean";else if (type === _index.NumberValue) typeofString = "number";else if (type === _index.IntegralValue) typeofString = "number";else if (type === _index.SymbolValue) typeofString = "symbol";else if (type === _index.ObjectValue) typeofString = "object";
757
758 if (typeofString !== undefined && this.realm.invariantLevel >= 1) {
759 // Verify that the types are as expected, a failure of this invariant
760 // should mean the model is wrong.
761 this._emitInvariant([new _index.StringValue(this.realm, typeofString), res, res], createOperationDescriptor("DERIVED_ABSTRACT_INVARIANT"), createOperationDescriptor("SINGLE_ARG"));
762 }
763
764 return res;
765 }
766
767 visit(callbacks) {
768 let visitFn = () => {
769 for (let entry of this._entries) entry.visit(callbacks, this);
770
771 return null;
772 };
773
774 if (this.effectsToApply) {
775 this.realm.withEffectsAppliedInGlobalEnv(visitFn, this.effectsToApply);
776 } else {
777 visitFn();
778 }
779 }
780
781 serialize(context) {
782 let serializeFn = () => {
783 context.initGenerator(this);
784
785 for (let entry of this._entries) entry.serialize(context);
786
787 context.finalizeGenerator(this);
788 return null;
789 };
790
791 if (this.effectsToApply) {
792 this.realm.withEffectsAppliedInGlobalEnv(serializeFn, this.effectsToApply);
793 } else {
794 serializeFn();
795 }
796 }
797
798 getDependencies() {
799 let res = [];
800
801 for (let entry of this._entries) {
802 let dependencies = entry.getDependencies();
803 if (dependencies !== undefined) res.push(...dependencies);
804 }
805
806 return res;
807 }
808
809 _addEntry(entryArgs) {
810 let entry;
811 let operationDescriptor = entryArgs.operationDescriptor;
812
813 if (operationDescriptor && operationDescriptor.type === "OBJECT_ASSIGN") {
814 entry = new TemporalObjectAssignEntry(this.realm, entryArgs);
815 } else {
816 entry = new TemporalOperationEntry(this.realm, entryArgs);
817 }
818
819 this.realm.saveTemporalGeneratorEntryArgs(entry);
820
821 this._entries.push(entry);
822
823 return entry;
824 }
825
826 _addDerivedEntry(entryArgs) {
827 let declared = entryArgs.declared;
828 (0, _invariant.default)(declared !== undefined);
829 let id = declared.intrinsicName;
830 (0, _invariant.default)(id !== undefined);
831
832 let entry = this._addEntry(entryArgs);
833
834 this.realm.derivedIds.set(id, entry);
835 }
836
837 appendGenerator(other, leadingComment) {
838 (0, _invariant.default)(other !== this);
839 (0, _invariant.default)(other.realm === this.realm);
840 (0, _invariant.default)(other.preludeGenerator === this.preludeGenerator);
841 (0, _invariant.default)(other.effectsToApply === undefined);
842 if (other.empty()) return;
843
844 this._entries.push(...other._entries);
845 }
846
847 joinGenerators(joinCondition, generator1, generator2) {
848 (0, _invariant.default)(generator1 !== this && generator2 !== this && generator1 !== generator2);
849 if (generator1.empty() && generator2.empty()) return;
850 let generators = [generator1, generator2];
851
852 this._addEntry({
853 args: [joinCondition],
854 operationDescriptor: createOperationDescriptor("JOIN_GENERATORS", {
855 generators
856 }),
857 dependencies: generators
858 });
859 }
860
861}
862
863exports.Generator = Generator;
864
865// This function attempts to optimize Object.assign calls, by merging mulitple
866// calls into one another where possible. For example:
867//
868// var a = Object.assign({}, someAbstact);
869// var b = Object.assign({}, a);
870//
871// Becomes:
872// var b = Object.assign({}, someAbstract, a);
873//
874function attemptToMergeEquivalentObjectAssigns(realm, callbacks, temporalOperationEntry) {
875 let args = temporalOperationEntry.args; // If we are Object.assigning 2 or more args
876
877 if (args.length < 2) {
878 return "NO_OPTIMIZATION";
879 }
880
881 let to = args[0]; // Then scan through the args after the "to" of this Object.assign, to see if any
882 // other sources are the "to" of a previous Object.assign call
883
884 loopThroughArgs: for (let i = 1; i < args.length; i++) {
885 let possibleOtherObjectAssignTo = args[i]; // Ensure that the "to" value can be omitted
886 // Note: this check is still somewhat fragile and depends on the visiting order
887 // but it's not a functional problem right now and can be better addressed at a
888 // later point.
889
890 if (!callbacks.canOmit(possibleOtherObjectAssignTo)) {
891 continue;
892 } // Check if the "to" was definitely an Object.assign, it should
893 // be a snapshot AbstractObjectValue
894
895
896 if (possibleOtherObjectAssignTo instanceof _index.AbstractObjectValue) {
897 let otherTemporalOperationEntry = realm.getTemporalOperationEntryFromDerivedValue(possibleOtherObjectAssignTo);
898
899 if (!(otherTemporalOperationEntry instanceof TemporalObjectAssignEntry)) {
900 continue;
901 }
902
903 let otherArgs = otherTemporalOperationEntry.args; // Object.assign has at least 1 arg
904
905 if (otherArgs.length < 1) {
906 continue;
907 }
908
909 let otherArgsToUse = [];
910
911 for (let x = 1; x < otherArgs.length; x++) {
912 let arg = otherArgs[x]; // The arg might have been leaked, so ensure we do not continue in this case
913
914 if (arg instanceof _index.ObjectValue && arg.mightBeLeakedObject()) {
915 continue loopThroughArgs;
916 }
917
918 if (arg instanceof _index.ObjectValue || arg instanceof _index.AbstractValue) {
919 let temporalGeneratorEntries = realm.getTemporalGeneratorEntriesReferencingArg(arg); // We need to now check if there are any other temporal entries that exist
920 // between the Object.assign TemporalObjectAssignEntry that we're trying to
921 // merge and the current TemporalObjectAssignEntry we're going to merge into.
922
923 if (temporalGeneratorEntries !== undefined) {
924 for (let temporalGeneratorEntry of temporalGeneratorEntries) {
925 // If the entry is that of another Object.assign, then
926 // we know that this entry isn't going to cause issues
927 // with merging the TemporalObjectAssignEntry.
928 if (temporalGeneratorEntry instanceof TemporalObjectAssignEntry) {
929 continue;
930 } // TODO: what if the temporalGeneratorEntry can be omitted and not needed?
931 // If the index of this entry exists between start and end indexes,
932 // then we cannot optimize and merge the TemporalObjectAssignEntry
933 // because another generator entry may have a dependency on the Object.assign
934 // TemporalObjectAssignEntry we're trying to merge.
935
936
937 if (temporalGeneratorEntry.notEqualToAndDoesNotHappenBefore(otherTemporalOperationEntry) && temporalGeneratorEntry.notEqualToAndDoesNotHappenAfter(temporalOperationEntry)) {
938 continue loopThroughArgs;
939 }
940 }
941 }
942 }
943
944 otherArgsToUse.push(arg);
945 } // If we cannot omit the "to" value that means it's being used, so we shall not try to
946 // optimize this Object.assign.
947
948
949 if (!callbacks.canOmit(to)) {
950 // our merged Object.assign, shoud look like:
951 // Object.assign(to, ...prefixArgs, ...otherArgsToUse, ...suffixArgs)
952 let prefixArgs = args.slice(1, i - 1); // We start at 1, as 0 is the index of "to" a
953
954 let suffixArgs = args.slice(i + 1);
955 let newArgs = [to, ...prefixArgs, ...otherArgsToUse, ...suffixArgs]; // We now create a new TemporalObjectAssignEntry, without mutating the existing
956 // entry at this point. This new entry is essentially a TemporalObjectAssignEntry
957 // that contains two Object.assign call TemporalObjectAssignEntry entries that have
958 // been merged into a single entry. The previous Object.assign TemporalObjectAssignEntry
959 // should dead-code eliminate away once we replace the original TemporalObjectAssignEntry
960 // we started with with the new merged on as they will no longer be referenced.
961
962 let newTemporalObjectAssignEntryArgs = Object.assign({}, temporalOperationEntry, {
963 args: newArgs
964 });
965 return new TemporalObjectAssignEntry(realm, newTemporalObjectAssignEntryArgs);
966 } // We might be able to optimize, but we are not sure because "to" can still omit.
967 // So we return possible optimization status and wait until "to" does get visited.
968 // It may never get visited, but that's okay as we'll skip the optimization all
969 // together.
970
971
972 return "POSSIBLE_OPTIMIZATION";
973 }
974 }
975
976 return "NO_OPTIMIZATION";
977}
978//# sourceMappingURL=generator.js.map
\No newline at end of file