| // 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:front_end/src/api_prototype/constant_evaluator.dart' as ir; |
| import 'package:front_end/src/api_unstable/dart2js.dart' as ir; |
| import 'package:kernel/ast.dart' as ir; |
| import 'package:kernel/src/printer.dart' as ir; |
| import 'package:kernel/type_environment.dart' as ir; |
| |
| import '../environment.dart'; |
| import '../kernel/dart2js_target.dart'; |
| |
| typedef ReportErrorFunction = void Function( |
| ir.LocatedMessage message, List<ir.LocatedMessage>? context); |
| |
| class Dart2jsConstantEvaluator extends ir.ConstantEvaluator { |
| final bool _supportReevaluationForTesting; |
| |
| Dart2jsConstantEvaluator(ir.Component component, |
| ir.TypeEnvironment typeEnvironment, ReportErrorFunction reportError, |
| {Environment? environment, |
| bool supportReevaluationForTesting = false, |
| required ir.EvaluationMode evaluationMode}) |
| : _supportReevaluationForTesting = supportReevaluationForTesting, |
| assert((evaluationMode as dynamic) != null), |
| super( |
| const Dart2jsDartLibrarySupport(), |
| const Dart2jsConstantsBackend(supportsUnevaluatedConstants: false), |
| component, |
| environment?.definitions ?? const {}, |
| typeEnvironment, |
| ErrorReporter(reportError), |
| enableTripleShift: true, |
| evaluationMode: evaluationMode); |
| |
| @override |
| ErrorReporter get errorReporter => super.errorReporter as ErrorReporter; |
| // TODO(48820): ^Store another reference to the error reporter with the |
| // refined type and use that. |
| |
| // We can't override [ir.ConstantEvaluator.evaluate] and have a nullable |
| // return type. |
| // TODO(48820): Consider using composition. We will need to ensure that |
| // [Dart2jsConstantEvaluator] is not referenced via [ir.ConstantEvaluator]. |
| @override |
| ir.Constant evaluate( |
| ir.StaticTypeContext staticTypeContext, ir.Expression node, |
| {ir.TreeNode? contextNode}) { |
| return evaluateOrNull(staticTypeContext, node, contextNode: contextNode)!; |
| } |
| |
| /// Evaluates [node] to a constant in the given [staticTypeContext]. |
| /// |
| /// If [requireConstant] is `true`, an error is reported if [node] is not |
| /// a valid constant. Otherwise, `null` if [node] is not a valid constant. |
| ir.Constant? evaluateOrNull( |
| ir.StaticTypeContext staticTypeContext, ir.Expression node, |
| {ir.TreeNode? contextNode, 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( |
| staticTypeContext, constant.expression, |
| contextNode: contextNode); |
| 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) { |
| return super.evaluate(staticTypeContext, node, contextNode: contextNode); |
| } else { |
| ir.Constant constant = |
| super.evaluate(staticTypeContext, node, contextNode: contextNode); |
| if (constant is ir.UnevaluatedConstant && |
| constant.expression is ir.InvalidExpression) { |
| return null; |
| } |
| return constant; |
| } |
| } |
| } |
| |
| class ErrorReporter implements ir.ErrorReporter { |
| final ReportErrorFunction _reportError; |
| late bool requiresConstant; |
| |
| ErrorReporter(this._reportError); |
| |
| @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 visitInstantiationConstant(ir.InstantiationConstant 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 UnsupportedError("ConstantReference.visitChildren"); |
| } |
| |
| @override |
| R accept<R>(ir.TreeVisitor<R> v) { |
| throw UnsupportedError("ConstantReference.accept"); |
| } |
| |
| @override |
| R accept1<R, A>(ir.TreeVisitor1<R, A> v, A arg) { |
| throw UnsupportedError("ConstantReference.accept"); |
| } |
| |
| @override |
| transformChildren(ir.Transformer v) { |
| throw UnsupportedError("ConstantReference.transformChildren"); |
| } |
| |
| @override |
| transformOrRemoveChildren(ir.RemovingTransformer v) { |
| throw UnsupportedError("ConstantReference.transformOrRemoveChildren"); |
| } |
| |
| @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(${toStringInternal()})'; |
| |
| @override |
| String toStringInternal() => 'constant=${constant.toStringInternal()}'; |
| |
| @override |
| String toText(ir.AstTextStrategy strategy) => constant.toText(strategy); |
| |
| @override |
| void toTextInternal(ir.AstPrinter printer) => |
| constant.toTextInternal(printer); |
| } |