mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 18:16:04 +00:00
[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:
parent
62e5a482e4
commit
9fa1c16029
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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}');
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
19
pkg/compiler/lib/src/kernel/invocation_mirror.dart
Normal file
19
pkg/compiler/lib/src/kernel/invocation_mirror.dart
Normal 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);
|
||||
}
|
|
@ -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;
|
|
@ -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,
|
||||
|
|
|
@ -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,13 +2317,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
|||
js.Name name = _namer.instanceFieldPropertyName(element);
|
||||
use(node.receiver);
|
||||
js.Expression fieldReference = js.PropertyAccess(pop(), name);
|
||||
if (node.isPreOp) {
|
||||
switch (node.opKind) {
|
||||
case ReadModifyWriteKind.prefix:
|
||||
push(js.Prefix(node.jsOp, fieldReference)
|
||||
.withSourceInformation(node.sourceInformation));
|
||||
} else if (node.isPostOp) {
|
||||
case ReadModifyWriteKind.postfix:
|
||||
push(js.Postfix(node.jsOp, fieldReference)
|
||||
.withSourceInformation(node.sourceInformation));
|
||||
} else {
|
||||
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,12 +2989,14 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
|||
js.Block oldContainer = currentContainer;
|
||||
js.Block body = currentContainer = js.Block.empty();
|
||||
final sourceInformation = node.sourceInformation;
|
||||
if (node.isArgumentTypeCheck) {
|
||||
switch (node.kind) {
|
||||
case PrimitiveCheckKind.argumentType:
|
||||
use(node.checkedInput);
|
||||
_pushCallStatic(_commonElements.throwIllegalArgumentException, [pop()],
|
||||
node.sourceInformation);
|
||||
pushStatement(js.Return(pop()).withSourceInformation(sourceInformation));
|
||||
} else if (node.isReceiverTypeCheck) {
|
||||
pushStatement(
|
||||
js.Return(pop()).withSourceInformation(sourceInformation));
|
||||
case PrimitiveCheckKind.receiverType:
|
||||
use(node.checkedInput);
|
||||
js.Name methodName =
|
||||
_namer.invocationName(node.receiverTypeCheckSelector!);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -312,12 +312,13 @@ class HInstructionStringifier implements HVisitor<String> {
|
|||
String? fieldName = node.element.name;
|
||||
String receiverId = temporaryId(node.receiver);
|
||||
String op = node.jsOp;
|
||||
if (node.isAssignOp) {
|
||||
switch (node.opKind) {
|
||||
case ReadModifyWriteKind.assign:
|
||||
String valueId = temporaryId(node.value);
|
||||
return 'ReadModifyWrite: $receiverId.$fieldName $op= $valueId';
|
||||
} else if (node.isPreOp) {
|
||||
case ReadModifyWriteKind.prefix:
|
||||
return 'ReadModifyWrite: $op$receiverId.$fieldName';
|
||||
} else {
|
||||
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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -7,15 +7,18 @@ 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) {
|
||||
|
@ -28,7 +31,9 @@ test(check) {
|
|||
return sum;
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -41,7 +46,9 @@ test(value) {
|
|||
return sum;
|
||||
}
|
||||
""",
|
||||
ABOVE_ZERO,
|
||||
_Result.aboveZero
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(check) {
|
||||
|
@ -56,35 +63,45 @@ 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) {
|
||||
|
@ -93,7 +110,9 @@ test(value) {
|
|||
return a[value];
|
||||
}
|
||||
""",
|
||||
KEPT,
|
||||
_Result.kept
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -102,7 +121,9 @@ test(value) {
|
|||
return a[1023 & value];
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -111,14 +132,18 @@ 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) {
|
||||
|
@ -128,7 +153,9 @@ test(value, call) {
|
|||
return a[value] + call() + a[value];
|
||||
}
|
||||
""",
|
||||
ONE_ZERO_CHECK,
|
||||
_Result.oneZeroCheck
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -137,7 +164,9 @@ test(value) {
|
|||
return a[1] + a[0];
|
||||
}
|
||||
""",
|
||||
ONE_CHECK,
|
||||
_Result.oneCheck
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(n) {
|
||||
|
@ -150,7 +179,9 @@ test(n) {
|
|||
return sum;
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(n) {
|
||||
|
@ -163,7 +194,9 @@ test(n) {
|
|||
return sum;
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(dynamic value) {
|
||||
|
@ -176,7 +209,9 @@ test(dynamic value) {
|
|||
return a[value];
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -191,7 +226,9 @@ test(value) {
|
|||
}
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -204,7 +241,9 @@ test(value) {
|
|||
return a[value];
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -218,7 +257,9 @@ test(value) {
|
|||
return sum;
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -232,7 +273,9 @@ test(value) {
|
|||
return sum;
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -246,7 +289,9 @@ test(value) {
|
|||
return sum;
|
||||
}
|
||||
""",
|
||||
REMOVED,
|
||||
_Result.removed
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -260,7 +305,9 @@ test(value) {
|
|||
return sum;
|
||||
}
|
||||
""",
|
||||
BELOW_ZERO_CHECK,
|
||||
_Result.belowZeroCheck
|
||||
),
|
||||
(
|
||||
"""
|
||||
@pragma('dart2js:assumeDynamic')
|
||||
test(value) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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=[{}],
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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(']');
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
|
@ -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: '',
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in a new issue