Created a signature method on closures that returns the type of the closure function.

Some additional optimization work will need to be done at the end so
that we take out these functions when they are not needed (when we
don't have type variables).


Bug:
Change-Id: I28d59d04844ec18510b9befe45f26f4109d86ffa
Reviewed-on: https://dart-review.googlesource.com/32667
Commit-Queue: Emily Fortuna <efortuna@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Emily Fortuna 2018-01-13 00:27:22 +00:00 committed by commit-bot@chromium.org
parent ed431d5a61
commit cbd05542cc
14 changed files with 192 additions and 33 deletions

View file

@ -39,6 +39,7 @@ class Flags {
/// See [CompilerOptions.useKernel] for details.
static const String useKernel = '--use-kernel';
static const String strongMode = '--strong';
static const String addMethodSignatures = '--method-signatures';
static const String platformBinaries = '--platform-binaries=.+';
static const String minify = '--minify';

View file

@ -401,6 +401,7 @@ Future<api.CompilationResult> compile(List<String> argv,
new OptionHandler(Flags.enableExperimentalMirrors, passThrough),
new OptionHandler(Flags.enableAssertMessage, passThrough),
new OptionHandler(Flags.strongMode, passThrough),
new OptionHandler(Flags.addMethodSignatures, passThrough),
// TODO(floitsch): remove conditional directives flag.
// We don't provide the info-message yet, since we haven't publicly

View file

@ -213,6 +213,8 @@ class KernelInferrerEngine extends InferrerEngineImpl<ir.Node> {
break;
case MemberKind.closureField:
break;
case MemberKind.signature:
break;
}
failedAt(member, 'Unexpected member definition: $definition.');
return null;

View file

@ -94,8 +94,12 @@ 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._globalLocalsMap, this._addTypeChecks, this._disableRtiOptimization)
: super(measurer);
/// The combined steps of generating our intermediate representation of
@ -116,7 +120,8 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
MemberEntity outermostEntity) {
if (localFunctionsNeedingRti.contains(node) ||
classesNeedingRti.contains(outermostEntity.enclosingClass) ||
_addTypeChecks) {
_addTypeChecks ||
_disableRtiOptimization) {
if (outermostEntity is FunctionEntity &&
outermostEntity is! ConstructorEntity) {
scope.thisUsedAsFreeVariable = scope.thisUsedAsFreeVariableIfNeedsRti ||
@ -178,6 +183,7 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
classesNeedingRti);
// Add also for the call method.
_scopeMap[closureClassInfo.callMethod] = closureClassInfo;
_scopeMap[closureClassInfo.signatureMethod] = closureClassInfo;
callMethods.add(closureClassInfo.callMethod);
}
});
@ -209,6 +215,8 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
// to the correct closure class.
_memberClosureRepresentationMap[closureClassInfo.callMethod] =
closureClassInfo;
_memberClosureRepresentationMap[closureClassInfo.signatureMethod] =
closureClassInfo;
_globalLocalsMap.setLocalsMap(closureClassInfo.callMethod, localsMap);
if (node.parent is ir.Member) {
assert(_elementMap.getMember(node.parent) == member);
@ -247,6 +255,7 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
case MemberKind.constructor:
case MemberKind.constructorBody:
case MemberKind.closureCall:
case MemberKind.signature:
return _capturedScopesMap[definition.node] ?? const CapturedScope();
default:
throw failedAt(entity, "Unexpected member definition $definition");
@ -477,6 +486,7 @@ class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope {
class KernelClosureClassInfo extends JsScopeInfo
implements ClosureRepresentationInfo {
JFunction callMethod;
JSignatureMethod signatureMethod;
final Local closureEntity;
final Local thisLocal;
final JClass closureClassEntity;

View file

@ -237,26 +237,18 @@ class CapturedScopeBuilder extends ir.Visitor {
_useTypeVariableAsLocal(new ir.TypeParameterType(typeParameter));
}
if (_executableContext is ir.Member &&
_executableContext is! ir.Field &&
_hasThisLocal) {
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.
// TODO(efortuna): This is not correct for the case of type variables on
// methods. For example, the code:
// class Foo<T> {
// int bar<E>(...) {
// ...use of E...
// }
// }
// We do not need the `this` variable in this case to use E.
// Add code to distinguish between this case and the case of a class
// type variable (like T, where we need `this`).
_registerNeedsThis();
if (_hasThisLocal) {
_registerNeedsThis();
} else {
_useTypeVariableAsLocal(new ir.TypeParameterType(typeParameter));
}
}
}
}
@ -461,7 +453,17 @@ class CapturedScopeBuilder extends ir.Visitor {
@override
visitTypeParameterType(ir.TypeParameterType type) {
_analyzeType(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);
}
}
}
/// Returns true if the node is a field, or a constructor (factory or
@ -471,17 +473,6 @@ class CapturedScopeBuilder extends ir.Visitor {
node is ir.Field ||
(node is ir.Procedure && node.isFactory);
void _analyzeType(ir.TypeParameterType type) {
if (_outermostNode is ir.Member) {
ir.Member outermostMember = _outermostNode;
if (_isFieldOrConstructor(_outermostNode)) {
_useTypeVariableAsLocal(type, onlyForRtiChecks: true);
} else if (outermostMember.isInstanceMember) {
_registerNeedsThis(onlyIfNeedsRti: true);
}
}
}
/// 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,

View file

@ -537,6 +537,18 @@ class JClosureCallMethod extends JMethod {
String get _kind => 'closure_call';
}
/// A method that returns the signature of the Dart closure/tearoff that this
/// method's parent class is representing.
class JSignatureMethod extends JMethod {
JSignatureMethod(LibraryEntity enclosingLibrary, ClassEntity enclosingClass,
ParameterStructure parameterStructure, AsyncMarker asyncMarker)
: super(enclosingLibrary, enclosingClass, const PublicName('\$signature'),
parameterStructure, asyncMarker,
isStatic: false, isExternal: false, isAbstract: false);
String get _kind => 'signature';
}
class JTypeVariable extends IndexedTypeVariable {
final Entity typeDeclaration;
final String name;

View file

@ -73,8 +73,12 @@ 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);
_closureDataLookup = new KernelClosureConversionTask(
_compiler.measurer,
_elementMap,
_globalLocalsMap,
_compiler.options.enableTypeAssertions,
_compiler.options.disableRtiOptimization);
JsClosedWorldBuilder closedWorldBuilder = new JsClosedWorldBuilder(
_elementMap, _closureDataLookup, _compiler.options);
return closedWorldBuilder._convertClosedWorld(

View file

@ -255,6 +255,10 @@ enum MemberKind {
// A field corresponding to a captured variable in the closure. It does not
// have a corresponding ir.Node.
closureField,
// A method that describes the type of a function (in this case the type of
// the closure class. It does not have a corresponding ir.Node or a method
// body.
signature,
}
/// Definition information for a [MemberEntity].

View file

@ -2452,6 +2452,11 @@ class JsKernelToElementMap extends KernelToElementMapBase
_buildClosureClassFields(closureClassInfo, member, memberThisType, info,
localsMap, recordFieldsVisibleInScope, memberMap);
if (options.addMethodSignatures) {
_constructSignatureMethod(closureClassInfo, memberMap, node,
memberThisType, location, typeVariableAccess);
}
FunctionEntity callMethod = new JClosureCallMethod(
closureClassInfo, _getParameterStructure(node), getAsyncMarker(node));
_nestedClosureMap
@ -2602,6 +2607,32 @@ class JsKernelToElementMap extends KernelToElementMapBase
return true;
}
void _constructSignatureMethod(
KernelClosureClassInfo closureClassInfo,
Map<String, MemberEntity> memberMap,
ir.FunctionNode closureSourceNode,
InterfaceType memberThisType,
SourceSpan location,
ClassTypeVariableAccess typeVariableAccess) {
FunctionEntity signatureMethod = new JSignatureMethod(
closureClassInfo.closureClassEntity.library,
closureClassInfo.closureClassEntity,
// SignatureMethod takes no arguments.
const ParameterStructure(0, 0, const [], 0),
getAsyncMarker(closureSourceNode));
_members.register<IndexedFunction, FunctionData>(
signatureMethod,
new SignatureFunctionData(
new SpecialMemberDefinition(signatureMethod,
closureSourceNode.parent, MemberKind.signature),
memberThisType,
null,
closureSourceNode.typeParameters,
typeVariableAccess));
memberMap[signatureMethod.name] =
closureClassInfo.signatureMethod = signatureMethod;
}
_constructClosureField(
Local capturedLocal,
KernelClosureClassInfo closureClassInfo,

View file

@ -644,6 +644,43 @@ class FunctionDataImpl extends MemberDataImpl
}
}
class SignatureFunctionData implements FunctionData {
final FunctionType functionType;
final MemberDefinition definition;
final InterfaceType memberThisType;
final ClassTypeVariableAccess classTypeVariableAccess;
final List<ir.TypeParameter> typeParameters;
SignatureFunctionData(this.definition, this.memberThisType, this.functionType,
this.typeParameters, this.classTypeVariableAccess);
FunctionType getFunctionType(covariant KernelToElementMapBase elementMap) {
return functionType;
}
List<TypeVariableType> getFunctionTypeVariables(
KernelToElementMap elementMap) {
return typeParameters
.map<TypeVariableType>((ir.TypeParameter typeParameter) {
return elementMap.getDartType(new ir.TypeParameterType(typeParameter));
}).toList();
}
void forEachParameter(KernelToElementMapForBuilding elementMap,
void f(DartType type, String name, ConstantValue defaultValue)) {
throw new UnimplementedError('SignatureData.forEachParameter');
}
@override
Iterable<ConstantValue> getMetadata(KernelToElementMap elementMap) {
return const <ConstantValue>[];
}
InterfaceType getMemberThisType(KernelToElementMapForBuilding elementMap) {
return memberThisType;
}
}
abstract class ConstructorData extends FunctionData {
ConstantConstructor getConstructorConstant(
KernelToElementMapBase elementMap, ConstructorEntity constructor);

View file

@ -259,6 +259,12 @@ class CompilerOptions implements DiagnosticOptions {
/// This is an experimental feature.
final String experimentalAllocationsPath;
/// Add signatures to closures that return the type of the original closure
/// call. Currently hidden behind a flag because this interacts with generic
/// function types and strong mode, hitting some edge cases that have not yet
/// been implemented.
final bool addMethodSignatures;
// -------------------------------------------------
// Options for deprecated features
// -------------------------------------------------
@ -336,6 +342,7 @@ class CompilerOptions implements DiagnosticOptions {
sourceMapUri: _extractUriOption(options, '--source-map='),
strips: _extractCsvOption(options, '--force-strip='),
strongMode: _hasOption(options, Flags.strongMode),
addMethodSignatures: _hasOption(options, Flags.addMethodSignatures),
testMode: _hasOption(options, Flags.testMode),
trustJSInteropTypeAnnotations:
_hasOption(options, Flags.trustJSInteropTypeAnnotations),
@ -403,6 +410,7 @@ class CompilerOptions implements DiagnosticOptions {
Uri sourceMapUri: null,
List<String> strips: const [],
bool strongMode: false,
bool addMethodSignatures: false,
bool testMode: false,
bool trustJSInteropTypeAnnotations: false,
bool trustPrimitives: false,
@ -485,6 +493,7 @@ class CompilerOptions implements DiagnosticOptions {
sourceMapUri: sourceMapUri,
strips: strips,
strongMode: strongMode,
addMethodSignatures: addMethodSignatures,
testMode: testMode,
trustJSInteropTypeAnnotations: trustJSInteropTypeAnnotations,
trustPrimitives: trustPrimitives,
@ -539,6 +548,7 @@ class CompilerOptions implements DiagnosticOptions {
this.sourceMapUri: null,
this.strips: const [],
this.strongMode: false,
this.addMethodSignatures: false,
this.testMode: false,
this.trustJSInteropTypeAnnotations: false,
this.trustPrimitives: false,
@ -601,6 +611,7 @@ class CompilerOptions implements DiagnosticOptions {
sourceMapUri,
strips,
strongMode,
addMethodSignatures,
testMode,
trustJSInteropTypeAnnotations,
trustPrimitives,
@ -671,6 +682,7 @@ class CompilerOptions implements DiagnosticOptions {
sourceMapUri: sourceMapUri ?? options.sourceMapUri,
strips: strips ?? options.strips,
strongMode: strongMode ?? options.strongMode,
addMethodSignatures: addMethodSignatures ?? options.addMethodSignatures,
testMode: testMode ?? options.testMode,
trustJSInteropTypeAnnotations: trustJSInteropTypeAnnotations ??
options.trustJSInteropTypeAnnotations,

View file

@ -228,6 +228,23 @@ class KernelSsaGraphBuilder extends ir.Visitor
case MemberKind.closureField:
failedAt(targetElement, "Unexpected closure field: $targetElement");
break;
case MemberKind.signature:
ir.Node target = definition.node;
ir.FunctionNode originalClosureNode;
if (target is ir.Procedure) {
originalClosureNode = target.function;
} else if (target is ir.FunctionExpression) {
originalClosureNode = target.function;
} else if (target is ir.FunctionDeclaration) {
originalClosureNode = target.function;
} else {
failedAt(
targetElement,
"Unexpected function signature: "
"$targetElement inside a non-closure: $target");
}
buildMethodSignature(originalClosureNode);
break;
}
assert(graph.isValid());
@ -482,8 +499,8 @@ class KernelSsaGraphBuilder extends ir.Visitor
// Create the runtime type information, if needed.
bool hasRtiInput = closedWorld.rtiNeed.classNeedsRtiField(cls);
if (hasRtiInput) {
// Read the values of the type arguments and create a HTypeInfoExpression
// to set on the newly create object.
// Read the values of the type arguments and create a
// HTypeInfoExpression to set on the newly created object.
List<HInstruction> typeArguments = <HInstruction>[];
InterfaceType thisType =
_elementMap.elementEnvironment.getThisType(cls);
@ -870,6 +887,35 @@ class KernelSsaGraphBuilder extends ir.Visitor
localsHandler.scopeInfo = oldScopeInfo;
}
/// Constructs a special signature function for a closure. It is unique in
/// that no corresponding ir.Node actually exists for it. We just use the
/// targetElement.
void buildMethodSignature(ir.FunctionNode originalClosureNode) {
openFunction(targetElement);
List<HInstruction> typeArguments = <HInstruction>[];
// Add function type variables.
FunctionType functionType =
_elementMap.getFunctionType(originalClosureNode);
functionType.forEachTypeVariable((TypeVariableType typeVariableType) {
DartType result = localsHandler.substInContext(typeVariableType);
HInstruction argument =
typeBuilder.analyzeTypeArgument(result, sourceElement);
typeArguments.add(argument);
});
push(new HTypeInfoExpression(
TypeInfoExpressionKind.COMPLETE,
_elementMap.getFunctionType(originalClosureNode),
typeArguments,
commonMasks.functionType));
HInstruction value = pop();
close(new HReturn(
value, _sourceInformationBuilder.buildReturn(originalClosureNode)))
.addSuccessor(graph.exit);
closeFunction();
}
/// Builds generative constructor body.
void buildConstructorBody(ir.Constructor constructor) {
openFunction(

View file

@ -203,7 +203,14 @@ class _FunctionUsage extends _MemberUsage {
bool hasInvoke = false;
bool hasRead = false;
_FunctionUsage(FunctionEntity function) : super.internal(function);
_FunctionUsage(FunctionEntity function) : super.internal(function) {
if (function is JSignatureMethod) {
// We mark signature methods as "always used" to prevent them from being
// optimized away.
// TODO(johnniwinther): Make this a part of the regular enqueueing.
invoke();
}
}
EnumSet<MemberUse> get _originalUse =>
entity.isInstanceMember ? MemberUses.ALL_INSTANCE : MemberUses.ALL_STATIC;

View file

@ -25,6 +25,7 @@ import '../js_backend/native_data.dart' show NativeBasicData, NativeDataBuilder;
import '../js_backend/no_such_method_registry.dart';
import '../js_backend/runtime_types.dart';
import '../js_model/locals.dart';
import '../js_model/elements.dart' show JSignatureMethod;
import '../kernel/element_map_impl.dart';
import '../native/enqueue.dart' show NativeResolutionEnqueuer;
import '../options.dart';