diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart index 534b8a51293..8dc8e01dd69 100644 --- a/pkg/compiler/lib/src/common/codegen.dart +++ b/pkg/compiler/lib/src/common/codegen.dart @@ -4,7 +4,7 @@ library dart2js.common.codegen; -import 'package:js_ast/src/precedence.dart' as js show PRIMARY; +import 'package:js_ast/src/precedence.dart' as js show Precedence; import '../common/elements.dart'; import '../constants/values.dart'; @@ -595,7 +595,8 @@ class ModularExpression extends js.DeferredExpression } @override - int get precedenceLevel => _value?.precedenceLevel ?? js.PRIMARY; + js.Precedence get precedenceLevel => + _value?.precedenceLevel ?? js.Precedence.primary; @override Iterable get containedNodes { diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart index 48262401683..5c32b5e36d6 100644 --- a/pkg/compiler/lib/src/compiler.dart +++ b/pkg/compiler/lib/src/compiler.dart @@ -69,6 +69,12 @@ import 'universe/codegen_world_builder.dart'; import 'universe/resolution_world_builder.dart'; import 'universe/world_impact.dart' show WorldImpact, WorldImpactBuilderImpl; +enum _ResolutionStatus { + resolving, + doneResolving, + compiling, +} + /// Implementation of the compiler using a [api.CompilerInput] for supplying /// the sources. class Compiler { @@ -129,11 +135,7 @@ class Compiler { Progress progress = const Progress(); - static const int RESOLUTION_STATUS_SCANNING = 0; - static const int RESOLUTION_STATUS_RESOLVING = 1; - static const int RESOLUTION_STATUS_DONE_RESOLVING = 2; - static const int RESOLUTION_STATUS_COMPILING = 3; - int? resolutionStatus; + _ResolutionStatus? _resolutionStatus; Dart2JSStage get stage => options.stage; @@ -362,7 +364,7 @@ class Compiler { // this until after the resolution queue is processed. deferredLoadTask.beforeResolution(rootLibraryUri, libraries); - resolutionStatus = RESOLUTION_STATUS_RESOLVING; + _resolutionStatus = _ResolutionStatus.resolving; resolutionEnqueuer.applyImpact(mainImpact); if (options.showInternalProgress) reporter.log('Computing closed world'); @@ -591,7 +593,7 @@ class Compiler { GlobalTypeInferenceResults globalTypeInferenceResults) { backendStrategy .registerJClosedWorld(globalTypeInferenceResults.closedWorld); - resolutionStatus = RESOLUTION_STATUS_COMPILING; + _resolutionStatus = _ResolutionStatus.compiling; return backendStrategy.onCodegenStart(globalTypeInferenceResults); } @@ -727,7 +729,7 @@ class Compiler { /// Perform the steps needed to fully end the resolution phase. JClosedWorld? closeResolution(FunctionEntity mainFunction, ResolutionWorldBuilder resolutionWorldBuilder) { - resolutionStatus = RESOLUTION_STATUS_DONE_RESOLVING; + _resolutionStatus = _ResolutionStatus.doneResolving; KClosedWorld kClosedWorld = resolutionWorldBuilder.closeWorld(reporter); OutputUnitData result = deferredLoadTask.run(mainFunction, kClosedWorld); @@ -784,8 +786,8 @@ class Compiler { } void showResolutionProgress(Enqueuer enqueuer) { - assert(resolutionStatus == RESOLUTION_STATUS_RESOLVING, - 'Unexpected phase: $resolutionStatus'); + assert(_resolutionStatus == _ResolutionStatus.resolving, + 'Unexpected phase: $_resolutionStatus'); progress.showProgress( 'Resolved ', enqueuer.processedEntities.length, ' elements.'); } @@ -876,7 +878,7 @@ class Compiler { /// context. SourceSpan spanFromSpannable(Spannable spannable, Entity? currentElement) { SourceSpan span; - if (resolutionStatus == Compiler.RESOLUTION_STATUS_COMPILING) { + if (_resolutionStatus == _ResolutionStatus.compiling) { span = backendStrategy.spanFromSpannable(spannable, currentElement); } else { span = frontendStrategy.spanFromSpannable(spannable, currentElement); diff --git a/pkg/compiler/lib/src/js/size_estimator.dart b/pkg/compiler/lib/src/js/size_estimator.dart index 4e4622a6c36..5d1a7007452 100644 --- a/pkg/compiler/lib/src/js/size_estimator.dart +++ b/pkg/compiler/lib/src/js/size_estimator.dart @@ -116,7 +116,7 @@ class SizeEstimator implements NodeVisitor { node.accept(this); } - void visitCommaSeparated(List nodes, int hasRequiredType, + void visitCommaSeparated(List nodes, Precedence hasRequiredType, {required bool newInForInit, required bool newAtStatementBegin}) { for (int i = 0; i < nodes.length; i++) { if (i != 0) { @@ -175,7 +175,7 @@ class SizeEstimator implements NodeVisitor { @override void visitExpressionStatement(ExpressionStatement node) { - visitNestedExpression(node.expression, EXPRESSION, + visitNestedExpression(node.expression, Precedence.expression, newInForInit: false, newAtStatementBegin: true); outSemicolonLn(); } @@ -191,7 +191,7 @@ class SizeEstimator implements NodeVisitor { bool hasElse = node.hasElse; out('if('); // 'if(' - visitNestedExpression(node.condition, EXPRESSION, + visitNestedExpression(node.condition, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' blockBody(then, needsSeparation: false); @@ -215,17 +215,17 @@ class SizeEstimator implements NodeVisitor { void visitFor(For loop) { out('for('); // 'for(' if (loop.init != null) { - visitNestedExpression(loop.init!, EXPRESSION, + visitNestedExpression(loop.init!, Precedence.expression, newInForInit: true, newAtStatementBegin: false); } out(';'); // ';' if (loop.condition != null) { - visitNestedExpression(loop.condition!, EXPRESSION, + visitNestedExpression(loop.condition!, Precedence.expression, newInForInit: false, newAtStatementBegin: false); } out(';'); // ';' if (loop.update != null) { - visitNestedExpression(loop.update!, EXPRESSION, + visitNestedExpression(loop.update!, Precedence.expression, newInForInit: false, newAtStatementBegin: false); } out(')'); // ')' @@ -235,11 +235,11 @@ class SizeEstimator implements NodeVisitor { @override void visitForIn(ForIn loop) { out('for('); // 'for(' - visitNestedExpression(loop.leftHandSide, EXPRESSION, + visitNestedExpression(loop.leftHandSide, Precedence.expression, newInForInit: true, newAtStatementBegin: false); out(' in'); // ' in' pendingSpace = true; - visitNestedExpression(loop.object, EXPRESSION, + visitNestedExpression(loop.object, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' blockBody(loop.body, needsSeparation: false); @@ -248,7 +248,7 @@ class SizeEstimator implements NodeVisitor { @override void visitWhile(While loop) { out('while('); // 'while(' - visitNestedExpression(loop.condition, EXPRESSION, + visitNestedExpression(loop.condition, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' blockBody(loop.body, needsSeparation: false); @@ -259,7 +259,7 @@ class SizeEstimator implements NodeVisitor { out('do'); // 'do' if (blockBody(loop.body, needsSeparation: true)) {} out('while('); // 'while(' - visitNestedExpression(loop.condition, EXPRESSION, + visitNestedExpression(loop.condition, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' outSemicolonLn(); @@ -290,7 +290,7 @@ class SizeEstimator implements NodeVisitor { out('return'); // 'return' if (node.value != null) { pendingSpace = true; - visitNestedExpression(node.value!, EXPRESSION, + visitNestedExpression(node.value!, Precedence.expression, newInForInit: false, newAtStatementBegin: false); } outSemicolonLn(); @@ -304,7 +304,7 @@ class SizeEstimator implements NodeVisitor { out('yield'); // 'yield' } pendingSpace = true; - visitNestedExpression(node.expression, EXPRESSION, + visitNestedExpression(node.expression, Precedence.expression, newInForInit: false, newAtStatementBegin: false); outSemicolonLn(); } @@ -313,7 +313,7 @@ class SizeEstimator implements NodeVisitor { void visitThrow(Throw node) { out('throw'); // 'throw' pendingSpace = true; - visitNestedExpression(node.expression, EXPRESSION, + visitNestedExpression(node.expression, Precedence.expression, newInForInit: false, newAtStatementBegin: false); outSemicolonLn(); } @@ -334,7 +334,7 @@ class SizeEstimator implements NodeVisitor { @override void visitCatch(Catch node) { out('catch('); // 'catch(' - visitNestedExpression(node.declaration, EXPRESSION, + visitNestedExpression(node.declaration, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' blockBody(node.body, needsSeparation: false); @@ -343,7 +343,7 @@ class SizeEstimator implements NodeVisitor { @override void visitSwitch(Switch node) { out('switch('); // 'switch(' - visitNestedExpression(node.key, EXPRESSION, + visitNestedExpression(node.key, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out('){'); // '){ visitAll(node.cases); @@ -354,7 +354,7 @@ class SizeEstimator implements NodeVisitor { void visitCase(Case node) { out('case'); // 'case' pendingSpace = true; - visitNestedExpression(node.expression, EXPRESSION, + visitNestedExpression(node.expression, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(':'); // ':' if (!node.body.statements.isEmpty) { @@ -381,11 +381,11 @@ class SizeEstimator implements NodeVisitor { if (name != null) { out(' '); // ' ' // Name must be a [Decl]. Therefore only test for primary expressions. - visitNestedExpression(name, PRIMARY, + visitNestedExpression(name, Precedence.primary, newInForInit: false, newAtStatementBegin: false); } out('('); // '(' - visitCommaSeparated(fun.params, PRIMARY, + visitCommaSeparated(fun.params, Precedence.primary, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' switch (fun.asyncModifier) { @@ -412,12 +412,12 @@ class SizeEstimator implements NodeVisitor { functionOut(declaration.function, declaration.name, vars); } - visitNestedExpression(Expression node, int requiredPrecedence, + visitNestedExpression(Expression node, Precedence requiredPrecedence, {required bool newInForInit, required bool newAtStatementBegin}) { bool needsParentheses = !node.isFinalized || // a - (b + c). - (requiredPrecedence != EXPRESSION && - node.precedenceLevel < requiredPrecedence) || + (requiredPrecedence != Precedence.expression && + node.precedenceLevel.index < requiredPrecedence.index) || // for (a = (x in o); ... ; ... ) { ... } (newInForInit && node is Binary && node.op == "in") || // (function() { ... })(). @@ -444,7 +444,7 @@ class SizeEstimator implements NodeVisitor { out('var '); // 'var ' final nodes = list.declarations; if (inForInit) { - visitCommaSeparated(nodes, ASSIGNMENT, + visitCommaSeparated(nodes, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } else { for (int i = 0; i < nodes.length; i++) { @@ -453,7 +453,7 @@ class SizeEstimator implements NodeVisitor { atStatementBegin = false; out(','); // ',' } - visitNestedExpression(node, ASSIGNMENT, + visitNestedExpression(node, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } } @@ -467,7 +467,7 @@ class SizeEstimator implements NodeVisitor { } else { out(' --'); } - visitNestedExpression(variable, UNARY, + visitNestedExpression(variable, Precedence.unary, newInForInit: inForInit, newAtStatementBegin: false); } @@ -510,65 +510,65 @@ class SizeEstimator implements NodeVisitor { return; } // Output 'a = a + b' as 'a += b'. - visitNestedExpression(assignment.leftHandSide, CALL, + visitNestedExpression(assignment.leftHandSide, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); assert(op.length == 1); out('$op='); // '$op=' - visitNestedExpression(rRight, ASSIGNMENT, + visitNestedExpression(rRight, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); return; } } } - visitNestedExpression(assignment.leftHandSide, CALL, + visitNestedExpression(assignment.leftHandSide, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); if (op != null) out(op); out('='); // '=' - visitNestedExpression(assignment.value, ASSIGNMENT, + visitNestedExpression(assignment.value, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } @override visitVariableInitialization(VariableInitialization initialization) { - visitNestedExpression(initialization.declaration, CALL, + visitNestedExpression(initialization.declaration, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); if (initialization.value != null) { out('='); - visitNestedExpression(initialization.value!, ASSIGNMENT, + visitNestedExpression(initialization.value!, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } } @override visitConditional(Conditional cond) { - visitNestedExpression(cond.condition, LOGICAL_OR, + visitNestedExpression(cond.condition, Precedence.logicalOr, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); out('?'); // '?' // The then part is allowed to have an 'in'. - visitNestedExpression(cond.then, ASSIGNMENT, + visitNestedExpression(cond.then, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); out(':'); // ':' - visitNestedExpression(cond.otherwise, ASSIGNMENT, + visitNestedExpression(cond.otherwise, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } @override visitNew(New node) { out('new'); // 'new' - visitNestedExpression(node.target, LEFT_HAND_SIDE, + visitNestedExpression(node.target, Precedence.leftHandSide, newInForInit: inForInit, newAtStatementBegin: false); out('('); // '(' - visitCommaSeparated(node.arguments, ASSIGNMENT, + visitCommaSeparated(node.arguments, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' } @override visitCall(Call call) { - visitNestedExpression(call.target, CALL, + visitNestedExpression(call.target, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); out('('); // '(' - visitCommaSeparated(call.arguments, ASSIGNMENT, + visitCommaSeparated(call.arguments, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' } @@ -578,45 +578,45 @@ class SizeEstimator implements NodeVisitor { Expression left = binary.left; Expression right = binary.right; String op = binary.op; - int leftPrecedenceRequirement; - int rightPrecedenceRequirement; + Precedence leftPrecedenceRequirement; + Precedence rightPrecedenceRequirement; switch (op) { case ',': // x, (y, z) <=> (x, y), z. - leftPrecedenceRequirement = EXPRESSION; - rightPrecedenceRequirement = EXPRESSION; + leftPrecedenceRequirement = Precedence.expression; + rightPrecedenceRequirement = Precedence.expression; break; case "||": - leftPrecedenceRequirement = LOGICAL_OR; + leftPrecedenceRequirement = Precedence.logicalOr; // x || (y || z) <=> (x || y) || z. - rightPrecedenceRequirement = LOGICAL_OR; + rightPrecedenceRequirement = Precedence.logicalOr; break; case "&&": - leftPrecedenceRequirement = LOGICAL_AND; + leftPrecedenceRequirement = Precedence.logicalAnd; // x && (y && z) <=> (x && y) && z. - rightPrecedenceRequirement = LOGICAL_AND; + rightPrecedenceRequirement = Precedence.logicalAnd; break; case "|": - leftPrecedenceRequirement = BIT_OR; + leftPrecedenceRequirement = Precedence.bitOr; // x | (y | z) <=> (x | y) | z. - rightPrecedenceRequirement = BIT_OR; + rightPrecedenceRequirement = Precedence.bitOr; break; case "^": - leftPrecedenceRequirement = BIT_XOR; + leftPrecedenceRequirement = Precedence.bitXor; // x ^ (y ^ z) <=> (x ^ y) ^ z. - rightPrecedenceRequirement = BIT_XOR; + rightPrecedenceRequirement = Precedence.bitXor; break; case "&": - leftPrecedenceRequirement = BIT_AND; + leftPrecedenceRequirement = Precedence.bitAnd; // x & (y & z) <=> (x & y) & z. - rightPrecedenceRequirement = BIT_AND; + rightPrecedenceRequirement = Precedence.bitAnd; break; case "==": case "!=": case "===": case "!==": - leftPrecedenceRequirement = EQUALITY; - rightPrecedenceRequirement = RELATIONAL; + leftPrecedenceRequirement = Precedence.equality; + rightPrecedenceRequirement = Precedence.relational; break; case "<": case ">": @@ -624,36 +624,36 @@ class SizeEstimator implements NodeVisitor { case ">=": case "instanceof": case "in": - leftPrecedenceRequirement = RELATIONAL; - rightPrecedenceRequirement = SHIFT; + leftPrecedenceRequirement = Precedence.relational; + rightPrecedenceRequirement = Precedence.shift; break; case ">>": case "<<": case ">>>": - leftPrecedenceRequirement = SHIFT; - rightPrecedenceRequirement = ADDITIVE; + leftPrecedenceRequirement = Precedence.shift; + rightPrecedenceRequirement = Precedence.additive; break; case "+": case "-": - leftPrecedenceRequirement = ADDITIVE; + leftPrecedenceRequirement = Precedence.additive; // We cannot remove parenthesis for "+" because // x + (y + z) (x + y) + z: // Example: // "a" + (1 + 2) => "a3"; // ("a" + 1) + 2 => "a12"; - rightPrecedenceRequirement = MULTIPLICATIVE; + rightPrecedenceRequirement = Precedence.multiplicative; break; case "*": case "/": case "%": - leftPrecedenceRequirement = MULTIPLICATIVE; + leftPrecedenceRequirement = Precedence.multiplicative; // We cannot remove parenthesis for "*" because of precision issues. - rightPrecedenceRequirement = UNARY; + rightPrecedenceRequirement = Precedence.unary; break; case "**": - leftPrecedenceRequirement = EXPONENTIATION; + leftPrecedenceRequirement = Precedence.exponentiation; // We cannot remove parenthesis for "**" because of precision issues. - rightPrecedenceRequirement = UNARY; + rightPrecedenceRequirement = Precedence.unary; break; default: throw UnsupportedError("Forgot operator: $op"); @@ -691,13 +691,13 @@ class SizeEstimator implements NodeVisitor { default: out('$op'); // '$op' } - visitNestedExpression(unary.argument, UNARY, + visitNestedExpression(unary.argument, Precedence.unary, newInForInit: inForInit, newAtStatementBegin: false); } @override void visitPostfix(Postfix postfix) { - visitNestedExpression(postfix.argument, CALL, + visitNestedExpression(postfix.argument, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); out(postfix.op); // '${postfix.op}' } @@ -757,7 +757,7 @@ class SizeEstimator implements NodeVisitor { @override void visitAccess(PropertyAccess access) { - visitNestedExpression(access.receiver, CALL, + visitNestedExpression(access.receiver, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); Node selector = access.selector; if (selector is LiteralString) { @@ -786,7 +786,7 @@ class SizeEstimator implements NodeVisitor { return; } out('['); // '[' - visitNestedExpression(access.selector, EXPRESSION, + visitNestedExpression(access.selector, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(']'); // '] } @@ -815,11 +815,11 @@ class SizeEstimator implements NodeVisitor { int arrowFunctionOut(ArrowFunction fun, VarCollector vars) { // TODO: support static, get/set, async, and generators. if (fun.params.length == 1) { - visitNestedExpression(fun.params.single, ASSIGNMENT, + visitNestedExpression(fun.params.single, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); } else { out("("); - visitCommaSeparated(fun.params, PRIMARY, + visitCommaSeparated(fun.params, Precedence.primary, newInForInit: false, newAtStatementBegin: false); out(")"); @@ -835,7 +835,7 @@ class SizeEstimator implements NodeVisitor { // https://tc39.github.io/ecma262/#sec-arrow-function-definitions bool needsParens = body is ObjectInitializer; if (needsParens) out("("); - visitNestedExpression(body as Expression, ASSIGNMENT, + visitNestedExpression(body as Expression, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); if (needsParens) out(")"); closingPosition = charCount; @@ -918,7 +918,7 @@ class SizeEstimator implements NodeVisitor { @override visitParentheses(Parentheses node) { out('('); // '(' - visitNestedExpression(node.enclosed, EXPRESSION, + visitNestedExpression(node.enclosed, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); // ')' } @@ -947,7 +947,7 @@ class SizeEstimator implements NodeVisitor { out(','); // ',' continue; } - visitNestedExpression(element, ASSIGNMENT, + visitNestedExpression(element, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); // We can skip the trailing "," for the last element (since it's not // an array hole). @@ -992,7 +992,7 @@ class SizeEstimator implements NodeVisitor { void visitProperty(Property node) { propertyNameOut(node); out(':'); // ':' - visitNestedExpression(node.value, ASSIGNMENT, + visitNestedExpression(node.value, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); } @@ -1008,7 +1008,7 @@ class SizeEstimator implements NodeVisitor { // TODO: support static, get/set, async, and generators. Fun fun = node.function; out("("); - visitCommaSeparated(fun.params, PRIMARY, + visitCommaSeparated(fun.params, Precedence.primary, newInForInit: false, newAtStatementBegin: false); out(")"); int closingPosition = blockOut(fun.body); diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart index 368f25a6184..095df0218aa 100644 --- a/pkg/compiler/lib/src/js_backend/backend.dart +++ b/pkg/compiler/lib/src/js_backend/backend.dart @@ -23,27 +23,24 @@ abstract class FunctionCompiler { List get tasks; } +enum _Decision { + unknown, + mustNotInline, + mayInlineInLoopMustNotOutside, + canInlineInLoopMustNotOutside, + canInlineInLoopMayInlineOutside, + canInline, +} + /* * Invariants: * canInline(function) implies canInline(function, insideLoop:true) * !canInline(function, insideLoop: true) implies !canInline(function) */ class FunctionInlineCache { - static const int _unknown = -1; - static const int _mustNotInline = 0; - // May-inline-in-loop means that the function may not be inlined outside loops - // but may be inlined in a loop. - static const int _mayInlineInLoopMustNotOutside = 1; - // The function can be inlined in a loop, but not outside. - static const int _canInlineInLoopMustNotOutside = 2; - // May-inline means that we know that it can be inlined inside a loop, but - // don't know about the general case yet. - static const int _canInlineInLoopMayInlineOutside = 3; - static const int _canInline = 4; - final AnnotationsData _annotationsData; - final Map _cachedDecisions = {}; + final Map _cachedDecisions = {}; FunctionInlineCache(this._annotationsData) {} @@ -58,103 +55,94 @@ class FunctionInlineCache { // Returns `null` otherwise. bool? canInline(FunctionEntity element, {required bool insideLoop}) { assert(checkFunction(element), failedAt(element)); - int? decision = _cachedDecisions[element]; - if (decision == null) { - // TODO(sra): Have annotations for mustInline / noInline for constructor - // bodies. (There used to be some logic here to have constructor bodies, - // inherit the settings from annotations on the generative - // constructor. This was conflated with the heuristic decisions, leading - // to lack of inlining where it was beneficial.) - decision = _unknown; - } + // TODO(sra): Have annotations for mustInline / noInline for constructor + // bodies. (There used to be some logic here to have constructor bodies, + // inherit the settings from annotations on the generative + // constructor. This was conflated with the heuristic decisions, leading + // to lack of inlining where it was beneficial.) + + final decision = _cachedDecisions[element] ?? _Decision.unknown; if (insideLoop) { switch (decision) { - case _mustNotInline: + case _Decision.mustNotInline: return false; - case _unknown: - case _mayInlineInLoopMustNotOutside: + case _Decision.unknown: + case _Decision.mayInlineInLoopMustNotOutside: // We know we can't inline outside a loop, but don't know for the // loop case. Return `null` to indicate that we don't know yet. return null; - case _canInlineInLoopMustNotOutside: - case _canInlineInLoopMayInlineOutside: - case _canInline: + case _Decision.canInlineInLoopMustNotOutside: + case _Decision.canInlineInLoopMayInlineOutside: + case _Decision.canInline: return true; } } else { switch (decision) { - case _mustNotInline: - case _mayInlineInLoopMustNotOutside: - case _canInlineInLoopMustNotOutside: + case _Decision.mustNotInline: + case _Decision.mayInlineInLoopMustNotOutside: + case _Decision.canInlineInLoopMustNotOutside: return false; - case _unknown: - case _canInlineInLoopMayInlineOutside: + case _Decision.unknown: + case _Decision.canInlineInLoopMayInlineOutside: // We know we can inline inside a loop, but don't know for the // non-loop case. Return `null` to indicate that we don't know yet. return null; - case _canInline: + case _Decision.canInline: return true; } } - - // Quiet static checker. - return null; } void markAsInlinable(FunctionEntity element, {required bool insideLoop}) { assert(checkFunction(element), failedAt(element)); - int? oldDecision = _cachedDecisions[element]; - - if (oldDecision == null) { - oldDecision = _unknown; - } + final oldDecision = _cachedDecisions[element] ?? _Decision.unknown; if (insideLoop) { switch (oldDecision) { - case _mustNotInline: + case _Decision.mustNotInline: throw failedAt( element, "Can't mark $element as non-inlinable and inlinable at the " "same time."); - case _unknown: + case _Decision.unknown: // We know that it can be inlined in a loop, but don't know about the // non-loop case yet. - _cachedDecisions[element] = _canInlineInLoopMayInlineOutside; + _cachedDecisions[element] = _Decision.canInlineInLoopMayInlineOutside; break; - case _mayInlineInLoopMustNotOutside: - _cachedDecisions[element] = _canInlineInLoopMustNotOutside; + case _Decision.mayInlineInLoopMustNotOutside: + _cachedDecisions[element] = _Decision.canInlineInLoopMustNotOutside; break; - case _canInlineInLoopMustNotOutside: - case _canInlineInLoopMayInlineOutside: - case _canInline: + case _Decision.canInlineInLoopMustNotOutside: + case _Decision.canInlineInLoopMayInlineOutside: + case _Decision.canInline: // Do nothing. break; } } else { switch (oldDecision) { - case _mustNotInline: - case _mayInlineInLoopMustNotOutside: - case _canInlineInLoopMustNotOutside: + case _Decision.mustNotInline: + case _Decision.mayInlineInLoopMustNotOutside: + case _Decision.canInlineInLoopMustNotOutside: throw failedAt( element, "Can't mark $element as non-inlinable and inlinable at the " "same time."); - case _unknown: - case _canInlineInLoopMayInlineOutside: - _cachedDecisions[element] = _canInline; + case _Decision.unknown: + case _Decision.canInlineInLoopMayInlineOutside: + _cachedDecisions[element] = _Decision.canInline; break; - case _canInline: + case _Decision.canInline: // Do nothing. break; } @@ -163,55 +151,51 @@ class FunctionInlineCache { void markAsNonInlinable(FunctionEntity element, {bool insideLoop = true}) { assert(checkFunction(element), failedAt(element)); - int? oldDecision = _cachedDecisions[element]; - - if (oldDecision == null) { - oldDecision = _unknown; - } + final oldDecision = _cachedDecisions[element] ?? _Decision.unknown; if (insideLoop) { switch (oldDecision) { - case _canInlineInLoopMustNotOutside: - case _canInlineInLoopMayInlineOutside: - case _canInline: + case _Decision.canInlineInLoopMustNotOutside: + case _Decision.canInlineInLoopMayInlineOutside: + case _Decision.canInline: throw failedAt( element, "Can't mark $element as non-inlinable and inlinable at the " "same time."); - case _mayInlineInLoopMustNotOutside: - case _unknown: - _cachedDecisions[element] = _mustNotInline; + case _Decision.mayInlineInLoopMustNotOutside: + case _Decision.unknown: + _cachedDecisions[element] = _Decision.mustNotInline; break; - case _mustNotInline: + case _Decision.mustNotInline: // Do nothing. break; } } else { switch (oldDecision) { - case _canInline: + case _Decision.canInline: throw failedAt( element, "Can't mark $element as non-inlinable and inlinable at the " "same time."); - case _unknown: + case _Decision.unknown: // We can't inline outside a loop, but we might still be allowed to do // so outside. - _cachedDecisions[element] = _mayInlineInLoopMustNotOutside; + _cachedDecisions[element] = _Decision.mayInlineInLoopMustNotOutside; break; - case _canInlineInLoopMayInlineOutside: + case _Decision.canInlineInLoopMayInlineOutside: // We already knew that the function could be inlined inside a loop, // but didn't have information about the non-loop case. Now we know // that it can't be inlined outside a loop. - _cachedDecisions[element] = _canInlineInLoopMustNotOutside; + _cachedDecisions[element] = _Decision.canInlineInLoopMustNotOutside; break; - case _mayInlineInLoopMustNotOutside: - case _canInlineInLoopMustNotOutside: - case _mustNotInline: + case _Decision.mayInlineInLoopMustNotOutside: + case _Decision.canInlineInLoopMustNotOutside: + case _Decision.mustNotInline: // Do nothing. break; } diff --git a/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart b/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart index 8c6ae62a34e..89f23ae0f91 100644 --- a/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart +++ b/pkg/compiler/lib/src/js_backend/deferred_holder_expression.dart @@ -2,7 +2,7 @@ // 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. -import 'package:js_ast/src/precedence.dart' as js show PRIMARY; +import 'package:js_ast/src/precedence.dart' as js show Precedence; import 'package:front_end/src/api_unstable/dart2js.dart' show $A; import '../common/elements.dart' show JCommonElements; @@ -120,7 +120,8 @@ class DeferredHolderExpression extends js.DeferredExpression } @override - int get precedenceLevel => _value?.precedenceLevel ?? js.PRIMARY; + js.Precedence get precedenceLevel => + _value?.precedenceLevel ?? js.Precedence.primary; @override int get hashCode { @@ -209,7 +210,7 @@ class DeferredHolderParameter extends js.Expression implements js.Parameter { } @override - int get precedenceLevel => js.PRIMARY; + js.Precedence get precedenceLevel => js.Precedence.primary; @override T accept(js.NodeVisitor visitor) => visitor.visitParameter(this); diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart index 34dd323a8fc..f9330e463dd 100644 --- a/pkg/compiler/lib/src/js_backend/namer.dart +++ b/pkg/compiler/lib/src/js_backend/namer.dart @@ -1427,6 +1427,22 @@ class ConstantNamingVisitor implements ConstantValueVisitor { } } +/// Hash seeds by kind of constant. These mostly ensure that similar collections +/// of different kinds do not collide. +enum _HashSeed { + function, + string, + constructed, + type, + interceptor, + infinity, + record, + list, + set, + map, + javaScriptObject, +} + /// Generates canonical hash values for [ConstantValue]s. /// /// Unfortunately, [Constant.hashCode] is not stable under minor perturbations, @@ -1441,20 +1457,6 @@ class ConstantCanonicalHasher implements ConstantValueVisitor { final JClosedWorld _closedWorld; final Map _hashes = {}; - // Hash seeds by kind of constant. These mostly ensure that similar - // collections of different kinds do not collide. - static const int _seedFunction = 1; - static const int _seedString = 2; - static const int _seedConstructed = 3; - static const int _seedType = 4; - static const int _seedInterceptor = 5; - static const int _seedInfinity = 6; - static const int _seedRecord = 7; - static const int _seedList = 10; - static const int _seedSet = 11; - static const int _seedMap = 12; - static const int _seedJavaScriptObject = 13; - ConstantCanonicalHasher(this._namer, this._closedWorld); JElementEnvironment get _elementEnvironment => @@ -1477,7 +1479,7 @@ class ConstantCanonicalHasher implements ConstantValueVisitor { @override int visitFunction(FunctionConstantValue constant, [_]) { - return _hashString(_seedFunction, constant.element.name!); + return _hashString(_HashSeed.function.index, constant.element.name!); } @override @@ -1501,28 +1503,29 @@ class ConstantCanonicalHasher implements ConstantValueVisitor { @override int visitString(StringConstantValue constant, [_]) { - return _hashString(_seedString, constant.stringValue); + return _hashString(_HashSeed.string.index, constant.stringValue); } @override int visitList(ListConstantValue constant, [_]) { - return _hashList(_seedList, constant.entries); + return _hashList(_HashSeed.list.index, constant.entries); } @override int visitSet(SetConstantValue constant, [_]) { - return _hashList(_seedSet, constant.values); + return _hashList(_HashSeed.set.index, constant.values); } @override int visitMap(MapConstantValue constant, [_]) { - int hash = _hashList(_seedMap, constant.keys); + int hash = _hashList(_HashSeed.map.index, constant.keys); return _hashList(hash, constant.values); } @override int visitConstructed(ConstructedConstantValue constant, [_]) { - int hash = _hashString(_seedConstructed, constant.type.element.name); + int hash = + _hashString(_HashSeed.constructed.index, constant.type.element.name); _elementEnvironment.forEachInstanceField(constant.type.element, (_, FieldEntity field) { if (_fieldAnalysis.getFieldData(field as JField).isElided) return; @@ -1533,7 +1536,8 @@ class ConstantCanonicalHasher implements ConstantValueVisitor { @override int visitRecord(RecordConstantValue constant, [_]) { - int hash = _combine(_seedRecord, _hashInt(constant.shape.fieldCount)); + int hash = + _combine(_HashSeed.record.index, _hashInt(constant.shape.fieldCount)); for (String name in constant.shape.fieldNames) { hash = _hashString(hash, name); } @@ -1545,18 +1549,18 @@ class ConstantCanonicalHasher implements ConstantValueVisitor { DartType type = constant.representedType; // This name includes the library name and type parameters. String name = _namer.getTypeRepresentationForTypeConstant(type); - return _hashString(_seedType, name); + return _hashString(_HashSeed.type.index, name); } @override int visitInterceptor(InterceptorConstantValue constant, [_]) { String typeName = constant.cls.name; - return _hashString(_seedInterceptor, typeName); + return _hashString(_HashSeed.interceptor.index, typeName); } @override int visitJavaScriptObject(JavaScriptObjectConstantValue constant, [_]) { - int hash = _seedJavaScriptObject; + int hash = _HashSeed.javaScriptObject.index; hash = _hashList(hash, constant.keys); hash = _hashList(hash, constant.values); return hash; @@ -1636,7 +1640,7 @@ class ConstantCanonicalHasher implements ConstantValueVisitor { hash = _combine(hash, fraction); return hash; } else if (value.isInfinite) { - return _combine(_seedInfinity, sign); + return _combine(_HashSeed.infinity.index, sign); } else if (value.isNaN) { return 7; } else { diff --git a/pkg/compiler/lib/src/js_backend/string_reference.dart b/pkg/compiler/lib/src/js_backend/string_reference.dart index 29d1807a793..a1f066f18c2 100644 --- a/pkg/compiler/lib/src/js_backend/string_reference.dart +++ b/pkg/compiler/lib/src/js_backend/string_reference.dart @@ -60,6 +60,8 @@ /// names. library js_backend.string_reference; +import 'package:js_ast/src/precedence.dart' as js_precedence; + import '../constants/values.dart' show StringConstantValue; import '../js/js.dart' as js; import '../serialization/serialization.dart'; @@ -131,7 +133,7 @@ class StringReference extends js.DeferredExpression implements js.AstContainer { // Precedence will be CALL or LEFT_HAND_SIDE depending on what expression the // reference is resolved to. @override - int get precedenceLevel => value.precedenceLevel; + js_precedence.Precedence get precedenceLevel => value.precedenceLevel; @override StringReference withSourceInformation( diff --git a/pkg/compiler/lib/src/js_backend/type_reference.dart b/pkg/compiler/lib/src/js_backend/type_reference.dart index e7dc28f58c3..7b78c7e0237 100644 --- a/pkg/compiler/lib/src/js_backend/type_reference.dart +++ b/pkg/compiler/lib/src/js_backend/type_reference.dart @@ -64,6 +64,8 @@ library js_backend.type_reference; import 'package:front_end/src/api_unstable/dart2js.dart' show $0, $9, $A, $Z, $_, $a, $z; +import 'package:js_ast/src/precedence.dart' as js_precedence; + import '../common/elements.dart' show CommonElements; import '../elements/types.dart'; import '../js/js.dart' as js; @@ -146,7 +148,7 @@ class TypeReference extends js.DeferredExpression implements js.AstContainer { // Precedence will be CALL or LEFT_HAND_SIDE depending on what expression the // reference is resolved to. @override - int get precedenceLevel => value.precedenceLevel; + js_precedence.Precedence get precedenceLevel => value.precedenceLevel; @override TypeReference withSourceInformation( diff --git a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart index e1e2b55cf15..d9faeeb0c43 100644 --- a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart +++ b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart @@ -158,8 +158,7 @@ class ClassStubGenerator { } StubMethod generateStubForNoSuchMethod(jsAst.Name name, Selector selector) { - // Values match JSInvocationMirror in js-helper library. - int type = selector.invocationMirrorKind; + int type = selector.invocationMirrorKind.value; List parameterNames = List.generate(selector.argumentCount, (i) => '\$$i') + List.generate(selector.typeArgumentCount, (i) => '\$T${i + 1}'); diff --git a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart index c98b00daa96..76476360f7b 100644 --- a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart +++ b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart @@ -87,7 +87,8 @@ class _MetadataList extends jsAst.DeferredExpression { } @override - int get precedenceLevel => js_precedence.PRIMARY; + js_precedence.Precedence get precedenceLevel => + js_precedence.Precedence.primary; } class MetadataCollector implements jsAst.TokenFinalizer { diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart index a11388c8f57..e7dd978efe7 100644 --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart @@ -2148,5 +2148,6 @@ class DeferredPrimaryExpression extends js.DeferredExpression { } @override - int get precedenceLevel => js_precedence.PRIMARY; + js_precedence.Precedence get precedenceLevel => + js_precedence.Precedence.primary; } diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart index 1c805245aba..32112110711 100644 --- a/pkg/compiler/lib/src/kernel/dart2js_target.dart +++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart @@ -20,7 +20,7 @@ import 'package:kernel/target/targets.dart'; import 'package:kernel/type_environment.dart'; import '../options.dart'; -import 'invocation_mirror_constants.dart'; +import 'invocation_mirror.dart'; import 'transformations/modular/transform.dart' as modularTransforms; const Iterable _allowedDartSchemePaths = [ @@ -183,15 +183,15 @@ class Dart2jsTarget extends Target { ir.Arguments arguments, int offset, bool isSuper) { - int kind; + InvocationMirrorKind kind; if (name.startsWith('get:')) { - kind = invocationMirrorGetterKind; + kind = InvocationMirrorKind.getter; name = name.substring(4); } else if (name.startsWith('set:')) { - kind = invocationMirrorSetterKind; + kind = InvocationMirrorKind.setter; name = name.substring(4); } else { - kind = invocationMirrorMethodKind; + kind = InvocationMirrorKind.method; } return ir.StaticInvocation( coreTypes.index @@ -211,7 +211,7 @@ class Dart2jsTarget extends Target { })), keyType: coreTypes.stringNonNullableRawType) ..isConst = (arguments.named.length == 0) ..fileOffset = arguments.fileOffset, - ir.IntLiteral(kind)..fileOffset = offset, + ir.IntLiteral(kind.value)..fileOffset = offset, ])) ..fileOffset = offset; } @@ -263,6 +263,7 @@ const requiredLibraries = >{ 'dart:_http', 'dart:_interceptors', 'dart:_internal', + 'dart:_invocation_mirror_constants', 'dart:_js', 'dart:_js_annotations', 'dart:_js_embedded_names', @@ -305,6 +306,7 @@ const requiredLibraries = >{ 'dart:_http', 'dart:_interceptors', 'dart:_internal', + 'dart:_invocation_mirror_constants', 'dart:_js', 'dart:_js_annotations', 'dart:_js_embedded_names', diff --git a/pkg/compiler/lib/src/kernel/invocation_mirror.dart b/pkg/compiler/lib/src/kernel/invocation_mirror.dart new file mode 100644 index 00000000000..cc0b9019430 --- /dev/null +++ b/pkg/compiler/lib/src/kernel/invocation_mirror.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// 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. + +import 'package:js_runtime/synced/invocation_mirror_constants.dart' + as constants; + +enum InvocationMirrorKind { + method._(constants.method), + getter._(constants.getter), + setter._(constants.setter), + ; + + // This is preferred over [index] to avoid coupling the enum ordering to + // codegen. + final int value; + + const InvocationMirrorKind._(this.value); +} diff --git a/pkg/compiler/lib/src/kernel/invocation_mirror_constants.dart b/pkg/compiler/lib/src/kernel/invocation_mirror_constants.dart deleted file mode 100644 index 246429e7c27..00000000000 --- a/pkg/compiler/lib/src/kernel/invocation_mirror_constants.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file -// 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. - -const int invocationMirrorMethodKind = 0; -const int invocationMirrorGetterKind = 1; -const int invocationMirrorSetterKind = 2; diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart index 0fd748e0628..affde1f6949 100644 --- a/pkg/compiler/lib/src/ssa/builder.dart +++ b/pkg/compiler/lib/src/ssa/builder.dart @@ -45,7 +45,7 @@ import '../js_model/js_world.dart' show JClosedWorld; import '../js_model/locals.dart' show GlobalLocalsMap, JumpVisitor; import '../js_model/type_recipe.dart'; import '../js_model/records.dart' show RecordData, JRecordGetter; -import '../kernel/invocation_mirror_constants.dart'; +import '../kernel/invocation_mirror.dart'; import '../native/behavior.dart'; import '../native/js.dart'; import '../options.dart'; @@ -2100,7 +2100,7 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault return; } } - assert(!current!.isClosed()); + assert(!current!.isClosed); if (stack.isNotEmpty) { reporter.internalError( NO_LOCATION_SPANNABLE, 'Non-empty instruction stack'); @@ -2681,8 +2681,7 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault node.condition.accept(this); assert(!isAborted()); HInstruction conditionInstruction = popBoolified(); - HBasicBlock conditionEndBlock = - close(HLoopBranch(conditionInstruction, HLoopBranch.DO_WHILE_LOOP)); + HBasicBlock conditionEndBlock = close(HLoopBranch(conditionInstruction)); HBasicBlock avoidCriticalEdge = addNewBlock(); conditionEndBlock.addSuccessor(avoidCriticalEdge); @@ -2705,7 +2704,7 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault SubGraph bodyGraph = SubGraph(loopEntryBlock, bodyExitBlock); final newLoopInfo = loopEntryBlock.loopInformation!; HLoopBlockInformation loopBlockInfo = HLoopBlockInformation( - HLoopBlockInformation.DO_WHILE_LOOP, + LoopBlockInformationKind.doWhileLoop, null, wrapExpressionGraph(conditionExpression), wrapStatementGraph(bodyGraph), @@ -4701,15 +4700,15 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault int kind = _readIntLiteral(invocation.arguments.positional[4]); Name memberName = Name(name, _currentFrame!.member.library.canonicalUri); - Selector? selector; - switch (kind) { - case invocationMirrorGetterKind: + Selector selector; + switch (InvocationMirrorKind.values[kind]) { + case InvocationMirrorKind.getter: selector = Selector.getter(memberName); break; - case invocationMirrorSetterKind: + case InvocationMirrorKind.setter: selector = Selector.setter(memberName); break; - case invocationMirrorMethodKind: + case InvocationMirrorKind.method: if (memberName == Names.INDEX_NAME) { selector = Selector.index(); } else if (memberName == Names.INDEX_SET_NAME) { @@ -4763,7 +4762,7 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault value.accept(this); namedValues[name] = pop(); }); - for (String name in selector!.callStructure.getOrderedNamedArguments()) { + for (String name in selector.callStructure.getOrderedNamedArguments()) { arguments.add(namedValues[name]!); } } @@ -4776,7 +4775,7 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault List argumentNames = []; for (String argumentName - in selector!.callStructure.getOrderedNamedArguments()) { + in selector.callStructure.getOrderedNamedArguments()) { ConstantValue argumentNameConstant = constant_system.createString(argumentName); argumentNames.add(graph.addConstant(argumentNameConstant, closedWorld) @@ -4793,7 +4792,7 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault js.Name internalName = _namer.invocationName(selector); ConstantValue kindConstant = - constant_system.createIntFromInt(selector.invocationMirrorKind); + constant_system.createIntFromInt(selector.invocationMirrorKind.index); _pushStaticInvocation( _commonElements.createUnmangledInvocationMirror, diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart index f5b58f9dfbe..83328c33126 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart @@ -214,21 +214,24 @@ class _CodegenMetrics extends MetricsBase { ]; } -class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { - /// Returned by [expressionType] to tell how code can be generated for - /// a subgraph. - /// - [TYPE_STATEMENT] means that the graph must be generated as a statement, - /// which is always possible. - /// - [TYPE_EXPRESSION] means that the graph can be generated as an expression, - /// or possibly several comma-separated expressions. - /// - [TYPE_DECLARATION] means that the graph can be generated as an - /// expression, and that it only generates expressions of the form +/// Returned by [_expressionType] to tell how code can be generated for +/// a subgraph. +enum _ExpressionCodegenType { + /// The graph must be generated as a statement, which is always possible. + statement, + + /// The graph can be generated as an expression, or possibly several + /// comma-separated expressions. + expression, + + /// The graph can be generated as an expression, and it only generates + /// expressions of the form /// variable = expression /// which are also valid as parts of a "var" declaration. - static const int TYPE_STATEMENT = 0; - static const int TYPE_EXPRESSION = 1; - static const int TYPE_DECLARATION = 2; + declaration, +} +class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { /// Whether we are currently generating expressions instead of statements. /// This includes declarations, which are generated as expressions. bool isGeneratingExpression = false; @@ -455,7 +458,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { /// Declarations must only generate assignments on the form "id = expression", /// and not, e.g., expressions where the value isn't assigned, or where it's /// assigned to something that's not a simple variable. - int expressionType(HExpressionInformation info) { + _ExpressionCodegenType _expressionType(HExpressionInformation info) { // The only HExpressionInformation used as part of a HBlockInformation is // current HSubExpressionBlockInformation, so it's the only one reaching // here. If we start using the other HExpressionInformation types too, @@ -470,19 +473,19 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { // statement, and in the latter case, we can return immediately since // it can't get any worse. E.g., a function call where the return value // isn't used can't be in a declaration. - int result = TYPE_DECLARATION; + var result = _ExpressionCodegenType.declaration; HBasicBlock basicBlock = limits.start; do { HInstruction current = basicBlock.first!; while (current != basicBlock.last) { // E.g, bounds check. if (current.isControlFlow()) { - return TYPE_STATEMENT; + return _ExpressionCodegenType.statement; } // HFieldSet generates code on the form "x.y = ...", which isn't valid // in a declaration. if (current.usedBy.isEmpty || current is HFieldSet) { - result = TYPE_EXPRESSION; + result = _ExpressionCodegenType.expression; } current = current.next!; } @@ -497,18 +500,20 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { basicBlock = basicBlock.successors[0]; } else { // We allow an expression to end on an HIf (a condition expression). - return identical(basicBlock, limits.end) ? result : TYPE_STATEMENT; + return identical(basicBlock, limits.end) + ? result + : _ExpressionCodegenType.statement; } } else { // Expression-incompatible control flow. - return TYPE_STATEMENT; + return _ExpressionCodegenType.statement; } } while (limits.contains(basicBlock)); return result; } bool isJSExpression(HExpressionInformation info) { - return !identical(expressionType(info), TYPE_STATEMENT); + return !identical(_expressionType(info), _ExpressionCodegenType.statement); } bool isJSCondition(HExpressionInformation? info) { @@ -516,7 +521,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { info as HSubExpressionBlockInformation; SubExpression? limits = info.subExpression; - return !identical(expressionType(info), TYPE_STATEMENT) && + return !identical( + _expressionType(info), _ExpressionCodegenType.statement) && (limits!.end.last is HConditionalBranch); } @@ -934,15 +940,15 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { switch (info.kind) { // Treat all three "test-first" loops the same way. - case HLoopBlockInformation.FOR_LOOP: - case HLoopBlockInformation.WHILE_LOOP: - case HLoopBlockInformation.FOR_IN_LOOP: - case HLoopBlockInformation.SWITCH_CONTINUE_LOOP: + case LoopBlockInformationKind.forLoop: + case LoopBlockInformationKind.whileLoop: + case LoopBlockInformationKind.forInLoop: + case LoopBlockInformationKind.switchContinueLoop: HExpressionInformation? initialization = info.initializer; - int initializationType = TYPE_STATEMENT; + var initializationType = _ExpressionCodegenType.statement; if (initialization != null) { - initializationType = expressionType(initialization); - if (initializationType == TYPE_STATEMENT) { + initializationType = _expressionType(initialization); + if (initializationType == _ExpressionCodegenType.statement) { generateStatements(initialization); initialization = null; } @@ -1061,7 +1067,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { sourceInformation: info.sourceInformation); } break; - case HLoopBlockInformation.DO_WHILE_LOOP: + case LoopBlockInformationKind.doWhileLoop: if (info.initializer != null) { generateStatements(info.initializer); } @@ -1131,12 +1137,12 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { } currentContainer = oldContainer; break; - default: + case LoopBlockInformationKind.notALoop: failedAt(condition!.conditionExpression!, 'Unexpected loop kind: ${info.kind}.'); } js.Statement result = loop; - if (info.kind == HLoopBlockInformation.SWITCH_CONTINUE_LOOP) { + if (info.kind == LoopBlockInformationKind.switchContinueLoop) { String continueLabelString = _namer.implicitContinueLabelName(info.target!); result = js.LabeledStatement(continueLabelString, result); @@ -2311,16 +2317,17 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { js.Name name = _namer.instanceFieldPropertyName(element); use(node.receiver); js.Expression fieldReference = js.PropertyAccess(pop(), name); - if (node.isPreOp) { - push(js.Prefix(node.jsOp, fieldReference) - .withSourceInformation(node.sourceInformation)); - } else if (node.isPostOp) { - push(js.Postfix(node.jsOp, fieldReference) - .withSourceInformation(node.sourceInformation)); - } else { - use(node.value); - push(js.Assignment.compound(fieldReference, node.jsOp, pop()) - .withSourceInformation(node.sourceInformation)); + switch (node.opKind) { + case ReadModifyWriteKind.prefix: + push(js.Prefix(node.jsOp, fieldReference) + .withSourceInformation(node.sourceInformation)); + case ReadModifyWriteKind.postfix: + push(js.Postfix(node.jsOp, fieldReference) + .withSourceInformation(node.sourceInformation)); + case ReadModifyWriteKind.assign: + use(node.value); + push(js.Assignment.compound(fieldReference, node.jsOp, pop()) + .withSourceInformation(node.sourceInformation)); } } @@ -2691,9 +2698,9 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { // If the checks always succeeds, we would have removed the bounds check // completely. - assert(node.staticChecks != HBoundsCheck.ALWAYS_TRUE); + assert(node.staticChecks != StaticBoundsChecks.alwaysTrue); - if (node.staticChecks == HBoundsCheck.ALWAYS_FALSE) { + if (node.staticChecks == StaticBoundsChecks.alwaysFalse) { _pushThrowWithHelper(_commonElements.throwIndexOutOfRangeException, [node.array, node.reportedIndex], sourceInformation: node.sourceInformation); @@ -2728,7 +2735,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { // This test is 'NaN-safe' since `a!==b` is the same as `!(a===b)`. under = js.js("# >>> 0 !== #", [jsIndex, jsIndex]); indexCanBeNaN = false; - } else if (node.staticChecks != HBoundsCheck.ALWAYS_ABOVE_ZERO) { + } else if (node.staticChecks != StaticBoundsChecks.alwaysAboveZero) { use(index); // The index must be an `int`, otherwise we could have used the combined // check above. @@ -2739,7 +2746,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { } } - if (node.staticChecks != HBoundsCheck.ALWAYS_BELOW_LENGTH) { + if (node.staticChecks != StaticBoundsChecks.alwaysBelowLength) { use(index); js.Expression jsIndex = pop(); use(node.length); @@ -2982,18 +2989,20 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { js.Block oldContainer = currentContainer; js.Block body = currentContainer = js.Block.empty(); final sourceInformation = node.sourceInformation; - if (node.isArgumentTypeCheck) { - use(node.checkedInput); - _pushCallStatic(_commonElements.throwIllegalArgumentException, [pop()], - node.sourceInformation); - pushStatement(js.Return(pop()).withSourceInformation(sourceInformation)); - } else if (node.isReceiverTypeCheck) { - use(node.checkedInput); - js.Name methodName = - _namer.invocationName(node.receiverTypeCheckSelector!); - js.Expression call = js.propertyCall( - pop(), methodName, []).withSourceInformation(sourceInformation); - pushStatement(js.Return(call).withSourceInformation(sourceInformation)); + switch (node.kind) { + case PrimitiveCheckKind.argumentType: + use(node.checkedInput); + _pushCallStatic(_commonElements.throwIllegalArgumentException, [pop()], + node.sourceInformation); + pushStatement( + js.Return(pop()).withSourceInformation(sourceInformation)); + case PrimitiveCheckKind.receiverType: + use(node.checkedInput); + js.Name methodName = + _namer.invocationName(node.receiverTypeCheckSelector!); + js.Expression call = js.propertyCall( + pop(), methodName, []).withSourceInformation(sourceInformation); + pushStatement(js.Return(call).withSourceInformation(sourceInformation)); } currentContainer = oldContainer; final then = unwrapStatement(body); diff --git a/pkg/compiler/lib/src/ssa/logging.dart b/pkg/compiler/lib/src/ssa/logging.dart index 32b8a2b408d..9ef4915fffd 100644 --- a/pkg/compiler/lib/src/ssa/logging.dart +++ b/pkg/compiler/lib/src/ssa/logging.dart @@ -245,11 +245,10 @@ class OptimizationTestLog { void registerPrimitiveCheck(HInstruction original, HPrimitiveCheck check) { Features features = Features(); - if (check.isReceiverTypeCheck) { - features['kind'] = 'receiver'; - } else if (check.isArgumentTypeCheck) { - features['kind'] = 'argument'; - } + features['kind'] = switch (check.kind) { + PrimitiveCheckKind.receiverType => 'receiver', + PrimitiveCheckKind.argumentType => 'argument', + }; features['type'] = '${check.typeExpression}'; entries.add(OptimizationLogEntry('PrimitiveCheck', features)); } diff --git a/pkg/compiler/lib/src/ssa/loop_handler.dart b/pkg/compiler/lib/src/ssa/loop_handler.dart index 61b11dfd137..0f4575e8408 100644 --- a/pkg/compiler/lib/src/ssa/loop_handler.dart +++ b/pkg/compiler/lib/src/ssa/loop_handler.dart @@ -282,9 +282,7 @@ abstract class LoopHandler { } /// Determine what kind of loop [node] represents. - /// - /// The result is one of the kinds defined in [HLoopBlockInformation]. - int loopKind(ir.TreeNode node); + LoopBlockInformationKind loopKind(ir.TreeNode node); /// Creates a [JumpHandler] for a statement. The node must be a jump /// target. If there are no breaks or continues targeting the statement, @@ -308,30 +306,33 @@ class KernelLoopHandler extends LoopHandler { builder.createJumpHandler(node, jumpTarget, isLoopJump: isLoopJump); @override - int loopKind(ir.TreeNode node) => node.accept(_KernelLoopTypeVisitor()); + LoopBlockInformationKind loopKind(ir.TreeNode node) => + node.accept(_KernelLoopTypeVisitor()); } -class _KernelLoopTypeVisitor extends ir.VisitorDefault - with ir.VisitorDefaultValueMixin { +class _KernelLoopTypeVisitor extends ir.VisitorDefault + with ir.VisitorDefaultValueMixin { @override - int get defaultValue => HLoopBlockInformation.NOT_A_LOOP; + LoopBlockInformationKind get defaultValue => + LoopBlockInformationKind.notALoop; @override - int visitWhileStatement(ir.WhileStatement node) => - HLoopBlockInformation.WHILE_LOOP; + LoopBlockInformationKind visitWhileStatement(ir.WhileStatement node) => + LoopBlockInformationKind.whileLoop; @override - int visitForStatement(ir.ForStatement node) => HLoopBlockInformation.FOR_LOOP; + LoopBlockInformationKind visitForStatement(ir.ForStatement node) => + LoopBlockInformationKind.forLoop; @override - int visitDoStatement(ir.DoStatement node) => - HLoopBlockInformation.DO_WHILE_LOOP; + LoopBlockInformationKind visitDoStatement(ir.DoStatement node) => + LoopBlockInformationKind.doWhileLoop; @override - int visitForInStatement(ir.ForInStatement node) => - HLoopBlockInformation.FOR_IN_LOOP; + LoopBlockInformationKind visitForInStatement(ir.ForInStatement node) => + LoopBlockInformationKind.forInLoop; @override - int visitSwitchStatement(ir.SwitchStatement node) => - HLoopBlockInformation.SWITCH_CONTINUE_LOOP; + LoopBlockInformationKind visitSwitchStatement(ir.SwitchStatement node) => + LoopBlockInformationKind.switchContinueLoop; } diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index b11c6a1579b..2e6d062a374 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -763,15 +763,18 @@ class HPhiList extends HInstructionList { HPhi? get lastPhi => last as HPhi?; } +enum _BasicBlockStatus { + new_, + open, + closed, +} + class HBasicBlock extends HInstructionList { // The [id] must be such that any successor's id is greater than // this [id]. The exception are back-edges. int id = -1; - static const int STATUS_NEW = 0; - static const int STATUS_OPEN = 1; - static const int STATUS_CLOSED = 2; - int status = STATUS_NEW; + _BasicBlockStatus _status = _BasicBlockStatus.new_; var phis = HPhiList(); @@ -793,9 +796,9 @@ class HBasicBlock extends HInstructionList { @override int get hashCode => id; - bool isNew() => status == STATUS_NEW; - bool isOpen() => status == STATUS_OPEN; - bool isClosed() => status == STATUS_CLOSED; + bool get isNew => _status == _BasicBlockStatus.new_; + bool get isOpen => _status == _BasicBlockStatus.open; + bool get isClosed => _status == _BasicBlockStatus.closed; bool isLoopHeader() { return loopInformation != null; @@ -814,14 +817,14 @@ class HBasicBlock extends HInstructionList { } void open() { - assert(isNew()); - status = STATUS_OPEN; + assert(isNew); + _status = _BasicBlockStatus.open; } void close(HControlFlow end) { - assert(isOpen()); + assert(isOpen); addAfter(last, end); - status = STATUS_CLOSED; + _status = _BasicBlockStatus.closed; } void addAtEntry(HInstruction instruction) { @@ -831,7 +834,7 @@ class HBasicBlock extends HInstructionList { } void addAtExit(HInstruction instruction) { - assert(isClosed()); + assert(isClosed); assert(last is HControlFlow); assert(instruction is! HPhi); internalAddBefore(last, instruction); @@ -841,7 +844,7 @@ class HBasicBlock extends HInstructionList { void moveAtExit(HInstruction instruction) { assert(instruction is! HPhi); assert(instruction.isInBasicBlock()); - assert(isClosed()); + assert(isClosed); assert(last is HControlFlow); internalAddBefore(last, instruction); instruction.block = this; @@ -871,7 +874,7 @@ class HBasicBlock extends HInstructionList { void addAfter(HInstruction? cursor, HInstruction instruction) { assert(cursor is! HPhi); assert(instruction is! HPhi); - assert(isOpen() || isClosed()); + assert(isOpen || isClosed); internalAddAfter(cursor, instruction); instruction.notifyAddedToBlock(this); } @@ -879,14 +882,14 @@ class HBasicBlock extends HInstructionList { void addBefore(HInstruction? cursor, HInstruction instruction) { assert(cursor is! HPhi); assert(instruction is! HPhi); - assert(isOpen() || isClosed()); + assert(isOpen || isClosed); internalAddBefore(cursor, instruction); instruction.notifyAddedToBlock(this); } @override void remove(HInstruction instruction) { - assert(isOpen() || isClosed()); + assert(isOpen || isClosed); assert(instruction is! HPhi); super.remove(instruction); assert(instruction.block == this); @@ -957,7 +960,7 @@ class HBasicBlock extends HInstructionList { } void addDominatedBlock(HBasicBlock block) { - assert(isClosed()); + assert(isClosed); assert(id >= 0 && block.id >= 0); assert(dominatedBlocks.indexOf(block) < 0); // Keep the list of dominated blocks sorted such that if there are two @@ -977,7 +980,7 @@ class HBasicBlock extends HInstructionList { } void removeDominatedBlock(HBasicBlock block) { - assert(isClosed()); + assert(isClosed); assert(id >= 0 && block.id >= 0); int index = dominatedBlocks.indexOf(block); assert(index >= 0); @@ -991,7 +994,7 @@ class HBasicBlock extends HInstructionList { } void assignCommonDominator(HBasicBlock predecessor) { - assert(isClosed()); + assert(isClosed); if (dominator == null) { // If this basic block doesn't have a dominator yet we use the // given predecessor as the dominator. @@ -1036,7 +1039,7 @@ class HBasicBlock extends HInstructionList { } bool isValid() { - assert(isClosed()); + assert(isClosed); HValidator validator = HValidator(); validator.visitBasicBlock(this); return validator.isValid; @@ -1051,6 +1054,62 @@ class HBasicBlock extends HInstructionList { toString() => 'HBasicBlock($id)'; } +enum _GvnType { + undefined, + boundsCheck, + interceptor, + add, + divide, + multiply, + subtract, + shiftLeft, + bitOr, + bitAnd, + bitXor, + negate, + bitNot, + not, + identity, + greater, + greaterEqual, + less, + lessEqual, + static, + staticStore, + fieldGet, + functionReference, + typeKnown, + invokeStatic, + index_, + invokeDynamic, + shiftRight, + truncatingDivide, + invokeExternal, + foreignCode, + remainder, + getLength, + abs, + boolConversion, + nullCheck, + primitiveCheck, + isTest, + isTestSimple, + asCheck, + asCheckSimple, + subtypeCheck, + loadType, + instanceEnvironment, + typeEval, + typeBind, + isLateSentinel, + stringConcat, + stringify, + lateReadCheck, + lateWriteOnceCheck, + lateInitializeOnceCheck, + charCodeAt, +} + abstract class HInstruction implements SpannableWithEntity { Entity? sourceElement; SourceInformation? sourceInformation; @@ -1071,69 +1130,6 @@ abstract class HInstruction implements SpannableWithEntity { SideEffects sideEffects = SideEffects.empty(); bool _useGvn = false; - // Type codes. - static const int UNDEFINED_TYPECODE = -1; - static const int TYPE_GUARD_TYPECODE = 1; - static const int BOUNDS_CHECK_TYPECODE = 2; - static const int INTEGER_CHECK_TYPECODE = 3; - static const int INTERCEPTOR_TYPECODE = 4; - static const int ADD_TYPECODE = 5; - static const int DIVIDE_TYPECODE = 6; - static const int MULTIPLY_TYPECODE = 7; - static const int SUBTRACT_TYPECODE = 8; - static const int SHIFT_LEFT_TYPECODE = 9; - static const int BIT_OR_TYPECODE = 10; - static const int BIT_AND_TYPECODE = 11; - static const int BIT_XOR_TYPECODE = 12; - static const int NEGATE_TYPECODE = 13; - static const int BIT_NOT_TYPECODE = 14; - static const int NOT_TYPECODE = 15; - static const int IDENTITY_TYPECODE = 16; - static const int GREATER_TYPECODE = 17; - static const int GREATER_EQUAL_TYPECODE = 18; - static const int LESS_TYPECODE = 19; - static const int LESS_EQUAL_TYPECODE = 20; - static const int STATIC_TYPECODE = 21; - static const int STATIC_STORE_TYPECODE = 22; - static const int FIELD_GET_TYPECODE = 23; - static const int FUNCTION_REFERENCE_TYPECODE = 24; - static const int TYPE_KNOWN_TYPECODE = 26; - static const int INVOKE_STATIC_TYPECODE = 27; - static const int INDEX_TYPECODE = 28; - static const int INVOKE_DYNAMIC_TYPECODE = 29; - static const int SHIFT_RIGHT_TYPECODE = 30; - - static const int TRUNCATING_DIVIDE_TYPECODE = 36; - - static const int INVOKE_EXTERNAL_TYPECODE = 41; - static const int FOREIGN_CODE_TYPECODE = 42; - static const int REMAINDER_TYPECODE = 43; - static const int GET_LENGTH_TYPECODE = 44; - static const int ABS_TYPECODE = 45; - static const int BOOL_CONVERSION_TYPECODE = 46; - static const int NULL_CHECK_TYPECODE = 47; - static const int PRIMITIVE_CHECK_TYPECODE = 48; - - static const int IS_TEST_TYPECODE = 49; - static const int IS_TEST_SIMPLE_TYPECODE = 50; - static const int AS_CHECK_TYPECODE = 51; - static const int AS_CHECK_SIMPLE_TYPECODE = 52; - static const int SUBTYPE_CHECK_TYPECODE = 53; - static const int LOAD_TYPE_TYPECODE = 54; - static const int INSTANCE_ENVIRONMENT_TYPECODE = 55; - static const int TYPE_EVAL_TYPECODE = 56; - static const int TYPE_BIND_TYPECODE = 57; - - static const int IS_LATE_SENTINEL_TYPECODE = 58; - static const int STRING_CONCAT_TYPECODE = 59; - static const int STRINGIFY_TYPECODE = 60; - - static const int LATE_READ_CHECK_TYPECODE = 61; - static const int LATE_WRITE_ONCE_CHECK_TYPECODE = 62; - static const int LATE_INITIALIZE_ONCE_CHECK_TYPECODE = 63; - - static const int CHAR_CODE_AT_TYPECODE = 64; - HInstruction(this.inputs, this.instructionType); HInstruction.noType(this.inputs); @@ -1268,7 +1264,7 @@ abstract class HInstruction implements SpannableWithEntity { assert(useGvn() && other.useGvn()); // Check that the type and the sideEffects match. bool hasSameType = typeEquals(other); - assert(hasSameType == (typeCode() == other.typeCode())); + assert(hasSameType == (_gvnType == other._gvnType)); if (!hasSameType) return false; if (sideEffects != other.sideEffects) return false; // Check that the inputs match. @@ -1285,7 +1281,7 @@ abstract class HInstruction implements SpannableWithEntity { } int gvnHashCode() { - int result = typeCode(); + int result = _gvnType.index; int length = inputs.length; for (int i = 0; i < length; i++) { result = (result * 19) + (inputs[i].nonCheck().id) + (result >> 7); @@ -1295,7 +1291,7 @@ abstract class HInstruction implements SpannableWithEntity { // These methods should be overwritten by instructions that // participate in global value numbering. - int typeCode() => HInstruction.UNDEFINED_TYPECODE; + _GvnType get _gvnType => _GvnType.undefined; bool typeEquals(covariant HInstruction other) => false; bool dataEquals(covariant HInstruction other) => false; @@ -1618,16 +1614,18 @@ abstract class HCheck extends HInstruction { HInstruction nonCheck() => checkedInput.nonCheck(); } -class HBoundsCheck extends HCheck { - static const int ALWAYS_FALSE = 0; - static const int FULL_CHECK = 1; - static const int ALWAYS_ABOVE_ZERO = 2; - static const int ALWAYS_BELOW_LENGTH = 3; - static const int ALWAYS_TRUE = 4; +enum StaticBoundsChecks { + alwaysFalse, + fullCheck, + alwaysAboveZero, + alwaysBelowLength, + alwaysTrue, +} +class HBoundsCheck extends HCheck { /// Details which tests have been done statically during compilation. /// Default is that all checks must be performed dynamically. - int staticChecks = FULL_CHECK; + StaticBoundsChecks staticChecks = StaticBoundsChecks.fullCheck; HBoundsCheck(HInstruction index, HInstruction length, HInstruction array, AbstractValue type) @@ -1645,7 +1643,7 @@ class HBoundsCheck extends HCheck { @override R accept(HVisitor visitor) => visitor.visitBoundsCheck(this); @override - int typeCode() => HInstruction.BOUNDS_CHECK_TYPECODE; + _GvnType get _gvnType => _GvnType.boundsCheck; @override bool typeEquals(other) => other is HBoundsCheck; @override @@ -1825,7 +1823,7 @@ abstract class HInvokeDynamic extends HInvoke implements InstructionContext { } @override - int typeCode() => HInstruction.INVOKE_DYNAMIC_TYPECODE; + _GvnType get _gvnType => _GvnType.invokeDynamic; @override bool typeEquals(other) => other is HInvokeDynamic; @@ -1990,7 +1988,7 @@ class HInvokeStatic extends HInvoke { R accept(HVisitor visitor) => visitor.visitInvokeStatic(this); @override - int typeCode() => HInstruction.INVOKE_STATIC_TYPECODE; + _GvnType get _gvnType => _GvnType.invokeStatic; @override String toString() => 'invoke static: $element'; @@ -2133,7 +2131,7 @@ class HFieldGet extends HFieldAccess { R accept(HVisitor visitor) => visitor.visitFieldGet(this); @override - int typeCode() => HInstruction.FIELD_GET_TYPECODE; + _GvnType get _gvnType => _GvnType.fieldGet; @override bool typeEquals(other) => other is HFieldGet; @override @@ -2184,7 +2182,7 @@ class HFunctionReference extends HInstruction { R accept(HVisitor visitor) => visitor.visitFunctionReference(this); @override - int typeCode() => HInstruction.FUNCTION_REFERENCE_TYPECODE; + _GvnType get _gvnType => _GvnType.functionReference; @override bool typeEquals(other) => other is HFunctionReference; @override @@ -2221,7 +2219,7 @@ class HGetLength extends HInstruction { R accept(HVisitor visitor) => visitor.visitGetLength(this); @override - int typeCode() => HInstruction.GET_LENGTH_TYPECODE; + _GvnType get _gvnType => _GvnType.getLength; @override bool typeEquals(other) => other is HGetLength; @override @@ -2230,15 +2228,18 @@ class HGetLength extends HInstruction { String toString() => "GetLength()"; } +enum ReadModifyWriteKind { + assign, + prefix, + postfix, +} + /// HReadModifyWrite is a late stage instruction for a field (property) update /// via an assignment operation or pre- or post-increment. class HReadModifyWrite extends HLateInstruction { - static const ASSIGN_OP = 0; - static const PRE_OP = 1; - static const POST_OP = 2; final FieldEntity element; final String jsOp; - final int opKind; + final ReadModifyWriteKind opKind; HReadModifyWrite._(this.element, this.jsOp, this.opKind, List inputs, AbstractValue type) @@ -2251,21 +2252,22 @@ class HReadModifyWrite extends HLateInstruction { HReadModifyWrite.assignOp(FieldEntity element, String jsOp, HInstruction receiver, HInstruction operand, AbstractValue type) - : this._(element, jsOp, ASSIGN_OP, [receiver, operand], type); + : this._(element, jsOp, ReadModifyWriteKind.assign, [receiver, operand], + type); HReadModifyWrite.preOp(FieldEntity element, String jsOp, HInstruction receiver, AbstractValue type) - : this._(element, jsOp, PRE_OP, [receiver], type); + : this._(element, jsOp, ReadModifyWriteKind.prefix, [receiver], type); HReadModifyWrite.postOp(FieldEntity element, String jsOp, HInstruction receiver, AbstractValue type) - : this._(element, jsOp, POST_OP, [receiver], type); + : this._(element, jsOp, ReadModifyWriteKind.postfix, [receiver], type); HInstruction get receiver => inputs[0]; - bool get isPreOp => opKind == PRE_OP; - bool get isPostOp => opKind == POST_OP; - bool get isAssignOp => opKind == ASSIGN_OP; + bool get isPreOp => opKind == ReadModifyWriteKind.prefix; + bool get isPostOp => opKind == ReadModifyWriteKind.postfix; + bool get isAssignOp => opKind == ReadModifyWriteKind.assign; @override bool canThrow(AbstractValueDomain domain) => @@ -2409,7 +2411,7 @@ class HInvokeExternal extends HInvoke { } @override - int typeCode() => HInstruction.INVOKE_EXTERNAL_TYPECODE; + _GvnType get _gvnType => _GvnType.invokeExternal; @override bool typeEquals(other) => other is HInvokeExternal; @override @@ -2504,7 +2506,7 @@ class HForeignCode extends HForeign { } @override - int typeCode() => HInstruction.FOREIGN_CODE_TYPECODE; + _GvnType get _gvnType => _GvnType.foreignCode; @override bool typeEquals(other) => other is HForeignCode; @override @@ -2547,7 +2549,7 @@ class HAdd extends HBinaryArithmetic { @override constant_system.BinaryOperation operation() => constant_system.add; @override - int typeCode() => HInstruction.ADD_TYPECODE; + _GvnType get _gvnType => _GvnType.add; @override bool typeEquals(other) => other is HAdd; @override @@ -2563,7 +2565,7 @@ class HDivide extends HBinaryArithmetic { @override constant_system.BinaryOperation operation() => constant_system.divide; @override - int typeCode() => HInstruction.DIVIDE_TYPECODE; + _GvnType get _gvnType => _GvnType.divide; @override bool typeEquals(other) => other is HDivide; @override @@ -2579,7 +2581,7 @@ class HMultiply extends HBinaryArithmetic { @override constant_system.BinaryOperation operation() => constant_system.multiply; @override - int typeCode() => HInstruction.MULTIPLY_TYPECODE; + _GvnType get _gvnType => _GvnType.multiply; @override bool typeEquals(other) => other is HMultiply; @override @@ -2595,7 +2597,7 @@ class HSubtract extends HBinaryArithmetic { @override constant_system.BinaryOperation operation() => constant_system.subtract; @override - int typeCode() => HInstruction.SUBTRACT_TYPECODE; + _GvnType get _gvnType => _GvnType.subtract; @override bool typeEquals(other) => other is HSubtract; @override @@ -2612,7 +2614,7 @@ class HTruncatingDivide extends HBinaryArithmetic { constant_system.BinaryOperation operation() => constant_system.truncatingDivide; @override - int typeCode() => HInstruction.TRUNCATING_DIVIDE_TYPECODE; + _GvnType get _gvnType => _GvnType.truncatingDivide; @override bool typeEquals(other) => other is HTruncatingDivide; @override @@ -2628,7 +2630,7 @@ class HRemainder extends HBinaryArithmetic { @override constant_system.BinaryOperation operation() => constant_system.remainder; @override - int typeCode() => HInstruction.REMAINDER_TYPECODE; + _GvnType get _gvnType => _GvnType.remainder; @override bool typeEquals(other) => other is HRemainder; @override @@ -2677,7 +2679,7 @@ class HShiftLeft extends HBinaryBitOp { @override constant_system.BinaryOperation operation() => constant_system.shiftLeft; @override - int typeCode() => HInstruction.SHIFT_LEFT_TYPECODE; + _GvnType get _gvnType => _GvnType.shiftLeft; @override bool typeEquals(other) => other is HShiftLeft; @override @@ -2693,7 +2695,7 @@ class HShiftRight extends HBinaryBitOp { @override constant_system.BinaryOperation operation() => constant_system.shiftRight; @override - int typeCode() => HInstruction.SHIFT_RIGHT_TYPECODE; + _GvnType get _gvnType => _GvnType.shiftRight; @override bool typeEquals(other) => other is HShiftRight; @override @@ -2709,7 +2711,7 @@ class HBitOr extends HBinaryBitOp { @override constant_system.BinaryOperation operation() => constant_system.bitOr; @override - int typeCode() => HInstruction.BIT_OR_TYPECODE; + _GvnType get _gvnType => _GvnType.bitOr; @override bool typeEquals(other) => other is HBitOr; @override @@ -2725,7 +2727,7 @@ class HBitAnd extends HBinaryBitOp { @override constant_system.BinaryOperation operation() => constant_system.bitAnd; @override - int typeCode() => HInstruction.BIT_AND_TYPECODE; + _GvnType get _gvnType => _GvnType.bitAnd; @override bool typeEquals(other) => other is HBitAnd; @override @@ -2741,7 +2743,7 @@ class HBitXor extends HBinaryBitOp { @override constant_system.BinaryOperation operation() => constant_system.bitXor; @override - int typeCode() => HInstruction.BIT_XOR_TYPECODE; + _GvnType get _gvnType => _GvnType.bitXor; @override bool typeEquals(other) => other is HBitXor; @override @@ -2768,7 +2770,7 @@ class HNegate extends HInvokeUnary { @override constant_system.UnaryOperation operation() => constant_system.negate; @override - int typeCode() => HInstruction.NEGATE_TYPECODE; + _GvnType get _gvnType => _GvnType.negate; @override bool typeEquals(other) => other is HNegate; @override @@ -2783,7 +2785,7 @@ class HAbs extends HInvokeUnary { @override constant_system.UnaryOperation operation() => constant_system.abs; @override - int typeCode() => HInstruction.ABS_TYPECODE; + _GvnType get _gvnType => _GvnType.abs; @override bool typeEquals(other) => other is HAbs; @override @@ -2804,7 +2806,7 @@ class HBitNot extends HInvokeUnary { @override constant_system.UnaryOperation operation() => constant_system.bitNot; @override - int typeCode() => HInstruction.BIT_NOT_TYPECODE; + _GvnType get _gvnType => _GvnType.bitNot; @override bool typeEquals(other) => other is HBitNot; @override @@ -2927,12 +2929,7 @@ class HIf extends HConditionalBranch { } class HLoopBranch extends HConditionalBranch { - static const int CONDITION_FIRST_LOOP = 0; - static const int DO_WHILE_LOOP = 1; - - final int kind; - HLoopBranch(HInstruction condition, [this.kind = CONDITION_FIRST_LOOP]) - : super([condition]); + HLoopBranch(HInstruction condition) : super([condition]); @override toString() => 'loop-branch'; @override @@ -2990,7 +2987,7 @@ class HNot extends HInstruction { @override R accept(HVisitor visitor) => visitor.visitNot(this); @override - int typeCode() => HInstruction.NOT_TYPECODE; + _GvnType get _gvnType => _GvnType.not; @override bool typeEquals(other) => other is HNot; @override @@ -3073,12 +3070,6 @@ class HThis extends HParameterValue { } class HPhi extends HInstruction { - static const IS_NOT_LOGICAL_OPERATOR = 0; - static const IS_AND = 1; - static const IS_OR = 2; - - int logicalOperatorType = IS_NOT_LOGICAL_OPERATOR; - HPhi? get previousPhi => previous as HPhi?; HPhi? get nextPhi => next as HPhi?; @@ -3127,7 +3118,7 @@ class HIdentity extends HRelational { @override constant_system.BinaryOperation operation() => constant_system.identity; @override - int typeCode() => HInstruction.IDENTITY_TYPECODE; + _GvnType get _gvnType => _GvnType.identity; @override bool typeEquals(other) => other is HIdentity; @override @@ -3143,7 +3134,7 @@ class HGreater extends HRelational { @override constant_system.BinaryOperation operation() => constant_system.greater; @override - int typeCode() => HInstruction.GREATER_TYPECODE; + _GvnType get _gvnType => _GvnType.greater; @override bool typeEquals(other) => other is HGreater; @override @@ -3159,7 +3150,7 @@ class HGreaterEqual extends HRelational { @override constant_system.BinaryOperation operation() => constant_system.greaterEqual; @override - int typeCode() => HInstruction.GREATER_EQUAL_TYPECODE; + _GvnType get _gvnType => _GvnType.greaterEqual; @override bool typeEquals(other) => other is HGreaterEqual; @override @@ -3175,7 +3166,7 @@ class HLess extends HRelational { @override constant_system.BinaryOperation operation() => constant_system.less; @override - int typeCode() => HInstruction.LESS_TYPECODE; + _GvnType get _gvnType => _GvnType.less; @override bool typeEquals(other) => other is HLess; @override @@ -3191,7 +3182,7 @@ class HLessEqual extends HRelational { @override constant_system.BinaryOperation operation() => constant_system.lessEqual; @override - int typeCode() => HInstruction.LESS_EQUAL_TYPECODE; + _GvnType get _gvnType => _GvnType.lessEqual; @override bool typeEquals(other) => other is HLessEqual; @override @@ -3292,7 +3283,7 @@ class HStatic extends HInstruction { @override int gvnHashCode() => super.gvnHashCode() ^ element.hashCode; @override - int typeCode() => HInstruction.STATIC_TYPECODE; + _GvnType get _gvnType => _GvnType.static; @override bool typeEquals(other) => other is HStatic; @override @@ -3340,7 +3331,7 @@ class HInterceptor extends HInstruction { bool isInterceptor(JClosedWorld closedWorld) => true; @override - int typeCode() => HInstruction.INTERCEPTOR_TYPECODE; + _GvnType get _gvnType => _GvnType.interceptor; @override bool typeEquals(other) => other is HInterceptor; @override @@ -3427,7 +3418,7 @@ class HStaticStore extends HInstruction { HInstruction get value => inputs.single; @override - int typeCode() => HInstruction.STATIC_STORE_TYPECODE; + _GvnType get _gvnType => _GvnType.staticStore; @override bool typeEquals(other) => other is HStaticStore; @override @@ -3482,7 +3473,7 @@ class HIndex extends HInstruction { receiver.isNull(domain).isPotentiallyTrue; @override - int typeCode() => HInstruction.INDEX_TYPECODE; + _GvnType get _gvnType => _GvnType.index_; @override bool typeEquals(HInstruction other) => other is HIndex; @override @@ -3548,7 +3539,7 @@ class HCharCodeAt extends HInstruction { receiver.isNull(domain).isPotentiallyTrue; @override - int typeCode() => HInstruction.CHAR_CODE_AT_TYPECODE; + _GvnType get _gvnType => _GvnType.charCodeAt; @override bool typeEquals(other) => other is HCharCodeAt; @override @@ -3573,6 +3564,11 @@ class HLateValue extends HLateInstruction { toString() => 'HLateValue($target)'; } +enum PrimitiveCheckKind { + argumentType, + receiverType, +} + /// Check for receiver or argument type when lowering operation to a primitive, /// e.g. lowering `+` to [HAdd]. /// @@ -3581,12 +3577,8 @@ class HLateValue extends HLateInstruction { /// that time, this check should be removed. If needed, the `!` check can be /// optimized to give the same signals to the JavaScript VM. class HPrimitiveCheck extends HCheck { - // Values for [kind]. - static const int ARGUMENT_TYPE_CHECK = 1; - static const int RECEIVER_TYPE_CHECK = 3; - final DartType typeExpression; - final int kind; + final PrimitiveCheckKind kind; // [receiverTypeCheckSelector] is the selector used for a receiver type check // on open-coded operators, e.g. the not-null check on `x` in `x + 1` would be @@ -3608,8 +3600,8 @@ class HPrimitiveCheck extends HCheck { this.sourceInformation = sourceInformation; } - bool get isArgumentTypeCheck => kind == ARGUMENT_TYPE_CHECK; - bool get isReceiverTypeCheck => kind == RECEIVER_TYPE_CHECK; + bool get isArgumentTypeCheck => kind == PrimitiveCheckKind.argumentType; + bool get isReceiverTypeCheck => kind == PrimitiveCheckKind.receiverType; @override R accept(HVisitor visitor) => visitor.visitPrimitiveCheck(this); @@ -3620,7 +3612,7 @@ class HPrimitiveCheck extends HCheck { bool isControlFlow() => true; @override - int typeCode() => HInstruction.PRIMITIVE_CHECK_TYPECODE; + _GvnType get _gvnType => _GvnType.primitiveCheck; @override bool typeEquals(HInstruction other) => other is HPrimitiveCheck; @override @@ -3669,7 +3661,7 @@ class HBoolConversion extends HCheck { R accept(HVisitor visitor) => visitor.visitBoolConversion(this); @override - int typeCode() => HInstruction.BOOL_CONVERSION_TYPECODE; + _GvnType get _gvnType => _GvnType.boolConversion; @override bool typeEquals(HInstruction other) => other is HBoolConversion; @override @@ -3714,7 +3706,7 @@ class HNullCheck extends HCheck { R accept(HVisitor visitor) => visitor.visitNullCheck(this); @override - int typeCode() => HInstruction.NULL_CHECK_TYPECODE; + _GvnType get _gvnType => _GvnType.nullCheck; @override bool typeEquals(HInstruction other) => other is HNullCheck; @override @@ -3770,7 +3762,7 @@ class HLateReadCheck extends HLateCheck { R accept(HVisitor visitor) => visitor.visitLateReadCheck(this); @override - int typeCode() => HInstruction.LATE_READ_CHECK_TYPECODE; + _GvnType get _gvnType => _GvnType.lateReadCheck; @override bool typeEquals(HInstruction other) => other is HLateReadCheck; @@ -3805,7 +3797,7 @@ class HLateWriteOnceCheck extends HLateCheck { R accept(HVisitor visitor) => visitor.visitLateWriteOnceCheck(this); @override - int typeCode() => HInstruction.LATE_WRITE_ONCE_CHECK_TYPECODE; + _GvnType get _gvnType => _GvnType.lateWriteOnceCheck; @override bool typeEquals(HInstruction other) => other is HLateWriteOnceCheck; @@ -3841,7 +3833,7 @@ class HLateInitializeOnceCheck extends HLateCheck { visitor.visitLateInitializeOnceCheck(this); @override - int typeCode() => HInstruction.LATE_INITIALIZE_ONCE_CHECK_TYPECODE; + _GvnType get _gvnType => _GvnType.lateInitializeOnceCheck; @override bool typeEquals(HInstruction other) => other is HLateInitializeOnceCheck; @@ -3892,7 +3884,7 @@ class HTypeKnown extends HCheck { HInstruction? get witness => inputs.length == 2 ? inputs[1] : null; @override - int typeCode() => HInstruction.TYPE_KNOWN_TYPECODE; + _GvnType get _gvnType => _GvnType.typeKnown; @override bool typeEquals(HInstruction other) => other is HTypeKnown; @override @@ -3943,7 +3935,7 @@ class HStringConcat extends HInstruction { toString() => "string concat"; @override - int typeCode() => HInstruction.STRING_CONCAT_TYPECODE; + _GvnType get _gvnType => _GvnType.stringConcat; @override bool typeEquals(HInstruction other) => other is HStringConcat; @override @@ -3976,7 +3968,7 @@ class HStringify extends HInstruction { toString() => "stringify"; @override - int typeCode() => HInstruction.STRINGIFY_TYPECODE; + _GvnType get _gvnType => _GvnType.stringify; @override bool typeEquals(HInstruction other) => other is HStringify; @override @@ -4146,15 +4138,17 @@ class HLabeledBlockInformation implements HStatementInformation { visitor.visitLabeledBlockInfo(this); } -class HLoopBlockInformation implements HStatementInformation { - static const int WHILE_LOOP = 0; - static const int FOR_LOOP = 1; - static const int DO_WHILE_LOOP = 2; - static const int FOR_IN_LOOP = 3; - static const int SWITCH_CONTINUE_LOOP = 4; - static const int NOT_A_LOOP = -1; +enum LoopBlockInformationKind { + notALoop, + whileLoop, + forLoop, + doWhileLoop, + forInLoop, + switchContinueLoop, +} - final int kind; +class HLoopBlockInformation implements HStatementInformation { + final LoopBlockInformationKind kind; final HExpressionInformation? initializer; final HExpressionInformation? condition; final HStatementInformation? body; @@ -4165,27 +4159,31 @@ class HLoopBlockInformation implements HStatementInformation { HLoopBlockInformation(this.kind, this.initializer, this.condition, this.body, this.updates, this.target, this.labels, this.sourceInformation) { - assert((kind == DO_WHILE_LOOP ? body!.start : condition!.start) + assert((kind == LoopBlockInformationKind.doWhileLoop + ? body!.start + : condition!.start) .isLoopHeader()); } @override HBasicBlock get start { if (initializer != null) return initializer!.start; - if (kind == DO_WHILE_LOOP) { + if (kind == LoopBlockInformationKind.doWhileLoop) { return body!.start; } return condition!.start; } HBasicBlock get loopHeader { - return kind == DO_WHILE_LOOP ? body!.start : condition!.start; + return kind == LoopBlockInformationKind.doWhileLoop + ? body!.start + : condition!.start; } @override HBasicBlock get end { if (updates != null) return updates!.end; - if (kind == DO_WHILE_LOOP && condition != null) { + if (kind == LoopBlockInformationKind.doWhileLoop && condition != null) { return condition!.end; } return body!.end; @@ -4285,7 +4283,7 @@ class HIsTest extends HInstruction { R accept(HVisitor visitor) => visitor.visitIsTest(this); @override - int typeCode() => HInstruction.IS_TEST_TYPECODE; + _GvnType get _gvnType => _GvnType.isTest; @override bool typeEquals(HInstruction other) => other is HIsTest; @@ -4321,7 +4319,7 @@ class HIsTestSimple extends HInstruction { R accept(HVisitor visitor) => visitor.visitIsTestSimple(this); @override - int typeCode() => HInstruction.IS_TEST_SIMPLE_TYPECODE; + _GvnType get _gvnType => _GvnType.isTestSimple; @override bool typeEquals(HInstruction other) => other is HIsTestSimple; @@ -4476,7 +4474,7 @@ class HAsCheck extends HCheck { R accept(HVisitor visitor) => visitor.visitAsCheck(this); @override - int typeCode() => HInstruction.AS_CHECK_TYPECODE; + _GvnType get _gvnType => _GvnType.asCheck; @override bool typeEquals(HInstruction other) => other is HAsCheck; @@ -4526,7 +4524,7 @@ class HAsCheckSimple extends HCheck { .isDefinitelyTrue; @override - int typeCode() => HInstruction.AS_CHECK_SIMPLE_TYPECODE; + _GvnType get _gvnType => _GvnType.asCheckSimple; @override bool typeEquals(HInstruction other) => other is HAsCheckSimple; @@ -4557,7 +4555,7 @@ class HSubtypeCheck extends HCheck { R accept(HVisitor visitor) => visitor.visitSubtypeCheck(this); @override - int typeCode() => HInstruction.SUBTYPE_CHECK_TYPECODE; + _GvnType get _gvnType => _GvnType.subtypeCheck; @override bool typeEquals(HInstruction other) => other is HSubtypeCheck; @@ -4591,7 +4589,7 @@ class HLoadType extends HRtiInstruction { R accept(HVisitor visitor) => visitor.visitLoadType(this); @override - int typeCode() => HInstruction.LOAD_TYPE_TYPECODE; + _GvnType get _gvnType => _GvnType.loadType; @override bool typeEquals(HInstruction other) => other is HLoadType; @@ -4622,7 +4620,7 @@ class HInstanceEnvironment extends HRtiInstruction { R accept(HVisitor visitor) => visitor.visitInstanceEnvironment(this); @override - int typeCode() => HInstruction.INSTANCE_ENVIRONMENT_TYPECODE; + _GvnType get _gvnType => _GvnType.instanceEnvironment; @override bool typeEquals(HInstruction other) => other is HInstanceEnvironment; @@ -4649,7 +4647,7 @@ class HTypeEval extends HRtiInstruction { R accept(HVisitor visitor) => visitor.visitTypeEval(this); @override - int typeCode() => HInstruction.TYPE_EVAL_TYPECODE; + _GvnType get _gvnType => _GvnType.typeEval; @override bool typeEquals(HInstruction other) => other is HTypeEval; @@ -4676,7 +4674,7 @@ class HTypeBind extends HRtiInstruction { R accept(HVisitor visitor) => visitor.visitTypeBind(this); @override - int typeCode() => HInstruction.TYPE_BIND_TYPECODE; + _GvnType get _gvnType => _GvnType.typeBind; @override bool typeEquals(HInstruction other) => other is HTypeBind; @@ -4698,7 +4696,7 @@ class HIsLateSentinel extends HInstruction { R accept(HVisitor visitor) => visitor.visitIsLateSentinel(this); @override - int typeCode() => HInstruction.IS_LATE_SENTINEL_TYPECODE; + _GvnType get _gvnType => _GvnType.isLateSentinel; @override bool typeEquals(HInstruction other) => other is HIsLateSentinel; diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart index 8be10dcb3eb..4bc59266aa5 100644 --- a/pkg/compiler/lib/src/ssa/optimize.dart +++ b/pkg/compiler/lib/src/ssa/optimize.dart @@ -1143,7 +1143,7 @@ class SsaInstructionSimplifier extends HBaseVisitor assert(index.constant is! IntConstantValue); if (!constant_system.isInt(index.constant)) { // -0.0 is a double but will pass the runtime integer check. - node.staticChecks = HBoundsCheck.ALWAYS_FALSE; + node.staticChecks = StaticBoundsChecks.alwaysFalse; } } return node; diff --git a/pkg/compiler/lib/src/ssa/tracer.dart b/pkg/compiler/lib/src/ssa/tracer.dart index 88aa3a0bd4a..d68e6d8f748 100644 --- a/pkg/compiler/lib/src/ssa/tracer.dart +++ b/pkg/compiler/lib/src/ssa/tracer.dart @@ -312,13 +312,14 @@ class HInstructionStringifier implements HVisitor { String? fieldName = node.element.name; String receiverId = temporaryId(node.receiver); String op = node.jsOp; - if (node.isAssignOp) { - String valueId = temporaryId(node.value); - return 'ReadModifyWrite: $receiverId.$fieldName $op= $valueId'; - } else if (node.isPreOp) { - return 'ReadModifyWrite: $op$receiverId.$fieldName'; - } else { - return 'ReadModifyWrite: $receiverId.$fieldName$op'; + switch (node.opKind) { + case ReadModifyWriteKind.assign: + String valueId = temporaryId(node.value); + return 'ReadModifyWrite: $receiverId.$fieldName $op= $valueId'; + case ReadModifyWriteKind.prefix: + return 'ReadModifyWrite: $op$receiverId.$fieldName'; + case ReadModifyWriteKind.postfix: + return 'ReadModifyWrite: $receiverId.$fieldName$op'; } } @@ -654,11 +655,10 @@ class HInstructionStringifier implements HVisitor { return "PrimitiveCheck: $kind $checkedInput to ${node.instructionType}"; } - String _primitiveCheckKind(HPrimitiveCheck node) { - if (node.isReceiverTypeCheck) return 'RECEIVER'; - if (node.isArgumentTypeCheck) return 'ARGUMENT'; - return '?'; - } + String _primitiveCheckKind(HPrimitiveCheck node) => switch (node.kind) { + PrimitiveCheckKind.receiverType => 'RECEIVER', + PrimitiveCheckKind.argumentType => 'ARGUMENT', + }; @override String visitBoolConversion(HBoolConversion node) { diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart index 1e3c447318c..11569df45c3 100644 --- a/pkg/compiler/lib/src/ssa/types_propagation.dart +++ b/pkg/compiler/lib/src/ssa/types_propagation.dart @@ -259,12 +259,9 @@ class SsaTypePropagator extends HBaseVisitor } void convertInput(HInvokeDynamic instruction, HInstruction input, - AbstractValue type, int kind, DartType typeExpression) { - assert(kind == HPrimitiveCheck.RECEIVER_TYPE_CHECK || - kind == HPrimitiveCheck.ARGUMENT_TYPE_CHECK); - Selector? selector = (kind == HPrimitiveCheck.RECEIVER_TYPE_CHECK) - ? instruction.selector - : null; + AbstractValue type, PrimitiveCheckKind kind, DartType typeExpression) { + Selector? selector = + (kind == PrimitiveCheckKind.receiverType) ? instruction.selector : null; HPrimitiveCheck converted = HPrimitiveCheck( typeExpression, kind, type, input, instruction.sourceInformation, receiverTypeCheckSelector: selector); @@ -300,7 +297,7 @@ class SsaTypePropagator extends HBaseVisitor instruction, receiver, abstractValueDomain.excludeNull(receiver.instructionType), - HPrimitiveCheck.RECEIVER_TYPE_CHECK, + PrimitiveCheckKind.receiverType, commonElements.numType); return true; } else if (instruction.element == null) { @@ -326,7 +323,7 @@ class SsaTypePropagator extends HBaseVisitor if (!isCheckEnoughForNsmOrAe(receiver, type)) return false; instruction.element = target; convertInput(instruction, receiver, type, - HPrimitiveCheck.RECEIVER_TYPE_CHECK, typeExpression); + PrimitiveCheckKind.receiverType, typeExpression); return true; } } @@ -354,8 +351,8 @@ class SsaTypePropagator extends HBaseVisitor // variant and will do the check in their method anyway. We // still add a check because it allows to GVN these operations, // but we should find a better way. - convertInput(instruction, right, type, - HPrimitiveCheck.ARGUMENT_TYPE_CHECK, commonElements.numType); + convertInput(instruction, right, type, PrimitiveCheckKind.argumentType, + commonElements.numType); return true; } return false; diff --git a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart index 635fe644639..82c93c151ae 100644 --- a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart +++ b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart @@ -903,14 +903,14 @@ class SsaValueRangeAnalyzer extends HBaseVisitor checkBlock.rewrite(check, check.index); checkBlock.remove(check); } else if (indexRange.isNegative || lengthRange < indexRange) { - check.staticChecks = HBoundsCheck.ALWAYS_FALSE; + check.staticChecks = StaticBoundsChecks.alwaysFalse; // The check is always false, and whatever instruction it // dominates is dead code. return indexRange; } else if (indexRange.isPositive) { - check.staticChecks = HBoundsCheck.ALWAYS_ABOVE_ZERO; + check.staticChecks = StaticBoundsChecks.alwaysAboveZero; } else if (belowLength) { - check.staticChecks = HBoundsCheck.ALWAYS_BELOW_LENGTH; + check.staticChecks = StaticBoundsChecks.alwaysBelowLength; } if (indexRange.isPositive) { diff --git a/pkg/compiler/lib/src/universe/selector.dart b/pkg/compiler/lib/src/universe/selector.dart index c8af97bdcdd..bf38b34086a 100644 --- a/pkg/compiler/lib/src/universe/selector.dart +++ b/pkg/compiler/lib/src/universe/selector.dart @@ -10,7 +10,7 @@ import '../elements/entities.dart'; import '../elements/entity_utils.dart' as utils; import '../elements/names.dart'; import '../elements/operators.dart'; -import '../kernel/invocation_mirror_constants.dart'; +import '../kernel/invocation_mirror.dart'; import '../serialization/serialization.dart'; import '../util/util.dart' show Hashing; import 'call_structure.dart' show CallStructure; @@ -209,12 +209,12 @@ class Selector { /// The member name for invocation mirrors created from this selector. String get invocationMirrorMemberName => isSetter ? '$name=' : name; - int get invocationMirrorKind { - int kind = invocationMirrorMethodKind; + InvocationMirrorKind get invocationMirrorKind { + var kind = InvocationMirrorKind.method; if (isGetter) { - kind = invocationMirrorGetterKind; + kind = InvocationMirrorKind.getter; } else if (isSetter) { - kind = invocationMirrorSetterKind; + kind = InvocationMirrorKind.setter; } return kind; } diff --git a/pkg/compiler/test/codegen/value_range_test.dart b/pkg/compiler/test/codegen/value_range_test.dart index 53d5183fa72..d91ce71a82f 100644 --- a/pkg/compiler/test/codegen/value_range_test.dart +++ b/pkg/compiler/test/codegen/value_range_test.dart @@ -7,16 +7,19 @@ import 'package:async_helper/async_helper.dart'; import 'package:expect/expect.dart'; import '../helpers/compiler_helper.dart'; -const int REMOVED = 0; -const int ABOVE_ZERO = 1; -const int BELOW_LENGTH = 2; -const int KEPT = 3; -const int ONE_CHECK = 4; -const int ONE_ZERO_CHECK = 5; -const int BELOW_ZERO_CHECK = 6; +enum _Result { + removed, + aboveZero, + belowLength, + kept, + oneCheck, + oneZeroCheck, + belowZeroCheck, +} -final List TESTS = [ - """ +final List<(String, _Result)> tests = [ + ( + """ @pragma('dart2js:assumeDynamic') test(check) { check as bool; @@ -28,8 +31,10 @@ test(check) { return sum; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -41,8 +46,10 @@ test(value) { return sum; } """, - ABOVE_ZERO, - """ + _Result.aboveZero + ), + ( + """ @pragma('dart2js:assumeDynamic') test(check) { check as bool; @@ -56,36 +63,46 @@ test(check) { return sum; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ test() { var a = []; return a[0]; } """, - KEPT, - """ + _Result.kept + ), + ( + """ test() { var a = []; return a.removeLast(); } """, - KEPT, - """ + _Result.kept + ), + ( + """ test() { var a = List.filled(4, null); return a[0]; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ test() { var a = List.filled(4, null); return a.removeLast(); } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -93,8 +110,10 @@ test(value) { return a[value]; } """, - KEPT, - """ + _Result.kept + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -102,8 +121,10 @@ test(value) { return a[1023 & value]; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -111,15 +132,19 @@ test(value) { return a[1024 & value]; } """, - ABOVE_ZERO, - """ + _Result.aboveZero + ), + ( + """ test() { var a = []; return a[1]; } """, - ABOVE_ZERO, - """ + _Result.aboveZero + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value, call) { value as int; @@ -128,8 +153,10 @@ test(value, call) { return a[value] + call() + a[value]; } """, - ONE_ZERO_CHECK, - """ + _Result.oneZeroCheck + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as bool; @@ -137,8 +164,10 @@ test(value) { return a[1] + a[0]; } """, - ONE_CHECK, - """ + _Result.oneCheck + ), + ( + """ @pragma('dart2js:assumeDynamic') test(n) { n as int; @@ -150,8 +179,10 @@ test(n) { return sum; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(n) { n as int; @@ -163,8 +194,10 @@ test(n) { return sum; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(dynamic value) { value = value is int ? value as int : 42; @@ -176,8 +209,10 @@ test(dynamic value) { return a[value]; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value = value is int ? value as int : 42; @@ -191,8 +226,10 @@ test(value) { } } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value = value is int ? value as int : 42; @@ -204,8 +241,10 @@ test(value) { return a[value]; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -218,8 +257,10 @@ test(value) { return sum; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -232,8 +273,10 @@ test(value) { return sum; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -246,8 +289,10 @@ test(value) { return sum; } """, - REMOVED, - """ + _Result.removed + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -260,8 +305,10 @@ test(value) { return sum; } """, - BELOW_ZERO_CHECK, - """ + _Result.belowZeroCheck + ), + ( + """ @pragma('dart2js:assumeDynamic') test(value) { value as int; @@ -273,45 +320,46 @@ test(value) { return sum; } """, - BELOW_ZERO_CHECK, + _Result.belowZeroCheck + ), ]; -Future expect(String code, int kind) { +Future expect(String code, _Result kind) { return compile(code, entry: 'test', disableTypeInference: false, check: (String generated) { switch (kind) { - case REMOVED: + case _Result.removed: Expect.isFalse(generated.contains('ioore')); break; - case ABOVE_ZERO: + case _Result.aboveZero: Expect.isFalse(generated.contains('< 0') || generated.contains('>= 0')); Expect.isTrue(generated.contains('ioore')); break; - case BELOW_ZERO_CHECK: + case _Result.belowZeroCheck: // May generate `!(ix < 0)` or `ix >= 0` depending if `ix` can be NaN Expect.isTrue(generated.contains('< 0') || generated.contains('>= 0')); Expect.isFalse(generated.contains('||') || generated.contains('&&')); Expect.isTrue(generated.contains('ioore')); break; - case BELOW_LENGTH: + case _Result.belowLength: Expect.isFalse(generated.contains('||') || generated.contains('&&')); Expect.isTrue(generated.contains('ioore')); break; - case KEPT: + case _Result.kept: Expect.isTrue(generated.contains('ioore')); break; - case ONE_CHECK: + case _Result.oneCheck: RegExp regexp = RegExp('ioore'); Iterator matches = regexp.allMatches(generated).iterator; checkNumberOfMatches(matches, 1); break; - case ONE_ZERO_CHECK: + case _Result.oneZeroCheck: RegExp regexp = RegExp('< 0|>>> 0 !=='); Iterator matches = regexp.allMatches(generated).iterator; checkNumberOfMatches(matches, 1); @@ -321,8 +369,8 @@ Future expect(String code, int kind) { } runTests() async { - for (int i = 0; i < TESTS.length; i += 2) { - await expect(TESTS[i], TESTS[i + 1]); + for (final (input, expected) in tests) { + await expect(input, expected); } } diff --git a/pkg/compiler/test/dump_info/data_new/mixin_with_tearoff_test.dart b/pkg/compiler/test/dump_info/data_new/mixin_with_tearoff_test.dart index d1326a6e444..44b0e745dba 100644 --- a/pkg/compiler/test/dump_info/data_new/mixin_with_tearoff_test.dart +++ b/pkg/compiler/test/dump_info/data_new/mixin_with_tearoff_test.dart @@ -49,20 +49,20 @@ "code": "B.JavaScriptObject_methods = J.JavaScriptObject.prototype;\n" }, { - "id": "constant/B.Type_Object_xQ6 = A.typeLiteral(\"Object\");\n", + "id": "constant/B.Type_Object_QJv = A.typeLiteral(\"Object\");\n", "kind": "constant", "name": "", "size": 45, "outputUnit": "outputUnit/main", - "code": "B.Type_Object_xQ6 = A.typeLiteral(\"Object\");\n" + "code": "B.Type_Object_QJv = A.typeLiteral(\"Object\");\n" }, { - "id": "constant/B.Type_dynamic_0Rz = A.typeLiteral(\"@\");\n", + "id": "constant/B.Type_dynamic_PLF = A.typeLiteral(\"@\");\n", "kind": "constant", "name": "", "size": 41, "outputUnit": "outputUnit/main", - "code": "B.Type_dynamic_0Rz = A.typeLiteral(\"@\");\n" + "code": "B.Type_dynamic_PLF = A.typeLiteral(\"@\");\n" }], deferredFiles=[{}], dependencies=[{}], diff --git a/pkg/compiler/test/js/js_parser_test.dart b/pkg/compiler/test/js/js_parser_test.dart index 40639cc1022..4b5a052f4f8 100644 --- a/pkg/compiler/test/js/js_parser_test.dart +++ b/pkg/compiler/test/js/js_parser_test.dart @@ -99,7 +99,7 @@ void main() { testExpression('var false = 42'); testExpression('var new = 42'); // Bad keyword. - testError('var typeof = 42', "Expected ALPHA"); + testError('var typeof = 42', "Expected _Category.alpha"); // Malformed decimal/hex. testError('var x = 1.1.1', "Unparseable number"); testError('var x = 0xabcdefga', "Unparseable number"); diff --git a/pkg/js_ast/lib/src/builder.dart b/pkg/js_ast/lib/src/builder.dart index db39a156689..61e55d27320 100644 --- a/pkg/js_ast/lib/src/builder.dart +++ b/pkg/js_ast/lib/src/builder.dart @@ -383,6 +383,52 @@ class MiniJsParserError { } } +enum _Category { + none, + alpha, + numeric, + string, + symbol, + assignment, + dot, + lparen, + rparen, + lbrace, + rbrace, + lsquare, + rsquare, + comma, + query, + colon, + semicolon, + arrow, + hash, + whitespace, + other, + ; + + static const _asciiTable = <_Category>[ + other, other, other, other, other, other, other, other, // 0-7 + other, whitespace, whitespace, other, other, whitespace, // 8-13 + other, other, other, other, other, other, other, other, // 14-21 + other, other, other, other, other, other, other, other, // 22-29 + other, other, whitespace, // 30-32 + symbol, other, hash, alpha, symbol, symbol, other, // !"#$%&` + lparen, rparen, symbol, symbol, comma, symbol, dot, symbol, // ()*+,-./ + numeric, numeric, numeric, numeric, numeric, // 01234 + numeric, numeric, numeric, numeric, numeric, // 56789 + colon, semicolon, symbol, symbol, symbol, query, other, // :;<=>?@ + alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, // ABCDEFGH + alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, // IJKLMNOP + alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, // QRSTUVWX + alpha, alpha, lsquare, other, rsquare, symbol, alpha, other, // YZ[\]^_' + alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, // abcdefgh + alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, // ijklmnop + alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, // qrstuvwx + alpha, alpha, lbrace, symbol, rbrace, symbol, // yx{|}~ + ]; +} + /// Mini JavaScript parser for tiny snippets of code that we want to make into /// AST nodes. Handles: /// * identifiers. @@ -411,7 +457,7 @@ class MiniJsParser { getToken(); } - int lastCategory = NONE; + _Category _lastCategory = _Category.none; String lastToken = ''; int lastPosition = 0; int position = 0; @@ -424,99 +470,9 @@ class MiniJsParser { bool get hasPositionalHoles => interpolatedValues.isNotEmpty && interpolatedValues.first.isPositional; - static const NONE = -1; - static const ALPHA = 0; - static const NUMERIC = 1; - static const STRING = 2; - static const SYMBOL = 3; - static const ASSIGNMENT = 4; - static const DOT = 5; - static const LPAREN = 6; - static const RPAREN = 7; - static const LBRACE = 8; - static const RBRACE = 9; - static const LSQUARE = 10; - static const RSQUARE = 11; - static const COMMA = 12; - static const QUERY = 13; - static const COLON = 14; - static const SEMICOLON = 15; - static const ARROW = 16; - static const HASH = 17; - static const WHITESPACE = 18; - static const OTHER = 19; - // Make sure that ]] is two symbols. - bool singleCharCategory(int category) => category >= DOT; - - static String categoryToString(int cat) { - switch (cat) { - case NONE: - return 'NONE'; - case ALPHA: - return 'ALPHA'; - case NUMERIC: - return 'NUMERIC'; - case SYMBOL: - return 'SYMBOL'; - case ASSIGNMENT: - return 'ASSIGNMENT'; - case DOT: - return 'DOT'; - case LPAREN: - return 'LPAREN'; - case RPAREN: - return 'RPAREN'; - case LBRACE: - return 'LBRACE'; - case RBRACE: - return 'RBRACE'; - case LSQUARE: - return 'LSQUARE'; - case RSQUARE: - return 'RSQUARE'; - case STRING: - return 'STRING'; - case COMMA: - return 'COMMA'; - case QUERY: - return 'QUERY'; - case COLON: - return 'COLON'; - case SEMICOLON: - return 'SEMICOLON'; - case ARROW: - return 'ARROW'; - case HASH: - return 'HASH'; - case WHITESPACE: - return 'WHITESPACE'; - case OTHER: - return 'OTHER'; - } - return 'Unknown: $cat'; - } - - static const CATEGORIES = [ - OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 0-7 - OTHER, WHITESPACE, WHITESPACE, OTHER, OTHER, WHITESPACE, // 8-13 - OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 14-21 - OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, OTHER, // 22-29 - OTHER, OTHER, WHITESPACE, // 30-32 - SYMBOL, OTHER, HASH, ALPHA, SYMBOL, SYMBOL, OTHER, // !"#$%&´ - LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL, // ()*+,-./ - NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 01234 - NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC, // 56789 - COLON, SEMICOLON, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER, // :;<=>?@ - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ABCDEFGH - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // IJKLMNOP - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // QRSTUVWX - ALPHA, ALPHA, LSQUARE, OTHER, RSQUARE, SYMBOL, ALPHA, OTHER, // YZ[\]^_' - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // abcdefgh - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // ijklmnop - ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, // qrstuvwx - ALPHA, ALPHA, LBRACE, SYMBOL, RBRACE, SYMBOL - ]; // yz{|}~ + bool _singleCharCategory(_Category category) => + category.index >= _Category.dot.index; // This must be a >= the highest precedence number handled by parseBinary. static var HIGHEST_PARSE_BINARY_PRECEDENCE = 16; @@ -584,9 +540,9 @@ class MiniJsParser { 'await' }; - static int category(int code) { - if (code >= CATEGORIES.length) return OTHER; - return CATEGORIES[code]; + static _Category _category(int code) { + if (code >= _Category._asciiTable.length) return _Category.other; + return _Category._asciiTable[code]; } String getRegExp(int startPosition) { @@ -605,7 +561,7 @@ class MiniJsParser { escaped == char_codes.$X || escaped == char_codes.$u || escaped == char_codes.$U || - category(escaped) == NUMERIC) { + _category(escaped) == _Category.numeric) { error('Numeric and hex escapes are not supported in RegExp literals'); } } @@ -673,13 +629,13 @@ class MiniJsParser { continue; } } - if (category(code) != WHITESPACE) break; + if (_category(code) != _Category.whitespace) break; if (code == char_codes.$LF) skippedNewline = true; ++position; } if (position == src.length) { - lastCategory = NONE; + _lastCategory = _Category.none; lastToken = ''; lastPosition = position; return; @@ -688,24 +644,24 @@ class MiniJsParser { lastPosition = position; if (code == char_codes.$SQ || code == char_codes.$DQ) { // String literal. - lastCategory = STRING; + _lastCategory = _Category.string; lastToken = getString(position, code); } else if (code == char_codes.$0 && position + 2 < src.length && src.codeUnitAt(position + 1) == char_codes.$x) { // Hex literal. for (position += 2; position < src.length; position++) { - int cat = category(src.codeUnitAt(position)); - if (cat != NUMERIC && cat != ALPHA) break; + final cat = _category(src.codeUnitAt(position)); + if (cat != _Category.numeric && cat != _Category.alpha) break; } - lastCategory = NUMERIC; + _lastCategory = _Category.numeric; lastToken = src.substring(lastPosition, position); if (int.tryParse(lastToken) == null) { error('Unparseable number'); } } else if (code == char_codes.$SLASH) { // Tokens that start with / are special due to regexp literals. - lastCategory = SYMBOL; + _lastCategory = _Category.symbol; position++; if (position < src.length && src.codeUnitAt(position) == char_codes.$EQ) { position++; @@ -713,8 +669,8 @@ class MiniJsParser { lastToken = src.substring(lastPosition, position); } else { // All other tokens handled here. - int cat = category(src.codeUnitAt(position)); - int newCat; + final cat = _category(src.codeUnitAt(position)); + _Category newCat; do { position++; if (position == src.length) break; @@ -725,44 +681,46 @@ class MiniJsParser { newCat = (code == char_codes.$BANG || code == char_codes.$SLASH || code == char_codes.$TILDE) - ? NONE - : category(code); - } while (!singleCharCategory(cat) && + ? _Category.none + : _category(code); + } while (!_singleCharCategory(cat) && (cat == newCat || - (cat == ALPHA && newCat == NUMERIC) || // eg. level42. - (cat == NUMERIC && newCat == DOT))); // eg. 3.1415 - lastCategory = cat; + (cat == _Category.alpha && + newCat == _Category.numeric) || // eg. level42. + (cat == _Category.numeric && + newCat == _Category.dot))); // eg. 3.1415 + _lastCategory = cat; lastToken = src.substring(lastPosition, position); - if (cat == NUMERIC) { + if (cat == _Category.numeric) { if (double.tryParse(lastToken) == null) { error('Unparseable number'); } - } else if (cat == SYMBOL) { + } else if (cat == _Category.symbol) { if (lastToken == '=>') { - lastCategory = ARROW; + _lastCategory = _Category.arrow; } else { int? binaryPrecedence = BINARY_PRECEDENCE[lastToken]; if (binaryPrecedence == null && !UNARY_OPERATORS.contains(lastToken)) { error('Unknown operator'); } - if (isAssignment(lastToken)) lastCategory = ASSIGNMENT; + if (isAssignment(lastToken)) _lastCategory = _Category.assignment; } - } else if (cat == ALPHA) { + } else if (cat == _Category.alpha) { if (OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(lastToken)) { - lastCategory = SYMBOL; + _lastCategory = _Category.symbol; } } } } - void expectCategory(int cat) { - if (cat != lastCategory) error('Expected ${categoryToString(cat)}'); + void _expectCategory(_Category cat) { + if (cat != _lastCategory) error('Expected $cat'); getToken(); } - bool acceptCategory(int cat) { - if (cat == lastCategory) { + bool _acceptCategory(_Category cat) { + if (cat == _lastCategory) { getToken(); return true; } @@ -777,12 +735,12 @@ class MiniJsParser { bool acceptSemicolon() { // Accept semicolon or automatically inserted semicolon before close brace. // Miniparser forbids other kinds of semicolon insertion. - if (RBRACE == lastCategory) return true; - if (NONE == lastCategory) return true; // end of input + if (_Category.rbrace == _lastCategory) return true; + if (_Category.none == _lastCategory) return true; // end of input if (skippedNewline) { error('No automatic semicolon insertion at preceding newline'); } - return acceptCategory(SEMICOLON); + return _acceptCategory(_Category.semicolon); } bool acceptString(String string) { @@ -800,7 +758,7 @@ class MiniJsParser { /// Returns either the name for the hole, or its integer position. Object parseHash() { String holeName = lastToken; - if (acceptCategory(ALPHA)) { + if (_acceptCategory(_Category.alpha)) { // Named hole. Example: 'function #funName() { ... }' if (hasPositionalHoles) { error('Holes must all be positional or named. $holeName'); @@ -817,7 +775,7 @@ class MiniJsParser { Expression parsePrimary() { String last = lastToken; - if (acceptCategory(ALPHA)) { + if (_acceptCategory(_Category.alpha)) { if (last == 'true') { return LiteralBool(true); } else if (last == 'false') { @@ -831,35 +789,35 @@ class MiniJsParser { } else { return VariableUse(last); } - } else if (acceptCategory(LPAREN)) { + } else if (_acceptCategory(_Category.lparen)) { return parseExpressionOrArrowFunction(); - } else if (acceptCategory(STRING)) { + } else if (_acceptCategory(_Category.string)) { return LiteralString(last); - } else if (acceptCategory(NUMERIC)) { + } else if (_acceptCategory(_Category.numeric)) { return LiteralNumber(last); - } else if (acceptCategory(LBRACE)) { + } else if (_acceptCategory(_Category.lbrace)) { return parseObjectInitializer(); - } else if (acceptCategory(LSQUARE)) { + } else if (_acceptCategory(_Category.lsquare)) { var values = []; while (true) { - if (acceptCategory(COMMA)) { + if (_acceptCategory(_Category.comma)) { values.add(ArrayHole()); continue; } - if (acceptCategory(RSQUARE)) break; + if (_acceptCategory(_Category.rsquare)) break; values.add(parseAssignment()); - if (acceptCategory(RSQUARE)) break; - expectCategory(COMMA); + if (_acceptCategory(_Category.rsquare)) break; + _expectCategory(_Category.comma); } return ArrayInitializer(values); } else if (last.startsWith('/')) { String regexp = getRegExp(lastPosition); getToken(); String flags = lastToken; - if (!acceptCategory(ALPHA)) flags = ''; + if (!_acceptCategory(_Category.alpha)) flags = ''; Expression expression = RegExpLiteral(regexp + flags); return expression; - } else if (acceptCategory(HASH)) { + } else if (_acceptCategory(_Category.hash)) { var nameOrPosition = parseHash(); InterpolatedExpression expression = InterpolatedExpression(nameOrPosition); @@ -871,7 +829,7 @@ class MiniJsParser { } Expression parseFunctionExpression() { - if (lastCategory == ALPHA || lastCategory == HASH) { + if (_lastCategory == _Category.alpha || _lastCategory == _Category.hash) { Declaration name = parseVariableDeclaration(); return NamedFunction(name, parseFun()); } @@ -880,10 +838,10 @@ class MiniJsParser { Fun parseFun() { List params = []; - expectCategory(LPAREN); - if (!acceptCategory(RPAREN)) { + _expectCategory(_Category.lparen); + if (!_acceptCategory(_Category.rparen)) { for (;;) { - if (acceptCategory(HASH)) { + if (_acceptCategory(_Category.hash)) { var nameOrPosition = parseHash(); InterpolatedParameter parameter = InterpolatedParameter(nameOrPosition); @@ -891,11 +849,11 @@ class MiniJsParser { params.add(parameter); } else { String argumentName = lastToken; - expectCategory(ALPHA); + _expectCategory(_Category.alpha); params.add(Parameter(argumentName)); } - if (acceptCategory(COMMA)) continue; - expectCategory(RPAREN); + if (_acceptCategory(_Category.comma)) continue; + _expectCategory(_Category.rparen); break; } } @@ -912,7 +870,7 @@ class MiniJsParser { } else { asyncModifier = AsyncModifier.sync; } - expectCategory(LBRACE); + _expectCategory(_Category.lbrace); Block block = parseBlock(); return Fun(params, block, asyncModifier: asyncModifier); } @@ -920,10 +878,10 @@ class MiniJsParser { Expression parseObjectInitializer() { List properties = []; for (;;) { - if (acceptCategory(RBRACE)) break; + if (_acceptCategory(_Category.rbrace)) break; properties.add(parseMethodDefinitionOrProperty()); - if (acceptCategory(RBRACE)) break; - expectCategory(COMMA); + if (_acceptCategory(_Category.rbrace)) break; + _expectCategory(_Category.comma); } return ObjectInitializer(properties); } @@ -932,14 +890,14 @@ class MiniJsParser { // Limited subset: keys are identifiers, no 'get' or 'set' properties. Literal propertyName; String identifier = lastToken; - if (acceptCategory(ALPHA)) { + if (_acceptCategory(_Category.alpha)) { propertyName = LiteralString(identifier); - } else if (acceptCategory(STRING)) { + } else if (_acceptCategory(_Category.string)) { propertyName = LiteralString(identifier); - } else if (acceptCategory(SYMBOL)) { + } else if (_acceptCategory(_Category.symbol)) { // e.g. void propertyName = LiteralString(identifier); - } else if (acceptCategory(HASH)) { + } else if (_acceptCategory(_Category.hash)) { var nameOrPosition = parseHash(); InterpolatedLiteral interpolatedLiteral = InterpolatedLiteral(nameOrPosition); @@ -948,7 +906,7 @@ class MiniJsParser { } else { error('Expected property name'); } - if (acceptCategory(COLON)) { + if (_acceptCategory(_Category.colon)) { Expression value = parseAssignment(); return Property(propertyName, value); } else { @@ -960,11 +918,11 @@ class MiniJsParser { Expression parseMember() { Expression receiver = parsePrimary(); while (true) { - if (acceptCategory(DOT)) { + if (_acceptCategory(_Category.dot)) { receiver = getDotRhs(receiver); - } else if (acceptCategory(LSQUARE)) { + } else if (_acceptCategory(_Category.lsquare)) { Expression inBraces = parseExpression(); - expectCategory(RSQUARE); + _expectCategory(_Category.rsquare); receiver = PropertyAccess(receiver, inBraces); } else { break; @@ -977,24 +935,24 @@ class MiniJsParser { bool constructor = acceptString('new'); Expression receiver = parseMember(); while (true) { - if (acceptCategory(LPAREN)) { + if (_acceptCategory(_Category.lparen)) { final arguments = []; - if (!acceptCategory(RPAREN)) { + if (!_acceptCategory(_Category.rparen)) { while (true) { Expression argument = parseAssignment(); arguments.add(argument); - if (acceptCategory(RPAREN)) break; - expectCategory(COMMA); + if (_acceptCategory(_Category.rparen)) break; + _expectCategory(_Category.comma); } } receiver = constructor ? New(receiver, arguments) : Call(receiver, arguments); constructor = false; - } else if (!constructor && acceptCategory(LSQUARE)) { + } else if (!constructor && _acceptCategory(_Category.lsquare)) { Expression inBraces = parseExpression(); - expectCategory(RSQUARE); + _expectCategory(_Category.rsquare); receiver = PropertyAccess(receiver, inBraces); - } else if (!constructor && acceptCategory(DOT)) { + } else if (!constructor && _acceptCategory(_Category.dot)) { receiver = getDotRhs(receiver); } else { // JS allows new without (), but we don't. @@ -1006,7 +964,7 @@ class MiniJsParser { } Expression getDotRhs(Expression receiver) { - if (acceptCategory(HASH)) { + if (_acceptCategory(_Category.hash)) { var nameOrPosition = parseHash(); InterpolatedSelector property = InterpolatedSelector(nameOrPosition); interpolatedValues.add(property); @@ -1015,12 +973,12 @@ class MiniJsParser { String identifier = lastToken; // In ES5 keywords like delete and continue are allowed as property // names, and the IndexedDB API uses that, so we need to allow it here. - if (acceptCategory(SYMBOL)) { + if (_acceptCategory(_Category.symbol)) { if (!OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(identifier)) { error('Expected alphanumeric identifier'); } } else { - expectCategory(ALPHA); + _expectCategory(_Category.alpha); } return PropertyAccess.field(receiver, identifier); } @@ -1030,7 +988,7 @@ class MiniJsParser { String operator = lastToken; // JavaScript grammar is: // LeftHandSideExpression [no LineTerminator here] ++ - if (lastCategory == SYMBOL && + if (_lastCategory == _Category.symbol && !skippedNewline && (acceptString('++') || acceptString('--'))) { return Postfix(operator, expression); @@ -1043,7 +1001,7 @@ class MiniJsParser { Expression parseUnaryHigh() { String operator = lastToken; - if (lastCategory == SYMBOL && + if (_lastCategory == _Category.symbol && UNARY_OPERATORS.contains(operator) && (acceptString('++') || acceptString('--') || acceptString('await'))) { if (operator == 'await') return Await(parsePostfix()); @@ -1054,11 +1012,11 @@ class MiniJsParser { Expression parseUnaryLow() { String operator = lastToken; - if (lastCategory == SYMBOL && + if (_lastCategory == _Category.symbol && UNARY_OPERATORS.contains(operator) && operator != '++' && operator != '--') { - expectCategory(SYMBOL); + _expectCategory(_Category.symbol); if (operator == 'await') return Await(parsePostfix()); return Prefix(operator, parseUnaryLow()); } @@ -1073,12 +1031,12 @@ class MiniJsParser { while (true) { final symbol = lastToken; - if (lastCategory != SYMBOL) break; + if (_lastCategory != _Category.symbol) break; final symbolPrecedence = BINARY_PRECEDENCE[symbol]; if (symbolPrecedence == null) break; if (symbolPrecedence > maxPrecedence) break; - expectCategory(SYMBOL); + _expectCategory(_Category.symbol); if (rhs == null || symbolPrecedence >= minPrecedence) { if (rhs != null) lhs = Binary(lastSymbol, lhs, rhs); minPrecedence = symbolPrecedence; @@ -1096,9 +1054,9 @@ class MiniJsParser { Expression parseConditional() { Expression lhs = parseBinary(HIGHEST_PARSE_BINARY_PRECEDENCE); - if (!acceptCategory(QUERY)) return lhs; + if (!_acceptCategory(_Category.query)) return lhs; Expression ifTrue = parseAssignment(); - expectCategory(COLON); + _expectCategory(_Category.colon); Expression ifFalse = parseAssignment(); return Conditional(lhs, ifTrue, ifFalse); } @@ -1106,7 +1064,7 @@ class MiniJsParser { Expression parseAssignment() { Expression lhs = parseConditional(); String assignmentOperator = lastToken; - if (acceptCategory(ASSIGNMENT)) { + if (_acceptCategory(_Category.assignment)) { Expression rhs = parseAssignment(); if (assignmentOperator == '=') { return Assignment(lhs, rhs); @@ -1122,7 +1080,7 @@ class MiniJsParser { Expression parseExpression() { Expression expression = parseAssignment(); - while (acceptCategory(COMMA)) { + while (_acceptCategory(_Category.comma)) { Expression right = parseAssignment(); expression = Binary(',', expression, right); } @@ -1130,16 +1088,16 @@ class MiniJsParser { } Expression parseExpressionOrArrowFunction() { - if (acceptCategory(RPAREN)) { - expectCategory(ARROW); + if (_acceptCategory(_Category.rparen)) { + _expectCategory(_Category.arrow); return parseArrowFunctionBody([]); } List expressions = [parseAssignment()]; - while (acceptCategory(COMMA)) { + while (_acceptCategory(_Category.comma)) { expressions.add(parseAssignment()); } - expectCategory(RPAREN); - if (acceptCategory(ARROW)) { + _expectCategory(_Category.rparen); + if (_acceptCategory(_Category.arrow)) { var params = []; for (Expression e in expressions) { if (e is VariableUse) { @@ -1158,7 +1116,7 @@ class MiniJsParser { Expression parseArrowFunctionBody(List params) { Node body; - if (acceptCategory(LBRACE)) { + if (_acceptCategory(_Category.lbrace)) { body = parseBlock(); } else { body = parseAssignment(); @@ -1184,7 +1142,7 @@ class MiniJsParser { } declare(firstVariable); - while (acceptCategory(COMMA)) { + while (_acceptCategory(_Category.comma)) { Declaration variable = parseVariableDeclaration(); declare(variable); } @@ -1201,16 +1159,16 @@ class MiniJsParser { Expression expression() { Expression expression = parseVarDeclarationOrExpression(); - if (lastCategory != NONE || position != src.length) { - error('Unparsed junk: ${categoryToString(lastCategory)}'); + if (_lastCategory != _Category.none || position != src.length) { + error('Unparsed junk: $_lastCategory'); } return expression; } Statement statement() { Statement statement = parseStatement(); - if (lastCategory != NONE || position != src.length) { - error('Unparsed junk: ${categoryToString(lastCategory)}'); + if (_lastCategory != _Category.none || position != src.length) { + error('Unparsed junk: $_lastCategory'); } // TODO(sra): interpolated capture here? return statement; @@ -1219,7 +1177,7 @@ class MiniJsParser { Block parseBlock() { List statements = []; - while (!acceptCategory(RBRACE)) { + while (!_acceptCategory(_Category.rbrace)) { Statement statement = parseStatement(); statements.add(statement); } @@ -1227,11 +1185,11 @@ class MiniJsParser { } Statement parseStatement() { - if (acceptCategory(LBRACE)) return parseBlock(); + if (_acceptCategory(_Category.lbrace)) return parseBlock(); - if (acceptCategory(SEMICOLON)) return EmptyStatement(); + if (_acceptCategory(_Category.semicolon)) return EmptyStatement(); - if (lastCategory == ALPHA) { + if (_lastCategory == _Category.alpha) { if (acceptString('return')) return parseReturn(); if (acceptString('throw')) return parseThrow(); @@ -1275,11 +1233,11 @@ class MiniJsParser { } } - bool checkForInterpolatedStatement = lastCategory == HASH; + bool checkForInterpolatedStatement = _lastCategory == _Category.hash; Expression expression = parseExpression(); - if (expression is VariableUse && acceptCategory(COLON)) { + if (expression is VariableUse && _acceptCategory(_Category.colon)) { return LabeledStatement(expression.name, parseStatement()); } @@ -1323,7 +1281,7 @@ class MiniJsParser { Statement parseBreakOrContinue(Statement Function(String?) constructor) { var identifier = lastToken; - if (!skippedNewline && acceptCategory(ALPHA)) { + if (!skippedNewline && _acceptCategory(_Category.alpha)) { expectSemicolon(); return constructor(identifier); } @@ -1332,9 +1290,9 @@ class MiniJsParser { } Statement parseIfThenElse() { - expectCategory(LPAREN); + _expectCategory(_Category.lparen); Expression condition = parseExpression(); - expectCategory(RPAREN); + _expectCategory(_Category.rparen); Statement thenStatement = parseStatement(); if (acceptString('else')) { // Resolves dangling else by binding 'else' to closest 'if'. @@ -1354,21 +1312,21 @@ class MiniJsParser { // Statement finishFor(Expression? init) { Expression? condition; - if (!acceptCategory(SEMICOLON)) { + if (!_acceptCategory(_Category.semicolon)) { condition = parseExpression(); - expectCategory(SEMICOLON); + _expectCategory(_Category.semicolon); } Expression? update; - if (!acceptCategory(RPAREN)) { + if (!_acceptCategory(_Category.rparen)) { update = parseExpression(); - expectCategory(RPAREN); + _expectCategory(_Category.rparen); } Statement body = parseStatement(); return For(init, condition, update, body); } - expectCategory(LPAREN); - if (acceptCategory(SEMICOLON)) { + _expectCategory(_Category.lparen); + if (_acceptCategory(_Category.semicolon)) { return finishFor(null); } @@ -1376,7 +1334,7 @@ class MiniJsParser { Declaration declaration = parseVariableDeclaration(); if (acceptString('in')) { Expression objectExpression = parseExpression(); - expectCategory(RPAREN); + _expectCategory(_Category.rparen); Statement body = parseStatement(); return ForIn( VariableDeclarationList( @@ -1385,17 +1343,17 @@ class MiniJsParser { body); } Expression declarations = finishVariableDeclarationList(declaration); - expectCategory(SEMICOLON); + _expectCategory(_Category.semicolon); return finishFor(declarations); } Expression init = parseExpression(); - expectCategory(SEMICOLON); + _expectCategory(_Category.semicolon); return finishFor(init); } Declaration parseVariableDeclaration() { - if (acceptCategory(HASH)) { + if (_acceptCategory(_Category.hash)) { var nameOrPosition = parseHash(); InterpolatedDeclaration declaration = InterpolatedDeclaration(nameOrPosition); @@ -1403,7 +1361,7 @@ class MiniJsParser { return declaration; } else { String token = lastToken; - expectCategory(ALPHA); + _expectCategory(_Category.alpha); return VariableDeclaration(token); } } @@ -1415,13 +1373,13 @@ class MiniJsParser { } Statement parseTry() { - expectCategory(LBRACE); + _expectCategory(_Category.lbrace); Block body = parseBlock(); Catch? catchPart; if (acceptString('catch')) catchPart = parseCatch(); Block? finallyPart; if (acceptString('finally')) { - expectCategory(LBRACE); + _expectCategory(_Category.lbrace); finallyPart = parseBlock(); } else { if (catchPart == null) error("expected 'finally'"); @@ -1433,15 +1391,15 @@ class MiniJsParser { Expression? expression; if (acceptString('case')) { expression = parseExpression(); - expectCategory(COLON); + _expectCategory(_Category.colon); } else { if (!acceptString('default')) { error('expected case or default'); } - expectCategory(COLON); + _expectCategory(_Category.colon); } List statements = []; - while (lastCategory != RBRACE && + while (_lastCategory != _Category.rbrace && lastToken != 'case' && lastToken != 'default') { statements.add(parseStatement()); @@ -1452,9 +1410,9 @@ class MiniJsParser { } Statement parseWhile() { - expectCategory(LPAREN); + _expectCategory(_Category.lparen); Expression condition = parseExpression(); - expectCategory(RPAREN); + _expectCategory(_Category.rparen); Statement body = parseStatement(); return While(condition, body); } @@ -1463,31 +1421,31 @@ class MiniJsParser { Statement body = parseStatement(); if (lastToken != 'while') error('Missing while after do body.'); getToken(); - expectCategory(LPAREN); + _expectCategory(_Category.lparen); Expression condition = parseExpression(); - expectCategory(RPAREN); + _expectCategory(_Category.rparen); expectSemicolon(); return Do(body, condition); } Statement parseSwitch() { - expectCategory(LPAREN); + _expectCategory(_Category.lparen); Expression key = parseExpression(); - expectCategory(RPAREN); - expectCategory(LBRACE); + _expectCategory(_Category.rparen); + _expectCategory(_Category.lbrace); List clauses = []; - while (lastCategory != RBRACE) { + while (_lastCategory != _Category.rbrace) { clauses.add(parseSwitchClause()); } - expectCategory(RBRACE); + _expectCategory(_Category.rbrace); return Switch(key, clauses); } Catch parseCatch() { - expectCategory(LPAREN); + _expectCategory(_Category.lparen); Declaration errorName = parseVariableDeclaration(); - expectCategory(RPAREN); - expectCategory(LBRACE); + _expectCategory(_Category.rparen); + _expectCategory(_Category.lbrace); Block body = parseBlock(); return Catch(errorName, body); } diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart index 595781db269..31429c30e74 100644 --- a/pkg/js_ast/lib/src/nodes.dart +++ b/pkg/js_ast/lib/src/nodes.dart @@ -1382,7 +1382,7 @@ class DartYield extends Statement { abstract class Expression extends Node { // [precedenceLevel] must not be used before printing, as deferred nodes can // have precedence depending on how the deferred node is resolved. - int get precedenceLevel; + Precedence get precedenceLevel; // Override for refined return type. @override @@ -1501,7 +1501,7 @@ class LiteralExpression extends Expression { // Code that uses LiteralExpression must take care of operator precedences, // and put parenthesis if needed. @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } /// [VariableDeclarationList] is a subclass of [Expression] to simplify the AST. @@ -1541,7 +1541,7 @@ class VariableDeclarationList extends Expression { VariableDeclarationList _clone() => VariableDeclarationList(declarations); @override - int get precedenceLevel => EXPRESSION; + Precedence get precedenceLevel => Precedence.expression; } /// Forced parenthesized expression. Pretty-printing will emit parentheses based @@ -1572,7 +1572,7 @@ class Parentheses extends Expression { Parentheses _clone() => Parentheses(enclosed); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } class Assignment extends Expression { @@ -1586,7 +1586,7 @@ class Assignment extends Expression { Assignment.compound(this.leftHandSide, this.op, this.value); @override - int get precedenceLevel => ASSIGNMENT; + Precedence get precedenceLevel => Precedence.assignment; bool get isCompound => op != null; @@ -1626,7 +1626,7 @@ class VariableInitialization extends Expression { } @override - int get precedenceLevel => ASSIGNMENT; + Precedence get precedenceLevel => Precedence.assignment; @override T accept(NodeVisitor visitor) => @@ -1684,7 +1684,7 @@ class Conditional extends Expression { Conditional _clone() => Conditional(condition, then, otherwise); @override - int get precedenceLevel => ASSIGNMENT; + Precedence get precedenceLevel => Precedence.assignment; } class Call extends Expression { @@ -1723,7 +1723,7 @@ class Call extends Expression { Call _clone() => Call(target, arguments); @override - int get precedenceLevel => CALL; + Precedence get precedenceLevel => Precedence.call; } class New extends Call { @@ -1740,7 +1740,7 @@ class New extends Call { New _clone() => New(target, arguments); @override - int get precedenceLevel => LEFT_HAND_SIDE; + Precedence get precedenceLevel => Precedence.leftHandSide; } class Binary extends Expression { @@ -1776,46 +1776,46 @@ class Binary extends Expression { bool get isCommaOperator => op == ','; @override - int get precedenceLevel { + Precedence get precedenceLevel { // TODO(floitsch): switch to constant map. switch (op) { case '**': - return EXPONENTIATION; + return Precedence.exponentiation; case '*': case '/': case '%': - return MULTIPLICATIVE; + return Precedence.multiplicative; case '+': case '-': - return ADDITIVE; + return Precedence.additive; case '<<': case '>>': case '>>>': - return SHIFT; + return Precedence.shift; case '<': case '>': case '<=': case '>=': case 'instanceof': case 'in': - return RELATIONAL; + return Precedence.relational; case '==': case '===': case '!=': case '!==': - return EQUALITY; + return Precedence.equality; case '&': - return BIT_AND; + return Precedence.bitAnd; case '^': - return BIT_XOR; + return Precedence.bitXor; case '|': - return BIT_OR; + return Precedence.bitOr; case '&&': - return LOGICAL_AND; + return Precedence.logicalAnd; case '||': - return LOGICAL_OR; + return Precedence.logicalOr; case ',': - return EXPRESSION; + return Precedence.expression; default: throw 'Internal Error: Unhandled binary operator: $op'; } @@ -1849,7 +1849,7 @@ class Prefix extends Expression { } @override - int get precedenceLevel => UNARY; + Precedence get precedenceLevel => Precedence.unary; } class Postfix extends Expression { @@ -1879,7 +1879,7 @@ class Postfix extends Expression { } @override - int get precedenceLevel => UNARY; + Precedence get precedenceLevel => Precedence.unary; } RegExp _identifierRE = RegExp(r'^[A-Za-z_$][A-Za-z_$0-9]*$'); @@ -1895,7 +1895,7 @@ abstract class VariableReference extends Expression { T accept(NodeVisitor visitor); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; @override void visitChildren(NodeVisitor visitor) {} @@ -1994,7 +1994,7 @@ class NamedFunction extends Expression { NamedFunction _clone() => NamedFunction(name, function); @override - int get precedenceLevel => LEFT_HAND_SIDE; + Precedence get precedenceLevel => Precedence.leftHandSide; } abstract class FunctionExpression extends Expression { @@ -2040,7 +2040,7 @@ class Fun extends FunctionExpression { Fun _clone() => Fun(params, body, asyncModifier: asyncModifier); @override - int get precedenceLevel => LEFT_HAND_SIDE; + Precedence get precedenceLevel => Precedence.leftHandSide; } class ArrowFunction extends FunctionExpression { @@ -2082,7 +2082,7 @@ class ArrowFunction extends FunctionExpression { ArrowFunction(params, body, asyncModifier: asyncModifier); @override - int get precedenceLevel => ASSIGNMENT; + Precedence get precedenceLevel => Precedence.assignment; } enum AsyncModifier { @@ -2138,7 +2138,7 @@ class PropertyAccess extends Expression { PropertyAccess _clone() => PropertyAccess(receiver, selector); @override - int get precedenceLevel => LEFT_HAND_SIDE; + Precedence get precedenceLevel => Precedence.leftHandSide; } /// A [DeferredToken] is a placeholder for some [Expression] that is not known @@ -2169,7 +2169,8 @@ abstract class DeferredNumber extends DeferredToken implements Literal { int get value; @override - int get precedenceLevel => value.isNegative ? UNARY : PRIMARY; + Precedence get precedenceLevel => + value.isNegative ? Precedence.unary : Precedence.primary; } /// Interface for a deferred string value. An implementation has to provide @@ -2185,7 +2186,7 @@ abstract class DeferredString extends DeferredToken implements Literal { String get value; @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } /// Interface for a deferred [Expression] value. An implementation has to provide @@ -2211,7 +2212,7 @@ abstract class Literal extends Expression { void visitChildren1(NodeVisitor1 visitor, A arg) {} @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } class LiteralBool extends Literal { @@ -2330,7 +2331,8 @@ class LiteralNumber extends Literal { LiteralNumber(this.value); @override - int get precedenceLevel => value.startsWith('-') ? UNARY : PRIMARY; + Precedence get precedenceLevel => + value.startsWith('-') ? Precedence.unary : Precedence.primary; @override T accept(NodeVisitor visitor) => visitor.visitLiteralNumber(this); @@ -2373,7 +2375,7 @@ class ArrayInitializer extends Expression { ArrayInitializer _clone() => ArrayInitializer(elements); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } /// An empty place in an [ArrayInitializer]. @@ -2396,7 +2398,7 @@ class ArrayHole extends Expression { ArrayHole _clone() => ArrayHole(); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } class ObjectInitializer extends Expression { @@ -2436,7 +2438,7 @@ class ObjectInitializer extends Expression { ObjectInitializer(properties, isOneLiner: isOneLiner); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } class Property extends Node { @@ -2535,7 +2537,7 @@ class InterpolatedExpression extends Expression with InterpolatedNode { InterpolatedExpression _clone() => InterpolatedExpression(nameOrPosition); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } class InterpolatedLiteral extends Literal with InterpolatedNode { @@ -2595,7 +2597,7 @@ class InterpolatedParameter extends Expression InterpolatedParameter _clone() => InterpolatedParameter(nameOrPosition); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } class InterpolatedSelector extends Expression with InterpolatedNode { @@ -2622,7 +2624,7 @@ class InterpolatedSelector extends Expression with InterpolatedNode { InterpolatedSelector _clone() => InterpolatedSelector(nameOrPosition); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } class InterpolatedStatement extends Statement with InterpolatedNode { @@ -2680,7 +2682,7 @@ class InterpolatedDeclaration extends Expression String get name => throw 'No name for the interpolated node'; @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } /// [RegExpLiteral]s, despite being called "Literal", do not inherit from @@ -2711,7 +2713,7 @@ class RegExpLiteral extends Expression { RegExpLiteral _clone() => RegExpLiteral(pattern); @override - int get precedenceLevel => PRIMARY; + Precedence get precedenceLevel => Precedence.primary; } /// An asynchronous await. @@ -2725,7 +2727,7 @@ class Await extends Expression { Await(this.expression); @override - int get precedenceLevel => UNARY; + Precedence get precedenceLevel => Precedence.unary; @override T accept(NodeVisitor visitor) => visitor.visitAwait(this); diff --git a/pkg/js_ast/lib/src/precedence.dart b/pkg/js_ast/lib/src/precedence.dart index af8f7698810..b146b131478 100644 --- a/pkg/js_ast/lib/src/precedence.dart +++ b/pkg/js_ast/lib/src/precedence.dart @@ -4,21 +4,26 @@ library precedence; -const EXPRESSION = 0; -const ASSIGNMENT = EXPRESSION + 1; -const LOGICAL_OR = ASSIGNMENT + 1; -const LOGICAL_AND = LOGICAL_OR + 1; -const BIT_OR = LOGICAL_AND + 1; -const BIT_XOR = BIT_OR + 1; -const BIT_AND = BIT_XOR + 1; -const EQUALITY = BIT_AND + 1; -const RELATIONAL = EQUALITY + 1; -const SHIFT = RELATIONAL + 1; -const ADDITIVE = SHIFT + 1; -const MULTIPLICATIVE = ADDITIVE + 1; -const EXPONENTIATION = MULTIPLICATIVE + 1; -const UNARY = EXPONENTIATION + 1; -const UPDATE = UNARY + 1; -const CALL = UPDATE + 1; -const LEFT_HAND_SIDE = CALL + 1; -const PRIMARY = LEFT_HAND_SIDE + 1; +// The ordering of the values in this enum is important. Higher enum indices +// correspond to higher precedences derived from the expression grammar +// specification at https://tc39.es/ecma262/. +enum Precedence { + expression, + assignment, + logicalOr, + logicalAnd, + bitOr, + bitXor, + bitAnd, + equality, + relational, + shift, + additive, + multiplicative, + exponentiation, + unary, + update, + call, + leftHandSide, + primary, +} diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart index 0803586f396..e1fdd9f0d86 100644 --- a/pkg/js_ast/lib/src/printer.dart +++ b/pkg/js_ast/lib/src/printer.dart @@ -291,7 +291,7 @@ class Printer implements NodeVisitor { endNode(node); } - void visitCommaSeparated(List nodes, int hasRequiredType, + void visitCommaSeparated(List nodes, Precedence hasRequiredType, {required bool newInForInit, required bool newAtStatementBegin}) { for (int i = 0; i < nodes.length; i++) { if (i != 0) { @@ -393,7 +393,7 @@ class Printer implements NodeVisitor { @override void visitExpressionStatement(ExpressionStatement node) { indent(); - visitNestedExpression(node.expression, EXPRESSION, + visitNestedExpression(node.expression, Precedence.expression, newInForInit: false, newAtStatementBegin: true); outSemicolonLn(); } @@ -417,7 +417,7 @@ class Printer implements NodeVisitor { out('if'); spaceOut(); out('('); - visitNestedExpression(node.condition, EXPRESSION, + visitNestedExpression(node.condition, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); bool thenWasBlock = blockBody(then, @@ -453,19 +453,19 @@ class Printer implements NodeVisitor { spaceOut(); out('('); if (loop.init != null) { - visitNestedExpression(loop.init!, EXPRESSION, + visitNestedExpression(loop.init!, Precedence.expression, newInForInit: true, newAtStatementBegin: false); } out(';'); if (loop.condition != null) { spaceOut(); - visitNestedExpression(loop.condition!, EXPRESSION, + visitNestedExpression(loop.condition!, Precedence.expression, newInForInit: false, newAtStatementBegin: false); } out(';'); if (loop.update != null) { spaceOut(); - visitNestedExpression(loop.update!, EXPRESSION, + visitNestedExpression(loop.update!, Precedence.expression, newInForInit: false, newAtStatementBegin: false); } out(')'); @@ -477,11 +477,11 @@ class Printer implements NodeVisitor { outIndent('for'); spaceOut(); out('('); - visitNestedExpression(loop.leftHandSide, EXPRESSION, + visitNestedExpression(loop.leftHandSide, Precedence.expression, newInForInit: true, newAtStatementBegin: false); out(' in'); pendingSpace = true; - visitNestedExpression(loop.object, EXPRESSION, + visitNestedExpression(loop.object, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); blockBody(loop.body, needsSeparation: false, needsNewline: true); @@ -492,7 +492,7 @@ class Printer implements NodeVisitor { outIndent('while'); spaceOut(); out('('); - visitNestedExpression(loop.condition, EXPRESSION, + visitNestedExpression(loop.condition, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); blockBody(loop.body, needsSeparation: false, needsNewline: true); @@ -509,7 +509,7 @@ class Printer implements NodeVisitor { out('while'); spaceOut(); out('('); - visitNestedExpression(loop.condition, EXPRESSION, + visitNestedExpression(loop.condition, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); outSemicolonLn(); @@ -543,7 +543,7 @@ class Printer implements NodeVisitor { } else { outIndent('return'); pendingSpace = true; - visitNestedExpression(value, EXPRESSION, + visitNestedExpression(value, Precedence.expression, newInForInit: false, newAtStatementBegin: false); } // Set the closing position to be before the optional semicolon. @@ -559,7 +559,7 @@ class Printer implements NodeVisitor { outIndent('yield'); } pendingSpace = true; - visitNestedExpression(node.expression, EXPRESSION, + visitNestedExpression(node.expression, Precedence.expression, newInForInit: false, newAtStatementBegin: false); outSemicolonLn(); } @@ -568,7 +568,7 @@ class Printer implements NodeVisitor { void visitThrow(Throw node) { outIndent('throw'); pendingSpace = true; - visitNestedExpression(node.expression, EXPRESSION, + visitNestedExpression(node.expression, Precedence.expression, newInForInit: false, newAtStatementBegin: false); outSemicolonLn(); } @@ -595,7 +595,7 @@ class Printer implements NodeVisitor { out('catch'); spaceOut(); out('('); - visitNestedExpression(node.declaration, EXPRESSION, + visitNestedExpression(node.declaration, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); blockBody(node.body, needsSeparation: false, needsNewline: false); @@ -606,7 +606,7 @@ class Printer implements NodeVisitor { outIndent('switch'); spaceOut(); out('('); - visitNestedExpression(node.key, EXPRESSION, + visitNestedExpression(node.key, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); spaceOut(); @@ -621,7 +621,7 @@ class Printer implements NodeVisitor { void visitCase(Case node) { outIndent('case'); pendingSpace = true; - visitNestedExpression(node.expression, EXPRESSION, + visitNestedExpression(node.expression, Precedence.expression, newInForInit: false, newAtStatementBegin: false); outLn(':'); if (node.body.statements.isNotEmpty) { @@ -652,12 +652,12 @@ class Printer implements NodeVisitor { if (name != null) { out(' '); // Name must be a [Decl]. Therefore only test for primary expressions. - visitNestedExpression(name, PRIMARY, + visitNestedExpression(name, Precedence.primary, newInForInit: false, newAtStatementBegin: false); } localNamer.enterScope(vars); out('('); - visitCommaSeparated(fun.params, PRIMARY, + visitCommaSeparated(fun.params, Precedence.primary, newInForInit: false, newAtStatementBegin: false); out(')'); switch (fun.asyncModifier) { @@ -695,14 +695,15 @@ class Printer implements NodeVisitor { lineOut(); } - void visitNestedExpression(Expression node, int requiredPrecedence, + void visitNestedExpression(Expression node, Precedence requiredPrecedence, {required bool newInForInit, required bool newAtStatementBegin}) { - int precedenceLevel = - (isDebugContext && !node.isFinalized) ? CALL : node.precedenceLevel; + Precedence precedenceLevel = (isDebugContext && !node.isFinalized) + ? Precedence.call + : node.precedenceLevel; bool needsParentheses = // a - (b + c). - (requiredPrecedence != EXPRESSION && - precedenceLevel < requiredPrecedence) || + (requiredPrecedence != Precedence.expression && + precedenceLevel.index < requiredPrecedence.index) || // for (a = (x in o); ... ; ... ) { ... } (newInForInit && node is Binary && node.op == 'in') || // (function() { ... })(). @@ -732,7 +733,7 @@ class Printer implements NodeVisitor { out('var '); final nodes = list.declarations; if (inForInit) { - visitCommaSeparated(nodes, ASSIGNMENT, + visitCommaSeparated(nodes, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } else { // Print 'big' declarations on their own line, while keeping adjacent @@ -755,7 +756,7 @@ class Printer implements NodeVisitor { spaceOut(); } } - visitNestedExpression(node, ASSIGNMENT, + visitNestedExpression(node, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); lastWasBig = thisIsBig; } @@ -789,7 +790,7 @@ class Printer implements NodeVisitor { out('--'); } if (alias != null) startNode(alias); - visitNestedExpression(variable, UNARY, + visitNestedExpression(variable, Precedence.unary, newInForInit: inForInit, newAtStatementBegin: false); if (alias != null) endNode(alias); } @@ -834,57 +835,57 @@ class Printer implements NodeVisitor { } // Output 'a = a + b' as 'a += b'. startNode(rightHandSide.left); - visitNestedExpression(assignment.leftHandSide, CALL, + visitNestedExpression(assignment.leftHandSide, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); endNode(rightHandSide.left); spaceOut(); out(op); out('='); spaceOut(); - visitNestedExpression(rRight, ASSIGNMENT, + visitNestedExpression(rRight, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); return; } } } - visitNestedExpression(assignment.leftHandSide, CALL, + visitNestedExpression(assignment.leftHandSide, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); spaceOut(); if (op != null) out(op); out('='); spaceOut(); - visitNestedExpression(assignment.value, ASSIGNMENT, + visitNestedExpression(assignment.value, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } @override void visitVariableInitialization(VariableInitialization initialization) { - visitNestedExpression(initialization.declaration, CALL, + visitNestedExpression(initialization.declaration, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); if (initialization.value != null) { spaceOut(); out('='); spaceOut(); - visitNestedExpression(initialization.value!, ASSIGNMENT, + visitNestedExpression(initialization.value!, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } } @override void visitConditional(Conditional cond) { - visitNestedExpression(cond.condition, LOGICAL_OR, + visitNestedExpression(cond.condition, Precedence.logicalOr, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); spaceOut(); out('?'); spaceOut(); // The then part is allowed to have an 'in'. - visitNestedExpression(cond.then, ASSIGNMENT, + visitNestedExpression(cond.then, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); spaceOut(); out(':'); spaceOut(); - visitNestedExpression(cond.otherwise, ASSIGNMENT, + visitNestedExpression(cond.otherwise, Precedence.assignment, newInForInit: inForInit, newAtStatementBegin: false); } @@ -893,11 +894,11 @@ class Printer implements NodeVisitor { out('new '); final savedInNewTarget = inNewTarget; inNewTarget = true; - visitNestedExpression(node.target, LEFT_HAND_SIDE, + visitNestedExpression(node.target, Precedence.leftHandSide, newInForInit: inForInit, newAtStatementBegin: false); out('('); inNewTarget = false; - visitCommaSeparated(node.arguments, ASSIGNMENT, + visitCommaSeparated(node.arguments, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); out(')'); inNewTarget = savedInNewTarget; @@ -905,10 +906,10 @@ class Printer implements NodeVisitor { @override void visitCall(Call call) { - visitNestedExpression(call.target, CALL, + visitNestedExpression(call.target, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); out('('); - visitCommaSeparated(call.arguments, ASSIGNMENT, + visitCommaSeparated(call.arguments, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); out(')'); } @@ -918,47 +919,47 @@ class Printer implements NodeVisitor { Expression left = binary.left; Expression right = binary.right; String op = binary.op; - int leftPrecedenceRequirement; - int rightPrecedenceRequirement; + Precedence leftPrecedenceRequirement; + Precedence rightPrecedenceRequirement; bool leftSpace = true; // leftop right switch (op) { case ',': // x, (y, z) <=> (x, y), z. - leftPrecedenceRequirement = EXPRESSION; - rightPrecedenceRequirement = EXPRESSION; + leftPrecedenceRequirement = Precedence.expression; + rightPrecedenceRequirement = Precedence.expression; leftSpace = false; break; case '||': - leftPrecedenceRequirement = LOGICAL_OR; + leftPrecedenceRequirement = Precedence.logicalOr; // x || (y || z) <=> (x || y) || z. - rightPrecedenceRequirement = LOGICAL_OR; + rightPrecedenceRequirement = Precedence.logicalOr; break; case '&&': - leftPrecedenceRequirement = LOGICAL_AND; + leftPrecedenceRequirement = Precedence.logicalAnd; // x && (y && z) <=> (x && y) && z. - rightPrecedenceRequirement = LOGICAL_AND; + rightPrecedenceRequirement = Precedence.logicalAnd; break; case '|': - leftPrecedenceRequirement = BIT_OR; + leftPrecedenceRequirement = Precedence.bitOr; // x | (y | z) <=> (x | y) | z. - rightPrecedenceRequirement = BIT_OR; + rightPrecedenceRequirement = Precedence.bitOr; break; case '^': - leftPrecedenceRequirement = BIT_XOR; + leftPrecedenceRequirement = Precedence.bitXor; // x ^ (y ^ z) <=> (x ^ y) ^ z. - rightPrecedenceRequirement = BIT_XOR; + rightPrecedenceRequirement = Precedence.bitXor; break; case '&': - leftPrecedenceRequirement = BIT_AND; + leftPrecedenceRequirement = Precedence.bitAnd; // x & (y & z) <=> (x & y) & z. - rightPrecedenceRequirement = BIT_AND; + rightPrecedenceRequirement = Precedence.bitAnd; break; case '==': case '!=': case '===': case '!==': - leftPrecedenceRequirement = EQUALITY; - rightPrecedenceRequirement = RELATIONAL; + leftPrecedenceRequirement = Precedence.equality; + rightPrecedenceRequirement = Precedence.relational; break; case '<': case '>': @@ -966,31 +967,31 @@ class Printer implements NodeVisitor { case '>=': case 'instanceof': case 'in': - leftPrecedenceRequirement = RELATIONAL; - rightPrecedenceRequirement = SHIFT; + leftPrecedenceRequirement = Precedence.relational; + rightPrecedenceRequirement = Precedence.shift; break; case '>>': case '<<': case '>>>': - leftPrecedenceRequirement = SHIFT; - rightPrecedenceRequirement = ADDITIVE; + leftPrecedenceRequirement = Precedence.shift; + rightPrecedenceRequirement = Precedence.additive; break; case '+': case '-': - leftPrecedenceRequirement = ADDITIVE; + leftPrecedenceRequirement = Precedence.additive; // We cannot remove parenthesis for "+" because // x + (y + z) (x + y) + z: // Example: // "a" + (1 + 2) => "a3"; // ("a" + 1) + 2 => "a12"; - rightPrecedenceRequirement = MULTIPLICATIVE; + rightPrecedenceRequirement = Precedence.multiplicative; break; case '*': case '/': case '%': - leftPrecedenceRequirement = MULTIPLICATIVE; + leftPrecedenceRequirement = Precedence.multiplicative; // We cannot remove parenthesis for "*" because of precision issues. - rightPrecedenceRequirement = UNARY; + rightPrecedenceRequirement = Precedence.unary; break; case '**': // Exponentiation associates to the right, so `a ** b ** c` parses as `a @@ -1000,12 +1001,12 @@ class Printer implements NodeVisitor { // operator [must be an UPDATE // expression](https://tc39.es/ecma262/#sec-exp-operator). Skipping // [UNARY] avoids printing `-1 ** 2`, which is a syntax error. - leftPrecedenceRequirement = UPDATE; - rightPrecedenceRequirement = EXPONENTIATION; + leftPrecedenceRequirement = Precedence.update; + rightPrecedenceRequirement = Precedence.exponentiation; break; default: - leftPrecedenceRequirement = EXPRESSION; - rightPrecedenceRequirement = EXPRESSION; + leftPrecedenceRequirement = Precedence.expression; + rightPrecedenceRequirement = Precedence.expression; context.error('Forgot operator: $op'); } @@ -1052,13 +1053,13 @@ class Printer implements NodeVisitor { default: out(op); } - visitNestedExpression(unary.argument, UNARY, + visitNestedExpression(unary.argument, Precedence.unary, newInForInit: inForInit, newAtStatementBegin: false); } @override void visitPostfix(Postfix postfix) { - visitNestedExpression(postfix.argument, CALL, + visitNestedExpression(postfix.argument, Precedence.call, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); out(postfix.op); } @@ -1111,7 +1112,7 @@ class Printer implements NodeVisitor { @override void visitAccess(PropertyAccess access) { - final precedence = inNewTarget ? LEFT_HAND_SIDE : CALL; + final precedence = inNewTarget ? Precedence.leftHandSide : Precedence.call; visitNestedExpression(access.receiver, precedence, newInForInit: inForInit, newAtStatementBegin: atStatementBegin); @@ -1138,7 +1139,7 @@ class Printer implements NodeVisitor { out('['); inNewTarget = false; - visitNestedExpression(access.selector, EXPRESSION, + visitNestedExpression(access.selector, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(']'); } @@ -1195,11 +1196,11 @@ class Printer implements NodeVisitor { localNamer.enterScope(vars); final List params = fun.params; if (params.length == 1 && _isIdentifierParameter(params.first)) { - visitNestedExpression(params.single, ASSIGNMENT, + visitNestedExpression(params.single, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); } else { out('('); - visitCommaSeparated(fun.params, PRIMARY, + visitCommaSeparated(fun.params, Precedence.primary, newInForInit: false, newAtStatementBegin: false); out(')'); } @@ -1217,7 +1218,7 @@ class Printer implements NodeVisitor { // https://tc39.github.io/ecma262/#sec-arrow-function-definitions bool needsParens = body is ObjectInitializer; if (needsParens) out('('); - visitNestedExpression(body as Expression, ASSIGNMENT, + visitNestedExpression(body as Expression, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); if (needsParens) out(')'); closingPosition = _charCount; @@ -1306,7 +1307,7 @@ class Printer implements NodeVisitor { @override void visitParentheses(Parentheses node) { out('('); - visitNestedExpression(node.enclosed, EXPRESSION, + visitNestedExpression(node.enclosed, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(')'); } @@ -1338,7 +1339,7 @@ class Printer implements NodeVisitor { continue; } if (i != 0) spaceOut(); - visitNestedExpression(element, ASSIGNMENT, + visitNestedExpression(element, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); // We can skip the trailing "," for the last element (since it's not // an array hole). @@ -1392,7 +1393,7 @@ class Printer implements NodeVisitor { propertyNameOut(node); out(':'); spaceOut(); - visitNestedExpression(node.value, ASSIGNMENT, + visitNestedExpression(node.value, Precedence.assignment, newInForInit: false, newAtStatementBegin: false); } @@ -1411,7 +1412,7 @@ class Printer implements NodeVisitor { Fun fun = node.function; localNamer.enterScope(vars); out('('); - visitCommaSeparated(fun.params, PRIMARY, + visitCommaSeparated(fun.params, Precedence.primary, newInForInit: false, newAtStatementBegin: false); out(')'); spaceOut(); @@ -1434,7 +1435,7 @@ class Printer implements NodeVisitor { // Handle general expressions, .e.g. `{[x]: 1}`. // String concatenation could be better. out('['); - visitNestedExpression(node.name, EXPRESSION, + visitNestedExpression(node.name, Precedence.expression, newInForInit: false, newAtStatementBegin: false); out(']'); } diff --git a/pkg/js_ast/test/deferred_expression_test.dart b/pkg/js_ast/test/deferred_expression_test.dart index f5199641ebb..e9b4195c511 100644 --- a/pkg/js_ast/test/deferred_expression_test.dart +++ b/pkg/js_ast/test/deferred_expression_test.dart @@ -4,6 +4,7 @@ import 'package:expect/expect.dart'; import 'package:js_ast/js_ast.dart'; +import 'package:js_ast/src/precedence.dart'; void main() { Map map = {}; @@ -118,7 +119,7 @@ class _DeferredExpression extends DeferredExpression { _DeferredExpression(this.value); @override - int get precedenceLevel => value.precedenceLevel; + Precedence get precedenceLevel => value.precedenceLevel; } class _Context implements JavaScriptPrintingContext { diff --git a/pkg/js_runtime/lib/synced/invocation_mirror_constants.dart b/pkg/js_runtime/lib/synced/invocation_mirror_constants.dart new file mode 100644 index 00000000000..9e80cecb7c8 --- /dev/null +++ b/pkg/js_runtime/lib/synced/invocation_mirror_constants.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// 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. + +const int method = 0; +const int getter = 1; +const int setter = 2; diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart index bcaf7907642..589f5aab0b9 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -79,6 +79,8 @@ import 'dart:_rti' as newRti import 'dart:_load_library_priority'; +import 'dart:_invocation_mirror_constants' as mirrors; + part 'annotations.dart'; part 'constant_map.dart'; part 'instantiation.dart'; @@ -213,10 +215,6 @@ void traceHelper(dynamic /*int*/ id, dynamic /*String*/ qualifiedName) { } class JSInvocationMirror implements Invocation { - static const METHOD = 0; - static const GETTER = 1; - static const SETTER = 2; - /// When [_memberName] is a String, it holds the mangled name of this /// invocation. When it is a Symbol, it holds the unmangled name. var /* String or Symbol */ _memberName; @@ -234,10 +232,10 @@ class JSInvocationMirror implements Invocation { return _memberName = _symbol_dev.Symbol.unvalidated(_memberName); } - bool get isMethod => _kind == METHOD; - bool get isGetter => _kind == GETTER; - bool get isSetter => _kind == SETTER; - bool get isAccessor => _kind != METHOD; + bool get isMethod => _kind == mirrors.method; + bool get isGetter => _kind == mirrors.getter; + bool get isSetter => _kind == mirrors.setter; + bool get isAccessor => _kind != mirrors.method; List get typeArguments { if (_typeArgumentCount == 0) return const []; @@ -864,12 +862,7 @@ class Primitives { '${JS_GET_NAME(JsGetName.CALL_PREFIX)}\$$argumentCount$names'; return function.noSuchMethod(createUnmangledInvocationMirror( - #call, - selectorName, - JSInvocationMirror.METHOD, - arguments, - namedArgumentList, - 0)); + #call, selectorName, mirrors.method, arguments, namedArgumentList, 0)); } /// Implements [Function.apply] for the lazy and startup emitters. diff --git a/sdk/lib/_internal/js_runtime/lib/synced/invocation_mirror_constants.dart b/sdk/lib/_internal/js_runtime/lib/synced/invocation_mirror_constants.dart new file mode 100644 index 00000000000..9e80cecb7c8 --- /dev/null +++ b/sdk/lib/_internal/js_runtime/lib/synced/invocation_mirror_constants.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// 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. + +const int method = 0; +const int getter = 1; +const int setter = 2; diff --git a/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart b/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart index 1e99ff86aa3..f219c3f1bdb 100644 --- a/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart +++ b/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart @@ -285,6 +285,12 @@ const Map libraries = const { documented: false, platforms: DART2JS_PLATFORM, ), + '_invocation_mirror_constants': const LibraryInfo( + '_internal/js_runtime/lib/synced/invocation_mirror_constants.dart', + categories: '', + documented: false, + platforms: DART2JS_PLATFORM, + ), '_recipe_syntax': const LibraryInfo( '_internal/js_shared/lib/synced/recipe_syntax.dart', categories: '', diff --git a/sdk/lib/libraries.json b/sdk/lib/libraries.json index 853d286fa3b..989e4ddf408 100644 --- a/sdk/lib/libraries.json +++ b/sdk/lib/libraries.json @@ -522,6 +522,9 @@ "_async_status_codes": { "uri": "_internal/js_runtime/lib/synced/async_status_codes.dart" }, + "_invocation_mirror_constants": { + "uri": "_internal/js_runtime/lib/synced/invocation_mirror_constants.dart" + }, "_load_library_priority": { "uri": "_internal/js_runtime/lib/synced/load_library_priority.dart" }, diff --git a/sdk/lib/libraries.yaml b/sdk/lib/libraries.yaml index aea1ed6d51d..e5b4c23c285 100644 --- a/sdk/lib/libraries.yaml +++ b/sdk/lib/libraries.yaml @@ -433,6 +433,9 @@ _dart2js_common: _async_status_codes: uri: "_internal/js_runtime/lib/synced/async_status_codes.dart" + _invocation_mirror_constants: + uri: "_internal/js_runtime/lib/synced/invocation_mirror_constants.dart" + _load_library_priority: uri: "_internal/js_runtime/lib/synced/load_library_priority.dart"