| 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 |