Chromium Code Reviews| Index: pkg/compiler/lib/src/js_emitter/old_emitter/container_builder.dart |
| diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/container_builder.dart b/pkg/compiler/lib/src/js_emitter/old_emitter/container_builder.dart |
| index b221f8889a045c6766dbdb998b9e8aeed9c0c450..94bd8901ea907fd113f3f9388d70db6645b3deee 100644 |
| --- a/pkg/compiler/lib/src/js_emitter/old_emitter/container_builder.dart |
| +++ b/pkg/compiler/lib/src/js_emitter/old_emitter/container_builder.dart |
| @@ -9,32 +9,42 @@ part of dart2js.js_emitter; |
| /// Initially, it is just a placeholder for code that is moved from |
| /// [CodeEmitterTask]. |
| class ContainerBuilder extends CodeEmitterHelper { |
| + final Set<Selector> emptySelectorSet = new Set<Selector>(); |
| + |
| bool needsSuperGetter(FunctionElement element) => |
| compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); |
| /** |
| - * Generate stubs to handle invocation of methods with optional |
| + * Generates stubs to handle invocation of methods with optional |
| * arguments. |
| * |
| - * A method like [: foo([x]) :] may be invoked by the following |
| - * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this |
| - * function for detailed examples. |
| + * A method like `foo([x])` may be invoked by the following |
| + * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the |
| + * given [selector] and returns the generated [AdapterStubMethod]. |
| + * |
| + * Returns null if no stub is needed. |
| + * |
| + * Members may be invoked in two ways: directly, or through a closure. In the |
| + * latter case the caller invokes the closure's `call` method. This method |
| + * accepts two selectors. The returned stub method has the corresponding |
| + * name [AdapterStubMethod.name] and/or [AdapterStubMethod.callName] set if |
| + * the input selector is non-null (and the member needs an adapter). |
| */ |
| - void addParameterStub(FunctionElement member, |
| - Selector selector, |
| - AddStubFunction addStub) { |
| + AdapterStubMethod generateParameterStub(FunctionElement member, |
| + Selector selector, |
| + Selector callSelector) { |
| FunctionSignature parameters = member.functionSignature; |
| int positionalArgumentCount = selector.positionalArgumentCount; |
| if (positionalArgumentCount == parameters.parameterCount) { |
| assert(selector.namedArgumentCount == 0); |
| - return; |
| + return null; |
| } |
| if (parameters.optionalParametersAreNamed |
| && selector.namedArgumentCount == parameters.optionalParameterCount) { |
| // If the selector has the same number of named arguments as the element, |
| // we don't need to add a stub. The call site will hit the method |
| // directly. |
| - return; |
| + return null; |
| } |
| JavaScriptConstantCompiler handler = backend.constants; |
| List<String> names = selector.getOrderedNamedArguments(); |
| @@ -62,7 +72,6 @@ class ContainerBuilder extends CodeEmitterHelper { |
| count++; |
| parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); |
| argumentsBuffer[0] = js('#', receiverArgumentName); |
| - emitter.interceptorEmitter.interceptorInvocationNames.add(invocationName); |
| } |
| int optionalParameterStart = positionalArgumentCount + extraArgumentCount; |
| @@ -136,11 +145,45 @@ class ContainerBuilder extends CodeEmitterHelper { |
| jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); |
| - addStub(selector, function); |
| + String name = namer.invocationName(selector); |
| + String callName = |
| + (callSelector != null) ? namer.invocationName(callSelector) : null; |
| + return new AdapterStubMethod(name, callName, function); |
| } |
| - void addParameterStubs(FunctionElement member, AddStubFunction defineStub, |
| - [bool canTearOff = false]) { |
| + // We fill the lists depending on possible/invoked selectors. For example, |
| + // take method foo: |
| + // foo(a, b, {c, d}); |
| + // |
| + // We may have multiple ways of calling foo: |
| + // (1) foo(1, 2); |
| + // (2) foo(1, 2, c: 3); |
| + // (3) foo(1, 2, d: 4); |
| + // (4) foo(1, 2, c: 3, d: 4); |
| + // (5) foo(1, 2, d: 4, c: 3); |
| + // |
| + // What we generate at the call sites are: |
| + // (1) foo$2(1, 2); |
| + // (2) foo$3$c(1, 2, 3); |
| + // (3) foo$3$d(1, 2, 4); |
| + // (4) foo$4$c$d(1, 2, 3, 4); |
| + // (5) foo$4$c$d(1, 2, 3, 4); |
| + // |
| + // The stubs we generate are (expressed in Dart): |
| + // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) |
| + // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); |
| + // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); |
| + // (4) No stub generated, call is direct. |
| + // (5) No stub generated, call is direct. |
| + // |
| + // We need to pay attention if this stub is for a function that has been |
| + // invoked from a subclass. Then we cannot just redirect, since that |
| + // would invoke the methods of the subclass. We have to compile to: |
| + // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) |
| + // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); |
| + // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); |
| + List<AdapterStubMethod> generateParameterStubs(FunctionElement member, |
|
floitsch
2015/01/30 00:00:14
I'm sorry, but I couldn't resist rewriting this fu
|
| + {bool canTearOff: true}) { |
| if (member.enclosingElement.isClosure) { |
| ClosureClassElement cls = member.enclosingElement; |
| if (cls.supertype.element == backend.boundClosureClass) { |
| @@ -151,87 +194,84 @@ class ContainerBuilder extends CodeEmitterHelper { |
| } |
| } |
| - // We fill the lists depending on the selector. For example, |
| - // take method foo: |
| - // foo(a, b, {c, d}); |
| - // |
| - // We may have multiple ways of calling foo: |
| - // (1) foo(1, 2); |
| - // (2) foo(1, 2, c: 3); |
| - // (3) foo(1, 2, d: 4); |
| - // (4) foo(1, 2, c: 3, d: 4); |
| - // (5) foo(1, 2, d: 4, c: 3); |
| - // |
| - // What we generate at the call sites are: |
| - // (1) foo$2(1, 2); |
| - // (2) foo$3$c(1, 2, 3); |
| - // (3) foo$3$d(1, 2, 4); |
| - // (4) foo$4$c$d(1, 2, 3, 4); |
| - // (5) foo$4$c$d(1, 2, 3, 4); |
| - // |
| - // The stubs we generate are (expressed in Dart): |
| - // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) |
| - // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); |
| - // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); |
| - // (4) No stub generated, call is direct. |
| - // (5) No stub generated, call is direct. |
| - // |
| - // We need to pay attention if this stub is for a function that has been |
| - // invoked from a subclass. Then we cannot just redirect, since that |
| - // would invoke the methods of the subclass. We have to compile to: |
| - // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) |
| - // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); |
| - // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); |
| - |
| - 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.library, |
| - callSelector.argumentCount, callSelector.namedArguments); |
| - }).toSet(); |
| + // The set of selectors that apply to `member`. For example, for |
| + // a member `foo(x, [y])` the following selectors may apply: |
| + // `foo(x)`, and `foo(x, y)`. |
| + Set<Selector> selectors; |
| + // The set of selectors that apply to `member` if it's name was `call`. |
| + // This happens when a member is torn-off. In that case calls to the |
| + // function use the name `call`, and we must be able to handle every |
| + // `call` invocation that matches the signature. For example, for |
| + // a member `foo(x, [y])` the following selectors would be possible |
| + // call-selectors: `call(x)`, and `call(x, y)`. |
| + Set<Selector> callSelectors; |
| + |
| + // Only instance members (not static methods) need stubs. |
| + if (member.isInstanceMember) { |
| + selectors = compiler.codegenWorld.invokedNames[member.name]; |
| } |
| - if (selectors == null) { |
| - selectors = callSelectorsAsNamed(); |
| - if (selectors == null) return; |
| - } else { |
| - Set<Selector> callSelectors = callSelectorsAsNamed(); |
| - if (callSelectors != null) { |
| - selectors = selectors.union(callSelectors); |
| - } |
| + |
| + if (canTearOff) { |
| + String call = namer.closureInvocationSelectorName; |
| + callSelectors = compiler.codegenWorld.invokedNames[call]; |
| + } |
| + |
| + assert(emptySelectorSet.isEmpty); |
| + if (selectors == null) selectors = emptySelectorSet; |
| + if (callSelectors == null) callSelectors = emptySelectorSet; |
| + |
| + List<AdapterStubMethod> adapters = <AdapterStubMethod>[]; |
| + |
| + if (selectors.isEmpty && callSelectors.isEmpty) { |
| + return adapters; |
| } |
| + |
| + // For every call-selector the corresponding selector with the name of the |
| + // member. |
| + // |
| + // For example, for the call-selector `call(x, y)` the renamed selector |
| + // for member `foo` would be `foo(x, y)`. |
| + Set<Selector> renamedCallSelectors = |
| + callSelectors.isEmpty ? emptySelectorSet : new Set<Selector>(); |
| + |
| Set<Selector> untypedSelectors = new Set<Selector>(); |
| - if (selectors != null) { |
| - for (Selector selector in selectors) { |
| - if (!selector.appliesUnnamed(member, compiler.world)) continue; |
| - if (untypedSelectors.add(selector.asUntyped)) { |
| - addParameterStub(member, selector, defineStub); |
| + |
| + // Start with the callSelectors since they imply the generation of the |
| + // non-call version. |
| + for (Selector selector in callSelectors) { |
| + Selector renamedSelector = new Selector.call( |
| + member.name, member.library, |
| + selector.argumentCount, selector.namedArguments); |
| + renamedCallSelectors.add(renamedSelector); |
| + |
| + if (!renamedSelector.appliesUnnamed(member, compiler.world)) continue; |
| + |
| + if (untypedSelectors.add(renamedSelector.asUntyped)) { |
| + AdapterStubMethod adapter = |
| + generateParameterStub(member, renamedSelector, selector); |
| + if (adapter != null) { |
| + adapters.add(adapter); |
| } |
| } |
| } |
| - if (canTearOff) { |
| - selectors = compiler.codegenWorld.invokedNames[ |
| - namer.closureInvocationSelectorName]; |
| - if (selectors != null) { |
| - for (Selector selector in selectors) { |
| - selector = new Selector.call( |
| - member.name, member.library, |
| - selector.argumentCount, selector.namedArguments); |
| - if (!selector.appliesUnnamed(member, compiler.world)) continue; |
| - if (untypedSelectors.add(selector)) { |
| - addParameterStub(member, selector, defineStub); |
| - } |
| + |
| + // Now run through the actual member selectors (eg. `foo$2(x, y)` and not |
| + // `call$2(x, y)`. Some of them have already been generated because of the |
| + // call-selectors (and they are in the renamedCallSelectors set. |
| + for (Selector selector in selectors) { |
| + if (renamedCallSelectors.contains(selector)) continue; |
| + if (!selector.appliesUnnamed(member, compiler.world)) continue; |
| + |
| + if (untypedSelectors.add(selector.asUntyped)) { |
| + AdapterStubMethod adapter = |
| + generateParameterStub(member, selector, null); |
| + if (adapter != null) { |
| + adapters.add(adapter); |
| } |
| } |
| } |
| + |
| + return adapters; |
| } |
| void addMemberMethod(DartMethod method, ClassBuilder builder) { |
| @@ -256,13 +296,15 @@ class ContainerBuilder extends CodeEmitterHelper { |
| compiler.dumpInfoTask.registerElementAst(member, |
| builder.addProperty(name, code)); |
| if (needsStubs) { |
| - addParameterStubs( |
| - member, |
| - (Selector selector, jsAst.Fun function) { |
| - compiler.dumpInfoTask.registerElementAst(member, |
| - builder.addProperty(namer.invocationName(selector), |
| - function)); |
| - }); |
| + for (AdapterStubMethod adapter in |
| + generateParameterStubs(member, canTearOff: false)) { |
| + assert(adapter.callName == null); |
| + String invocationName = adapter.name; |
| + emitter.interceptorEmitter |
| + .recordMangledNameOfMemberMethod(member, invocationName); |
| + compiler.dumpInfoTask.registerElementAst( |
| + member, builder.addProperty(invocationName, adapter.code)); |
| + } |
| } |
| return; |
| } |
| @@ -335,29 +377,25 @@ class ContainerBuilder extends CodeEmitterHelper { |
| List tearOffInfo = [new jsAst.LiteralString(callSelectorString)]; |
| if (needsStubs || canTearOff) { |
| - addParameterStubs(member, (Selector selector, jsAst.Fun function) { |
| - expressions.add(function); |
| + for (AdapterStubMethod adapter in |
| + generateParameterStubs(member, canTearOff: canTearOff)) { |
| + String invocationName = adapter.name; |
| + emitter.interceptorEmitter |
| + .recordMangledNameOfMemberMethod(member, invocationName); |
| + |
| + expressions.add(adapter.code); |
| if (member.isInstanceMember) { |
| - Set invokedSelectors = |
| - compiler.codegenWorld.invokedNames[member.name]; |
| - expressions.add(js.string(namer.invocationName(selector))); |
| + expressions.add(js.string(invocationName)); |
| } else { |
| + // TOOD(floitsch): Since we know when reading static data versus |
| + // instance data, we can eliminate this element. |
| expressions.add(js('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)}"'; |
| } |
| + String callName = adapter.callName; |
| + String callSelectorString = (callName == null) ? 'null' : '"$callName"'; |
| tearOffInfo.add(new jsAst.LiteralString(callSelectorString)); |
| - }, canTearOff); |
| + } |
| } |
| - |
| jsAst.Expression memberTypeExpression; |
| if (canTearOff || canBeReflected) { |
| DartType memberType; |