UNPKG

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