| // 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; | |
| } | |
| } | |
| } |