[dart2js] Convert some collections of const ints to enums

Change-Id: Ib1f5c686c97f8f097fa21a435e684af86f2b569c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352975
Commit-Queue: Mayank Patke <fishythefish@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
This commit is contained in:
Mayank Patke 2024-02-23 19:52:50 +00:00 committed by Commit Queue
parent 62e5a482e4
commit 9fa1c16029
38 changed files with 1014 additions and 966 deletions

View file

@ -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<js.Node> get containedNodes {

View file

@ -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);

View file

@ -116,7 +116,7 @@ class SizeEstimator implements NodeVisitor {
node.accept(this);
}
void visitCommaSeparated(List<Expression> nodes, int hasRequiredType,
void visitCommaSeparated(List<Expression> 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);

View file

@ -23,27 +23,24 @@ abstract class FunctionCompiler {
List<CompilerTask> 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<FunctionEntity, int> _cachedDecisions = {};
final Map<FunctionEntity, _Decision> _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;
}

View file

@ -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<T>(js.NodeVisitor<T> visitor) => visitor.visitParameter(this);

View file

@ -1427,6 +1427,22 @@ class ConstantNamingVisitor implements ConstantValueVisitor<void, Null> {
}
}
/// 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<int, Null> {
final JClosedWorld _closedWorld;
final Map<ConstantValue, int> _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<int, Null> {
@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<int, Null> {
@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<int, Null> {
@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<int, Null> {
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<int, Null> {
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 {

View file

@ -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(

View file

@ -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(

View file

@ -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<String> parameterNames =
List.generate(selector.argumentCount, (i) => '\$$i') +
List.generate(selector.typeArgumentCount, (i) => '\$T${i + 1}');

View file

@ -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 {

View file

@ -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;
}

View file

@ -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<String> _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 = <String, List<String>>{
'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 = <String, List<String>>{
'dart:_http',
'dart:_interceptors',
'dart:_internal',
'dart:_invocation_mirror_constants',
'dart:_js',
'dart:_js_annotations',
'dart:_js_embedded_names',

View file

@ -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);
}

View file

@ -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;

View file

@ -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<void>
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<void>
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<void>
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<void>
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<void>
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<void>
List<HInstruction> argumentNames = <HInstruction>[];
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<void>
js.Name internalName = _namer.invocationName(selector);
ConstantValue kindConstant =
constant_system.createIntFromInt(selector.invocationMirrorKind);
constant_system.createIntFromInt(selector.invocationMirrorKind.index);
_pushStaticInvocation(
_commonElements.createUnmangledInvocationMirror,

View file

@ -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);

View file

@ -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));
}

View file

@ -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<int>
with ir.VisitorDefaultValueMixin<int> {
class _KernelLoopTypeVisitor extends ir.VisitorDefault<LoopBlockInformationKind>
with ir.VisitorDefaultValueMixin<LoopBlockInformationKind> {
@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;
}

View file

@ -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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<HInstruction> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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<R>(HVisitor<R> 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;

View file

@ -1143,7 +1143,7 @@ class SsaInstructionSimplifier extends HBaseVisitor<HInstruction>
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;

View file

@ -312,13 +312,14 @@ class HInstructionStringifier implements HVisitor<String> {
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<String> {
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) {

View file

@ -259,12 +259,9 @@ class SsaTypePropagator extends HBaseVisitor<AbstractValue>
}
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<AbstractValue>
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<AbstractValue>
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<AbstractValue>
// 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;

View file

@ -903,14 +903,14 @@ class SsaValueRangeAnalyzer extends HBaseVisitor<Range>
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) {

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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=[{}],

View file

@ -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");

View file

@ -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 = <int>[
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 = <Expression>[];
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<Parameter> 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<Property> 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 = <Expression>[];
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<Expression> 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 = <Parameter>[];
for (Expression e in expressions) {
if (e is VariableUse) {
@ -1158,7 +1116,7 @@ class MiniJsParser {
Expression parseArrowFunctionBody(List<Parameter> 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<Statement> 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<Statement> 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<SwitchClause> 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);
}

View file

@ -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<T>(NodeVisitor<T> 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<T>(NodeVisitor<T> visitor);
@override
int get precedenceLevel => PRIMARY;
Precedence get precedenceLevel => Precedence.primary;
@override
void visitChildren<T>(NodeVisitor<T> 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<R, A>(NodeVisitor1<R, A> 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<T>(NodeVisitor<T> 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<T>(NodeVisitor<T> visitor) => visitor.visitAwait(this);

View file

@ -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,
}

View file

@ -291,7 +291,7 @@ class Printer implements NodeVisitor<void> {
endNode(node);
}
void visitCommaSeparated(List<Expression> nodes, int hasRequiredType,
void visitCommaSeparated(List<Expression> 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<void> {
@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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
} 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<void> {
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> {
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<void> {
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<void> {
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> {
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<void> {
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<void> {
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<void> {
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<void> {
spaceOut();
}
}
visitNestedExpression(node, ASSIGNMENT,
visitNestedExpression(node, Precedence.assignment,
newInForInit: inForInit, newAtStatementBegin: false);
lastWasBig = thisIsBig;
}
@ -789,7 +790,7 @@ class Printer implements NodeVisitor<void> {
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<void> {
}
// 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<void> {
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<void> {
@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<void> {
Expression left = binary.left;
Expression right = binary.right;
String op = binary.op;
int leftPrecedenceRequirement;
int rightPrecedenceRequirement;
Precedence leftPrecedenceRequirement;
Precedence rightPrecedenceRequirement;
bool leftSpace = true; // left<HERE>op 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<void> {
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<void> {
// 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<void> {
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<void> {
@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<void> {
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<void> {
localNamer.enterScope(vars);
final List<Parameter> 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<void> {
// 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<void> {
@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<void> {
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<void> {
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<void> {
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<void> {
// 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(']');
}

View file

@ -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<Expression, DeferredExpression> 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 {

View file

@ -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;

View file

@ -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<Type> get typeArguments {
if (_typeArgumentCount == 0) return const <Type>[];
@ -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.

View file

@ -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;

View file

@ -285,6 +285,12 @@ const Map<String, LibraryInfo> 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: '',

View file

@ -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"
},

View file

@ -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"