blob: ac1418a18e647596e0527b6558aafbd957f362b4 [file] [log] [blame] [edit]
// Copyright (c) 2023, 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';
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/target/targets.dart'
show ConstantsBackend, DartLibrarySupport, Target;
import 'package:kernel/type_environment.dart';
import 'package:front_end/src/api_prototype/constant_evaluator.dart'
show ConstantEvaluator, ErrorReporter, EvaluationMode, SimpleErrorReporter;
import '../target_os.dart';
import 'pragma.dart';
/// Evaluates uses of static fields and getters using VM-specific and
/// platform-specific knowledge.
///
/// The provided [TargetOS], when non-null, is used when evaluating static
/// fields and getters annotated with "vm:platform-const".
///
/// To avoid restricting getters annotated with "vm:platform-const" to be just
/// a single return statement whose body is evaluated, as well as handling
/// immediately-invoked closures wrapping complex initializing code in field
/// initializers, we enable constant evaluation of functions.
class VMConstantEvaluator extends ConstantEvaluator {
final TargetOS? _targetOS;
final Map<String, Constant> _constantFields = {};
final Class? _platformClass;
final PragmaAnnotationParser _pragmaParser;
VMConstantEvaluator(
DartLibrarySupport dartLibrarySupport,
ConstantsBackend backend,
Component component,
Map<String, String>? environmentDefines,
TypeEnvironment typeEnvironment,
ErrorReporter errorReporter,
this._targetOS,
this._pragmaParser,
{bool enableTripleShift = false,
bool enableAsserts = true,
bool errorOnUnevaluatedConstant = false})
: _platformClass = typeEnvironment.coreTypes.platformClass,
super(dartLibrarySupport, backend, component, environmentDefines,
typeEnvironment, errorReporter,
enableTripleShift: enableTripleShift,
// We use evaluation of const functions for getters and immediately
// invoked closures in field initializers.
enableConstFunctions: true,
enableAsserts: enableAsserts,
errorOnUnevaluatedConstant: errorOnUnevaluatedConstant,
evaluationMode: EvaluationMode.strong) {
// Only add Platform fields if the Platform class is part of the component
// being evaluated.
if (_targetOS != null && _platformClass != null) {
_constantFields['operatingSystem'] = StringConstant(_targetOS.name);
_constantFields['pathSeparator'] =
StringConstant(_targetOS.pathSeparator);
}
}
static VMConstantEvaluator create(
Target target, Component component, TargetOS? targetOS,
{bool evaluateAnnotations = true,
bool enableTripleShift = false,
bool enableConstructorTearOff = false,
bool enableAsserts = true,
bool errorOnUnevaluatedConstant = false,
ErrorReporter? errorReporter,
Map<String, String>? environmentDefines,
CoreTypes? coreTypes,
ClassHierarchy? hierarchy}) {
coreTypes ??= CoreTypes(component);
hierarchy ??= ClassHierarchy(component, coreTypes);
final typeEnvironment = TypeEnvironment(coreTypes, hierarchy);
// Use the empty environment if unevaluated constants are not supported,
// as passing null for environmentDefines in this case is an error.
environmentDefines ??=
target.constantsBackend.supportsUnevaluatedConstants ? null : {};
return VMConstantEvaluator(
target.dartLibrarySupport,
target.constantsBackend,
component,
environmentDefines,
typeEnvironment,
errorReporter ?? const SimpleErrorReporter(),
targetOS,
ConstantPragmaAnnotationParser(coreTypes, target),
enableTripleShift: enableTripleShift,
enableAsserts: enableAsserts,
errorOnUnevaluatedConstant: errorOnUnevaluatedConstant);
}
bool get _hasTargetOS => _targetOS != null;
bool _isPlatformConst(Member member) => _pragmaParser
.parsedPragmas<ParsedPlatformConstPragma>(member.annotations)
.isNotEmpty;
bool shouldEvaluateMember(Member node) =>
_hasTargetOS && _isPlatformConst(node);
@override
Constant visitStaticGet(StaticGet node) {
assert(_hasTargetOS);
final target = node.target;
// This visitor can be called recursively while evaluating an abstraction
// over the Platform getters and fields, so check that the visited node has
// an appropriately annotated target.
if (!_isPlatformConst(target)) return super.visitStaticGet(node);
visitedLibraries.add(target.enclosingLibrary);
if (target is Field) {
// If this is a special Platform field that has a pre-calculated value
// for the given operating system, just use the canonicalized value.
if (target.enclosingClass == _platformClass) {
final constant = _constantFields[target.name.text];
if (constant != null) {
return canonicalize(constant);
}
}
// The base class only evaluates const fields, so the VM constant
// evaluator must manually request the initializer be evaluated.
// instead of just calling super.visitStaticGet(target).
return withNewEnvironment(
() => evaluateExpressionInContext(target, target.initializer!));
}
// The base class already handles constant evaluation of a getter.
return super.visitStaticGet(node);
}
}