| // 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:kernel/ast.dart' as ir; |
| |
| import '../constants/values.dart'; |
| import '../js_model/elements.dart' show JsToFrontendMap, JField; |
| import '../kernel/element_map.dart'; |
| import '../kernel/kernel_strategy.dart'; |
| import '../kernel/kelements.dart' show KClass, KField; |
| import '../options.dart'; |
| import '../serialization/serialization.dart'; |
| |
| abstract class AllocatorAnalysis {} |
| |
| /// AllocatorAnalysis |
| /// |
| /// Analysis to determine features of the allocator functions. The allocator |
| /// function takes parameters for each field initializer and initializes the |
| /// fields. Parameters may be omitted if the initializer is always the same |
| /// constant value. How the allocator is emitted will determine what kind of |
| /// constants can be handled. The initial implementation only permits `null`. |
| |
| // TODO(sra): Analysis to determine field order. Current field order is |
| // essentially declaration order, subclass first. We can reorder fields so that |
| // fields initialized with the same constant are together to allow chained |
| // initialization. Fields of a class and superclass can be reordered to allow |
| // chaining, e.g. |
| // |
| // this.x = this.z = null; |
| // |
| class KAllocatorAnalysis implements AllocatorAnalysis { |
| final CompilerOptions _options; |
| final KernelToElementMap _elementMap; |
| |
| final Map<KField, ConstantValue> _fixedInitializers = {}; |
| |
| KAllocatorAnalysis(this._options, KernelFrontEndStrategy kernelStrategy) |
| : _elementMap = kernelStrategy.elementMap; |
| |
| // Register class during resolution. Use simple syntactic analysis to find |
| // null-initialized fields. |
| void registerInstantiatedClass(KClass class_) { |
| ir.Class classNode = _elementMap.getClassNode(class_); |
| |
| Map<ir.Field, ConstantValue> inits = {}; |
| for (ir.Field field in classNode.fields) { |
| if (!field.isInstanceMember) continue; |
| ir.Expression initializer = field.initializer; |
| // TODO(sra): Should really be using constant evaluator to determine |
| // value. |
| if (initializer == null || initializer is ir.NullLiteral) { |
| inits[field] = const NullConstantValue(); |
| } else if (initializer is ir.IntLiteral) { |
| if (_options.useStartupEmitter) { |
| BigInt intValue = BigInt.from(initializer.value).toUnsigned(64); |
| inits[field] = IntConstantValue(intValue); |
| } |
| } else if (initializer is ir.BoolLiteral) { |
| if (_options.useStartupEmitter) { |
| inits[field] = BoolConstantValue(initializer.value); |
| } |
| } else if (initializer is ir.StringLiteral) { |
| if (_options.useStartupEmitter) { |
| if (initializer.value.length <= 20) { |
| inits[field] = StringConstantValue(initializer.value); |
| } |
| } |
| } |
| } |
| |
| for (ir.Constructor constructor in classNode.constructors) { |
| for (ir.Initializer initializer in constructor.initializers) { |
| if (initializer is ir.FieldInitializer) { |
| // TODO(sra): Check explicit initializer value to see if consistent |
| // over all constructors. |
| inits.remove(initializer.field); |
| } |
| } |
| } |
| |
| inits.forEach((ir.Field fieldNode, ConstantValue value) { |
| _fixedInitializers[_elementMap.getField(fieldNode)] = value; |
| }); |
| } |
| } |
| |
| class JAllocatorAnalysis implements AllocatorAnalysis { |
| /// Tag used for identifying serialized [JAllocatorAnalysis] objects in a |
| /// debugging data stream. |
| static const String tag = 'allocator-analysis'; |
| |
| // --csp and --fast-startup have different constraints to the generated code. |
| final CompilerOptions _options; |
| final Map<JField, ConstantValue> _fixedInitializers = {}; |
| |
| JAllocatorAnalysis._(this._options); |
| |
| /// Deserializes a [JAllocatorAnalysis] object from [source]. |
| factory JAllocatorAnalysis.readFromDataSource( |
| DataSource source, CompilerOptions options) { |
| source.begin(tag); |
| JAllocatorAnalysis analysis = new JAllocatorAnalysis._(options); |
| int fieldCount = source.readInt(); |
| for (int i = 0; i < fieldCount; i++) { |
| JField field = source.readMember(); |
| ConstantValue value = source.readConstant(); |
| analysis._fixedInitializers[field] = value; |
| } |
| source.end(tag); |
| return analysis; |
| } |
| |
| /// Serializes this [JAllocatorAnalysis] to [sink]. |
| void writeToDataSink(DataSink sink) { |
| sink.begin(tag); |
| sink.writeInt(_fixedInitializers.length); |
| _fixedInitializers.forEach((JField field, ConstantValue value) { |
| sink.writeMember(field); |
| sink.writeConstant(value); |
| }); |
| sink.end(tag); |
| } |
| |
| static JAllocatorAnalysis from(KAllocatorAnalysis kAnalysis, |
| JsToFrontendMap map, CompilerOptions options) { |
| var result = new JAllocatorAnalysis._(options); |
| |
| kAnalysis._fixedInitializers.forEach((KField kField, ConstantValue value) { |
| // TODO(sra): Translate constant, but Null and these primitives do not |
| // need translating. |
| if (value.isNull || value.isInt || value.isBool || value.isString) { |
| JField jField = map.toBackendMember(kField); |
| if (jField != null) { |
| result._fixedInitializers[jField] = value; |
| } |
| } |
| }); |
| |
| return result; |
| } |
| |
| bool get _isEnabled { |
| if (_options.useContentSecurityPolicy && !_options.useStartupEmitter) { |
| // TODO(sra): Refactor csp 'precompiled' constructor generation to allow |
| // in-allocator initialization. |
| return false; |
| } |
| return true; |
| } |
| // TODO(sra): Add way to let injected fields be initialized to a constant in |
| // allocator. |
| |
| bool isInitializedInAllocator(JField field) { |
| if (!_isEnabled) return false; |
| return _fixedInitializers[field] != null; |
| } |
| |
| /// Return constant for a field initialized in allocator. Returns `null` for |
| /// fields not initialized in allocator. |
| ConstantValue initializerValue(JField field) { |
| assert(_isEnabled); |
| return _fixedInitializers[field]; |
| } |
| } |