mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
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:
parent
ed431d5a61
commit
cbd05542cc
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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].
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in a new issue