[dart2js] Migrate ssa/codegen.dart
Migrate the cycle codegen.dart, codegen_helpers.dart and variable_allocator.dart Change-Id: I89b2b59736bd8024fe5a65bba4bbb60f3379b700 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/268863 Commit-Queue: Stephen Adams <sra@google.com> Reviewed-by: Nate Biggs <natebiggs@google.com>
This commit is contained in:
committed by
Commit Queue
parent
8e70a02f05
commit
58c15eeeeb
@@ -106,7 +106,7 @@ class SpecializedChecks {
|
||||
return null;
|
||||
}
|
||||
|
||||
static MemberEntity? findAsCheck(DartType dartType,
|
||||
static FunctionEntity? findAsCheck(DartType dartType,
|
||||
JCommonElements commonElements, bool useLegacySubtyping) {
|
||||
if (dartType is InterfaceType) {
|
||||
if (dartType.typeArguments.isNotEmpty) return null;
|
||||
@@ -147,7 +147,7 @@ class SpecializedChecks {
|
||||
/// String nullable: false legacy: true String yes
|
||||
/// String nullable: false legacy: false String no
|
||||
///
|
||||
static MemberEntity? _findAsCheck(
|
||||
static FunctionEntity? _findAsCheck(
|
||||
ClassEntity element, JCommonElements commonElements,
|
||||
{required bool nullable, required bool legacy}) {
|
||||
if (element == commonElements.jsStringClass ||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,6 @@
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// @dart = 2.10
|
||||
|
||||
import '../constants/values.dart';
|
||||
import '../elements/entities.dart';
|
||||
import '../inferrer/abstract_value_domain.dart';
|
||||
@@ -30,11 +28,11 @@ bool canUseAliasedSuperMember(MemberEntity member, Selector selector) {
|
||||
///
|
||||
/// - Combine read/modify/write sequences into HReadModifyWrite instructions to
|
||||
/// simplify codegen of expressions like `a.x += y`.
|
||||
class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
class SsaInstructionSelection extends HBaseVisitor<HInstruction?>
|
||||
with CodegenPhase {
|
||||
final JClosedWorld _closedWorld;
|
||||
final CompilerOptions _options;
|
||||
HGraph graph;
|
||||
late final HGraph graph;
|
||||
|
||||
SsaInstructionSelection(this._options, this._closedWorld);
|
||||
|
||||
@@ -49,10 +47,10 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
|
||||
@override
|
||||
void visitBasicBlock(HBasicBlock block) {
|
||||
HInstruction instruction = block.first;
|
||||
HInstruction? instruction = block.first;
|
||||
while (instruction != null) {
|
||||
HInstruction next = instruction.next;
|
||||
HInstruction replacement = instruction.accept(this);
|
||||
HInstruction? next = instruction.next;
|
||||
HInstruction? replacement = instruction.accept(this);
|
||||
if (replacement != instruction && replacement != null) {
|
||||
block.rewrite(instruction, replacement);
|
||||
|
||||
@@ -84,7 +82,7 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
@override
|
||||
HInstruction visitNullCheck(HNullCheck node) {
|
||||
// If we remove this NullCheck, does the program behave the same?
|
||||
HInstruction faultingInstruction = _followingSameFaultInstruction(node);
|
||||
HInstruction? faultingInstruction = _followingSameFaultInstruction(node);
|
||||
if (faultingInstruction != null) {
|
||||
// Force [faultingInstruction] to appear in same source location as
|
||||
// [node]. This avoids the source-mapped stack trace containing an
|
||||
@@ -103,13 +101,13 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
/// Searches the instructions following [nullCheck] to see if the first
|
||||
/// instruction with an effect or exception will fault on a `null` input just
|
||||
/// like the [nullCheck].
|
||||
HInstruction _followingSameFaultInstruction(HNullCheck nullCheck) {
|
||||
HInstruction current = nullCheck.next;
|
||||
HInstruction? _followingSameFaultInstruction(HNullCheck nullCheck) {
|
||||
HInstruction? current = nullCheck.next;
|
||||
do {
|
||||
// The instructionType of [nullCheck] is not nullable (since it is the
|
||||
// (not) null check!) This means that if we do need to check the type, we
|
||||
// should test against nullCheck.checkedInput, not the direct input.
|
||||
if (current.getDartReceiver(_closedWorld) == nullCheck) {
|
||||
if (current!.getDartReceiver(_closedWorld) == nullCheck) {
|
||||
if (current is HFieldGet) return current;
|
||||
if (current is HFieldSet) return current;
|
||||
if (current is HGetLength) return current;
|
||||
@@ -148,13 +146,13 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
return null;
|
||||
}
|
||||
|
||||
HInstruction next = current.next;
|
||||
HInstruction? next = current.next;
|
||||
if (next == null) {
|
||||
// We do not merge blocks in our SSA graph, so if this block just jumps
|
||||
// to a single successor, visit the successor, avoiding back-edges.
|
||||
HBasicBlock successor;
|
||||
HBasicBlock? successor;
|
||||
if (current is HGoto) {
|
||||
successor = current.block.successors.single;
|
||||
successor = current.block!.successors.single;
|
||||
} else if (current is HIf) {
|
||||
// We also leave HIf nodes in place when one branch is dead.
|
||||
HInstruction condition = current.inputs.first;
|
||||
@@ -164,7 +162,7 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
: current.elseBlock;
|
||||
}
|
||||
}
|
||||
if (successor != null && successor.id > current.block.id) {
|
||||
if (successor != null && successor.id > current.block!.id) {
|
||||
next = successor.first;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +180,7 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
/// Returns the single JavaScript comparison (`==` or `===`) if that
|
||||
/// implements `identical(left, right)`, or returns `null` if the more complex
|
||||
/// ternary `left == null ? right == null : left === right` is required.
|
||||
String simpleOp(HInstruction left, HInstruction right) {
|
||||
String? simpleOp(HInstruction left, HInstruction right) {
|
||||
AbstractValue leftType = left.instructionType;
|
||||
AbstractValue rightType = right.instructionType;
|
||||
if (_abstractValueDomain.isNull(leftType).isDefinitelyFalse) {
|
||||
@@ -225,12 +223,12 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
.isPotentiallyTrue;
|
||||
|
||||
@override
|
||||
HInstruction visitFieldSet(HFieldSet setter) {
|
||||
HInstruction? visitFieldSet(HFieldSet setter) {
|
||||
// Pattern match
|
||||
// t1 = x.f; t2 = t1 + 1; x.f = t2; use(t2) --> ++x.f
|
||||
// t1 = x.f; t2 = t1 op y; x.f = t2; use(t2) --> x.f op= y
|
||||
// t1 = x.f; t2 = t1 + 1; x.f = t2; use(t1) --> x.f++
|
||||
HBasicBlock block = setter.block;
|
||||
HBasicBlock block = setter.block!;
|
||||
HInstruction op = setter.value;
|
||||
HInstruction receiver = setter.receiver;
|
||||
|
||||
@@ -249,12 +247,12 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
return false;
|
||||
}
|
||||
|
||||
HInstruction noMatchingRead() {
|
||||
HInstruction? noMatchingRead() {
|
||||
// If we have other HFieldSet optimizations, they go here.
|
||||
return null;
|
||||
}
|
||||
|
||||
HInstruction replaceOp(HInstruction replacement, HInstruction getter) {
|
||||
HInstruction? replaceOp(HInstruction replacement, HInstruction getter) {
|
||||
block.addBefore(setter, replacement);
|
||||
block.remove(setter);
|
||||
block.rewrite(op, replacement);
|
||||
@@ -263,10 +261,8 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
return null;
|
||||
}
|
||||
|
||||
HInstruction plusOrMinus(String assignOp, String incrementOp) {
|
||||
HInvokeBinary binary = op;
|
||||
HInstruction left = binary.left;
|
||||
HInstruction right = binary.right;
|
||||
HInstruction? plusOrMinus(String assignOp, String incrementOp,
|
||||
HInstruction left, HInstruction right) {
|
||||
if (isMatchingRead(left)) {
|
||||
if (left.usedBy.length == 1) {
|
||||
if (right is HConstant && right.constant.isOne) {
|
||||
@@ -294,7 +290,7 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
return noMatchingRead();
|
||||
}
|
||||
|
||||
HInstruction simple(
|
||||
HInstruction? simple(
|
||||
String assignOp, HInstruction left, HInstruction right) {
|
||||
if (isMatchingRead(left)) {
|
||||
if (left.usedBy.length == 1) {
|
||||
@@ -306,31 +302,26 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
return noMatchingRead();
|
||||
}
|
||||
|
||||
HInstruction simpleBinary(String assignOp) {
|
||||
HInvokeBinary binary = op;
|
||||
return simple(assignOp, binary.left, binary.right);
|
||||
}
|
||||
|
||||
HInstruction bitop(String assignOp) {
|
||||
HInstruction? bitop(String assignOp, HInvokeBinary binary) {
|
||||
// HBitAnd, HBitOr etc. are more difficult because HBitAnd(a.x, y)
|
||||
// sometimes needs to be forced to unsigned: a.x = (a.x & y) >>> 0.
|
||||
if (op.isUInt31(_abstractValueDomain).isDefinitelyTrue) {
|
||||
return simpleBinary(assignOp);
|
||||
return simple(assignOp, binary.left, binary.right);
|
||||
}
|
||||
return noMatchingRead();
|
||||
}
|
||||
|
||||
if (op is HAdd) return plusOrMinus('+', '++');
|
||||
if (op is HSubtract) return plusOrMinus('-', '--');
|
||||
if (op is HAdd) return plusOrMinus('+', '++', op.left, op.right);
|
||||
if (op is HSubtract) return plusOrMinus('-', '--', op.left, op.right);
|
||||
|
||||
if (op is HStringConcat) return simple('+', op.left, op.right);
|
||||
|
||||
if (op is HMultiply) return simpleBinary('*');
|
||||
if (op is HDivide) return simpleBinary('/');
|
||||
if (op is HMultiply) return simple('*', op.left, op.right);
|
||||
if (op is HDivide) return simple('/', op.left, op.right);
|
||||
|
||||
if (op is HBitAnd) return bitop('&');
|
||||
if (op is HBitOr) return bitop('|');
|
||||
if (op is HBitXor) return bitop('^');
|
||||
if (op is HBitAnd) return bitop('&', op);
|
||||
if (op is HBitOr) return bitop('|', op);
|
||||
if (op is HBitXor) return bitop('^', op);
|
||||
|
||||
return noMatchingRead();
|
||||
}
|
||||
@@ -349,8 +340,8 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
!_intercepted(operand1.instructionType)) {
|
||||
if (test.usedBy.length == 1 && condition.usedBy.length == 1) {
|
||||
node.changeUse(condition, operand1);
|
||||
condition.block.remove(condition);
|
||||
test.block.remove(test);
|
||||
condition.block!.remove(condition);
|
||||
test.block!.remove(test);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,9 +353,9 @@ class SsaInstructionSelection extends HBaseVisitor<HInstruction /*?*/ >
|
||||
if (operand2.isNull(_abstractValueDomain).isDefinitelyTrue &&
|
||||
!_intercepted(operand1.instructionType)) {
|
||||
var not = HNot(operand1, _abstractValueDomain.boolType);
|
||||
node.block.addBefore(node, not);
|
||||
node.block!.addBefore(node, not);
|
||||
node.changeUse(condition, not);
|
||||
condition.block.remove(condition);
|
||||
condition.block!.remove(condition);
|
||||
}
|
||||
}
|
||||
return node;
|
||||
@@ -383,9 +374,9 @@ class SsaTypeKnownRemover extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
@override
|
||||
void visitBasicBlock(HBasicBlock block) {
|
||||
HInstruction instruction = block.last;
|
||||
HInstruction? instruction = block.last;
|
||||
while (instruction != null) {
|
||||
HInstruction previous = instruction.previous;
|
||||
HInstruction? previous = instruction.previous;
|
||||
instruction.accept(this);
|
||||
instruction = previous;
|
||||
}
|
||||
@@ -393,8 +384,8 @@ class SsaTypeKnownRemover extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
@override
|
||||
void visitTypeKnown(HTypeKnown instruction) {
|
||||
instruction.block.rewrite(instruction, instruction.checkedInput);
|
||||
instruction.block.remove(instruction);
|
||||
instruction.block!.rewrite(instruction, instruction.checkedInput);
|
||||
instruction.block!.remove(instruction);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -419,9 +410,9 @@ class SsaTrustedPrimitiveCheckRemover extends HBaseVisitor<void>
|
||||
|
||||
@override
|
||||
void visitBasicBlock(HBasicBlock block) {
|
||||
HInstruction instruction = block.first;
|
||||
HInstruction? instruction = block.first;
|
||||
while (instruction != null) {
|
||||
HInstruction next = instruction.next;
|
||||
HInstruction? next = instruction.next;
|
||||
instruction.accept(this);
|
||||
instruction = next;
|
||||
}
|
||||
@@ -429,14 +420,14 @@ class SsaTrustedPrimitiveCheckRemover extends HBaseVisitor<void>
|
||||
|
||||
@override
|
||||
void visitPrimitiveCheck(HPrimitiveCheck instruction) {
|
||||
instruction.block.rewrite(instruction, instruction.checkedInput);
|
||||
instruction.block.remove(instruction);
|
||||
instruction.block!.rewrite(instruction, instruction.checkedInput);
|
||||
instruction.block!.remove(instruction);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBoolConversion(HBoolConversion instruction) {
|
||||
instruction.block.rewrite(instruction, instruction.checkedInput);
|
||||
instruction.block.remove(instruction);
|
||||
instruction.block!.rewrite(instruction, instruction.checkedInput);
|
||||
instruction.block!.remove(instruction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,9 +444,9 @@ class SsaTrustedLateCheckRemover extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
@override
|
||||
void visitBasicBlock(HBasicBlock block) {
|
||||
HInstruction instruction = block.first;
|
||||
HInstruction? instruction = block.first;
|
||||
while (instruction != null) {
|
||||
HInstruction next = instruction.next;
|
||||
HInstruction? next = instruction.next;
|
||||
instruction.accept(this);
|
||||
instruction = next;
|
||||
}
|
||||
@@ -465,14 +456,14 @@ class SsaTrustedLateCheckRemover extends HBaseVisitor<void> with CodegenPhase {
|
||||
void visitLateCheck(HLateCheck instruction) {
|
||||
if (!instruction.isTrusted) return;
|
||||
final inputs = instruction.inputs.toList();
|
||||
instruction.block.rewrite(instruction, instruction.checkedInput);
|
||||
instruction.block.remove(instruction);
|
||||
instruction.block!.rewrite(instruction, instruction.checkedInput);
|
||||
instruction.block!.remove(instruction);
|
||||
// TODO(sra): There might be a unused name.
|
||||
|
||||
// Remove pure unused inputs.
|
||||
for (HInstruction input in inputs) {
|
||||
if (input.usedBy.isNotEmpty) continue;
|
||||
HBasicBlock block = input.block;
|
||||
HBasicBlock? block = input.block;
|
||||
if (block == null) continue; // Already removed.
|
||||
if (input.isPure(_abstractValueDomain)) {
|
||||
// Special cases that are removed properly by other phases.
|
||||
@@ -497,7 +488,7 @@ class SsaTrustedLateCheckRemover extends HBaseVisitor<void> with CodegenPhase {
|
||||
/// b.y = v;
|
||||
/// -->
|
||||
/// b.y = a.x = v;
|
||||
class SsaAssignmentChaining extends HBaseVisitor<HInstruction /*?*/ >
|
||||
class SsaAssignmentChaining extends HBaseVisitor<HInstruction?>
|
||||
with CodegenPhase {
|
||||
final JClosedWorld _closedWorld;
|
||||
|
||||
@@ -508,35 +499,34 @@ class SsaAssignmentChaining extends HBaseVisitor<HInstruction /*?*/ >
|
||||
|
||||
@override
|
||||
void visitGraph(HGraph graph) {
|
||||
//this.graph = graph;
|
||||
visitDominatorTree(graph);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBasicBlock(HBasicBlock block) {
|
||||
HInstruction instruction = block.first;
|
||||
HInstruction? instruction = block.first;
|
||||
while (instruction != null) {
|
||||
instruction = instruction.accept(this);
|
||||
instruction = instruction.accept<HInstruction?>(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the next instruction.
|
||||
@override
|
||||
HInstruction visitInstruction(HInstruction node) {
|
||||
HInstruction? visitInstruction(HInstruction node) {
|
||||
return node.next;
|
||||
}
|
||||
|
||||
@override
|
||||
HInstruction visitFieldSet(HFieldSet setter) {
|
||||
HInstruction? visitFieldSet(HFieldSet setter) {
|
||||
return tryChainAssignment(setter, setter.value);
|
||||
}
|
||||
|
||||
@override
|
||||
HInstruction visitStaticStore(HStaticStore store) {
|
||||
HInstruction? visitStaticStore(HStaticStore store) {
|
||||
return tryChainAssignment(store, store.inputs.single);
|
||||
}
|
||||
|
||||
HInstruction tryChainAssignment(HInstruction setter, HInstruction value) {
|
||||
HInstruction? tryChainAssignment(HInstruction setter, HInstruction value) {
|
||||
// Try to use result of field or static assignment
|
||||
//
|
||||
// t1 = v; x.f = t1; ... t1 ...
|
||||
@@ -551,7 +541,7 @@ class SsaAssignmentChaining extends HBaseVisitor<HInstruction /*?*/ >
|
||||
// the number of references to [value].
|
||||
HInstruction chain = setter;
|
||||
setter.instructionType = value.instructionType;
|
||||
for (HInstruction current = setter.next;;) {
|
||||
for (HInstruction? current = setter.next;;) {
|
||||
if (current is HFieldSet) {
|
||||
HFieldSet nextSetter = current;
|
||||
if (nextSetter.value == value && nextSetter.receiver != value) {
|
||||
@@ -579,7 +569,7 @@ class SsaAssignmentChaining extends HBaseVisitor<HInstruction /*?*/ >
|
||||
break;
|
||||
}
|
||||
|
||||
final HInstruction next = chain.next;
|
||||
final HInstruction? next = chain.next;
|
||||
|
||||
if (value.usedBy.length <= 1) return next; // setter is only remaining use.
|
||||
|
||||
@@ -597,7 +587,7 @@ class SsaAssignmentChaining extends HBaseVisitor<HInstruction /*?*/ >
|
||||
// assignment.
|
||||
// TODO(sra): Better analysis to permit phis that are part of a
|
||||
// forwards-only tree.
|
||||
if (use.block.id < chain.block.id) return next;
|
||||
if (use.block!.id < chain.block!.id) return next;
|
||||
if (use.usedBy.any((node) => node is HPhi)) return next;
|
||||
|
||||
// A forward phi often has a new name. We want to avoid [value] having a
|
||||
@@ -670,12 +660,12 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
/// List of [HInstruction] that the instruction merger expects in
|
||||
/// order when visiting the inputs of an instruction.
|
||||
List<HInstruction> expectedInputs;
|
||||
List<HInstruction>? expectedInputs;
|
||||
|
||||
/// Set of pure [HInstruction] that the instruction merger expects to
|
||||
/// find. The order of pure instructions do not matter, as they will
|
||||
/// not be affected by side effects.
|
||||
Set<HInstruction> pureInputs;
|
||||
Set<HInstruction>? pureInputs;
|
||||
Set<HInstruction> generateAtUseSite;
|
||||
|
||||
void markAsGenerateAtUseSite(HInstruction instruction) {
|
||||
@@ -708,7 +698,7 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
// Move it closer to [user], so that instructions in
|
||||
// between do not prevent making it generate at use site.
|
||||
input.moveBefore(user);
|
||||
pureInputs.add(input);
|
||||
pureInputs!.add(input);
|
||||
// Previous computations done on [input] are now invalid
|
||||
// because we moved [input] to another place. So all
|
||||
// non code motion invariant instructions need
|
||||
@@ -723,7 +713,7 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
input.accept(this);
|
||||
}
|
||||
} else {
|
||||
expectedInputs.add(input);
|
||||
expectedInputs!.add(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -889,8 +879,8 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
// Return true if it is found, or false if not.
|
||||
bool findInInputsAndPopNonMatching(HInstruction instruction) {
|
||||
assert(!isEffectivelyPure(instruction));
|
||||
while (!expectedInputs.isEmpty) {
|
||||
HInstruction nextInput = expectedInputs.removeLast();
|
||||
while (!expectedInputs!.isEmpty) {
|
||||
HInstruction nextInput = expectedInputs!.removeLast();
|
||||
assert(!generateAtUseSite.contains(nextInput));
|
||||
assert(nextInput.usedBy.length == 1);
|
||||
if (identical(nextInput, instruction)) {
|
||||
@@ -900,8 +890,8 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
return false;
|
||||
}
|
||||
|
||||
block.last.accept(this);
|
||||
for (HInstruction instruction = block.last.previous;
|
||||
block.last!.accept(this);
|
||||
for (HInstruction? instruction = block.last!.previous;
|
||||
instruction != null;
|
||||
instruction = instruction.previous) {
|
||||
if (generateAtUseSite.contains(instruction)) {
|
||||
@@ -912,7 +902,7 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
continue;
|
||||
}
|
||||
if (isEffectivelyPure(instruction)) {
|
||||
if (pureInputs.contains(instruction)) {
|
||||
if (pureInputs!.contains(instruction)) {
|
||||
tryGenerateAtUseSite(instruction);
|
||||
} else {
|
||||
// If the input is not in the [pureInputs] set, it has not
|
||||
@@ -961,6 +951,7 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
// f(bar(), t3);
|
||||
// use(t3);
|
||||
//
|
||||
final expectedInputs = this.expectedInputs!;
|
||||
int oldLength = expectedInputs.length;
|
||||
instruction.accept(this);
|
||||
if (oldLength != 0 && oldLength != expectedInputs.length) {
|
||||
@@ -978,7 +969,7 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
// expected input.
|
||||
tryGenerateAtUseSite(instruction);
|
||||
} else {
|
||||
assert(expectedInputs.isEmpty);
|
||||
assert(expectedInputs!.isEmpty);
|
||||
}
|
||||
instruction.accept(this);
|
||||
}
|
||||
@@ -1000,7 +991,7 @@ class SsaInstructionMerger extends HBaseVisitor<void> with CodegenPhase {
|
||||
/// using these operators instead of nested ifs and boolean variables.
|
||||
class SsaConditionMerger extends HGraphVisitor with CodegenPhase {
|
||||
Set<HInstruction> generateAtUseSite;
|
||||
Set<HInstruction> controlFlowOperators;
|
||||
Set<HIf> controlFlowOperators;
|
||||
|
||||
void markAsGenerateAtUseSite(HInstruction instruction) {
|
||||
assert(!instruction.isJsStatement());
|
||||
@@ -1027,16 +1018,16 @@ class SsaConditionMerger extends HGraphVisitor with CodegenPhase {
|
||||
// before the control flow instruction, or the last instruction,
|
||||
// then we will have to emit a statement for that last instruction.
|
||||
if (instruction != block.last &&
|
||||
!identical(instruction, block.last.previous)) return true;
|
||||
!identical(instruction, block.last!.previous)) return true;
|
||||
|
||||
// If one of the instructions in the block until [instruction] is
|
||||
// not generated at use site, then we will have to emit a
|
||||
// statement for it.
|
||||
// TODO(ngeoffray): we could generate a comma separated
|
||||
// list of expressions.
|
||||
for (HInstruction temp = block.first;
|
||||
for (HInstruction? temp = block.first;
|
||||
!identical(temp, instruction);
|
||||
temp = temp.next) {
|
||||
temp = temp!.next) {
|
||||
if (!generateAtUseSite.contains(temp)) return true;
|
||||
}
|
||||
|
||||
@@ -1059,8 +1050,8 @@ class SsaConditionMerger extends HGraphVisitor with CodegenPhase {
|
||||
@override
|
||||
void visitBasicBlock(HBasicBlock block) {
|
||||
if (block.last is! HIf) return;
|
||||
HIf startIf = block.last;
|
||||
HBasicBlock end = startIf.joinBlock;
|
||||
HIf startIf = block.last as HIf;
|
||||
HBasicBlock? end = startIf.joinBlock;
|
||||
|
||||
// We check that the structure is the following:
|
||||
// If
|
||||
@@ -1096,7 +1087,7 @@ class SsaConditionMerger extends HGraphVisitor with CodegenPhase {
|
||||
HBasicBlock elseBlock = startIf.elseBlock;
|
||||
|
||||
if (!identical(end.predecessors[1], elseBlock)) return;
|
||||
HPhi phi = end.phis.first;
|
||||
HPhi phi = end.phis.first as HPhi;
|
||||
// This useless phi should have been removed. Do not generate-at-use if
|
||||
// there is no use. See #48383.
|
||||
if (phi.usedBy.isEmpty) return;
|
||||
@@ -1118,16 +1109,16 @@ class SsaConditionMerger extends HGraphVisitor with CodegenPhase {
|
||||
// have any statement and its join block is [end], we can emit a
|
||||
// sequence of control flow operation.
|
||||
if (controlFlowOperators.contains(thenBlock.last)) {
|
||||
HIf otherIf = thenBlock.last;
|
||||
HIf otherIf = thenBlock.last as HIf;
|
||||
if (!identical(otherIf.joinBlock, end)) {
|
||||
// This could be a join block that just feeds into our join block.
|
||||
HBasicBlock otherJoin = otherIf.joinBlock;
|
||||
HBasicBlock otherJoin = otherIf.joinBlock!;
|
||||
if (otherJoin.first != otherJoin.last) return;
|
||||
if (otherJoin.successors.length != 1) return;
|
||||
if (otherJoin.successors[0] != end) return;
|
||||
if (otherJoin.phis.isEmpty) return;
|
||||
if (!identical(otherJoin.phis.first, otherJoin.phis.last)) return;
|
||||
HPhi otherPhi = otherJoin.phis.first;
|
||||
HPhi otherPhi = otherJoin.phis.first as HPhi;
|
||||
if (thenInput != otherPhi) return;
|
||||
if (elseInput != otherPhi.inputs[1]) return;
|
||||
}
|
||||
@@ -1143,9 +1134,9 @@ class SsaConditionMerger extends HGraphVisitor with CodegenPhase {
|
||||
controlFlowOperators.add(startIf);
|
||||
|
||||
// Find the next non-HGoto instruction following the phi.
|
||||
HInstruction nextInstruction = phi.block.first;
|
||||
HInstruction? nextInstruction = phi.block!.first;
|
||||
while (nextInstruction is HGoto) {
|
||||
nextInstruction = nextInstruction.block.successors[0].first;
|
||||
nextInstruction = nextInstruction.block!.successors[0].first;
|
||||
}
|
||||
|
||||
// If the operation is only used by the first instruction
|
||||
@@ -1190,9 +1181,9 @@ class SsaShareRegionConstants extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
@override
|
||||
void visitBasicBlock(HBasicBlock block) {
|
||||
HInstruction instruction = block.first;
|
||||
HInstruction? instruction = block.first;
|
||||
while (instruction != null) {
|
||||
HInstruction next = instruction.next;
|
||||
HInstruction? next = instruction.next;
|
||||
instruction.accept(this);
|
||||
instruction = next;
|
||||
}
|
||||
@@ -1214,7 +1205,7 @@ class SsaShareRegionConstants extends HBaseVisitor<void> with CodegenPhase {
|
||||
// entry, not the use of `this`.
|
||||
reference.sourceInformation = node.sourceInformation;
|
||||
reference.sourceElement = _ExpressionName(name);
|
||||
node.block.addAfter(node, reference);
|
||||
node.block!.addAfter(node, reference);
|
||||
for (HInstruction user in users) {
|
||||
if (cacheable(user)) {
|
||||
user.changeUse(node, reference);
|
||||
|
||||
@@ -768,6 +768,9 @@ class HBasicBlock extends HInstructionList {
|
||||
static const int STATUS_CLOSED = 2;
|
||||
int status = STATUS_NEW;
|
||||
|
||||
// TODO(48820): Can we make the Phi list better typed? As it stands, the
|
||||
// first/last fields and the next/previous fields of the HPhi nodes are all
|
||||
// typed as HInstruction, requiring downcasts to HPhi/HPhi?
|
||||
HInstructionList phis = HInstructionList();
|
||||
|
||||
HLoopInformation? loopInformation = null;
|
||||
@@ -3417,7 +3420,7 @@ class HLazyStatic extends HInstruction {
|
||||
}
|
||||
|
||||
class HStaticStore extends HInstruction {
|
||||
MemberEntity element;
|
||||
FieldEntity element;
|
||||
HStaticStore(AbstractValueDomain domain, this.element, HInstruction value)
|
||||
: super([value], domain.emptyType) {
|
||||
sideEffects.clearAllSideEffects();
|
||||
@@ -4036,7 +4039,6 @@ abstract class HStatementInformationVisitor {
|
||||
}
|
||||
|
||||
abstract class HExpressionInformationVisitor {
|
||||
bool visitAndOrInfo(HAndOrBlockInformation info);
|
||||
bool visitSubExpressionInfo(HSubExpressionBlockInformation info);
|
||||
}
|
||||
|
||||
@@ -4184,28 +4186,6 @@ class HIfBlockInformation implements HStatementInformation {
|
||||
visitor.visitIfInfo(this);
|
||||
}
|
||||
|
||||
class HAndOrBlockInformation implements HExpressionInformation {
|
||||
final bool isAnd;
|
||||
final HExpressionInformation left;
|
||||
final HExpressionInformation right;
|
||||
HAndOrBlockInformation(this.isAnd, this.left, this.right);
|
||||
|
||||
@override
|
||||
HBasicBlock get start => left.start;
|
||||
@override
|
||||
HBasicBlock get end => right.end;
|
||||
|
||||
// We don't currently use HAndOrBlockInformation.
|
||||
@override
|
||||
HInstruction? get conditionExpression {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
bool accept(HExpressionInformationVisitor visitor) =>
|
||||
visitor.visitAndOrInfo(this);
|
||||
}
|
||||
|
||||
class HTryBlockInformation implements HStatementInformation {
|
||||
final HStatementInformation? body;
|
||||
final HLocalValue? catchVariable;
|
||||
@@ -4494,7 +4474,7 @@ class HAsCheckSimple extends HCheck {
|
||||
final DartType dartType;
|
||||
final AbstractValueWithPrecision checkedType;
|
||||
final bool isTypeError;
|
||||
final MemberEntity method;
|
||||
final FunctionEntity method;
|
||||
|
||||
HAsCheckSimple(HInstruction checked, this.dartType, this.checkedType,
|
||||
this.isTypeError, this.method, AbstractValue type)
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// @dart = 2.10
|
||||
|
||||
import '../common.dart';
|
||||
import '../js_backend/namer.dart' show ModularNamer;
|
||||
import 'codegen.dart' show CodegenPhase;
|
||||
@@ -28,7 +26,9 @@ class LiveInterval {
|
||||
/// The id where the instruction is defined.
|
||||
int start;
|
||||
final List<LiveRange> ranges;
|
||||
LiveInterval() : ranges = [];
|
||||
LiveInterval()
|
||||
: start = -1,
|
||||
ranges = [];
|
||||
|
||||
// We want [HCheck] instructions to have the same name as the
|
||||
// instruction it checks, so both instructions should share the same
|
||||
@@ -73,7 +73,7 @@ class LiveInterval {
|
||||
class LiveEnvironment {
|
||||
/// The instruction id where the basic block starts. See
|
||||
/// [SsaLiveIntervalBuilder.instructionId].
|
||||
int startId;
|
||||
int startId = -1;
|
||||
|
||||
/// The instruction id where the basic block ends.
|
||||
final int endId;
|
||||
@@ -101,7 +101,7 @@ class LiveEnvironment {
|
||||
void remove(HInstruction instruction, int id) {
|
||||
LiveInterval interval =
|
||||
liveIntervals.putIfAbsent(instruction, () => LiveInterval());
|
||||
int lastId = liveInstructions[instruction];
|
||||
int? lastId = liveInstructions[instruction];
|
||||
// If [lastId] is null, then this instruction is not being used.
|
||||
interval.add(LiveRange(id, lastId ?? id));
|
||||
// The instruction is defined at [id].
|
||||
@@ -159,7 +159,7 @@ class LiveEnvironment {
|
||||
/// instruction, and computes the liveIns of each basic block.
|
||||
class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
final Set<HInstruction> generateAtUseSite;
|
||||
final Set<HInstruction> controlFlowOperators;
|
||||
final Set<HIf> controlFlowOperators;
|
||||
|
||||
/// A counter to assign start and end ids to live ranges. The initial
|
||||
/// value is not relevant. Note that instructionId goes downward to ease
|
||||
@@ -181,14 +181,14 @@ class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
SsaLiveIntervalBuilder(this.generateAtUseSite, this.controlFlowOperators) {
|
||||
for (HIf ifNode in controlFlowOperators) {
|
||||
_phiToCondition[ifNode.joinBlock.phis.first] = ifNode.condition;
|
||||
_phiToCondition[ifNode.joinBlock!.phis.first!] = ifNode.condition;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitGraph(HGraph graph) {
|
||||
visitPostDominatorTree(graph);
|
||||
if (!liveInstructions[graph.entry].isEmpty) {
|
||||
if (!liveInstructions[graph.entry]!.isEmpty) {
|
||||
failedAt(CURRENT_ELEMENT_SPANNABLE, 'LiveIntervalBuilder.');
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,7 @@ class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
void markInputsAsLiveInEnvironment(
|
||||
HInstruction instruction, LiveEnvironment environment) {
|
||||
if (instruction is HPhi) {
|
||||
HInstruction condition = _phiToCondition[instruction];
|
||||
HInstruction? condition = _phiToCondition[instruction];
|
||||
if (condition != null) {
|
||||
markAsLiveInEnvironment(condition, environment);
|
||||
}
|
||||
@@ -223,9 +223,8 @@ class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
// When looking for the checkedInstructionOrNonGenerateAtUseSite of t3 we must
|
||||
// return t2.
|
||||
HInstruction checkedInstructionOrNonGenerateAtUseSite(HCheck check) {
|
||||
dynamic checked = check.checkedInput;
|
||||
HInstruction checked = check.checkedInput;
|
||||
while (checked is HCheck) {
|
||||
// ignore: avoid_dynamic_calls
|
||||
HInstruction next = checked.checkedInput;
|
||||
if (generateAtUseSite.contains(next)) break;
|
||||
checked = next;
|
||||
@@ -265,7 +264,7 @@ class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
// Unconditionally force the live ranges of the HCheck to
|
||||
// be the live ranges of the instruction it is checking.
|
||||
liveIntervals[instruction] =
|
||||
LiveInterval.forCheck(instructionId, liveIntervals[checked]);
|
||||
LiveInterval.forCheck(instructionId, liveIntervals[checked]!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,7 +277,7 @@ class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
// the inputs of the phis of the successor that flow from this block.
|
||||
for (int i = 0; i < block.successors.length; i++) {
|
||||
HBasicBlock successor = block.successors[i];
|
||||
LiveEnvironment successorEnv = liveInstructions[successor];
|
||||
LiveEnvironment? successorEnv = liveInstructions[successor];
|
||||
if (successorEnv != null) {
|
||||
environment.mergeWith(successorEnv);
|
||||
} else {
|
||||
@@ -286,14 +285,14 @@ class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
}
|
||||
|
||||
int index = successor.predecessors.indexOf(block);
|
||||
for (HPhi phi = successor.phis.first; phi != null; phi = phi.next) {
|
||||
for (var phi = successor.phis.first; phi != null; phi = phi.next) {
|
||||
markAsLiveInEnvironment(phi.inputs[index], environment);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all instructions to remove an instruction from the
|
||||
// environment and add its inputs.
|
||||
HInstruction instruction = block.last;
|
||||
HInstruction? instruction = block.last;
|
||||
while (instruction != null) {
|
||||
if (!generateAtUseSite.contains(instruction)) {
|
||||
removeFromEnvironment(instruction, environment);
|
||||
@@ -305,7 +304,7 @@ class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
// We just remove the phis from the environment. The inputs of the
|
||||
// phis will be put in the environment of the predecessors.
|
||||
for (HPhi phi = block.phis.first; phi != null; phi = phi.next) {
|
||||
for (var phi = block.phis.first; phi != null; phi = phi.next) {
|
||||
if (!generateAtUseSite.contains(phi)) {
|
||||
environment.remove(phi, instructionId);
|
||||
}
|
||||
@@ -325,8 +324,8 @@ class SsaLiveIntervalBuilder extends HBaseVisitor<void> with CodegenPhase {
|
||||
}
|
||||
|
||||
void updateLoopMarker(HBasicBlock header) {
|
||||
LiveEnvironment env = liveInstructions[header];
|
||||
int lastId = env.loopMarkers[header];
|
||||
LiveEnvironment env = liveInstructions[header]!;
|
||||
int lastId = env.loopMarkers[header]!;
|
||||
// Update all instructions that are liveIns in [header] to have a
|
||||
// range that covers the loop.
|
||||
env.liveInstructions.forEach((HInstruction instruction, int id) {
|
||||
@@ -411,11 +410,11 @@ class VariableNames {
|
||||
|
||||
int get numberOfVariables => allUsedNames.length;
|
||||
|
||||
String getName(HInstruction instruction) {
|
||||
String? getName(HInstruction? instruction) {
|
||||
return ownName[instruction];
|
||||
}
|
||||
|
||||
CopyHandler getCopyHandler(HBasicBlock block) {
|
||||
CopyHandler? getCopyHandler(HBasicBlock block) {
|
||||
return copyHandlers[block];
|
||||
}
|
||||
|
||||
@@ -423,7 +422,7 @@ class VariableNames {
|
||||
allUsedNames.add(name);
|
||||
}
|
||||
|
||||
bool hasName(HInstruction instruction) => ownName.containsKey(instruction);
|
||||
bool hasName(HInstruction? instruction) => ownName.containsKey(instruction);
|
||||
|
||||
void addCopy(HBasicBlock block, HInstruction source, HPhi destination) {
|
||||
CopyHandler handler = copyHandlers.putIfAbsent(block, () => CopyHandler());
|
||||
@@ -453,7 +452,7 @@ class VariableNamer {
|
||||
// All liveIns instructions must have a name at this point, so we
|
||||
// add them to the list of used names.
|
||||
environment.liveInstructions.forEach((HInstruction instruction, int index) {
|
||||
String name = names.getName(instruction);
|
||||
String? name = names.getName(instruction);
|
||||
if (name != null) {
|
||||
usedNames.add(name);
|
||||
names.addNameUsed(name);
|
||||
@@ -480,7 +479,7 @@ class VariableNamer {
|
||||
return name;
|
||||
}
|
||||
|
||||
HPhi firstPhiUserWithElement(HInstruction instruction) {
|
||||
HPhi? firstPhiUserWithElement(HInstruction instruction) {
|
||||
for (HInstruction user in instruction.usedBy) {
|
||||
if (user is HPhi && user.sourceElement != null) {
|
||||
return user;
|
||||
@@ -490,7 +489,7 @@ class VariableNamer {
|
||||
}
|
||||
|
||||
String allocateName(HInstruction instruction) {
|
||||
String name;
|
||||
String? name;
|
||||
if (instruction is HCheck) {
|
||||
// Special case this instruction to use the name of its
|
||||
// input if it has one.
|
||||
@@ -503,8 +502,8 @@ class VariableNamer {
|
||||
}
|
||||
|
||||
if (instruction.sourceElement != null) {
|
||||
if (instruction.sourceElement.name != null) {
|
||||
name = allocateWithHint(instruction.sourceElement.name);
|
||||
if (instruction.sourceElement!.name != null) {
|
||||
name = allocateWithHint(instruction.sourceElement!.name!);
|
||||
} else {
|
||||
// Source element is synthesized and has no name.
|
||||
name = allocateTemporary();
|
||||
@@ -513,9 +512,10 @@ class VariableNamer {
|
||||
// We could not find an element for the instruction. If the
|
||||
// instruction is used by a phi, try to use the name of the phi.
|
||||
// Otherwise, just allocate a temporary name.
|
||||
HPhi phi = firstPhiUserWithElement(instruction);
|
||||
if (phi != null && phi.sourceElement.name != null) {
|
||||
name = allocateWithHint(phi.sourceElement.name);
|
||||
HPhi? phi = firstPhiUserWithElement(instruction);
|
||||
final phiName = phi?.sourceElement?.name;
|
||||
if (phiName != null) {
|
||||
name = allocateWithHint(phiName);
|
||||
} else {
|
||||
name = allocateTemporary();
|
||||
}
|
||||
@@ -532,7 +532,7 @@ class VariableNamer {
|
||||
|
||||
/// Frees [instruction]'s name so it can be used for other instructions.
|
||||
void freeName(HInstruction instruction) {
|
||||
String ownName = names.ownName[instruction];
|
||||
String? ownName = names.ownName[instruction];
|
||||
if (ownName != null) {
|
||||
// We check if we have already looked for temporary names
|
||||
// because if we haven't, chances are the temporary we allocate
|
||||
@@ -576,7 +576,7 @@ class SsaVariableAllocator extends HBaseVisitor<void> with CodegenPhase {
|
||||
@override
|
||||
void visitBasicBlock(HBasicBlock block) {
|
||||
VariableNamer variableNamer =
|
||||
VariableNamer(liveInstructions[block], names, _namer);
|
||||
VariableNamer(liveInstructions[block]!, names, _namer);
|
||||
|
||||
block.forEachPhi((HPhi phi) {
|
||||
handlePhi(phi, variableNamer);
|
||||
@@ -599,8 +599,8 @@ class SsaVariableAllocator extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
/// Returns whether [instruction] dies at the instruction [at].
|
||||
bool diesAt(HInstruction instruction, HInstruction at) {
|
||||
LiveInterval atInterval = liveIntervals[at];
|
||||
LiveInterval instructionInterval = liveIntervals[instruction];
|
||||
LiveInterval atInterval = liveIntervals[at]!;
|
||||
LiveInterval instructionInterval = liveIntervals[instruction]!;
|
||||
int start = atInterval.start;
|
||||
return instructionInterval.diesAt(start);
|
||||
}
|
||||
@@ -642,7 +642,7 @@ class SsaVariableAllocator extends HBaseVisitor<void> with CodegenPhase {
|
||||
|
||||
for (int i = 0; i < phi.inputs.length; i++) {
|
||||
HInstruction input = phi.inputs[i];
|
||||
HBasicBlock predecessor = phi.block.predecessors[i];
|
||||
HBasicBlock predecessor = phi.block!.predecessors[i];
|
||||
// A [HTypeKnown] instruction never has a name, but its checked
|
||||
// input might, therefore we need to do a copy instead of an
|
||||
// assignment.
|
||||
|
||||
@@ -496,7 +496,7 @@ class StaticUse {
|
||||
|
||||
/// Direct invocation of a method [element] with the given [callStructure].
|
||||
factory StaticUse.directInvoke(FunctionEntity element,
|
||||
CallStructure callStructure, List<DartType> typeArguments) {
|
||||
CallStructure callStructure, List<DartType>? typeArguments) {
|
||||
assert(
|
||||
element.isInstanceMember,
|
||||
failedAt(element,
|
||||
|
||||
@@ -805,7 +805,11 @@ class For extends Loop {
|
||||
final Expression? condition;
|
||||
final Expression? update;
|
||||
|
||||
For(this.init, this.condition, this.update, Statement body) : super(body);
|
||||
For(this.init, this.condition, this.update, Statement body,
|
||||
{JavaScriptNodeSourceInformation? sourceInformation})
|
||||
: super(body) {
|
||||
_sourceInformation = sourceInformation;
|
||||
}
|
||||
|
||||
@override
|
||||
T accept<T>(NodeVisitor<T> visitor) => visitor.visitFor(this);
|
||||
@@ -840,7 +844,11 @@ class ForIn extends Loop {
|
||||
final Expression leftHandSide;
|
||||
final Expression object;
|
||||
|
||||
ForIn(this.leftHandSide, this.object, Statement body) : super(body);
|
||||
ForIn(this.leftHandSide, this.object, Statement body,
|
||||
{JavaScriptNodeSourceInformation? sourceInformation})
|
||||
: super(body) {
|
||||
_sourceInformation = sourceInformation;
|
||||
}
|
||||
|
||||
@override
|
||||
T accept<T>(NodeVisitor<T> visitor) => visitor.visitForIn(this);
|
||||
@@ -870,7 +878,11 @@ class ForIn extends Loop {
|
||||
class While extends Loop {
|
||||
final Expression condition;
|
||||
|
||||
While(this.condition, Statement body) : super(body);
|
||||
While(this.condition, Statement body,
|
||||
{JavaScriptNodeSourceInformation? sourceInformation})
|
||||
: super(body) {
|
||||
_sourceInformation = sourceInformation;
|
||||
}
|
||||
|
||||
@override
|
||||
T accept<T>(NodeVisitor<T> visitor) => visitor.visitWhile(this);
|
||||
@@ -898,7 +910,11 @@ class While extends Loop {
|
||||
class Do extends Loop {
|
||||
final Expression condition;
|
||||
|
||||
Do(Statement body, this.condition) : super(body);
|
||||
Do(Statement body, this.condition,
|
||||
{JavaScriptNodeSourceInformation? sourceInformation})
|
||||
: super(body) {
|
||||
_sourceInformation = sourceInformation;
|
||||
}
|
||||
|
||||
@override
|
||||
T accept<T>(NodeVisitor<T> visitor) => visitor.visitDo(this);
|
||||
@@ -1509,7 +1525,10 @@ class VariableInitialization extends Expression {
|
||||
// The initializing value can be missing, e.g. for `a` in `var a, b=1;`.
|
||||
final Expression? value;
|
||||
|
||||
VariableInitialization(this.declaration, this.value);
|
||||
VariableInitialization(this.declaration, this.value,
|
||||
{JavaScriptNodeSourceInformation? sourceInformation}) {
|
||||
_sourceInformation = sourceInformation;
|
||||
}
|
||||
|
||||
@override
|
||||
int get precedenceLevel => ASSIGNMENT;
|
||||
|
||||
@@ -134,6 +134,16 @@ class Template {
|
||||
}
|
||||
throw ArgumentError.value(arguments, 'arguments', 'Must be a List or Map');
|
||||
}
|
||||
|
||||
// TODO(sra): We should rather make the return type of `instantiate` be what
|
||||
// we need, either by making Template be generic or have Expression and
|
||||
// Statement subclasses that override `instantiate`. Checking is likely still
|
||||
// required since the argument can be the result (e.g. "#").
|
||||
Expression instantiateExpression(Object arguments) =>
|
||||
instantiate(arguments) as Expression;
|
||||
|
||||
Statement instantiateStatement(Object arguments) =>
|
||||
instantiate(arguments) as Statement;
|
||||
}
|
||||
|
||||
/// An Instantiator is a Function that generates a JS AST tree or List of
|
||||
|
||||
Reference in New Issue
Block a user