blob: 958c1ce70fe845025787da609d5689cb9adc5682 [file] [log] [blame]
// Copyright (c) 2019, 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:kernel/ast.dart' as ir;
import 'package:kernel/type_environment.dart' as ir;
import 'package:front_end/src/api_prototype/constant_evaluator.dart' as ir;
import 'package:front_end/src/api_unstable/dart2js.dart' as ir;
import '../kernel/dart2js_target.dart';
typedef ReportErrorFunction = void Function(
ir.LocatedMessage message, List<ir.LocatedMessage> context);
class Dart2jsConstantEvaluator extends ir.ConstantEvaluator {
final bool _supportReevaluationForTesting;
bool requiresConstant;
Dart2jsConstantEvaluator(
ir.TypeEnvironment typeEnvironment, ReportErrorFunction reportError,
{Map<String, String> environment: const {},
bool supportReevaluationForTesting: false})
: _supportReevaluationForTesting = supportReevaluationForTesting,
super(
const Dart2jsConstantsBackend(supportsUnevaluatedConstants: false),
environment,
typeEnvironment,
new ErrorReporter(reportError));
@override
ErrorReporter get errorReporter => super.errorReporter;
@override
ir.Constant evaluate(ir.Expression node, {bool requireConstant: true}) {
errorReporter.requiresConstant = requireConstant;
if (node is ir.ConstantExpression) {
ir.Constant constant = node.constant;
if (constant is ir.UnevaluatedConstant) {
ir.Constant result = super.evaluate(constant.expression);
assert(
result is ir.UnevaluatedConstant ||
!result.accept(const UnevaluatedConstantFinder()),
"Invalid constant result $result from ${constant.expression}.");
if (!_supportReevaluationForTesting) {
node.constant = result;
}
return result;
}
return constant;
}
if (requireConstant) {
// TODO(johnniwinther): Handle reporting of compile-time constant
// evaluation errors.
return super.evaluate(node);
} else {
try {
return super.evaluate(node);
} catch (e) {
return null;
}
}
}
}
class ErrorReporter implements ir.ErrorReporter {
final ReportErrorFunction _reportError;
bool requiresConstant;
ErrorReporter(this._reportError);
@override
void reportInvalidExpression(ir.InvalidExpression node) {
// Ignore.
}
@override
void report(ir.LocatedMessage message, List<ir.LocatedMessage> context) {
if (requiresConstant) {
_reportError(message, context);
}
}
}
/// [ir.Constant] visitor that returns `true` if the visitor constant contains
/// an [ir.UnevaluatedConstant].
class UnevaluatedConstantFinder extends ir.ConstantVisitor<bool> {
const UnevaluatedConstantFinder();
@override
bool defaultConstant(ir.Constant node) => false;
@override
bool visitUnevaluatedConstant(ir.UnevaluatedConstant node) => true;
@override
bool visitPartialInstantiationConstant(ir.PartialInstantiationConstant node) {
return node.tearOffConstant.accept(this);
}
@override
bool visitInstanceConstant(ir.InstanceConstant node) {
for (ir.Constant value in node.fieldValues.values) {
if (value.accept(this)) {
return true;
}
}
return false;
}
@override
bool visitSetConstant(ir.SetConstant node) {
for (ir.Constant value in node.entries) {
if (value.accept(this)) {
return true;
}
}
return false;
}
@override
bool visitListConstant(ir.ListConstant node) {
for (ir.Constant value in node.entries) {
if (value.accept(this)) {
return true;
}
}
return false;
}
@override
bool visitMapConstant(ir.MapConstant node) {
for (ir.ConstantMapEntry entry in node.entries) {
if (entry.key.accept(this)) {
return true;
}
if (entry.value.accept(this)) {
return true;
}
}
return false;
}
}
/// Class to represent a reference to a constant in allocation nodes.
///
/// This class is needed in order to support serialization of references to
/// constant nodes. Since the constant nodes are not [ir.TreeNode]s we can only
/// serialize the constants as values which would bypass by the canonicalization
/// performed by the CFE. This class extends only as a trick to easily pass
/// it through serialization.
///
/// By adding a reference to the constant expression in which the constant
/// occurred, we can serialize references to constants in two steps: a reference
/// to the constant expression followed by an index of the referred constant
/// in the traversal order of the constant held by the constant expression.
///
/// This is used for list, map, and set literals.
class ConstantReference extends ir.TreeNode {
final ir.ConstantExpression expression;
final ir.Constant constant;
ConstantReference(this.expression, this.constant);
@override
void visitChildren(ir.Visitor v) {
throw new UnsupportedError("ConstantReference.visitChildren");
}
@override
R accept<R>(ir.TreeVisitor<R> v) {
throw new UnsupportedError("ConstantReference.accept");
}
@override
transformChildren(ir.Transformer v) {
throw new UnsupportedError("ConstantReference.transformChildren");
}
@override
int get hashCode => 13 * constant.hashCode;
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ConstantReference && constant == other.constant;
}
@override
String toString() => 'ConstantReference(constant=$constant)';
}