160
state.assign(index++, effect.from, effect.into);
161
} else if (effect.kind === 'Alias') {
162
state.assign(index++, effect.from, effect.into);
163
+ } else if (effect.kind === 'MaybeAlias') {
164
+ state.maybeAlias(index++, effect.from, effect.into);
165
} else if (effect.kind === 'Capture') {
166
state.capture(index++, effect.from, effect.into);
167
} else if (
348
case 'Assign':
349
case 'Alias':
350
case 'Capture':
349
- case 'CreateFrom': {
351
+ case 'CreateFrom':
352
+ case 'MaybeAlias': {
353
const isMutatedOrReassigned =
354
effect.into.identifier.mutableRange.end > instr.id;
355
if (isMutatedOrReassigned) {
570
createdFrom: Map<Identifier, number>;
571
captures: Map<Identifier, number>;
572
aliases: Map<Identifier, number>;
570
- edges: Array<{index: number; node: Identifier; kind: 'capture' | 'alias'}>;
573
+ maybeAliases: Map<Identifier, number>;
574
+ edges: Array<{
575
+ index: number;
576
+ node: Identifier;
577
+ kind: 'capture' | 'alias' | 'maybeAlias';
578
+ }>;
579
transitive: {kind: MutationKind; loc: SourceLocation} | null;
580
local: {kind: MutationKind; loc: SourceLocation} | null;
581
lastMutated: number;
593
createdFrom: new Map(),
594
captures: new Map(),
595
aliases: new Map(),
596
+ maybeAliases: new Map(),
597
edges: [],
598
transitive: null,
599
local: null,
639
}
640
}
641
642
+ maybeAlias(index: number, from: Place, into: Place): void {
643
+ const fromNode = this.nodes.get(from.identifier);
644
+ const toNode = this.nodes.get(into.identifier);
645
+ if (fromNode == null || toNode == null) {
646
+ return;
647
+ }
648
+ fromNode.edges.push({index, node: into.identifier, kind: 'maybeAlias'});
649
+ if (!toNode.maybeAliases.has(from.identifier)) {
650
+ toNode.maybeAliases.set(from.identifier, index);
651
+ }
652
+ }
653
+
654
render(index: number, start: Identifier, errors: CompilerError): void {
655
const seen = new Set<Identifier>();
656
const queue: Array<Identifier> = [start];
694
// Null is used for simulated mutations
695
end: InstructionId | null,
696
transitive: boolean,
676
- kind: MutationKind,
697
+ startKind: MutationKind,
698
loc: SourceLocation,
699
errors: CompilerError,
700
): void {
680
- const seen = new Set<Identifier>();
701
+ const seen = new Map<Identifier, MutationKind>();
702
const queue: Array<{
703
place: Identifier;
704
transitive: boolean;
705
direction: 'backwards' | 'forwards';
685
- }> = [{place: start, transitive, direction: 'backwards'}];
706
+ kind: MutationKind;
707
+ }> = [{place: start, transitive, direction: 'backwards', kind: startKind}];
708
while (queue.length !== 0) {
687
- const {place: current, transitive, direction} = queue.pop()!;
688
- if (seen.has(current)) {
709
+ const {place: current, transitive, direction, kind} = queue.pop()!;
710
+ const previousKind = seen.get(current);
711
+ if (previousKind != null && previousKind >= kind) {
712
continue;
713
}
691
- seen.add(current);
714
+ seen.set(current, kind);
715
const node = this.nodes.get(current);
716
if (node == null) {
717
continue;
747
if (edge.index >= index) {
748
break;
749
}
727
- queue.push({place: edge.node, transitive, direction: 'forwards'});
750
+ queue.push({place: edge.node, transitive, direction: 'forwards', kind});
751
}
752
for (const [alias, when] of node.createdFrom) {
753
if (when >= index) {
754
continue;
755
}
733
- queue.push({place: alias, transitive: true, direction: 'backwards'});
756
+ queue.push({
757
+ place: alias,
758
+ transitive: true,
759
+ direction: 'backwards',
760
+ kind,
761
+ });
762
}
763
if (direction === 'backwards' || node.value.kind !== 'Phi') {
764
/**
775
if (when >= index) {
776
continue;
777
}
750
- queue.push({place: alias, transitive, direction: 'backwards'});
778
+ queue.push({place: alias, transitive, direction: 'backwards', kind});
779
+ }
780
+ /**
781
+ * MaybeAlias indicates potential data flow from unknown function calls,
782
+ * so we downgrade mutations through these aliases to consider them
783
+ * conditional. This means we'll consider them for mutation *range*
784
+ * purposes but not report validation errors for mutations, since
785
+ * we aren't sure that the `from` value could actually be aliased.
786
+ */
787
+ for (const [alias, when] of node.maybeAliases) {
788
+ if (when >= index) {
789
+ continue;
790
+ }
791
+ queue.push({
792
+ place: alias,
793
+ transitive,
794
+ direction: 'backwards',
795
+ kind: MutationKind.Conditional,
796
+ });
797
}
798
}
799
/**
804
if (when >= index) {
805
continue;
806
}
761
- queue.push({place: capture, transitive, direction: 'backwards'});
807
+ queue.push({
808
+ place: capture,
809
+ transitive,
810
+ direction: 'backwards',
811
+ kind,
812
+ });
813
}
814
}
815
}