Skip to content

Commit 1babc13

Browse files
alexmarkovcommit-bot@chromium.org
authored andcommitted
[vm/kernel/bytecode] Report compile-time errors from constant evaluation while generating bytecode
Change-Id: I931622f5e442a439f5bfa241aade686b1bc91afd Reviewed-on: https://dart-review.googlesource.com/68222 Commit-Queue: Alexander Markov <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent 8114ecb commit 1babc13

File tree

11 files changed

+282
-216
lines changed

11 files changed

+282
-216
lines changed

pkg/kernel/lib/transformations/constants.dart

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -343,19 +343,11 @@ class ConstantsTransformer extends Transformer {
343343

344344
tryEvaluateWithContext(TreeNode treeContext, Expression node) {
345345
if (treeContext == node) {
346-
try {
347-
return constantEvaluator.evaluate(node);
348-
} on _AbortCurrentEvaluation catch (_) {
349-
return null;
350-
}
346+
return constantEvaluator.evaluate(node);
351347
}
352348

353349
return constantEvaluator.runInsideContext(treeContext, () {
354-
try {
355-
return constantEvaluator.evaluate(node);
356-
} on _AbortCurrentEvaluation catch (_) {
357-
return null;
358-
}
350+
return constantEvaluator.evaluate(node);
359351
});
360352
}
361353
}
@@ -387,7 +379,18 @@ class ConstantEvaluator extends RecursiveVisitor {
387379
nodeCache = <Node, Constant>{};
388380

389381
/// Evaluates [node] and possibly cache the evaluation result.
382+
/// Returns `null` if expression can't be evaluated.
390383
Constant evaluate(Expression node) {
384+
try {
385+
return _evaluateSubexpression(node);
386+
} on _AbortCurrentEvaluation catch (_) {
387+
return null;
388+
}
389+
}
390+
391+
/// Evaluates [node] and possibly cache the evaluation result.
392+
/// @throws _AbortCurrentEvaluation if expression can't be evaluated.
393+
Constant _evaluateSubexpression(Expression node) {
391394
if (node == null) return nullConstant;
392395
if (env.isEmpty) {
393396
// We only try to evaluate the same [node] *once* within an empty
@@ -572,27 +575,30 @@ class ConstantEvaluator extends RecursiveVisitor {
572575
function.positionalParameters[i];
573576
final Constant value = (i < positionalArguments.length)
574577
? positionalArguments[i]
575-
: evaluate(parameter.initializer);
578+
: _evaluateSubexpression(parameter.initializer);
576579
env.addVariableValue(parameter, value);
577580
}
578581
for (final VariableDeclaration parameter in function.namedParameters) {
579-
final Constant value =
580-
namedArguments[parameter.name] ?? evaluate(parameter.initializer);
582+
final Constant value = namedArguments[parameter.name] ??
583+
_evaluateSubexpression(parameter.initializer);
581584
env.addVariableValue(parameter, value);
582585
}
583586

584587
// Step 2) Run all initializers (including super calls) with environment setup.
585588
for (final Field field in klass.fields) {
586589
if (!field.isStatic) {
587-
instanceBuilder.setFieldValue(field, evaluate(field.initializer));
590+
instanceBuilder.setFieldValue(
591+
field, _evaluateSubexpression(field.initializer));
588592
}
589593
}
590594
for (final Initializer init in constructor.initializers) {
591595
if (init is FieldInitializer) {
592-
instanceBuilder.setFieldValue(init.field, evaluate(init.value));
596+
instanceBuilder.setFieldValue(
597+
init.field, _evaluateSubexpression(init.value));
593598
} else if (init is LocalInitializer) {
594599
final VariableDeclaration variable = init.variable;
595-
env.addVariableValue(variable, evaluate(variable.initializer));
600+
env.addVariableValue(
601+
variable, _evaluateSubexpression(variable.initializer));
596602
} else if (init is SuperInitializer) {
597603
handleConstructorInvocation(
598604
init.target,
@@ -653,7 +659,7 @@ class ConstantEvaluator extends RecursiveVisitor {
653659
// We have no support for generic method invocation atm.
654660
assert(node.arguments.named.isEmpty);
655661

656-
final Constant receiver = evaluate(node.receiver);
662+
final Constant receiver = _evaluateSubexpression(node.receiver);
657663
final List<Constant> arguments =
658664
evaluatePositionalArguments(node.arguments);
659665

@@ -810,13 +816,13 @@ class ConstantEvaluator extends RecursiveVisitor {
810816
}
811817

812818
visitLogicalExpression(LogicalExpression node) {
813-
final Constant left = evaluate(node.left);
819+
final Constant left = _evaluateSubexpression(node.left);
814820
switch (node.operator) {
815821
case '||':
816822
if (left is BoolConstant) {
817823
if (left.value) return trueConstant;
818824

819-
final Constant right = evaluate(node.right);
825+
final Constant right = _evaluateSubexpression(node.right);
820826
if (right is BoolConstant) {
821827
return right;
822828
}
@@ -836,7 +842,7 @@ class ConstantEvaluator extends RecursiveVisitor {
836842
if (left is BoolConstant) {
837843
if (!left.value) return falseConstant;
838844

839-
final Constant right = evaluate(node.right);
845+
final Constant right = _evaluateSubexpression(node.right);
840846
if (right is BoolConstant) {
841847
return right;
842848
}
@@ -853,7 +859,9 @@ class ConstantEvaluator extends RecursiveVisitor {
853859
contextChain, node, left, '${node.operator}');
854860
throw const _AbortCurrentEvaluation();
855861
case '??':
856-
return (left is! NullConstant) ? left : evaluate(node.right);
862+
return (left is! NullConstant)
863+
? left
864+
: _evaluateSubexpression(node.right);
857865
default:
858866
errorReporter.invalidMethodInvocation(
859867
contextChain, node, left, '${node.operator}');
@@ -862,11 +870,11 @@ class ConstantEvaluator extends RecursiveVisitor {
862870
}
863871

864872
visitConditionalExpression(ConditionalExpression node) {
865-
final Constant constant = evaluate(node.condition);
873+
final Constant constant = _evaluateSubexpression(node.condition);
866874
if (constant == trueConstant) {
867-
return evaluate(node.then);
875+
return _evaluateSubexpression(node.then);
868876
} else if (constant == falseConstant) {
869-
return evaluate(node.otherwise);
877+
return _evaluateSubexpression(node.otherwise);
870878
} else {
871879
errorReporter.invalidDartType(
872880
contextChain, node, constant, typeEnvironment.boolType);
@@ -885,7 +893,7 @@ class ConstantEvaluator extends RecursiveVisitor {
885893
throw 'Could not evaluate field get ${node.name} on incomplete instance';
886894
}
887895

888-
final Constant receiver = evaluate(node.receiver);
896+
final Constant receiver = _evaluateSubexpression(node.receiver);
889897
if (receiver is StringConstant && node.name.name == 'length') {
890898
return canonicalize(new IntConstant(receiver.value.length));
891899
} else if (receiver is InstanceConstant) {
@@ -899,7 +907,8 @@ class ConstantEvaluator extends RecursiveVisitor {
899907
}
900908

901909
visitLet(Let node) {
902-
env.addVariableValue(node.variable, evaluate(node.variable.initializer));
910+
env.addVariableValue(
911+
node.variable, _evaluateSubexpression(node.variable.initializer));
903912
return node.body.accept(this);
904913
}
905914

@@ -920,7 +929,7 @@ class ConstantEvaluator extends RecursiveVisitor {
920929
return constant;
921930
}
922931
if (variable.isConst) {
923-
return evaluate(variable.initializer);
932+
return _evaluateSubexpression(variable.initializer);
924933
}
925934
throw new Exception('The front-end should ensure we do not encounter a '
926935
'variable get of a non-const variable.');
@@ -931,7 +940,7 @@ class ConstantEvaluator extends RecursiveVisitor {
931940
final Member target = node.target;
932941
if (target is Field && target.isConst) {
933942
return runInsideContext(target, () {
934-
return evaluate(target.initializer);
943+
return _evaluateSubexpression(target.initializer);
935944
});
936945
} else if (target is Procedure) {
937946
if (target.kind == ProcedureKind.Method) {
@@ -1021,7 +1030,7 @@ class ConstantEvaluator extends RecursiveVisitor {
10211030
}
10221031

10231032
visitInstantiation(Instantiation node) {
1024-
final Constant constant = evaluate(node.expression);
1033+
final Constant constant = _evaluateSubexpression(node.expression);
10251034
if (constant is TearOffConstant) {
10261035
if (node.typeArguments.length ==
10271036
constant.procedure.function.typeParameters.length) {

pkg/kernel/lib/vm/constants_native_effects.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class VmConstantsBackend implements ConstantsBackend {
5959
} else {
6060
value = new bool.fromEnvironment(name, defaultValue: defaultValue);
6161
}
62-
return new BoolConstant(value);
62+
return value != null ? new BoolConstant(value) : new NullConstant();
6363
case 'Integer_fromEnvironment':
6464
final String name = (positionalArguments[0] as StringConstant).value;
6565
final Constant constant = namedArguments['defaultValue'];

pkg/vm/lib/bytecode/assembler.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ library vm.bytecode.assembler;
66

77
import 'dart:typed_data';
88

9-
import 'package:vm/bytecode/dbc.dart';
10-
import 'package:vm/bytecode/exceptions.dart' show ExceptionsTable;
9+
import 'dbc.dart';
10+
import 'exceptions.dart' show ExceptionsTable;
1111

1212
class Label {
1313
List<int> _jumps = <int>[];

pkg/vm/lib/bytecode/disassembler.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ library vm.bytecode.disassembler;
66

77
import 'dart:typed_data';
88

9-
import 'package:vm/bytecode/dbc.dart';
10-
import 'package:vm/bytecode/exceptions.dart';
9+
import 'dbc.dart';
10+
import 'exceptions.dart';
1111

1212
class _Instruction {
1313
final Opcode opcode;

pkg/vm/lib/bytecode/gen_bytecode.dart

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,23 @@ import 'package:kernel/core_types.dart' show CoreTypes;
1010
import 'package:kernel/external_name.dart' show getExternalName;
1111
import 'package:kernel/library_index.dart' show LibraryIndex;
1212
import 'package:kernel/transformations/constants.dart'
13-
show ConstantEvaluator, ConstantsBackend, EvaluationEnvironment;
13+
show
14+
ConstantEvaluator,
15+
ConstantsBackend,
16+
EvaluationEnvironment,
17+
ErrorReporter;
1418
import 'package:kernel/type_algebra.dart'
1519
show Substitution, containsTypeVariable;
1620
import 'package:kernel/type_environment.dart' show TypeEnvironment;
1721
import 'package:kernel/vm/constants_native_effects.dart'
1822
show VmConstantsBackend;
19-
import 'package:vm/bytecode/assembler.dart';
20-
import 'package:vm/bytecode/constant_pool.dart';
21-
import 'package:vm/bytecode/dbc.dart';
22-
import 'package:vm/bytecode/exceptions.dart';
23-
import 'package:vm/bytecode/local_vars.dart' show LocalVariables;
24-
import 'package:vm/metadata/bytecode.dart';
23+
import 'assembler.dart';
24+
import 'constant_pool.dart';
25+
import 'dbc.dart';
26+
import 'exceptions.dart';
27+
import 'local_vars.dart' show LocalVariables;
28+
import '../constants_error_reporter.dart' show ForwardConstantEvaluationErrors;
29+
import '../metadata/bytecode.dart';
2530

2631
/// Flag to toggle generation of bytecode in kernel files.
2732
const bool isKernelBytecodeEnabled = false;
@@ -32,16 +37,20 @@ const bool isKernelBytecodeEnabledForPlatform = isKernelBytecodeEnabled;
3237
void generateBytecode(Component component,
3338
{bool strongMode: true,
3439
bool dropAST: false,
35-
bool omitSourcePositions: false}) {
40+
bool omitSourcePositions: false,
41+
Map<String, String> environmentDefines,
42+
ErrorReporter errorReporter}) {
3643
final coreTypes = new CoreTypes(component);
3744
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
3845
final hierarchy = new ClassHierarchy(component,
3946
onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
4047
final typeEnvironment =
4148
new TypeEnvironment(coreTypes, hierarchy, strongMode: strongMode);
42-
final constantsBackend = new VmConstantsBackend(null, coreTypes);
49+
final constantsBackend =
50+
new VmConstantsBackend(environmentDefines, coreTypes);
51+
final errorReporter = new ForwardConstantEvaluationErrors(typeEnvironment);
4352
new BytecodeGenerator(component, coreTypes, hierarchy, typeEnvironment,
44-
constantsBackend, strongMode, omitSourcePositions)
53+
constantsBackend, strongMode, omitSourcePositions, errorReporter)
4554
.visitComponent(component);
4655
if (dropAST) {
4756
new DropAST().visitComponent(component);
@@ -56,6 +65,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
5665
final ConstantsBackend constantsBackend;
5766
final bool strongMode;
5867
final bool omitSourcePositions;
68+
final ErrorReporter errorReporter;
5969
final BytecodeMetadataRepository metadata = new BytecodeMetadataRepository();
6070

6171
Class enclosingClass;
@@ -78,6 +88,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
7888
ConstantEmitter constantEmitter;
7989
BytecodeAssembler asm;
8090
List<BytecodeAssembler> savedAssemblers;
91+
bool hasErrors;
8192

8293
BytecodeGenerator(
8394
this.component,
@@ -86,7 +97,8 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
8697
this.typeEnvironment,
8798
this.constantsBackend,
8899
this.strongMode,
89-
this.omitSourcePositions) {
100+
this.omitSourcePositions,
101+
this.errorReporter) {
90102
component.addMetadataRepository(metadata);
91103
}
92104

@@ -288,8 +300,19 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
288300
asm.emitPushConstant(cpIndex);
289301
}
290302

291-
void _genPushConstExpr(Expression expr) {
303+
Constant _evaluateConstantExpression(Expression expr) {
292304
final constant = constantEvaluator.evaluate(expr);
305+
if (constant == null) {
306+
// Compile-time error is already reported. Proceed with compilation
307+
// in order to report as many errors as possible.
308+
hasErrors = true;
309+
return new NullConstant();
310+
}
311+
return constant;
312+
}
313+
314+
void _genPushConstExpr(Expression expr) {
315+
final constant = _evaluateConstantExpression(expr);
293316
asm.emitPushConstant(constant.accept(constantEmitter));
294317
}
295318

@@ -526,7 +549,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
526549
if (param.initializer == null) {
527550
return cp.add(const ConstantNull());
528551
}
529-
final constant = constantEvaluator.evaluate(param.initializer);
552+
final constant = _evaluateConstantExpression(param.initializer);
530553
return constant.accept(constantEmitter);
531554
}
532555

@@ -567,6 +590,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
567590
enclosingMember = node;
568591
enclosingFunction = node.function;
569592
parentFunction = null;
593+
hasErrors = false;
570594
if (node.isInstanceMember ||
571595
node is Constructor ||
572596
(node is Procedure && node.isFactory)) {
@@ -595,7 +619,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
595619
locals = new LocalVariables(node);
596620
// TODO(alexmarkov): improve caching in ConstantEvaluator and reuse it
597621
constantEvaluator = new ConstantEvaluator(constantsBackend, typeEnvironment,
598-
coreTypes, strongMode, /* enableAsserts = */ true)
622+
coreTypes, strongMode, /* enableAsserts = */ true, errorReporter)
599623
..env = new EvaluationEnvironment();
600624
labeledStatements = <LabeledStatement, Label>{};
601625
switchCases = <SwitchCase, Label>{};
@@ -642,8 +666,10 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
642666
}
643667

644668
void end(Member node) {
645-
metadata.mapping[node] =
646-
new BytecodeMetadata(cp, asm.bytecode, asm.exceptionsTable, closures);
669+
if (!hasErrors) {
670+
metadata.mapping[node] =
671+
new BytecodeMetadata(cp, asm.bytecode, asm.exceptionsTable, closures);
672+
}
647673

648674
enclosingClass = null;
649675
enclosingMember = null;
@@ -665,6 +691,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
665691
constantEmitter = null;
666692
asm = null;
667693
savedAssemblers = null;
694+
hasErrors = false;
668695
}
669696

670697
void _genPrologue(Node node, FunctionNode function) {
@@ -2269,7 +2296,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
22692296
@override
22702297
visitVariableDeclaration(VariableDeclaration node) {
22712298
if (node.isConst) {
2272-
final Constant constant = constantEvaluator.evaluate(node.initializer);
2299+
final Constant constant = _evaluateConstantExpression(node.initializer);
22732300
constantEvaluator.env.addVariableValue(node, constant);
22742301
} else {
22752302
final bool isCaptured = locals.isCaptured(node);

pkg/vm/lib/bytecode/local_vars.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'dart:math' show max;
99
import 'package:kernel/ast.dart';
1010
import 'package:kernel/transformations/continuation.dart'
1111
show ContinuationVariables;
12-
import 'package:vm/bytecode/dbc.dart';
12+
import 'dbc.dart';
1313

1414
class LocalVariables {
1515
final Map<TreeNode, Scope> _scopes = <TreeNode, Scope>{};

0 commit comments

Comments
 (0)