| // 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, |
| {bool enableAsserts, |
| Map<String, String> environment: const {}, |
| bool supportReevaluationForTesting: false}) |
| : _supportReevaluationForTesting = supportReevaluationForTesting, |
| super(const Dart2jsConstantsBackend(), environment, typeEnvironment, |
| enableAsserts, 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 |
| accept(ir.TreeVisitor 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)'; |
| } |