Index: dart/sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart |
diff --git a/dart/sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart b/dart/sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart |
index b8e8210ab1136caf110312b00277963789a796ed..1ebb578d2635a53beb60969f7b8d5d76255c2bab 100644 |
--- a/dart/sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart |
+++ b/dart/sdk/lib/_internal/compiler/implementation/js_emitter/container_builder.dart |
@@ -25,7 +25,7 @@ class ContainerBuilder extends CodeEmitterHelper { |
*/ |
void addParameterStub(FunctionElement member, |
Selector selector, |
- DefineStubFunction defineStub, |
+ AddStubFunction addStub, |
Set<String> alreadyGenerated) { |
FunctionSignature parameters = member.computeSignature(compiler); |
int positionalArgumentCount = selector.positionalArgumentCount; |
@@ -81,10 +81,7 @@ class ContainerBuilder extends CodeEmitterHelper { |
int parameterIndex = 0; |
parameters.orderedForEachParameter((Element element) { |
- // Use generic names for closures to facilitate code sharing. |
- String jsName = member is ClosureInvocationElement |
- ? 'p${parameterIndex++}' |
- : backend.namer.safeName(element.name); |
+ String jsName = backend.namer.safeName(element.name); |
assert(jsName != receiverArgumentName); |
if (count < optionalParameterStart) { |
parametersBuffer[count] = new jsAst.Parameter(jsName); |
@@ -121,25 +118,30 @@ class ContainerBuilder extends CodeEmitterHelper { |
member, isInterceptedMethod, invocationName, |
parametersBuffer, argumentsBuffer, |
indexOfLastOptionalArgumentInParameters); |
- } else { |
+ } else if (member.isInstanceMember()) { |
body = [js.return_( |
js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; |
+ } else { |
+ body = [js.return_(namer.elementAccess(member)(argumentsBuffer))]; |
} |
jsAst.Fun function = js.fun(parametersBuffer, body); |
- defineStub(invocationName, function); |
+ addStub(selector, function); |
+ } |
- String reflectionName = task.getReflectionName(selector, invocationName); |
- if (reflectionName != null) { |
- var reflectable = |
- js(backend.isAccessibleByReflection(member) ? '1' : '0'); |
- defineStub('+$reflectionName', reflectable); |
+ void addParameterStubs(FunctionElement member, AddStubFunction defineStub, |
+ [bool canTearOff = false]) { |
+ if (member.enclosingElement.isClosure()) { |
+ ClosureClassElement cls = member.enclosingElement; |
+ if (cls.supertype.element == compiler.boundClosureClass) { |
+ compiler.internalErrorOnElement(cls.methodElement, 'Bound closure1.'); |
ngeoffray
2013/12/09 11:15:58
Maybe add a better error message.
ahe
2013/12/09 17:06:47
Johnni said the same, so I'm going to repeat my an
ngeoffray
2013/12/09 17:14:55
BoundClosure1 may be enough for you, but besides t
|
+ } |
+ if (cls.methodElement.isInstanceMember()) { |
+ compiler.internalErrorOnElement(cls.methodElement, 'Bound closure2.'); |
ngeoffray
2013/12/09 11:15:58
Ditto.
ahe
2013/12/09 17:06:47
Ditto :-)
|
+ } |
} |
- } |
- void addParameterStubs(FunctionElement member, |
- DefineStubFunction defineStub) { |
// We fill the lists depending on the selector. For example, |
// take method foo: |
// foo(a, b, {c, d}); |
@@ -165,331 +167,59 @@ class ContainerBuilder extends CodeEmitterHelper { |
// (4) No stub generated, call is direct. |
// (5) No stub generated, call is direct. |
- // Keep a cache of which stubs have already been generated, to |
- // avoid duplicates. Note that even if selectors are |
- // canonicalized, we would still need this cache: a typed selector |
- // on A and a typed selector on B could yield the same stub. |
- Set<String> generatedStubNames = new Set<String>(); |
- bool isClosureInvocation = |
- member.name == namer.closureInvocationSelectorName; |
- if (backend.isNeededForReflection(member) || |
- (compiler.enabledFunctionApply && isClosureInvocation)) { |
- // If [Function.apply] is called, we pessimistically compile all |
- // possible stubs for this closure. |
- FunctionSignature signature = member.computeSignature(compiler); |
- Set<Selector> selectors = signature.optionalParametersAreNamed |
- ? computeSeenNamedSelectors(member) |
- : computeOptionalSelectors(signature, member); |
- for (Selector selector in selectors) { |
- addParameterStub(member, selector, defineStub, generatedStubNames); |
- } |
- if (signature.optionalParametersAreNamed && isClosureInvocation) { |
- addCatchAllParameterStub(member, signature, defineStub); |
- } |
- } else { |
- Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; |
+ Set<Selector> selectors = member.isInstanceMember() |
ngeoffray
2013/12/09 11:15:58
For readability, I would not initialize this set h
ahe
2013/12/09 17:06:47
Done.
|
+ ? compiler.codegenWorld.invokedNames[member.name] |
+ : null; // No stubs needed for static methods. |
+ |
+ /// Returns all closure call selectors renamed to match this member. |
+ Set<Selector> callSelectorsAsNamed() { |
+ if (!canTearOff) return null; |
+ Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ |
+ namer.closureInvocationSelectorName]; |
+ if (callSelectors == null) return null; |
+ return callSelectors.map((Selector callSelector) { |
+ return new Selector.call( |
+ member.name, member.getLibrary(), |
+ callSelector.argumentCount, callSelector.namedArguments); |
+ }).toSet(); |
+ } |
+ if (selectors == null) { |
+ selectors = callSelectorsAsNamed(); |
if (selectors == null) return; |
- for (Selector selector in selectors) { |
- if (!selector.applies(member, compiler)) continue; |
- addParameterStub(member, selector, defineStub, generatedStubNames); |
+ } else { |
+ Set<Selector> callSelectors = callSelectorsAsNamed(); |
+ if (callSelectors != null) { |
+ selectors = selectors.union(callSelectors); |
} |
} |
- } |
- |
- Set<Selector> computeSeenNamedSelectors(FunctionElement element) { |
- Set<Selector> selectors = compiler.codegenWorld.invokedNames[element.name]; |
- Set<Selector> result = new Set<Selector>(); |
- if (selectors == null) return result; |
- for (Selector selector in selectors) { |
- if (!selector.applies(element, compiler)) continue; |
- result.add(selector); |
- } |
- return result; |
- } |
- |
- void addCatchAllParameterStub(FunctionElement member, |
- FunctionSignature signature, |
- DefineStubFunction defineStub) { |
- // See Primities.applyFunction in js_helper.dart for details. |
- List<jsAst.Property> properties = <jsAst.Property>[]; |
- for (Element element in signature.orderedOptionalParameters) { |
- String jsName = backend.namer.safeName(element.name); |
- Constant value = compiler.constantHandler.initialVariableValues[element]; |
- jsAst.Expression reference = null; |
- if (value == null) { |
- reference = new jsAst.LiteralNull(); |
- } else { |
- reference = task.constantReference(value); |
+ Set<Selector> untypedSelectors = new Set<Selector>(); |
+ if (selectors != null) { |
+ for (Selector selector in selectors) { |
+ if (!selector.appliesUnnamed(member, compiler)) continue; |
+ if (untypedSelectors.add(selector.asUntyped)) { |
+ // TODO(ahe): Is the last argument to [addParameterStub] needed? |
+ addParameterStub(member, selector, defineStub, new Set<String>()); |
+ } |
} |
- properties.add(new jsAst.Property(js.string(jsName), reference)); |
- } |
- defineStub( |
- backend.namer.callCatchAllName, |
- js.fun([], js.return_(new jsAst.ObjectInitializer(properties)))); |
- } |
- |
- /** |
- * Compute the set of possible selectors in the presence of optional |
- * non-named parameters. |
- */ |
- Set<Selector> computeOptionalSelectors(FunctionSignature signature, |
- FunctionElement element) { |
- Set<Selector> selectors = new Set<Selector>(); |
- // Add the selector that does not have any optional argument. |
- selectors.add(new Selector(SelectorKind.CALL, |
- element.name, |
- element.getLibrary(), |
- signature.requiredParameterCount, |
- <String>[])); |
- |
- // For each optional parameter, we increment the number of passed |
- // argument. |
- for (int i = 1; i <= signature.optionalParameterCount; i++) { |
- selectors.add(new Selector(SelectorKind.CALL, |
- element.name, |
- element.getLibrary(), |
- signature.requiredParameterCount + i, |
- <String>[])); |
} |
- return selectors; |
- } |
- |
- void emitStaticFunctionGetters(CodeBuffer eagerBuffer) { |
- task.addComment('Static function getters', task.mainBuffer); |
- for (FunctionElement element in |
- Elements.sortedByPosition(staticGetters.keys)) { |
- Element closure = staticGetters[element]; |
- CodeBuffer buffer = |
- task.isDeferred(element) ? task.deferredConstants : eagerBuffer; |
- String closureClass = namer.isolateAccess(closure); |
- String name = namer.getStaticClosureName(element); |
- |
- String closureName = namer.getStaticClosureName(element); |
- jsAst.Node assignment = js( |
- 'init.globalFunctions["$closureName"] =' |
- ' ${namer.globalObjectFor(element)}.$name =' |
- ' new $closureClass(#, "$closureName")', |
- namer.elementAccess(element)); |
- buffer.write(jsAst.prettyPrint(assignment, compiler)); |
- buffer.write('$N'); |
- } |
- } |
- |
- void emitStaticFunctionClosures() { |
- Set<FunctionElement> functionsNeedingGetter = |
- compiler.codegenWorld.staticFunctionsNeedingGetter; |
- for (FunctionElement element in |
- Elements.sortedByPosition(functionsNeedingGetter)) { |
- String superName = namer.getNameOfClass(compiler.closureClass); |
- int parameterCount = element.functionSignature.parameterCount; |
- String name = 'Closure\$$parameterCount'; |
- assert(task.instantiatedClasses.contains(compiler.closureClass)); |
- |
- ClassElement closureClassElement = new ClosureClassElement( |
- null, name, compiler, element, |
- element.getCompilationUnit()); |
- // Now add the methods on the closure class. The instance method does not |
- // have the correct name. Since [addParameterStubs] use the name to create |
- // its stubs we simply create a fake element with the correct name. |
- // Note: the callElement will not have any enclosingElement. |
- FunctionElement callElement = |
- new ClosureInvocationElement(namer.closureInvocationSelectorName, |
- element); |
- |
- String invocationName = namer.instanceMethodName(callElement); |
- String mangledName = namer.getNameOfClass(closureClassElement); |
- |
- // Define the constructor with a name so that Object.toString can |
- // find the class name of the closure class. |
- ClassBuilder closureBuilder = new ClassBuilder(); |
- // If a static function is used as a closure we need to add its name |
- // in case it is used in spawnFunction. |
- String methodName = namer.STATIC_CLOSURE_NAME_NAME; |
- List<String> fieldNames = <String>[invocationName, methodName]; |
- closureBuilder.addProperty('', |
- js.string("$superName;${fieldNames.join(',')}")); |
- |
- addParameterStubs(callElement, closureBuilder.addProperty); |
- |
- void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
- RuntimeTypes rti = backend.rti; |
- // [:() => null:] is dummy encoding of [this] which is never needed for |
- // the encoding of the type of the static [method]. |
- jsAst.Expression encoding = |
- rti.getSignatureEncoding(methodType, js('null')); |
- String operatorSignature = namer.operatorSignature(); |
- // TODO(johnniwinther): Make MiniJsParser support function expressions. |
- closureBuilder.addProperty(operatorSignature, encoding); |
+ if (canTearOff) { |
+ selectors = compiler.codegenWorld.invokedNames[ |
+ namer.closureInvocationSelectorName]; |
+ if (selectors != null) { |
+ for (Selector selector in selectors) { |
+ selector = new Selector.call( |
+ member.name, member.getLibrary(), |
+ selector.argumentCount, selector.namedArguments); |
+ if (!selector.appliesUnnamed(member, compiler)) continue; |
+ if (untypedSelectors.add(selector)) { |
+ // TODO(ahe): Is the last argument to [addParameterStub] needed? |
+ addParameterStub(member, selector, defineStub, new Set<String>()); |
+ } |
+ } |
} |
- |
- FunctionType methodType = element.computeType(compiler); |
- Map<FunctionType, bool> functionTypeChecks = |
- task.typeTestEmitter.getFunctionTypeChecksOn(methodType); |
- task.typeTestEmitter.generateFunctionTypeTests( |
- element, methodType, functionTypeChecks, |
- emitFunctionTypeSignature); |
- |
- closureClassElement = |
- addClosureIfNew(closureBuilder, closureClassElement, fieldNames); |
- staticGetters[element] = closureClassElement; |
- |
} |
} |
- ClassElement addClosureIfNew(ClassBuilder builder, |
- ClassElement closure, |
- List<String> fieldNames) { |
- String key = |
- jsAst.prettyPrint(builder.toObjectInitializer(), compiler).getText(); |
- return methodClosures.putIfAbsent(key, () { |
- String mangledName = namer.getNameOfClass(closure); |
- emitClosureInPrecompiledFunction(mangledName, fieldNames); |
- return closure; |
- }); |
- } |
- |
- void emitClosureInPrecompiledFunction(String mangledName, |
- List<String> fieldNames) { |
- List<String> fields = fieldNames; |
- String constructorName = mangledName; |
- task.precompiledFunction.add(new jsAst.FunctionDeclaration( |
- new jsAst.VariableDeclaration(constructorName), |
- js.fun(fields, fields.map( |
- (name) => js('this.$name = $name')).toList()))); |
- task.precompiledFunction.addAll([ |
- js('$constructorName.builtin\$cls = "$constructorName"'), |
- js('\$desc=\$collectedClasses.$constructorName'), |
- js.if_('\$desc instanceof Array', js('\$desc = \$desc[1]')), |
- js('$constructorName.prototype = \$desc'), |
- ]); |
- |
- task.precompiledConstructorNames.add(js(constructorName)); |
- } |
- |
- /** |
- * Documentation wanted -- johnniwinther |
- * |
- * Invariant: [member] must be a declaration element. |
- */ |
- void emitDynamicFunctionGetter(FunctionElement member, |
- DefineStubFunction defineStub) { |
- assert(invariant(member, member.isDeclaration)); |
- assert(task.instantiatedClasses.contains(compiler.boundClosureClass)); |
- // For every method that has the same name as a property-get we create a |
- // getter that returns a bound closure. Say we have a class 'A' with method |
- // 'foo' and somewhere in the code there is a dynamic property get of |
- // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): |
- // |
- // class A { |
- // foo(x, y, z) { ... } // Original function. |
- // get foo { return new BoundClosure499(this, "foo"); } |
- // } |
- // class BoundClosure499 extends BoundClosure { |
- // BoundClosure499(this.self, this.name); |
- // $call3(x, y, z) { return self[name](x, y, z); } |
- // } |
- |
- bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; |
- int parameterCount = member.parameterCount(compiler); |
- |
- // Intercepted methods take an extra parameter, which is the |
- // receiver of the call. |
- bool inInterceptor = backend.isInterceptedMethod(member); |
- List<String> fieldNames = <String>[]; |
- compiler.boundClosureClass.forEachInstanceField((_, Element field) { |
- fieldNames.add(namer.instanceFieldPropertyName(field)); |
- }); |
- |
- ClassElement classElement = member.getEnclosingClass(); |
- String name = inInterceptor |
- ? 'BoundClosure\$i${parameterCount}' |
- : 'BoundClosure\$${parameterCount}'; |
- |
- ClassElement closureClassElement = new ClosureClassElement( |
- null, name, compiler, member, |
- member.getCompilationUnit()); |
- String superName = namer.getNameOfClass(closureClassElement.superclass); |
- |
- // Define the constructor with a name so that Object.toString can |
- // find the class name of the closure class. |
- ClassBuilder boundClosureBuilder = new ClassBuilder(); |
- boundClosureBuilder.addProperty('', |
- js.string("$superName;${fieldNames.join(',')}")); |
- // Now add the methods on the closure class. The instance method does not |
- // have the correct name. Since [addParameterStubs] use the name to create |
- // its stubs we simply create a fake element with the correct name. |
- // Note: the callElement will not have any enclosingElement. |
- FunctionElement callElement = new ClosureInvocationElement( |
- namer.closureInvocationSelectorName, member); |
- |
- String invocationName = namer.instanceMethodName(callElement); |
- |
- List<String> parameters = <String>[]; |
- List<jsAst.Expression> arguments = |
- <jsAst.Expression>[js('this')[fieldNames[0]]]; |
- if (inInterceptor) { |
- arguments.add(js('this')[fieldNames[2]]); |
- } |
- for (int i = 0; i < parameterCount; i++) { |
- String name = 'p$i'; |
- parameters.add(name); |
- arguments.add(js(name)); |
- } |
- |
- jsAst.Expression fun = js.fun( |
- parameters, |
- js.return_( |
- js('this')[fieldNames[1]]['call'](arguments))); |
- boundClosureBuilder.addProperty(invocationName, fun); |
- |
- addParameterStubs(callElement, boundClosureBuilder.addProperty); |
- |
- void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
- jsAst.Expression encoding = backend.rti.getSignatureEncoding( |
- methodType, js('this')[fieldNames[0]]); |
- String operatorSignature = namer.operatorSignature(); |
- boundClosureBuilder.addProperty(operatorSignature, encoding); |
- } |
- |
- DartType memberType = member.computeType(compiler); |
- Map<FunctionType, bool> functionTypeChecks = |
- task.typeTestEmitter.getFunctionTypeChecksOn(memberType); |
- |
- task.typeTestEmitter.generateFunctionTypeTests( |
- member, memberType, functionTypeChecks, |
- emitFunctionTypeSignature); |
- |
- closureClassElement = |
- addClosureIfNew(boundClosureBuilder, closureClassElement, fieldNames); |
- |
- String closureClass = namer.isolateAccess(closureClassElement); |
- |
- // And finally the getter. |
- String getterName = namer.getterName(member); |
- String targetName = namer.instanceMethodName(member); |
- |
- parameters = <String>[]; |
- jsAst.PropertyAccess method = |
- backend.namer.elementAccess(classElement)['prototype'][targetName]; |
- arguments = <jsAst.Expression>[js('this'), method]; |
- |
- if (inInterceptor) { |
- String receiverArg = fieldNames[2]; |
- parameters.add(receiverArg); |
- arguments.add(js(receiverArg)); |
- } else { |
- // Put null in the intercepted receiver field. |
- arguments.add(new jsAst.LiteralNull()); |
- } |
- |
- arguments.add(js.string(targetName)); |
- |
- jsAst.Expression getterFunction = js.fun( |
- parameters, js.return_(js(closureClass).newWith(arguments))); |
- |
- defineStub(getterName, getterFunction); |
- } |
- |
/** |
* Documentation wanted -- johnniwinther |
* |
@@ -497,7 +227,7 @@ class ContainerBuilder extends CodeEmitterHelper { |
*/ |
void emitCallStubForGetter(Element member, |
Set<Selector> selectors, |
- DefineStubFunction defineStub) { |
+ AddPropertyFunction addProperty) { |
assert(invariant(member, member.isDeclaration)); |
LibraryElement memberLibrary = member.getLibrary(); |
// If the method is intercepted, the stub gets the |
@@ -549,7 +279,7 @@ class ContainerBuilder extends CodeEmitterHelper { |
parameters, |
js.return_(buildGetter()[closureCallName](arguments))); |
- defineStub(invocationName, function); |
+ addProperty(invocationName, function); |
} |
} |
} |
@@ -566,10 +296,6 @@ class ContainerBuilder extends CodeEmitterHelper { |
if (selectors != null && !selectors.isEmpty) { |
emitCallStubForGetter(member, selectors, builder.addProperty); |
} |
- } else if (member.isFunction()) { |
- if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { |
- emitDynamicFunctionGetter(member, builder.addProperty); |
- } |
} |
} |
@@ -595,30 +321,182 @@ class ContainerBuilder extends CodeEmitterHelper { |
jsAst.Expression code = backend.generatedCode[member]; |
if (code == null) return; |
String name = namer.getNameOfMember(member); |
- if (backend.isInterceptedMethod(member)) { |
- task.interceptorEmitter.interceptorInvocationNames.add(name); |
+ task.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); |
+ FunctionSignature parameters = member.computeSignature(compiler); |
+ bool needsStubs = !parameters.optionalParameters.isEmpty; |
+ bool canTearOff = false; |
+ bool isClosure = false; |
+ String tearOffName; |
+ if (!member.isFunction() || member.isConstructor() || member.isAccessor()) { |
+ canTearOff = false; |
+ } else if (member.isInstanceMember()) { |
+ if (member.getEnclosingClass().isClosure()) { |
+ canTearOff = false; |
+ isClosure = true; |
+ } else { |
+ // Careful with operators. |
ngeoffray
2013/12/09 11:15:58
I don't understand this comment.
ahe
2013/12/09 17:06:47
Changed it to:
// TODO(ahe): What happens
|
+ canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler); |
+ tearOffName = namer.getterName(member); |
+ } |
+ } else { |
+ canTearOff = |
+ compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member); |
+ tearOffName = namer.getStaticClosureName(member); |
} |
- code = task.metadataEmitter.extendWithMetadata(member, code); |
- builder.addProperty(name, code); |
- String reflectionName = task.getReflectionName(member, name); |
- if (reflectionName != null) { |
- var reflectable = |
- js(backend.isAccessibleByReflection(member) ? '1' : '0'); |
- builder.addProperty('+$reflectionName', reflectable); |
- jsAst.Node defaultValues = |
- task.metadataEmitter.reifyDefaultArguments(member); |
- if (defaultValues != null) { |
- String unmangledName = member.name; |
- builder.addProperty('*$unmangledName', defaultValues); |
+ |
+ bool canBeReflected = backend.isAccessibleByReflection(member); |
+ bool needStructuredInfo = |
+ canTearOff || canBeReflected || compiler.enabledFunctionApply; |
+ if (!needStructuredInfo) { |
+ builder.addProperty(name, code); |
+ if (needsStubs) { |
+ addParameterStubs( |
+ member, |
+ (Selector selector, jsAst.Fun function) { |
+ builder.addProperty(namer.invocationName(selector), function); |
+ }); |
} |
+ return; |
} |
- if (member.isInstanceMember()) { |
- // TODO(ahe): Where is this done for static/top-level methods? |
- FunctionSignature parameters = member.computeSignature(compiler); |
- if (!parameters.optionalParameters.isEmpty) { |
- addParameterStubs(member, builder.addProperty); |
+ |
+ if (canTearOff) { |
+ assert(invariant(member, !member.isGenerativeConstructor())); |
+ assert(invariant(member, !member.isGenerativeConstructorBody())); |
+ assert(invariant(member, !member.isConstructor())); |
+ } |
+ |
+ // This element is needed for reflection or needs additional stubs. So we |
+ // need to retain additional information. |
+ |
+ // The information is stored in an array with this format: |
+ // |
+ // 1. The JS function for this member. |
+ // 2. First stub. |
+ // 3. Name of first stub. |
+ // ... |
+ // M. Call name of this member. |
+ // M+1. Call name of first stub. |
+ // ... |
+ // N. Getter name for tearOff. |
+ // N+1. (Required parameter count << 1) + (member.isAccessor() ? 1 : 0). |
+ // N+2. (Optional parameter count << 1) + |
+ // (parameters.optionalParametersAreNamed ? 1 : 0). |
+ // N+3. Index to function type in constant pool. |
+ // N+4. First default argument. |
+ // ... |
+ // O. First parameter name (if needed for reflection or Function.apply). |
+ // ... |
+ // P. Unmangled name (if reflectable). |
+ // P+1. First metadata (if reflectable). |
+ // ... |
+ |
+ List expressions = []; |
+ |
+ String callSelectorString = 'null'; |
+ if (member.isFunction()) { |
+ Selector callSelector = |
+ new Selector.fromElement(member, compiler).toCallSelector(); |
+ callSelectorString = '"${namer.invocationName(callSelector)}"'; |
+ } |
+ |
+ // On [requiredParameterCount], the lower bit is set if this method can be |
+ // called reflectively. |
+ int requiredParameterCount = parameters.requiredParameterCount << 1; |
+ if (member.isAccessor()) requiredParameterCount++; |
+ |
+ int optionalParameterCount = parameters.optionalParameterCount << 1; |
+ if (parameters.optionalParametersAreNamed) optionalParameterCount++; |
+ |
+ expressions.add(code); |
+ |
+ // TODO(ahe): Remove comments from output. |
+ List tearOffInfo = |
+ [new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')]; |
+ |
+ if (needsStubs || canTearOff) { |
+ addParameterStubs(member, (Selector selector, jsAst.Fun function) { |
+ expressions.add(function); |
+ if (member.isInstanceMember()) { |
+ Set invokedSelectors = |
+ compiler.codegenWorld.invokedNames[member.name]; |
+ if (invokedSelectors != null && invokedSelectors.contains(selector)) { |
+ expressions.add(js.string(namer.invocationName(selector))); |
+ } else { |
+ // Don't add a stub for calling this as a regular instance method, |
+ // we only need the "call" stub for implicit closures of this |
+ // method. |
+ expressions.add("null"); |
+ } |
+ } else { |
+ // Static methods don't need "named" stubs as the default arguments |
+ // are inlined at call sites. But static methods might need "call" |
+ // stubs for implicit closures. |
+ expressions.add("null"); |
+ // TOOD(ahe): Since we know when reading static data versus instance |
+ // data, we can eliminate this element. |
ngeoffray
2013/12/09 11:15:58
What does eliminate mean here?
ahe
2013/12/09 17:06:47
That it doesn't need to be added to the reflective
|
+ } |
+ Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ |
+ namer.closureInvocationSelectorName]; |
+ Selector callSelector = selector.toCallSelector(); |
+ String callSelectorString = 'null'; |
+ if (canTearOff && callSelectors != null && |
+ callSelectors.contains(callSelector)) { |
+ callSelectorString = '"${namer.invocationName(callSelector)}"'; |
+ } |
+ tearOffInfo.add( |
+ new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')); |
+ }, canTearOff); |
+ } |
+ |
+ jsAst.Expression memberTypeExpression; |
+ if ((canTearOff || canBeReflected) && |
+ !member.isGenerativeConstructorBody()) { |
ngeoffray
2013/12/09 11:15:58
A generative constructor body can be reflected?
ahe
2013/12/09 17:06:47
Yeah. It is kind of a hack, but these end up being
|
+ DartType memberType = member.computeType(compiler); |
+ if (memberType.containsTypeVariables) { |
+ jsAst.Expression thisAccess = js(r'this.$receiver'); |
+ memberTypeExpression = |
+ backend.rti.getSignatureEncoding(memberType, thisAccess); |
+ } else { |
+ memberTypeExpression = |
+ js.toExpression(task.metadataEmitter.reifyType(memberType)); |
} |
+ } else { |
+ memberTypeExpression = js('null'); |
} |
+ |
+ expressions |
+ ..addAll(tearOffInfo) |
+ ..add((tearOffName == null || member.isAccessor()) |
+ ? js("null") : js.string(tearOffName)) |
+ ..add(requiredParameterCount) |
+ ..add(optionalParameterCount) |
+ ..add(memberTypeExpression) |
+ ..addAll(task.metadataEmitter.reifyDefaultArguments(member)); |
+ |
+ if (canBeReflected || compiler.enabledFunctionApply) { |
+ parameters.orderedForEachParameter((Element parameter) { |
+ expressions.add(task.metadataEmitter.reifyName(parameter.name)); |
+ }); |
+ } |
+ if (canBeReflected) { |
+ jsAst.LiteralString reflectionName; |
+ if (member.isConstructor()) { |
+ String reflectionNameString = task.getReflectionName(member, name); |
+ reflectionName = |
+ new jsAst.LiteralString( |
+ '"new ${Elements.reconstructConstructorName(member)}"' |
+ ' /* $reflectionNameString */'); |
+ } else { |
+ reflectionName = js.string(member.name); |
+ } |
+ expressions |
+ ..add(reflectionName) |
+ ..addAll(task.metadataEmitter.computeMetadata(member)); |
+ } else if (isClosure && compiler.enabledFunctionApply) { |
+ expressions.add(js.string(member.name)); |
+ } |
+ |
+ builder.addProperty(name, js.toExpression(expressions)); |
} |
void addMemberField(VariableElement member, ClassBuilder builder) { |