| Index: pkg/kernel/lib/type_propagation/builder.dart
|
| diff --git a/pkg/kernel/lib/type_propagation/builder.dart b/pkg/kernel/lib/type_propagation/builder.dart
|
| deleted file mode 100644
|
| index a7b4c9612e0ce26ab3be8e5bc96d285be748b6b2..0000000000000000000000000000000000000000
|
| --- a/pkg/kernel/lib/type_propagation/builder.dart
|
| +++ /dev/null
|
| @@ -1,1984 +0,0 @@
|
| -// Copyright (c) 2016, 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.
|
| -library kernel.type_propagation.builder;
|
| -
|
| -import '../ast.dart';
|
| -import '../class_hierarchy.dart';
|
| -import '../core_types.dart';
|
| -import 'canonicalizer.dart';
|
| -import 'constraints.dart';
|
| -import 'type_propagation.dart';
|
| -import 'visualizer.dart';
|
| -
|
| -/// Maps AST nodes to constraint variables at the level of function boundaries.
|
| -///
|
| -/// Bindings internally in a function are only preserved by the [Visualizer].
|
| -class VariableMapping {
|
| - /// Variable holding all values that may flow into the given field.
|
| - final Map<Field, int> fields = <Field, int>{};
|
| -
|
| - /// Variable holding all values that may be returned from the given function.
|
| - final Map<FunctionNode, int> returns = <FunctionNode, int>{};
|
| -
|
| - /// Variable holding all values that may be passed into the given function
|
| - /// parameter (possibly through a default parameter value).
|
| - final Map<VariableDeclaration, int> parameters = <VariableDeclaration, int>{};
|
| -
|
| - /// Variable holding the function object for the given function.
|
| - final Map<FunctionNode, int> functions = <FunctionNode, int>{};
|
| -
|
| - static VariableMapping make(int _) => new VariableMapping();
|
| -}
|
| -
|
| -/// Maps AST nodes to the lattice employed by the constraint system.
|
| -class LatticeMapping {
|
| - /// Lattice point containing the torn-off functions originating from an
|
| - /// instance procedure that overrides the given procedure.
|
| - final Map<Procedure, int> functionsOverridingMethod = <Procedure, int>{};
|
| -
|
| - /// Lattice point containing all torn-off functions originating from an
|
| - /// instance procedure of the given name,
|
| - ///
|
| - /// This ensures that calls to a method with unknown receiver may still
|
| - /// recover some information about the callee based on the name alone.
|
| - final Map<Name, int> functionsWithName = <Name, int>{};
|
| -
|
| - /// Maps a class index to a lattice point containing all values that are
|
| - /// subtypes of that class.
|
| - final List<int> subtypesOfClass;
|
| -
|
| - /// Maps a class index to a lattice point containing all values that are
|
| - /// subclasses of that class.
|
| - final List<int> subclassesOfClass;
|
| -
|
| - LatticeMapping(int numberOfClasses)
|
| - : subtypesOfClass = new List<int>(numberOfClasses),
|
| - subclassesOfClass = new List<int>(numberOfClasses);
|
| -}
|
| -
|
| -/// Generates a [ConstraintSystem] to be solved by [Solver].
|
| -class Builder {
|
| - final Program program;
|
| - final ClassHierarchy hierarchy;
|
| - final CoreTypes coreTypes;
|
| - final ConstraintSystem constraints;
|
| - final FieldNames fieldNames;
|
| - final Visualizer visualizer;
|
| -
|
| - final LatticeMapping lattice;
|
| -
|
| - /// Bindings for all members. The values inferred for these variables is the
|
| - /// output of the analysis.
|
| - ///
|
| - /// For static members, these are the canonical variables representing the
|
| - /// member.
|
| - ///
|
| - /// For instance members, these are the context-insensitive joins over all
|
| - /// the specialized copies of the instance member.
|
| - final VariableMapping global = new VariableMapping();
|
| -
|
| - /// Maps a class index to the bindings for instance members specific to that
|
| - /// class as the host class.
|
| - final List<VariableMapping> classMapping;
|
| -
|
| - final Map<TypeParameter, int> functionTypeParameters = <TypeParameter, int>{};
|
| -
|
| - /// Variable holding the result of the declaration-site field initializer
|
| - /// for the given field.
|
| - final Map<Field, int> declarationSiteFieldInitializer = <Field, int>{};
|
| -
|
| - /// Maps a class index to the result of [getInterfaceEscapeVariable].
|
| - final List<int> interfaceEscapeVariables;
|
| -
|
| - /// Maps a class index to the result of [getExternalInstanceVariable].
|
| - final List<int> externalClassVariables;
|
| - final List<int> externalClassValues;
|
| -
|
| - final List<int> externalClassWorklist = <int>[];
|
| -
|
| - final Uint31PairMap<int> _stores = new Uint31PairMap<int>();
|
| - final Uint31PairMap<int> _loads = new Uint31PairMap<int>();
|
| - final List<InferredValue> _baseTypeOfLatticePoint = <InferredValue>[];
|
| -
|
| - int bottomNode;
|
| - int dynamicNode;
|
| - int boolNode;
|
| - int intNode;
|
| - int doubleNode;
|
| - int stringNode;
|
| - int symbolNode;
|
| - int typeNode;
|
| - int listNode;
|
| - int mapNode;
|
| - int nullNode;
|
| - int iterableNode;
|
| - int futureNode;
|
| - int streamNode;
|
| - int functionValueNode;
|
| -
|
| - int iteratorField;
|
| - int currentField;
|
| -
|
| - /// Lattice point containing all function values.
|
| - int latticePointForAllFunctions;
|
| -
|
| - Member identicalFunction;
|
| -
|
| - bool verbose;
|
| -
|
| - Builder(Program program,
|
| - {ClassHierarchy hierarchy,
|
| - FieldNames names,
|
| - CoreTypes coreTypes,
|
| - Visualizer visualizer,
|
| - bool verbose: false})
|
| - : this._internal(
|
| - program,
|
| - hierarchy ?? new ClassHierarchy(program),
|
| - names ?? new FieldNames(),
|
| - coreTypes ?? new CoreTypes(program),
|
| - visualizer,
|
| - verbose);
|
| -
|
| - Builder._internal(this.program, ClassHierarchy hierarchy, FieldNames names,
|
| - this.coreTypes, Visualizer visualizer, this.verbose)
|
| - : this.hierarchy = hierarchy,
|
| - this.fieldNames = names,
|
| - this.visualizer = visualizer,
|
| - this.constraints = new ConstraintSystem(),
|
| - this.classMapping = new List<VariableMapping>.generate(
|
| - hierarchy.classes.length, VariableMapping.make),
|
| - this.interfaceEscapeVariables = new List<int>(hierarchy.classes.length),
|
| - this.externalClassVariables = new List<int>(hierarchy.classes.length),
|
| - this.externalClassValues = new List<int>(hierarchy.classes.length),
|
| - this.lattice = new LatticeMapping(hierarchy.classes.length) {
|
| - if (visualizer != null) {
|
| - visualizer.builder = this;
|
| - visualizer.constraints = constraints;
|
| - visualizer.fieldNames = fieldNames;
|
| - }
|
| -
|
| - // Build the subtype lattice points.
|
| - // The order in which lattice points are created determines how ambiguous
|
| - // upper bounds are resolved. The lattice point with highest index among
|
| - // the potential upper bounds is the result of a join.
|
| - // We create all the subtype lattice point before all the subclass lattice
|
| - // points, to ensure that subclass information takes precedence over
|
| - // subtype information.
|
| - for (int i = 0; i < hierarchy.classes.length; ++i) {
|
| - Class class_ = hierarchy.classes[i];
|
| - List<int> supers = <int>[];
|
| - if (class_.supertype != null) {
|
| - supers.add(getLatticePointForSubtypesOfClass(class_.superclass));
|
| - }
|
| - if (class_.mixedInType != null) {
|
| - supers.add(getLatticePointForSubtypesOfClass(class_.mixedInClass));
|
| - }
|
| - for (Supertype supertype in class_.implementedTypes) {
|
| - supers.add(getLatticePointForSubtypesOfClass(supertype.classNode));
|
| - }
|
| - int subtypePoint = newLatticePoint(supers, class_,
|
| - i == 0 ? BaseClassKind.Subclass : BaseClassKind.Subtype);
|
| - lattice.subtypesOfClass[i] = subtypePoint;
|
| - visualizer?.annotateLatticePoint(subtypePoint, class_, 'subtype');
|
| - }
|
| -
|
| - // Build the lattice points for subclasses and exact classes.
|
| - for (int i = 0; i < hierarchy.classes.length; ++i) {
|
| - Class class_ = hierarchy.classes[i];
|
| - int subtypePoint = lattice.subtypesOfClass[i];
|
| - assert(subtypePoint != null);
|
| - int subclassPoint;
|
| - if (class_.supertype == null) {
|
| - subclassPoint = subtypePoint;
|
| - } else {
|
| - subclassPoint = newLatticePoint(<int>[
|
| - getLatticePointForSubclassesOf(class_.superclass),
|
| - subtypePoint
|
| - ], class_, BaseClassKind.Subclass);
|
| - }
|
| - lattice.subclassesOfClass[i] = subclassPoint;
|
| - int concretePoint =
|
| - newLatticePoint(<int>[subclassPoint], class_, BaseClassKind.Exact);
|
| - int value = constraints.newValue(concretePoint);
|
| - int variable = constraints.newVariable();
|
| - // We construct the constraint system so the first N variables and values
|
| - // correspond to the N classes in the program.
|
| - assert(variable == i);
|
| - assert(value == -i);
|
| - visualizer?.annotateLatticePoint(subclassPoint, class_, 'subclass');
|
| - visualizer?.annotateLatticePoint(concretePoint, class_, 'concrete');
|
| - visualizer?.annotateVariable(variable, class_);
|
| - visualizer?.annotateValue(value, class_);
|
| - addInput(value, ValueBit.other, variable);
|
| - }
|
| -
|
| - bottomNode = newVariable(null, 'bottom');
|
| - dynamicNode = getExternalInstanceVariable(coreTypes.objectClass);
|
| - boolNode = getExternalInstanceVariable(coreTypes.boolClass);
|
| - intNode = getExternalInstanceVariable(coreTypes.intClass);
|
| - doubleNode = getExternalInstanceVariable(coreTypes.doubleClass);
|
| - stringNode = getExternalInstanceVariable(coreTypes.stringClass);
|
| - symbolNode = getExternalInstanceVariable(coreTypes.symbolClass);
|
| - typeNode = getExternalInstanceVariable(coreTypes.typeClass);
|
| - listNode = getExternalInstanceVariable(coreTypes.listClass);
|
| - mapNode = getExternalInstanceVariable(coreTypes.mapClass);
|
| - iterableNode = getExternalInstanceVariable(coreTypes.iterableClass);
|
| - futureNode = getExternalInstanceVariable(coreTypes.futureClass);
|
| - streamNode = getExternalInstanceVariable(coreTypes.streamClass);
|
| - functionValueNode = getExternalInstanceVariable(coreTypes.functionClass);
|
| - nullNode = newVariable(null, 'Null');
|
| -
|
| - iteratorField = getPropertyField(Names.iterator);
|
| - currentField = getPropertyField(Names.current);
|
| -
|
| - latticePointForAllFunctions =
|
| - getLatticePointForSubtypesOfClass(coreTypes.functionClass);
|
| -
|
| - identicalFunction = coreTypes.getTopLevelMember('dart:core', 'identical');
|
| -
|
| - // Seed bitmasks for built-in values.
|
| - constraints.addBitmaskInput(ValueBit.null_, nullNode);
|
| - constraints.addBitmaskInput(ValueBit.all, dynamicNode);
|
| -
|
| - for (Library library in program.libraries) {
|
| - for (Procedure procedure in library.procedures) {
|
| - buildProcedure(null, procedure);
|
| - }
|
| - for (Field field in library.fields) {
|
| - buildStaticField(field);
|
| - }
|
| - for (Class class_ in library.classes) {
|
| - for (Procedure procedure in class_.procedures) {
|
| - if (procedure.isStatic) {
|
| - buildProcedure(null, procedure);
|
| - }
|
| - }
|
| - for (Field field in class_.fields) {
|
| - if (field.isStatic) {
|
| - buildStaticField(field);
|
| - }
|
| - }
|
| - if (!class_.isAbstract) {
|
| - buildInstanceValue(class_);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // We don't track the values flowing into the identical function, as it
|
| - // causes a lot of spurious escape. Every class that inherits Object.==
|
| - // would escape its 'this' value into a dynamic context.
|
| - // Mark the identical() parameters as 'dynamic' so the output is sound.
|
| - for (int i = 0; i < 2; ++i) {
|
| - constraints.addAssign(
|
| - dynamicNode,
|
| - getSharedParameterVariable(
|
| - identicalFunction.function.positionalParameters[i]));
|
| - }
|
| -
|
| - // Build constraints mocking the external interfaces.
|
| - while (externalClassWorklist.isNotEmpty) {
|
| - int classIndex = externalClassWorklist.removeLast();
|
| - _buildExternalClassValue(classIndex);
|
| - }
|
| - }
|
| -
|
| - int newLatticePoint(
|
| - List<int> parentLatticePoints, Class baseClass, BaseClassKind kind) {
|
| - _baseTypeOfLatticePoint.add(new InferredValue(baseClass, kind, 0));
|
| - return constraints.newLatticePoint(parentLatticePoints);
|
| - }
|
| -
|
| - void addInput(int value, int bitmask, int destination) {
|
| - constraints.addAllocation(value, destination);
|
| - constraints.addBitmaskInput(bitmask, destination);
|
| - }
|
| -
|
| - /// Returns an [InferredValue] with the base type relation for the given
|
| - /// lattice point but whose bitmask is 0. The bitmask must be filled in
|
| - /// before this value is exposed to analysis clients.
|
| - InferredValue getBaseTypeOfLatticePoint(int latticePoint) {
|
| - return _baseTypeOfLatticePoint[latticePoint];
|
| - }
|
| -
|
| - /// Returns the lattice point containing all subtypes of the given class.
|
| - int getLatticePointForSubtypesOfClass(Class classNode) {
|
| - int index = hierarchy.getClassIndex(classNode);
|
| - return lattice.subtypesOfClass[index];
|
| - }
|
| -
|
| - /// Returns the lattice point containing all subclasses of the given class.
|
| - int getLatticePointForSubclassesOf(Class classNode) {
|
| - int index = hierarchy.getClassIndex(classNode);
|
| - return lattice.subclassesOfClass[index];
|
| - }
|
| -
|
| - /// Returns the lattice point containing all function implementing the given
|
| - /// instance method.
|
| - int getLatticePointForFunctionsOverridingMethod(Procedure node) {
|
| - assert(!node.isStatic);
|
| - if (node.isAccessor) return latticePointForAllFunctions;
|
| - if (node.enclosingClass.supertype == null)
|
| - return latticePointForAllFunctions;
|
| - return lattice.functionsOverridingMethod[node] ??=
|
| - _makeLatticePointForFunctionsOverridingMethod(node);
|
| - }
|
| -
|
| - int _makeLatticePointForFunctionsOverridingMethod(Procedure node) {
|
| - Class host = node.enclosingClass;
|
| - Member superMember = host.supertype == null
|
| - ? null
|
| - : hierarchy.getInterfaceMember(host.superclass, node.name);
|
| - int super_;
|
| - if (superMember is Procedure && !superMember.isAccessor) {
|
| - super_ = getLatticePointForFunctionsOverridingMethod(superMember);
|
| - } else {
|
| - super_ = getLatticePointForFunctionsWithName(node.name);
|
| - }
|
| - int point = newLatticePoint(
|
| - <int>[super_], coreTypes.functionClass, BaseClassKind.Subtype);
|
| - visualizer?.annotateLatticePoint(point, node, 'overriders');
|
| - return point;
|
| - }
|
| -
|
| - int newVariable([TreeNode node, String info]) {
|
| - int variable = constraints.newVariable();
|
| - visualizer?.annotateVariable(variable, node, info);
|
| - return variable;
|
| - }
|
| -
|
| - VariableMapping getClassMapping(Class host) {
|
| - if (host == null) return global;
|
| - int index = hierarchy.getClassIndex(host);
|
| - return classMapping[index];
|
| - }
|
| -
|
| - /// Returns a variable that should contain all values that may be contained
|
| - /// in any copy the given field (hence "shared" between the copies).
|
| - int getSharedFieldVariable(Field field) {
|
| - return global.fields[field] ??= newVariable(field);
|
| - }
|
| -
|
| - /// Returns a variable representing the given field on the given class.
|
| - ///
|
| - /// If the field is static, [host] should be `null`.
|
| - int getFieldVariable(Class host, Field field) {
|
| - if (host == null) return getSharedFieldVariable(field);
|
| - VariableMapping mapping = getClassMapping(host);
|
| - return mapping.fields[field] ??= _makeFieldVariable(host, field);
|
| - }
|
| -
|
| - int _makeFieldVariable(Class host, Field field) {
|
| - // Create a variable specific to this host class, and add an assignment
|
| - // to the global sink for this field.
|
| - assert(host != null);
|
| - int variable = newVariable(field);
|
| - int sink = getSharedFieldVariable(field);
|
| - constraints.addSink(variable, sink);
|
| - visualizer?.annotateSink(variable, sink, field);
|
| - return variable;
|
| - }
|
| -
|
| - /// Variable containing all values that may be passed into the given parameter
|
| - /// of any instantiation of the given function (hence "shared" between them).
|
| - int getSharedParameterVariable(VariableDeclaration node) {
|
| - return global.parameters[node] ??= newVariable(node, 'shared parameter');
|
| - }
|
| -
|
| - int getParameterVariable(Class host, VariableDeclaration node) {
|
| - if (host == null) return getSharedParameterVariable(node);
|
| - VariableMapping mapping = getClassMapping(host);
|
| - return mapping.parameters[node] ??= _makeParameterVariable(host, node);
|
| - }
|
| -
|
| - int _makeParameterVariable(Class host, VariableDeclaration node) {
|
| - assert(host != null);
|
| - int variable = newVariable(node, 'parameter');
|
| - int sink = getSharedParameterVariable(node);
|
| - constraints.addSink(variable, sink);
|
| - visualizer?.annotateSink(variable, sink, node);
|
| - return variable;
|
| - }
|
| -
|
| - /// Returns the variable representing all the values that would be checked
|
| - /// against the given function type parameter in checked mode.
|
| - ///
|
| - /// This is used to model the behavior of external generic methods.
|
| - ///
|
| - /// For example:
|
| - ///
|
| - /// class List {
|
| - /// external static factory List<T> filled<T>(int length, T value);
|
| - /// }
|
| - ///
|
| - /// A variable `v` representing `T` will be generated. All values that are
|
| - /// passed into the `value` parameter will flow into `v`, and `v` will
|
| - /// in turn flow into the type parameter field of `List`, because the method
|
| - /// returns `List<T>`. Also see [FieldNames.getTypeParameterField].
|
| - int getFunctionTypeParameterVariable(TypeParameter node) {
|
| - return functionTypeParameters[node] ??= newVariable(node);
|
| - }
|
| -
|
| - /// Variable containing all values that may be returned from any instantiation
|
| - /// of the given function (hence "shared" between them).
|
| - int getSharedReturnVariable(FunctionNode node) {
|
| - return global.returns[node] ??= newVariable(node, 'return');
|
| - }
|
| -
|
| - int getReturnVariable(Class host, Procedure node) {
|
| - if (host == null) return getSharedReturnVariable(node.function);
|
| - VariableMapping mapping = getClassMapping(host);
|
| - return mapping.returns[node.function] ??= _makeReturnVariable(host, node);
|
| - }
|
| -
|
| - int _makeReturnVariable(Class host, Procedure node) {
|
| - assert(host != null);
|
| - int variable = newVariable(node, 'return');
|
| - int sink = getSharedReturnVariable(node.function);
|
| - constraints.addSink(variable, sink);
|
| - visualizer?.annotateSink(variable, sink, node);
|
| - return variable;
|
| - }
|
| -
|
| - /// Returns a variable containing all the function objects for all
|
| - /// instantiations of the given function.
|
| - int getSharedTearOffVariable(FunctionNode node) {
|
| - return global.functions[node] ??= newVariable(node);
|
| - }
|
| -
|
| - /// Returns a variable containing the torn-off copy of the given function
|
| - /// occurring in static context.
|
| - int getStaticTearOffVariable(FunctionNode node) {
|
| - return global.functions[node] ??= _makeStaticTearOffVariable(node);
|
| - }
|
| -
|
| - int _makeStaticTearOffVariable(FunctionNode node) {
|
| - return newFunction(node);
|
| - }
|
| -
|
| - /// Returns a variable containing the torn-off copy of the given procedure.
|
| - int getTearOffVariable(Class host, Procedure node) {
|
| - if (host == null) return getStaticTearOffVariable(node.function);
|
| - VariableMapping mapping = getClassMapping(host);
|
| - return mapping.functions[node.function] ??=
|
| - _makeTearOffVariable(host, node);
|
| - }
|
| -
|
| - int _makeTearOffVariable(Class host, Procedure node) {
|
| - int variable = newFunction(node.function, node);
|
| - int sink = getSharedTearOffVariable(node.function);
|
| - constraints.addSink(variable, sink);
|
| - visualizer?.annotateSink(variable, sink, node);
|
| - return variable;
|
| - }
|
| -
|
| - /// Returns the variable holding the result of a 'get' selector dispatched
|
| - /// to the given member, or `null` if the member cannot respond to a 'get'
|
| - /// selector.
|
| - int getMemberGetter(Class host, Member member) {
|
| - if (member is Field) {
|
| - return getFieldVariable(host, member);
|
| - } else if (member is Procedure) {
|
| - if (member.isGetter) {
|
| - return getReturnVariable(host, member);
|
| - } else if (!member.isAccessor) {
|
| - return getTearOffVariable(host, member);
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /// Returns the variable holding the argument to a 'set' selector dispatched
|
| - /// to the given member, or `null` if the member cannot respond to a 'set'
|
| - /// selector.
|
| - int getMemberSetter(Class host, Member member) {
|
| - if (member is Field && !member.isFinal) {
|
| - return getFieldVariable(host, member);
|
| - } else if (member is Procedure && member.isSetter) {
|
| - return getParameterVariable(
|
| - host, member.function.positionalParameters[0]);
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /// Returns a lattice point containing all instance methods with the given
|
| - /// name.
|
| - int getLatticePointForFunctionsWithName(Name name) {
|
| - if (name == null) return latticePointForAllFunctions;
|
| - return lattice.functionsWithName[name] ??=
|
| - _makeLatticePointForFunctionsWithName(name);
|
| - }
|
| -
|
| - int _makeLatticePointForFunctionsWithName(Name name) {
|
| - int point = newLatticePoint(<int>[latticePointForAllFunctions],
|
| - coreTypes.functionClass, BaseClassKind.Subtype);
|
| - visualizer?.annotateLatticePoint(point, null, 'Methods of name $name');
|
| - return point;
|
| - }
|
| -
|
| - /// Returns a variable holding a new function value annotated with given AST
|
| - /// node.
|
| - ///
|
| - /// If the function is the body of an instance procedure, it should be passed
|
| - /// as [member] to ensure an effective lattice is built for it.
|
| - /// Otherwise, [member] should be omitted.
|
| - int newFunction(FunctionNode node, [Procedure member]) {
|
| - assert(node != null);
|
| - int functionVariable = newVariable(node);
|
| - int baseLatticePoint = member == null
|
| - ? latticePointForAllFunctions
|
| - : getLatticePointForFunctionsOverridingMethod(member);
|
| - int latticePoint = newLatticePoint(<int>[baseLatticePoint],
|
| - coreTypes.functionClass, BaseClassKind.Subtype);
|
| - visualizer?.annotateLatticePoint(latticePoint, member, 'function');
|
| - int minArity = node.requiredParameterCount;
|
| - int maxArity = node.positionalParameters.length;
|
| - int functionValue = constraints.newValue(latticePoint);
|
| - for (int i = 0; i < node.positionalParameters.length; ++i) {
|
| - int variable = newVariable();
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int field = fieldNames.getPositionalParameterField(arity, i);
|
| - constraints.setStoreLocation(functionValue, field, variable);
|
| - constraints.setLoadLocation(functionValue, field, variable);
|
| - }
|
| - }
|
| - for (int i = 0; i < node.namedParameters.length; ++i) {
|
| - int variable = newVariable();
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int field = fieldNames.getNamedParameterField(
|
| - arity, node.namedParameters[i].name);
|
| - constraints.setStoreLocation(functionValue, field, variable);
|
| - constraints.setLoadLocation(functionValue, field, variable);
|
| - }
|
| - }
|
| - int returnVariable = newVariable();
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int returnField = fieldNames.getReturnField(arity);
|
| - constraints.setStoreLocation(functionValue, returnField, returnVariable);
|
| - constraints.setLoadLocation(functionValue, returnField, returnVariable);
|
| - }
|
| - visualizer?.annotateFunction(functionValue, node);
|
| - visualizer?.annotateValue(functionValue, member, 'function');
|
| - addInput(functionValue, ValueBit.other, functionVariable);
|
| - constraints.setLoadLocation(
|
| - functionValue, fieldNames.callHandlerField, functionVariable);
|
| - constraints.setLoadLocation(functionValue,
|
| - fieldNames.getPropertyField(Names.call_), functionVariable);
|
| - return functionVariable;
|
| - }
|
| -
|
| - /// Returns a variable containing the concrete instances of the given class.
|
| - int getInstanceVariable(Class node) {
|
| - assert(!node.isAbstract);
|
| - return hierarchy.getClassIndex(node);
|
| - }
|
| -
|
| - /// Returns the value representing the concrete instances of the given class.
|
| - int getInstanceValue(Class node) {
|
| - assert(!node.isAbstract);
|
| - // Values are negated to help distinguish them from variables and
|
| - // lattice points.
|
| - return -hierarchy.getClassIndex(node);
|
| - }
|
| -
|
| - /// Returns a variable containing the external instances of the given class.
|
| - ///
|
| - /// An "external instance of C" is an instance allocated by external code,
|
| - /// and is either a direct instance of C or an instance of an external class
|
| - /// that implements C.
|
| - ///
|
| - /// For the moment, basic types like `int` and `bool` are treated as external
|
| - /// instances of their respective classes.
|
| - ///
|
| - /// Unlike [getInstanceVariable], this method ensures that the relevant
|
| - /// constraints have been generated to model an external implementation of the
|
| - /// class.
|
| - int getExternalInstanceVariable(Class node) {
|
| - int classIndex = hierarchy.getClassIndex(node);
|
| - return externalClassVariables[classIndex] ??=
|
| - _makeExternalInstanceVariable(node, classIndex);
|
| - }
|
| -
|
| - int getValueBitForExternalClass(Class node) {
|
| - if (node == coreTypes.intClass) {
|
| - return ValueBit.integer;
|
| - } else if (node == coreTypes.doubleClass) {
|
| - return ValueBit.double_;
|
| - } else if (node == coreTypes.stringClass) {
|
| - return ValueBit.string;
|
| - } else {
|
| - return ValueBit.other;
|
| - }
|
| - }
|
| -
|
| - int _makeExternalInstanceVariable(Class node, int classIndex) {
|
| - if (node == coreTypes.numClass) {
|
| - // Don't build an interface based on the "num" class, instead treat it
|
| - // as the union of "int" and "double".
|
| - int variable = newVariable(node);
|
| - constraints.addAssign(intNode, variable);
|
| - constraints.addAssign(doubleNode, variable);
|
| - return variable;
|
| - }
|
| - int baseLatticePoint = getLatticePointForSubtypesOfClass(node);
|
| - // TODO(asgerf): Use more fine-grained handling of externals, based on
|
| - // metadata or on a specification read from a separate file (issue #22).
|
| - int latticePoint =
|
| - newLatticePoint(<int>[baseLatticePoint], node, BaseClassKind.Subtype);
|
| - visualizer?.annotateLatticePoint(latticePoint, node, 'external');
|
| - int value = constraints.newValue(latticePoint);
|
| - int variable = newVariable(node, 'external');
|
| - addInput(value, getValueBitForExternalClass(node), variable);
|
| - externalClassValues[classIndex] = value;
|
| - externalClassWorklist.add(classIndex);
|
| - return variable;
|
| - }
|
| -
|
| - void _buildExternalClassValue(int index) {
|
| - Class node = hierarchy.classes[index];
|
| - int variable = externalClassVariables[index];
|
| - int externalObject = externalClassValues[index];
|
| - Name previousName = null;
|
| - for (Member member in hierarchy.getInterfaceMembers(node, setters: false)) {
|
| - // Do not generate an interface member for a given name more than once.
|
| - // This can happen if a class inherits two methods through different
|
| - // inheritance paths.
|
| - if (member.name == previousName) continue;
|
| - previousName = member.name;
|
| - _buildExternalInterfaceMember(node, member, externalObject, variable,
|
| - isSetter: false);
|
| - }
|
| - previousName = null;
|
| - for (Member member in hierarchy.getInterfaceMembers(node, setters: true)) {
|
| - if (member.name == previousName) continue;
|
| - previousName = member.name;
|
| - _buildExternalInterfaceMember(node, member, externalObject, variable,
|
| - isSetter: true);
|
| - }
|
| - for (TypeParameter parameter in node.typeParameters) {
|
| - int field = fieldNames.getTypeParameterField(parameter);
|
| - int location = newVariable(parameter);
|
| - constraints.setStoreLocation(externalObject, field, location);
|
| - constraints.setLoadLocation(externalObject, field, location);
|
| - }
|
| - }
|
| -
|
| - void _buildExternalInterfaceMember(
|
| - Class host, Member member, int object, int variable,
|
| - {bool isSetter}) {
|
| - // TODO(asgerf): Handle nullability of return values.
|
| - TypeEnvironment environment =
|
| - new TypeEnvironment(this, host, member, thisVariable: variable);
|
| - int propertyField = fieldNames.getPropertyField(member.name);
|
| - if (member is Field) {
|
| - int fieldType = buildCovariantType(member.type, environment);
|
| - if (isSetter) {
|
| - constraints.setStoreLocation(object, propertyField, fieldType);
|
| - } else {
|
| - constraints.setLoadLocation(object, propertyField, fieldType);
|
| - }
|
| - } else {
|
| - Procedure procedure = member;
|
| - FunctionNode function = procedure.function;
|
| - if (procedure.isGetter) {
|
| - int returned = buildCovariantType(function.returnType, environment);
|
| - constraints.setLoadLocation(object, propertyField, returned);
|
| - } else if (procedure.isSetter) {
|
| - int escaping = environment.getLoad(variable, propertyField);
|
| - buildContravariantType(
|
| - function.positionalParameters[0].type, environment, escaping);
|
| - } else {
|
| - int externalMember = buildCovariantFunctionType(function, environment);
|
| - constraints.setLoadLocation(object, propertyField, externalMember);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /// Returns a variable that is exposed to external calls through the
|
| - /// given interface.
|
| - ///
|
| - /// For example, consider this code with a simplified version of SendPort:
|
| - ///
|
| - /// abstract class SendPort {
|
| - /// void send(dynamic x);
|
| - /// }
|
| - ///
|
| - /// class MySendPort implements SendPort {
|
| - /// void send(x) { ... }
|
| - /// }
|
| - ///
|
| - /// external void spawnFunction(SendPort readyPort);
|
| - ///
|
| - /// main() {
|
| - /// spawnFunction(new MySendPort());
|
| - /// }
|
| - ///
|
| - /// We must ensure that the parameter to `MySendPort::send` is inferred to
|
| - /// be unknown because the external function `spawnFunction` may cause an
|
| - /// invocation of its `send` method with an unknown argument.
|
| - ///
|
| - /// The interface escape variable for this version of `SendPort` would be a
|
| - /// variable `v` with constraints corresponding to a call `v.send(<dynamic>)`.
|
| - ///
|
| - /// Values that escape into an external parameter typed as `SendPort`, such
|
| - /// as `new MySendPort()` must then be made to flow into `v`.
|
| - int getInterfaceEscapeVariable(Class node) {
|
| - int index = hierarchy.getClassIndex(node);
|
| - return interfaceEscapeVariables[index] ??
|
| - _buildInterfaceEscapeVariable(node, index);
|
| - }
|
| -
|
| - int _buildInterfaceEscapeVariable(Class node, int index) {
|
| - int escapingObject = constraints.newVariable();
|
| - visualizer?.annotateVariable(escapingObject, node, 'escape point');
|
| - interfaceEscapeVariables[index] = escapingObject;
|
| - for (Member member in hierarchy.getInterfaceMembers(node, setters: false)) {
|
| - _buildEscapingInterfaceMember(node, member, escapingObject);
|
| - }
|
| - for (Member member in hierarchy.getInterfaceMembers(node, setters: true)) {
|
| - _buildEscapingInterfaceMember(node, member, escapingObject);
|
| - }
|
| - return escapingObject;
|
| - }
|
| -
|
| - /// Models the behavior of external code invoking [member] on
|
| - /// [escapingObject].
|
| - void _buildEscapingInterfaceMember(
|
| - Class host, Member member, int escapingObject) {
|
| - TypeEnvironment environment =
|
| - new TypeEnvironment(this, host, member, thisVariable: escapingObject);
|
| - int propertyField = fieldNames.getPropertyField(member.name);
|
| - if (member is Field) {
|
| - int escapingMember = environment.getLoad(escapingObject, propertyField);
|
| - buildContravariantType(member.type, environment, escapingMember);
|
| - } else {
|
| - Procedure procedure = member;
|
| - FunctionNode function = procedure.function;
|
| - if (procedure.isGetter) {
|
| - int escapingMember = environment.getLoad(escapingObject, propertyField);
|
| - buildContravariantType(
|
| - function.returnType, environment, escapingMember);
|
| - } else if (procedure.isSetter) {
|
| - VariableDeclaration parameter = function.positionalParameters[0];
|
| - int argument = buildCovariantType(parameter.type, environment);
|
| - environment.addStore(escapingObject, propertyField, argument);
|
| - } else {
|
| - int escapingMember = environment.getLoad(escapingObject, propertyField);
|
| - buildContravariantFunctionType(function, environment, escapingMember);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /// Returns a variable with the possible values of [type] as provided by
|
| - /// external code.
|
| - int buildCovariantType(DartType type, TypeEnvironment environment) {
|
| - return new CovariantExternalTypeVisitor(this, environment).visit(type);
|
| - }
|
| -
|
| - /// Like [buildCovariantType], but for the function type implied by the
|
| - /// type annotations on a function AST node.
|
| - int buildCovariantFunctionType(
|
| - FunctionNode node, TypeEnvironment environment) {
|
| - return new CovariantExternalTypeVisitor(this, environment)
|
| - .buildFunctionNode(node);
|
| - }
|
| -
|
| - /// Generates constraints to model the behavior of [input] escaping into
|
| - /// external code through a parameter annotated with [type].
|
| - void buildContravariantType(
|
| - DartType type, TypeEnvironment environment, int input) {
|
| - new ContravariantExternalTypeVisitor(this, environment, input).visit(type);
|
| - }
|
| -
|
| - /// Like [buildContravariantType], but for the function type implied by the
|
| - /// type annotations on a function AST node.
|
| - void buildContravariantFunctionType(
|
| - FunctionNode node, TypeEnvironment environment, int input) {
|
| - new ContravariantExternalTypeVisitor(this, environment, input)
|
| - .buildFunctionNode(node);
|
| - }
|
| -
|
| - int getPropertyField(Name name) {
|
| - return fieldNames.getPropertyField(name);
|
| - }
|
| -
|
| - int getPositionalParameterField(int arity, int position) {
|
| - return fieldNames.getPositionalParameterField(arity, position);
|
| - }
|
| -
|
| - int getNamedParameterField(int arity, String name) {
|
| - return fieldNames.getNamedParameterField(arity, name);
|
| - }
|
| -
|
| - int getReturnField(int arity) {
|
| - return fieldNames.getReturnField(arity);
|
| - }
|
| -
|
| - void buildInstanceValue(Class host) {
|
| - int value = getInstanceValue(host);
|
| - for (Member target in hierarchy.getDispatchTargets(host, setters: false)) {
|
| - var getter = getMemberGetter(host, target);
|
| - constraints.setLoadLocation(value, getPropertyField(target.name), getter);
|
| - }
|
| - for (Member target in hierarchy.getDispatchTargets(host, setters: true)) {
|
| - constraints.setStoreLocation(
|
| - value, getPropertyField(target.name), getMemberSetter(host, target));
|
| - }
|
| - for (Class node = host; node != null; node = node.superclass) {
|
| - for (Procedure procedure in node.mixin.procedures) {
|
| - if (!procedure.isStatic) {
|
| - buildProcedure(host, procedure);
|
| - }
|
| - }
|
| - for (Constructor constructor in node.constructors) {
|
| - buildConstructor(host, constructor);
|
| - }
|
| - }
|
| - // If the object is callable as a function, set up its call handler.
|
| - Member callHandler = hierarchy.getDispatchTarget(host, Names.call_);
|
| - if (callHandler != null) {
|
| - if (callHandler is Procedure && !callHandler.isAccessor) {
|
| - constraints.setLoadLocation(value, fieldNames.callHandlerField,
|
| - getTearOffVariable(host, callHandler));
|
| - } else {
|
| - // Generate `this.[call] = this.call.[call]` where [call] is the
|
| - // call handler field, corresponding to repeatedly reading "call".
|
| - var environment = new TypeEnvironment(this, host, callHandler);
|
| - int getter = getMemberGetter(host, callHandler);
|
| - constraints.setLoadLocation(value, fieldNames.callHandlerField,
|
| - environment.getLoad(getter, fieldNames.callHandlerField));
|
| - }
|
| - }
|
| - }
|
| -
|
| - void buildStaticField(Field field) {
|
| - var environment = new Environment(this, null, field);
|
| - int initializer = field.initializer == null
|
| - ? nullNode
|
| - : new StatementBuilder(this, environment)
|
| - .buildExpression(field.initializer);
|
| - environment.addAssign(initializer, getSharedFieldVariable(field));
|
| - }
|
| -
|
| - void buildProcedure(Class hostClass, Procedure node) {
|
| - if (node.isAbstract) return;
|
| - int host = hostClass == null ? null : getInstanceVariable(hostClass);
|
| - int function = getTearOffVariable(hostClass, node);
|
| - int returnVariable = getReturnVariable(hostClass, node);
|
| - var environment = new Environment(this, hostClass, node,
|
| - returnVariable: returnVariable, thisVariable: host);
|
| - buildFunctionNode(node.function, environment,
|
| - addTypeBasedSummary: node.isExternal, function: function);
|
| - }
|
| -
|
| - int getDeclarationSiteFieldInitializer(Field field) {
|
| - if (field.initializer == null) return nullNode;
|
| - return declarationSiteFieldInitializer[field] ??=
|
| - _makeDeclarationSiteFieldInitializer(field);
|
| - }
|
| -
|
| - int _makeDeclarationSiteFieldInitializer(Field field) {
|
| - final initializerEnvironment = new Environment(this, null, field);
|
| - return new StatementBuilder(this, initializerEnvironment)
|
| - .buildExpression(field.initializer);
|
| - }
|
| -
|
| - void buildConstructor(Class hostClass, Constructor node) {
|
| - int host = getInstanceVariable(hostClass);
|
| - var environment =
|
| - new Environment(this, hostClass, node, thisVariable: host);
|
| - buildFunctionNode(node.function, environment);
|
| - InitializerBuilder builder = new InitializerBuilder(this, environment);
|
| - Set<Field> initializedFields = new Set<Field>();
|
| - for (Initializer initializer in node.initializers) {
|
| - builder.build(initializer);
|
| - if (initializer is FieldInitializer) {
|
| - initializedFields.add(initializer.field);
|
| - }
|
| - }
|
| - for (Field field in node.enclosingClass.mixin.fields) {
|
| - if (field.isInstanceMember) {
|
| - // Note: ensure the initializer is built even if it is not used.
|
| - int initializer = getDeclarationSiteFieldInitializer(field);
|
| - if (!initializedFields.contains(field)) {
|
| - int variable = getFieldVariable(hostClass, field);
|
| - environment.addAssign(initializer, variable);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - /// Builds constraints to model the behavior of the given function.
|
| - ///
|
| - /// If the function is external, [addTypeBasedSummary] should be `true`;
|
| - /// its parameter and return type are then used to model its behavior instead
|
| - /// of the body.
|
| - ///
|
| - /// [function] should be a variable holding the function object itself, if
|
| - /// such an object exists (which is always the case except for constructors,
|
| - /// which currently do have function values).
|
| - void buildFunctionNode(FunctionNode node, Environment environment,
|
| - {int function, bool addTypeBasedSummary: false}) {
|
| - var expressionBuilder =
|
| - new StatementBuilder(this, environment).expressionBuilder;
|
| - int minArity = node.requiredParameterCount;
|
| - int maxArity = node.positionalParameters.length;
|
| - for (int i = 0; i < node.positionalParameters.length; ++i) {
|
| - var parameter = node.positionalParameters[i];
|
| - int variable = getParameterVariable(environment.host, parameter);
|
| - environment.localVariables[parameter] = variable;
|
| - if (function != null) {
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - if (i < arity) {
|
| - environment.addLoad(
|
| - function, getPositionalParameterField(arity, i), variable);
|
| - }
|
| - }
|
| - }
|
| - if (i >= node.requiredParameterCount) {
|
| - int parameterDefault = parameter.initializer == null
|
| - ? nullNode
|
| - : expressionBuilder.build(parameter.initializer);
|
| - environment.addAssign(parameterDefault, variable);
|
| - }
|
| - if (addTypeBasedSummary) {
|
| - buildContravariantType(parameter.type, environment, variable);
|
| - }
|
| - }
|
| - for (int i = 0; i < node.namedParameters.length; ++i) {
|
| - var parameter = node.namedParameters[i];
|
| - int variable = getParameterVariable(environment.host, parameter);
|
| - environment.localVariables[parameter] = variable;
|
| - if (function != null) {
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - environment.addLoad(function,
|
| - getNamedParameterField(arity, parameter.name), variable);
|
| - }
|
| - }
|
| - int parameterDefault = parameter.initializer == null
|
| - ? nullNode
|
| - : expressionBuilder.build(parameter.initializer);
|
| - environment.addAssign(parameterDefault, variable);
|
| - if (addTypeBasedSummary) {
|
| - buildContravariantType(parameter.type, environment, variable);
|
| - }
|
| - }
|
| - if (environment.returnVariable == null) {
|
| - environment.returnVariable = newVariable(node, 'return');
|
| - environment.addSink(
|
| - environment.returnVariable, getSharedReturnVariable(node));
|
| - } else {
|
| - visualizer?.annotateVariable(environment.returnVariable, node, 'return');
|
| - }
|
| - if (function != null) {
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - environment.addStore(
|
| - function, getReturnField(arity), environment.returnVariable);
|
| - }
|
| - }
|
| - if (addTypeBasedSummary) {
|
| - int returnFromType = buildCovariantType(node.returnType, environment);
|
| - environment.addAssign(returnFromType, environment.returnVariable);
|
| - } else if (node.body != null) {
|
| - Completion completes =
|
| - new StatementBuilder(this, environment).build(node.body);
|
| - if (completes == Completion.Maybe) {
|
| - // Null is returned when control falls over the end.
|
| - environment.addAssign(nullNode, environment.returnVariable);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /// Returns true if we can assume that externals treat the given types as
|
| - /// covariant.
|
| - ///
|
| - /// For example, if an external method returns a `List`, the values stored
|
| - /// in the list from user code are not considered escaping.
|
| - bool isAssumedCovariant(Class classNode) {
|
| - return classNode == coreTypes.listClass ||
|
| - classNode == coreTypes.mapClass ||
|
| - classNode == coreTypes.iterableClass ||
|
| - classNode == coreTypes.iteratorClass ||
|
| - classNode == coreTypes.futureClass ||
|
| - classNode == coreTypes.streamClass;
|
| - }
|
| -
|
| - Set<String> _unsupportedNodes = new Set<String>();
|
| -
|
| - int unsupported(Node node) {
|
| - if (verbose && _unsupportedNodes.add('${node.runtimeType}')) {
|
| - print('Unsupported: ${node.runtimeType}');
|
| - }
|
| - return dynamicNode;
|
| - }
|
| -}
|
| -
|
| -/// Generates unique IDs for fields in the constraint system.
|
| -///
|
| -/// We use several fields in the constraint system that do not correspond to
|
| -/// Dart fields. A "field" in this context should be seen as a storage location
|
| -/// that is specific to an instance.
|
| -class FieldNames {
|
| - final TupleCanonicalizer _table = new TupleCanonicalizer();
|
| - static const int _TagName = 1;
|
| - static const int _TagPositionalParameter = 2;
|
| - static const int _TagNamedParameter = 3;
|
| - static const int _TagReturn = 4;
|
| - static const int _TagTypeParameter = 5;
|
| - static const int _TagCallHandler = 6;
|
| -
|
| - /// Field mapping an object to the function value that should be invoked when
|
| - /// the object is called as a function.
|
| - ///
|
| - /// This is the equivalent of repeatedly reading the "call" property of an
|
| - /// object until a function value is found.
|
| - int callHandlerField;
|
| -
|
| - FieldNames() {
|
| - callHandlerField = _table.get1(_TagCallHandler);
|
| - }
|
| -
|
| - /// Field representing the value returned from a getter, passed into a setter,
|
| - /// or stored in a Dart field with the given name.
|
| - int getPropertyField(Name name) {
|
| - return _table.get2(_TagName, name);
|
| - }
|
| -
|
| - /// Field representing the given positional parameter passed to a method
|
| - /// invoked with the given arity.
|
| - int getPositionalParameterField(int arity, int position) {
|
| - return _table.get3(_TagPositionalParameter, arity, position);
|
| - }
|
| -
|
| - /// Field representing the given named parameter passed to a method invoked
|
| - /// with the given arity.
|
| - int getNamedParameterField(int arity, String name) {
|
| - return _table.get3(_TagNamedParameter, arity, name);
|
| - }
|
| -
|
| - /// Field representing the return value of a method invoked the given arity.
|
| - int getReturnField(int arity) {
|
| - return _table.get2(_TagReturn, arity);
|
| - }
|
| -
|
| - /// Field representing the values that would be checked against the given
|
| - /// type parameter in checked mode.
|
| - ///
|
| - /// The type-based modeling of externals uses this to handle types that
|
| - /// involve type variables. Roughly speaking, we assume that a method whose
|
| - /// return type is a type variable T can return any value that was passed into
|
| - /// any parameter of type T. In particular, this is used to model the
|
| - /// external backend storage in collection types.
|
| - ///
|
| - /// This field keeps track of the values that may flow into and out of a
|
| - /// type variable for a given instance.
|
| - int getTypeParameterField(TypeParameter parameter) {
|
| - return _table.get2(_TagTypeParameter, parameter);
|
| - }
|
| -
|
| - int get length => _table.length;
|
| -
|
| - String getDiagnosticNameOfField(int field) {
|
| - List<Object> tuple = _table.getFromIndex(field);
|
| - switch (tuple[0]) {
|
| - case _TagName:
|
| - return '${tuple[1]}';
|
| - case _TagPositionalParameter:
|
| - return 'pos(${tuple[1]},${tuple[2]})';
|
| - case _TagNamedParameter:
|
| - return 'named(${tuple[1]},${tuple[2]})';
|
| - case _TagReturn:
|
| - return 'return(${tuple[1]})';
|
| - case _TagTypeParameter:
|
| - return 'type-param(${tuple[1]})';
|
| - case _TagCallHandler:
|
| - return 'call-handler()';
|
| - default:
|
| - return '!error';
|
| - }
|
| - }
|
| -}
|
| -
|
| -class TypeEnvironment {
|
| - final Builder builder;
|
| - final Class host;
|
| - final Member member;
|
| - int thisVariable;
|
| -
|
| - ConstraintSystem get constraints => builder.constraints;
|
| - Visualizer get visualizer => builder.visualizer;
|
| -
|
| - TypeEnvironment(this.builder, this.host, this.member, {this.thisVariable});
|
| -
|
| - void addAssign(int source, int destination) {
|
| - constraints.addAssign(source, destination);
|
| - visualizer?.annotateAssign(source, destination, member);
|
| - }
|
| -
|
| - int getJoin(int first, int second) {
|
| - // TODO(asgerf): Avoid redundant joins in common cases.
|
| - int joinPoint = constraints.newVariable();
|
| - addAssign(first, joinPoint);
|
| - addAssign(second, joinPoint);
|
| - return joinPoint;
|
| - }
|
| -
|
| - int getLoad(int object, int field) {
|
| - int variable = builder._loads.lookup(object, field);
|
| - if (variable != null) return variable;
|
| - variable = constraints.newVariable();
|
| - constraints.addLoad(object, field, variable);
|
| - visualizer?.annotateLoad(object, field, variable, member);
|
| - builder._loads.put(variable);
|
| - return variable;
|
| - }
|
| -
|
| - void addLoad(int object, int field, int destination) {
|
| - constraints.addLoad(object, field, destination);
|
| - visualizer?.annotateLoad(object, field, destination, member);
|
| - }
|
| -
|
| - int getStore(int object, int field) {
|
| - int variable = builder._stores.lookup(object, field);
|
| - if (variable != null) return variable;
|
| - variable = constraints.newVariable();
|
| - constraints.addStore(object, field, variable);
|
| - visualizer?.annotateStore(object, field, variable, member);
|
| - builder._stores.put(variable);
|
| - return variable;
|
| - }
|
| -
|
| - void addStore(int object, int field, int source) {
|
| - addAssign(source, getStore(object, field));
|
| - }
|
| -
|
| - void addSink(int source, int sink) {
|
| - constraints.addSink(source, sink);
|
| - visualizer?.annotateSink(source, sink, member);
|
| - }
|
| -}
|
| -
|
| -class Environment extends TypeEnvironment {
|
| - final Map<VariableDeclaration, int> localVariables;
|
| - int returnVariable;
|
| -
|
| - Environment(Builder builder, Class host, Member member,
|
| - {int thisVariable, this.returnVariable})
|
| - : localVariables = <VariableDeclaration, int>{},
|
| - super(builder, host, member, thisVariable: thisVariable);
|
| -
|
| - Environment.inner(Environment outer, {this.returnVariable})
|
| - : localVariables = outer.localVariables,
|
| - super(outer.builder, outer.host, outer.member,
|
| - thisVariable: outer.thisVariable);
|
| -
|
| - int getVariable(VariableDeclaration variable) {
|
| - return localVariables[variable] ??= builder.newVariable(variable);
|
| - }
|
| -}
|
| -
|
| -class ExpressionBuilder extends ExpressionVisitor<int> {
|
| - final Builder builder;
|
| - final Environment environment;
|
| - final StatementBuilder statementBuilder;
|
| -
|
| - ConstraintSystem get constraints => builder.constraints;
|
| - Visualizer get visualizer => builder.visualizer;
|
| - FieldNames get fieldNames => builder.fieldNames;
|
| -
|
| - ExpressionBuilder(this.builder, this.statementBuilder, this.environment);
|
| -
|
| - int build(Expression node) {
|
| - int variable = node.accept(this);
|
| - visualizer?.annotateVariable(variable, node);
|
| - return variable;
|
| - }
|
| -
|
| - int unsupported(Expression node) {
|
| - return builder.unsupported(node);
|
| - }
|
| -
|
| - defaultExpression(Expression node) {
|
| - return unsupported(node);
|
| - }
|
| -
|
| - int visitInvalidExpression(InvalidExpression node) {
|
| - return builder.bottomNode;
|
| - }
|
| -
|
| - int visitVariableGet(VariableGet node) {
|
| - return environment.getVariable(node.variable);
|
| - }
|
| -
|
| - int visitVariableSet(VariableSet node) {
|
| - int rightHandSide = build(node.value);
|
| - int variable = environment.getVariable(node.variable);
|
| - environment.addAssign(rightHandSide, variable);
|
| - return rightHandSide;
|
| - }
|
| -
|
| - int visitPropertyGet(PropertyGet node) {
|
| - if (node.receiver is ThisExpression) {
|
| - Class host = environment.host;
|
| - Member target = builder.hierarchy.getDispatchTarget(host, node.name);
|
| - int source = builder.getMemberGetter(host, target);
|
| - return source == null ? builder.bottomNode : source;
|
| - }
|
| - int object = build(node.receiver);
|
| - int field = fieldNames.getPropertyField(node.name);
|
| - return environment.getLoad(object, field);
|
| - }
|
| -
|
| - int visitPropertySet(PropertySet node) {
|
| - int object = build(node.receiver);
|
| - int rightHandSide = build(node.value);
|
| - if (node.receiver is ThisExpression) {
|
| - Class host = environment.host;
|
| - Member target =
|
| - builder.hierarchy.getDispatchTarget(host, node.name, setter: true);
|
| - int destination = builder.getMemberSetter(host, target);
|
| - if (destination != null) {
|
| - environment.addAssign(rightHandSide, destination);
|
| - }
|
| - return rightHandSide;
|
| - }
|
| - int field = fieldNames.getPropertyField(node.name);
|
| - environment.addStore(object, field, rightHandSide);
|
| - return rightHandSide;
|
| - }
|
| -
|
| - int visitDirectPropertyGet(DirectPropertyGet node) {
|
| - return builder.getMemberGetter(environment.host, node.target);
|
| - }
|
| -
|
| - int visitDirectPropertySet(DirectPropertySet node) {
|
| - int rightHandSide = build(node.value);
|
| - int destination = builder.getMemberSetter(environment.host, node.target);
|
| - if (destination != null) {
|
| - environment.addAssign(rightHandSide, destination);
|
| - }
|
| - return rightHandSide;
|
| - }
|
| -
|
| - int visitSuperPropertyGet(SuperPropertyGet node) {
|
| - return unsupported(node);
|
| - }
|
| -
|
| - int visitSuperPropertySet(SuperPropertySet node) {
|
| - build(node.value);
|
| - return unsupported(node);
|
| - }
|
| -
|
| - int visitStaticGet(StaticGet node) {
|
| - return builder.getMemberGetter(null, node.target);
|
| - }
|
| -
|
| - int visitStaticSet(StaticSet node) {
|
| - int rightHandSide = build(node.value);
|
| - int destination = builder.getMemberSetter(null, node.target);
|
| - assert(destination != null); // Static accessors must be valid.
|
| - environment.addAssign(rightHandSide, destination);
|
| - return rightHandSide;
|
| - }
|
| -
|
| - int visitMethodInvocation(MethodInvocation node) {
|
| - // Resolve calls on 'this' directly.
|
| - if (node.receiver is ThisExpression) {
|
| - Class host = environment.host;
|
| - Member target = builder.hierarchy.getDispatchTarget(host, node.name);
|
| - if (target is Procedure && !target.isAccessor) {
|
| - FunctionNode function = target.function;
|
| - passArgumentsToFunction(node.arguments, host, function);
|
| - return builder.getReturnVariable(host, target);
|
| - }
|
| - }
|
| - // Dispatch call dynamically.
|
| - int receiver = build(node.receiver);
|
| - int methodProperty = builder.getPropertyField(node.name);
|
| - int function = node.name.name == 'call'
|
| - ? receiver
|
| - : environment.getLoad(receiver, methodProperty);
|
| - // We have to dispatch through any number of 'call' getters to get to
|
| - // the actual function. The 'call handler' field unfolds all the 'call'
|
| - // getters and refers directly to the actual function (if it exists).
|
| - // TODO(asgerf): When we have strong mode types, skip the 'call handler'
|
| - // load if the static type system resolves the target to a method.
|
| - // It is only needed for getters, fields, and untyped calls.
|
| - int handler = environment.getLoad(function, fieldNames.callHandlerField);
|
| - visualizer?.annotateVariable(function, node, 'function');
|
| - visualizer?.annotateVariable(handler, node, 'call handler');
|
| - int arity = node.arguments.positional.length;
|
| - for (int i = 0; i < node.arguments.positional.length; ++i) {
|
| - int field = builder.getPositionalParameterField(arity, i);
|
| - int argument = build(node.arguments.positional[i]);
|
| - environment.addStore(handler, field, argument);
|
| - }
|
| - for (int i = 0; i < node.arguments.named.length; ++i) {
|
| - NamedExpression namedNode = node.arguments.named[i];
|
| - int field = builder.getNamedParameterField(arity, namedNode.name);
|
| - int argument = build(namedNode.value);
|
| - environment.addStore(handler, field, argument);
|
| - }
|
| - return environment.getLoad(handler, builder.getReturnField(arity));
|
| - }
|
| -
|
| - void passArgumentsToFunction(
|
| - Arguments node, Class host, FunctionNode function) {
|
| - // TODO(asgerf): Check that arity matches (although mismatches are rare).
|
| - for (int i = 0; i < node.positional.length; ++i) {
|
| - int argument = build(node.positional[i]);
|
| - if (i < function.positionalParameters.length) {
|
| - int parameter = builder.getParameterVariable(
|
| - host, function.positionalParameters[i]);
|
| - environment.addAssign(argument, parameter);
|
| - }
|
| - }
|
| - for (int i = 0; i < node.named.length; ++i) {
|
| - NamedExpression namedNode = node.named[i];
|
| - int argument = build(namedNode.value);
|
| - // TODO(asgerf): Avoid the slow lookup for named parameters.
|
| - for (int j = 0; j < function.namedParameters.length; ++j) {
|
| - var namedParameter = function.namedParameters[j];
|
| - if (namedParameter.name == namedNode.name) {
|
| - int parameter = builder.getParameterVariable(host, namedParameter);
|
| - environment.addAssign(argument, parameter);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - int visitDirectMethodInvocation(DirectMethodInvocation node) {
|
| - // TODO(asgerf): Support cases where the receiver is not 'this'.
|
| - passArgumentsToFunction(
|
| - node.arguments, environment.host, node.target.function);
|
| - return builder.getReturnVariable(environment.host, node.target);
|
| - }
|
| -
|
| - int visitSuperMethodInvocation(SuperMethodInvocation node) {
|
| - return unsupported(node);
|
| - }
|
| -
|
| - void passArgumentsNowhere(Arguments node) {
|
| - for (int i = 0; i < node.positional.length; ++i) {
|
| - build(node.positional[i]);
|
| - }
|
| - for (int i = 0; i < node.named.length; ++i) {
|
| - build(node.named[i].value);
|
| - }
|
| - }
|
| -
|
| - int visitStaticInvocation(StaticInvocation node) {
|
| - if (node.target == builder.identicalFunction) {
|
| - // Ignore calls to identical() as they cause a lot of spurious escape.
|
| - passArgumentsNowhere(node.arguments);
|
| - return builder.boolNode;
|
| - }
|
| - passArgumentsToFunction(node.arguments, null, node.target.function);
|
| - return builder.getReturnVariable(null, node.target);
|
| - }
|
| -
|
| - int visitConstructorInvocation(ConstructorInvocation node) {
|
| - Class host = node.target.enclosingClass;
|
| - passArgumentsToFunction(node.arguments, host, node.target.function);
|
| - return builder.getInstanceVariable(host);
|
| - }
|
| -
|
| - int visitNot(Not node) {
|
| - build(node.operand);
|
| - return builder.boolNode;
|
| - }
|
| -
|
| - int visitLogicalExpression(LogicalExpression node) {
|
| - build(node.left);
|
| - build(node.right);
|
| - return builder.boolNode;
|
| - }
|
| -
|
| - int visitConditionalExpression(ConditionalExpression node) {
|
| - build(node.condition);
|
| - int then = build(node.then);
|
| - int otherwise = build(node.otherwise);
|
| - return environment.getJoin(then, otherwise);
|
| - }
|
| -
|
| - int visitStringConcatenation(StringConcatenation node) {
|
| - for (int i = 0; i < node.expressions.length; ++i) {
|
| - build(node.expressions[i]);
|
| - }
|
| - return builder.stringNode;
|
| - }
|
| -
|
| - int visitIsExpression(IsExpression node) {
|
| - build(node.operand);
|
| - return builder.boolNode;
|
| - }
|
| -
|
| - int visitAsExpression(AsExpression node) {
|
| - return build(node.operand);
|
| - }
|
| -
|
| - int visitSymbolLiteral(SymbolLiteral node) {
|
| - return builder.symbolNode;
|
| - }
|
| -
|
| - int visitTypeLiteral(TypeLiteral node) {
|
| - return builder.typeNode;
|
| - }
|
| -
|
| - int visitThisExpression(ThisExpression node) {
|
| - return environment.thisVariable;
|
| - }
|
| -
|
| - int visitRethrow(Rethrow node) {
|
| - return builder.bottomNode;
|
| - }
|
| -
|
| - int visitThrow(Throw node) {
|
| - build(node.expression);
|
| - return builder.bottomNode;
|
| - }
|
| -
|
| - int visitListLiteral(ListLiteral node) {
|
| - var object = builder.listNode;
|
| - TypeParameter parameter = builder.coreTypes.listClass.typeParameters.single;
|
| - int field = fieldNames.getTypeParameterField(parameter);
|
| - for (int i = 0; i < node.expressions.length; ++i) {
|
| - int content = build(node.expressions[i]);
|
| - environment.addStore(object, field, content);
|
| - }
|
| - return object;
|
| - }
|
| -
|
| - int visitMapLiteral(MapLiteral node) {
|
| - var object = builder.mapNode;
|
| - List<TypeParameter> parameters = builder.coreTypes.mapClass.typeParameters;
|
| - int keys = fieldNames.getTypeParameterField(parameters[0]);
|
| - int values = fieldNames.getTypeParameterField(parameters[1]);
|
| - for (int i = 0; i < node.entries.length; ++i) {
|
| - var entry = node.entries[i];
|
| - environment.addStore(object, keys, build(entry.key));
|
| - environment.addStore(object, values, build(entry.value));
|
| - }
|
| - return object;
|
| - }
|
| -
|
| - int visitAwaitExpression(AwaitExpression node) {
|
| - return unsupported(node);
|
| - }
|
| -
|
| - int visitFunctionExpression(FunctionExpression node) {
|
| - return buildInnerFunction(node.function);
|
| - }
|
| -
|
| - int visitStringLiteral(StringLiteral node) {
|
| - return builder.stringNode;
|
| - }
|
| -
|
| - int visitIntLiteral(IntLiteral node) {
|
| - return builder.intNode;
|
| - }
|
| -
|
| - int visitDoubleLiteral(DoubleLiteral node) {
|
| - return builder.doubleNode;
|
| - }
|
| -
|
| - int visitBoolLiteral(BoolLiteral node) {
|
| - return builder.boolNode;
|
| - }
|
| -
|
| - int visitNullLiteral(NullLiteral node) {
|
| - return builder.nullNode;
|
| - }
|
| -
|
| - int visitLet(Let node) {
|
| - environment.localVariables[node.variable] =
|
| - build(node.variable.initializer);
|
| - return build(node.body);
|
| - }
|
| -
|
| - int buildInnerFunction(FunctionNode node, {VariableDeclaration self}) {
|
| - int variable = builder.newFunction(node);
|
| - if (self != null) {
|
| - assert(!environment.localVariables.containsKey(self));
|
| - environment.localVariables[self] = variable;
|
| - }
|
| - Environment inner = new Environment.inner(environment);
|
| - builder.buildFunctionNode(node, inner, function: variable);
|
| - return variable;
|
| - }
|
| -}
|
| -
|
| -/// Indicates whether a statement can complete normally.
|
| -enum Completion {
|
| - /// The statement might complete normally.
|
| - Maybe,
|
| -
|
| - /// The statement never completes normally, because it throws, returns,
|
| - /// breaks, loops forever, etc.
|
| - Never,
|
| -}
|
| -
|
| -Completion neverCompleteIf(bool condition) {
|
| - return condition ? Completion.Never : Completion.Maybe;
|
| -}
|
| -
|
| -Completion completeIfBoth(Completion first, Completion second) {
|
| - return first == Completion.Maybe && second == Completion.Maybe
|
| - ? Completion.Maybe
|
| - : Completion.Never;
|
| -}
|
| -
|
| -Completion completeIfEither(Completion first, Completion second) {
|
| - return first == Completion.Maybe || second == Completion.Maybe
|
| - ? Completion.Maybe
|
| - : Completion.Never;
|
| -}
|
| -
|
| -bool _isTrueConstant(Expression node) {
|
| - return node is BoolLiteral && node.value == true;
|
| -}
|
| -
|
| -bool _isThrowing(Expression node) {
|
| - return node is Throw || node is Rethrow;
|
| -}
|
| -
|
| -/// Translates a statement to constraints.
|
| -///
|
| -/// The visit methods return a [Completion] indicating if the statement can
|
| -/// complete normally. This is used to check if null can be returned due to
|
| -/// control falling over the end of the method.
|
| -class StatementBuilder extends StatementVisitor<Completion> {
|
| - final Builder builder;
|
| - final Environment environment;
|
| - ExpressionBuilder expressionBuilder;
|
| -
|
| - ConstraintSystem get constraints => builder.constraints;
|
| - Visualizer get visualizer => builder.visualizer;
|
| - FieldNames get names => builder.fieldNames;
|
| -
|
| - StatementBuilder(this.builder, this.environment) {
|
| - expressionBuilder = new ExpressionBuilder(builder, this, environment);
|
| - }
|
| -
|
| - Completion build(Statement node) => node.accept(this);
|
| -
|
| - Completion buildOptional(Statement node) {
|
| - return node != null ? node.accept(this) : Completion.Maybe;
|
| - }
|
| -
|
| - int buildExpression(Expression node) {
|
| - return expressionBuilder.build(node);
|
| - }
|
| -
|
| - void unsupported(Statement node) {
|
| - builder.unsupported(node);
|
| - }
|
| -
|
| - Completion visitInvalidStatement(InvalidStatement node) => Completion.Never;
|
| -
|
| - visitExpressionStatement(ExpressionStatement node) {
|
| - buildExpression(node.expression);
|
| - return neverCompleteIf(_isThrowing(node.expression));
|
| - }
|
| -
|
| - visitBlock(Block node) {
|
| - for (int i = 0; i < node.statements.length; ++i) {
|
| - if (build(node.statements[i]) == Completion.Never) {
|
| - return Completion.Never;
|
| - }
|
| - }
|
| - return Completion.Maybe;
|
| - }
|
| -
|
| - visitEmptyStatement(EmptyStatement node) => Completion.Maybe;
|
| -
|
| - visitAssertStatement(AssertStatement node) {
|
| - unsupported(node);
|
| - return Completion.Maybe;
|
| - }
|
| -
|
| - visitLabeledStatement(LabeledStatement node) {
|
| - build(node.body);
|
| - // We don't track reachability of breaks in the body, so just assume we
|
| - // might hit a break.
|
| - return Completion.Maybe;
|
| - }
|
| -
|
| - visitBreakStatement(BreakStatement node) => Completion.Never;
|
| -
|
| - visitWhileStatement(WhileStatement node) {
|
| - buildExpression(node.condition);
|
| - build(node.body);
|
| - return neverCompleteIf(_isTrueConstant(node.condition));
|
| - }
|
| -
|
| - visitDoStatement(DoStatement node) {
|
| - build(node.body);
|
| - buildExpression(node.condition);
|
| - return neverCompleteIf(_isTrueConstant(node.condition));
|
| - }
|
| -
|
| - visitForStatement(ForStatement node) {
|
| - for (int i = 0; i < node.variables.length; ++i) {
|
| - build(node.variables[i]);
|
| - }
|
| - if (node.condition != null) {
|
| - buildExpression(node.condition);
|
| - }
|
| - for (int i = 0; i < node.updates.length; ++i) {
|
| - buildExpression(node.updates[i]);
|
| - }
|
| - build(node.body);
|
| - return neverCompleteIf(_isTrueConstant(node.condition));
|
| - }
|
| -
|
| - visitForInStatement(ForInStatement node) {
|
| - int iterable = buildExpression(node.iterable);
|
| - int iterator = environment.getLoad(iterable, builder.iteratorField);
|
| - int current = environment.getLoad(iterator, builder.currentField);
|
| - int variable = environment.getVariable(node.variable);
|
| - environment.addAssign(current, variable);
|
| - build(node.body);
|
| - return Completion.Maybe;
|
| - }
|
| -
|
| - visitSwitchStatement(SwitchStatement node) {
|
| - buildExpression(node.expression);
|
| - Completion lastCanComplete = Completion.Maybe;
|
| - for (int i = 0; i < node.cases.length; ++i) {
|
| - // There is no need to visit the expression since constants cannot
|
| - // have side effects.
|
| - // Note that only the last case can actually fall out of the switch,
|
| - // as the others will throw an exception if they fall through.
|
| - // Also note that breaks from the switch have been desugared to breaks
|
| - // to a [LabeledStatement].
|
| - lastCanComplete = build(node.cases[i].body);
|
| - }
|
| - return lastCanComplete;
|
| - }
|
| -
|
| - visitContinueSwitchStatement(ContinueSwitchStatement node) {
|
| - return Completion.Never;
|
| - }
|
| -
|
| - visitIfStatement(IfStatement node) {
|
| - buildExpression(node.condition);
|
| - Completion thenCompletes = build(node.then);
|
| - Completion elseCompletes = buildOptional(node.otherwise);
|
| - return completeIfEither(thenCompletes, elseCompletes);
|
| - }
|
| -
|
| - visitReturnStatement(ReturnStatement node) {
|
| - if (node.expression != null) {
|
| - int returned = buildExpression(node.expression);
|
| - environment.addAssign(returned, environment.returnVariable);
|
| - }
|
| - return Completion.Never;
|
| - }
|
| -
|
| - visitTryCatch(TryCatch node) {
|
| - Completion bodyCompletes = build(node.body);
|
| - Completion catchCompletes = Completion.Never;
|
| - for (int i = 0; i < node.catches.length; ++i) {
|
| - Catch catchNode = node.catches[i];
|
| - if (catchNode.exception != null) {
|
| - environment.localVariables[catchNode.exception] = builder.dynamicNode;
|
| - }
|
| - if (catchNode.stackTrace != null) {
|
| - environment.localVariables[catchNode.stackTrace] = builder.dynamicNode;
|
| - }
|
| - if (build(catchNode.body) == Completion.Maybe) {
|
| - catchCompletes = Completion.Maybe;
|
| - }
|
| - }
|
| - return completeIfEither(bodyCompletes, catchCompletes);
|
| - }
|
| -
|
| - visitTryFinally(TryFinally node) {
|
| - Completion bodyCompletes = build(node.body);
|
| - Completion finalizerCompletes = build(node.finalizer);
|
| - return completeIfBoth(bodyCompletes, finalizerCompletes);
|
| - }
|
| -
|
| - visitYieldStatement(YieldStatement node) {
|
| - unsupported(node);
|
| - return Completion.Maybe;
|
| - }
|
| -
|
| - visitVariableDeclaration(VariableDeclaration node) {
|
| - int initializer = node.initializer == null
|
| - ? builder.nullNode
|
| - : buildExpression(node.initializer);
|
| - int variable = environment.getVariable(node);
|
| - environment.addAssign(initializer, variable);
|
| - return neverCompleteIf(_isThrowing(node.initializer));
|
| - }
|
| -
|
| - visitFunctionDeclaration(FunctionDeclaration node) {
|
| - expressionBuilder.buildInnerFunction(node.function, self: node.variable);
|
| - return Completion.Maybe;
|
| - }
|
| -}
|
| -
|
| -class InitializerBuilder extends InitializerVisitor<Null> {
|
| - final Builder builder;
|
| - final Environment environment;
|
| - ExpressionBuilder expressionBuilder;
|
| -
|
| - FieldNames get fieldNames => builder.fieldNames;
|
| -
|
| - InitializerBuilder(this.builder, this.environment) {
|
| - expressionBuilder =
|
| - new StatementBuilder(builder, environment).expressionBuilder;
|
| - }
|
| -
|
| - void build(Initializer node) {
|
| - node.accept(this);
|
| - }
|
| -
|
| - int buildExpression(Expression node) {
|
| - return expressionBuilder.build(node);
|
| - }
|
| -
|
| - visitInvalidInitializer(InvalidInitializer node) {}
|
| -
|
| - visitFieldInitializer(FieldInitializer node) {
|
| - int fieldVariable = builder.getFieldVariable(environment.host, node.field);
|
| - int rightHandSide = buildExpression(node.value);
|
| - environment.addAssign(rightHandSide, fieldVariable);
|
| - }
|
| -
|
| - visitSuperInitializer(SuperInitializer node) {
|
| - expressionBuilder.passArgumentsToFunction(
|
| - node.arguments, environment.host, node.target.function);
|
| - }
|
| -
|
| - visitRedirectingInitializer(RedirectingInitializer node) {
|
| - expressionBuilder.passArgumentsToFunction(
|
| - node.arguments, environment.host, node.target.function);
|
| - }
|
| -
|
| - visitLocalInitializer(LocalInitializer node) {
|
| - environment.localVariables[node.variable] =
|
| - buildExpression(node.variable.initializer);
|
| - }
|
| -}
|
| -
|
| -class Names {
|
| - static final Name current = new Name('current');
|
| - static final Name iterator = new Name('iterator');
|
| - static final Name then = new Name('then');
|
| - static final Name call_ = new Name('call');
|
| -}
|
| -
|
| -/// Returns a variable with the possible values of a given type, as provided
|
| -/// by external code.
|
| -class CovariantExternalTypeVisitor extends DartTypeVisitor<int> {
|
| - final Builder builder;
|
| - final TypeEnvironment environment;
|
| -
|
| - FieldNames get fieldNames => builder.fieldNames;
|
| -
|
| - CovariantExternalTypeVisitor(this.builder, this.environment);
|
| -
|
| - void visitContravariant(DartType type, int input) {
|
| - return new ContravariantExternalTypeVisitor(builder, environment, input)
|
| - .visit(type);
|
| - }
|
| -
|
| - int visit(DartType type) => type.accept(this);
|
| -
|
| - int visitInvalidType(InvalidType node) {
|
| - return builder.bottomNode;
|
| - }
|
| -
|
| - int visitDynamicType(DynamicType node) {
|
| - return builder.dynamicNode;
|
| - }
|
| -
|
| - int visitVoidType(VoidType node) {
|
| - return builder.nullNode;
|
| - }
|
| -
|
| - int visitVectorType(VectorType node) {
|
| - throw "Internal error: CovariantExternalTypeVisitor encountered VectorType "
|
| - "in native method signature";
|
| - }
|
| -
|
| - int visitInterfaceType(InterfaceType node) {
|
| - int object = builder.getExternalInstanceVariable(node.classNode);
|
| - for (int i = 0; i < node.typeArguments.length; ++i) {
|
| - int field =
|
| - fieldNames.getTypeParameterField(node.classNode.typeParameters[i]);
|
| - int outputValue = visit(node.typeArguments[i]);
|
| - environment.addStore(object, field, outputValue);
|
| - if (!builder.isAssumedCovariant(node.classNode)) {
|
| - int userValue = environment.getLoad(object, field);
|
| - visitContravariant(node.typeArguments[i], userValue);
|
| - }
|
| - }
|
| - return object;
|
| - }
|
| -
|
| - int visitTypeParameterType(TypeParameterType node) {
|
| - if (node.parameter.parent is Class) {
|
| - assert(environment.thisVariable != null);
|
| - return environment.getLoad(environment.thisVariable,
|
| - fieldNames.getTypeParameterField(node.parameter));
|
| - } else {
|
| - return builder.getFunctionTypeParameterVariable(node.parameter);
|
| - }
|
| - }
|
| -
|
| - int visitFunctionType(FunctionType node) {
|
| - // TODO: Handle arity range.
|
| - int arity = node.positionalParameters.length;
|
| - int function = builder.functionValueNode;
|
| - for (int i = 0; i < node.positionalParameters.length; ++i) {
|
| - int field = fieldNames.getPositionalParameterField(arity, i);
|
| - int argument = environment.getLoad(function, field);
|
| - visitContravariant(node.positionalParameters[i], argument);
|
| - }
|
| - for (int i = 0; i < node.namedParameters.length; ++i) {
|
| - var parameter = node.namedParameters[i];
|
| - int field = fieldNames.getNamedParameterField(arity, parameter.name);
|
| - int argument = environment.getLoad(function, field);
|
| - visitContravariant(parameter.type, argument);
|
| - }
|
| - int returnVariable = visit(node.returnType);
|
| - environment.addStore(
|
| - function, fieldNames.getReturnField(arity), returnVariable);
|
| - return function;
|
| - }
|
| -
|
| - /// Equivalent to visiting the FunctionType for the given function.
|
| - int buildFunctionNode(FunctionNode node) {
|
| - int minArity = node.requiredParameterCount;
|
| - int maxArity = node.positionalParameters.length;
|
| - Member member = node.parent is Member ? node.parent : null;
|
| - int function = builder.newFunction(node, member);
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - for (int i = 0; i < arity; ++i) {
|
| - int field = fieldNames.getPositionalParameterField(arity, i);
|
| - int argument = environment.getLoad(function, field);
|
| - visitContravariant(node.positionalParameters[i].type, argument);
|
| - }
|
| - }
|
| - for (int i = 0; i < node.namedParameters.length; ++i) {
|
| - VariableDeclaration variable = node.namedParameters[i];
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int field = fieldNames.getNamedParameterField(arity, variable.name);
|
| - int argument = environment.getLoad(function, field);
|
| - visitContravariant(variable.type, argument);
|
| - }
|
| - }
|
| - int returnVariable = visit(node.returnType);
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - environment.addStore(
|
| - function, fieldNames.getReturnField(arity), returnVariable);
|
| - }
|
| - return function;
|
| - }
|
| -}
|
| -
|
| -/// Generates constraints to model the behavior of a value escaping into
|
| -/// external code through a given type.
|
| -class ContravariantExternalTypeVisitor extends DartTypeVisitor<Null> {
|
| - final Builder builder;
|
| - final TypeEnvironment environment;
|
| - final int input;
|
| -
|
| - FieldNames get fieldNames => builder.fieldNames;
|
| - ConstraintSystem get constraints => builder.constraints;
|
| -
|
| - ContravariantExternalTypeVisitor(this.builder, this.environment, this.input);
|
| -
|
| - void visit(DartType type) {
|
| - type.accept(this);
|
| - }
|
| -
|
| - void visitContravariant(DartType type, int input) {
|
| - return new ContravariantExternalTypeVisitor(builder, environment, input)
|
| - .visit(type);
|
| - }
|
| -
|
| - int visitCovariant(DartType type) {
|
| - return new CovariantExternalTypeVisitor(builder, environment).visit(type);
|
| - }
|
| -
|
| - visitInvalidType(InvalidType node) {}
|
| -
|
| - visitDynamicType(DynamicType node) {}
|
| -
|
| - visitVoidType(VoidType node) {}
|
| -
|
| - visitVectorType(VectorType node) {}
|
| -
|
| - visitInterfaceType(InterfaceType node) {
|
| - int escapePoint = builder.getInterfaceEscapeVariable(node.classNode);
|
| - environment.addAssign(input, escapePoint);
|
| - }
|
| -
|
| - visitTypeParameterType(TypeParameterType node) {
|
| - if (node.parameter.parent is Class) {
|
| - assert(environment.thisVariable != null);
|
| - environment.addStore(environment.thisVariable,
|
| - fieldNames.getTypeParameterField(node.parameter), input);
|
| - } else {
|
| - environment.addAssign(
|
| - input, builder.getFunctionTypeParameterVariable(node.parameter));
|
| - }
|
| - }
|
| -
|
| - visitFunctionType(FunctionType node) {
|
| - int minArity = node.requiredParameterCount;
|
| - int maxArity = node.positionalParameters.length;
|
| - for (int i = 0; i < node.positionalParameters.length; ++i) {
|
| - int argument = visitCovariant(node.positionalParameters[i]);
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int field = fieldNames.getPositionalParameterField(arity, i);
|
| - environment.addStore(input, field, argument);
|
| - }
|
| - }
|
| - for (var parameter in node.namedParameters) {
|
| - int argument = visitCovariant(parameter.type);
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int field = fieldNames.getNamedParameterField(arity, parameter.name);
|
| - environment.addStore(input, field, argument);
|
| - }
|
| - }
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int returnLocation =
|
| - environment.getLoad(input, fieldNames.getReturnField(arity));
|
| - visitContravariant(node.returnType, returnLocation);
|
| - }
|
| - }
|
| -
|
| - /// Equivalent to visiting the FunctionType for the given function.
|
| - void buildFunctionNode(FunctionNode node) {
|
| - int minArity = node.requiredParameterCount;
|
| - int maxArity = node.positionalParameters.length;
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - for (int i = 0; i < arity; ++i) {
|
| - int argument = visitCovariant(node.positionalParameters[i].type);
|
| - int field = fieldNames.getPositionalParameterField(arity, i);
|
| - environment.addStore(input, field, argument);
|
| - }
|
| - }
|
| - for (int i = 0; i < node.namedParameters.length; ++i) {
|
| - VariableDeclaration variable = node.namedParameters[i];
|
| - int argument = visitCovariant(variable.type);
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int field = fieldNames.getNamedParameterField(arity, variable.name);
|
| - environment.addStore(input, field, argument);
|
| - }
|
| - }
|
| - for (int arity = minArity; arity <= maxArity; ++arity) {
|
| - int returnLocation =
|
| - environment.getLoad(input, fieldNames.getReturnField(arity));
|
| - visitContravariant(node.returnType, returnLocation);
|
| - }
|
| - }
|
| -}
|
|
|