OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 /// This class should morph into something that makes it easy to build | 7 /// This class should morph into something that makes it easy to build |
8 /// JavaScript representations of libraries, class-sides, and instance-sides. | 8 /// JavaScript representations of libraries, class-sides, and instance-sides. |
9 /// Initially, it is just a placeholder for code that is moved from | 9 /// Initially, it is just a placeholder for code that is moved from |
10 /// [CodeEmitterTask]. | 10 /// [CodeEmitterTask]. |
11 class ContainerBuilder extends CodeEmitterHelper { | 11 class ContainerBuilder extends CodeEmitterHelper { |
12 final Map<Element, Element> staticGetters = new Map<Element, Element>(); | 12 final Map<Element, Element> staticGetters = new Map<Element, Element>(); |
13 | 13 |
14 /// A cache of synthesized closures for top-level, static or | 14 /// A cache of synthesized closures for top-level, static or |
15 /// instance methods. | 15 /// instance methods. |
16 final Map<String, Element> methodClosures = <String, Element>{}; | 16 final Map<String, Element> methodClosures = <String, Element>{}; |
17 | 17 |
18 /** | 18 /** |
19 * Generate stubs to handle invocation of methods with optional | 19 * Generate stubs to handle invocation of methods with optional |
20 * arguments. | 20 * arguments. |
21 * | 21 * |
22 * A method like [: foo([x]) :] may be invoked by the following | 22 * A method like [: foo([x]) :] may be invoked by the following |
23 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this | 23 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this |
24 * function for detailed examples. | 24 * function for detailed examples. |
25 */ | 25 */ |
26 void addParameterStub(FunctionElement member, | 26 void addParameterStub(FunctionElement member, |
27 Selector selector, | 27 Selector selector, |
28 DefineStubFunction defineStub, | 28 AddStubFunction addStub, |
29 Set<String> alreadyGenerated) { | 29 Set<String> alreadyGenerated) { |
30 FunctionSignature parameters = member.computeSignature(compiler); | 30 FunctionSignature parameters = member.computeSignature(compiler); |
31 int positionalArgumentCount = selector.positionalArgumentCount; | 31 int positionalArgumentCount = selector.positionalArgumentCount; |
32 if (positionalArgumentCount == parameters.parameterCount) { | 32 if (positionalArgumentCount == parameters.parameterCount) { |
33 assert(selector.namedArgumentCount == 0); | 33 assert(selector.namedArgumentCount == 0); |
34 return; | 34 return; |
35 } | 35 } |
36 if (parameters.optionalParametersAreNamed | 36 if (parameters.optionalParametersAreNamed |
37 && selector.namedArgumentCount == parameters.optionalParameterCount) { | 37 && selector.namedArgumentCount == parameters.optionalParameterCount) { |
38 // If the selector has the same number of named arguments as the element, | 38 // If the selector has the same number of named arguments as the element, |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
114 } | 114 } |
115 count++; | 115 count++; |
116 }); | 116 }); |
117 | 117 |
118 List body; | 118 List body; |
119 if (member.hasFixedBackendName()) { | 119 if (member.hasFixedBackendName()) { |
120 body = task.nativeEmitter.generateParameterStubStatements( | 120 body = task.nativeEmitter.generateParameterStubStatements( |
121 member, isInterceptedMethod, invocationName, | 121 member, isInterceptedMethod, invocationName, |
122 parametersBuffer, argumentsBuffer, | 122 parametersBuffer, argumentsBuffer, |
123 indexOfLastOptionalArgumentInParameters); | 123 indexOfLastOptionalArgumentInParameters); |
124 } else { | 124 } else if (member.isInstanceMember()) { |
125 body = [js.return_( | 125 body = [js.return_( |
126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; | 126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; |
127 } else { | |
128 body = [js.return_(namer.elementAccess(member)(argumentsBuffer))]; | |
127 } | 129 } |
128 | 130 |
129 jsAst.Fun function = js.fun(parametersBuffer, body); | 131 jsAst.Fun function = js.fun(parametersBuffer, body); |
130 | 132 |
131 defineStub(invocationName, function); | 133 addStub(selector, function); |
132 | |
133 String reflectionName = task.getReflectionName(selector, invocationName); | |
134 if (reflectionName != null) { | |
135 var reflectable = | |
136 js(backend.isAccessibleByReflection(member) ? '1' : '0'); | |
137 defineStub('+$reflectionName', reflectable); | |
138 } | |
139 } | 134 } |
140 | 135 |
141 void addParameterStubs(FunctionElement member, | 136 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, |
142 DefineStubFunction defineStub) { | 137 [bool canTearOff = false]) { |
138 if (member.enclosingElement.isClosure()) { | |
139 ClosureClassElement cls = member.enclosingElement; | |
140 if (cls.supertype.element == compiler.boundClosureClass) { | |
141 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure1.'); | |
142 } | |
143 if (cls.methodElement.isInstanceMember()) { | |
144 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure2.'); | |
145 } | |
146 } | |
147 | |
143 // We fill the lists depending on the selector. For example, | 148 // We fill the lists depending on the selector. For example, |
144 // take method foo: | 149 // take method foo: |
145 // foo(a, b, {c, d}); | 150 // foo(a, b, {c, d}); |
146 // | 151 // |
147 // We may have multiple ways of calling foo: | 152 // We may have multiple ways of calling foo: |
148 // (1) foo(1, 2); | 153 // (1) foo(1, 2); |
149 // (2) foo(1, 2, c: 3); | 154 // (2) foo(1, 2, c: 3); |
150 // (3) foo(1, 2, d: 4); | 155 // (3) foo(1, 2, d: 4); |
151 // (4) foo(1, 2, c: 3, d: 4); | 156 // (4) foo(1, 2, c: 3, d: 4); |
152 // (5) foo(1, 2, d: 4, c: 3); | 157 // (5) foo(1, 2, d: 4, c: 3); |
153 // | 158 // |
154 // What we generate at the call sites are: | 159 // What we generate at the call sites are: |
155 // (1) foo$2(1, 2); | 160 // (1) foo$2(1, 2); |
156 // (2) foo$3$c(1, 2, 3); | 161 // (2) foo$3$c(1, 2, 3); |
157 // (3) foo$3$d(1, 2, 4); | 162 // (3) foo$3$d(1, 2, 4); |
158 // (4) foo$4$c$d(1, 2, 3, 4); | 163 // (4) foo$4$c$d(1, 2, 3, 4); |
159 // (5) foo$4$c$d(1, 2, 3, 4); | 164 // (5) foo$4$c$d(1, 2, 3, 4); |
160 // | 165 // |
161 // The stubs we generate are (expressed in Dart): | 166 // The stubs we generate are (expressed in Dart): |
162 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) | 167 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) |
163 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); | 168 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); |
164 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); | 169 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); |
165 // (4) No stub generated, call is direct. | 170 // (4) No stub generated, call is direct. |
166 // (5) No stub generated, call is direct. | 171 // (5) No stub generated, call is direct. |
167 | 172 |
168 // Keep a cache of which stubs have already been generated, to | 173 // Keep a cache of which stubs have already been generated, to |
169 // avoid duplicates. Note that even if selectors are | 174 // avoid duplicates. Note that even if selectors are |
170 // canonicalized, we would still need this cache: a typed selector | 175 // canonicalized, we would still need this cache: a typed selector |
171 // on A and a typed selector on B could yield the same stub. | 176 // on A and a typed selector on B could yield the same stub. |
172 Set<String> generatedStubNames = new Set<String>(); | 177 Set<Selector> selectors = null; |
kasperl
2013/11/29 10:10:55
Maybe use ?: so the comment about not needing stub
ahe
2013/12/06 15:57:53
Done.
| |
173 bool isClosureInvocation = | 178 if (member.isInstanceMember()) { |
174 member.name == namer.closureInvocationSelectorName; | 179 // No stubs needed for static methods. |
175 if (backend.isNeededForReflection(member) || | 180 selectors = compiler.codegenWorld.invokedNames[member.name]; |
176 (compiler.enabledFunctionApply && isClosureInvocation)) { | 181 } |
177 // If [Function.apply] is called, we pessimistically compile all | 182 |
178 // possible stubs for this closure. | 183 /// Returns all closure call selectors renamed to match this member. |
179 FunctionSignature signature = member.computeSignature(compiler); | 184 Set<Selector> callSelectorsAsNamed() { |
180 Set<Selector> selectors = signature.optionalParametersAreNamed | 185 if (!canTearOff) return null; |
181 ? computeSeenNamedSelectors(member) | 186 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ |
182 : computeOptionalSelectors(signature, member); | 187 namer.closureInvocationSelectorName]; |
188 if (callSelectors == null) return null; | |
189 return callSelectors.map((Selector callSelector) { | |
190 return new Selector.call( | |
191 member.name, member.getLibrary(), | |
192 callSelector.argumentCount, callSelector.namedArguments); | |
193 }).toSet(); | |
194 } | |
195 if (selectors == null) { | |
196 selectors = callSelectorsAsNamed(); | |
197 if (selectors == null) return; | |
198 } else { | |
199 Set<Selector> callSelectors = callSelectorsAsNamed(); | |
200 if (callSelectors != null) { | |
201 selectors = selectors.union(callSelectors); | |
202 } | |
203 } | |
204 Set<Selector> untypedSelectors = new Set(); | |
205 if (selectors != null) { | |
183 for (Selector selector in selectors) { | 206 for (Selector selector in selectors) { |
184 addParameterStub(member, selector, defineStub, generatedStubNames); | 207 if (!selector.appliesUnnamed(member, compiler)) continue; |
208 if (untypedSelectors.add(selector.asUntyped)) { | |
209 // TODO(ahe): Is the last argument to [addParameterStub] needed? | |
210 addParameterStub(member, selector, defineStub, new Set<String>()); | |
211 } | |
185 } | 212 } |
186 if (signature.optionalParametersAreNamed && isClosureInvocation) { | 213 } |
187 addCatchAllParameterStub(member, signature, defineStub); | 214 if (canTearOff) { |
188 } | 215 selectors = compiler.codegenWorld.invokedNames[ |
189 } else { | 216 namer.closureInvocationSelectorName]; |
190 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 217 if (selectors != null) { |
191 if (selectors == null) return; | 218 for (Selector selector in selectors) { |
192 for (Selector selector in selectors) { | 219 selector = new Selector.call( |
193 if (!selector.applies(member, compiler)) continue; | 220 member.name, member.getLibrary(), |
194 addParameterStub(member, selector, defineStub, generatedStubNames); | 221 selector.argumentCount, selector.namedArguments); |
222 if (!selector.appliesUnnamed(member, compiler)) continue; | |
223 if (untypedSelectors.add(selector)) { | |
224 // TODO(ahe): Is the last argument to [addParameterStub] needed? | |
225 addParameterStub(member, selector, defineStub, new Set<String>()); | |
226 } | |
227 } | |
195 } | 228 } |
196 } | 229 } |
197 } | 230 } |
198 | 231 |
199 Set<Selector> computeSeenNamedSelectors(FunctionElement element) { | |
200 Set<Selector> selectors = compiler.codegenWorld.invokedNames[element.name]; | |
201 Set<Selector> result = new Set<Selector>(); | |
202 if (selectors == null) return result; | |
203 for (Selector selector in selectors) { | |
204 if (!selector.applies(element, compiler)) continue; | |
205 result.add(selector); | |
206 } | |
207 return result; | |
208 } | |
209 | |
210 void addCatchAllParameterStub(FunctionElement member, | |
211 FunctionSignature signature, | |
212 DefineStubFunction defineStub) { | |
213 // See Primities.applyFunction in js_helper.dart for details. | |
214 List<jsAst.Property> properties = <jsAst.Property>[]; | |
215 for (Element element in signature.orderedOptionalParameters) { | |
216 String jsName = backend.namer.safeName(element.name); | |
217 Constant value = compiler.constantHandler.initialVariableValues[element]; | |
218 jsAst.Expression reference = null; | |
219 if (value == null) { | |
220 reference = new jsAst.LiteralNull(); | |
221 } else { | |
222 reference = task.constantReference(value); | |
223 } | |
224 properties.add(new jsAst.Property(js.string(jsName), reference)); | |
225 } | |
226 defineStub( | |
227 backend.namer.callCatchAllName, | |
228 js.fun([], js.return_(new jsAst.ObjectInitializer(properties)))); | |
229 } | |
230 | |
231 /** | |
232 * Compute the set of possible selectors in the presence of optional | |
233 * non-named parameters. | |
234 */ | |
235 Set<Selector> computeOptionalSelectors(FunctionSignature signature, | |
236 FunctionElement element) { | |
237 Set<Selector> selectors = new Set<Selector>(); | |
238 // Add the selector that does not have any optional argument. | |
239 selectors.add(new Selector(SelectorKind.CALL, | |
240 element.name, | |
241 element.getLibrary(), | |
242 signature.requiredParameterCount, | |
243 <String>[])); | |
244 | |
245 // For each optional parameter, we increment the number of passed | |
246 // argument. | |
247 for (int i = 1; i <= signature.optionalParameterCount; i++) { | |
248 selectors.add(new Selector(SelectorKind.CALL, | |
249 element.name, | |
250 element.getLibrary(), | |
251 signature.requiredParameterCount + i, | |
252 <String>[])); | |
253 } | |
254 return selectors; | |
255 } | |
256 | |
257 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) { | 232 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) { |
233 return; | |
258 task.addComment('Static function getters', task.mainBuffer); | 234 task.addComment('Static function getters', task.mainBuffer); |
259 for (FunctionElement element in | 235 for (FunctionElement element in |
260 Elements.sortedByPosition(staticGetters.keys)) { | 236 Elements.sortedByPosition(staticGetters.keys)) { |
261 Element closure = staticGetters[element]; | 237 Element closure = staticGetters[element]; |
262 CodeBuffer buffer = | 238 CodeBuffer buffer = |
263 task.isDeferred(element) ? task.deferredConstants : eagerBuffer; | 239 task.isDeferred(element) ? task.deferredConstants : eagerBuffer; |
264 String closureClass = namer.isolateAccess(closure); | 240 String closureClass = namer.isolateAccess(closure); |
265 String name = namer.getStaticClosureName(element); | 241 String name = namer.getStaticClosureName(element); |
266 | 242 |
267 String closureName = namer.getStaticClosureName(element); | 243 String closureName = namer.getStaticClosureName(element); |
268 jsAst.Node assignment = js( | 244 jsAst.Node assignment = js( |
269 'init.globalFunctions["$closureName"] =' | 245 'init.globalFunctions["$closureName"] =' |
270 ' ${namer.globalObjectFor(element)}.$name =' | 246 ' ${namer.globalObjectFor(element)}.$name =' |
271 ' new $closureClass(#, "$closureName")', | 247 ' new $closureClass(#, "$closureName")', |
272 namer.elementAccess(element)); | 248 namer.elementAccess(element)); |
273 buffer.write(jsAst.prettyPrint(assignment, compiler)); | 249 buffer.write(jsAst.prettyPrint(assignment, compiler)); |
274 buffer.write('$N'); | 250 buffer.write('$N'); |
275 } | 251 } |
276 } | 252 } |
277 | 253 |
278 void emitStaticFunctionClosures() { | 254 void emitStaticFunctionClosures() { |
255 return; | |
279 Set<FunctionElement> functionsNeedingGetter = | 256 Set<FunctionElement> functionsNeedingGetter = |
280 compiler.codegenWorld.staticFunctionsNeedingGetter; | 257 compiler.codegenWorld.staticFunctionsNeedingGetter; |
281 for (FunctionElement element in | 258 for (FunctionElement element in |
282 Elements.sortedByPosition(functionsNeedingGetter)) { | 259 Elements.sortedByPosition(functionsNeedingGetter)) { |
283 String superName = namer.getNameOfClass(compiler.closureClass); | 260 String superName = namer.getNameOfClass(compiler.closureClass); |
284 int parameterCount = element.functionSignature.parameterCount; | 261 int parameterCount = element.functionSignature.parameterCount; |
285 String name = 'Closure\$$parameterCount'; | 262 String name = 'Closure\$$parameterCount'; |
286 assert(task.instantiatedClasses.contains(compiler.closureClass)); | 263 assert(task.instantiatedClasses.contains(compiler.closureClass)); |
287 | 264 |
288 ClassElement closureClassElement = new ClosureClassElement( | 265 ClassElement closureClassElement = new ClosureClassElement( |
(...skipping 13 matching lines...) Expand all Loading... | |
302 // Define the constructor with a name so that Object.toString can | 279 // Define the constructor with a name so that Object.toString can |
303 // find the class name of the closure class. | 280 // find the class name of the closure class. |
304 ClassBuilder closureBuilder = new ClassBuilder(); | 281 ClassBuilder closureBuilder = new ClassBuilder(); |
305 // If a static function is used as a closure we need to add its name | 282 // If a static function is used as a closure we need to add its name |
306 // in case it is used in spawnFunction. | 283 // in case it is used in spawnFunction. |
307 String methodName = namer.STATIC_CLOSURE_NAME_NAME; | 284 String methodName = namer.STATIC_CLOSURE_NAME_NAME; |
308 List<String> fieldNames = <String>[invocationName, methodName]; | 285 List<String> fieldNames = <String>[invocationName, methodName]; |
309 closureBuilder.addProperty('', | 286 closureBuilder.addProperty('', |
310 js.string("$superName;${fieldNames.join(',')}")); | 287 js.string("$superName;${fieldNames.join(',')}")); |
311 | 288 |
312 addParameterStubs(callElement, closureBuilder.addProperty); | 289 addParameterStubs( |
290 callElement, | |
291 (Selector selector, jsAst.Fun function) { | |
292 closureBuilder.addProperty( | |
293 namer.invocationName(selector), function); | |
294 }); | |
313 | 295 |
314 void emitFunctionTypeSignature(Element method, FunctionType methodType) { | 296 void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
315 RuntimeTypes rti = backend.rti; | 297 RuntimeTypes rti = backend.rti; |
316 // [:() => null:] is dummy encoding of [this] which is never needed for | 298 // [:() => null:] is dummy encoding of [this] which is never needed for |
317 // the encoding of the type of the static [method]. | 299 // the encoding of the type of the static [method]. |
318 jsAst.Expression encoding = | 300 jsAst.Expression encoding = |
319 rti.getSignatureEncoding(methodType, js('null')); | 301 rti.getSignatureEncoding(methodType, js('null')); |
320 String operatorSignature = namer.operatorSignature(); | 302 String operatorSignature = namer.operatorSignature(); |
321 // TODO(johnniwinther): Make MiniJsParser support function expressions. | 303 // TODO(johnniwinther): Make MiniJsParser support function expressions. |
322 closureBuilder.addProperty(operatorSignature, encoding); | 304 closureBuilder.addProperty(operatorSignature, encoding); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
365 | 347 |
366 task.precompiledConstructorNames.add(js(constructorName)); | 348 task.precompiledConstructorNames.add(js(constructorName)); |
367 } | 349 } |
368 | 350 |
369 /** | 351 /** |
370 * Documentation wanted -- johnniwinther | 352 * Documentation wanted -- johnniwinther |
371 * | 353 * |
372 * Invariant: [member] must be a declaration element. | 354 * Invariant: [member] must be a declaration element. |
373 */ | 355 */ |
374 void emitDynamicFunctionGetter(FunctionElement member, | 356 void emitDynamicFunctionGetter(FunctionElement member, |
375 DefineStubFunction defineStub) { | 357 AddPropertyFunction addProperty) { |
376 assert(invariant(member, member.isDeclaration)); | 358 assert(invariant(member, member.isDeclaration)); |
377 assert(task.instantiatedClasses.contains(compiler.boundClosureClass)); | 359 assert(task.instantiatedClasses.contains(compiler.boundClosureClass)); |
378 // For every method that has the same name as a property-get we create a | 360 // For every method that has the same name as a property-get we create a |
379 // getter that returns a bound closure. Say we have a class 'A' with method | 361 // getter that returns a bound closure. Say we have a class 'A' with method |
380 // 'foo' and somewhere in the code there is a dynamic property get of | 362 // 'foo' and somewhere in the code there is a dynamic property get of |
381 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): | 363 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): |
382 // | 364 // |
383 // class A { | 365 // class A { |
384 // foo(x, y, z) { ... } // Original function. | 366 // foo(x, y, z) { ... } // Original function. |
385 // get foo { return new BoundClosure499(this, "foo"); } | 367 // get foo { return new BoundClosure499(this, "foo"); } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
435 parameters.add(name); | 417 parameters.add(name); |
436 arguments.add(js(name)); | 418 arguments.add(js(name)); |
437 } | 419 } |
438 | 420 |
439 jsAst.Expression fun = js.fun( | 421 jsAst.Expression fun = js.fun( |
440 parameters, | 422 parameters, |
441 js.return_( | 423 js.return_( |
442 js('this')[fieldNames[1]]['call'](arguments))); | 424 js('this')[fieldNames[1]]['call'](arguments))); |
443 boundClosureBuilder.addProperty(invocationName, fun); | 425 boundClosureBuilder.addProperty(invocationName, fun); |
444 | 426 |
445 addParameterStubs(callElement, boundClosureBuilder.addProperty); | 427 addParameterStubs( |
428 callElement, | |
429 (Selector selector, jsAst.Fun function) { | |
430 boundClosureBuilder.addProperty( | |
431 namer.invocationName(selector), function); | |
432 }); | |
446 | 433 |
447 void emitFunctionTypeSignature(Element method, FunctionType methodType) { | 434 void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
448 jsAst.Expression encoding = backend.rti.getSignatureEncoding( | 435 jsAst.Expression encoding = backend.rti.getSignatureEncoding( |
449 methodType, js('this')[fieldNames[0]]); | 436 methodType, js('this')[fieldNames[0]]); |
450 String operatorSignature = namer.operatorSignature(); | 437 String operatorSignature = namer.operatorSignature(); |
451 boundClosureBuilder.addProperty(operatorSignature, encoding); | 438 boundClosureBuilder.addProperty(operatorSignature, encoding); |
452 } | 439 } |
453 | 440 |
454 DartType memberType = member.computeType(compiler); | 441 DartType memberType = member.computeType(compiler); |
455 Map<FunctionType, bool> functionTypeChecks = | 442 Map<FunctionType, bool> functionTypeChecks = |
(...skipping 24 matching lines...) Expand all Loading... | |
480 } else { | 467 } else { |
481 // Put null in the intercepted receiver field. | 468 // Put null in the intercepted receiver field. |
482 arguments.add(new jsAst.LiteralNull()); | 469 arguments.add(new jsAst.LiteralNull()); |
483 } | 470 } |
484 | 471 |
485 arguments.add(js.string(targetName)); | 472 arguments.add(js.string(targetName)); |
486 | 473 |
487 jsAst.Expression getterFunction = js.fun( | 474 jsAst.Expression getterFunction = js.fun( |
488 parameters, js.return_(js(closureClass).newWith(arguments))); | 475 parameters, js.return_(js(closureClass).newWith(arguments))); |
489 | 476 |
490 defineStub(getterName, getterFunction); | 477 addProperty(getterName, getterFunction); |
491 } | 478 } |
492 | 479 |
493 /** | 480 /** |
494 * Documentation wanted -- johnniwinther | 481 * Documentation wanted -- johnniwinther |
495 * | 482 * |
496 * Invariant: [member] must be a declaration element. | 483 * Invariant: [member] must be a declaration element. |
497 */ | 484 */ |
498 void emitCallStubForGetter(Element member, | 485 void emitCallStubForGetter(Element member, |
499 Set<Selector> selectors, | 486 Set<Selector> selectors, |
500 DefineStubFunction defineStub) { | 487 AddPropertyFunction addProperty) { |
501 assert(invariant(member, member.isDeclaration)); | 488 assert(invariant(member, member.isDeclaration)); |
502 LibraryElement memberLibrary = member.getLibrary(); | 489 LibraryElement memberLibrary = member.getLibrary(); |
503 // If the method is intercepted, the stub gets the | 490 // If the method is intercepted, the stub gets the |
504 // receiver explicitely and we need to pass it to the getter call. | 491 // receiver explicitely and we need to pass it to the getter call. |
505 bool isInterceptedMethod = backend.isInterceptedMethod(member); | 492 bool isInterceptedMethod = backend.isInterceptedMethod(member); |
506 | 493 |
507 const String receiverArgumentName = r'$receiver'; | 494 const String receiverArgumentName = r'$receiver'; |
508 | 495 |
509 jsAst.Expression buildGetter() { | 496 jsAst.Expression buildGetter() { |
510 if (member.isGetter()) { | 497 if (member.isGetter()) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
542 for (int i = 0; i < selector.argumentCount; i++) { | 529 for (int i = 0; i < selector.argumentCount; i++) { |
543 String name = 'arg$i'; | 530 String name = 'arg$i'; |
544 parameters.add(new jsAst.Parameter(name)); | 531 parameters.add(new jsAst.Parameter(name)); |
545 arguments.add(js(name)); | 532 arguments.add(js(name)); |
546 } | 533 } |
547 | 534 |
548 jsAst.Fun function = js.fun( | 535 jsAst.Fun function = js.fun( |
549 parameters, | 536 parameters, |
550 js.return_(buildGetter()[closureCallName](arguments))); | 537 js.return_(buildGetter()[closureCallName](arguments))); |
551 | 538 |
552 defineStub(invocationName, function); | 539 addProperty(invocationName, function); |
553 } | 540 } |
554 } | 541 } |
555 } | 542 } |
556 | 543 |
557 /** | 544 /** |
558 * Documentation wanted -- johnniwinther | 545 * Documentation wanted -- johnniwinther |
559 * | 546 * |
560 * Invariant: [member] must be a declaration element. | 547 * Invariant: [member] must be a declaration element. |
561 */ | 548 */ |
562 void emitExtraAccessors(Element member, ClassBuilder builder) { | 549 void emitExtraAccessors(Element member, ClassBuilder builder) { |
563 assert(invariant(member, member.isDeclaration)); | 550 assert(invariant(member, member.isDeclaration)); |
564 if (member.isGetter() || member.isField()) { | 551 if (member.isGetter() || member.isField()) { |
565 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 552 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; |
566 if (selectors != null && !selectors.isEmpty) { | 553 if (selectors != null && !selectors.isEmpty) { |
567 emitCallStubForGetter(member, selectors, builder.addProperty); | 554 emitCallStubForGetter(member, selectors, builder.addProperty); |
568 } | 555 } |
569 } else if (member.isFunction()) { | |
570 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { | |
571 emitDynamicFunctionGetter(member, builder.addProperty); | |
572 } | |
573 } | 556 } |
574 } | 557 } |
575 | 558 |
576 void addMember(Element member, ClassBuilder builder) { | 559 void addMember(Element member, ClassBuilder builder) { |
577 assert(invariant(member, member.isDeclaration)); | 560 assert(invariant(member, member.isDeclaration)); |
578 | 561 |
579 if (member.isField()) { | 562 if (member.isField()) { |
580 addMemberField(member, builder); | 563 addMemberField(member, builder); |
581 } else if (member.isFunction() || | 564 } else if (member.isFunction() || |
582 member.isGenerativeConstructorBody() || | 565 member.isGenerativeConstructorBody() || |
583 member.isGenerativeConstructor() || | 566 member.isGenerativeConstructor() || |
584 member.isAccessor()) { | 567 member.isAccessor()) { |
585 addMemberMethod(member, builder); | 568 addMemberMethod(member, builder); |
586 } else { | 569 } else { |
587 compiler.internalErrorOnElement( | 570 compiler.internalErrorOnElement( |
588 member, 'unexpected kind: "${member.kind}"'); | 571 member, 'unexpected kind: "${member.kind}"'); |
589 } | 572 } |
590 if (member.isInstanceMember()) emitExtraAccessors(member, builder); | 573 if (member.isInstanceMember()) emitExtraAccessors(member, builder); |
591 } | 574 } |
592 | 575 |
593 void addMemberMethod(FunctionElement member, ClassBuilder builder) { | 576 void addMemberMethod(FunctionElement member, ClassBuilder builder) { |
594 if (member.isAbstract(compiler)) return; | 577 if (member.isAbstract(compiler)) return; |
595 jsAst.Expression code = backend.generatedCode[member]; | 578 jsAst.Expression code = backend.generatedCode[member]; |
596 if (code == null) return; | 579 if (code == null) return; |
597 String name = namer.getNameOfMember(member); | 580 String name = namer.getNameOfMember(member); |
598 if (backend.isInterceptedMethod(member)) { | 581 task.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); |
599 task.interceptorEmitter.interceptorInvocationNames.add(name); | 582 FunctionSignature parameters = member.computeSignature(compiler); |
583 bool needsStubs = !parameters.optionalParameters.isEmpty; | |
584 bool canTearOff = false; | |
585 bool isClosure = false; | |
586 String tearOffName; | |
587 if (!member.isFunction() || member.isConstructor() || member.isAccessor()) { | |
588 canTearOff = false; | |
589 } else if (member.isInstanceMember()) { | |
590 if (member.getEnclosingClass().isClosure()) { | |
591 canTearOff = false; | |
592 isClosure = true; | |
593 } else { | |
594 // Careful with operators. | |
595 canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler); | |
596 tearOffName = namer.getterName(member); | |
597 } | |
598 } else { | |
599 canTearOff = | |
600 compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member); | |
601 tearOffName = namer.getStaticClosureName(member); | |
600 } | 602 } |
601 code = task.metadataEmitter.extendWithMetadata(member, code); | 603 |
602 builder.addProperty(name, code); | 604 bool canBeReflected = backend.isAccessibleByReflection(member); |
603 String reflectionName = task.getReflectionName(member, name); | 605 bool needStructuredInfo = canTearOff || canBeReflected || compiler.enabledFu nctionApply; |
kasperl
2013/11/29 10:10:55
Long line.
ahe
2013/12/06 15:57:53
Done.
| |
604 if (reflectionName != null) { | 606 if (!needStructuredInfo) { |
605 var reflectable = | 607 builder.addProperty(name, code); |
606 js(backend.isAccessibleByReflection(member) ? '1' : '0'); | 608 if (needsStubs) { |
607 builder.addProperty('+$reflectionName', reflectable); | 609 addParameterStubs( |
608 jsAst.Node defaultValues = | 610 member, |
609 task.metadataEmitter.reifyDefaultArguments(member); | 611 (Selector selector, jsAst.Fun function) { |
610 if (defaultValues != null) { | 612 builder.addProperty(namer.invocationName(selector), function); |
611 String unmangledName = member.name; | 613 }); |
612 builder.addProperty('*$unmangledName', defaultValues); | |
613 } | 614 } |
615 return; | |
614 } | 616 } |
615 if (member.isInstanceMember()) { | 617 |
616 // TODO(ahe): Where is this done for static/top-level methods? | 618 if (canTearOff) { |
617 FunctionSignature parameters = member.computeSignature(compiler); | 619 assert(invariant(member, !member.isGenerativeConstructor())); |
618 if (!parameters.optionalParameters.isEmpty) { | 620 assert(invariant(member, !member.isGenerativeConstructorBody())); |
619 addParameterStubs(member, builder.addProperty); | 621 assert(invariant(member, !member.isConstructor())); |
622 } | |
623 | |
624 // This element is needed for reflection or needs additional stubs. So we | |
625 // need to retain additional information. | |
626 | |
627 // The information is stored in an array with this format: | |
628 // | |
629 // 1. The JS function for this member. | |
630 // 2. First stub. | |
631 // 3. Name of first stub. | |
632 // ... | |
633 // M. Call name of this member. | |
634 // M+1. Call name of first stub. | |
635 // ... | |
636 // N. Getter name for tearOff. | |
637 // N+1. (Required parameter count << 1) + (member.isAccessor() ? 1 : 0). | |
638 // N+2. (Optional parameter count << 1) + | |
639 // (parameters.optionalParametersAreNamed ? 1 : 0). | |
640 // N+3. Index to function type in constant pool. | |
641 // N+4. First default argument. | |
642 // ... | |
643 // O. First parameter name (if needed for reflection or Function.apply). | |
644 // ... | |
645 // P. Unmangled name (if reflectable). | |
646 // P+1. First metadata (if reflectable). | |
647 // ... | |
648 | |
649 List expressions = []; | |
650 | |
651 Selector callSelector; | |
652 if (member.isFunction()) { | |
653 callSelector = | |
654 new Selector.fromElement(member, compiler).toCallSelector(); | |
655 } | |
656 | |
657 // On [requiredParameterCount], the lower bit is set if this method can be | |
658 // called reflectively. | |
659 int requiredParameterCount = parameters.requiredParameterCount << 1; | |
660 if (member.isAccessor()) requiredParameterCount++; | |
661 | |
662 int optionalParameterCount = parameters.optionalParameterCount << 1; | |
663 if (parameters.optionalParametersAreNamed) optionalParameterCount++; | |
664 | |
665 expressions.add(code); | |
666 | |
667 String callSelectorString = | |
668 callSelector == null ? null : '"${namer.invocationName(callSelector)}"'; | |
669 List tearOffInfo = | |
670 [new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')]; | |
671 | |
672 if (needsStubs || canTearOff) { | |
673 addParameterStubs(member, (Selector selector, jsAst.Fun function) { | |
674 expressions.add(function); | |
675 if (member.isInstanceMember()) { | |
676 Set invokedSelectors = | |
677 compiler.codegenWorld.invokedNames[member.name]; | |
678 if (invokedSelectors != null && invokedSelectors.contains(selector)) { | |
679 expressions.add(js.string(namer.invocationName(selector))); | |
680 } else { | |
681 // Don't add a stub for calling this as a regular instance method, | |
682 // we only need the "call" stub for implicit closures of this | |
683 // method. | |
684 expressions.add("null"); | |
685 } | |
686 } else { | |
687 // Static methods don't need "named" stubs as the default arguments | |
688 // are inlined at call sites. But static methods might need "call" | |
689 // stubs for implicit closures. | |
690 expressions.add("null"); | |
691 // TOOD(ahe): Since we know when reading static data versus instance | |
692 // data, we can eliminate this element. | |
693 } | |
694 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | |
695 namer.closureInvocationSelectorName]; | |
696 Selector callSelector = selector.toCallSelector(); | |
697 String callSelectorString = 'null'; | |
698 if (canTearOff && callSelectors != null && callSelectors.contains(callSe lector)) { | |
kasperl
2013/11/29 10:10:55
Long lines (there are a number of occurrences in t
ahe
2013/12/06 15:57:53
Done.
| |
699 callSelectorString = '"${namer.invocationName(callSelector)}"'; | |
700 } | |
701 tearOffInfo.add(new jsAst.LiteralString('$callSelectorString /* tearOffI nfo */')); | |
702 }, canTearOff); | |
703 } | |
704 | |
705 jsAst.Expression memberTypeExpression; | |
706 if ((canTearOff || canBeReflected) && !member.isGenerativeConstructorBody()) { | |
707 DartType memberType = member.computeType(compiler); | |
708 if (memberType.containsTypeVariables) { | |
709 jsAst.Expression thisAccess = js(r'this.$receiver'); | |
710 memberTypeExpression = | |
711 backend.rti.getSignatureEncoding(memberType, thisAccess); | |
712 } else { | |
713 memberTypeExpression = | |
714 js.toExpression(task.metadataEmitter.reifyType(memberType)); | |
620 } | 715 } |
716 } else { | |
717 memberTypeExpression = js('null'); | |
621 } | 718 } |
719 | |
720 expressions | |
721 ..addAll(tearOffInfo) | |
722 ..add((tearOffName == null || member.isAccessor()) | |
723 ? js("null") : js.string(tearOffName)) | |
724 ..add(requiredParameterCount) | |
725 ..add(optionalParameterCount) | |
726 ..add(memberTypeExpression) | |
727 ..addAll(task.metadataEmitter.reifyDefaultArguments(member)); | |
728 | |
729 if (canBeReflected || compiler.enabledFunctionApply) { | |
730 parameters.orderedForEachParameter((Element parameter) { | |
731 expressions.add(task.metadataEmitter.reifyName(parameter.name)); | |
732 }); | |
733 } | |
734 if (canBeReflected) { | |
735 jsAst.LiteralString reflectionName; | |
736 if (member.isConstructor()) { | |
737 String reflectionNameString = task.getReflectionName(member, name); | |
738 reflectionName = | |
739 new jsAst.LiteralString( | |
740 '"new ${Elements.reconstructConstructorName(member)}" /* $reflec tionNameString */'); | |
741 } else { | |
742 reflectionName = js.string(member.name); | |
743 } | |
744 expressions | |
745 ..add(reflectionName) | |
746 ..addAll(task.metadataEmitter.computeMetadata(member)); | |
747 } else if (isClosure && compiler.enabledFunctionApply) { | |
748 expressions.add(js.string(member.name)); | |
749 } | |
750 | |
751 builder.addProperty(name, js.toExpression(expressions)); | |
752 | |
753 // if (canTearOff) { | |
754 // emitDynamicFunctionGetter(member, builder.addProperty); | |
755 // } | |
622 } | 756 } |
623 | 757 |
624 void addMemberField(VariableElement member, ClassBuilder builder) { | 758 void addMemberField(VariableElement member, ClassBuilder builder) { |
625 // For now, do nothing. | 759 // For now, do nothing. |
626 } | 760 } |
627 } | 761 } |
OLD | NEW |