OLD | NEW |
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>(); | 8 static final Set<Selector> emptySelectorSet = new Set<Selector>(); |
9 | 9 |
10 final Namer namer; | 10 final Namer namer; |
11 final Compiler compiler; | 11 final Compiler compiler; |
12 final JavaScriptBackend backend; | 12 final JavaScriptBackend backend; |
13 | 13 |
14 ParameterStubGenerator(this.compiler, this.namer, this.backend); | 14 ParameterStubGenerator(this.compiler, this.namer, this.backend); |
15 | 15 |
16 Emitter get emitter => backend.emitter.emitter; | 16 Emitter get emitter => backend.emitter.emitter; |
17 CodeEmitterTask get emitterTask => backend.emitter; | 17 CodeEmitterTask get emitterTask => backend.emitter; |
18 DiagnosticReporter get reporter => compiler.reporter; | 18 DiagnosticReporter get reporter => compiler.reporter; |
19 | 19 |
20 bool needsSuperGetter(FunctionElement element) => | 20 bool needsSuperGetter(FunctionElement element) => |
21 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); | 21 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); |
22 | 22 |
23 /** | 23 /** |
24 * Generates stubs to handle invocation of methods with optional | 24 * Generates stubs to handle invocation of methods with optional |
25 * arguments. | 25 * arguments. |
26 * | 26 * |
27 * A method like `foo([x])` may be invoked by the following | 27 * A method like `foo([x])` may be invoked by the following |
28 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the | 28 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the |
29 * given [selector] and returns the generated [ParameterStubMethod]. | 29 * given [selector] and returns the generated [ParameterStubMethod]. |
30 * | 30 * |
31 * Returns null if no stub is needed. | 31 * Returns null if no stub is needed. |
32 * | 32 * |
33 * Members may be invoked in two ways: directly, or through a closure. In the | 33 * Members may be invoked in two ways: directly, or through a closure. In the |
34 * latter case the caller invokes the closure's `call` method. This method | 34 * latter case the caller invokes the closure's `call` method. This method |
35 * accepts two selectors. The returned stub method has the corresponding | 35 * accepts two selectors. The returned stub method has the corresponding |
36 * name [ParameterStubMethod.name] and [ParameterStubMethod.callName] set if | 36 * name [ParameterStubMethod.name] and [ParameterStubMethod.callName] set if |
37 * the input selector is non-null (and the member needs a stub). | 37 * the input selector is non-null (and the member needs a stub). |
38 */ | 38 */ |
39 ParameterStubMethod generateParameterStub(FunctionElement member, | 39 ParameterStubMethod generateParameterStub( |
40 Selector selector, | 40 FunctionElement member, Selector selector, Selector callSelector) { |
41 Selector callSelector) { | |
42 CallStructure callStructure = selector.callStructure; | 41 CallStructure callStructure = selector.callStructure; |
43 FunctionSignature parameters = member.functionSignature; | 42 FunctionSignature parameters = member.functionSignature; |
44 int positionalArgumentCount = callStructure.positionalArgumentCount; | 43 int positionalArgumentCount = callStructure.positionalArgumentCount; |
45 if (positionalArgumentCount == parameters.parameterCount) { | 44 if (positionalArgumentCount == parameters.parameterCount) { |
46 assert(callStructure.isUnnamed); | 45 assert(callStructure.isUnnamed); |
47 return null; | 46 return null; |
48 } | 47 } |
49 if (parameters.optionalParametersAreNamed && | 48 if (parameters.optionalParametersAreNamed && |
50 callStructure.namedArgumentCount == parameters.optionalParameterCount) { | 49 callStructure.namedArgumentCount == parameters.optionalParameterCount) { |
51 // If the selector has the same number of named arguments as the element, | 50 // If the selector has the same number of named arguments as the element, |
(...skipping 10 matching lines...) Expand all Loading... |
62 int extraArgumentCount = isInterceptedMethod ? 1 : 0; | 61 int extraArgumentCount = isInterceptedMethod ? 1 : 0; |
63 // Use '$receiver' to avoid clashes with other parameter names. Using | 62 // Use '$receiver' to avoid clashes with other parameter names. Using |
64 // '$receiver' works because namer.safeVariableName used for getting | 63 // '$receiver' works because namer.safeVariableName used for getting |
65 // parameter names never returns a name beginning with a single '$'. | 64 // parameter names never returns a name beginning with a single '$'. |
66 String receiverArgumentName = r'$receiver'; | 65 String receiverArgumentName = r'$receiver'; |
67 | 66 |
68 // The parameters that this stub takes. | 67 // The parameters that this stub takes. |
69 List<jsAst.Parameter> parametersBuffer = | 68 List<jsAst.Parameter> parametersBuffer = |
70 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | 69 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); |
71 // The arguments that will be passed to the real method. | 70 // The arguments that will be passed to the real method. |
72 List<jsAst.Expression> argumentsBuffer = | 71 List<jsAst.Expression> argumentsBuffer = new List<jsAst.Expression>( |
73 new List<jsAst.Expression>( | 72 parameters.parameterCount + extraArgumentCount); |
74 parameters.parameterCount + extraArgumentCount); | |
75 | 73 |
76 int count = 0; | 74 int count = 0; |
77 if (isInterceptedMethod) { | 75 if (isInterceptedMethod) { |
78 count++; | 76 count++; |
79 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | 77 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); |
80 argumentsBuffer[0] = js('#', receiverArgumentName); | 78 argumentsBuffer[0] = js('#', receiverArgumentName); |
81 } | 79 } |
82 | 80 |
83 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | 81 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; |
84 // Includes extra receiver argument when using interceptor convention | 82 // Includes extra receiver argument when using interceptor convention |
(...skipping 25 matching lines...) Expand all Loading... |
110 // down to the native method. | 108 // down to the native method. |
111 indexOfLastOptionalArgumentInParameters = count; | 109 indexOfLastOptionalArgumentInParameters = count; |
112 } | 110 } |
113 argumentsBuffer[count] = emitter.constantReference(value); | 111 argumentsBuffer[count] = emitter.constantReference(value); |
114 } | 112 } |
115 } | 113 } |
116 } | 114 } |
117 count++; | 115 count++; |
118 }); | 116 }); |
119 | 117 |
120 var body; // List or jsAst.Statement. | 118 var body; // List or jsAst.Statement. |
121 if (backend.nativeData.hasFixedBackendName(member)) { | 119 if (backend.nativeData.hasFixedBackendName(member)) { |
122 body = emitterTask.nativeEmitter.generateParameterStubStatements( | 120 body = emitterTask.nativeEmitter.generateParameterStubStatements( |
123 member, isInterceptedMethod, namer.invocationName(selector), | 121 member, |
124 parametersBuffer, argumentsBuffer, | 122 isInterceptedMethod, |
| 123 namer.invocationName(selector), |
| 124 parametersBuffer, |
| 125 argumentsBuffer, |
125 indexOfLastOptionalArgumentInParameters); | 126 indexOfLastOptionalArgumentInParameters); |
126 } else if (member.isInstanceMember) { | 127 } else if (member.isInstanceMember) { |
127 if (needsSuperGetter(member)) { | 128 if (needsSuperGetter(member)) { |
128 ClassElement superClass = member.enclosingClass; | 129 ClassElement superClass = member.enclosingClass; |
129 jsAst.Name methodName = namer.instanceMethodName(member); | 130 jsAst.Name methodName = namer.instanceMethodName(member); |
130 // When redirecting, we must ensure that we don't end up in a subclass. | 131 // When redirecting, we must ensure that we don't end up in a subclass. |
131 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. | 132 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. |
132 // Instead we need to call the statically resolved target. | 133 // Instead we need to call the statically resolved target. |
133 // `<class>.prototype.bar$1.call(this, argument0, ...)`. | 134 // `<class>.prototype.bar$1.call(this, argument0, ...)`. |
134 body = js.statement( | 135 body = js.statement('return #.#.call(this, #);', [ |
135 'return #.#.call(this, #);', | 136 backend.emitter |
136 [backend.emitter.prototypeAccess(superClass, | 137 .prototypeAccess(superClass, hasBeenInstantiated: true), |
137 hasBeenInstantiated: true), | 138 methodName, |
138 methodName, | 139 argumentsBuffer |
139 argumentsBuffer]); | 140 ]); |
140 } else { | 141 } else { |
141 body = js.statement( | 142 body = js.statement('return this.#(#);', |
142 'return this.#(#);', | |
143 [namer.instanceMethodName(member), argumentsBuffer]); | 143 [namer.instanceMethodName(member), argumentsBuffer]); |
144 } | 144 } |
145 } else { | 145 } else { |
146 body = js.statement('return #(#)', | 146 body = js.statement('return #(#)', |
147 [emitter.staticFunctionAccess(member), argumentsBuffer]); | 147 [emitter.staticFunctionAccess(member), argumentsBuffer]); |
148 } | 148 } |
149 | 149 |
150 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); | 150 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); |
151 | 151 |
152 jsAst.Name name = member.isStatic ? null : namer.invocationName(selector); | 152 jsAst.Name name = member.isStatic ? null : namer.invocationName(selector); |
(...skipping 27 matching lines...) Expand all Loading... |
180 // (4) No stub generated, call is direct. | 180 // (4) No stub generated, call is direct. |
181 // (5) No stub generated, call is direct. | 181 // (5) No stub generated, call is direct. |
182 // | 182 // |
183 // We need to pay attention if this stub is for a function that has been | 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 | 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: | 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) | 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); | 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); | 188 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); |
189 List<ParameterStubMethod> generateParameterStubs(MethodElement member, | 189 List<ParameterStubMethod> generateParameterStubs(MethodElement member, |
190 {bool canTearOff: true}) { | 190 {bool canTearOff: true}) { |
191 if (member.enclosingElement.isClosure) { | 191 if (member.enclosingElement.isClosure) { |
192 ClosureClassElement cls = member.enclosingElement; | 192 ClosureClassElement cls = member.enclosingElement; |
193 if (cls.supertype.element == backend.helpers.boundClosureClass) { | 193 if (cls.supertype.element == backend.helpers.boundClosureClass) { |
194 reporter.internalError(cls.methodElement, 'Bound closure1.'); | 194 reporter.internalError(cls.methodElement, 'Bound closure1.'); |
195 } | 195 } |
196 if (cls.methodElement.isInstanceMember) { | 196 if (cls.methodElement.isInstanceMember) { |
197 reporter.internalError(cls.methodElement, 'Bound closure2.'); | 197 reporter.internalError(cls.methodElement, 'Bound closure2.'); |
198 } | 198 } |
199 } | 199 } |
200 | 200 |
201 // The set of selectors that apply to `member`. For example, for | 201 // The set of selectors that apply to `member`. For example, for |
202 // a member `foo(x, [y])` the following selectors may apply: | 202 // a member `foo(x, [y])` the following selectors may apply: |
203 // `foo(x)`, and `foo(x, y)`. | 203 // `foo(x)`, and `foo(x, y)`. |
204 Map<Selector, SelectorConstraints> selectors; | 204 Map<Selector, SelectorConstraints> selectors; |
205 // The set of selectors that apply to `member` if it's name was `call`. | 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 | 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 | 207 // function use the name `call`, and we must be able to handle every |
208 // `call` invocation that matches the signature. For example, for | 208 // `call` invocation that matches the signature. For example, for |
209 // a member `foo(x, [y])` the following selectors would be possible | 209 // a member `foo(x, [y])` the following selectors would be possible |
210 // call-selectors: `call(x)`, and `call(x, y)`. | 210 // call-selectors: `call(x)`, and `call(x, y)`. |
211 Map<Selector, SelectorConstraints> callSelectors; | 211 Map<Selector, SelectorConstraints> callSelectors; |
212 | 212 |
213 // Only instance members (not static methods) need stubs. | 213 // Only instance members (not static methods) need stubs. |
214 if (member.isInstanceMember) { | 214 if (member.isInstanceMember) { |
215 selectors = compiler.codegenWorld.invocationsByName(member.name); | 215 selectors = compiler.codegenWorld.invocationsByName(member.name); |
216 } | 216 } |
217 | 217 |
218 if (canTearOff) { | 218 if (canTearOff) { |
219 String call = namer.closureInvocationSelectorName; | 219 String call = namer.closureInvocationSelectorName; |
220 callSelectors = compiler.codegenWorld.invocationsByName(call); | 220 callSelectors = compiler.codegenWorld.invocationsByName(call); |
221 } | 221 } |
222 | 222 |
223 assert(emptySelectorSet.isEmpty); | 223 assert(emptySelectorSet.isEmpty); |
224 if (selectors == null) selectors = const <Selector, SelectorConstraints>{}; | 224 if (selectors == null) selectors = const <Selector, SelectorConstraints>{}; |
225 if (callSelectors == null) callSelectors = | 225 if (callSelectors == null) |
226 const <Selector, SelectorConstraints>{}; | 226 callSelectors = const <Selector, SelectorConstraints>{}; |
227 | 227 |
228 List<ParameterStubMethod> stubs = <ParameterStubMethod>[]; | 228 List<ParameterStubMethod> stubs = <ParameterStubMethod>[]; |
229 | 229 |
230 if (selectors.isEmpty && callSelectors.isEmpty) { | 230 if (selectors.isEmpty && callSelectors.isEmpty) { |
231 return stubs; | 231 return stubs; |
232 } | 232 } |
233 | 233 |
234 // For every call-selector the corresponding selector with the name of the | 234 // For every call-selector the corresponding selector with the name of the |
235 // member. | 235 // member. |
236 // | 236 // |
237 // For example, for the call-selector `call(x, y)` the renamed selector | 237 // For example, for the call-selector `call(x, y)` the renamed selector |
238 // for member `foo` would be `foo(x, y)`. | 238 // for member `foo` would be `foo(x, y)`. |
239 Set<Selector> renamedCallSelectors = | 239 Set<Selector> renamedCallSelectors = |
240 callSelectors.isEmpty ? emptySelectorSet : new Set<Selector>(); | 240 callSelectors.isEmpty ? emptySelectorSet : new Set<Selector>(); |
241 | 241 |
242 Set<Selector> untypedSelectors = new Set<Selector>(); | 242 Set<Selector> untypedSelectors = new Set<Selector>(); |
243 | 243 |
244 // Start with the callSelectors since they imply the generation of the | 244 // Start with the callSelectors since they imply the generation of the |
245 // non-call version. | 245 // non-call version. |
246 for (Selector selector in callSelectors.keys) { | 246 for (Selector selector in callSelectors.keys) { |
247 Selector renamedSelector = new Selector.call( | 247 Selector renamedSelector = |
248 member.memberName, | 248 new Selector.call(member.memberName, selector.callStructure); |
249 selector.callStructure); | |
250 renamedCallSelectors.add(renamedSelector); | 249 renamedCallSelectors.add(renamedSelector); |
251 | 250 |
252 if (!renamedSelector.appliesUnnamed(member, compiler.world)) continue; | 251 if (!renamedSelector.appliesUnnamed(member, compiler.world)) continue; |
253 | 252 |
254 if (untypedSelectors.add(renamedSelector)) { | 253 if (untypedSelectors.add(renamedSelector)) { |
255 ParameterStubMethod stub = | 254 ParameterStubMethod stub = |
256 generateParameterStub(member, renamedSelector, selector); | 255 generateParameterStub(member, renamedSelector, selector); |
257 if (stub != null) { | 256 if (stub != null) { |
258 stubs.add(stub); | 257 stubs.add(stub); |
259 } | 258 } |
(...skipping 15 matching lines...) Expand all Loading... |
275 generateParameterStub(member, selector, null); | 274 generateParameterStub(member, selector, null); |
276 if (stub != null) { | 275 if (stub != null) { |
277 stubs.add(stub); | 276 stubs.add(stub); |
278 } | 277 } |
279 } | 278 } |
280 } | 279 } |
281 | 280 |
282 return stubs; | 281 return stubs; |
283 } | 282 } |
284 } | 283 } |
OLD | NEW |