bc1c71fd9b
Closes #47108 TEST=existing Change-Id: I590939161cde1316eede7d828dff5fe6ac2790fd Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212577 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
122 lines
4.0 KiB
Dart
122 lines
4.0 KiB
Dart
// Copyright (c) 2018, 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:collection/collection.dart';
|
|
import 'package:kernel/kernel.dart';
|
|
import 'package:kernel/target/targets.dart';
|
|
|
|
/// Implements constant evaluation for dev compiler:
|
|
///
|
|
/// [isConstant] determines if an expression is constant.
|
|
/// [evaluate] computes the value of a constant expression, if available.
|
|
class DevCompilerConstants {
|
|
DevCompilerConstants();
|
|
|
|
/// Determines if an expression is constant.
|
|
bool isConstant(Expression e) => e is ConstantExpression;
|
|
|
|
/// Evaluates [e] to find its constant value, returning `null` if evaluation
|
|
/// failed, or if the constant was unavailable.
|
|
///
|
|
/// Returns [NullConstant] to represent the `null` value.
|
|
Constant? evaluate(Expression e) {
|
|
return e is ConstantExpression ? e.constant : null;
|
|
}
|
|
|
|
/// If [node] is an annotation with a field named [name], returns that field's
|
|
/// value.
|
|
///
|
|
/// This assumes the field is populated from a named argument with that name,
|
|
/// or from the first positional argument.
|
|
///
|
|
/// For example:
|
|
///
|
|
/// class MyAnnotation {
|
|
/// final String name;
|
|
/// // ...
|
|
/// const MyAnnotation(this.name/*, ... other params ... */);
|
|
/// }
|
|
///
|
|
/// @MyAnnotation('FooBar')
|
|
/// main() { ... }
|
|
///
|
|
/// Given the node for `@MyAnnotation('FooBar')` this will return `'FooBar'`.
|
|
Object? getFieldValueFromAnnotation(Expression node, String name) {
|
|
if (node is ConstantExpression) {
|
|
var constant = node.constant;
|
|
if (constant is InstanceConstant) {
|
|
var value = constant.fieldValues.entries
|
|
.firstWhereOrNull((e) => e.key.asField.name.text == name)
|
|
?.value;
|
|
if (value is PrimitiveConstant) return value.value;
|
|
if (value is UnevaluatedConstant) {
|
|
return _evaluateAnnotationArgument(value.expression);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// TODO(jmesserly): this does not use the normal evaluation engine, because
|
|
// it won't work if we don't have the const constructor body available.
|
|
//
|
|
// We may need to address this in the kernel outline files.
|
|
if (node is ConstructorInvocation) {
|
|
Expression? first;
|
|
var named = node.arguments.named;
|
|
if (named.isNotEmpty) {
|
|
first = named.firstWhereOrNull((n) => n.name == name)?.value;
|
|
}
|
|
var positional = node.arguments.positional;
|
|
if (positional.isNotEmpty) first ??= positional[0];
|
|
if (first != null) {
|
|
return _evaluateAnnotationArgument(first);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Object? _evaluateAnnotationArgument(Expression node) {
|
|
if (node is ConstantExpression) {
|
|
var constant = node.constant;
|
|
if (constant is PrimitiveConstant) return constant.value;
|
|
}
|
|
if (node is StaticGet) {
|
|
var target = node.target;
|
|
if (target is Field) {
|
|
return _evaluateAnnotationArgument(target.initializer!);
|
|
}
|
|
}
|
|
return node is BasicLiteral ? node.value : null;
|
|
}
|
|
}
|
|
|
|
/// Implement the class for compiler specific behavior.
|
|
class DevCompilerConstantsBackend extends ConstantsBackend {
|
|
const DevCompilerConstantsBackend();
|
|
|
|
@override
|
|
NumberSemantics get numberSemantics => NumberSemantics.js;
|
|
|
|
@override
|
|
bool get alwaysInlineConstants => false;
|
|
|
|
@override
|
|
bool shouldInlineConstant(ConstantExpression initializer) {
|
|
assert(!alwaysInlineConstants);
|
|
var constant = initializer.constant;
|
|
if (constant is StringConstant) {
|
|
// Only inline small string constants, not large ones.
|
|
// (The upper bound value is arbitrary.)
|
|
return constant.value.length < 32;
|
|
} else if (constant is PrimitiveConstant) {
|
|
// Inline all other primitives.
|
|
return true;
|
|
} else {
|
|
// Don't inline other constants, because it would take too much code size.
|
|
// Better to refer to them by their field/variable name.
|
|
return false;
|
|
}
|
|
}
|
|
}
|