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 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
74 | 74 |
75 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | 75 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; |
76 // Includes extra receiver argument when using interceptor convention | 76 // Includes extra receiver argument when using interceptor convention |
77 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; | 77 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; |
78 | 78 |
79 TreeElements elements = | 79 TreeElements elements = |
80 compiler.enqueuer.resolution.getCachedElements(member); | 80 compiler.enqueuer.resolution.getCachedElements(member); |
81 | 81 |
82 int parameterIndex = 0; | 82 int parameterIndex = 0; |
83 parameters.orderedForEachParameter((Element element) { | 83 parameters.orderedForEachParameter((Element element) { |
84 // Use generic names for closures to facilitate code sharing. | 84 String jsName = backend.namer.safeName(element.name); |
85 String jsName = member is ClosureInvocationElement | |
86 ? 'p${parameterIndex++}' | |
87 : backend.namer.safeName(element.name); | |
88 assert(jsName != receiverArgumentName); | 85 assert(jsName != receiverArgumentName); |
89 if (count < optionalParameterStart) { | 86 if (count < optionalParameterStart) { |
90 parametersBuffer[count] = new jsAst.Parameter(jsName); | 87 parametersBuffer[count] = new jsAst.Parameter(jsName); |
91 argumentsBuffer[count] = js(jsName); | 88 argumentsBuffer[count] = js(jsName); |
92 } else { | 89 } else { |
93 int index = names.indexOf(element.name); | 90 int index = names.indexOf(element.name); |
94 if (index != -1) { | 91 if (index != -1) { |
95 indexOfLastOptionalArgumentInParameters = count; | 92 indexOfLastOptionalArgumentInParameters = count; |
96 // The order of the named arguments is not the same as the | 93 // The order of the named arguments is not the same as the |
97 // one in the real method (which is in Dart source order). | 94 // one in the real method (which is in Dart source order). |
(...skipping 16 matching lines...) Expand all Loading... | |
114 } | 111 } |
115 count++; | 112 count++; |
116 }); | 113 }); |
117 | 114 |
118 List body; | 115 List body; |
119 if (member.hasFixedBackendName()) { | 116 if (member.hasFixedBackendName()) { |
120 body = task.nativeEmitter.generateParameterStubStatements( | 117 body = task.nativeEmitter.generateParameterStubStatements( |
121 member, isInterceptedMethod, invocationName, | 118 member, isInterceptedMethod, invocationName, |
122 parametersBuffer, argumentsBuffer, | 119 parametersBuffer, argumentsBuffer, |
123 indexOfLastOptionalArgumentInParameters); | 120 indexOfLastOptionalArgumentInParameters); |
124 } else { | 121 } else if (member.isInstanceMember()) { |
125 body = [js.return_( | 122 body = [js.return_( |
126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; | 123 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; |
124 } else { | |
125 body = [js.return_(namer.elementAccess(member)(argumentsBuffer))]; | |
127 } | 126 } |
128 | 127 |
129 jsAst.Fun function = js.fun(parametersBuffer, body); | 128 jsAst.Fun function = js.fun(parametersBuffer, body); |
130 | 129 |
131 defineStub(invocationName, function); | 130 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 } | 131 } |
140 | 132 |
141 void addParameterStubs(FunctionElement member, | 133 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, |
142 DefineStubFunction defineStub) { | 134 [bool canTearOff = false]) { |
135 if (member.enclosingElement.isClosure()) { | |
136 ClosureClassElement cls = member.enclosingElement; | |
137 if (cls.supertype.element == compiler.boundClosureClass) { | |
138 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure1.'); | |
ngeoffray
2013/12/09 11:15:58
Maybe add a better error message.
ahe
2013/12/09 17:06:47
Johnni said the same, so I'm going to repeat my an
ngeoffray
2013/12/09 17:14:55
BoundClosure1 may be enough for you, but besides t
| |
139 } | |
140 if (cls.methodElement.isInstanceMember()) { | |
141 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure2.'); | |
ngeoffray
2013/12/09 11:15:58
Ditto.
ahe
2013/12/09 17:06:47
Ditto :-)
| |
142 } | |
143 } | |
144 | |
143 // We fill the lists depending on the selector. For example, | 145 // We fill the lists depending on the selector. For example, |
144 // take method foo: | 146 // take method foo: |
145 // foo(a, b, {c, d}); | 147 // foo(a, b, {c, d}); |
146 // | 148 // |
147 // We may have multiple ways of calling foo: | 149 // We may have multiple ways of calling foo: |
148 // (1) foo(1, 2); | 150 // (1) foo(1, 2); |
149 // (2) foo(1, 2, c: 3); | 151 // (2) foo(1, 2, c: 3); |
150 // (3) foo(1, 2, d: 4); | 152 // (3) foo(1, 2, d: 4); |
151 // (4) foo(1, 2, c: 3, d: 4); | 153 // (4) foo(1, 2, c: 3, d: 4); |
152 // (5) foo(1, 2, d: 4, c: 3); | 154 // (5) foo(1, 2, d: 4, c: 3); |
153 // | 155 // |
154 // What we generate at the call sites are: | 156 // What we generate at the call sites are: |
155 // (1) foo$2(1, 2); | 157 // (1) foo$2(1, 2); |
156 // (2) foo$3$c(1, 2, 3); | 158 // (2) foo$3$c(1, 2, 3); |
157 // (3) foo$3$d(1, 2, 4); | 159 // (3) foo$3$d(1, 2, 4); |
158 // (4) foo$4$c$d(1, 2, 3, 4); | 160 // (4) foo$4$c$d(1, 2, 3, 4); |
159 // (5) foo$4$c$d(1, 2, 3, 4); | 161 // (5) foo$4$c$d(1, 2, 3, 4); |
160 // | 162 // |
161 // The stubs we generate are (expressed in Dart): | 163 // The stubs we generate are (expressed in Dart): |
162 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) | 164 // (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); | 165 // (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); | 166 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); |
165 // (4) No stub generated, call is direct. | 167 // (4) No stub generated, call is direct. |
166 // (5) No stub generated, call is direct. | 168 // (5) No stub generated, call is direct. |
167 | 169 |
168 // Keep a cache of which stubs have already been generated, to | 170 Set<Selector> selectors = member.isInstanceMember() |
ngeoffray
2013/12/09 11:15:58
For readability, I would not initialize this set h
ahe
2013/12/09 17:06:47
Done.
| |
169 // avoid duplicates. Note that even if selectors are | 171 ? compiler.codegenWorld.invokedNames[member.name] |
170 // canonicalized, we would still need this cache: a typed selector | 172 : null; // No stubs needed for static methods. |
171 // on A and a typed selector on B could yield the same stub. | 173 |
172 Set<String> generatedStubNames = new Set<String>(); | 174 /// Returns all closure call selectors renamed to match this member. |
173 bool isClosureInvocation = | 175 Set<Selector> callSelectorsAsNamed() { |
174 member.name == namer.closureInvocationSelectorName; | 176 if (!canTearOff) return null; |
175 if (backend.isNeededForReflection(member) || | 177 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ |
176 (compiler.enabledFunctionApply && isClosureInvocation)) { | 178 namer.closureInvocationSelectorName]; |
177 // If [Function.apply] is called, we pessimistically compile all | 179 if (callSelectors == null) return null; |
178 // possible stubs for this closure. | 180 return callSelectors.map((Selector callSelector) { |
179 FunctionSignature signature = member.computeSignature(compiler); | 181 return new Selector.call( |
180 Set<Selector> selectors = signature.optionalParametersAreNamed | 182 member.name, member.getLibrary(), |
181 ? computeSeenNamedSelectors(member) | 183 callSelector.argumentCount, callSelector.namedArguments); |
182 : computeOptionalSelectors(signature, member); | 184 }).toSet(); |
183 for (Selector selector in selectors) { | 185 } |
184 addParameterStub(member, selector, defineStub, generatedStubNames); | 186 if (selectors == null) { |
185 } | 187 selectors = callSelectorsAsNamed(); |
186 if (signature.optionalParametersAreNamed && isClosureInvocation) { | 188 if (selectors == null) return; |
187 addCatchAllParameterStub(member, signature, defineStub); | |
188 } | |
189 } else { | 189 } else { |
190 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 190 Set<Selector> callSelectors = callSelectorsAsNamed(); |
191 if (selectors == null) return; | 191 if (callSelectors != null) { |
192 for (Selector selector in selectors) { | 192 selectors = selectors.union(callSelectors); |
193 if (!selector.applies(member, compiler)) continue; | |
194 addParameterStub(member, selector, defineStub, generatedStubNames); | |
195 } | 193 } |
196 } | 194 } |
197 } | 195 Set<Selector> untypedSelectors = new Set<Selector>(); |
198 | 196 if (selectors != null) { |
199 Set<Selector> computeSeenNamedSelectors(FunctionElement element) { | 197 for (Selector selector in selectors) { |
200 Set<Selector> selectors = compiler.codegenWorld.invokedNames[element.name]; | 198 if (!selector.appliesUnnamed(member, compiler)) continue; |
201 Set<Selector> result = new Set<Selector>(); | 199 if (untypedSelectors.add(selector.asUntyped)) { |
202 if (selectors == null) return result; | 200 // TODO(ahe): Is the last argument to [addParameterStub] needed? |
203 for (Selector selector in selectors) { | 201 addParameterStub(member, selector, defineStub, new Set<String>()); |
204 if (!selector.applies(element, compiler)) continue; | 202 } |
205 result.add(selector); | 203 } |
206 } | 204 } |
207 return result; | 205 if (canTearOff) { |
208 } | 206 selectors = compiler.codegenWorld.invokedNames[ |
209 | 207 namer.closureInvocationSelectorName]; |
210 void addCatchAllParameterStub(FunctionElement member, | 208 if (selectors != null) { |
211 FunctionSignature signature, | 209 for (Selector selector in selectors) { |
212 DefineStubFunction defineStub) { | 210 selector = new Selector.call( |
213 // See Primities.applyFunction in js_helper.dart for details. | 211 member.name, member.getLibrary(), |
214 List<jsAst.Property> properties = <jsAst.Property>[]; | 212 selector.argumentCount, selector.namedArguments); |
215 for (Element element in signature.orderedOptionalParameters) { | 213 if (!selector.appliesUnnamed(member, compiler)) continue; |
216 String jsName = backend.namer.safeName(element.name); | 214 if (untypedSelectors.add(selector)) { |
217 Constant value = compiler.constantHandler.initialVariableValues[element]; | 215 // TODO(ahe): Is the last argument to [addParameterStub] needed? |
218 jsAst.Expression reference = null; | 216 addParameterStub(member, selector, defineStub, new Set<String>()); |
219 if (value == null) { | 217 } |
220 reference = new jsAst.LiteralNull(); | 218 } |
221 } else { | |
222 reference = task.constantReference(value); | |
223 } | 219 } |
224 properties.add(new jsAst.Property(js.string(jsName), reference)); | |
225 } | 220 } |
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) { | |
258 task.addComment('Static function getters', task.mainBuffer); | |
259 for (FunctionElement element in | |
260 Elements.sortedByPosition(staticGetters.keys)) { | |
261 Element closure = staticGetters[element]; | |
262 CodeBuffer buffer = | |
263 task.isDeferred(element) ? task.deferredConstants : eagerBuffer; | |
264 String closureClass = namer.isolateAccess(closure); | |
265 String name = namer.getStaticClosureName(element); | |
266 | |
267 String closureName = namer.getStaticClosureName(element); | |
268 jsAst.Node assignment = js( | |
269 'init.globalFunctions["$closureName"] =' | |
270 ' ${namer.globalObjectFor(element)}.$name =' | |
271 ' new $closureClass(#, "$closureName")', | |
272 namer.elementAccess(element)); | |
273 buffer.write(jsAst.prettyPrint(assignment, compiler)); | |
274 buffer.write('$N'); | |
275 } | |
276 } | |
277 | |
278 void emitStaticFunctionClosures() { | |
279 Set<FunctionElement> functionsNeedingGetter = | |
280 compiler.codegenWorld.staticFunctionsNeedingGetter; | |
281 for (FunctionElement element in | |
282 Elements.sortedByPosition(functionsNeedingGetter)) { | |
283 String superName = namer.getNameOfClass(compiler.closureClass); | |
284 int parameterCount = element.functionSignature.parameterCount; | |
285 String name = 'Closure\$$parameterCount'; | |
286 assert(task.instantiatedClasses.contains(compiler.closureClass)); | |
287 | |
288 ClassElement closureClassElement = new ClosureClassElement( | |
289 null, name, compiler, element, | |
290 element.getCompilationUnit()); | |
291 // Now add the methods on the closure class. The instance method does not | |
292 // have the correct name. Since [addParameterStubs] use the name to create | |
293 // its stubs we simply create a fake element with the correct name. | |
294 // Note: the callElement will not have any enclosingElement. | |
295 FunctionElement callElement = | |
296 new ClosureInvocationElement(namer.closureInvocationSelectorName, | |
297 element); | |
298 | |
299 String invocationName = namer.instanceMethodName(callElement); | |
300 String mangledName = namer.getNameOfClass(closureClassElement); | |
301 | |
302 // Define the constructor with a name so that Object.toString can | |
303 // find the class name of the closure class. | |
304 ClassBuilder closureBuilder = new ClassBuilder(); | |
305 // If a static function is used as a closure we need to add its name | |
306 // in case it is used in spawnFunction. | |
307 String methodName = namer.STATIC_CLOSURE_NAME_NAME; | |
308 List<String> fieldNames = <String>[invocationName, methodName]; | |
309 closureBuilder.addProperty('', | |
310 js.string("$superName;${fieldNames.join(',')}")); | |
311 | |
312 addParameterStubs(callElement, closureBuilder.addProperty); | |
313 | |
314 void emitFunctionTypeSignature(Element method, FunctionType methodType) { | |
315 RuntimeTypes rti = backend.rti; | |
316 // [:() => null:] is dummy encoding of [this] which is never needed for | |
317 // the encoding of the type of the static [method]. | |
318 jsAst.Expression encoding = | |
319 rti.getSignatureEncoding(methodType, js('null')); | |
320 String operatorSignature = namer.operatorSignature(); | |
321 // TODO(johnniwinther): Make MiniJsParser support function expressions. | |
322 closureBuilder.addProperty(operatorSignature, encoding); | |
323 } | |
324 | |
325 FunctionType methodType = element.computeType(compiler); | |
326 Map<FunctionType, bool> functionTypeChecks = | |
327 task.typeTestEmitter.getFunctionTypeChecksOn(methodType); | |
328 task.typeTestEmitter.generateFunctionTypeTests( | |
329 element, methodType, functionTypeChecks, | |
330 emitFunctionTypeSignature); | |
331 | |
332 closureClassElement = | |
333 addClosureIfNew(closureBuilder, closureClassElement, fieldNames); | |
334 staticGetters[element] = closureClassElement; | |
335 | |
336 } | |
337 } | |
338 | |
339 ClassElement addClosureIfNew(ClassBuilder builder, | |
340 ClassElement closure, | |
341 List<String> fieldNames) { | |
342 String key = | |
343 jsAst.prettyPrint(builder.toObjectInitializer(), compiler).getText(); | |
344 return methodClosures.putIfAbsent(key, () { | |
345 String mangledName = namer.getNameOfClass(closure); | |
346 emitClosureInPrecompiledFunction(mangledName, fieldNames); | |
347 return closure; | |
348 }); | |
349 } | |
350 | |
351 void emitClosureInPrecompiledFunction(String mangledName, | |
352 List<String> fieldNames) { | |
353 List<String> fields = fieldNames; | |
354 String constructorName = mangledName; | |
355 task.precompiledFunction.add(new jsAst.FunctionDeclaration( | |
356 new jsAst.VariableDeclaration(constructorName), | |
357 js.fun(fields, fields.map( | |
358 (name) => js('this.$name = $name')).toList()))); | |
359 task.precompiledFunction.addAll([ | |
360 js('$constructorName.builtin\$cls = "$constructorName"'), | |
361 js('\$desc=\$collectedClasses.$constructorName'), | |
362 js.if_('\$desc instanceof Array', js('\$desc = \$desc[1]')), | |
363 js('$constructorName.prototype = \$desc'), | |
364 ]); | |
365 | |
366 task.precompiledConstructorNames.add(js(constructorName)); | |
367 } | 221 } |
368 | 222 |
369 /** | 223 /** |
370 * Documentation wanted -- johnniwinther | 224 * Documentation wanted -- johnniwinther |
371 * | 225 * |
372 * Invariant: [member] must be a declaration element. | |
373 */ | |
374 void emitDynamicFunctionGetter(FunctionElement member, | |
375 DefineStubFunction defineStub) { | |
376 assert(invariant(member, member.isDeclaration)); | |
377 assert(task.instantiatedClasses.contains(compiler.boundClosureClass)); | |
378 // 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 | |
380 // '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): | |
382 // | |
383 // class A { | |
384 // foo(x, y, z) { ... } // Original function. | |
385 // get foo { return new BoundClosure499(this, "foo"); } | |
386 // } | |
387 // class BoundClosure499 extends BoundClosure { | |
388 // BoundClosure499(this.self, this.name); | |
389 // $call3(x, y, z) { return self[name](x, y, z); } | |
390 // } | |
391 | |
392 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; | |
393 int parameterCount = member.parameterCount(compiler); | |
394 | |
395 // Intercepted methods take an extra parameter, which is the | |
396 // receiver of the call. | |
397 bool inInterceptor = backend.isInterceptedMethod(member); | |
398 List<String> fieldNames = <String>[]; | |
399 compiler.boundClosureClass.forEachInstanceField((_, Element field) { | |
400 fieldNames.add(namer.instanceFieldPropertyName(field)); | |
401 }); | |
402 | |
403 ClassElement classElement = member.getEnclosingClass(); | |
404 String name = inInterceptor | |
405 ? 'BoundClosure\$i${parameterCount}' | |
406 : 'BoundClosure\$${parameterCount}'; | |
407 | |
408 ClassElement closureClassElement = new ClosureClassElement( | |
409 null, name, compiler, member, | |
410 member.getCompilationUnit()); | |
411 String superName = namer.getNameOfClass(closureClassElement.superclass); | |
412 | |
413 // Define the constructor with a name so that Object.toString can | |
414 // find the class name of the closure class. | |
415 ClassBuilder boundClosureBuilder = new ClassBuilder(); | |
416 boundClosureBuilder.addProperty('', | |
417 js.string("$superName;${fieldNames.join(',')}")); | |
418 // Now add the methods on the closure class. The instance method does not | |
419 // have the correct name. Since [addParameterStubs] use the name to create | |
420 // its stubs we simply create a fake element with the correct name. | |
421 // Note: the callElement will not have any enclosingElement. | |
422 FunctionElement callElement = new ClosureInvocationElement( | |
423 namer.closureInvocationSelectorName, member); | |
424 | |
425 String invocationName = namer.instanceMethodName(callElement); | |
426 | |
427 List<String> parameters = <String>[]; | |
428 List<jsAst.Expression> arguments = | |
429 <jsAst.Expression>[js('this')[fieldNames[0]]]; | |
430 if (inInterceptor) { | |
431 arguments.add(js('this')[fieldNames[2]]); | |
432 } | |
433 for (int i = 0; i < parameterCount; i++) { | |
434 String name = 'p$i'; | |
435 parameters.add(name); | |
436 arguments.add(js(name)); | |
437 } | |
438 | |
439 jsAst.Expression fun = js.fun( | |
440 parameters, | |
441 js.return_( | |
442 js('this')[fieldNames[1]]['call'](arguments))); | |
443 boundClosureBuilder.addProperty(invocationName, fun); | |
444 | |
445 addParameterStubs(callElement, boundClosureBuilder.addProperty); | |
446 | |
447 void emitFunctionTypeSignature(Element method, FunctionType methodType) { | |
448 jsAst.Expression encoding = backend.rti.getSignatureEncoding( | |
449 methodType, js('this')[fieldNames[0]]); | |
450 String operatorSignature = namer.operatorSignature(); | |
451 boundClosureBuilder.addProperty(operatorSignature, encoding); | |
452 } | |
453 | |
454 DartType memberType = member.computeType(compiler); | |
455 Map<FunctionType, bool> functionTypeChecks = | |
456 task.typeTestEmitter.getFunctionTypeChecksOn(memberType); | |
457 | |
458 task.typeTestEmitter.generateFunctionTypeTests( | |
459 member, memberType, functionTypeChecks, | |
460 emitFunctionTypeSignature); | |
461 | |
462 closureClassElement = | |
463 addClosureIfNew(boundClosureBuilder, closureClassElement, fieldNames); | |
464 | |
465 String closureClass = namer.isolateAccess(closureClassElement); | |
466 | |
467 // And finally the getter. | |
468 String getterName = namer.getterName(member); | |
469 String targetName = namer.instanceMethodName(member); | |
470 | |
471 parameters = <String>[]; | |
472 jsAst.PropertyAccess method = | |
473 backend.namer.elementAccess(classElement)['prototype'][targetName]; | |
474 arguments = <jsAst.Expression>[js('this'), method]; | |
475 | |
476 if (inInterceptor) { | |
477 String receiverArg = fieldNames[2]; | |
478 parameters.add(receiverArg); | |
479 arguments.add(js(receiverArg)); | |
480 } else { | |
481 // Put null in the intercepted receiver field. | |
482 arguments.add(new jsAst.LiteralNull()); | |
483 } | |
484 | |
485 arguments.add(js.string(targetName)); | |
486 | |
487 jsAst.Expression getterFunction = js.fun( | |
488 parameters, js.return_(js(closureClass).newWith(arguments))); | |
489 | |
490 defineStub(getterName, getterFunction); | |
491 } | |
492 | |
493 /** | |
494 * Documentation wanted -- johnniwinther | |
495 * | |
496 * Invariant: [member] must be a declaration element. | 226 * Invariant: [member] must be a declaration element. |
497 */ | 227 */ |
498 void emitCallStubForGetter(Element member, | 228 void emitCallStubForGetter(Element member, |
499 Set<Selector> selectors, | 229 Set<Selector> selectors, |
500 DefineStubFunction defineStub) { | 230 AddPropertyFunction addProperty) { |
501 assert(invariant(member, member.isDeclaration)); | 231 assert(invariant(member, member.isDeclaration)); |
502 LibraryElement memberLibrary = member.getLibrary(); | 232 LibraryElement memberLibrary = member.getLibrary(); |
503 // If the method is intercepted, the stub gets the | 233 // If the method is intercepted, the stub gets the |
504 // receiver explicitely and we need to pass it to the getter call. | 234 // receiver explicitely and we need to pass it to the getter call. |
505 bool isInterceptedMethod = backend.isInterceptedMethod(member); | 235 bool isInterceptedMethod = backend.isInterceptedMethod(member); |
506 | 236 |
507 const String receiverArgumentName = r'$receiver'; | 237 const String receiverArgumentName = r'$receiver'; |
508 | 238 |
509 jsAst.Expression buildGetter() { | 239 jsAst.Expression buildGetter() { |
510 if (member.isGetter()) { | 240 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++) { | 272 for (int i = 0; i < selector.argumentCount; i++) { |
543 String name = 'arg$i'; | 273 String name = 'arg$i'; |
544 parameters.add(new jsAst.Parameter(name)); | 274 parameters.add(new jsAst.Parameter(name)); |
545 arguments.add(js(name)); | 275 arguments.add(js(name)); |
546 } | 276 } |
547 | 277 |
548 jsAst.Fun function = js.fun( | 278 jsAst.Fun function = js.fun( |
549 parameters, | 279 parameters, |
550 js.return_(buildGetter()[closureCallName](arguments))); | 280 js.return_(buildGetter()[closureCallName](arguments))); |
551 | 281 |
552 defineStub(invocationName, function); | 282 addProperty(invocationName, function); |
553 } | 283 } |
554 } | 284 } |
555 } | 285 } |
556 | 286 |
557 /** | 287 /** |
558 * Documentation wanted -- johnniwinther | 288 * Documentation wanted -- johnniwinther |
559 * | 289 * |
560 * Invariant: [member] must be a declaration element. | 290 * Invariant: [member] must be a declaration element. |
561 */ | 291 */ |
562 void emitExtraAccessors(Element member, ClassBuilder builder) { | 292 void emitExtraAccessors(Element member, ClassBuilder builder) { |
563 assert(invariant(member, member.isDeclaration)); | 293 assert(invariant(member, member.isDeclaration)); |
564 if (member.isGetter() || member.isField()) { | 294 if (member.isGetter() || member.isField()) { |
565 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 295 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; |
566 if (selectors != null && !selectors.isEmpty) { | 296 if (selectors != null && !selectors.isEmpty) { |
567 emitCallStubForGetter(member, selectors, builder.addProperty); | 297 emitCallStubForGetter(member, selectors, builder.addProperty); |
568 } | 298 } |
569 } else if (member.isFunction()) { | |
570 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { | |
571 emitDynamicFunctionGetter(member, builder.addProperty); | |
572 } | |
573 } | 299 } |
574 } | 300 } |
575 | 301 |
576 void addMember(Element member, ClassBuilder builder) { | 302 void addMember(Element member, ClassBuilder builder) { |
577 assert(invariant(member, member.isDeclaration)); | 303 assert(invariant(member, member.isDeclaration)); |
578 | 304 |
579 if (member.isField()) { | 305 if (member.isField()) { |
580 addMemberField(member, builder); | 306 addMemberField(member, builder); |
581 } else if (member.isFunction() || | 307 } else if (member.isFunction() || |
582 member.isGenerativeConstructorBody() || | 308 member.isGenerativeConstructorBody() || |
583 member.isGenerativeConstructor() || | 309 member.isGenerativeConstructor() || |
584 member.isAccessor()) { | 310 member.isAccessor()) { |
585 addMemberMethod(member, builder); | 311 addMemberMethod(member, builder); |
586 } else { | 312 } else { |
587 compiler.internalErrorOnElement( | 313 compiler.internalErrorOnElement( |
588 member, 'unexpected kind: "${member.kind}"'); | 314 member, 'unexpected kind: "${member.kind}"'); |
589 } | 315 } |
590 if (member.isInstanceMember()) emitExtraAccessors(member, builder); | 316 if (member.isInstanceMember()) emitExtraAccessors(member, builder); |
591 } | 317 } |
592 | 318 |
593 void addMemberMethod(FunctionElement member, ClassBuilder builder) { | 319 void addMemberMethod(FunctionElement member, ClassBuilder builder) { |
594 if (member.isAbstract) return; | 320 if (member.isAbstract) return; |
595 jsAst.Expression code = backend.generatedCode[member]; | 321 jsAst.Expression code = backend.generatedCode[member]; |
596 if (code == null) return; | 322 if (code == null) return; |
597 String name = namer.getNameOfMember(member); | 323 String name = namer.getNameOfMember(member); |
598 if (backend.isInterceptedMethod(member)) { | 324 task.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); |
599 task.interceptorEmitter.interceptorInvocationNames.add(name); | 325 FunctionSignature parameters = member.computeSignature(compiler); |
326 bool needsStubs = !parameters.optionalParameters.isEmpty; | |
327 bool canTearOff = false; | |
328 bool isClosure = false; | |
329 String tearOffName; | |
330 if (!member.isFunction() || member.isConstructor() || member.isAccessor()) { | |
331 canTearOff = false; | |
332 } else if (member.isInstanceMember()) { | |
333 if (member.getEnclosingClass().isClosure()) { | |
334 canTearOff = false; | |
335 isClosure = true; | |
336 } else { | |
337 // Careful with operators. | |
ngeoffray
2013/12/09 11:15:58
I don't understand this comment.
ahe
2013/12/09 17:06:47
Changed it to:
// TODO(ahe): What happens
| |
338 canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler); | |
339 tearOffName = namer.getterName(member); | |
340 } | |
341 } else { | |
342 canTearOff = | |
343 compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member); | |
344 tearOffName = namer.getStaticClosureName(member); | |
600 } | 345 } |
601 code = task.metadataEmitter.extendWithMetadata(member, code); | 346 |
602 builder.addProperty(name, code); | 347 bool canBeReflected = backend.isAccessibleByReflection(member); |
603 String reflectionName = task.getReflectionName(member, name); | 348 bool needStructuredInfo = |
604 if (reflectionName != null) { | 349 canTearOff || canBeReflected || compiler.enabledFunctionApply; |
605 var reflectable = | 350 if (!needStructuredInfo) { |
606 js(backend.isAccessibleByReflection(member) ? '1' : '0'); | 351 builder.addProperty(name, code); |
607 builder.addProperty('+$reflectionName', reflectable); | 352 if (needsStubs) { |
608 jsAst.Node defaultValues = | 353 addParameterStubs( |
609 task.metadataEmitter.reifyDefaultArguments(member); | 354 member, |
610 if (defaultValues != null) { | 355 (Selector selector, jsAst.Fun function) { |
611 String unmangledName = member.name; | 356 builder.addProperty(namer.invocationName(selector), function); |
612 builder.addProperty('*$unmangledName', defaultValues); | 357 }); |
613 } | 358 } |
359 return; | |
614 } | 360 } |
615 if (member.isInstanceMember()) { | 361 |
616 // TODO(ahe): Where is this done for static/top-level methods? | 362 if (canTearOff) { |
617 FunctionSignature parameters = member.computeSignature(compiler); | 363 assert(invariant(member, !member.isGenerativeConstructor())); |
618 if (!parameters.optionalParameters.isEmpty) { | 364 assert(invariant(member, !member.isGenerativeConstructorBody())); |
619 addParameterStubs(member, builder.addProperty); | 365 assert(invariant(member, !member.isConstructor())); |
366 } | |
367 | |
368 // This element is needed for reflection or needs additional stubs. So we | |
369 // need to retain additional information. | |
370 | |
371 // The information is stored in an array with this format: | |
372 // | |
373 // 1. The JS function for this member. | |
374 // 2. First stub. | |
375 // 3. Name of first stub. | |
376 // ... | |
377 // M. Call name of this member. | |
378 // M+1. Call name of first stub. | |
379 // ... | |
380 // N. Getter name for tearOff. | |
381 // N+1. (Required parameter count << 1) + (member.isAccessor() ? 1 : 0). | |
382 // N+2. (Optional parameter count << 1) + | |
383 // (parameters.optionalParametersAreNamed ? 1 : 0). | |
384 // N+3. Index to function type in constant pool. | |
385 // N+4. First default argument. | |
386 // ... | |
387 // O. First parameter name (if needed for reflection or Function.apply). | |
388 // ... | |
389 // P. Unmangled name (if reflectable). | |
390 // P+1. First metadata (if reflectable). | |
391 // ... | |
392 | |
393 List expressions = []; | |
394 | |
395 String callSelectorString = 'null'; | |
396 if (member.isFunction()) { | |
397 Selector callSelector = | |
398 new Selector.fromElement(member, compiler).toCallSelector(); | |
399 callSelectorString = '"${namer.invocationName(callSelector)}"'; | |
400 } | |
401 | |
402 // On [requiredParameterCount], the lower bit is set if this method can be | |
403 // called reflectively. | |
404 int requiredParameterCount = parameters.requiredParameterCount << 1; | |
405 if (member.isAccessor()) requiredParameterCount++; | |
406 | |
407 int optionalParameterCount = parameters.optionalParameterCount << 1; | |
408 if (parameters.optionalParametersAreNamed) optionalParameterCount++; | |
409 | |
410 expressions.add(code); | |
411 | |
412 // TODO(ahe): Remove comments from output. | |
413 List tearOffInfo = | |
414 [new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')]; | |
415 | |
416 if (needsStubs || canTearOff) { | |
417 addParameterStubs(member, (Selector selector, jsAst.Fun function) { | |
418 expressions.add(function); | |
419 if (member.isInstanceMember()) { | |
420 Set invokedSelectors = | |
421 compiler.codegenWorld.invokedNames[member.name]; | |
422 if (invokedSelectors != null && invokedSelectors.contains(selector)) { | |
423 expressions.add(js.string(namer.invocationName(selector))); | |
424 } else { | |
425 // Don't add a stub for calling this as a regular instance method, | |
426 // we only need the "call" stub for implicit closures of this | |
427 // method. | |
428 expressions.add("null"); | |
429 } | |
430 } else { | |
431 // Static methods don't need "named" stubs as the default arguments | |
432 // are inlined at call sites. But static methods might need "call" | |
433 // stubs for implicit closures. | |
434 expressions.add("null"); | |
435 // TOOD(ahe): Since we know when reading static data versus instance | |
436 // data, we can eliminate this element. | |
ngeoffray
2013/12/09 11:15:58
What does eliminate mean here?
ahe
2013/12/09 17:06:47
That it doesn't need to be added to the reflective
| |
437 } | |
438 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | |
439 namer.closureInvocationSelectorName]; | |
440 Selector callSelector = selector.toCallSelector(); | |
441 String callSelectorString = 'null'; | |
442 if (canTearOff && callSelectors != null && | |
443 callSelectors.contains(callSelector)) { | |
444 callSelectorString = '"${namer.invocationName(callSelector)}"'; | |
445 } | |
446 tearOffInfo.add( | |
447 new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')); | |
448 }, canTearOff); | |
449 } | |
450 | |
451 jsAst.Expression memberTypeExpression; | |
452 if ((canTearOff || canBeReflected) && | |
453 !member.isGenerativeConstructorBody()) { | |
ngeoffray
2013/12/09 11:15:58
A generative constructor body can be reflected?
ahe
2013/12/09 17:06:47
Yeah. It is kind of a hack, but these end up being
| |
454 DartType memberType = member.computeType(compiler); | |
455 if (memberType.containsTypeVariables) { | |
456 jsAst.Expression thisAccess = js(r'this.$receiver'); | |
457 memberTypeExpression = | |
458 backend.rti.getSignatureEncoding(memberType, thisAccess); | |
459 } else { | |
460 memberTypeExpression = | |
461 js.toExpression(task.metadataEmitter.reifyType(memberType)); | |
620 } | 462 } |
463 } else { | |
464 memberTypeExpression = js('null'); | |
621 } | 465 } |
466 | |
467 expressions | |
468 ..addAll(tearOffInfo) | |
469 ..add((tearOffName == null || member.isAccessor()) | |
470 ? js("null") : js.string(tearOffName)) | |
471 ..add(requiredParameterCount) | |
472 ..add(optionalParameterCount) | |
473 ..add(memberTypeExpression) | |
474 ..addAll(task.metadataEmitter.reifyDefaultArguments(member)); | |
475 | |
476 if (canBeReflected || compiler.enabledFunctionApply) { | |
477 parameters.orderedForEachParameter((Element parameter) { | |
478 expressions.add(task.metadataEmitter.reifyName(parameter.name)); | |
479 }); | |
480 } | |
481 if (canBeReflected) { | |
482 jsAst.LiteralString reflectionName; | |
483 if (member.isConstructor()) { | |
484 String reflectionNameString = task.getReflectionName(member, name); | |
485 reflectionName = | |
486 new jsAst.LiteralString( | |
487 '"new ${Elements.reconstructConstructorName(member)}"' | |
488 ' /* $reflectionNameString */'); | |
489 } else { | |
490 reflectionName = js.string(member.name); | |
491 } | |
492 expressions | |
493 ..add(reflectionName) | |
494 ..addAll(task.metadataEmitter.computeMetadata(member)); | |
495 } else if (isClosure && compiler.enabledFunctionApply) { | |
496 expressions.add(js.string(member.name)); | |
497 } | |
498 | |
499 builder.addProperty(name, js.toExpression(expressions)); | |
622 } | 500 } |
623 | 501 |
624 void addMemberField(VariableElement member, ClassBuilder builder) { | 502 void addMemberField(VariableElement member, ClassBuilder builder) { |
625 // For now, do nothing. | 503 // For now, do nothing. |
626 } | 504 } |
627 } | 505 } |
OLD | NEW |