OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 /** | |
6 * A function element that represents a closure call. The signature is copied | |
7 * from the given element. | |
8 */ | |
9 class ClosureInvocationElement extends FunctionElement { | |
10 ClosureInvocationElement(SourceString name, | |
11 FunctionElement other) | |
12 : super.from(name, other, other.enclosingElement); | |
13 | |
14 isInstanceMember() => true; | |
15 } | |
16 | |
17 /** | |
18 * Generates the code for all used classes in the program. Static fields (even | |
19 * in classes) are ignored, since they can be treated as non-class elements. | |
20 * | |
21 * The code for the containing (used) methods must exist in the [:universe:]. | |
22 */ | |
23 class CodeEmitterTask extends CompilerTask { | |
24 static final String INHERIT_FUNCTION = ''' | |
25 function(child, parent) { | |
26 if (child.prototype.__proto__) { | |
27 child.prototype.__proto__ = parent.prototype; | |
28 } else { | |
29 function tmp() {}; | |
30 tmp.prototype = parent.prototype; | |
31 child.prototype = new tmp(); | |
32 child.prototype.constructor = child; | |
33 } | |
34 }'''; | |
35 | |
36 bool addedInheritFunction = false; | |
37 final Namer namer; | |
38 final NativeEmitter nativeEmitter; | |
39 Set<ClassElement> generatedClasses; | |
40 StringBuffer mainBuffer; | |
41 | |
42 CodeEmitterTask(Compiler compiler) | |
43 : namer = compiler.namer, | |
44 nativeEmitter = new NativeEmitter(compiler), | |
45 generatedClasses = new Set<ClassElement>(), | |
46 mainBuffer = new StringBuffer(), | |
47 super(compiler); | |
48 | |
49 String get name() => 'CodeEmitter'; | |
50 | |
51 String get inheritsName() => '${namer.ISOLATE}.\$inherits'; | |
52 | |
53 String get objectClassName() { | |
54 ClassElement objectClass = | |
55 compiler.coreLibrary.find(const SourceString('Object')); | |
56 return namer.isolatePropertyAccess(objectClass); | |
57 } | |
58 | |
59 void addInheritFunctionIfNecessary() { | |
60 if (addedInheritFunction) return; | |
61 addedInheritFunction = true; | |
62 mainBuffer.add('$inheritsName = '); | |
63 mainBuffer.add(INHERIT_FUNCTION); | |
64 mainBuffer.add(';\n'); | |
65 } | |
66 | |
67 void addParameterStub(FunctionElement member, | |
68 String attachTo(String invocationName), | |
69 StringBuffer buffer, | |
70 Selector selector, | |
71 bool isNative) { | |
72 FunctionParameters parameters = member.computeParameters(compiler); | |
73 int positionalArgumentCount = selector.positionalArgumentCount; | |
74 if (positionalArgumentCount == parameters.parameterCount) { | |
75 assert(selector.namedArgumentCount == 0); | |
76 return; | |
77 } | |
78 ConstantHandler handler = compiler.constantHandler; | |
79 List<SourceString> names = selector.getOrderedNamedArguments(); | |
80 | |
81 String invocationName = | |
82 namer.instanceMethodInvocationName(member.getLibrary(), member.name, | |
83 selector); | |
84 buffer.add('${attachTo(invocationName)} = function('); | |
85 | |
86 // The parameters that this stub takes. | |
87 List<String> parametersBuffer = new List<String>(selector.argumentCount); | |
88 // The arguments that will be passed to the real method. | |
89 List<String> argumentsBuffer = new List<String>(parameters.parameterCount); | |
90 | |
91 // We fill the lists depending on the selector. For example, | |
92 // take method foo: | |
93 // foo(a, b, [c, d]); | |
94 // | |
95 // We may have multiple ways of calling foo: | |
96 // (1) foo(1, 2, 3, 4) | |
97 // (2) foo(1, 2); | |
98 // (3) foo(1, 2, 3); | |
99 // (4) foo(1, 2, c: 3); | |
100 // (5) foo(1, 2, d: 4); | |
101 // (6) foo(1, 2, c: 3, d: 4); | |
102 // (7) foo(1, 2, d: 4, c: 3); | |
103 // | |
104 // What we generate at the call sites are: | |
105 // (1) foo$4(1, 2, 3, 4) | |
106 // (2) foo$2(1, 2); | |
107 // (3) foo$3(1, 2, 3); | |
108 // (4) foo$3$c(1, 2, 3); | |
109 // (5) foo$3$d(1, 2, 4); | |
110 // (6) foo$4$c$d(1, 2, 3, 4); | |
111 // (7) foo$4$c$d(1, 2, 3, 4); | |
112 // | |
113 // The stubs we generate are (expressed in Dart): | |
114 // (1) No stub generated, call is direct. | |
115 // (2) foo$2(a, b) => foo$4(a, b, null, null) | |
116 // (3) foo$3(a, b, c) => foo$4(a, b, c, null) | |
117 // (4) foo$3$c(a, b, c) => foo$4(a, b, c, null); | |
118 // (5) foo$3$d(a, b, d) => foo$4(a, b, null, d); | |
119 // (6) foo$4$c$d(a, b, c, d) => foo$4(a, b, c, d); | |
120 // (7) Same as (5). | |
121 // | |
122 // We need to generate a stub for (5) because the order of the | |
123 // stub arguments and the real method may be different. | |
124 | |
125 int count = 0; | |
126 int indexOfLastOptionalArgumentInParameters = positionalArgumentCount - 1; | |
127 parameters.forEachParameter((Element element) { | |
128 String jsName = JsNames.getValid(element.name.slowToString()); | |
129 if (count < positionalArgumentCount) { | |
130 parametersBuffer[count] = jsName; | |
131 argumentsBuffer[count] = jsName; | |
132 } else { | |
133 int index = names.indexOf(element.name); | |
134 if (index != -1) { | |
135 indexOfLastOptionalArgumentInParameters = count; | |
136 // The order of the named arguments is not the same as the | |
137 // one in the real method (which is in Dart source order). | |
138 argumentsBuffer[count] = jsName; | |
139 parametersBuffer[selector.positionalArgumentCount + index] = jsName; | |
140 } else { | |
141 Constant value = handler.initialVariableValues[element]; | |
142 if (value == null) { | |
143 argumentsBuffer[count] = '(void 0)'; | |
144 } else { | |
145 if (!value.isNull()) { | |
146 // If the value is the null constant, we should not pass it | |
147 // down to the native method. | |
148 indexOfLastOptionalArgumentInParameters = count; | |
149 } | |
150 argumentsBuffer[count] = | |
151 handler.writeJsCode(new StringBuffer(), value).toString(); | |
152 } | |
153 } | |
154 } | |
155 count++; | |
156 }); | |
157 String parametersString = Strings.join(parametersBuffer, ","); | |
158 buffer.add('$parametersString) {\n'); | |
159 | |
160 if (isNative) { | |
161 nativeEmitter.emitParameterStub( | |
162 member, invocationName, parametersString, argumentsBuffer, | |
163 indexOfLastOptionalArgumentInParameters); | |
164 } else { | |
165 String arguments = Strings.join(argumentsBuffer, ","); | |
166 buffer.add(' return this.${namer.getName(member)}($arguments)'); | |
167 } | |
168 buffer.add('\n};\n'); | |
169 } | |
170 | |
171 void addParameterStubs(FunctionElement member, | |
172 String attachTo(String invocationName), | |
173 StringBuffer buffer, | |
174 [bool isNative = false]) { | |
175 Set<Selector> selectors = compiler.universe.invokedNames[member.name]; | |
176 if (selectors == null) return; | |
177 FunctionParameters parameters = member.computeParameters(compiler); | |
178 for (Selector selector in selectors) { | |
179 if (!selector.applies(parameters)) continue; | |
180 addParameterStub(member, attachTo, buffer, selector, isNative); | |
181 } | |
182 } | |
183 | |
184 void addInstanceMember(Element member, | |
185 String attachTo(String name), | |
186 StringBuffer buffer, | |
187 [bool isNative = false]) { | |
188 // TODO(floitsch): we don't need to deal with members of | |
189 // uninstantiated classes, that have been overwritten by subclasses. | |
190 | |
191 if (member.kind === ElementKind.FUNCTION | |
192 || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY | |
193 || member.kind === ElementKind.GETTER | |
194 || member.kind === ElementKind.SETTER) { | |
195 if (member.modifiers !== null && member.modifiers.isAbstract()) return; | |
196 String codeBlock = compiler.universe.generatedCode[member]; | |
197 if (codeBlock == null) return; | |
198 buffer.add('${attachTo(namer.getName(member))} = $codeBlock;\n'); | |
199 codeBlock = compiler.universe.generatedBailoutCode[member]; | |
200 if (codeBlock !== null) { | |
201 String name = compiler.namer.getBailoutName(member); | |
202 buffer.add('${attachTo(name)} = $codeBlock;\n'); | |
203 } | |
204 FunctionElement function = member; | |
205 FunctionParameters parameters = function.computeParameters(compiler); | |
206 if (!parameters.optionalParameters.isEmpty()) { | |
207 addParameterStubs(member, attachTo, buffer, isNative: isNative); | |
208 } | |
209 } else if (member.kind === ElementKind.FIELD) { | |
210 // TODO(ngeoffray): Have another class generate the code for the | |
211 // fields. | |
212 if ((member.modifiers === null || !member.modifiers.isFinal()) && | |
213 compiler.universe.invokedSetters.contains(member.name)) { | |
214 String setterName = namer.setterName(member.getLibrary(), member.name); | |
215 String name = | |
216 isNative ? member.name.slowToString() : namer.getName(member); | |
217 buffer.add('${attachTo(setterName)} = function(v){\n'); | |
218 buffer.add(' this.$name = v;\n};\n'); | |
219 } | |
220 if (compiler.universe.invokedGetters.contains(member.name)) { | |
221 String getterName = namer.getterName(member.getLibrary(), member.name); | |
222 String name = | |
223 isNative ? member.name.slowToString() : namer.getName(member); | |
224 buffer.add('${attachTo(getterName)} = function(){\n'); | |
225 buffer.add(' return this.$name;\n};\n'); | |
226 } | |
227 } else { | |
228 compiler.internalError('unexpected kind: "${member.kind}"', | |
229 element: member); | |
230 } | |
231 emitExtraAccessors(member, attachTo, buffer); | |
232 } | |
233 | |
234 bool generateFieldInits(ClassElement classElement, | |
235 StringBuffer argumentsBuffer, | |
236 StringBuffer bodyBuffer) { | |
237 bool isFirst = true; | |
238 do { | |
239 // TODO(floitsch): make sure there are no name clashes. | |
240 String className = namer.getName(classElement); | |
241 | |
242 void generateFieldInit(Element member) { | |
243 if (member.isInstanceMember() && member.kind == ElementKind.FIELD) { | |
244 if (!isFirst) argumentsBuffer.add(', '); | |
245 isFirst = false; | |
246 String memberName = namer.instanceFieldName(member.getLibrary(), | |
247 member.name); | |
248 argumentsBuffer.add('${className}_$memberName'); | |
249 bodyBuffer.add(' this.$memberName = ${className}_$memberName;\n'); | |
250 } | |
251 } | |
252 | |
253 for (Element element in classElement.members) { | |
254 generateFieldInit(element); | |
255 } | |
256 for (Element element in classElement.backendMembers) { | |
257 generateFieldInit(element); | |
258 } | |
259 | |
260 classElement = classElement.superclass; | |
261 } while(classElement !== null); | |
262 } | |
263 | |
264 void emitInherits(ClassElement cls, StringBuffer buffer) { | |
265 ClassElement superclass = cls.superclass; | |
266 if (superclass !== null) { | |
267 addInheritFunctionIfNecessary(); | |
268 String className = namer.isolatePropertyAccess(cls); | |
269 String superName = namer.isolatePropertyAccess(superclass); | |
270 buffer.add('${inheritsName}($className, $superName);\n'); | |
271 } | |
272 } | |
273 | |
274 void ensureGenerated(ClassElement classElement, StringBuffer buffer) { | |
275 if (classElement == null) return; | |
276 if (generatedClasses.contains(classElement)) return; | |
277 generatedClasses.add(classElement); | |
278 generateClass(classElement, buffer); | |
279 } | |
280 | |
281 void generateClass(ClassElement classElement, StringBuffer buffer) { | |
282 ensureGenerated(classElement.superclass, buffer); | |
283 | |
284 if (classElement.isNative()) { | |
285 nativeEmitter.generateNativeClass(classElement); | |
286 return; | |
287 } else { | |
288 // TODO(ngeoffray): Instead of switching between buffer, we | |
289 // should create code sections, and decide where to emit them at | |
290 // the end. | |
291 buffer = mainBuffer; | |
292 } | |
293 | |
294 String className = namer.isolatePropertyAccess(classElement); | |
295 String constructorName = namer.safeName(classElement.name.slowToString()); | |
296 buffer.add('$className = function $constructorName('); | |
297 StringBuffer bodyBuffer = new StringBuffer(); | |
298 // If the class is never instantiated we still need to set it up for | |
299 // inheritance purposes, but we can leave its JavaScript constructor empty. | |
300 if (compiler.universe.instantiatedClasses.contains(classElement)) { | |
301 generateFieldInits(classElement, buffer, bodyBuffer); | |
302 } | |
303 buffer.add(') {\n'); | |
304 buffer.add(bodyBuffer); | |
305 buffer.add('};\n'); | |
306 | |
307 emitInherits(classElement, buffer); | |
308 | |
309 String attachTo(String name) => '$className.prototype.$name'; | |
310 for (Element member in classElement.members) { | |
311 if (member.isInstanceMember()) { | |
312 addInstanceMember(member, attachTo, buffer); | |
313 } | |
314 } | |
315 for (Element member in classElement.backendMembers) { | |
316 if (member.isInstanceMember()) { | |
317 addInstanceMember(member, attachTo, buffer); | |
318 } | |
319 } | |
320 generateTypeTests(classElement, (Element other) { | |
321 buffer.add('${attachTo(namer.operatorIs(other))} = '); | |
322 if (nativeEmitter.requiresNativeIsCheck(other)) { | |
323 buffer.add('function() { return true; }'); | |
324 } else { | |
325 buffer.add('true'); | |
326 } | |
327 buffer.add(';\n'); | |
328 }); | |
329 | |
330 if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) { | |
331 // Emit the noSuchMethods on the Object prototype now, so that | |
332 // the code in the dynamicMethod can find them. Note that the | |
333 // code in dynamicMethod is invoked before analyzing the full JS | |
334 // script. | |
335 emitNoSuchMethodCalls(buffer); | |
336 } | |
337 } | |
338 | |
339 void generateTypeTests(ClassElement cls, | |
340 void generateTypeTest(ClassElement element)) { | |
341 if (compiler.universe.isChecks.contains(cls)) { | |
342 generateTypeTest(cls); | |
343 } | |
344 generateInterfacesIsTests(cls, generateTypeTest, new Set<Element>()); | |
345 } | |
346 | |
347 void generateInterfacesIsTests(ClassElement cls, | |
348 void generateTypeTest(ClassElement element), | |
349 Set<Element> alreadyGenerated) { | |
350 for (Type interfaceType in cls.interfaces) { | |
351 Element element = interfaceType.element; | |
352 if (!alreadyGenerated.contains(element) && | |
353 compiler.universe.isChecks.contains(element)) { | |
354 alreadyGenerated.add(element); | |
355 generateTypeTest(element); | |
356 } | |
357 generateInterfacesIsTests(element, generateTypeTest, alreadyGenerated); | |
358 } | |
359 } | |
360 | |
361 void emitClasses(StringBuffer buffer) { | |
362 for (ClassElement element in compiler.universe.instantiatedClasses) { | |
363 ensureGenerated(element, buffer); | |
364 } | |
365 } | |
366 | |
367 void emitStaticFunctionsWithNamer(StringBuffer buffer, | |
368 Map<Element, String> generatedCode, | |
369 String functionNamer(Element element)) { | |
370 generatedCode.forEach((Element element, String codeBlock) { | |
371 if (!element.isInstanceMember()) { | |
372 buffer.add('${functionNamer(element)} = '); | |
373 buffer.add(codeBlock); | |
374 buffer.add(';\n\n'); | |
375 } | |
376 }); | |
377 } | |
378 | |
379 void emitStaticFunctions(StringBuffer buffer) { | |
380 emitStaticFunctionsWithNamer(buffer, | |
381 compiler.universe.generatedCode, | |
382 namer.isolatePropertyAccess); | |
383 emitStaticFunctionsWithNamer(buffer, | |
384 compiler.universe.generatedBailoutCode, | |
385 namer.isolateBailoutPropertyAccess); | |
386 } | |
387 | |
388 void emitStaticFunctionGetters(StringBuffer buffer) { | |
389 Set<FunctionElement> functionsNeedingGetter = | |
390 compiler.universe.staticFunctionsNeedingGetter; | |
391 for (FunctionElement element in functionsNeedingGetter) { | |
392 // The static function does not have the correct name. Since | |
393 // [addParameterStubs] use the name to create its stubs we simply | |
394 // create a fake element with the correct name. | |
395 // Note: the callElement will not have any enclosingElement. | |
396 FunctionElement callElement = | |
397 new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, element); | |
398 String staticName = namer.isolatePropertyAccess(element); | |
399 int parameterCount = element.parameterCount(compiler); | |
400 String invocationName = | |
401 namer.instanceMethodName(element.getLibrary(), callElement.name, | |
402 parameterCount); | |
403 buffer.add("$staticName.$invocationName = $staticName;\n"); | |
404 addParameterStubs(callElement, (name) => '$staticName.$name', buffer); | |
405 } | |
406 } | |
407 | |
408 void emitDynamicFunctionGetter(StringBuffer buffer, | |
409 String attachTo(String invocationName), | |
410 FunctionElement member) { | |
411 // For every method that has the same name as a property-get we create a | |
412 // getter that returns a bound closure. Say we have a class 'A' with method | |
413 // 'foo' and somewhere in the code there is a dynamic property get of | |
414 // 'foo'. Then we generate the following code (in pseudo Dart): | |
415 // | |
416 // class A { | |
417 // foo(x, y, z) { ... } // Original function. | |
418 // get foo() { return new BoundClosure499(this); } | |
419 // } | |
420 // class BoundClosure499 extends Closure { | |
421 // var self; | |
422 // BoundClosure499(this.self); | |
423 // $call3(x, y, z) { return self.foo(x, y, z); } | |
424 // } | |
425 | |
426 // TODO(floitsch): share the closure classes with other classes | |
427 // if they share methods with the same signature. | |
428 | |
429 // The closure class. | |
430 SourceString name = const SourceString("BoundClosure"); | |
431 ClassElement closureClassElement = | |
432 new ClosureClassElement(compiler, member.getCompilationUnit()); | |
433 String isolateAccess = namer.isolatePropertyAccess(closureClassElement); | |
434 ensureGenerated(closureClassElement.superclass, buffer); | |
435 | |
436 // Define the constructor with a name so that Object.toString can | |
437 // find the class name of the closure class. | |
438 buffer.add("$isolateAccess = function $name(self) "); | |
439 buffer.add("{ this.self = self; };\n"); | |
440 emitInherits(closureClassElement, buffer); | |
441 | |
442 String prototype = "$isolateAccess.prototype"; | |
443 | |
444 // Now add the methods on the closure class. The instance method does not | |
445 // have the correct name. Since [addParameterStubs] use the name to create | |
446 // its stubs we simply create a fake element with the correct name. | |
447 // Note: the callElement will not have any enclosingElement. | |
448 FunctionElement callElement = | |
449 new ClosureInvocationElement(Namer.CLOSURE_INVOCATION_NAME, member); | |
450 | |
451 int parameterCount = member.parameterCount(compiler); | |
452 String invocationName = | |
453 namer.instanceMethodName(member.getLibrary(), | |
454 callElement.name, parameterCount); | |
455 String targetName = namer.instanceMethodName(member.getLibrary(), | |
456 member.name, parameterCount); | |
457 List<String> arguments = new List<String>(parameterCount); | |
458 for (int i = 0; i < parameterCount; i++) { | |
459 arguments[i] = "arg$i"; | |
460 } | |
461 String joinedArgs = Strings.join(arguments, ", "); | |
462 buffer.add("$prototype.$invocationName = function($joinedArgs) {\n"); | |
463 buffer.add(" return this.self.$targetName($joinedArgs);\n"); | |
464 buffer.add("};\n"); | |
465 addParameterStubs(callElement, | |
466 (invocationName) => '$prototype.$invocationName', | |
467 buffer); | |
468 | |
469 // And finally the getter. | |
470 String getterName = namer.getterName(member.getLibrary(), member.name); | |
471 String closureClass = namer.isolateAccess(closureClassElement); | |
472 buffer.add("${attachTo(getterName)} = function() {\n"); | |
473 buffer.add(" return new $closureClass(this);\n"); | |
474 buffer.add("};\n"); | |
475 } | |
476 | |
477 void emitCallStubForGetter(StringBuffer buffer, | |
478 String attachTo(String name), | |
479 Element member, | |
480 Set<Selector> selectors) { | |
481 String getter; | |
482 if (member.kind == ElementKind.GETTER) { | |
483 getter = "this.${namer.getterName(member.getLibrary(), member.name)}()"; | |
484 } else { | |
485 getter = | |
486 "this.${namer.instanceFieldName(member.getLibrary(), member.name)}"; | |
487 } | |
488 for (Selector selector in selectors) { | |
489 String invocationName = | |
490 namer.instanceMethodInvocationName(member.getLibrary(), member.name, | |
491 selector); | |
492 SourceString callName = Namer.CLOSURE_INVOCATION_NAME; | |
493 String closureCallName = | |
494 namer.instanceMethodInvocationName(member.getLibrary(), callName, | |
495 selector); | |
496 List<String> arguments = <String>[]; | |
497 for (int i = 0; i < selector.argumentCount; i++) { | |
498 arguments.add("arg$i"); | |
499 } | |
500 String joined = Strings.join(arguments, ", "); | |
501 buffer.add("${attachTo(invocationName)} = function($joined) {\n"); | |
502 buffer.add(" return $getter.$closureCallName($joined);\n"); | |
503 buffer.add("};\n"); | |
504 } | |
505 } | |
506 | |
507 void emitStaticNonFinalFieldInitializations(StringBuffer buffer) { | |
508 // Adds initializations inside the Isolate constructor. | |
509 // Example: | |
510 // function Isolate() { | |
511 // this.staticNonFinal = Isolate.prototype.someVal; | |
512 // ... | |
513 // } | |
514 ConstantHandler handler = compiler.constantHandler; | |
515 List<VariableElement> staticNonFinalFields = | |
516 handler.getStaticNonFinalFieldsForEmission(); | |
517 if (!staticNonFinalFields.isEmpty()) buffer.add('\n'); | |
518 for (Element element in staticNonFinalFields) { | |
519 buffer.add(' this.${namer.getName(element)} = '); | |
520 compiler.withCurrentElement(element, () { | |
521 handler.writeJsCodeForVariable(buffer, element); | |
522 }); | |
523 buffer.add(';\n'); | |
524 } | |
525 } | |
526 | |
527 void emitCompileTimeConstants(StringBuffer buffer) { | |
528 ConstantHandler handler = compiler.constantHandler; | |
529 List<Constant> constants = handler.getConstantsForEmission(); | |
530 String prototype = "${namer.ISOLATE}.prototype"; | |
531 bool addedMakeConstantList = false; | |
532 for (Constant constant in constants) { | |
533 if (!addedMakeConstantList && constant.isList()) { | |
534 addedMakeConstantList = true; | |
535 emitMakeConstantList(prototype, buffer); | |
536 } | |
537 String name = handler.getNameForConstant(constant); | |
538 buffer.add('$prototype.$name = '); | |
539 handler.writeJsCode(buffer, constant); | |
540 buffer.add(';\n'); | |
541 } | |
542 } | |
543 | |
544 void emitMakeConstantList(String prototype, StringBuffer buffer) { | |
545 buffer.add(prototype); | |
546 buffer.add(@'''.makeConstantList = function(list) { | |
547 list.immutable$list = true; | |
548 list.fixed$length = true; | |
549 return list; | |
550 }; | |
551 '''); | |
552 } | |
553 | |
554 void emitStaticFinalFieldInitializations(StringBuffer buffer) { | |
555 ConstantHandler handler = compiler.constantHandler; | |
556 List<VariableElement> staticFinalFields = | |
557 handler.getStaticFinalFieldsForEmission(); | |
558 for (VariableElement element in staticFinalFields) { | |
559 buffer.add('${namer.isolatePropertyAccess(element)} = '); | |
560 compiler.withCurrentElement(element, () { | |
561 handler.writeJsCodeForVariable(buffer, element); | |
562 }); | |
563 buffer.add(';\n'); | |
564 } | |
565 } | |
566 | |
567 void emitExtraAccessors(Element member, | |
568 String attachTo(String name), | |
569 StringBuffer buffer) { | |
570 if (member.kind == ElementKind.GETTER || member.kind == ElementKind.FIELD) { | |
571 Set<Selector> selectors = compiler.universe.invokedNames[member.name]; | |
572 if (selectors !== null && !selectors.isEmpty()) { | |
573 compiler.emitter.emitCallStubForGetter( | |
574 buffer, attachTo, member, selectors); | |
575 } | |
576 } else if (member.kind == ElementKind.FUNCTION) { | |
577 if (compiler.universe.invokedGetters.contains(member.name)) { | |
578 compiler.emitter.emitDynamicFunctionGetter( | |
579 buffer, attachTo, member); | |
580 } | |
581 } | |
582 } | |
583 | |
584 void emitNoSuchMethodCalls(StringBuffer buffer) { | |
585 // Do not generate no such method calls if there is no class. | |
586 if (compiler.universe.instantiatedClasses.isEmpty()) return; | |
587 | |
588 ClassElement objectClass = | |
589 compiler.coreLibrary.find(const SourceString('Object')); | |
590 String className = namer.isolatePropertyAccess(objectClass); | |
591 String prototype = '$className.prototype'; | |
592 String noSuchMethodName = | |
593 namer.instanceMethodName(null, Compiler.NO_SUCH_METHOD, 2); | |
594 Collection<LibraryElement> libraries = | |
595 compiler.universe.libraries.getValues(); | |
596 | |
597 void generateMethod(String methodName, String jsName, Selector selector) { | |
598 buffer.add('$prototype.$jsName = function'); | |
599 StringBuffer args = new StringBuffer(); | |
600 for (int i = 0; i < selector.argumentCount; i++) { | |
601 if (i != 0) args.add(', '); | |
602 args.add('arg$i'); | |
603 } | |
604 // We need to check if the object has a noSuchMethod. If not, it | |
605 // means the object is a native object, and we can just call our | |
606 // generic noSuchMethod. Note that when calling this method, the | |
607 // 'this' object is not a Dart object. | |
608 buffer.add(' ($args) {\n'); | |
609 buffer.add(' return this.$noSuchMethodName\n'); | |
610 buffer.add(" ? this.$noSuchMethodName('$methodName', [$args])\n"); | |
611 buffer.add(" : $objectClassName.prototype.$noSuchMethodName.call("); | |
612 buffer.add("this, '$methodName', [$args])\n"); | |
613 buffer.add('}\n'); | |
614 } | |
615 | |
616 compiler.universe.invokedNames.forEach((SourceString methodName, | |
617 Set<Selector> selectors) { | |
618 if (objectClass.lookupLocalMember(methodName) === null | |
619 && methodName != Namer.OPERATOR_EQUALS) { | |
620 for (Selector selector in selectors) { | |
621 if (methodName.isPrivate()) { | |
622 for (LibraryElement lib in libraries) { | |
623 String jsName = | |
624 namer.instanceMethodInvocationName(lib, methodName, selector); | |
625 generateMethod(methodName.slowToString(), jsName, selector); | |
626 } | |
627 } else { | |
628 String jsName = | |
629 namer.instanceMethodInvocationName(null, methodName, selector); | |
630 generateMethod(methodName.slowToString(), jsName, selector); | |
631 } | |
632 } | |
633 } | |
634 }); | |
635 | |
636 compiler.universe.invokedGetters.forEach((SourceString getterName) { | |
637 if (getterName.isPrivate()) { | |
638 for (LibraryElement lib in libraries) { | |
639 String jsName = namer.getterName(lib, getterName); | |
640 generateMethod('get ${getterName.slowToString()}', jsName, | |
641 Selector.GETTER); | |
642 } | |
643 } else { | |
644 String jsName = namer.getterName(null, getterName); | |
645 generateMethod('get ${getterName.slowToString()}', jsName, | |
646 Selector.GETTER); | |
647 } | |
648 }); | |
649 | |
650 compiler.universe.invokedSetters.forEach((SourceString setterName) { | |
651 if (setterName.isPrivate()) { | |
652 for (LibraryElement lib in libraries) { | |
653 String jsName = namer.setterName(lib, setterName); | |
654 generateMethod('set ${setterName.slowToString()}', jsName, | |
655 Selector.SETTER); | |
656 } | |
657 } else { | |
658 String jsName = namer.setterName(null, setterName); | |
659 generateMethod('set ${setterName.slowToString()}', jsName, | |
660 Selector.SETTER); | |
661 } | |
662 }); | |
663 } | |
664 | |
665 String buildIsolateSetup(Element appMain, Element isolateMain) { | |
666 String mainAccess = "${namer.isolateAccess(appMain)}"; | |
667 String currentIsolate = "${namer.CURRENT_ISOLATE}"; | |
668 String mainEnsureGetter = ''; | |
669 // Since we pass the closurized version of the main method to | |
670 // the isolate method, we must make sure that it exists. | |
671 if (!compiler.universe.staticFunctionsNeedingGetter.contains(appMain)) { | |
672 String invocationName = | |
673 "${namer.closureInvocationName(Selector.INVOCATION_0)}"; | |
674 mainEnsureGetter = "$mainAccess.$invocationName = $mainAccess"; | |
675 } | |
676 | |
677 // TODO(ngeoffray): These globals are currently required by the isolate | |
678 // library, but since leg already generates code on an Isolate object, they | |
679 // are not really needed. We should remove them once Leg replaces Frog. | |
680 return """ | |
681 var \$globalThis = $currentIsolate; | |
682 var \$globalState; | |
683 var \$globals; | |
684 function \$static_init(){}; | |
685 | |
686 function \$initGlobals(context) { | |
687 context.isolateStatics = new ${namer.ISOLATE}(); | |
688 } | |
689 function \$setGlobals(context) { | |
690 $currentIsolate = context.isolateStatics; | |
691 \$globalThis = $currentIsolate; | |
692 } | |
693 $mainEnsureGetter | |
694 ${namer.isolateAccess(isolateMain)}($mainAccess);"""; | |
695 } | |
696 | |
697 emitMain(StringBuffer buffer) { | |
698 if (compiler.isMockCompilation) return; | |
699 Element main = compiler.mainApp.find(Compiler.MAIN); | |
700 if (compiler.isolateLibrary != null) { | |
701 Element isolateMain = | |
702 compiler.isolateLibrary.find(Compiler.START_ROOT_ISOLATE); | |
703 buffer.add(buildIsolateSetup(main, isolateMain)); | |
704 } else { | |
705 buffer.add('${namer.isolateAccess(main)}();\n'); | |
706 } | |
707 } | |
708 | |
709 String assembleProgram() { | |
710 measure(() { | |
711 mainBuffer.add('function ${namer.ISOLATE}() {'); | |
712 emitStaticNonFinalFieldInitializations(mainBuffer); | |
713 mainBuffer.add('}\n\n'); | |
714 emitClasses(mainBuffer); | |
715 emitStaticFunctions(mainBuffer); | |
716 emitStaticFunctionGetters(mainBuffer); | |
717 emitCompileTimeConstants(mainBuffer); | |
718 emitStaticFinalFieldInitializations(mainBuffer); | |
719 nativeEmitter.emitDynamicDispatchMetadata(); | |
720 mainBuffer.add( | |
721 'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n'); | |
722 nativeEmitter.assembleCode(mainBuffer); | |
723 emitMain(mainBuffer); | |
724 compiler.assembledCode = mainBuffer.toString(); | |
725 }); | |
726 return compiler.assembledCode; | |
727 } | |
728 } | |
OLD | NEW |