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

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: Address comments. 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.
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 argumentsBuffer[count] = emitter.constantReference(value); 113 argumentsBuffer[count] = emitter.constantReference(value);
105 } 114 }
106 } 115 }
107 } 116 }
108 count++; 117 count++;
109 }); 118 });
110 119
111 var body; // List or jsAst.Statement. 120 var body; // List or jsAst.Statement.
112 if (member.hasFixedBackendName) { 121 if (member.hasFixedBackendName) {
113 body = emitterTask.nativeEmitter.generateParameterStubStatements( 122 body = emitterTask.nativeEmitter.generateParameterStubStatements(
114 member, isInterceptedMethod, invocationName, 123 member, isInterceptedMethod, namer.invocationName(selector),
115 parametersBuffer, argumentsBuffer, 124 parametersBuffer, argumentsBuffer,
116 indexOfLastOptionalArgumentInParameters); 125 indexOfLastOptionalArgumentInParameters);
117 } else if (member.isInstanceMember) { 126 } else if (member.isInstanceMember) {
118 if (needsSuperGetter(member)) { 127 if (needsSuperGetter(member)) {
119 ClassElement superClass = member.enclosingClass; 128 ClassElement superClass = member.enclosingClass;
120 String methodName = namer.getNameOfInstanceMember(member); 129 String methodName = namer.getNameOfInstanceMember(member);
121 // When redirecting, we must ensure that we don't end up in a subclass. 130 // When redirecting, we must ensure that we don't end up in a subclass.
122 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. 131 // We thus can't just invoke `this.foo$1.call(filledInArguments)`.
123 // Instead we need to call the statically resolved target. 132 // Instead we need to call the statically resolved target.
124 // `<class>.prototype.bar$1.call(this, argument0, ...)`. 133 // `<class>.prototype.bar$1.call(this, argument0, ...)`.
125 body = js.statement( 134 body = js.statement(
126 'return #.#.call(this, #);', 135 'return #.#.call(this, #);',
127 [backend.emitter.prototypeAccess(superClass, 136 [backend.emitter.prototypeAccess(superClass,
128 hasBeenInstantiated: true), 137 hasBeenInstantiated: true),
129 methodName, 138 methodName,
130 argumentsBuffer]); 139 argumentsBuffer]);
131 } else { 140 } else {
132 body = js.statement( 141 body = js.statement(
133 'return this.#(#);', 142 'return this.#(#);',
134 [namer.getNameOfInstanceMember(member), argumentsBuffer]); 143 [namer.getNameOfInstanceMember(member), argumentsBuffer]);
135 } 144 }
136 } else { 145 } else {
137 body = js.statement('return #(#)', 146 body = js.statement('return #(#)',
138 [emitter.staticFunctionAccess(member), argumentsBuffer]); 147 [emitter.staticFunctionAccess(member), argumentsBuffer]);
139 } 148 }
140 149
141 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); 150 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]);
142 151
143 return function; 152 String name = namer.invocationName(selector);
153 String callName =
154 (callSelector != null) ? namer.invocationName(callSelector) : null;
155 return new ParameterStubMethod(name, callName, function);
144 } 156 }
145 157
146 Map<Selector, jsAst.Expression> generateParameterStubs(FunctionElement member, 158 // We fill the lists depending on possible/invoked selectors. For example,
147 [bool canTearOff = false]) { 159 // take method foo:
148 Map<Selector, jsAst.Expression> generatedStubs 160 // foo(a, b, {c, d});
149 = <Selector, jsAst.Expression>{}; 161 //
150 162 // We may have multiple ways of calling foo:
163 // (1) foo(1, 2);
164 // (2) foo(1, 2, c: 3);
165 // (3) foo(1, 2, d: 4);
166 // (4) foo(1, 2, c: 3, d: 4);
167 // (5) foo(1, 2, d: 4, c: 3);
168 //
169 // What we generate at the call sites are:
170 // (1) foo$2(1, 2);
171 // (2) foo$3$c(1, 2, 3);
172 // (3) foo$3$d(1, 2, 4);
173 // (4) foo$4$c$d(1, 2, 3, 4);
174 // (5) foo$4$c$d(1, 2, 3, 4);
175 //
176 // The stubs we generate are (expressed in Dart):
177 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null)
178 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null);
179 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d);
180 // (4) No stub generated, call is direct.
181 // (5) No stub generated, call is direct.
182 //
183 // We need to pay attention if this stub is for a function that has been
184 // invoked from a subclass. Then we cannot just redirect, since that
185 // would invoke the methods of the subclass. We have to compile to:
186 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null)
187 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null);
188 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d);
189 List<ParameterStubMethod> generateParameterStubs(FunctionElement member,
190 {bool canTearOff: true}) {
151 if (member.enclosingElement.isClosure) { 191 if (member.enclosingElement.isClosure) {
152 ClosureClassElement cls = member.enclosingElement; 192 ClosureClassElement cls = member.enclosingElement;
153 if (cls.supertype.element == backend.boundClosureClass) { 193 if (cls.supertype.element == backend.boundClosureClass) {
154 compiler.internalError(cls.methodElement, 'Bound closure1.'); 194 compiler.internalError(cls.methodElement, 'Bound closure1.');
155 } 195 }
156 if (cls.methodElement.isInstanceMember) { 196 if (cls.methodElement.isInstanceMember) {
157 compiler.internalError(cls.methodElement, 'Bound closure2.'); 197 compiler.internalError(cls.methodElement, 'Bound closure2.');
158 } 198 }
159 } 199 }
160 200
161 // We fill the lists depending on the selector. For example, 201 // The set of selectors that apply to `member`. For example, for
162 // take method foo: 202 // a member `foo(x, [y])` the following selectors may apply:
163 // foo(a, b, {c, d}); 203 // `foo(x)`, and `foo(x, y)`.
204 Set<Selector> selectors;
205 // The set of selectors that apply to `member` if it's name was `call`.
206 // This happens when a member is torn off. In that case calls to the
207 // function use the name `call`, and we must be able to handle every
208 // `call` invocation that matches the signature. For example, for
209 // a member `foo(x, [y])` the following selectors would be possible
210 // call-selectors: `call(x)`, and `call(x, y)`.
211 Set<Selector> callSelectors;
212
213 // Only instance members (not static methods) need stubs.
214 if (member.isInstanceMember) {
215 selectors = compiler.codegenWorld.invokedNames[member.name];
216 }
217
218 if (canTearOff) {
219 String call = namer.closureInvocationSelectorName;
220 callSelectors = compiler.codegenWorld.invokedNames[call];
221 }
222
223 assert(emptySelectorSet.isEmpty);
224 if (selectors == null) selectors = emptySelectorSet;
225 if (callSelectors == null) callSelectors = emptySelectorSet;
226
227 List<ParameterStubMethod> stubs = <ParameterStubMethod>[];
228
229 if (selectors.isEmpty && callSelectors.isEmpty) {
230 return stubs;
231 }
232
233 // For every call-selector the corresponding selector with the name of the
234 // member.
164 // 235 //
165 // We may have multiple ways of calling foo: 236 // For example, for the call-selector `call(x, y)` the renamed selector
166 // (1) foo(1, 2); 237 // for member `foo` would be `foo(x, y)`.
167 // (2) foo(1, 2, c: 3); 238 Set<Selector> renamedCallSelectors =
168 // (3) foo(1, 2, d: 4); 239 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 240
193 Set<Selector> selectors = member.isInstanceMember 241 Set<Selector> untypedSelectors = new Set<Selector>();
194 ? compiler.codegenWorld.invokedNames[member.name]
195 : null; // No stubs needed for static methods.
196 242
197 /// Returns all closure call selectors renamed to match this member. 243 // Start with the callSelectors since they imply the generation of the
198 Set<Selector> callSelectorsAsNamed() { 244 // non-call version.
199 if (!canTearOff) return null; 245 for (Selector selector in callSelectors) {
200 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ 246 Selector renamedSelector = new Selector.call(
201 namer.closureInvocationSelectorName]; 247 member.name, member.library,
202 if (callSelectors == null) return null; 248 selector.argumentCount, selector.namedArguments);
203 return callSelectors.map((Selector callSelector) { 249 renamedCallSelectors.add(renamedSelector);
204 return new Selector.call( 250
205 member.name, member.library, 251 if (!renamedSelector.appliesUnnamed(member, compiler.world)) continue;
206 callSelector.argumentCount, callSelector.namedArguments); 252
207 }).toSet(); 253 if (untypedSelectors.add(renamedSelector.asUntyped)) {
208 } 254 ParameterStubMethod stub =
209 if (selectors == null) { 255 generateParameterStub(member, renamedSelector, selector);
210 selectors = callSelectorsAsNamed(); 256 if (stub != null) {
211 if (selectors == null) return generatedStubs; 257 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 } 258 }
228 } 259 }
229 } 260 }
230 if (canTearOff) { 261
231 selectors = compiler.codegenWorld.invokedNames[ 262 // Now run through the actual member selectors (eg. `foo$2(x, y)` and not
232 namer.closureInvocationSelectorName]; 263 // `call$2(x, y)`. Some of them have already been generated because of the
233 if (selectors != null) { 264 // call-selectors (and they are in the renamedCallSelectors set.
234 for (Selector selector in selectors) { 265 for (Selector selector in selectors) {
235 selector = new Selector.call( 266 if (renamedCallSelectors.contains(selector)) continue;
236 member.name, member.library, 267 if (!selector.appliesUnnamed(member, compiler.world)) continue;
237 selector.argumentCount, selector.namedArguments); 268
238 if (!selector.appliesUnnamed(member, compiler.world)) continue; 269 if (untypedSelectors.add(selector.asUntyped)) {
239 if (untypedSelectors.add(selector)) { 270 ParameterStubMethod stub =
240 jsAst.Expression stub = generateParameterStub(member, selector); 271 generateParameterStub(member, selector, null);
241 if (stub != null) { 272 if (stub != null) {
242 generatedStubs[selector] = stub; 273 stubs.add(stub);
243 }
244 }
245 } 274 }
246 } 275 }
247 } 276 }
248 return generatedStubs; 277
278 return stubs;
249 } 279 }
250 } 280 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698