| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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 /** | 5 /** |
| 6 * A function element that represents a closure call. The signature is copied | 6 * A function element that represents a closure call. The signature is copied |
| 7 * from the given element. | 7 * from the given element. |
| 8 */ | 8 */ |
| 9 class ClosureInvocationElement extends FunctionElement { | 9 class ClosureInvocationElement extends FunctionElement { |
| 10 ClosureInvocationElement(SourceString name, | 10 ClosureInvocationElement(SourceString name, |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 => '${namer.ISOLATE}.\$defineClass'; | 45 => '${namer.ISOLATE}.\$defineClass'; |
| 46 String get finishClassesName() | 46 String get finishClassesName() |
| 47 => '${namer.ISOLATE}.\$finishClasses'; | 47 => '${namer.ISOLATE}.\$finishClasses'; |
| 48 String get finishIsolateConstructorName() | 48 String get finishIsolateConstructorName() |
| 49 => '${namer.ISOLATE}.\$finishIsolateConstructor'; | 49 => '${namer.ISOLATE}.\$finishIsolateConstructor'; |
| 50 String get pendingClassesName() | 50 String get pendingClassesName() |
| 51 => '${namer.ISOLATE}.\$pendingClasses'; | 51 => '${namer.ISOLATE}.\$pendingClasses'; |
| 52 String get isolatePropertiesName() | 52 String get isolatePropertiesName() |
| 53 => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}'; | 53 => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}'; |
| 54 | 54 |
| 55 final String GETTER_SUFFIX = "?"; |
| 56 final String SETTER_SUFFIX = "!"; |
| 57 final String GETTER_SETTER_SUFFIX = "="; |
| 58 |
| 55 String get defineClassFunction() { | 59 String get defineClassFunction() { |
| 56 // First the class name, then the super class name, followed by the fields | 60 // First the class name, then the super class name, followed by the fields |
| 57 // (in an array) and the members (inside an Object literal). | 61 // (in an array) and the members (inside an Object literal). |
| 58 // The caller can also pass in the constructor as a function if needed. | 62 // The caller can also pass in the constructor as a function if needed. |
| 59 // | 63 // |
| 60 // Example: | 64 // Example: |
| 61 // defineClass("A", "B", ["x", "y"], { | 65 // defineClass("A", "B", ["x", "y"], { |
| 62 // foo$1: function(y) { | 66 // foo$1: function(y) { |
| 63 // print(this.x + y); | 67 // print(this.x + y); |
| 64 // }, | 68 // }, |
| 65 // bar$2: function(t, v) { | 69 // bar$2: function(t, v) { |
| 66 // this.x = t - v; | 70 // this.x = t - v; |
| 67 // }, | 71 // }, |
| 68 // }); | 72 // }); |
| 69 return """ | 73 return """ |
| 70 function(cls, superclass, fields, prototype) { | 74 function(cls, superclass, fields, prototype) { |
| 71 var constructor; | 75 var constructor; |
| 72 if (typeof fields == 'function') { | 76 if (typeof fields == 'function') { |
| 73 constructor = fields; | 77 constructor = fields; |
| 74 } else { | 78 } else { |
| 75 var str = "(function " + cls + "("; | 79 var str = "(function " + cls + "("; |
| 76 var body = ""; | 80 var body = ""; |
| 77 for (var i = 0; i < fields.length; i++) { | 81 for (var i = 0; i < fields.length; i++) { |
| 78 if (i != 0) str += ", "; | 82 if (i != 0) str += ", "; |
| 79 str += fields[i]; | 83 var field = fields[i]; |
| 80 body += "this." + fields[i] + " = " + fields[i] + ";\\n"; | 84 var len = field.length; |
| 85 var lastChar = field[len - 1]; |
| 86 var needsGetter = false; |
| 87 var needsSetter = false; |
| 88 switch (lastChar) { |
| 89 case '$GETTER_SUFFIX': needsGetter = true; break; |
| 90 case '$GETTER_SETTER_SUFFIX': needsGetter = true; // Fall-through. |
| 91 case '$SETTER_SUFFIX': needsSetter = true; |
| 92 } |
| 93 if (needsGetter || needsSetter) field = field.substring(0, len - 1); |
| 94 str += field; |
| 95 body += "this." + field + " = " + field + ";\\n"; |
| 96 if (needsGetter) { |
| 97 var getterString = "return this." + field + ";"; |
| 98 prototype["get\$" + field] = new Function(getterString); |
| 99 } |
| 100 if (needsSetter) { |
| 101 var setterString = "this." + field + " = v;"; |
| 102 prototype["set\$" + field] = new Function("v", setterString); |
| 103 } |
| 81 } | 104 } |
| 82 str += ") {" + body + "})"; | 105 str += ") {" + body + "})"; |
| 83 constructor = eval(str); | 106 constructor = eval(str); |
| 84 } | 107 } |
| 85 $isolatePropertiesName[cls] = constructor; | 108 $isolatePropertiesName[cls] = constructor; |
| 86 constructor.prototype = prototype; | 109 constructor.prototype = prototype; |
| 87 if (superclass !== "") { | 110 if (superclass !== "") { |
| 88 $pendingClassesName[cls] = superclass; | 111 $pendingClassesName[cls] = superclass; |
| 89 } | 112 } |
| 90 }"""; | 113 }"""; |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 void defineInstanceMember(String invocationName, | 342 void defineInstanceMember(String invocationName, |
| 320 String definition)) { | 343 String definition)) { |
| 321 Set<Selector> selectors = compiler.universe.invokedNames[member.name]; | 344 Set<Selector> selectors = compiler.universe.invokedNames[member.name]; |
| 322 if (selectors == null) return; | 345 if (selectors == null) return; |
| 323 for (Selector selector in selectors) { | 346 for (Selector selector in selectors) { |
| 324 if (!selector.applies(member, compiler)) continue; | 347 if (!selector.applies(member, compiler)) continue; |
| 325 addParameterStub(member, selector, defineInstanceMember); | 348 addParameterStub(member, selector, defineInstanceMember); |
| 326 } | 349 } |
| 327 } | 350 } |
| 328 | 351 |
| 352 bool instanceFieldNeedsGetter(Element member) { |
| 353 assert(member.kind === ElementKind.FIELD); |
| 354 return compiler.universe.hasGetter(member, compiler); |
| 355 } |
| 356 |
| 357 bool instanceFieldNeedsSetter(Element member) { |
| 358 assert(member.kind === ElementKind.FIELD); |
| 359 return (member.modifiers === null || !member.modifiers.isFinal()) |
| 360 && compiler.universe.hasSetter(member, compiler); |
| 361 } |
| 362 |
| 363 String compiledFieldName(Element member) { |
| 364 assert(member.kind === ElementKind.FIELD); |
| 365 return member.isNative() |
| 366 ? member.name.slowToString() |
| 367 : namer.getName(member); |
| 368 } |
| 369 |
| 329 void addInstanceMember(Element member, | 370 void addInstanceMember(Element member, |
| 371 bool needGettersAndSetters, |
| 330 void defineInstanceMember(String invocationName, | 372 void defineInstanceMember(String invocationName, |
| 331 String definition)) { | 373 String definition)) { |
| 332 // TODO(floitsch): we don't need to deal with members of | 374 // TODO(floitsch): we don't need to deal with members of |
| 333 // uninstantiated classes, that have been overwritten by subclasses. | 375 // uninstantiated classes, that have been overwritten by subclasses. |
| 334 | 376 |
| 335 if (member.kind === ElementKind.FUNCTION | 377 if (member.kind === ElementKind.FUNCTION |
| 336 || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY | 378 || member.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY |
| 337 || member.kind === ElementKind.GETTER | 379 || member.kind === ElementKind.GETTER |
| 338 || member.kind === ElementKind.SETTER) { | 380 || member.kind === ElementKind.SETTER) { |
| 339 if (member.modifiers !== null && member.modifiers.isAbstract()) return; | 381 if (member.modifiers !== null && member.modifiers.isAbstract()) return; |
| 340 String codeBlock = compiler.universe.generatedCode[member]; | 382 String codeBlock = compiler.universe.generatedCode[member]; |
| 341 if (codeBlock == null) return; | 383 if (codeBlock == null) return; |
| 342 defineInstanceMember(namer.getName(member), codeBlock); | 384 defineInstanceMember(namer.getName(member), codeBlock); |
| 343 codeBlock = compiler.universe.generatedBailoutCode[member]; | 385 codeBlock = compiler.universe.generatedBailoutCode[member]; |
| 344 if (codeBlock !== null) { | 386 if (codeBlock !== null) { |
| 345 defineInstanceMember(compiler.namer.getBailoutName(member), codeBlock); | 387 defineInstanceMember(compiler.namer.getBailoutName(member), codeBlock); |
| 346 } | 388 } |
| 347 FunctionElement function = member; | 389 FunctionElement function = member; |
| 348 FunctionSignature parameters = function.computeSignature(compiler); | 390 FunctionSignature parameters = function.computeSignature(compiler); |
| 349 if (!parameters.optionalParameters.isEmpty()) { | 391 if (!parameters.optionalParameters.isEmpty()) { |
| 350 addParameterStubs(member, defineInstanceMember); | 392 addParameterStubs(member, defineInstanceMember); |
| 351 } | 393 } |
| 352 } else if (member.kind === ElementKind.FIELD) { | 394 } else if (member.kind === ElementKind.FIELD) { |
| 353 // TODO(ngeoffray): Have another class generate the code for the | 395 if (needGettersAndSetters) { |
| 354 // fields. | 396 if (instanceFieldNeedsGetter(member)) { |
| 355 if ((member.modifiers === null || !member.modifiers.isFinal()) && | 397 String getter = namer.getterName(member.getLibrary(), member.name); |
| 356 compiler.universe.hasSetter(member, compiler)) { | 398 String name = compiledFieldName(member); |
| 357 String setterName = namer.setterName(member.getLibrary(), member.name); | 399 defineInstanceMember(getter, "function() { return this.$name; }"); |
| 358 String name = member.isNative() | 400 } |
| 359 ? member.name.slowToString() | 401 |
| 360 : namer.getName(member); | 402 if (instanceFieldNeedsSetter(member)) { |
| 361 defineInstanceMember(setterName, "function(v) { this.$name = v; }"); | 403 String setter = namer.setterName(member.getLibrary(), member.name); |
| 362 } | 404 String name = compiledFieldName(member); |
| 363 if (compiler.universe.hasGetter(member, compiler)) { | 405 defineInstanceMember(setter, "function(v) { this.$name = v; }"); |
| 364 String getterName = namer.getterName(member.getLibrary(), member.name); | 406 } |
| 365 String name = member.isNative() | |
| 366 ? member.name.slowToString() | |
| 367 : namer.getName(member); | |
| 368 defineInstanceMember(getterName, "function() { return this.$name; }"); | |
| 369 } | 407 } |
| 370 } else { | 408 } else { |
| 371 compiler.internalError('unexpected kind: "${member.kind}"', | 409 compiler.internalError('unexpected kind: "${member.kind}"', |
| 372 element: member); | 410 element: member); |
| 373 } | 411 } |
| 374 emitExtraAccessors(member, defineInstanceMember); | 412 emitExtraAccessors(member, defineInstanceMember); |
| 375 } | 413 } |
| 376 | 414 |
| 377 List<String> generateFieldList(ClassElement classElement) { | 415 Set<Element> emitClassFields(ClassElement classElement, StringBuffer buffer) { |
| 378 List<String> result = <String>[]; | 416 // If the class is never instantiated we still need to set it up for |
| 417 // inheritance purposes, but we can simplify its JavaScript constructor. |
| 418 bool isInstantiated = |
| 419 compiler.universe.instantiatedClasses.contains(classElement); |
| 420 |
| 421 bool isFirstField = true; |
| 379 void addField(ClassElement enclosingClass, Element member) { | 422 void addField(ClassElement enclosingClass, Element member) { |
| 380 result.add(namer.instanceFieldName(member.getLibrary(), member.name)); | 423 assert(!member.isNative()); |
| 424 // See if we can dynamically create getters and setters. |
| 425 // We can only generate getters and setters for [classElement] since |
| 426 // the fields of super classes could be overwritten with getters or |
| 427 // setters. |
| 428 bool needsDynamicGetter = false; |
| 429 bool needsDynamicSetter = false; |
| 430 if (enclosingClass === classElement) { |
| 431 needsDynamicGetter = instanceFieldNeedsGetter(member); |
| 432 needsDynamicSetter = instanceFieldNeedsSetter(member); |
| 433 } |
| 434 |
| 435 if (isInstantiated || needsDynamicGetter || needsDynamicSetter) { |
| 436 if (isFirstField) { |
| 437 isFirstField = false; |
| 438 } else { |
| 439 buffer.add(", "); |
| 440 } |
| 441 LibraryElement library = member.getLibrary(); |
| 442 SourceString name = member.name; |
| 443 String fieldName = namer.instanceFieldName(library, name); |
| 444 // Getters and setters with suffixes will be generated dynamically. |
| 445 buffer.add('"$fieldName'); |
| 446 if (needsDynamicGetter || needsDynamicSetter) { |
| 447 if (needsDynamicGetter && needsDynamicSetter) { |
| 448 buffer.add(GETTER_SETTER_SUFFIX); |
| 449 } else if (needsDynamicGetter) { |
| 450 buffer.add(GETTER_SUFFIX); |
| 451 } else { |
| 452 buffer.add(SETTER_SUFFIX); |
| 453 } |
| 454 } |
| 455 buffer.add('"'); |
| 456 } |
| 381 } | 457 } |
| 382 | 458 |
| 459 // If a class is not instantiated then we add the field just so we can |
| 460 // generate the field getter/setter dynamically. Since this is only |
| 461 // allowed on fields that are in [classElement] we don't need to visit |
| 462 // superclasses for non-instantiated classes. |
| 383 classElement.forEachInstanceField(addField, | 463 classElement.forEachInstanceField(addField, |
| 384 includeBackendMembers: true, | 464 includeBackendMembers: true, |
| 385 includeSuperMembers: true); | 465 includeSuperMembers: isInstantiated); |
| 386 return result; | |
| 387 } | 466 } |
| 388 | 467 |
| 389 void generateClass(ClassElement classElement, StringBuffer buffer) { | 468 void emitInstanceMembers(ClassElement classElement, StringBuffer buffer) { |
| 390 needsDefineClass = true; | |
| 391 | |
| 392 if (classElement.isNative()) { | |
| 393 nativeEmitter.generateNativeClass(classElement); | |
| 394 return; | |
| 395 } else { | |
| 396 // TODO(ngeoffray): Instead of switching between buffer, we | |
| 397 // should create code sections, and decide where to emit them at | |
| 398 // the end. | |
| 399 buffer = mainBuffer; | |
| 400 } | |
| 401 | |
| 402 String className = namer.getName(classElement); | |
| 403 ClassElement superclass = classElement.superclass; | |
| 404 String superName = ""; | |
| 405 if (superclass !== null) { | |
| 406 superName = namer.getName(superclass); | |
| 407 } | |
| 408 String constructorName = namer.safeName(classElement.name.slowToString()); | |
| 409 buffer.add('$defineClassName("$className", "$superName", '); | |
| 410 // If the class is never instantiated we still need to set it up for | |
| 411 // inheritance purposes, but we can simplify its JavaScript constructor. | |
| 412 if (!compiler.universe.instantiatedClasses.contains(classElement)) { | |
| 413 buffer.add("[]"); | |
| 414 } else { | |
| 415 List<String> fields = generateFieldList(classElement); | |
| 416 buffer.add('['); | |
| 417 for (int i = 0; i < fields.length; i++) { | |
| 418 if (i != 0) buffer.add(", "); | |
| 419 buffer.add('"${fields[i]}"'); | |
| 420 } | |
| 421 buffer.add(']'); | |
| 422 } | |
| 423 buffer.add(', {\n'); | |
| 424 | |
| 425 void defineInstanceMember(String name, String value) { | 469 void defineInstanceMember(String name, String value) { |
| 426 buffer.add(' $name: $value,\n'); | 470 buffer.add(' $name: $value,\n'); |
| 427 } | 471 } |
| 428 | 472 |
| 429 classElement.forEachMember(includeBackendMembers: true, | 473 classElement.forEachMember(includeBackendMembers: true, |
| 430 f: (ClassElement enclosing, Element member) { | 474 f: (ClassElement enclosing, Element member) { |
| 431 if (member.isInstanceMember()) { | 475 if (member.isInstanceMember()) { |
| 432 addInstanceMember(member, defineInstanceMember); | 476 // All getters and setters for non-native classes are generated |
| 477 // dynamically. |
| 478 bool needGettersAndSetters = false; |
| 479 addInstanceMember(member, needGettersAndSetters, defineInstanceMember); |
| 433 } | 480 } |
| 434 }); | 481 }); |
| 435 | 482 |
| 436 generateTypeTests(classElement, (Element other) { | 483 generateTypeTests(classElement, (Element other) { |
| 437 if (nativeEmitter.requiresNativeIsCheck(other)) { | 484 if (nativeEmitter.requiresNativeIsCheck(other)) { |
| 438 defineInstanceMember(namer.operatorIs(other), | 485 defineInstanceMember(namer.operatorIs(other), |
| 439 'function() { return true; }'); | 486 'function() { return true; }'); |
| 440 } else { | 487 } else { |
| 441 defineInstanceMember(namer.operatorIs(other), 'true'); | 488 defineInstanceMember(namer.operatorIs(other), 'true'); |
| 442 } | 489 } |
| 443 }); | 490 }); |
| 444 | 491 |
| 445 if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) { | 492 if (classElement === compiler.objectClass && compiler.enabledNoSuchMethod) { |
| 446 // Emit the noSuchMethods on the Object prototype now, so that | 493 // Emit the noSuchMethods on the Object prototype now, so that |
| 447 // the code in the dynamicMethod can find them. Note that the | 494 // the code in the dynamicMethod can find them. Note that the |
| 448 // code in dynamicMethod is invoked before analyzing the full JS | 495 // code in dynamicMethod is invoked before analyzing the full JS |
| 449 // script. | 496 // script. |
| 450 emitNoSuchMethodCalls(defineInstanceMember); | 497 emitNoSuchMethodCalls(defineInstanceMember); |
| 451 } | 498 } |
| 499 } |
| 500 |
| 501 void generateClass(ClassElement classElement, StringBuffer buffer) { |
| 502 needsDefineClass = true; |
| 503 |
| 504 if (classElement.isNative()) { |
| 505 nativeEmitter.generateNativeClass(classElement); |
| 506 return; |
| 507 } else { |
| 508 // TODO(ngeoffray): Instead of switching between buffer, we |
| 509 // should create code sections, and decide where to emit them at |
| 510 // the end. |
| 511 buffer = mainBuffer; |
| 512 } |
| 513 |
| 514 String className = namer.getName(classElement); |
| 515 ClassElement superclass = classElement.superclass; |
| 516 String superName = ""; |
| 517 if (superclass !== null) { |
| 518 superName = namer.getName(superclass); |
| 519 } |
| 520 String constructorName = namer.safeName(classElement.name.slowToString()); |
| 521 |
| 522 buffer.add('$defineClassName("$className", "$superName", ['); |
| 523 emitClassFields(classElement, buffer); |
| 524 buffer.add('], {\n'); |
| 525 emitInstanceMembers(classElement, buffer); |
| 452 buffer.add('});\n\n'); | 526 buffer.add('});\n\n'); |
| 453 } | 527 } |
| 454 | 528 |
| 455 void generateTypeTests(ClassElement cls, | 529 void generateTypeTests(ClassElement cls, |
| 456 void generateTypeTest(ClassElement element)) { | 530 void generateTypeTest(ClassElement element)) { |
| 457 if (compiler.universe.isChecks.contains(cls)) { | 531 if (compiler.universe.isChecks.contains(cls)) { |
| 458 generateTypeTest(cls); | 532 generateTypeTest(cls); |
| 459 } | 533 } |
| 460 generateInterfacesIsTests(cls, generateTypeTest, new Set<Element>()); | 534 generateInterfacesIsTests(cls, generateTypeTest, new Set<Element>()); |
| 461 } | 535 } |
| (...skipping 444 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 906 mainBuffer.add('function init() {\n'); | 980 mainBuffer.add('function init() {\n'); |
| 907 mainBuffer.add(' $isolateProperties = {};\n'); | 981 mainBuffer.add(' $isolateProperties = {};\n'); |
| 908 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); | 982 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); |
| 909 emitFinishIsolateConstructor(mainBuffer); | 983 emitFinishIsolateConstructor(mainBuffer); |
| 910 mainBuffer.add('}\n'); | 984 mainBuffer.add('}\n'); |
| 911 compiler.assembledCode = mainBuffer.toString(); | 985 compiler.assembledCode = mainBuffer.toString(); |
| 912 }); | 986 }); |
| 913 return compiler.assembledCode; | 987 return compiler.assembledCode; |
| 914 } | 988 } |
| 915 } | 989 } |
| OLD | NEW |