Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(888)

Side by Side Diff: pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart

Issue 886053004: dart2js: Refactor how parameter stubs are handled. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart2js.js_emitter; 5 part of dart2js.js_emitter;
6 6
7 class ParameterStubGenerator { 7 class ParameterStubGenerator {
8 static final Set<Selector> emptySelectorSet = new Set<Selector>();
9
8 final Namer namer; 10 final Namer namer;
9 final Compiler compiler; 11 final Compiler compiler;
10 final JavaScriptBackend backend; 12 final JavaScriptBackend backend;
11 13
12 ParameterStubGenerator(this.compiler, this.namer, this.backend); 14 ParameterStubGenerator(this.compiler, this.namer, this.backend);
13 15
14 Emitter get emitter => backend.emitter.emitter; 16 Emitter get emitter => backend.emitter.emitter;
15 CodeEmitterTask get emitterTask => backend.emitter; 17 CodeEmitterTask get emitterTask => backend.emitter;
16 18
17 bool needsSuperGetter(FunctionElement element) => 19 bool needsSuperGetter(FunctionElement element) =>
18 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); 20 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element);
19 21
20 /** 22 /**
21 * Generate stubs to handle invocation of methods with optional 23 * Generates stubs to handle invocation of methods with optional
22 * arguments. 24 * arguments.
23 * 25 *
24 * A method like [: foo([x]) :] may be invoked by the following 26 * A method like `foo([x])` may be invoked by the following
25 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this 27 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the
26 * function for detailed examples. 28 * given [selector] and returns the generated [ParameterStubMethod].
29 *
30 * Returns null if no stub is needed.
31 *
32 * Members may be invoked in two ways: directly, or through a closure. In the
33 * latter case the caller invokes the closure's `call` method. This method
34 * accepts two selectors. The returned stub method has the corresponding
35 * name [ParameterStubMethod.name] and [ParameterStubMethod.callName] set if
36 * the input selector is non-null (and the member needs a stub).
27 */ 37 */
28 jsAst.Expression generateParameterStub(FunctionElement member, 38 ParameterStubMethod generateParameterStub(FunctionElement member,
29 Selector selector) { 39 Selector selector,
40 Selector callSelector) {
30 FunctionSignature parameters = member.functionSignature; 41 FunctionSignature parameters = member.functionSignature;
31 int positionalArgumentCount = selector.positionalArgumentCount; 42 int positionalArgumentCount = selector.positionalArgumentCount;
32 if (positionalArgumentCount == parameters.parameterCount) { 43 if (positionalArgumentCount == parameters.parameterCount) {
33 assert(selector.namedArgumentCount == 0); 44 assert(selector.namedArgumentCount == 0);
34 return null; 45 return null;
35 } 46 }
36 if (parameters.optionalParametersAreNamed 47 if (parameters.optionalParametersAreNamed
37 && selector.namedArgumentCount == parameters.optionalParameterCount) { 48 && selector.namedArgumentCount == parameters.optionalParameterCount) {
38 // If the selector has the same number of named arguments as the element, 49 // If the selector has the same number of named arguments as the element,
39 // we don't need to add a stub. The call site will hit the method 50 // we don't need to add a stub. The call site will hit the method
40 // directly. 51 // directly.
41 return null; 52 return null;
42 } 53 }
43 JavaScriptConstantCompiler handler = backend.constants; 54 JavaScriptConstantCompiler handler = backend.constants;
44 List<String> names = selector.getOrderedNamedArguments(); 55 List<String> names = selector.getOrderedNamedArguments();
45 56
46 String invocationName = namer.invocationName(selector);
47
48 bool isInterceptedMethod = backend.isInterceptedMethod(member); 57 bool isInterceptedMethod = backend.isInterceptedMethod(member);
49 58
50 // If the method is intercepted, we need to also pass the actual receiver. 59 // If the method is intercepted, we need to also pass the actual receiver.
51 int extraArgumentCount = isInterceptedMethod ? 1 : 0; 60 int extraArgumentCount = isInterceptedMethod ? 1 : 0;
52 // Use '$receiver' to avoid clashes with other parameter names. Using 61 // Use '$receiver' to avoid clashes with other parameter names. Using
53 // '$receiver' works because [:namer.safeName:] used for getting parameter 62 // '$receiver' works because [:namer.safeName:] used for getting parameter
54 // names never returns a name beginning with a single '$'. 63 // names never returns a name beginning with a single '$'.
55 String receiverArgumentName = r'$receiver'; 64 String receiverArgumentName = r'$receiver';
56 65
57 // The parameters that this stub takes. 66 // The parameters that this stub takes.
58 List<jsAst.Parameter> parametersBuffer = 67 List<jsAst.Parameter> parametersBuffer =
59 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); 68 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount);
60 // The arguments that will be passed to the real method. 69 // The arguments that will be passed to the real method.
61 List<jsAst.Expression> argumentsBuffer = 70 List<jsAst.Expression> argumentsBuffer =
62 new List<jsAst.Expression>( 71 new List<jsAst.Expression>(
63 parameters.parameterCount + extraArgumentCount); 72 parameters.parameterCount + extraArgumentCount);
73 String invocationName = namer.invocationName(selector);
64 74
65 int count = 0; 75 int count = 0;
66 if (isInterceptedMethod) { 76 if (isInterceptedMethod) {
67 count++; 77 count++;
68 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); 78 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName);
69 argumentsBuffer[0] = js('#', receiverArgumentName); 79 argumentsBuffer[0] = js('#', receiverArgumentName);
70 } 80 }
71 81
72 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; 82 int optionalParameterStart = positionalArgumentCount + extraArgumentCount;
73 // Includes extra receiver argument when using interceptor convention 83 // Includes extra receiver argument when using interceptor convention
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 'return this.#(#);', 143 'return this.#(#);',
134 [namer.getNameOfInstanceMember(member), argumentsBuffer]); 144 [namer.getNameOfInstanceMember(member), argumentsBuffer]);
135 } 145 }
136 } else { 146 } else {
137 body = js.statement('return #(#)', 147 body = js.statement('return #(#)',
138 [emitter.staticFunctionAccess(member), argumentsBuffer]); 148 [emitter.staticFunctionAccess(member), argumentsBuffer]);
139 } 149 }
140 150
141 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); 151 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]);
142 152
143 return function; 153 String name = namer.invocationName(selector);
154 String callName =
155 (callSelector != null) ? namer.invocationName(callSelector) : null;
156 return new ParameterStubMethod(name, callName, function);
144 } 157 }
145 158
146 Map<Selector, jsAst.Expression> generateParameterStubs(FunctionElement member, 159 // We fill the lists depending on possible/invoked selectors. For example,
147 [bool canTearOff = false]) { 160 // take method foo:
148 Map<Selector, jsAst.Expression> generatedStubs 161 // foo(a, b, {c, d});
149 = <Selector, jsAst.Expression>{}; 162 //
150 163 // We may have multiple ways of calling foo:
164 // (1) foo(1, 2);
165 // (2) foo(1, 2, c: 3);
166 // (3) foo(1, 2, d: 4);
167 // (4) foo(1, 2, c: 3, d: 4);
168 // (5) foo(1, 2, d: 4, c: 3);
169 //
170 // What we generate at the call sites are:
171 // (1) foo$2(1, 2);
172 // (2) foo$3$c(1, 2, 3);
173 // (3) foo$3$d(1, 2, 4);
174 // (4) foo$4$c$d(1, 2, 3, 4);
175 // (5) foo$4$c$d(1, 2, 3, 4);
176 //
177 // The stubs we generate are (expressed in Dart):
178 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null)
179 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null);
180 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d);
181 // (4) No stub generated, call is direct.
182 // (5) No stub generated, call is direct.
183 //
184 // We need to pay attention if this stub is for a function that has been
185 // invoked from a subclass. Then we cannot just redirect, since that
186 // would invoke the methods of the subclass. We have to compile to:
187 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null)
188 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null);
189 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d);
190 List<ParameterStubMethod> generateParameterStubs(FunctionElement member,
191 {bool canTearOff: true}) {
151 if (member.enclosingElement.isClosure) { 192 if (member.enclosingElement.isClosure) {
152 ClosureClassElement cls = member.enclosingElement; 193 ClosureClassElement cls = member.enclosingElement;
153 if (cls.supertype.element == backend.boundClosureClass) { 194 if (cls.supertype.element == backend.boundClosureClass) {
154 compiler.internalError(cls.methodElement, 'Bound closure1.'); 195 compiler.internalError(cls.methodElement, 'Bound closure1.');
155 } 196 }
156 if (cls.methodElement.isInstanceMember) { 197 if (cls.methodElement.isInstanceMember) {
157 compiler.internalError(cls.methodElement, 'Bound closure2.'); 198 compiler.internalError(cls.methodElement, 'Bound closure2.');
158 } 199 }
159 } 200 }
160 201
161 // We fill the lists depending on the selector. For example, 202 // The set of selectors that apply to `member`. For example, for
162 // take method foo: 203 // a member `foo(x, [y])` the following selectors may apply:
163 // foo(a, b, {c, d}); 204 // `foo(x)`, and `foo(x, y)`.
205 Set<Selector> selectors;
206 // The set of selectors that apply to `member` if it's name was `call`.
207 // This happens when a member is torn-off. In that case calls to the
zarah 2015/01/30 14:07:28 nit: we sometimes write torn off and sometimes tor
floitsch 2015/01/30 21:48:13 Done.
208 // function use the name `call`, and we must be able to handle every
209 // `call` invocation that matches the signature. For example, for
210 // a member `foo(x, [y])` the following selectors would be possible
211 // call-selectors: `call(x)`, and `call(x, y)`.
212 Set<Selector> callSelectors;
213
214 // Only instance members (not static methods) need stubs.
215 if (member.isInstanceMember) {
216 selectors = compiler.codegenWorld.invokedNames[member.name];
217 }
218
219 if (canTearOff) {
220 String call = namer.closureInvocationSelectorName;
221 callSelectors = compiler.codegenWorld.invokedNames[call];
222 }
223
224 assert(emptySelectorSet.isEmpty);
225 if (selectors == null) selectors = emptySelectorSet;
226 if (callSelectors == null) callSelectors = emptySelectorSet;
227
228 List<ParameterStubMethod> stubs = <ParameterStubMethod>[];
229
230 if (selectors.isEmpty && callSelectors.isEmpty) {
231 return stubs;
232 }
233
234 // For every call-selector the corresponding selector with the name of the
235 // member.
164 // 236 //
165 // We may have multiple ways of calling foo: 237 // For example, for the call-selector `call(x, y)` the renamed selector
166 // (1) foo(1, 2); 238 // for member `foo` would be `foo(x, y)`.
167 // (2) foo(1, 2, c: 3); 239 Set<Selector> renamedCallSelectors =
168 // (3) foo(1, 2, d: 4); 240 callSelectors.isEmpty ? emptySelectorSet : new Set<Selector>();
169 // (4) foo(1, 2, c: 3, d: 4);
170 // (5) foo(1, 2, d: 4, c: 3);
171 //
172 // What we generate at the call sites are:
173 // (1) foo$2(1, 2);
174 // (2) foo$3$c(1, 2, 3);
175 // (3) foo$3$d(1, 2, 4);
176 // (4) foo$4$c$d(1, 2, 3, 4);
177 // (5) foo$4$c$d(1, 2, 3, 4);
178 //
179 // The stubs we generate are (expressed in Dart):
180 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null)
181 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null);
182 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d);
183 // (4) No stub generated, call is direct.
184 // (5) No stub generated, call is direct.
185 //
186 // We need to pay attention if this stub is for a function that has been
187 // invoked from a subclass. Then we cannot just redirect, since that
188 // would invoke the methods of the subclass. We have to compile to:
189 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null)
190 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null);
191 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d);
192 241
193 Set<Selector> selectors = member.isInstanceMember 242 Set<Selector> untypedSelectors = new Set<Selector>();
194 ? compiler.codegenWorld.invokedNames[member.name]
195 : null; // No stubs needed for static methods.
196 243
197 /// Returns all closure call selectors renamed to match this member. 244 // Start with the callSelectors since they imply the generation of the
198 Set<Selector> callSelectorsAsNamed() { 245 // non-call version.
199 if (!canTearOff) return null; 246 for (Selector selector in callSelectors) {
200 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ 247 Selector renamedSelector = new Selector.call(
201 namer.closureInvocationSelectorName]; 248 member.name, member.library,
202 if (callSelectors == null) return null; 249 selector.argumentCount, selector.namedArguments);
203 return callSelectors.map((Selector callSelector) { 250 renamedCallSelectors.add(renamedSelector);
204 return new Selector.call( 251
205 member.name, member.library, 252 if (!renamedSelector.appliesUnnamed(member, compiler.world)) continue;
206 callSelector.argumentCount, callSelector.namedArguments); 253
207 }).toSet(); 254 if (untypedSelectors.add(renamedSelector.asUntyped)) {
208 } 255 ParameterStubMethod stub =
209 if (selectors == null) { 256 generateParameterStub(member, renamedSelector, selector);
210 selectors = callSelectorsAsNamed(); 257 if (stub != null) {
211 if (selectors == null) return generatedStubs; 258 stubs.add(stub);
212 } else {
213 Set<Selector> callSelectors = callSelectorsAsNamed();
214 if (callSelectors != null) {
215 selectors = selectors.union(callSelectors);
216 }
217 }
218 Set<Selector> untypedSelectors = new Set<Selector>();
219 if (selectors != null) {
220 for (Selector selector in selectors) {
221 if (!selector.appliesUnnamed(member, compiler.world)) continue;
222 if (untypedSelectors.add(selector.asUntyped)) {
223 jsAst.Expression stub = generateParameterStub(member, selector);
224 if (stub != null) {
225 generatedStubs[selector] = stub;
226 }
227 } 259 }
228 } 260 }
229 } 261 }
230 if (canTearOff) { 262
231 selectors = compiler.codegenWorld.invokedNames[ 263 // Now run through the actual member selectors (eg. `foo$2(x, y)` and not
232 namer.closureInvocationSelectorName]; 264 // `call$2(x, y)`. Some of them have already been generated because of the
233 if (selectors != null) { 265 // call-selectors (and they are in the renamedCallSelectors set.
234 for (Selector selector in selectors) { 266 for (Selector selector in selectors) {
235 selector = new Selector.call( 267 if (renamedCallSelectors.contains(selector)) continue;
236 member.name, member.library, 268 if (!selector.appliesUnnamed(member, compiler.world)) continue;
237 selector.argumentCount, selector.namedArguments); 269
238 if (!selector.appliesUnnamed(member, compiler.world)) continue; 270 if (untypedSelectors.add(selector.asUntyped)) {
239 if (untypedSelectors.add(selector)) { 271 ParameterStubMethod stub =
240 jsAst.Expression stub = generateParameterStub(member, selector); 272 generateParameterStub(member, selector, null);
241 if (stub != null) { 273 if (stub != null) {
242 generatedStubs[selector] = stub; 274 stubs.add(stub);
243 }
244 }
245 } 275 }
246 } 276 }
247 } 277 }
248 return generatedStubs; 278
279 return stubs;
249 } 280 }
250 } 281 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698