blob: 07d745fcb575779776ed05b88820c0416c0a923b [file] [log] [blame]
// Copyright (c) 2017, 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 '../../compiler_new.dart';
import '../closure.dart';
import '../common.dart';
import '../common_elements.dart';
import '../common/names.dart';
import '../compiler.dart';
import '../constants/values.dart';
import '../elements/entities.dart';
import '../elements/types.dart';
import '../js_backend/inferred_data.dart';
import '../js_backend/no_such_method_registry.dart';
import '../js_emitter/sorter.dart';
import '../js_model/locals.dart';
import '../kernel/element_map.dart';
import '../options.dart';
import '../types/abstract_value_domain.dart';
import '../types/types.dart';
import '../world.dart';
import 'builder_kernel.dart';
import 'inferrer_engine.dart';
import 'type_graph_inferrer.dart';
import 'type_graph_nodes.dart';
import 'type_system.dart';
class KernelTypeGraphInferrer extends TypeGraphInferrer<ir.Node> {
final Compiler _compiler;
final KernelToElementMapForBuilding _elementMap;
final GlobalLocalsMap _globalLocalsMap;
final ClosureDataLookup<ir.Node> _closureDataLookup;
final InferredDataBuilder _inferredDataBuilder;
KernelTypeGraphInferrer(
this._compiler,
this._elementMap,
this._globalLocalsMap,
this._closureDataLookup,
JClosedWorld closedWorld,
this._inferredDataBuilder,
{bool disableTypeInference: false})
: super(closedWorld, disableTypeInference: disableTypeInference);
@override
InferrerEngine<ir.Node> createInferrerEngineFor(FunctionEntity main) {
return new KernelInferrerEngine(
_compiler.options,
_compiler.progress,
_compiler.reporter,
_compiler.outputProvider,
_elementMap,
_globalLocalsMap,
_closureDataLookup,
closedWorld,
_compiler.backend.noSuchMethodRegistry,
main,
_compiler.backendStrategy.sorter,
_inferredDataBuilder);
}
@override
GlobalTypeInferenceResults createResults() {
return new KernelGlobalTypeInferenceResults(this, closedWorld);
}
}
class KernelGlobalTypeInferenceResults
extends GlobalTypeInferenceResults<ir.Node> {
KernelGlobalTypeInferenceResults(
TypesInferrer<ir.Node> inferrer, JClosedWorld closedWorld)
: super(inferrer, closedWorld);
GlobalTypeInferenceMemberResult<ir.Node> createMemberResult(
TypeGraphInferrer<ir.Node> inferrer, MemberEntity member,
{bool isJsInterop: false}) {
return new GlobalTypeInferenceMemberResultImpl<ir.Node>(
member,
// We store data in the context of the enclosing method, even
// for closure elements.
inferrer.inferrer.lookupDataOfMember(member),
inferrer,
isJsInterop);
}
GlobalTypeInferenceParameterResult<ir.Node> createParameterResult(
TypeGraphInferrer<ir.Node> inferrer, Local parameter) {
return new GlobalTypeInferenceParameterResultImpl<ir.Node>(
parameter, inferrer);
}
}
class KernelInferrerEngine extends InferrerEngineImpl<ir.Node> {
final KernelToElementMapForBuilding _elementMap;
final GlobalLocalsMap _globalLocalsMap;
final ClosureDataLookup<ir.Node> _closureDataLookup;
KernelInferrerEngine(
CompilerOptions options,
Progress progress,
DiagnosticReporter reporter,
CompilerOutput compilerOutput,
this._elementMap,
this._globalLocalsMap,
this._closureDataLookup,
JClosedWorld closedWorld,
NoSuchMethodRegistry noSuchMethodRegistry,
FunctionEntity mainElement,
Sorter sorter,
InferredDataBuilder inferredDataBuilder)
: super(
options,
progress,
reporter,
compilerOutput,
closedWorld,
noSuchMethodRegistry,
mainElement,
sorter,
inferredDataBuilder,
new KernelTypeSystemStrategy(
_elementMap, _globalLocalsMap, _closureDataLookup));
ElementEnvironment get _elementEnvironment => _elementMap.elementEnvironment;
@override
ConstantValue getFieldConstant(FieldEntity field) {
return _elementMap.getFieldConstantValue(field);
}
@override
bool isFieldInitializerPotentiallyNull(
FieldEntity field, ir.Node initializer) {
// TODO(13429): We could do better here by using the
// constant handler to figure out if it's a lazy field or not.
// TODO(johnniwinther): Implement the ad-hoc check in ast inferrer? This
// mimicks that ast inferrer which return `true` for [ast.Send] and
// non-const [ast.NewExpression].
if (initializer is ir.MethodInvocation ||
initializer is ir.PropertyGet ||
initializer is ir.PropertySet ||
initializer is ir.StaticInvocation ||
initializer is ir.StaticGet ||
initializer is ir.StaticSet ||
initializer is ir.Let ||
initializer is ir.ConstructorInvocation && !initializer.isConst) {
return true;
}
return false;
}
@override
TypeInformation computeMemberTypeInformation(
MemberEntity member, ir.Node body) {
KernelTypeGraphBuilder visitor = new KernelTypeGraphBuilder(
options,
closedWorld,
_closureDataLookup,
this,
member,
body,
_elementMap,
_globalLocalsMap.getLocalsMap(member));
return visitor.run();
}
@override
FunctionEntity lookupCallMethod(ClassEntity cls) {
FunctionEntity function =
_elementEnvironment.lookupClassMember(cls, Identifiers.call);
if (function == null || function.isAbstract) {
function =
_elementEnvironment.lookupClassMember(cls, Identifiers.noSuchMethod_);
}
return function;
}
@override
ir.Node computeMemberBody(MemberEntity member) {
MemberDefinition definition = _elementMap.getMemberDefinition(member);
switch (definition.kind) {
case MemberKind.regular:
ir.Member node = definition.node;
if (node is ir.Field) {
return getFieldInitializer(_elementMap, member);
} else if (node is ir.Procedure) {
return node.function;
}
break;
case MemberKind.constructor:
return definition.node;
case MemberKind.constructorBody:
ir.Member node = definition.node;
if (node is ir.Constructor) {
return node.function;
} else if (node is ir.Procedure) {
return node.function;
}
break;
case MemberKind.closureCall:
ir.TreeNode node = definition.node;
if (node is ir.FunctionDeclaration) {
return node.function;
} else if (node is ir.FunctionExpression) {
return node.function;
}
break;
case MemberKind.closureField:
case MemberKind.signature:
case MemberKind.generatorBody:
break;
}
failedAt(member, 'Unexpected member definition: $definition.');
return null;
}
@override
int computeMemberSize(MemberEntity member) {
// TODO(johnniwinther): Find an ordering that can be shared between the
// front ends.
return 0;
}
@override
GlobalTypeInferenceElementData<ir.Node> createElementData() {
return new KernelGlobalTypeInferenceElementData();
}
@override
bool hasCallType(ClassEntity cls) {
return _elementMap.types
.getCallType(_elementMap.elementEnvironment.getThisType(cls)) !=
null;
}
}
class KernelTypeSystemStrategy implements TypeSystemStrategy<ir.Node> {
KernelToElementMapForBuilding _elementMap;
GlobalLocalsMap _globalLocalsMap;
ClosureDataLookup<ir.Node> _closureDataLookup;
KernelTypeSystemStrategy(
this._elementMap, this._globalLocalsMap, this._closureDataLookup);
ElementEnvironment get _elementEnvironment => _elementMap.elementEnvironment;
@override
bool checkClassEntity(ClassEntity cls) => true;
@override
bool checkMapNode(ir.Node node) => true;
@override
bool checkListNode(ir.Node node) => true;
@override
bool checkLoopPhiNode(ir.Node node) => true;
@override
bool checkPhiNode(ir.Node node) => true;
@override
void forEachParameter(FunctionEntity function, void f(Local parameter)) {
forEachOrderedParameter(_globalLocalsMap, _elementMap, function, f);
}
@override
ParameterTypeInformation createParameterTypeInformation(
AbstractValueDomain abstractValueDomain,
covariant JLocal parameter,
TypeSystem<ir.Node> types) {
MemberEntity context = parameter.memberContext;
KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(context);
ir.FunctionNode functionNode =
localsMap.getFunctionNodeForParameter(parameter);
DartType type = localsMap.getLocalType(_elementMap, parameter);
MemberEntity member;
bool isClosure = false;
if (functionNode.parent is ir.Member) {
member = _elementMap.getMember(functionNode.parent);
} else if (functionNode.parent is ir.FunctionExpression ||
functionNode.parent is ir.FunctionDeclaration) {
ClosureRepresentationInfo info =
_closureDataLookup.getClosureInfo(functionNode.parent);
member = info.callMethod;
isClosure = true;
}
MemberTypeInformation memberTypeInformation =
types.getInferredTypeOfMember(member);
if (isClosure) {
return new ParameterTypeInformation.localFunction(
abstractValueDomain, memberTypeInformation, parameter, type, member);
} else if (member.isInstanceMember) {
return new ParameterTypeInformation.instanceMember(
abstractValueDomain,
memberTypeInformation,
parameter,
type,
member,
new ParameterAssignments());
} else {
return new ParameterTypeInformation.static(
abstractValueDomain, memberTypeInformation, parameter, type, member);
}
}
@override
MemberTypeInformation createMemberTypeInformation(
AbstractValueDomain abstractValueDomain, MemberEntity member) {
if (member.isField) {
FieldEntity field = member;
DartType type = _elementEnvironment.getFieldType(field);
return new FieldTypeInformation(abstractValueDomain, field, type);
} else if (member.isGetter) {
FunctionEntity getter = member;
DartType type = _elementEnvironment.getFunctionType(getter);
return new GetterTypeInformation(abstractValueDomain, getter, type);
} else if (member.isSetter) {
FunctionEntity setter = member;
return new SetterTypeInformation(abstractValueDomain, setter);
} else if (member.isFunction) {
FunctionEntity method = member;
DartType type = _elementEnvironment.getFunctionType(method);
return new MethodTypeInformation(abstractValueDomain, method, type);
} else {
ConstructorEntity constructor = member;
if (constructor.isFactoryConstructor) {
DartType type = _elementEnvironment.getFunctionType(constructor);
return new FactoryConstructorTypeInformation(
abstractValueDomain, constructor, type);
} else {
return new GenerativeConstructorTypeInformation(
abstractValueDomain, constructor);
}
}
}
}
class KernelGlobalTypeInferenceElementData
extends GlobalTypeInferenceElementData<ir.Node> {
// TODO(johnniwinther): Rename this together with [typeOfSend].
Map<ir.Node, AbstractValue> _sendMap;
Map<ir.ForInStatement, AbstractValue> _iteratorMap;
Map<ir.ForInStatement, AbstractValue> _currentMap;
Map<ir.ForInStatement, AbstractValue> _moveNextMap;
@override
AbstractValue typeOfSend(ir.Node node) {
if (_sendMap == null) return null;
return _sendMap[node];
}
@override
void setCurrentTypeMask(
covariant ir.ForInStatement node, AbstractValue mask) {
_currentMap ??= <ir.ForInStatement, AbstractValue>{};
_currentMap[node] = mask;
}
@override
void setMoveNextTypeMask(
covariant ir.ForInStatement node, AbstractValue mask) {
_moveNextMap ??= <ir.ForInStatement, AbstractValue>{};
_moveNextMap[node] = mask;
}
@override
void setIteratorTypeMask(
covariant ir.ForInStatement node, AbstractValue mask) {
_iteratorMap ??= <ir.ForInStatement, AbstractValue>{};
_iteratorMap[node] = mask;
}
@override
AbstractValue typeOfIteratorCurrent(covariant ir.ForInStatement node) {
if (_currentMap == null) return null;
return _currentMap[node];
}
@override
AbstractValue typeOfIteratorMoveNext(covariant ir.ForInStatement node) {
if (_moveNextMap == null) return null;
return _moveNextMap[node];
}
@override
AbstractValue typeOfIterator(covariant ir.ForInStatement node) {
if (_iteratorMap == null) return null;
return _iteratorMap[node];
}
@override
void setTypeMask(ir.Node node, AbstractValue mask) {
_sendMap ??= <ir.Node, AbstractValue>{};
_sendMap[node] = mask;
}
@override
AbstractValue typeOfGetter(ir.Node node) {
if (_sendMap == null) return null;
return _sendMap[node];
}
}
/// Returns the initializer for [field].
///
/// If [field] is an instance field with a null literal initializer `null` is
/// returned, otherwise the initializer of the [ir.Field] is returned.
ir.Node getFieldInitializer(
KernelToElementMapForBuilding elementMap, FieldEntity field) {
MemberDefinition definition = elementMap.getMemberDefinition(field);
ir.Field node = definition.node;
if (node.isInstanceMember &&
!node.isFinal &&
node.initializer is ir.NullLiteral) {
return null;
}
return node.initializer;
}