| 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 1ebb578d2635a53beb60969f7b8d5d76255c2bab..b8e8210ab1136caf110312b00277963789a796ed 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,
|
| - AddStubFunction addStub,
|
| + DefineStubFunction defineStub,
|
| Set<String> alreadyGenerated) {
|
| FunctionSignature parameters = member.computeSignature(compiler);
|
| int positionalArgumentCount = selector.positionalArgumentCount;
|
| @@ -81,7 +81,10 @@ class ContainerBuilder extends CodeEmitterHelper {
|
|
|
| int parameterIndex = 0;
|
| parameters.orderedForEachParameter((Element element) {
|
| - String jsName = backend.namer.safeName(element.name);
|
| + // Use generic names for closures to facilitate code sharing.
|
| + String jsName = member is ClosureInvocationElement
|
| + ? 'p${parameterIndex++}'
|
| + : backend.namer.safeName(element.name);
|
| assert(jsName != receiverArgumentName);
|
| if (count < optionalParameterStart) {
|
| parametersBuffer[count] = new jsAst.Parameter(jsName);
|
| @@ -118,30 +121,25 @@ class ContainerBuilder extends CodeEmitterHelper {
|
| member, isInterceptedMethod, invocationName,
|
| parametersBuffer, argumentsBuffer,
|
| indexOfLastOptionalArgumentInParameters);
|
| - } else if (member.isInstanceMember()) {
|
| + } else {
|
| body = [js.return_(
|
| js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))];
|
| - } else {
|
| - body = [js.return_(namer.elementAccess(member)(argumentsBuffer))];
|
| }
|
|
|
| jsAst.Fun function = js.fun(parametersBuffer, body);
|
|
|
| - addStub(selector, function);
|
| - }
|
| + defineStub(invocationName, function);
|
|
|
| - 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.');
|
| - }
|
| - if (cls.methodElement.isInstanceMember()) {
|
| - compiler.internalErrorOnElement(cls.methodElement, 'Bound closure2.');
|
| - }
|
| + String reflectionName = task.getReflectionName(selector, invocationName);
|
| + if (reflectionName != null) {
|
| + var reflectable =
|
| + js(backend.isAccessibleByReflection(member) ? '1' : '0');
|
| + defineStub('+$reflectionName', reflectable);
|
| }
|
| + }
|
|
|
| + void addParameterStubs(FunctionElement member,
|
| + DefineStubFunction defineStub) {
|
| // We fill the lists depending on the selector. For example,
|
| // take method foo:
|
| // foo(a, b, {c, d});
|
| @@ -167,59 +165,331 @@ class ContainerBuilder extends CodeEmitterHelper {
|
| // (4) No stub generated, call is direct.
|
| // (5) No stub generated, call is direct.
|
|
|
| - Set<Selector> selectors = member.isInstanceMember()
|
| - ? 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;
|
| + // 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> callSelectors = callSelectorsAsNamed();
|
| - if (callSelectors != null) {
|
| - selectors = selectors.union(callSelectors);
|
| + Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
|
| + if (selectors == null) return;
|
| + for (Selector selector in selectors) {
|
| + if (!selector.applies(member, compiler)) continue;
|
| + addParameterStub(member, selector, defineStub, generatedStubNames);
|
| }
|
| }
|
| - 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>());
|
| - }
|
| + }
|
| +
|
| + 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);
|
| }
|
| + properties.add(new jsAst.Property(js.string(jsName), reference));
|
| }
|
| - 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>());
|
| - }
|
| - }
|
| + 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);
|
| }
|
| +
|
| + 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
|
| *
|
| @@ -227,7 +497,7 @@ class ContainerBuilder extends CodeEmitterHelper {
|
| */
|
| void emitCallStubForGetter(Element member,
|
| Set<Selector> selectors,
|
| - AddPropertyFunction addProperty) {
|
| + DefineStubFunction defineStub) {
|
| assert(invariant(member, member.isDeclaration));
|
| LibraryElement memberLibrary = member.getLibrary();
|
| // If the method is intercepted, the stub gets the
|
| @@ -279,7 +549,7 @@ class ContainerBuilder extends CodeEmitterHelper {
|
| parameters,
|
| js.return_(buildGetter()[closureCallName](arguments)));
|
|
|
| - addProperty(invocationName, function);
|
| + defineStub(invocationName, function);
|
| }
|
| }
|
| }
|
| @@ -296,6 +566,10 @@ 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);
|
| + }
|
| }
|
| }
|
|
|
| @@ -321,182 +595,30 @@ class ContainerBuilder extends CodeEmitterHelper {
|
| jsAst.Expression code = backend.generatedCode[member];
|
| if (code == null) return;
|
| String name = namer.getNameOfMember(member);
|
| - 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.
|
| - canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler);
|
| - tearOffName = namer.getterName(member);
|
| - }
|
| - } else {
|
| - canTearOff =
|
| - compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member);
|
| - tearOffName = namer.getStaticClosureName(member);
|
| + if (backend.isInterceptedMethod(member)) {
|
| + task.interceptorEmitter.interceptorInvocationNames.add(name);
|
| }
|
| -
|
| - 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);
|
| - });
|
| + 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);
|
| }
|
| - return;
|
| }
|
| -
|
| - 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.
|
| - }
|
| - 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()) {
|
| - 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));
|
| + 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);
|
| }
|
| - } 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) {
|
|
|