Update RTI optimization to handle type literal uses

... for generic methods and local function. Also update capture
conversion to recognize the new kinds of type variables.

Change-Id: I9b8771b0ebe6c9e926712fa31b0bdea966cc6a25
Reviewed-on: https://dart-review.googlesource.com/34600
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Emily Fortuna <efortuna@google.com>
This commit is contained in:
Johnni Winther 2018-01-17 15:12:55 +00:00
parent b2f8b5481d
commit 73e35eb035
14 changed files with 573 additions and 187 deletions

View file

@ -179,16 +179,18 @@ class JavaScriptImpactTransformer extends ImpactTransformer {
_customElementsResolutionAnalysis.registerTypeLiteral(type);
if (type.isTypeVariable) {
TypeVariableType typeVariable = type;
if (typeVariable.element.typeDeclaration is ClassEntity) {
// GENERIC_METHODS: The `is!` test above filters away method type
// variables, because they have the value `dynamic` with the
// incomplete support for generic methods offered with
// '--generic-method-syntax'. This must be revised in order to
// support generic methods fully.
ClassEntity cls = typeVariable.element.typeDeclaration;
_rtiNeedBuilder.registerClassUsingTypeVariableExpression(cls);
registerImpact(_impacts.typeVariableExpression);
Entity typeDeclaration = typeVariable.element.typeDeclaration;
if (typeDeclaration is ClassEntity) {
_rtiNeedBuilder
.registerClassUsingTypeVariableLiteral(typeDeclaration);
} else if (typeDeclaration is FunctionEntity) {
_rtiNeedBuilder
.registerMethodUsingTypeVariableLiteral(typeDeclaration);
} else if (typeDeclaration is Local) {
_rtiNeedBuilder.registerLocalFunctionUsingTypeVariableLiteral(
typeDeclaration);
}
registerImpact(_impacts.typeVariableExpression);
}
hasTypeLiteral = true;
break;

View file

@ -95,7 +95,7 @@ abstract class RuntimeTypesNeed {
// TODO(redemption): Remove this when the old frontend is deleted.
bool localFunctionNeedsSignature(Local localFunction);
bool classUsesTypeVariableExpression(ClassEntity cls);
bool classUsesTypeVariableLiteral(ClassEntity cls);
}
class TrivialRuntimeTypesNeed implements RuntimeTypesNeed {
@ -105,7 +105,7 @@ class TrivialRuntimeTypesNeed implements RuntimeTypesNeed {
bool classNeedsTypeArguments(ClassEntity cls) => true;
@override
bool classUsesTypeVariableExpression(ClassEntity cls) => true;
bool classUsesTypeVariableLiteral(ClassEntity cls) => true;
@override
bool localFunctionNeedsSignature(Local localFunction) => true;
@ -125,8 +125,15 @@ class TrivialRuntimeTypesNeed implements RuntimeTypesNeed {
/// Interface for computing classes and methods that need runtime types.
abstract class RuntimeTypesNeedBuilder {
/// Registers that [cls] contains a type variable literal.
void registerClassUsingTypeVariableExpression(ClassEntity cls);
/// Registers that [cls] uses one of its type variables as a literal.
void registerClassUsingTypeVariableLiteral(ClassEntity cls);
/// Registers that [method] uses one of its type variables as a literal.
void registerMethodUsingTypeVariableLiteral(FunctionEntity method);
/// Registers that [localFunction] uses one of its type variables as a
/// literal.
void registerLocalFunctionUsingTypeVariableLiteral(Local localFunction);
/// Registers that if [element] needs type arguments at runtime then so does
/// [dependency].
@ -163,7 +170,13 @@ class TrivialRuntimeTypesNeedBuilder implements RuntimeTypesNeedBuilder {
const TrivialRuntimeTypesNeedBuilder();
@override
void registerClassUsingTypeVariableExpression(ClassEntity cls) {}
void registerClassUsingTypeVariableLiteral(ClassEntity cls) {}
@override
void registerMethodUsingTypeVariableLiteral(FunctionEntity method) {}
@override
void registerLocalFunctionUsingTypeVariableLiteral(Local localFunction) {}
@override
RuntimeTypesNeed computeRuntimeTypesNeed(
@ -526,9 +539,8 @@ class RuntimeTypesNeedImpl implements RuntimeTypesNeed {
final Set<Local> localFunctionsNeedingSignature;
final Set<Local> localFunctionsNeedingTypeArguments;
/// The set of classes that use one of their type variables as expressions
/// to get the runtime type.
final Set<ClassEntity> classesUsingTypeVariableExpression;
/// The set of classes that use one of their type variables as literals.
final Set<ClassEntity> classesUsingTypeVariableLiterals;
RuntimeTypesNeedImpl(
this._elementEnvironment,
@ -538,7 +550,7 @@ class RuntimeTypesNeedImpl implements RuntimeTypesNeed {
this.methodsNeedingTypeArguments,
this.localFunctionsNeedingSignature,
this.localFunctionsNeedingTypeArguments,
this.classesUsingTypeVariableExpression);
this.classesUsingTypeVariableLiterals);
bool checkClass(covariant ClassEntity cls) => true;
@ -580,8 +592,8 @@ class RuntimeTypesNeedImpl implements RuntimeTypesNeed {
}
@override
bool classUsesTypeVariableExpression(ClassEntity cls) {
return classesUsingTypeVariableExpression.contains(cls);
bool classUsesTypeVariableLiteral(ClassEntity cls) {
return classesUsingTypeVariableLiterals.contains(cls);
}
}
@ -589,19 +601,19 @@ class _ResolutionRuntimeTypesNeed extends RuntimeTypesNeedImpl {
_ResolutionRuntimeTypesNeed(
ElementEnvironment elementEnvironment,
BackendUsage backendUsage,
Set<ClassEntity> classesNeedingRti,
Set<FunctionEntity> methodsNeedingRti,
Set<FunctionEntity> methodsNeedingGenericRti,
Set<Local> localFunctionsNeedingRti,
Set<ClassEntity> classesNeedingTypeArguments,
Set<FunctionEntity> methodsNeedingSignature,
Set<FunctionEntity> methodsNeedingTypeArguments,
Set<Local> localFunctionsNeedingSignature,
Set<Local> localFunctionsNeedingTypeArguments,
Set<ClassEntity> classesUsingTypeVariableExpression)
: super(
elementEnvironment,
backendUsage,
classesNeedingRti,
methodsNeedingRti,
methodsNeedingGenericRti,
localFunctionsNeedingRti,
classesNeedingTypeArguments,
methodsNeedingSignature,
methodsNeedingTypeArguments,
localFunctionsNeedingSignature,
localFunctionsNeedingTypeArguments,
classesUsingTypeVariableExpression);
@ -615,9 +627,14 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
final Map<Entity, Set<Entity>> typeArgumentDependencies =
<Entity, Set<Entity>>{};
final Set<ClassEntity> classesUsingTypeVariableExpression =
final Set<ClassEntity> classesUsingTypeVariableLiterals =
new Set<ClassEntity>();
final Set<FunctionEntity> methodsUsingTypeVariableLiterals =
new Set<FunctionEntity>();
final Set<Local> localFunctionsUsingTypeVariableLiterals = new Set<Local>();
final Set<ClassEntity> classesUsingTypeVariableTests = new Set<ClassEntity>();
Set<DartType> isChecks;
@ -630,8 +647,18 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
bool checkClass(covariant ClassEntity cls) => true;
@override
void registerClassUsingTypeVariableExpression(ClassEntity cls) {
classesUsingTypeVariableExpression.add(cls);
void registerClassUsingTypeVariableLiteral(ClassEntity cls) {
classesUsingTypeVariableLiterals.add(cls);
}
@override
void registerMethodUsingTypeVariableLiteral(FunctionEntity method) {
methodsUsingTypeVariableLiterals.add(method);
}
@override
void registerLocalFunctionUsingTypeVariableLiteral(Local localFunction) {
localFunctionsUsingTypeVariableLiterals.add(localFunction);
}
@override
@ -771,9 +798,12 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
checkClosures();
}
// Add the classes that need RTI because they use a type variable as
// expression.
classesUsingTypeVariableExpression.forEach(potentiallyNeedTypeArguments);
// Add the classes, methods and local functions that need type arguments
// because they use a type variable as a literal.
classesUsingTypeVariableLiterals.forEach(potentiallyNeedTypeArguments);
methodsUsingTypeVariableLiterals.forEach(potentiallyNeedTypeArguments);
localFunctionsUsingTypeVariableLiterals
.forEach(potentiallyNeedTypeArguments);
return _createRuntimeTypesNeed(
_elementEnvironment,
@ -783,7 +813,7 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
methodsNeedingTypeArguments,
localFunctionsNeedingSignature,
localFunctionsNeedingTypeArguments,
classesUsingTypeVariableExpression);
classesUsingTypeVariableLiterals);
}
RuntimeTypesNeed _createRuntimeTypesNeed(

View file

@ -331,7 +331,7 @@ class RuntimeTypeGenerator {
if (generated.contains(superclass)) return;
if (classesUsingTypeVariableTests.contains(superclass) ||
_rtiNeed.classUsesTypeVariableExpression(superclass) ||
_rtiNeed.classUsesTypeVariableLiteral(superclass) ||
checkedClasses.contains(superclass)) {
// Generate substitution. If no substitution is necessary, emit
// `null` to overwrite a (possibly) existing substitution from the

View file

@ -42,9 +42,8 @@ class KernelClosureAnalysis {
hasThisLocal = !constructor.isFactoryConstructor;
}
ScopeModel model = new ScopeModel();
CapturedScopeBuilder translator = new CapturedScopeBuilder(model,
hasThisLocal: hasThisLocal,
addTypeChecks: options.enableTypeAssertions);
CapturedScopeBuilder translator =
new CapturedScopeBuilder(model, options, hasThisLocal: hasThisLocal);
if (entity.isField) {
if (node is ir.Field && node.initializer != null) {
node.accept(translator);
@ -94,12 +93,8 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
/// with the stated type.
final bool _addTypeChecks;
/// If true, disable the optimization of trimming out passing extra
/// information for RTI checks.
final bool _disableRtiOptimization;
KernelClosureConversionTask(Measurer measurer, this._elementMap,
this._globalLocalsMap, this._addTypeChecks, this._disableRtiOptimization)
this._globalLocalsMap, this._addTypeChecks)
: super(measurer);
/// The combined steps of generating our intermediate representation of
@ -115,19 +110,41 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
void _updateScopeBasedOnRtiNeed(
KernelScopeInfo scope,
ir.Node node,
Set<ir.Node> localFunctionsNeedingRti,
Iterable<ClassEntity> classesNeedingRti,
bool Function(ir.Node) localFunctionNeedsSignature,
bool Function(ClassEntity) classNeedsTypeArguments,
bool Function(MemberEntity) methodNeedsTypeArguments,
bool Function(ir.Node) localFunctionNeedsTypeArguments,
MemberEntity outermostEntity) {
if (localFunctionsNeedingRti.contains(node) ||
classesNeedingRti.contains(outermostEntity.enclosingClass) ||
_addTypeChecks ||
_disableRtiOptimization) {
if (outermostEntity is FunctionEntity &&
outermostEntity is! ConstructorEntity) {
scope.thisUsedAsFreeVariable = scope.thisUsedAsFreeVariableIfNeedsRti ||
scope.thisUsedAsFreeVariable;
} else {
scope.freeVariables.addAll(scope.freeVariablesForRti);
if (scope.thisUsedAsFreeVariableIfNeedsRti &&
classNeedsTypeArguments(outermostEntity.enclosingClass)) {
scope.thisUsedAsFreeVariable = true;
}
if (_addTypeChecks) {
scope.freeVariables.addAll(scope.freeVariablesForRti);
} else {
for (TypeVariableTypeWithContext typeVariable
in scope.freeVariablesForRti) {
switch (typeVariable.kind) {
case TypeVariableKind.cls:
if (classNeedsTypeArguments(
_elementMap.getClass(typeVariable.typeDeclaration))) {
scope.freeVariables.add(typeVariable);
}
break;
case TypeVariableKind.method:
if (methodNeedsTypeArguments(
_elementMap.getMember(typeVariable.typeDeclaration))) {
scope.freeVariables.add(typeVariable);
}
break;
case TypeVariableKind.local:
if (localFunctionNeedsTypeArguments(typeVariable.typeDeclaration)) {
scope.freeVariables.add(typeVariable);
}
break;
case TypeVariableKind.function:
break;
}
}
}
}
@ -135,8 +152,10 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
Iterable<FunctionEntity> createClosureEntities(
JsClosedWorldBuilder closedWorldBuilder,
Map<MemberEntity, ScopeModel> closureModels,
Set<ir.Node> localFunctionsNeedingRti,
Iterable<ClassEntity> classesNeedingRti) {
{bool Function(ir.Node) localFunctionNeedsSignature,
bool Function(ClassEntity) classNeedsTypeArguments,
bool Function(FunctionEntity) methodNeedsTypeArguments,
bool Function(ir.Node) localFunctionNeedsTypeArguments}) {
List<FunctionEntity> callMethods = <FunctionEntity>[];
closureModels.forEach((MemberEntity member, ScopeModel model) {
KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member);
@ -150,7 +169,13 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
Map<Local, JRecordField> boxedVariables =
_elementMap.makeRecordContainer(scope, member, localsMap);
_updateScopeBasedOnRtiNeed(
scope, node, localFunctionsNeedingRti, classesNeedingRti, member);
scope,
node,
localFunctionNeedsSignature,
classNeedsTypeArguments,
methodNeedsTypeArguments,
localFunctionNeedsTypeArguments,
member);
if (scope is KernelCapturedLoopScope) {
_capturedScopesMap[node] = new JsCapturedLoopScope.from(
@ -179,8 +204,10 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
functionNode,
closuresToGenerate[node],
allBoxedVariables,
localFunctionsNeedingRti,
classesNeedingRti);
localFunctionNeedsSignature,
classNeedsTypeArguments,
methodNeedsTypeArguments,
localFunctionNeedsTypeArguments);
// Add also for the call method.
_scopeMap[closureClassInfo.callMethod] = closureClassInfo;
_scopeMap[closureClassInfo.signatureMethod] = closureClassInfo;
@ -202,10 +229,18 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
ir.FunctionNode node,
KernelScopeInfo info,
Map<Local, JRecordField> boxedVariables,
Set<ir.Node> localFunctionsNeedingRti,
Iterable<ClassEntity> classesNeedingRti) {
bool Function(ir.Node) localFunctionNeedsSignature,
bool Function(ClassEntity) classNeedsTypeArguments,
bool Function(FunctionEntity) methodNeedsTypeArguments,
bool Function(ir.Node) localFunctionNeedsTypeArguments) {
_updateScopeBasedOnRtiNeed(
info, node.parent, localFunctionsNeedingRti, classesNeedingRti, member);
info,
node.parent,
localFunctionNeedsSignature,
classNeedsTypeArguments,
methodNeedsTypeArguments,
localFunctionNeedsTypeArguments,
member);
KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member);
KernelClosureClassInfo closureClassInfo =
closedWorldBuilder.buildClosureClass(
@ -300,8 +335,8 @@ class KernelScopeInfo {
/// freeVariables set. Whether these variables are actually used as
/// freeVariables will be set by the time this structure is converted to a
/// JsScopeInfo, so JsScopeInfo does not need to use them.
Set<TypeParameterTypeWithContext> freeVariablesForRti =
new Set<TypeParameterTypeWithContext>();
Set<TypeVariableTypeWithContext> freeVariablesForRti =
new Set<TypeVariableTypeWithContext>();
/// If true, `this` is used as a free variable, in this scope. It is stored
/// separately from [freeVariables] because there is no single
@ -347,10 +382,10 @@ class KernelScopeInfo {
Local _getLocal(ir.Node variable, KernelToLocalsMap localsMap,
KernelToElementMap elementMap) {
assert(variable is ir.VariableDeclaration ||
variable is TypeParameterTypeWithContext);
variable is TypeVariableTypeWithContext);
if (variable is ir.VariableDeclaration) {
return localsMap.getLocalVariable(variable);
} else if (variable is TypeParameterTypeWithContext) {
} else if (variable is TypeVariableTypeWithContext) {
return localsMap.getLocalTypeVariable(variable.type, elementMap);
}
throw new ArgumentError('Only know how to get/create locals for '
@ -407,7 +442,7 @@ class KernelCapturedScope extends KernelScopeInfo {
Set<ir.VariableDeclaration> localsUsedInTryOrSync,
Set<ir.Node /* VariableDeclaration | TypeParameterTypeWithContext */ >
freeVariables,
Set<TypeParameterTypeWithContext> freeVariablesForRti,
Set<TypeVariableTypeWithContext> freeVariablesForRti,
bool thisUsedAsFreeVariable,
bool thisUsedAsFreeVariableIfNeedsRti,
bool hasThisLocal)
@ -449,7 +484,7 @@ class KernelCapturedLoopScope extends KernelCapturedScope {
Set<ir.VariableDeclaration> localsUsedInTryOrSync,
Set<ir.Node /* VariableDeclaration | TypeParameterTypeWithContext */ >
freeVariables,
Set<TypeParameterTypeWithContext> freeVariablesForRti,
Set<TypeVariableTypeWithContext> freeVariablesForRti,
bool thisUsedAsFreeVariable,
bool thisUsedAsFreeVariableIfNeedsRti,
bool hasThisLocal)
@ -785,27 +820,78 @@ class ScopeModel {
<ir.TreeNode, KernelScopeInfo>{};
}
enum TypeVariableKind { cls, method, local, function }
/// A fake ir.Node that holds the TypeParameterType as well as the context in
/// which it occurs.
class TypeParameterTypeWithContext implements ir.Node {
final ir.Node memberContext;
class TypeVariableTypeWithContext implements ir.Node {
final ir.Node context;
final ir.TypeParameterType type;
TypeParameterTypeWithContext(this.type, this.memberContext);
final TypeVariableKind kind;
final ir.TreeNode typeDeclaration;
factory TypeVariableTypeWithContext(
ir.TypeParameterType type, ir.Member memberContext) {
TypeVariableKind kind;
ir.TreeNode context = memberContext;
ir.TreeNode typeDeclaration = type.parameter.parent;
if (typeDeclaration == null) {
// We have a function type variable, like `T` in `void Function<T>(int)`.
kind = TypeVariableKind.function;
} else if (typeDeclaration is ir.Class) {
// We have a class type variable, like `T` in `class Class<T> { ... }`.
kind = TypeVariableKind.cls;
} else if (typeDeclaration.parent is ir.Member) {
ir.Member member = typeDeclaration.parent;
if (member is ir.Constructor ||
(member is ir.Procedure && member.isFactory)) {
// We have a synthesized generic method type variable for a class type
// variable.
// TODO(johnniwinther): Handle constructor/factory type variables as
// method type variables.
kind = TypeVariableKind.cls;
typeDeclaration = member.enclosingClass;
} else {
// We have a generic method type variable, like `T` in
// `m<T>() { ... }`.
kind = TypeVariableKind.method;
typeDeclaration = typeDeclaration.parent;
context = typeDeclaration;
}
} else {
// We have a generic local function type variable, like `T` in
// `m() { local<T>() { ... } ... }`.
assert(
typeDeclaration.parent is ir.FunctionExpression ||
typeDeclaration.parent is ir.FunctionDeclaration,
"Unexpected type declaration: $typeDeclaration");
kind = TypeVariableKind.local;
typeDeclaration = typeDeclaration.parent;
context = typeDeclaration;
}
return new TypeVariableTypeWithContext.internal(
type, context, kind, typeDeclaration);
}
TypeVariableTypeWithContext.internal(
this.type, this.context, this.kind, this.typeDeclaration);
accept(ir.Visitor v) {
throw new UnsupportedError('TypeParameterTypeWithContext.accept');
throw new UnsupportedError('TypeVariableTypeWithContext.accept');
}
visitChildren(ir.Visitor v) {
throw new UnsupportedError('TypeParameterTypeWithContext.visitChildren');
throw new UnsupportedError('TypeVariableTypeWithContext.visitChildren');
}
int get hashCode => type.hashCode;
bool operator ==(other) {
if (other is! TypeParameterTypeWithContext) return false;
return type == other.type && memberContext == other.memberContext;
if (other is! TypeVariableTypeWithContext) return false;
return type == other.type && context == other.context;
}
String toString() => 'TypeParameterTypeWithContext $type $memberContext';
String toString() =>
'TypeVariableTypeWithContext(type=$type,context=$context,'
'kind=$kind,typeDeclaration=$typeDeclaration)';
}

View file

@ -5,6 +5,7 @@
import 'package:kernel/ast.dart' as ir;
import '../closure.dart';
import '../options.dart';
import 'closure.dart';
/// This builder walks the code to determine what variables are captured/free at
@ -13,6 +14,8 @@ import 'closure.dart';
class CapturedScopeBuilder extends ir.Visitor {
ScopeModel _model;
CompilerOptions _options;
/// A map of each visited call node with the associated information about what
/// variables are captured/used. Each ir.Node key corresponds to a scope that
/// was encountered while visiting a closure (initially called through
@ -61,17 +64,16 @@ class CapturedScopeBuilder extends ir.Visitor {
final bool _hasThisLocal;
/// If true add type assetions to assert that at runtime the type is in line
/// with the stated type.
final bool _addTypeChecks;
/// Keeps track of the number of boxes that we've created so that they each
/// have unique names.
int _boxCounter = 0;
CapturedScopeBuilder(this._model, {bool hasThisLocal, bool addTypeChecks})
: this._hasThisLocal = hasThisLocal,
this._addTypeChecks = addTypeChecks;
CapturedScopeBuilder(this._model, this._options, {bool hasThisLocal})
: this._hasThisLocal = hasThisLocal;
/// If true add type assetions to assert that at runtime the type is in line
/// with the stated type.
bool get _addTypeChecks => _options.enableTypeAssertions;
/// Update the [CapturedScope] object corresponding to
/// this node if any variables are captured.
@ -202,7 +204,7 @@ class CapturedScopeBuilder extends ir.Visitor {
ir.Node /* VariableDeclaration | TypeParameterTypeWithContext */ variable,
{bool onlyForRtiChecks = false}) {
assert(variable is ir.VariableDeclaration ||
variable is TypeParameterTypeWithContext);
variable is TypeVariableTypeWithContext);
if (_isInsideClosure && !_inCurrentContext(variable)) {
// If the element is not declared in the current function and the element
// is not the closure itself we need to mark the element as free variable.
@ -230,11 +232,15 @@ class CapturedScopeBuilder extends ir.Visitor {
void visitTypeParameter(ir.TypeParameter typeParameter) {
if (_addTypeChecks) {
ir.TreeNode context = _executableContext;
TypeVariableTypeWithContext typeVariable =
new TypeVariableTypeWithContext(
new ir.TypeParameterType(typeParameter),
typeParameter.parent.parent);
if (_isInsideClosure && context is ir.Procedure && context.isFactory) {
// This is a closure in a factory constructor. Since there is no
// [:this:], we have to mark the type arguments as free variables to
// capture them in the closure.
_useTypeVariableAsLocal(new ir.TypeParameterType(typeParameter));
_useTypeVariableAsLocal(typeVariable);
}
if (_executableContext is ir.Member && _executableContext is! ir.Field) {
@ -247,7 +253,7 @@ class CapturedScopeBuilder extends ir.Visitor {
if (_hasThisLocal) {
_registerNeedsThis();
} else {
_useTypeVariableAsLocal(new ir.TypeParameterType(typeParameter));
_useTypeVariableAsLocal(typeVariable);
}
}
}
@ -415,9 +421,9 @@ class CapturedScopeBuilder extends ir.Visitor {
/// context.
bool _inCurrentContext(ir.Node variable) {
assert(variable is ir.VariableDeclaration ||
variable is TypeParameterTypeWithContext);
if (variable is TypeParameterTypeWithContext) {
return variable.memberContext == _executableContext;
variable is TypeVariableTypeWithContext);
if (variable is TypeVariableTypeWithContext) {
return variable.context == _executableContext;
}
ir.TreeNode node = variable;
while (node != _outermostNode && node != _executableContext) {
@ -453,16 +459,13 @@ class CapturedScopeBuilder extends ir.Visitor {
@override
visitTypeParameterType(ir.TypeParameterType type) {
if (_outermostNode is ir.Member) {
ir.Member outermostMember = _outermostNode;
if (_isFieldOrConstructor(_outermostNode)) {
_useTypeVariableAsLocal(type, onlyForRtiChecks: true);
} else if (type.parameter.parent is ir.FunctionNode) {
// This is a function type parameter reference: foo<T>(...) {...}
_useTypeVariableAsLocal(type, onlyForRtiChecks: true);
} else if (outermostMember.isInstanceMember) {
_registerNeedsThis(onlyIfNeedsRti: true);
}
_analyzeTypeVariable(type);
}
@override
visitTypeLiteral(ir.TypeLiteral node) {
if (node.type is ir.TypeParameterType) {
_analyzeTypeVariable(node.type, onlyIfNeedsRti: false);
}
}
@ -473,11 +476,44 @@ class CapturedScopeBuilder extends ir.Visitor {
node is ir.Field ||
(node is ir.Procedure && node.isFactory);
void _analyzeTypeVariable(ir.TypeParameterType type,
{bool onlyIfNeedsRti: true}) {
if (_outermostNode is ir.Member) {
TypeVariableTypeWithContext typeVariable =
new TypeVariableTypeWithContext(type, _outermostNode);
switch (typeVariable.kind) {
case TypeVariableKind.cls:
if (_isFieldOrConstructor(_outermostNode)) {
// Class type variable used in a field or constructor.
_useTypeVariableAsLocal(typeVariable,
onlyForRtiChecks: onlyIfNeedsRti);
} else {
// Class type variable used in a method.
_registerNeedsThis(onlyIfNeedsRti: onlyIfNeedsRti);
}
break;
case TypeVariableKind.method:
case TypeVariableKind.local:
_useTypeVariableAsLocal(typeVariable,
onlyForRtiChecks: onlyIfNeedsRti);
break;
case TypeVariableKind.function:
// The type variable is a function type variable, like `T` in
//
// List<void Function<T>(T)> list;
//
// which doesn't correspond to a captured local variable.
}
}
}
/// If [onlyForRtiChecks] is true, the variable will be added to a list
/// indicating it *may* be used only if runtime type information is checked.
void _useTypeVariableAsLocal(ir.TypeParameterType type,
{bool onlyForRtiChecks = false}) {
_markVariableAsUsed(new TypeParameterTypeWithContext(type, _outermostNode),
onlyForRtiChecks: onlyForRtiChecks);
void _useTypeVariableAsLocal(TypeVariableTypeWithContext typeVariable,
{bool onlyForRtiChecks: false}) {
if (typeVariable.kind != TypeVariableKind.cls && !_options.strongMode) {
return;
}
_markVariableAsUsed(typeVariable, onlyForRtiChecks: onlyForRtiChecks);
}
}

View file

@ -9,7 +9,6 @@ import '../elements/entities.dart';
import '../elements/names.dart';
import '../elements/types.dart';
import '../kernel/indexed.dart';
import 'closure.dart' show KernelClosureClassInfo;
/// Map from 'frontend' to 'backend' elements.
///
@ -522,17 +521,11 @@ class JField extends JMember implements FieldEntity, IndexedField {
}
class JClosureCallMethod extends JMethod {
JClosureCallMethod(KernelClosureClassInfo closureClassInfo,
JClosureCallMethod(ClassEntity enclosingClass,
ParameterStructure parameterStructure, AsyncMarker asyncMarker)
: super(
closureClassInfo.closureClassEntity.library,
closureClassInfo.closureClassEntity,
Names.call,
parameterStructure,
asyncMarker,
isStatic: false,
isExternal: false,
isAbstract: false);
: super(enclosingClass.library, enclosingClass, Names.call,
parameterStructure, asyncMarker,
isStatic: false, isExternal: false, isAbstract: false);
String get _kind => 'closure_call';
}

View file

@ -73,12 +73,8 @@ class JsBackendStrategy implements KernelBackendStrategy {
_compiler.reporter, _compiler.environment, strategy.elementMap);
_elementEnvironment = _elementMap.elementEnvironment;
_commonElements = _elementMap.commonElements;
_closureDataLookup = new KernelClosureConversionTask(
_compiler.measurer,
_elementMap,
_globalLocalsMap,
_compiler.options.enableTypeAssertions,
_compiler.options.disableRtiOptimization);
_closureDataLookup = new KernelClosureConversionTask(_compiler.measurer,
_elementMap, _globalLocalsMap, _compiler.options.enableTypeAssertions);
JsClosedWorldBuilder closedWorldBuilder = new JsClosedWorldBuilder(
_elementMap, _closureDataLookup, _compiler.options);
return closedWorldBuilder._convertClosedWorld(
@ -278,10 +274,11 @@ class JsClosedWorldBuilder {
if (_options.disableRtiOptimization) {
rtiNeed = new TrivialRuntimeTypesNeed();
callMethods = _closureConversionTask.createClosureEntities(
this,
map.toBackendMemberMap(closureModels, identity),
new Set<ir.Node>(),
_classSets.keys.toList());
this, map.toBackendMemberMap(closureModels, identity),
localFunctionNeedsSignature: (_) => true,
classNeedsTypeArguments: (_) => true,
methodNeedsTypeArguments: (_) => true,
localFunctionNeedsTypeArguments: (_) => true);
} else {
RuntimeTypesNeedImpl kernelRtiNeed = closedWorld.rtiNeed;
Set<ir.Node> localFunctionsNodesNeedingSignature = new Set<ir.Node>();
@ -298,11 +295,17 @@ class JsClosedWorldBuilder {
Set<ClassEntity> classesNeedingTypeArguments =
map.toBackendClassSet(kernelRtiNeed.classesNeedingTypeArguments);
Set<FunctionEntity> methodsNeedingTypeArguments =
map.toBackendFunctionSet(kernelRtiNeed.methodsNeedingTypeArguments);
callMethods = _closureConversionTask.createClosureEntities(
this,
map.toBackendMemberMap(closureModels, identity),
localFunctionsNodesNeedingSignature,
classesNeedingTypeArguments);
this, map.toBackendMemberMap(closureModels, identity),
localFunctionNeedsSignature:
localFunctionsNodesNeedingSignature.contains,
classNeedsTypeArguments: classesNeedingTypeArguments.contains,
methodNeedsTypeArguments: methodsNeedingTypeArguments.contains,
localFunctionNeedsTypeArguments:
localFunctionsNodesNeedingTypeArguments.contains);
List<FunctionEntity> callMethodsNeedingSignature = <FunctionEntity>[];
for (ir.Node node in localFunctionsNodesNeedingSignature) {
@ -321,7 +324,8 @@ class JsClosedWorldBuilder {
kernelRtiNeed,
callMethodsNeedingSignature,
callMethodsNeedingTypeArguments,
classesNeedingTypeArguments);
classesNeedingTypeArguments,
methodsNeedingTypeArguments);
}
NoSuchMethodDataImpl oldNoSuchMethodData = closedWorld.noSuchMethodData;
@ -497,15 +501,14 @@ class JsClosedWorldBuilder {
RuntimeTypesNeedImpl rtiNeed,
List<FunctionEntity> callMethodsNeedingSignature,
List<FunctionEntity> callMethodsNeedingTypeArguments,
Set<ClassEntity> classesNeedingTypeArguments) {
Set<ClassEntity> classesNeedingTypeArguments,
Set<FunctionEntity> methodsNeedingTypeArguments) {
Set<FunctionEntity> methodsNeedingSignature =
map.toBackendFunctionSet(rtiNeed.methodsNeedingSignature);
methodsNeedingSignature.addAll(callMethodsNeedingSignature);
Set<FunctionEntity> methodsNeedingTypeArguments =
map.toBackendFunctionSet(rtiNeed.methodsNeedingTypeArguments);
methodsNeedingTypeArguments.addAll(callMethodsNeedingTypeArguments);
Set<ClassEntity> classesUsingTypeVariableExpression =
map.toBackendClassSet(rtiNeed.classesUsingTypeVariableExpression);
map.toBackendClassSet(rtiNeed.classesUsingTypeVariableLiterals);
return new RuntimeTypesNeedImpl(
_elementEnvironment,
backendUsage,

View file

@ -2439,6 +2439,22 @@ class JsKernelToElementMap extends KernelToElementMapBase
closureEntity = new JLocal('', localsMap.currentMember);
}
FunctionEntity callMethod = new JClosureCallMethod(
classEntity, _getParameterStructure(node), getAsyncMarker(node));
_nestedClosureMap
.putIfAbsent(member, () => <FunctionEntity>[])
.add(callMethod);
// We need create the type variable here - before we try to make local
// variables from them (in `JsScopeInfo.from` called through
// `KernelClosureClassInfo.fromScopeInfo` below).
int index = 0;
for (ir.TypeParameter typeParameter in node.typeParameters) {
_typeVariableMap[typeParameter] = _typeVariables.register(
createTypeVariable(callMethod, typeParameter.name, index),
new TypeVariableData(typeParameter));
index++;
}
KernelClosureClassInfo closureClassInfo =
new KernelClosureClassInfo.fromScopeInfo(
classEntity,
@ -2457,18 +2473,6 @@ class JsKernelToElementMap extends KernelToElementMapBase
memberThisType, location, typeVariableAccess);
}
FunctionEntity callMethod = new JClosureCallMethod(
closureClassInfo, _getParameterStructure(node), getAsyncMarker(node));
_nestedClosureMap
.putIfAbsent(member, () => <FunctionEntity>[])
.add(callMethod);
int index = 0;
for (ir.TypeParameter typeParameter in node.typeParameters) {
_typeVariableMap[typeParameter] = _typeVariables.register(
createTypeVariable(callMethod, typeParameter.name, index),
new TypeVariableData(typeParameter));
index++;
}
_members.register<IndexedFunction, FunctionData>(
callMethod,
new ClosureFunctionData(
@ -2548,7 +2552,7 @@ class JsKernelToElementMap extends KernelToElementMapBase
fieldNumber);
fieldNumber++;
}
} else if (variable is TypeParameterTypeWithContext) {
} else if (variable is TypeVariableTypeWithContext) {
_constructClosureField(
localsMap.getLocalTypeVariable(variable.type, this),
closureClassInfo,

View file

@ -5,7 +5,6 @@
import 'dart:io' hide Link;
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/closure.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/common.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
@ -28,13 +27,7 @@ main(List<String> args) {
asyncTest(() async {
Directory dataDir = new Directory.fromUri(Platform.script.resolve('data'));
await checkTests(dataDir, computeClosureData, computeKernelClosureData,
skipForKernel: skipForKernel,
options: [
Flags.disableTypeInference,
// TODO(redemption): Enable inlining.
Flags.disableInlining
],
args: args);
skipForKernel: skipForKernel, args: args);
});
}

View file

@ -0,0 +1,140 @@
// 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.
class Class1<T> {
/*element: Class1.field:hasThis*/
var field = /*fields=[T],free=[T],hasThis*/ () => T;
/*element: Class1.funcField:hasThis*/
Function funcField;
/*element: Class1.:hasThis*/
Class1() {
field = /*fields=[T],free=[T],hasThis*/ () => T;
}
/*element: Class1.setFunc:hasThis*/
Class1.setFunc(this.funcField);
/*element: Class1.fact:*/
factory Class1.fact() => new Class1<T>();
/*element: Class1.fact2:*/
factory Class1.fact2() =>
new Class1.setFunc(/*fields=[T],free=[T]*/ () => new Set<T>());
/*element: Class1.method1:hasThis*/
method1() => T;
/*element: Class1.method2:hasThis*/
method2() {
return /*fields=[this],free=[this],hasThis*/ () => T;
}
/*element: Class1.method3:hasThis*/
method3<S>() => S;
/*element: Class1.method4:hasThis*/
method4<S>() {
return /*fields=[S],free=[S],hasThis*/ () => S;
}
/*element: Class1.method5:hasThis*/
method5() {
/*hasThis*/ local<S>() {
return /*fields=[S],free=[S],hasThis*/ () => S;
}
return local<double>();
}
/*element: Class1.method6:hasThis*/
method6<S>() {
/*fields=[S],free=[S],hasThis*/ local<U>() {
return /*fields=[S,U],free=[S,U],hasThis*/ () => '$S$U';
}
var local2 = /*fields=[S,this],free=[S,this],hasThis*/ (o) {
return /*fields=[S,this],free=[S,this],hasThis*/ () => new Map<T, S>();
};
return local2(local<double>());
}
/*element: Class1.staticMethod1:*/
static staticMethod1<S>() => S;
/*element: Class1.staticMethod2:*/
static staticMethod2<S>() {
return /*fields=[S],free=[S]*/ () => S;
}
/*element: Class1.staticMethod3:*/
static staticMethod3() {
local<S>() {
return /*fields=[S],free=[S]*/ () => S;
}
return local<double>();
}
/*element: Class1.staticMethod4:*/
static staticMethod4<S>() {
/*fields=[S],free=[S]*/ local<U>() {
return /*fields=[S,U],free=[S,U]*/ () => '$S$U';
}
var local2 = /*fields=[S],free=[S]*/ (o) {
return /*fields=[S],free=[S]*/ () => new Set<S>();
};
return local2(local<double>());
}
}
/*element: topLevelMethod1:*/
topLevelMethod1<S>() => S;
/*element: topLevelMethod2:*/
topLevelMethod2<S>() {
return /*fields=[S],free=[S]*/ () => S;
}
/*element: topLevelMethod3:*/
topLevelMethod3() {
local<S>() {
return /*fields=[S],free=[S]*/ () => S;
}
return local<double>();
}
/*element: topLevelMethod4:*/
topLevelMethod4<S>() {
/*fields=[S],free=[S]*/ local<U>() {
return /*fields=[S,U],free=[S,U]*/ () => '$S$U';
}
var local2 = /*fields=[S],free=[S]*/ (o) {
return /*fields=[S],free=[S]*/ () => new Set<S>();
};
return local2(local<double>());
}
/*element: main:*/
main() {
new Class1<int>().method1();
new Class1<int>.fact().method2();
new Class1<int>.fact2().funcField() is Set;
new Class1<int>().method3<double>();
new Class1<int>().method4<double>();
new Class1<int>().method5();
new Class1<int>().method6<double>();
Class1.staticMethod1<double>();
Class1.staticMethod2<double>();
Class1.staticMethod3();
Class1.staticMethod4<double>();
topLevelMethod1<double>();
topLevelMethod2<double>();
topLevelMethod3();
topLevelMethod4<double>();
}

View file

@ -386,7 +386,10 @@ Future checkTests(Directory dataDir, ComputeMemberDataFunction computeFromAst,
List<String> testOptions = options.toList();
if (name.endsWith('_ea.dart')) {
testOptions.add(Flags.enableAsserts);
} else if (name.endsWith('_strong.dart')) {
testOptions.add(Flags.strongMode);
}
print('----------------------------------------------------------------');
print('Test: $name');
// Pretend this is a dart2js_native test to allow use of 'native' keyword
@ -431,7 +434,7 @@ Future checkTests(Directory dataDir, ComputeMemberDataFunction computeFromAst,
if (setUpFunction != null) setUpFunction();
if (skipForAst.contains(name)) {
if (skipForAst.contains(name) || testOptions.contains(Flags.strongMode)) {
print('--skipped for ast-----------------------------------------------');
} else {
print('--from ast------------------------------------------------------');

View file

@ -72,8 +72,12 @@ CompilerDiagnostics createCompilerDiagnostics(
Expando<MemorySourceFileProvider> expando =
new Expando<MemorySourceFileProvider>();
// Cached kernel state for non-strong mode.
fe.InitializedCompilerState kernelInitializedCompilerState;
// Cached kernel state for strong mode.
fe.InitializedCompilerState strongKernelInitializedCompilerState;
/// memorySourceFiles can contain a map of string filename to string file
/// contents or string file name to binary file contents (hence the `dynamic`
/// type for the second parameter).
@ -110,14 +114,20 @@ Future<CompilationResult> runCompiler(
if (beforeRun != null) {
beforeRun(compiler);
}
fe.InitializedCompilerState compilerState;
bool isSuccess = await compiler.run(entryPoint);
if (compiler.libraryLoader is KernelLibraryLoaderTask) {
KernelLibraryLoaderTask loader = compiler.libraryLoader;
kernelInitializedCompilerState = loader.initializedCompilerState;
if (compiler.options.strongMode) {
compilerState = strongKernelInitializedCompilerState =
loader.initializedCompilerState;
} else {
compilerState =
kernelInitializedCompilerState = loader.initializedCompilerState;
}
}
return new CompilationResult(compiler,
isSuccess: isSuccess,
kernelInitializedCompilerState: kernelInitializedCompilerState);
isSuccess: isSuccess, kernelInitializedCompilerState: compilerState);
}
CompilerImpl compilerFor(
@ -172,21 +182,25 @@ CompilerImpl compilerFor(
outputProvider = const NullCompilerOutput();
}
CompilerOptions compilerOptions = new CompilerOptions.parse(
entryPoint: entryPoint,
resolutionInputs: resolutionInputs,
libraryRoot: libraryRoot,
packageRoot: packageRoot,
options: options,
environment: {},
platformBinaries: platformBinaries,
packageConfig: packageConfig,
packagesDiscoveryProvider: packagesDiscoveryProvider);
if (compilerOptions.strongMode) {
compilerOptions.kernelInitializedCompilerState =
strongKernelInitializedCompilerState;
} else {
compilerOptions.kernelInitializedCompilerState =
kernelInitializedCompilerState;
}
CompilerImpl compiler = new CompilerImpl(
provider,
outputProvider,
diagnosticHandler,
new CompilerOptions.parse(
entryPoint: entryPoint,
resolutionInputs: resolutionInputs,
libraryRoot: libraryRoot,
packageRoot: packageRoot,
options: options,
environment: {},
platformBinaries: platformBinaries,
packageConfig: packageConfig,
packagesDiscoveryProvider: packagesDiscoveryProvider)
..kernelInitializedCompilerState = kernelInitializedCompilerState);
provider, outputProvider, diagnosticHandler, compilerOptions);
if (cachedCompiler != null) {
Map copiedLibraries = {};

View file

@ -4,9 +4,66 @@
/*class: A:needsArgs,exp*/
class A<T> {
m() => T;
instanceMethod() => T;
/*ast.element: A.staticMethod:exp*/
/*kernel.element: A.staticMethod:needsArgs,exp*/
static staticMethod<S>() => S;
/*ast.element: A.staticMethodNested:exp*/
/*kernel.element: A.staticMethodNested:needsArgs,exp*/
static staticMethodNested<S>() {
var inner = () => S;
return inner();
}
/*ast.element: A.genericMethod:exp*/
/*kernel.element: A.genericMethod:needsArgs,exp*/
genericMethod<S>() => S;
/*ast.element: A.genericMethodNested:exp*/
/*kernel.element: A.genericMethodNested:needsArgs,exp*/
genericMethodNested<S>() {
var inner = () => S;
return inner();
}
localFunction() {
/*ast.exp*/ /*kernel.needsArgs,exp*/ local<S>() => S;
return local<bool>();
}
localFunctionNested() {
/*ast.exp*/ /*kernel.needsArgs,exp*/ local<S>() {
var inner = () => S;
return inner();
}
return local<bool>();
}
}
/*ast.element: topLevelMethod:exp*/
/*kernel.element: topLevelMethod:needsArgs,exp*/
topLevelMethod<S>() => S;
/*ast.element: topLevelMethodNested:exp*/
/*kernel.element: topLevelMethodNested:needsArgs,exp*/
topLevelMethodNested<S>() {
var inner = () => S;
return inner();
}
main() {
new A<int>().m();
var a = new A<int>();
a.instanceMethod();
a.genericMethod<String>();
a.genericMethodNested<String>();
a.localFunction();
a.localFunctionNested();
A.staticMethod<double>();
A.staticMethodNested<double>();
topLevelMethod<num>();
topLevelMethodNested<num>();
}

View file

@ -17,6 +17,7 @@ import 'package:compiler/src/tree/nodes.dart' as ast;
import 'package:compiler/src/js_backend/runtime_types.dart';
import 'package:compiler/src/kernel/element_map.dart';
import 'package:compiler/src/kernel/kernel_backend_strategy.dart';
import 'package:compiler/src/kernel/kernel_strategy.dart';
import 'package:compiler/src/ssa/builder.dart' as ast;
import 'package:compiler/src/universe/world_builder.dart';
import 'package:kernel/ast.dart' as ir;
@ -114,7 +115,7 @@ abstract class ComputeValueMixin<T> {
}
ClassEntity frontendClass = getFrontendClass(backendClass);
comma = findDependencies(sb, comma, frontendClass);
if (rtiNeedBuilder.classesUsingTypeVariableExpression
if (rtiNeedBuilder.classesUsingTypeVariableLiterals
.contains(frontendClass)) {
sb.write('${comma}exp');
comma = ',';
@ -146,16 +147,30 @@ abstract class ComputeValueMixin<T> {
sb.write('${comma}needsSignature');
comma = ',';
}
if (frontendClosure != null &&
rtiNeed.localFunctionNeedsSignature(frontendClosure)) {
sb.write('${comma}needsSignature');
comma = ',';
if (frontendClosure != null) {
if (frontendClosure is LocalFunctionElement &&
rtiNeed.localFunctionNeedsSignature(frontendClosure)) {
sb.write('${comma}needsSignature');
comma = ',';
}
if (rtiNeedBuilder.localFunctionsUsingTypeVariableLiterals
.contains(frontendClosure)) {
sb.write('${comma}exp');
comma = ',';
}
}
if (frontendMember != null) {
if (rtiNeedBuilder.methodsUsingTypeVariableLiterals
.contains(frontendMember)) {
sb.write('${comma}exp');
comma = ',';
}
comma = findDependencies(sb, comma, frontendMember);
comma = findChecks(
sb, comma, 'explicit', frontendMember, rtiNeedBuilder.isChecks);
comma = findChecks(sb, comma, 'implicit', frontendMember,
rtiNeedBuilder.implicitIsChecks);
}
comma = findDependencies(sb, comma, frontendMember);
comma = findChecks(
sb, comma, 'explicit', frontendMember, rtiNeedBuilder.isChecks);
comma = findChecks(sb, comma, 'implicit', frontendMember,
rtiNeedBuilder.implicitIsChecks);
}
return sb.toString();
}
@ -332,7 +347,17 @@ abstract class IrMixin implements ComputeValueMixin<ir.Node> {
}
@override
Local getFrontendClosure(MemberEntity member) => null;
Local getFrontendClosure(MemberEntity member) {
KernelBackendStrategy backendStrategy = compiler.backendStrategy;
ir.Node node = backendStrategy.elementMap.getMemberDefinition(member).node;
if (node is ir.FunctionDeclaration || node is ir.FunctionExpression) {
KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
KernelToElementMapForImpact frontendElementMap =
frontendStrategy.elementMap;
return frontendElementMap.getLocalFunction(node);
}
return null;
}
}
class RtiClassNeedIrComputer extends DataRegistry