mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 14:32:24 +00:00
Optimize RTI capture
Change-Id: I1b1b363de442e118ccb1e2018748a2ba7c5fe9e6 Reviewed-on: https://dart-review.googlesource.com/51642 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
parent
fab5bef194
commit
6a8cad4877
15 changed files with 614 additions and 272 deletions
|
@ -16,6 +16,7 @@ import '../kernel/element_map.dart';
|
|||
import '../kernel/env.dart';
|
||||
import '../options.dart';
|
||||
import '../ssa/type_builder.dart';
|
||||
import '../universe/selector.dart';
|
||||
import '../world.dart';
|
||||
import 'elements.dart';
|
||||
import 'closure_visitors.dart';
|
||||
|
@ -77,6 +78,7 @@ class KernelClosureAnalysis {
|
|||
class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
|
||||
final KernelToElementMapForBuilding _elementMap;
|
||||
final GlobalLocalsMap _globalLocalsMap;
|
||||
final CompilerOptions _options;
|
||||
|
||||
/// Map of the scoping information that corresponds to a particular entity.
|
||||
Map<MemberEntity, ScopeInfo> _scopeMap = <MemberEntity, ScopeInfo>{};
|
||||
|
@ -93,19 +95,8 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
|
|||
Map<ir.TreeNode, ClosureRepresentationInfo> _localClosureRepresentationMap =
|
||||
<ir.TreeNode, ClosureRepresentationInfo>{};
|
||||
|
||||
/// If true add type assertions to assert that at runtime the type is in line
|
||||
/// with the stated type.
|
||||
final bool _addTypeChecks;
|
||||
|
||||
/// If true, we are compiling using strong mode, and therefore we will create
|
||||
/// a "signatureMethod" for a closure that can output the type. In this
|
||||
/// instance, we may need access to a type variable that has not otherwise
|
||||
/// been captured, and therefore we need to mark it as being used so that the
|
||||
/// RTI optimization doesn't optimize it away..
|
||||
final bool _strongMode;
|
||||
|
||||
KernelClosureConversionTask(Measurer measurer, this._elementMap,
|
||||
this._globalLocalsMap, this._addTypeChecks, this._strongMode)
|
||||
KernelClosureConversionTask(
|
||||
Measurer measurer, this._elementMap, this._globalLocalsMap, this._options)
|
||||
: super(measurer);
|
||||
|
||||
/// The combined steps of generating our intermediate representation of
|
||||
|
@ -118,68 +109,127 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
|
|||
void convertClosures(Iterable<MemberEntity> processedEntities,
|
||||
ClosedWorldRefiner closedWorldRefiner) {}
|
||||
|
||||
void _updateScopeBasedOnRtiNeed(
|
||||
KernelScopeInfo scope,
|
||||
bool Function(ClassEntity) classNeedsTypeArguments,
|
||||
bool Function(MemberEntity) methodNeedsTypeArguments,
|
||||
bool Function(ir.Node) localFunctionNeedsTypeArguments,
|
||||
void _updateScopeBasedOnRtiNeed(KernelScopeInfo scope, ClosureRtiNeed rtiNeed,
|
||||
MemberEntity outermostEntity) {
|
||||
if (scope.thisUsedAsFreeVariableIfNeedsRti &&
|
||||
(classNeedsTypeArguments(outermostEntity.enclosingClass) ||
|
||||
// TODO(johnniwinther): Instead of _strongMode, make this branch test
|
||||
// if an added signature method needs type arguments (see comment in
|
||||
// TypeVariableKind.method branch below.
|
||||
_strongMode)) {
|
||||
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);
|
||||
bool includeForRti(Set<VariableUse> useSet) {
|
||||
for (VariableUse usage in useSet) {
|
||||
switch (usage.kind) {
|
||||
case VariableUseKind.explicit:
|
||||
return true;
|
||||
break;
|
||||
case VariableUseKind.implicitCast:
|
||||
if (_options.implicitDowncastCheckPolicy.isEmitted) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeVariableKind.method:
|
||||
if (methodNeedsTypeArguments(
|
||||
_elementMap.getMember(typeVariable.typeDeclaration)) ||
|
||||
// In Dart 2, we have the notion of generic methods. This is
|
||||
// partly implemented by adding a "method signature" function to
|
||||
// closure classes. This signature reports the type of the
|
||||
// method, and therefore may access the type variable of the
|
||||
// parameter, which might otherwise not be used (and therefore
|
||||
// isn't captured in the set that `methodNeedsTypeArguments`
|
||||
// compares against.
|
||||
// TODO(johnniwinther): Include this reasoning inside
|
||||
// [methodNeedsTypeArguments] rather than an add on here.
|
||||
_strongMode &&
|
||||
scope.freeVariablesForRti.contains(typeVariable)) {
|
||||
scope.freeVariables.add(typeVariable);
|
||||
case VariableUseKind.localType:
|
||||
if (_options.assignmentCheckPolicy.isEmitted) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeVariableKind.local:
|
||||
if (localFunctionNeedsTypeArguments(typeVariable.typeDeclaration)) {
|
||||
scope.freeVariables.add(typeVariable);
|
||||
|
||||
case VariableUseKind.constructorTypeArgument:
|
||||
ConstructorEntity constructor =
|
||||
_elementMap.getConstructor(usage.member);
|
||||
if (rtiNeed.classNeedsTypeArguments(constructor.enclosingClass)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TypeVariableKind.function:
|
||||
case VariableUseKind.staticTypeArgument:
|
||||
FunctionEntity method = _elementMap.getMethod(usage.member);
|
||||
if (rtiNeed.methodNeedsTypeArguments(method)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.instanceTypeArgument:
|
||||
Selector selector = _elementMap.getSelector(usage.invocation);
|
||||
if (rtiNeed.selectorNeedsTypeArguments(selector)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.localTypeArgument:
|
||||
// TODO(johnniwinther): We should be able to track direct local
|
||||
// function invocations and not have to use the selector here.
|
||||
Selector selector = _elementMap.getSelector(usage.invocation);
|
||||
if (rtiNeed.localFunctionNeedsTypeArguments(usage.localFunction) ||
|
||||
rtiNeed.selectorNeedsTypeArguments(selector)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.memberParameter:
|
||||
if (_options.parameterCheckPolicy.isEmitted) {
|
||||
return true;
|
||||
} else {
|
||||
FunctionEntity method = _elementMap.getMethod(usage.member);
|
||||
if (rtiNeed.methodNeedsSignature(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.localParameter:
|
||||
if (_options.parameterCheckPolicy.isEmitted) {
|
||||
return true;
|
||||
} else if (rtiNeed
|
||||
.localFunctionNeedsSignature(usage.localFunction)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.memberReturnType:
|
||||
if (_options.assignmentCheckPolicy.isEmitted) {
|
||||
return true;
|
||||
} else {
|
||||
FunctionEntity method = _elementMap.getMethod(usage.member);
|
||||
if (rtiNeed.methodNeedsSignature(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.localReturnType:
|
||||
if (_options.assignmentCheckPolicy.isEmitted) {
|
||||
return true;
|
||||
} else if (rtiNeed
|
||||
.localFunctionNeedsSignature(usage.localFunction)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.fieldType:
|
||||
if (_options.assignmentCheckPolicy.isEmitted ||
|
||||
_options.parameterCheckPolicy.isEmitted) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.listLiteral:
|
||||
if (rtiNeed.classNeedsTypeArguments(
|
||||
_elementMap.commonElements.jsArrayClass)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VariableUseKind.mapLiteral:
|
||||
if (rtiNeed.classNeedsTypeArguments(
|
||||
_elementMap.commonElements.mapLiteralClass)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (includeForRti(scope.thisUsedAsFreeVariableIfNeedsRti)) {
|
||||
scope.thisUsedAsFreeVariable = true;
|
||||
}
|
||||
scope.freeVariablesForRti.forEach(
|
||||
(TypeVariableTypeWithContext typeVariable, Set<VariableUse> useSet) {
|
||||
if (includeForRti(useSet)) {
|
||||
scope.freeVariables.add(typeVariable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Iterable<FunctionEntity> createClosureEntities(
|
||||
JsClosedWorldBuilder closedWorldBuilder,
|
||||
Map<MemberEntity, ScopeModel> closureModels,
|
||||
{bool Function(ir.Node) localFunctionNeedsSignature,
|
||||
bool Function(ClassEntity) classNeedsTypeArguments,
|
||||
bool Function(FunctionEntity) methodNeedsTypeArguments,
|
||||
bool Function(ir.Node) localFunctionNeedsTypeArguments}) {
|
||||
ClosureRtiNeed rtiNeed) {
|
||||
List<FunctionEntity> callMethods = <FunctionEntity>[];
|
||||
closureModels.forEach((MemberEntity member, ScopeModel model) {
|
||||
KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member);
|
||||
|
@ -192,8 +242,7 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
|
|||
.forEach((ir.Node node, KernelCapturedScope scope) {
|
||||
Map<Local, JRecordField> boxedVariables =
|
||||
_elementMap.makeRecordContainer(scope, member, localsMap);
|
||||
_updateScopeBasedOnRtiNeed(scope, classNeedsTypeArguments,
|
||||
methodNeedsTypeArguments, localFunctionNeedsTypeArguments, member);
|
||||
_updateScopeBasedOnRtiNeed(scope, rtiNeed, member);
|
||||
|
||||
if (scope is KernelCapturedLoopScope) {
|
||||
_capturedScopesMap[node] = new JsCapturedLoopScope.from(
|
||||
|
@ -222,11 +271,9 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
|
|||
functionNode,
|
||||
closuresToGenerate[node],
|
||||
allBoxedVariables,
|
||||
classNeedsTypeArguments,
|
||||
methodNeedsTypeArguments,
|
||||
localFunctionNeedsTypeArguments,
|
||||
rtiNeed,
|
||||
createSignatureMethod:
|
||||
localFunctionNeedsSignature(functionNode.parent));
|
||||
rtiNeed.localFunctionNeedsSignature(functionNode.parent));
|
||||
// Add also for the call method.
|
||||
_scopeMap[closureClassInfo.callMethod] = closureClassInfo;
|
||||
_scopeMap[closureClassInfo.signatureMethod] = closureClassInfo;
|
||||
|
@ -234,19 +281,14 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
|
|||
// Set up capturedScope for signature method. This is distinct from
|
||||
// _capturedScopesMap because there is no corresponding ir.Node for the
|
||||
// signature.
|
||||
if (localFunctionNeedsSignature(functionNode.parent) &&
|
||||
if (rtiNeed.localFunctionNeedsSignature(functionNode.parent) &&
|
||||
model.capturedScopesMap[functionNode] != null) {
|
||||
KernelCapturedScope capturedScope =
|
||||
model.capturedScopesMap[functionNode];
|
||||
assert(capturedScope is! KernelCapturedLoopScope);
|
||||
KernelCapturedScope signatureCapturedScope =
|
||||
new KernelCapturedScope.forSignature(capturedScope);
|
||||
_updateScopeBasedOnRtiNeed(
|
||||
signatureCapturedScope,
|
||||
classNeedsTypeArguments,
|
||||
methodNeedsTypeArguments,
|
||||
localFunctionNeedsTypeArguments,
|
||||
member);
|
||||
_updateScopeBasedOnRtiNeed(signatureCapturedScope, rtiNeed, member);
|
||||
_capturedScopeForSignatureMap[closureClassInfo.signatureMethod] =
|
||||
new JsCapturedScope.from(
|
||||
{}, signatureCapturedScope, localsMap, _elementMap);
|
||||
|
@ -269,12 +311,9 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
|
|||
ir.FunctionNode node,
|
||||
KernelScopeInfo info,
|
||||
Map<Local, JRecordField> boxedVariables,
|
||||
bool Function(ClassEntity) classNeedsTypeArguments,
|
||||
bool Function(FunctionEntity) methodNeedsTypeArguments,
|
||||
bool Function(ir.Node) localFunctionNeedsTypeArguments,
|
||||
ClosureRtiNeed rtiNeed,
|
||||
{bool createSignatureMethod}) {
|
||||
_updateScopeBasedOnRtiNeed(info, classNeedsTypeArguments,
|
||||
methodNeedsTypeArguments, localFunctionNeedsTypeArguments, member);
|
||||
_updateScopeBasedOnRtiNeed(info, rtiNeed, member);
|
||||
KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member);
|
||||
KernelClosureClassInfo closureClassInfo =
|
||||
closedWorldBuilder.buildClosureClass(
|
||||
|
@ -355,6 +394,150 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
|
|||
}
|
||||
}
|
||||
|
||||
enum VariableUseKind {
|
||||
/// An explicit variable use.
|
||||
///
|
||||
/// For type variable this is an explicit as-cast, an is-test or a type
|
||||
/// literal.
|
||||
explicit,
|
||||
|
||||
/// A type variable used in the type of a local variable.
|
||||
localType,
|
||||
|
||||
/// A type variable used in an implicit cast.
|
||||
implicitCast,
|
||||
|
||||
/// A type variable passed as the type argument of a list literal.
|
||||
listLiteral,
|
||||
|
||||
/// A type variable passed as the type argument of a map literal.
|
||||
mapLiteral,
|
||||
|
||||
/// A type variable passed as a type argument to a constructor.
|
||||
constructorTypeArgument,
|
||||
|
||||
/// A type variable passed as a type argument to a static method.
|
||||
staticTypeArgument,
|
||||
|
||||
/// A type variable passed as a type argument to an instance method.
|
||||
instanceTypeArgument,
|
||||
|
||||
/// A type variable passed as a type argument to a local function.
|
||||
localTypeArgument,
|
||||
|
||||
/// A type variable in a parameter type of a member.
|
||||
memberParameter,
|
||||
|
||||
/// A type variable in a parameter type of a local function.
|
||||
localParameter,
|
||||
|
||||
/// A type variable used in a return type of a member.
|
||||
memberReturnType,
|
||||
|
||||
/// A type variable used in a return type of a local function.
|
||||
localReturnType,
|
||||
|
||||
/// A type variable in a field type.
|
||||
fieldType,
|
||||
}
|
||||
|
||||
class VariableUse {
|
||||
final VariableUseKind kind;
|
||||
final ir.Member member;
|
||||
final ir.TreeNode /*ir.FunctionDeclaration|ir.FunctionExpression*/
|
||||
localFunction;
|
||||
final ir.MethodInvocation invocation;
|
||||
|
||||
const VariableUse._simple(this.kind)
|
||||
: this.member = null,
|
||||
this.localFunction = null,
|
||||
this.invocation = null;
|
||||
|
||||
VariableUse.memberParameter(this.member)
|
||||
: this.kind = VariableUseKind.memberParameter,
|
||||
this.localFunction = null,
|
||||
this.invocation = null;
|
||||
|
||||
VariableUse.localParameter(this.localFunction)
|
||||
: this.kind = VariableUseKind.localParameter,
|
||||
this.member = null,
|
||||
this.invocation = null {
|
||||
assert(localFunction is ir.FunctionDeclaration ||
|
||||
localFunction is ir.FunctionExpression);
|
||||
}
|
||||
|
||||
VariableUse.memberReturnType(this.member)
|
||||
: this.kind = VariableUseKind.memberReturnType,
|
||||
this.localFunction = null,
|
||||
this.invocation = null;
|
||||
|
||||
VariableUse.localReturnType(this.localFunction)
|
||||
: this.kind = VariableUseKind.localReturnType,
|
||||
this.member = null,
|
||||
this.invocation = null {
|
||||
assert(localFunction is ir.FunctionDeclaration ||
|
||||
localFunction is ir.FunctionExpression);
|
||||
}
|
||||
|
||||
VariableUse.constructorTypeArgument(this.member)
|
||||
: this.kind = VariableUseKind.constructorTypeArgument,
|
||||
this.localFunction = null,
|
||||
this.invocation = null;
|
||||
|
||||
VariableUse.staticTypeArgument(this.member)
|
||||
: this.kind = VariableUseKind.staticTypeArgument,
|
||||
this.localFunction = null,
|
||||
this.invocation = null;
|
||||
|
||||
VariableUse.instanceTypeArgument(this.invocation)
|
||||
: this.kind = VariableUseKind.instanceTypeArgument,
|
||||
this.member = null,
|
||||
this.localFunction = null;
|
||||
|
||||
VariableUse.localTypeArgument(this.localFunction, this.invocation)
|
||||
: this.kind = VariableUseKind.localTypeArgument,
|
||||
this.member = null {
|
||||
assert(localFunction is ir.FunctionDeclaration ||
|
||||
localFunction is ir.FunctionExpression);
|
||||
}
|
||||
|
||||
static const VariableUse explicit =
|
||||
const VariableUse._simple(VariableUseKind.explicit);
|
||||
|
||||
static const VariableUse localType =
|
||||
const VariableUse._simple(VariableUseKind.localType);
|
||||
|
||||
static const VariableUse implicitCast =
|
||||
const VariableUse._simple(VariableUseKind.implicitCast);
|
||||
|
||||
static const VariableUse listLiteral =
|
||||
const VariableUse._simple(VariableUseKind.listLiteral);
|
||||
|
||||
static const VariableUse mapLiteral =
|
||||
const VariableUse._simple(VariableUseKind.mapLiteral);
|
||||
|
||||
static const VariableUse fieldType =
|
||||
const VariableUse._simple(VariableUseKind.fieldType);
|
||||
|
||||
int get hashCode =>
|
||||
kind.hashCode * 11 +
|
||||
member.hashCode * 13 +
|
||||
localFunction.hashCode * 17 +
|
||||
invocation.hashCode * 19;
|
||||
|
||||
bool operator ==(other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! VariableUse) return false;
|
||||
return kind == other.kind &&
|
||||
member == other.member &&
|
||||
localFunction == other.localFunction &&
|
||||
invocation == other.invocation;
|
||||
}
|
||||
|
||||
String toString() => 'VariableUse(kind=$kind,member=$member,'
|
||||
'localFunction=$localFunction,invocation=$invocation)';
|
||||
}
|
||||
|
||||
class KernelScopeInfo {
|
||||
final Set<ir.VariableDeclaration> localsUsedInTryOrSync;
|
||||
final bool hasThisLocal;
|
||||
|
@ -375,8 +558,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<TypeVariableTypeWithContext> freeVariablesForRti =
|
||||
new Set<TypeVariableTypeWithContext>();
|
||||
Map<TypeVariableTypeWithContext, Set<VariableUse>> freeVariablesForRti =
|
||||
<TypeVariableTypeWithContext, Set<VariableUse>>{};
|
||||
|
||||
/// If true, `this` is used as a free variable, in this scope. It is stored
|
||||
/// separately from [freeVariables] because there is no single
|
||||
|
@ -387,7 +570,7 @@ class KernelScopeInfo {
|
|||
/// performing runtime type checks. It is stored
|
||||
/// separately from [thisUsedAsFreeVariable] because we don't know at this
|
||||
/// stage if we will be needing type checks for this scope.
|
||||
bool thisUsedAsFreeVariableIfNeedsRti = false;
|
||||
Set<VariableUse> thisUsedAsFreeVariableIfNeedsRti = new Set<VariableUse>();
|
||||
|
||||
KernelScopeInfo(this.hasThisLocal)
|
||||
: localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(),
|
||||
|
@ -411,10 +594,16 @@ class KernelScopeInfo {
|
|||
|
||||
String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.write('this=$hasThisLocal,');
|
||||
sb.write('KernelScopeInfo(this=$hasThisLocal,');
|
||||
sb.write('freeVriables=$freeVariables,');
|
||||
sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}');
|
||||
sb.write('freeVariablesForRti={${freeVariablesForRti.join(', ')}}');
|
||||
String comma = '';
|
||||
sb.write('freeVariablesForRti={');
|
||||
freeVariablesForRti.forEach((key, value) {
|
||||
sb.write('$comma$key:$value');
|
||||
comma = ',';
|
||||
});
|
||||
sb.write('})');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
@ -482,11 +671,11 @@ class KernelCapturedScope extends KernelScopeInfo {
|
|||
Set<ir.VariableDeclaration> boxedVariables,
|
||||
NodeBox capturedVariablesAccessor,
|
||||
Set<ir.VariableDeclaration> localsUsedInTryOrSync,
|
||||
Set<ir.Node /* VariableDeclaration | TypeParameterTypeWithContext */ >
|
||||
Set<ir.Node /* VariableDeclaration | TypeVariableTypeWithContext */ >
|
||||
freeVariables,
|
||||
Set<TypeVariableTypeWithContext> freeVariablesForRti,
|
||||
Map<TypeVariableTypeWithContext, Set<VariableUse>> freeVariablesForRti,
|
||||
bool thisUsedAsFreeVariable,
|
||||
bool thisUsedAsFreeVariableIfNeedsRti,
|
||||
Set<VariableUse> thisUsedAsFreeVariableIfNeedsRti,
|
||||
bool hasThisLocal)
|
||||
: super.withBoxedVariables(
|
||||
boxedVariables,
|
||||
|
@ -541,11 +730,11 @@ class KernelCapturedLoopScope extends KernelCapturedScope {
|
|||
NodeBox capturedVariablesAccessor,
|
||||
this.boxedLoopVariables,
|
||||
Set<ir.VariableDeclaration> localsUsedInTryOrSync,
|
||||
Set<ir.Node /* VariableDeclaration | TypeParameterTypeWithContext */ >
|
||||
Set<ir.Node /* VariableDeclaration | TypeVariableTypeWithContext */ >
|
||||
freeVariables,
|
||||
Set<TypeVariableTypeWithContext> freeVariablesForRti,
|
||||
Map<TypeVariableTypeWithContext, Set<VariableUse>> freeVariablesForRti,
|
||||
bool thisUsedAsFreeVariable,
|
||||
bool thisUsedAsFreeVariableIfNeedsRti,
|
||||
Set<VariableUse> thisUsedAsFreeVariableIfNeedsRti,
|
||||
bool hasThisLocal)
|
||||
: super(
|
||||
boxedVariables,
|
||||
|
@ -959,3 +1148,17 @@ class TypeVariableTypeWithContext implements ir.Node {
|
|||
'TypeVariableTypeWithContext(type=$type,context=$context,'
|
||||
'kind=$kind,typeDeclaration=$typeDeclaration)';
|
||||
}
|
||||
|
||||
abstract class ClosureRtiNeed {
|
||||
bool classNeedsTypeArguments(ClassEntity cls);
|
||||
|
||||
bool methodNeedsTypeArguments(FunctionEntity method);
|
||||
|
||||
bool methodNeedsSignature(MemberEntity method);
|
||||
|
||||
bool localFunctionNeedsTypeArguments(ir.Node node);
|
||||
|
||||
bool localFunctionNeedsSignature(ir.Node node);
|
||||
|
||||
bool selectorNeedsTypeArguments(Selector selector);
|
||||
}
|
||||
|
|
|
@ -68,18 +68,16 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
/// have unique names.
|
||||
int _boxCounter = 0;
|
||||
|
||||
/// Set to `true` in the visitor if a type annotation is always needed.
|
||||
/// The current usage of a type annotation.
|
||||
///
|
||||
/// This is for instance the case for expressions like `o is T` and `o as T`.
|
||||
bool _contextNeedsType = false;
|
||||
/// This is updated in the visitor to distinguish between unconditional
|
||||
/// type variable usage, such as type literals and is tests, and conditional
|
||||
/// type variable usage, such as type argument in method invocations.
|
||||
VariableUse _currentTypeUsage;
|
||||
|
||||
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.
|
||||
void attachCapturedScopeVariables(ir.TreeNode node) {
|
||||
|
@ -179,25 +177,31 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
|
||||
@override
|
||||
visitVariableGet(ir.VariableGet node) {
|
||||
_markVariableAsUsed(node.variable);
|
||||
node.visitChildren(this);
|
||||
_markVariableAsUsed(node.variable, VariableUse.explicit);
|
||||
// Don't visit `node.promotedType`.
|
||||
}
|
||||
|
||||
@override
|
||||
visitVariableSet(ir.VariableSet node) {
|
||||
_mutatedVariables.add(node.variable);
|
||||
_markVariableAsUsed(node.variable);
|
||||
if (_addTypeChecks) node.variable.type.accept(this);
|
||||
_markVariableAsUsed(node.variable, VariableUse.explicit);
|
||||
visitInContext(node.variable.type, VariableUse.localType);
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
@override
|
||||
visitVariableDeclaration(ir.VariableDeclaration declaration) {
|
||||
if (!declaration.isFieldFormal) {
|
||||
_scopeVariables.add(declaration);
|
||||
void handleVariableDeclaration(
|
||||
ir.VariableDeclaration node, VariableUse usage) {
|
||||
if (!node.isFieldFormal) {
|
||||
_scopeVariables.add(node);
|
||||
}
|
||||
|
||||
declaration.visitChildren(this);
|
||||
visitInContext(node.type, usage);
|
||||
node.initializer?.accept(this);
|
||||
}
|
||||
|
||||
@override
|
||||
visitVariableDeclaration(ir.VariableDeclaration node) {
|
||||
handleVariableDeclaration(node, VariableUse.localType);
|
||||
}
|
||||
|
||||
/// Add this variable to the set of free variables if appropriate and add to
|
||||
|
@ -207,9 +211,10 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
/// checked.
|
||||
void _markVariableAsUsed(
|
||||
ir.Node /* VariableDeclaration | TypeParameterTypeWithContext */ variable,
|
||||
{bool onlyForRtiChecks = false}) {
|
||||
VariableUse usage) {
|
||||
assert(variable is ir.VariableDeclaration ||
|
||||
variable is TypeVariableTypeWithContext);
|
||||
assert(usage != null);
|
||||
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.
|
||||
|
@ -217,10 +222,12 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
// optimization: factories have type parameters as function
|
||||
// parameters, and type parameters are declared in the class, not
|
||||
// the factory.
|
||||
if (!onlyForRtiChecks) {
|
||||
if (usage == VariableUse.explicit) {
|
||||
_currentScopeInfo.freeVariables.add(variable);
|
||||
} else {
|
||||
_currentScopeInfo.freeVariablesForRti.add(variable);
|
||||
_currentScopeInfo.freeVariablesForRti
|
||||
.putIfAbsent(variable, () => new Set<VariableUse>())
|
||||
.add(usage);
|
||||
}
|
||||
}
|
||||
if (_inTry && variable is ir.VariableDeclaration) {
|
||||
|
@ -230,41 +237,36 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
|
||||
@override
|
||||
void visitThisExpression(ir.ThisExpression thisExpression) {
|
||||
if (_hasThisLocal) _registerNeedsThis();
|
||||
if (_hasThisLocal) _registerNeedsThis(VariableUse.explicit);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitTypeParameter(ir.TypeParameter typeParameter) {
|
||||
if (_addTypeChecks) {
|
||||
ir.TreeNode context = _executableContext;
|
||||
TypeVariableTypeWithContext typeVariable =
|
||||
new TypeVariableTypeWithContext(
|
||||
new ir.TypeParameterType(typeParameter),
|
||||
// If this typeParameter is part of a typedef then its parent is
|
||||
// null because it has no context. Just pass in null for the
|
||||
// context in that case.
|
||||
typeParameter.parent != null
|
||||
? typeParameter.parent.parent
|
||||
: null);
|
||||
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(typeVariable);
|
||||
}
|
||||
ir.TreeNode context = _executableContext;
|
||||
TypeVariableTypeWithContext typeVariable = new TypeVariableTypeWithContext(
|
||||
new ir.TypeParameterType(typeParameter),
|
||||
// If this typeParameter is part of a typedef then its parent is
|
||||
// null because it has no context. Just pass in null for the
|
||||
// context in that case.
|
||||
typeParameter.parent != null ? typeParameter.parent.parent : null);
|
||||
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(typeVariable, _currentTypeUsage);
|
||||
}
|
||||
|
||||
if (_executableContext is ir.Member && _executableContext is! ir.Field) {
|
||||
// In checked mode, using a type variable in a type annotation may lead
|
||||
// to a runtime type check that needs to access the type argument and
|
||||
// therefore the closure needs a this-element, if it is not in a field
|
||||
// initializer; field initializers are evaluated in a context where
|
||||
// the type arguments are available in locals.
|
||||
if (_executableContext is ir.Member && _executableContext is! ir.Field) {
|
||||
// In checked mode, using a type variable in a type annotation may lead
|
||||
// to a runtime type check that needs to access the type argument and
|
||||
// therefore the closure needs a this-element, if it is not in a field
|
||||
// initializer; field initializers are evaluated in a context where
|
||||
// the type arguments are available in locals.
|
||||
|
||||
if (_hasThisLocal) {
|
||||
_registerNeedsThis();
|
||||
} else {
|
||||
_useTypeVariableAsLocal(typeVariable);
|
||||
}
|
||||
if (_hasThisLocal) {
|
||||
_registerNeedsThis(_currentTypeUsage);
|
||||
} else {
|
||||
_useTypeVariableAsLocal(typeVariable, _currentTypeUsage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,12 +276,12 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
/// If [onlyIfNeedsRti] is true, set thisUsedAsFreeVariableIfNeedsRti to true
|
||||
/// instead of thisUsedAsFreeVariable as we will only use `this` if runtime
|
||||
/// type information is checked.
|
||||
void _registerNeedsThis({bool onlyIfNeedsRti = false}) {
|
||||
void _registerNeedsThis(VariableUse usage) {
|
||||
if (_isInsideClosure) {
|
||||
if (!onlyIfNeedsRti) {
|
||||
if (usage == VariableUse.explicit) {
|
||||
_currentScopeInfo.thisUsedAsFreeVariable = true;
|
||||
} else {
|
||||
_currentScopeInfo.thisUsedAsFreeVariableIfNeedsRti = true;
|
||||
_currentScopeInfo.thisUsedAsFreeVariableIfNeedsRti.add(usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -362,19 +364,30 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
scope.hasThisLocal);
|
||||
}
|
||||
|
||||
void visitSuperMethodInvocation(ir.SuperMethodInvocation invocation) {
|
||||
if (_hasThisLocal) _registerNeedsThis();
|
||||
invocation.visitChildren(this);
|
||||
void visitSuperMethodInvocation(ir.SuperMethodInvocation node) {
|
||||
if (_hasThisLocal) {
|
||||
_registerNeedsThis(VariableUse.explicit);
|
||||
}
|
||||
if (node.arguments.types.isNotEmpty) {
|
||||
visitListInContext(node.arguments.types,
|
||||
new VariableUse.staticTypeArgument(node.interfaceTarget));
|
||||
}
|
||||
ir.visitList(node.arguments.positional, this);
|
||||
ir.visitList(node.arguments.named, this);
|
||||
}
|
||||
|
||||
void visitSuperPropertySet(ir.SuperPropertySet propertySet) {
|
||||
if (_hasThisLocal) _registerNeedsThis();
|
||||
propertySet.visitChildren(this);
|
||||
void visitSuperPropertySet(ir.SuperPropertySet node) {
|
||||
if (_hasThisLocal) {
|
||||
_registerNeedsThis(VariableUse.explicit);
|
||||
}
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
void visitSuperPropertyGet(ir.SuperPropertyGet propertyGet) {
|
||||
if (_hasThisLocal) _registerNeedsThis();
|
||||
propertyGet.visitChildren(this);
|
||||
void visitSuperPropertyGet(ir.SuperPropertyGet node) {
|
||||
if (_hasThisLocal) {
|
||||
_registerNeedsThis(VariableUse.explicit);
|
||||
}
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
void visitInvokable(ir.TreeNode node) {
|
||||
|
@ -417,16 +430,21 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
assert(freeVariables.isEmpty || savedIsInsideClosure);
|
||||
for (ir.Node freeVariable in freeVariables) {
|
||||
_capturedVariables.add(freeVariable);
|
||||
_markVariableAsUsed(freeVariable);
|
||||
}
|
||||
for (ir.Node freeVariableForRti in savedScopeInfo.freeVariablesForRti) {
|
||||
_markVariableAsUsed(freeVariableForRti, onlyForRtiChecks: true);
|
||||
_markVariableAsUsed(freeVariable, VariableUse.explicit);
|
||||
}
|
||||
savedScopeInfo.freeVariablesForRti.forEach(
|
||||
(TypeVariableTypeWithContext freeVariableForRti,
|
||||
Set<VariableUse> useSet) {
|
||||
for (VariableUse usage in useSet) {
|
||||
_markVariableAsUsed(freeVariableForRti, usage);
|
||||
}
|
||||
});
|
||||
if (_isInsideClosure && savedScopeInfo.thisUsedAsFreeVariable) {
|
||||
_currentScopeInfo.thisUsedAsFreeVariable = true;
|
||||
}
|
||||
if (_isInsideClosure && savedScopeInfo.thisUsedAsFreeVariableIfNeedsRti) {
|
||||
_currentScopeInfo.thisUsedAsFreeVariableIfNeedsRti = true;
|
||||
if (_isInsideClosure) {
|
||||
_currentScopeInfo.thisUsedAsFreeVariableIfNeedsRti
|
||||
.addAll(savedScopeInfo.thisUsedAsFreeVariableIfNeedsRti);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,7 +465,9 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
|
||||
@override
|
||||
void visitField(ir.Field field) {
|
||||
_currentTypeUsage = VariableUse.fieldType;
|
||||
visitInvokable(field);
|
||||
_currentTypeUsage = null;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -472,32 +492,141 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
|
||||
@override
|
||||
visitTypeParameterType(ir.TypeParameterType type) {
|
||||
_analyzeTypeVariable(type, onlyIfNeedsRti: !_contextNeedsType);
|
||||
_analyzeTypeVariable(type, _currentTypeUsage);
|
||||
}
|
||||
|
||||
visitInContext(ir.Node node, VariableUse use) {
|
||||
VariableUse oldCurrentTypeUsage = _currentTypeUsage;
|
||||
_currentTypeUsage = use;
|
||||
node?.accept(this);
|
||||
_currentTypeUsage = oldCurrentTypeUsage;
|
||||
}
|
||||
|
||||
visitListInContext(List<ir.Node> nodes, VariableUse use) {
|
||||
VariableUse oldCurrentTypeUsage = _currentTypeUsage;
|
||||
_currentTypeUsage = use;
|
||||
ir.visitList(nodes, this);
|
||||
_currentTypeUsage = oldCurrentTypeUsage;
|
||||
}
|
||||
|
||||
visitChildrenInContext(ir.Node node, VariableUse use) {
|
||||
VariableUse oldCurrentTypeUsage = _currentTypeUsage;
|
||||
_currentTypeUsage = use;
|
||||
node.visitChildren(this);
|
||||
_currentTypeUsage = oldCurrentTypeUsage;
|
||||
}
|
||||
|
||||
@override
|
||||
visitTypeLiteral(ir.TypeLiteral node) {
|
||||
bool oldContextNeedsType = _contextNeedsType;
|
||||
_contextNeedsType = true;
|
||||
node.visitChildren(this);
|
||||
_contextNeedsType = oldContextNeedsType;
|
||||
visitChildrenInContext(node, VariableUse.explicit);
|
||||
}
|
||||
|
||||
@override
|
||||
visitIsExpression(ir.IsExpression node) {
|
||||
bool oldContextNeedsType = _contextNeedsType;
|
||||
_contextNeedsType = true;
|
||||
node.visitChildren(this);
|
||||
_contextNeedsType = oldContextNeedsType;
|
||||
node.operand.accept(this);
|
||||
visitInContext(node.type, VariableUse.explicit);
|
||||
}
|
||||
|
||||
@override
|
||||
visitAsExpression(ir.AsExpression node) {
|
||||
bool oldContextNeedsType = _contextNeedsType;
|
||||
_contextNeedsType =
|
||||
!node.isTypeError || _options.implicitDowncastCheckPolicy.isEmitted;
|
||||
node.visitChildren(this);
|
||||
_contextNeedsType = oldContextNeedsType;
|
||||
node.operand.accept(this);
|
||||
visitInContext(node.type,
|
||||
node.isTypeError ? VariableUse.implicitCast : VariableUse.explicit);
|
||||
}
|
||||
|
||||
@override
|
||||
visitFunctionNode(ir.FunctionNode node) {
|
||||
VariableUse parameterUsage = node.parent is ir.Member
|
||||
? new VariableUse.memberParameter(node.parent)
|
||||
: new VariableUse.localParameter(node.parent);
|
||||
visitListInContext(node.typeParameters, parameterUsage);
|
||||
for (ir.VariableDeclaration declaration in node.positionalParameters) {
|
||||
handleVariableDeclaration(declaration, parameterUsage);
|
||||
}
|
||||
for (ir.VariableDeclaration declaration in node.namedParameters) {
|
||||
handleVariableDeclaration(declaration, parameterUsage);
|
||||
}
|
||||
visitInContext(
|
||||
node.returnType,
|
||||
node.parent is ir.Member
|
||||
? new VariableUse.memberReturnType(node.parent)
|
||||
: new VariableUse.localReturnType(node.parent));
|
||||
node.body?.accept(this);
|
||||
}
|
||||
|
||||
@override
|
||||
visitListLiteral(ir.ListLiteral node) {
|
||||
visitInContext(node.typeArgument, VariableUse.listLiteral);
|
||||
ir.visitList(node.expressions, this);
|
||||
}
|
||||
|
||||
@override
|
||||
visitMapLiteral(ir.MapLiteral node) {
|
||||
visitInContext(node.keyType, VariableUse.mapLiteral);
|
||||
visitInContext(node.valueType, VariableUse.mapLiteral);
|
||||
ir.visitList(node.entries, this);
|
||||
}
|
||||
|
||||
@override
|
||||
visitStaticInvocation(ir.StaticInvocation node) {
|
||||
if (node.arguments.types.isNotEmpty) {
|
||||
VariableUse usage;
|
||||
if (node.target.kind == ir.ProcedureKind.Factory) {
|
||||
usage = new VariableUse.constructorTypeArgument(node.target);
|
||||
} else {
|
||||
usage = new VariableUse.staticTypeArgument(node.target);
|
||||
}
|
||||
|
||||
visitListInContext(node.arguments.types, usage);
|
||||
}
|
||||
ir.visitList(node.arguments.positional, this);
|
||||
ir.visitList(node.arguments.named, this);
|
||||
}
|
||||
|
||||
@override
|
||||
visitConstructorInvocation(ir.ConstructorInvocation node) {
|
||||
if (node.arguments.types.isNotEmpty) {
|
||||
visitListInContext(node.arguments.types,
|
||||
new VariableUse.constructorTypeArgument(node.target));
|
||||
}
|
||||
ir.visitList(node.arguments.positional, this);
|
||||
ir.visitList(node.arguments.named, this);
|
||||
}
|
||||
|
||||
@override
|
||||
visitConditionalExpression(ir.ConditionalExpression node) {
|
||||
node.condition.accept(this);
|
||||
node.then.accept(this);
|
||||
node.otherwise.accept(this);
|
||||
// Don't visit `node.staticType`.
|
||||
}
|
||||
|
||||
@override
|
||||
visitMethodInvocation(ir.MethodInvocation node) {
|
||||
ir.TreeNode receiver = node.receiver;
|
||||
receiver.accept(this);
|
||||
if (node.arguments.types.isNotEmpty) {
|
||||
VariableUse usage;
|
||||
if (receiver is ir.VariableGet &&
|
||||
(receiver.variable.parent is ir.FunctionDeclaration ||
|
||||
receiver.variable.parent is ir.FunctionExpression)) {
|
||||
usage =
|
||||
new VariableUse.localTypeArgument(receiver.variable.parent, node);
|
||||
} else {
|
||||
usage = new VariableUse.instanceTypeArgument(node);
|
||||
}
|
||||
visitListInContext(node.arguments.types, usage);
|
||||
}
|
||||
ir.visitList(node.arguments.positional, this);
|
||||
ir.visitList(node.arguments.named, this);
|
||||
}
|
||||
|
||||
@override
|
||||
visitCatch(ir.Catch node) {
|
||||
visitInContext(node.guard, VariableUse.explicit);
|
||||
node.exception?.accept(this);
|
||||
node.stackTrace?.accept(this);
|
||||
node.body.accept(this);
|
||||
}
|
||||
|
||||
/// Returns true if the node is a field, or a constructor (factory or
|
||||
|
@ -507,8 +636,8 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
node is ir.Field ||
|
||||
(node is ir.Procedure && node.isFactory);
|
||||
|
||||
void _analyzeTypeVariable(ir.TypeParameterType type,
|
||||
{bool onlyIfNeedsRti: true}) {
|
||||
void _analyzeTypeVariable(ir.TypeParameterType type, VariableUse usage) {
|
||||
assert(usage != null);
|
||||
if (_outermostNode is ir.Member) {
|
||||
TypeVariableTypeWithContext typeVariable =
|
||||
new TypeVariableTypeWithContext(type, _outermostNode);
|
||||
|
@ -516,17 +645,15 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
case TypeVariableKind.cls:
|
||||
if (_isFieldOrConstructor(_outermostNode)) {
|
||||
// Class type variable used in a field or constructor.
|
||||
_useTypeVariableAsLocal(typeVariable,
|
||||
onlyForRtiChecks: onlyIfNeedsRti);
|
||||
_useTypeVariableAsLocal(typeVariable, usage);
|
||||
} else {
|
||||
// Class type variable used in a method.
|
||||
_registerNeedsThis(onlyIfNeedsRti: onlyIfNeedsRti);
|
||||
_registerNeedsThis(usage);
|
||||
}
|
||||
break;
|
||||
case TypeVariableKind.method:
|
||||
case TypeVariableKind.local:
|
||||
_useTypeVariableAsLocal(typeVariable,
|
||||
onlyForRtiChecks: onlyIfNeedsRti);
|
||||
_useTypeVariableAsLocal(typeVariable, usage);
|
||||
break;
|
||||
case TypeVariableKind.function:
|
||||
// The type variable is a function type variable, like `T` in
|
||||
|
@ -540,11 +667,11 @@ class CapturedScopeBuilder extends ir.Visitor {
|
|||
|
||||
/// 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(TypeVariableTypeWithContext typeVariable,
|
||||
{bool onlyForRtiChecks: false}) {
|
||||
void _useTypeVariableAsLocal(
|
||||
TypeVariableTypeWithContext typeVariable, VariableUse usage) {
|
||||
if (typeVariable.kind != TypeVariableKind.cls && !_options.strongMode) {
|
||||
return;
|
||||
}
|
||||
_markVariableAsUsed(typeVariable, onlyForRtiChecks: onlyForRtiChecks);
|
||||
_markVariableAsUsed(typeVariable, usage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,11 +76,7 @@ class JsBackendStrategy implements KernelBackendStrategy {
|
|||
_elementEnvironment = _elementMap.elementEnvironment;
|
||||
_commonElements = _elementMap.commonElements;
|
||||
_closureDataLookup = new KernelClosureConversionTask(
|
||||
_compiler.measurer,
|
||||
_elementMap,
|
||||
_globalLocalsMap,
|
||||
_compiler.options.enableTypeAssertions,
|
||||
_compiler.options.strongMode);
|
||||
_compiler.measurer, _elementMap, _globalLocalsMap, _compiler.options);
|
||||
JsClosedWorldBuilder closedWorldBuilder = new JsClosedWorldBuilder(
|
||||
_elementMap, _closureDataLookup, _compiler.options);
|
||||
return closedWorldBuilder._convertClosedWorld(
|
||||
|
@ -281,11 +277,9 @@ class JsClosedWorldBuilder {
|
|||
if (_options.disableRtiOptimization) {
|
||||
rtiNeed = new TrivialRuntimeTypesNeed();
|
||||
callMethods = _closureConversionTask.createClosureEntities(
|
||||
this, map.toBackendMemberMap(closureModels, identity),
|
||||
localFunctionNeedsSignature: (_) => true,
|
||||
classNeedsTypeArguments: (_) => true,
|
||||
methodNeedsTypeArguments: (_) => true,
|
||||
localFunctionNeedsTypeArguments: (_) => true);
|
||||
this,
|
||||
map.toBackendMemberMap(closureModels, identity),
|
||||
const TrivialClosureRtiNeed());
|
||||
} else {
|
||||
RuntimeTypesNeedImpl kernelRtiNeed = closedWorld.rtiNeed;
|
||||
Set<ir.Node> localFunctionsNodesNeedingSignature = new Set<ir.Node>();
|
||||
|
@ -308,23 +302,14 @@ class JsClosedWorldBuilder {
|
|||
RuntimeTypesNeedImpl jRtiNeed =
|
||||
_convertRuntimeTypesNeed(map, backendUsage, kernelRtiNeed);
|
||||
callMethods = _closureConversionTask.createClosureEntities(
|
||||
this, map.toBackendMemberMap(closureModels, identity),
|
||||
localFunctionNeedsSignature: (ir.Node node) {
|
||||
assert(node is ir.FunctionDeclaration ||
|
||||
node is ir.FunctionExpression);
|
||||
return backendUsage.isRuntimeTypeUsed
|
||||
? true
|
||||
: localFunctionsNodesNeedingSignature.contains(node);
|
||||
},
|
||||
classNeedsTypeArguments: jRtiNeed.classNeedsTypeArguments,
|
||||
methodNeedsTypeArguments: jRtiNeed.methodNeedsTypeArguments,
|
||||
localFunctionNeedsTypeArguments: (ir.Node node) {
|
||||
assert(node is ir.FunctionDeclaration ||
|
||||
node is ir.FunctionExpression);
|
||||
return backendUsage.isRuntimeTypeUsed
|
||||
? true
|
||||
: localFunctionsNodesNeedingTypeArguments.contains(node);
|
||||
});
|
||||
this,
|
||||
map.toBackendMemberMap(closureModels, identity),
|
||||
new JsClosureRtiNeed(
|
||||
backendUsage,
|
||||
jRtiNeed,
|
||||
localFunctionsNodesNeedingTypeArguments,
|
||||
localFunctionsNodesNeedingSignature,
|
||||
));
|
||||
|
||||
List<FunctionEntity> callMethodsNeedingSignature = <FunctionEntity>[];
|
||||
for (ir.Node node in localFunctionsNodesNeedingSignature) {
|
||||
|
@ -773,3 +758,53 @@ class TypeConverter extends DartTypeVisitor<DartType, Null> {
|
|||
List<DartType> _visitList(List<DartType> list) =>
|
||||
list.map<DartType>((t) => t.accept(this, null)).toList();
|
||||
}
|
||||
|
||||
class TrivialClosureRtiNeed implements ClosureRtiNeed {
|
||||
const TrivialClosureRtiNeed();
|
||||
|
||||
bool localFunctionNeedsSignature(ir.Node node) => true;
|
||||
bool classNeedsTypeArguments(ClassEntity cls) => true;
|
||||
bool methodNeedsTypeArguments(FunctionEntity method) => true;
|
||||
bool localFunctionNeedsTypeArguments(ir.Node node) => true;
|
||||
bool selectorNeedsTypeArguments(Selector selector) => true;
|
||||
bool methodNeedsSignature(MemberEntity method) => true;
|
||||
}
|
||||
|
||||
class JsClosureRtiNeed implements ClosureRtiNeed {
|
||||
final BackendUsage backendUsage;
|
||||
final RuntimeTypesNeed rtiNeed;
|
||||
final Set<ir.Node> localFunctionsNodesNeedingTypeArguments;
|
||||
final Set<ir.Node> localFunctionsNodesNeedingSignature;
|
||||
|
||||
JsClosureRtiNeed(
|
||||
this.backendUsage,
|
||||
this.rtiNeed,
|
||||
this.localFunctionsNodesNeedingTypeArguments,
|
||||
this.localFunctionsNodesNeedingSignature);
|
||||
|
||||
bool localFunctionNeedsSignature(ir.Node node) {
|
||||
assert(node is ir.FunctionDeclaration || node is ir.FunctionExpression);
|
||||
return backendUsage.isRuntimeTypeUsed
|
||||
? true
|
||||
: localFunctionsNodesNeedingSignature.contains(node);
|
||||
}
|
||||
|
||||
bool classNeedsTypeArguments(ClassEntity cls) =>
|
||||
rtiNeed.classNeedsTypeArguments(cls);
|
||||
|
||||
bool methodNeedsTypeArguments(FunctionEntity method) =>
|
||||
rtiNeed.methodNeedsTypeArguments(method);
|
||||
|
||||
bool localFunctionNeedsTypeArguments(ir.Node node) {
|
||||
assert(node is ir.FunctionDeclaration || node is ir.FunctionExpression);
|
||||
return backendUsage.isRuntimeTypeUsed
|
||||
? true
|
||||
: localFunctionsNodesNeedingTypeArguments.contains(node);
|
||||
}
|
||||
|
||||
bool selectorNeedsTypeArguments(Selector selector) =>
|
||||
rtiNeed.selectorNeedsTypeArguments(selector);
|
||||
|
||||
bool methodNeedsSignature(MemberEntity method) =>
|
||||
rtiNeed.methodNeedsSignature(method);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ import 'package:expect/expect.dart';
|
|||
|
||||
@NoInline()
|
||||
method<T>() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T],free=[T]*/ dynamic local() => <T>[];
|
||||
/**/ dynamic local() => <T>[];
|
||||
return local;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,7 @@ class A<T> {
|
|||
/*element: A.method:hasThis*/
|
||||
@NoInline()
|
||||
method() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.hasThis*/
|
||||
/*kernel.hasThis*/
|
||||
/*strong.fields=[this],free=[this],hasThis*/
|
||||
dynamic local() => <T>[];
|
||||
/*hasThis*/ dynamic local() => <T>[];
|
||||
return local;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ import 'package:expect/expect.dart';
|
|||
|
||||
@NoInline()
|
||||
method<T>() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T],free=[T]*/ dynamic local() => <T, int>{};
|
||||
/**/ dynamic local() => <T, int>{};
|
||||
return local;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,7 @@ class A<T> {
|
|||
/*element: A.method:hasThis*/
|
||||
@NoInline()
|
||||
method() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*ast.hasThis*/
|
||||
/*kernel.hasThis*/
|
||||
/*strong.fields=[this],free=[this],hasThis*/
|
||||
dynamic local() => <T, int>{};
|
||||
/*hasThis*/ dynamic local() => <T, int>{};
|
||||
return local;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@ method2<T>(dynamic o) {
|
|||
|
||||
/*element: method3:*/
|
||||
method3<T>(dynamic o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T,o],free=[T,o]*/
|
||||
/*fields=[o],free=[o]*/
|
||||
T local() => o;
|
||||
return local;
|
||||
}
|
||||
|
|
|
@ -38,10 +38,7 @@ class Class2<T> {
|
|||
class Class3<T> {
|
||||
/*element: Class3.method3:hasThis*/
|
||||
method3(dynamic o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.fields=[o],free=[o],hasThis*/
|
||||
/*kernel.fields=[o],free=[o],hasThis*/
|
||||
/*strong.fields=[o,this],free=[o,this],hasThis*/
|
||||
/*fields=[o],free=[o],hasThis*/
|
||||
T local() => o;
|
||||
return local;
|
||||
}
|
||||
|
|
|
@ -3,17 +3,15 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// A sound assignment to a local variable doesn't capture the type variable.
|
||||
/// A sound initialization of a local variable doesn't capture the type
|
||||
/// variable.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*element: Class1.:hasThis*/
|
||||
class Class1<T> {
|
||||
/*element: Class1.method1:hasThis*/
|
||||
method1(T o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.fields=[o],free=[o],hasThis*/
|
||||
/*kernel.fields=[o],free=[o],hasThis*/
|
||||
/*strong.fields=[o,this],free=[o,this],hasThis*/
|
||||
/*fields=[o],free=[o],hasThis*/
|
||||
dynamic local() {
|
||||
T t = o;
|
||||
return t;
|
||||
|
@ -23,6 +21,25 @@ class Class1<T> {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// A sound assignment to a local variable doesn't capture the type variable.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*element: Class1b.:hasThis*/
|
||||
class Class1b<T> {
|
||||
/*element: Class1b.method1b:hasThis*/
|
||||
method1b(T o) {
|
||||
/*fields=[o],free=[o],hasThis*/
|
||||
dynamic local() {
|
||||
T t = null;
|
||||
t = o;
|
||||
return t;
|
||||
}
|
||||
|
||||
return local;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// A local function parameter type is only captured in strong mode.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -139,6 +156,7 @@ class Class8<T> {
|
|||
|
||||
main() {
|
||||
new Class1<int>().method1(0).call();
|
||||
new Class1b<int>().method1b(0).call();
|
||||
new Class2<int>().method2().call(0);
|
||||
new Class3<int>().method3(0).call();
|
||||
new Class4<int>().method4(0).call();
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
method1<T>(T o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T,o],free=[T,o]*/
|
||||
/*fields=[o],free=[o]*/
|
||||
dynamic local() {
|
||||
T t = o;
|
||||
return t;
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
method1<T>(T o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T,o],free=[T,o]*/
|
||||
/*fields=[o],free=[o]*/
|
||||
dynamic local() {
|
||||
T t = o;
|
||||
return t;
|
||||
|
@ -22,8 +21,7 @@ method1<T>(T o) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
method2<T>() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T],free=[T]*/
|
||||
/**/
|
||||
dynamic local(T t) => t;
|
||||
return local;
|
||||
}
|
||||
|
@ -33,8 +31,7 @@ method2<T>() {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
method3<T>(dynamic o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T,o],free=[T,o]*/
|
||||
/*fields=[o],free=[o]*/
|
||||
T local() => o;
|
||||
return local;
|
||||
}
|
||||
|
@ -64,8 +61,7 @@ T method5<T>(dynamic o) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
method6<T>() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T],free=[T]*/
|
||||
/**/
|
||||
dynamic local(T t) {
|
||||
/*fields=[t],free=[t]*/
|
||||
dynamic inner() => t;
|
||||
|
@ -80,8 +76,7 @@ method6<T>() {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
method7<T>(dynamic o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T,o],free=[T,o]*/
|
||||
/*fields=[o],free=[o]*/
|
||||
T local() {
|
||||
/*fields=[o],free=[o]*/
|
||||
dynamic inner() => o;
|
||||
|
|
|
@ -10,10 +10,7 @@
|
|||
class Class1<T> {
|
||||
/*element: Class1.method1:hasThis*/
|
||||
method1(T o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.fields=[o],free=[o],hasThis*/
|
||||
/*kernel.fields=[o],free=[o],hasThis*/
|
||||
/*strong.fields=[o,this],free=[o,this],hasThis*/
|
||||
/*fields=[o],free=[o],hasThis*/
|
||||
dynamic local() {
|
||||
T t = o;
|
||||
return t;
|
||||
|
@ -31,10 +28,7 @@ class Class1<T> {
|
|||
class Class2<T> {
|
||||
/*element: Class2.method2:hasThis*/
|
||||
method2() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.hasThis*/
|
||||
/*kernel.hasThis*/
|
||||
/*strong.fields=[this],free=[this],hasThis*/
|
||||
/*hasThis*/
|
||||
dynamic local(T t) => t;
|
||||
return local;
|
||||
}
|
||||
|
@ -48,10 +42,7 @@ class Class2<T> {
|
|||
class Class3<T> {
|
||||
/*element: Class3.method3:hasThis*/
|
||||
method3(dynamic o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.fields=[o],free=[o],hasThis*/
|
||||
/*kernel.fields=[o],free=[o],hasThis*/
|
||||
/*strong.fields=[o,this],free=[o,this],hasThis*/
|
||||
/*fields=[o],free=[o],hasThis*/
|
||||
T local() => o;
|
||||
return local;
|
||||
}
|
||||
|
@ -93,10 +84,7 @@ class Class5<T> {
|
|||
class Class6<T> {
|
||||
/*element: Class6.method6:hasThis*/
|
||||
method6() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.hasThis*/
|
||||
/*kernel.hasThis*/
|
||||
/*strong.fields=[this],free=[this],hasThis*/
|
||||
/*hasThis*/
|
||||
dynamic local(T t) {
|
||||
/*fields=[t],free=[t],hasThis*/
|
||||
dynamic inner() => t;
|
||||
|
@ -115,10 +103,7 @@ class Class6<T> {
|
|||
class Class7<T> {
|
||||
/*element: Class7.method7:hasThis*/
|
||||
method7(dynamic o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.fields=[o],free=[o],hasThis*/
|
||||
/*kernel.fields=[o],free=[o],hasThis*/
|
||||
/*strong.fields=[o,this],free=[o,this],hasThis*/
|
||||
/*fields=[o],free=[o],hasThis*/
|
||||
T local() {
|
||||
/*fields=[o],free=[o],hasThis*/
|
||||
dynamic inner() => o;
|
||||
|
|
|
@ -32,10 +32,7 @@ class Class2a<T> {}
|
|||
class Class2b<T> {
|
||||
/*element: Class2b.method2:hasThis*/
|
||||
method2() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*ast.hasThis*/
|
||||
/*kernel.hasThis*/
|
||||
/*strong.fields=[this],free=[this],hasThis*/
|
||||
/*hasThis*/
|
||||
dynamic local() => new Class2a<T>();
|
||||
return local;
|
||||
}
|
||||
|
|
|
@ -32,8 +32,7 @@ class Class2a<T> {}
|
|||
class Class2b<T> {
|
||||
/*element: Class2b.method2:hasThis*/
|
||||
method2() {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `this`.
|
||||
/*fields=[this],free=[this],hasThis*/
|
||||
/*hasThis*/
|
||||
dynamic local() => new Class2a<T>();
|
||||
return local;
|
||||
}
|
||||
|
@ -60,8 +59,7 @@ method3b<T>(o) {
|
|||
method4a<T>(o) => o;
|
||||
|
||||
method4b<T>(o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T,o],free=[T,o]*/
|
||||
/*fields=[o],free=[o]*/
|
||||
dynamic local() => method4a<T>(o);
|
||||
return local;
|
||||
}
|
||||
|
@ -95,8 +93,7 @@ class Class6a {
|
|||
}
|
||||
|
||||
method6b<T>(o) {
|
||||
// TODO(johnniwinther): Improve rti tracking to avoid capture of `T`.
|
||||
/*fields=[T,o],free=[T,o]*/
|
||||
/*fields=[o],free=[o]*/
|
||||
dynamic local() => new Class6a().method6a<T>(o);
|
||||
return local;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue