| // 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'; | |
| 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 KernelToElementMap _elementMap; | |
| final Map<KField, ConstantValue> _fixedInitializers = | |
| <KField, ConstantValue>{}; | |
| KAllocatorAnalysis(KernelFrontEndStrategy kernelStrategy) | |
| : _elementMap = kernelStrategy.elementMap; | |
| // Register class during resolution. Use simple syntactic analysis to find | |
| // null-initialized fields. | |
| void registerInstantiatedClass(KClass class_) { | |
| ClassDefinition definition = _elementMap.getClassDefinition(class_); | |
| assert(definition.kind == ClassKind.regular); | |
| ir.Class classNode = definition.node; | |
| Set<ir.Field> nulls = new Set<ir.Field>(); | |
| for (ir.Field field in classNode.fields) { | |
| if (!field.isInstanceMember) continue; | |
| ir.Expression initializer = field.initializer; | |
| if (initializer == null || initializer is ir.NullLiteral) { | |
| nulls.add(field); | |
| } | |
| } | |
| 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. | |
| nulls.remove(initializer.field); | |
| } | |
| } | |
| } | |
| for (var fieldNode in nulls) { | |
| _fixedInitializers[_elementMap.getField(fieldNode)] = | |
| const NullConstantValue(); | |
| } | |
| } | |
| } | |
| class JAllocatorAnalysis implements AllocatorAnalysis { | |
| // --csp and --fast-startup have different constraints to the generated code. | |
| final CompilerOptions _options; | |
| final Map<JField, ConstantValue> _fixedInitializers = | |
| <JField, ConstantValue>{}; | |
| JAllocatorAnalysis._(this._options); | |
| 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 does not need translating. | |
| if (value.isNull) { | |
| 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]; | |
| } | |
| } |