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 part of js_backend; | 5 part of js_backend; |
6 | 6 |
7 /** | 7 /** |
8 * A function element that represents a closure call. The signature is copied | 8 * A function element that represents a closure call. The signature is copied |
9 * from the given element. | 9 * from the given element. |
10 */ | 10 */ |
11 class ClosureInvocationElement extends FunctionElement { | 11 class ClosureInvocationElement extends FunctionElement { |
12 ClosureInvocationElement(SourceString name, | 12 ClosureInvocationElement(SourceString name, |
13 FunctionElement other) | 13 FunctionElement other) |
14 : super.from(name, other, other.enclosingElement), | 14 : super.from(name, other, other.enclosingElement), |
15 methodElement = other; | 15 methodElement = other; |
16 | 16 |
17 isInstanceMember() => true; | 17 isInstanceMember() => true; |
18 | 18 |
19 Element getOutermostEnclosingMemberOrTopLevel() => methodElement; | 19 Element getOutermostEnclosingMemberOrTopLevel() => methodElement; |
20 | 20 |
21 /** | 21 /** |
22 * The [member] this invocation refers to. | 22 * The [member] this invocation refers to. |
23 */ | 23 */ |
24 Element methodElement; | 24 Element methodElement; |
25 } | 25 } |
26 | 26 |
27 /** | 27 /** |
28 * A convenient type alias for some functions that emit keyed values. | |
29 */ | |
30 typedef void DefineStubFunction(String invocationName, js.Expression value); | |
31 | |
32 /** | |
33 * A data structure for collecting fragments of a class definition. | |
34 */ | |
35 class ClassBuilder { | |
36 final List<js.Property> properties = <js.Property>[]; | |
37 | |
38 // Has the same signature as [DefineStubFunction]. | |
39 void addProperty(String name, js.Expression value) { | |
floitsch
2013/01/04 10:31:03
Since this is a class-builder, I would call this "
| |
40 properties.add(new js.Property(js.string(name), value)); | |
41 } | |
42 | |
43 js.Expression toObjectInitializer() => new js.ObjectInitializer(properties); | |
44 } | |
45 | |
46 /** | |
28 * Generates the code for all used classes in the program. Static fields (even | 47 * Generates the code for all used classes in the program. Static fields (even |
29 * in classes) are ignored, since they can be treated as non-class elements. | 48 * in classes) are ignored, since they can be treated as non-class elements. |
30 * | 49 * |
31 * The code for the containing (used) methods must exist in the [:universe:]. | 50 * The code for the containing (used) methods must exist in the [:universe:]. |
32 */ | 51 */ |
33 class CodeEmitterTask extends CompilerTask { | 52 class CodeEmitterTask extends CompilerTask { |
34 bool needsInheritFunction = false; | 53 bool needsInheritFunction = false; |
35 bool needsDefineClass = false; | 54 bool needsDefineClass = false; |
36 bool needsClosureClass = false; | 55 bool needsClosureClass = false; |
37 bool needsLazyInitializer = false; | 56 bool needsLazyInitializer = false; |
(...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
420 /** | 439 /** |
421 * Generate stubs to handle invocation of methods with optional | 440 * Generate stubs to handle invocation of methods with optional |
422 * arguments. | 441 * arguments. |
423 * | 442 * |
424 * A method like [: foo([x]) :] may be invoked by the following | 443 * A method like [: foo([x]) :] may be invoked by the following |
425 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this | 444 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this |
426 * function for detailed examples. | 445 * function for detailed examples. |
427 */ | 446 */ |
428 void addParameterStub(FunctionElement member, | 447 void addParameterStub(FunctionElement member, |
429 Selector selector, | 448 Selector selector, |
430 DefineMemberFunction defineInstanceMember, | 449 DefineStubFunction defineStub, |
431 Set<String> alreadyGenerated) { | 450 Set<String> alreadyGenerated) { |
432 FunctionSignature parameters = member.computeSignature(compiler); | 451 FunctionSignature parameters = member.computeSignature(compiler); |
433 int positionalArgumentCount = selector.positionalArgumentCount; | 452 int positionalArgumentCount = selector.positionalArgumentCount; |
434 if (positionalArgumentCount == parameters.parameterCount) { | 453 if (positionalArgumentCount == parameters.parameterCount) { |
435 assert(selector.namedArgumentCount == 0); | 454 assert(selector.namedArgumentCount == 0); |
436 return; | 455 return; |
437 } | 456 } |
438 if (parameters.optionalParametersAreNamed | 457 if (parameters.optionalParametersAreNamed |
439 && selector.namedArgumentCount == parameters.optionalParameterCount) { | 458 && selector.namedArgumentCount == parameters.optionalParameterCount) { |
440 // If the selector has the same number of named arguments as | 459 // If the selector has the same number of named arguments as |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
524 } else { | 543 } else { |
525 body = <js.Statement>[ | 544 body = <js.Statement>[ |
526 new js.Return( | 545 new js.Return( |
527 new js.VariableUse('this') | 546 new js.VariableUse('this') |
528 .dot(namer.getName(member)) | 547 .dot(namer.getName(member)) |
529 .callWith(argumentsBuffer))]; | 548 .callWith(argumentsBuffer))]; |
530 } | 549 } |
531 | 550 |
532 js.Fun function = new js.Fun(parametersBuffer, new js.Block(body)); | 551 js.Fun function = new js.Fun(parametersBuffer, new js.Block(body)); |
533 | 552 |
534 CodeBuffer buffer = new CodeBuffer(); | 553 defineStub(invocationName, function); |
535 buffer.add(js.prettyPrint(function, compiler)); | |
536 defineInstanceMember(invocationName, buffer); | |
537 } | 554 } |
538 | 555 |
539 void addParameterStubs(FunctionElement member, | 556 void addParameterStubs(FunctionElement member, |
540 DefineMemberFunction defineInstanceMember) { | 557 DefineStubFunction defineStub) { |
541 // We fill the lists depending on the selector. For example, | 558 // We fill the lists depending on the selector. For example, |
542 // take method foo: | 559 // take method foo: |
543 // foo(a, b, {c, d}); | 560 // foo(a, b, {c, d}); |
544 // | 561 // |
545 // We may have multiple ways of calling foo: | 562 // We may have multiple ways of calling foo: |
546 // (1) foo(1, 2); | 563 // (1) foo(1, 2); |
547 // (2) foo(1, 2, c: 3); | 564 // (2) foo(1, 2, c: 3); |
548 // (3) foo(1, 2, d: 4); | 565 // (3) foo(1, 2, d: 4); |
549 // (4) foo(1, 2, c: 3, d: 4); | 566 // (4) foo(1, 2, c: 3, d: 4); |
550 // (5) foo(1, 2, d: 4, c: 3); | 567 // (5) foo(1, 2, d: 4, c: 3); |
(...skipping 19 matching lines...) Expand all Loading... | |
570 Set<String> generatedStubNames = new Set<String>(); | 587 Set<String> generatedStubNames = new Set<String>(); |
571 if (compiler.enabledFunctionApply | 588 if (compiler.enabledFunctionApply |
572 && member.name == namer.closureInvocationSelectorName) { | 589 && member.name == namer.closureInvocationSelectorName) { |
573 // If [Function.apply] is called, we pessimistically compile all | 590 // If [Function.apply] is called, we pessimistically compile all |
574 // possible stubs for this closure. | 591 // possible stubs for this closure. |
575 FunctionSignature signature = member.computeSignature(compiler); | 592 FunctionSignature signature = member.computeSignature(compiler); |
576 Set<Selector> selectors = signature.optionalParametersAreNamed | 593 Set<Selector> selectors = signature.optionalParametersAreNamed |
577 ? computeNamedSelectors(signature, member) | 594 ? computeNamedSelectors(signature, member) |
578 : computeOptionalSelectors(signature, member); | 595 : computeOptionalSelectors(signature, member); |
579 for (Selector selector in selectors) { | 596 for (Selector selector in selectors) { |
580 addParameterStub( | 597 addParameterStub(member, selector, defineStub, generatedStubNames); |
581 member, selector, defineInstanceMember, generatedStubNames); | |
582 } | 598 } |
583 } else { | 599 } else { |
584 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 600 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; |
585 if (selectors == null) return; | 601 if (selectors == null) return; |
586 for (Selector selector in selectors) { | 602 for (Selector selector in selectors) { |
587 if (!selector.applies(member, compiler)) continue; | 603 if (!selector.applies(member, compiler)) continue; |
588 addParameterStub( | 604 addParameterStub(member, selector, defineStub, generatedStubNames); |
589 member, selector, defineInstanceMember, generatedStubNames); | |
590 } | 605 } |
591 } | 606 } |
592 } | 607 } |
593 | 608 |
594 /** | 609 /** |
595 * Compute the set of possible selectors in the presence of named | 610 * Compute the set of possible selectors in the presence of named |
596 * parameters. | 611 * parameters. |
597 */ | 612 */ |
598 Set<Selector> computeNamedSelectors(FunctionSignature signature, | 613 Set<Selector> computeNamedSelectors(FunctionSignature signature, |
599 FunctionElement element) { | 614 FunctionElement element) { |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
666 return member.hasFixedBackendName() | 681 return member.hasFixedBackendName() |
667 ? member.fixedBackendName() | 682 ? member.fixedBackendName() |
668 : namer.getName(member); | 683 : namer.getName(member); |
669 } | 684 } |
670 | 685 |
671 /** | 686 /** |
672 * Documentation wanted -- johnniwinther | 687 * Documentation wanted -- johnniwinther |
673 * | 688 * |
674 * Invariant: [member] must be a declaration element. | 689 * Invariant: [member] must be a declaration element. |
675 */ | 690 */ |
676 void addInstanceMember(Element member, | 691 void addInstanceMember(Element member, ClassBuilder builder) { |
677 DefineMemberFunction defineInstanceMember) { | |
678 assert(invariant(member, member.isDeclaration)); | 692 assert(invariant(member, member.isDeclaration)); |
679 // TODO(floitsch): we don't need to deal with members of | 693 // TODO(floitsch): we don't need to deal with members of |
680 // uninstantiated classes, that have been overwritten by subclasses. | 694 // uninstantiated classes, that have been overwritten by subclasses. |
681 | 695 |
682 if (member.isFunction() | 696 if (member.isFunction() |
683 || member.isGenerativeConstructorBody() | 697 || member.isGenerativeConstructorBody() |
684 || member.isAccessor()) { | 698 || member.isAccessor()) { |
685 if (member.isAbstract(compiler)) return; | 699 if (member.isAbstract(compiler)) return; |
686 CodeBuffer codeBuffer = compiler.codegenWorld.generatedCode[member]; | 700 js.Expression code = compiler.codegenWorld.generatedCode[member]; |
687 if (codeBuffer == null) return; | 701 if (code == null) return; |
688 defineInstanceMember(namer.getName(member), codeBuffer); | 702 builder.addProperty(namer.getName(member), code); |
689 codeBuffer = compiler.codegenWorld.generatedBailoutCode[member]; | 703 code = compiler.codegenWorld.generatedBailoutCode[member]; |
690 if (codeBuffer != null) { | 704 if (code != null) { |
691 defineInstanceMember(namer.getBailoutName(member), codeBuffer); | 705 builder.addProperty(namer.getBailoutName(member), code); |
692 } | 706 } |
693 FunctionElement function = member; | 707 FunctionElement function = member; |
694 FunctionSignature parameters = function.computeSignature(compiler); | 708 FunctionSignature parameters = function.computeSignature(compiler); |
695 if (!parameters.optionalParameters.isEmpty) { | 709 if (!parameters.optionalParameters.isEmpty) { |
696 addParameterStubs(member, defineInstanceMember); | 710 addParameterStubs(member, builder.addProperty); |
697 } | 711 } |
698 } else if (!member.isField()) { | 712 } else if (!member.isField()) { |
699 compiler.internalError('unexpected kind: "${member.kind}"', | 713 compiler.internalError('unexpected kind: "${member.kind}"', |
700 element: member); | 714 element: member); |
701 } | 715 } |
702 emitExtraAccessors(member, defineInstanceMember); | 716 emitExtraAccessors(member, builder); |
703 } | 717 } |
704 | 718 |
705 /** | 719 /** |
706 * Documentation wanted -- johnniwinther | 720 * Documentation wanted -- johnniwinther |
707 * | 721 * |
708 * Invariant: [classElement] must be a declaration element. | 722 * Invariant: [classElement] must be a declaration element. |
709 */ | 723 */ |
710 void emitInstanceMembers(ClassElement classElement, | 724 void emitInstanceMembers(ClassElement classElement, |
711 CodeBuffer buffer, | 725 ClassBuilder builder) { |
712 bool emitLeadingComma) { | |
713 assert(invariant(classElement, classElement.isDeclaration)); | 726 assert(invariant(classElement, classElement.isDeclaration)); |
714 void defineInstanceMember(String name, StringBuffer memberBuffer) { | |
715 if (emitLeadingComma) buffer.add(','); | |
716 emitLeadingComma = true; | |
717 buffer.add('\n'); | |
718 buffer.add('$_$name:$_'); | |
719 buffer.add(memberBuffer); | |
720 } | |
721 | |
722 JavaScriptBackend backend = compiler.backend; | 727 JavaScriptBackend backend = compiler.backend; |
723 if (classElement == backend.objectInterceptorClass) { | 728 if (classElement == backend.objectInterceptorClass) { |
724 emitInterceptorMethods(defineInstanceMember); | 729 emitInterceptorMethods(builder); |
725 // The ObjectInterceptor does not have any instance methods. | 730 // The ObjectInterceptor does not have any instance methods. |
726 return; | 731 return; |
727 } | 732 } |
728 | 733 |
729 classElement.implementation.forEachMember( | 734 classElement.implementation.forEachMember( |
730 (ClassElement enclosing, Element member) { | 735 (ClassElement enclosing, Element member) { |
731 assert(invariant(classElement, member.isDeclaration)); | 736 assert(invariant(classElement, member.isDeclaration)); |
732 if (member.isInstanceMember()) { | 737 if (member.isInstanceMember()) { |
733 addInstanceMember(member, defineInstanceMember); | 738 addInstanceMember(member, builder); |
734 } | 739 } |
735 }, | 740 }, |
736 includeBackendMembers: true); | 741 includeBackendMembers: true); |
737 | 742 |
738 generateIsTestsOn(classElement, (Element other) { | 743 generateIsTestsOn(classElement, (Element other) { |
739 String code; | 744 js.Expression code; |
740 if (compiler.objectClass == other) return; | 745 if (compiler.objectClass == other) return; |
741 if (nativeEmitter.requiresNativeIsCheck(other)) { | 746 if (nativeEmitter.requiresNativeIsCheck(other)) { |
742 code = 'function()$_{${_}return true;$_}'; | 747 code = js.fun([], js.block1(js.return_(new js.LiteralBool(true)))); |
743 } else { | 748 } else { |
744 code = 'true'; | 749 code = new js.LiteralBool(true); |
745 } | 750 } |
746 CodeBuffer typeTestBuffer = new CodeBuffer(); | 751 builder.addProperty(namer.operatorIs(other), code); |
747 typeTestBuffer.add(code); | |
748 defineInstanceMember(namer.operatorIs(other), typeTestBuffer); | |
749 }); | 752 }); |
750 | 753 |
751 if (identical(classElement, compiler.objectClass) | 754 if (identical(classElement, compiler.objectClass) |
752 && compiler.enabledNoSuchMethod) { | 755 && compiler.enabledNoSuchMethod) { |
753 // Emit the noSuchMethod handlers on the Object prototype now, | 756 // Emit the noSuchMethod handlers on the Object prototype now, |
754 // so that the code in the dynamicFunction helper can find | 757 // so that the code in the dynamicFunction helper can find |
755 // them. Note that this helper is invoked before analyzing the | 758 // them. Note that this helper is invoked before analyzing the |
756 // full JS script. | 759 // full JS script. |
757 if (!nativeEmitter.handleNoSuchMethod) { | 760 if (!nativeEmitter.handleNoSuchMethod) { |
758 emitNoSuchMethodHandlers(defineInstanceMember); | 761 emitNoSuchMethodHandlers(builder.addProperty); |
759 } | 762 } |
760 } | 763 } |
761 } | 764 } |
762 | 765 |
763 void emitRuntimeClassesAndTests(CodeBuffer buffer) { | 766 void emitRuntimeClassesAndTests(CodeBuffer buffer) { |
764 JavaScriptBackend backend = compiler.backend; | 767 JavaScriptBackend backend = compiler.backend; |
765 RuntimeTypeInformation rti = backend.rti; | 768 RuntimeTypeInformation rti = backend.rti; |
766 | 769 |
767 TypeChecks typeChecks = rti.computeRequiredChecks(); | 770 TypeChecks typeChecks = rti.computeRequiredChecks(); |
768 | 771 |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
872 // generate the field getter/setter dynamically. Since this is only | 875 // generate the field getter/setter dynamically. Since this is only |
873 // allowed on fields that are in [classElement] we don't need to visit | 876 // allowed on fields that are in [classElement] we don't need to visit |
874 // superclasses for non-instantiated classes. | 877 // superclasses for non-instantiated classes. |
875 classElement.implementation.forEachInstanceField( | 878 classElement.implementation.forEachInstanceField( |
876 visitField, | 879 visitField, |
877 includeBackendMembers: true, | 880 includeBackendMembers: true, |
878 includeSuperMembers: isInstantiated && !classElement.isNative()); | 881 includeSuperMembers: isInstantiated && !classElement.isNative()); |
879 } | 882 } |
880 | 883 |
881 void generateGetter(Element member, String fieldName, String accessorName, | 884 void generateGetter(Element member, String fieldName, String accessorName, |
882 CodeBuffer buffer) { | 885 ClassBuilder builder) { |
883 String getterName = namer.getterNameFromAccessorName(accessorName); | 886 String getterName = namer.getterNameFromAccessorName(accessorName); |
884 buffer.add("$getterName: function() { return this.$fieldName; }"); | 887 builder.addProperty(getterName, |
888 js.fun([], js.block1(js.return_(js.use('this').dot(fieldName))))); | |
885 } | 889 } |
886 | 890 |
887 void generateSetter(Element member, String fieldName, String accessorName, | 891 void generateSetter(Element member, String fieldName, String accessorName, |
888 CodeBuffer buffer) { | 892 ClassBuilder builder) { |
889 String setterName = namer.setterNameFromAccessorName(accessorName); | 893 String setterName = namer.setterNameFromAccessorName(accessorName); |
890 buffer.add("$setterName: function(v) { this.$fieldName = v; }"); | 894 builder.addProperty(setterName, |
895 js.fun(['v'], | |
896 js.block1( | |
897 new js.ExpressionStatement( | |
898 js.assign(js.use('this').dot(fieldName), js.use('v')))))); | |
891 } | 899 } |
892 | 900 |
893 bool canGenerateCheckedSetter(Element member) { | 901 bool canGenerateCheckedSetter(Element member) { |
894 DartType type = member.computeType(compiler); | 902 DartType type = member.computeType(compiler); |
895 if (type.element.isTypeVariable() | 903 if (type.element.isTypeVariable() |
896 || type.element == compiler.dynamicClass | 904 || type.element == compiler.dynamicClass |
897 || type.element == compiler.objectClass) { | 905 || type.element == compiler.objectClass) { |
898 // TODO(ngeoffray): Support type checks on type parameters. | 906 // TODO(ngeoffray): Support type checks on type parameters. |
899 return false; | 907 return false; |
900 } | 908 } |
901 return true; | 909 return true; |
902 } | 910 } |
903 | 911 |
904 void generateCheckedSetter(Element member, | 912 void generateCheckedSetter(Element member, |
905 String fieldName, | 913 String fieldName, |
906 String accessorName, | 914 String accessorName, |
907 CodeBuffer buffer) { | 915 ClassBuilder builder) { |
908 assert(canGenerateCheckedSetter(member)); | 916 assert(canGenerateCheckedSetter(member)); |
909 DartType type = member.computeType(compiler); | 917 DartType type = member.computeType(compiler); |
910 SourceString helper = compiler.backend.getCheckedModeHelper(type); | 918 SourceString helper = compiler.backend.getCheckedModeHelper(type); |
911 FunctionElement helperElement = compiler.findHelper(helper); | 919 FunctionElement helperElement = compiler.findHelper(helper); |
912 String helperName = namer.isolateAccess(helperElement); | 920 String helperName = namer.isolateAccess(helperElement); |
913 String additionalArgument = ''; | 921 List<js.Expression> arguments = <js.Expression>[js.use('v')]; |
914 if (helperElement.computeSignature(compiler).parameterCount != 1) { | 922 if (helperElement.computeSignature(compiler).parameterCount != 1) { |
915 additionalArgument = ",$_'${namer.operatorIs(type.element)}'"; | 923 arguments.add(js.string(namer.operatorIs(type.element))); |
floitsch
2013/01/04 10:31:03
maybe add an "assert" that the result of 'namer.op
| |
916 } | 924 } |
925 | |
917 String setterName = namer.setterNameFromAccessorName(accessorName); | 926 String setterName = namer.setterNameFromAccessorName(accessorName); |
918 buffer.add("$setterName:${_}function(v)$_{$_" | 927 builder.addProperty(setterName, |
919 "this.$fieldName$_=$_$helperName(v$additionalArgument);}"); | 928 js.fun(['v'], |
929 js.block1( | |
930 new js.ExpressionStatement( | |
931 js.assign( | |
932 js.use('this').dot(fieldName), | |
933 js.call(js.use(helperName), arguments)))))); | |
920 } | 934 } |
921 | 935 |
922 void emitClassConstructor(ClassElement classElement, CodeBuffer buffer) { | 936 void emitClassConstructor(ClassElement classElement, ClassBuilder builder) { |
923 /* Do nothing. */ | 937 /* Do nothing. */ |
924 } | 938 } |
925 | 939 |
926 void emitSuper(String superName, CodeBuffer buffer) { | 940 void emitSuper(String superName, ClassBuilder builder) { |
927 /* Do nothing. */ | 941 /* Do nothing. */ |
928 } | 942 } |
929 | 943 |
930 void emitClassFields(ClassElement classElement, | 944 void emitClassFields(ClassElement classElement, |
931 CodeBuffer buffer, | 945 ClassBuilder builder, |
932 bool emitEndingComma, | |
933 { String superClass: "", | 946 { String superClass: "", |
934 bool classIsNative: false}) { | 947 bool classIsNative: false}) { |
935 bool isFirstField = true; | 948 bool isFirstField = true; |
936 bool isAnythingOutput = false; | 949 StringBuffer buffer = new StringBuffer(); |
937 if (!classIsNative) { | 950 if (!classIsNative) { |
938 buffer.add('"":"$superClass;'); | 951 buffer.add('$superClass;'); |
939 isAnythingOutput = true; | |
940 } | 952 } |
941 visitClassFields(classElement, (Element member, | 953 visitClassFields(classElement, (Element member, |
942 String name, | 954 String name, |
943 String accessorName, | 955 String accessorName, |
944 bool needsGetter, | 956 bool needsGetter, |
945 bool needsSetter, | 957 bool needsSetter, |
946 bool needsCheckedSetter) { | 958 bool needsCheckedSetter) { |
947 // Ignore needsCheckedSetter - that is handled below. | 959 // Ignore needsCheckedSetter - that is handled below. |
948 bool needsAccessor = (needsGetter || needsSetter); | 960 bool needsAccessor = (needsGetter || needsSetter); |
949 // We need to output the fields for non-native classes so we can auto- | 961 // We need to output the fields for non-native classes so we can auto- |
950 // generate the constructor. For native classes there are no | 962 // generate the constructor. For native classes there are no |
951 // constructors, so we don't need the fields unless we are generating | 963 // constructors, so we don't need the fields unless we are generating |
952 // accessors at runtime. | 964 // accessors at runtime. |
953 if (!classIsNative || needsAccessor) { | 965 if (!classIsNative || needsAccessor) { |
954 // Emit correct commas. | 966 // Emit correct commas. |
955 if (isFirstField) { | 967 if (isFirstField) { |
956 isFirstField = false; | 968 isFirstField = false; |
957 if (!isAnythingOutput) { | |
958 buffer.add('"":"'); | |
959 isAnythingOutput = true; | |
960 } | |
961 } else { | 969 } else { |
962 buffer.add(","); | 970 buffer.add(','); |
963 } | 971 } |
964 int flag = 0; | 972 int flag = 0; |
965 if (!needsAccessor) { | 973 if (!needsAccessor) { |
966 // Emit field for constructor generation. | 974 // Emit field for constructor generation. |
967 assert(!classIsNative); | 975 assert(!classIsNative); |
968 buffer.add(name); | 976 buffer.add(name); |
969 } else { | 977 } else { |
970 // Emit (possibly renaming) field name so we can add accessors at | 978 // Emit (possibly renaming) field name so we can add accessors at |
971 // runtime. | 979 // runtime. |
972 buffer.add(accessorName); | 980 buffer.add(accessorName); |
973 if (name != accessorName) { | 981 if (name != accessorName) { |
974 buffer.add(':$name'); | 982 buffer.add(':$name'); |
975 // Only the native classes can have renaming accessors. | 983 // Only the native classes can have renaming accessors. |
976 assert(classIsNative); | 984 assert(classIsNative); |
977 flag = RENAMING_FLAG; | 985 flag = RENAMING_FLAG; |
978 } | 986 } |
979 } | 987 } |
980 if (needsGetter && needsSetter) { | 988 if (needsGetter && needsSetter) { |
981 buffer.addCharCode(GETTER_SETTER_CODE + flag); | 989 buffer.addCharCode(GETTER_SETTER_CODE + flag); |
982 } else if (needsGetter) { | 990 } else if (needsGetter) { |
983 buffer.addCharCode(GETTER_CODE + flag); | 991 buffer.addCharCode(GETTER_CODE + flag); |
984 } else if (needsSetter) { | 992 } else if (needsSetter) { |
985 buffer.addCharCode(SETTER_CODE + flag); | 993 buffer.addCharCode(SETTER_CODE + flag); |
986 } | 994 } |
987 } | 995 } |
988 }); | 996 }); |
989 if (isAnythingOutput) { | 997 |
990 buffer.add('"'); | 998 String compactClassData = buffer.toString(); |
991 if (emitEndingComma) { | 999 if (compactClassData.length > 0) { |
992 buffer.add(','); | 1000 builder.addProperty('', js.string(compactClassData)); |
993 } | |
994 } | 1001 } |
995 } | 1002 } |
996 | 1003 |
997 /** Each getter/setter must be prefixed with a ",\n ". */ | |
998 void emitClassGettersSetters(ClassElement classElement, | 1004 void emitClassGettersSetters(ClassElement classElement, |
999 CodeBuffer buffer, | 1005 ClassBuilder builder) { |
1000 bool emitLeadingComma) { | |
1001 emitComma() { | |
1002 if (emitLeadingComma) { | |
1003 buffer.add(",\n$_"); | |
1004 } else { | |
1005 emitLeadingComma = true; | |
1006 } | |
1007 } | |
1008 | 1006 |
1009 visitClassFields(classElement, (Element member, | 1007 visitClassFields(classElement, (Element member, |
1010 String name, | 1008 String name, |
1011 String accessorName, | 1009 String accessorName, |
1012 bool needsGetter, | 1010 bool needsGetter, |
1013 bool needsSetter, | 1011 bool needsSetter, |
1014 bool needsCheckedSetter) { | 1012 bool needsCheckedSetter) { |
1015 if (needsCheckedSetter) { | 1013 if (needsCheckedSetter) { |
1016 assert(!needsSetter); | 1014 assert(!needsSetter); |
1017 emitComma(); | 1015 generateCheckedSetter(member, name, accessorName, builder); |
1018 generateCheckedSetter(member, name, accessorName, buffer); | |
1019 } | 1016 } |
1020 if (!getterAndSetterCanBeImplementedByFieldSpec) { | 1017 if (!getterAndSetterCanBeImplementedByFieldSpec) { |
1021 if (needsGetter) { | 1018 if (needsGetter) { |
1022 emitComma(); | 1019 generateGetter(member, name, accessorName, builder); |
1023 generateGetter(member, name, accessorName, buffer); | |
1024 } | 1020 } |
1025 if (needsSetter) { | 1021 if (needsSetter) { |
1026 emitComma(); | 1022 generateSetter(member, name, accessorName, builder); |
1027 generateSetter(member, name, accessorName, buffer); | |
1028 } | 1023 } |
1029 } | 1024 } |
1030 }); | 1025 }); |
1031 } | 1026 } |
1032 | 1027 |
1033 /** | 1028 /** |
1034 * Documentation wanted -- johnniwinther | 1029 * Documentation wanted -- johnniwinther |
1035 * | 1030 * |
1036 * Invariant: [classElement] must be a declaration element. | 1031 * Invariant: [classElement] must be a declaration element. |
1037 */ | 1032 */ |
(...skipping 10 matching lines...) Expand all Loading... | |
1048 } | 1043 } |
1049 | 1044 |
1050 needsDefineClass = true; | 1045 needsDefineClass = true; |
1051 String className = namer.getName(classElement); | 1046 String className = namer.getName(classElement); |
1052 ClassElement superclass = classElement.superclass; | 1047 ClassElement superclass = classElement.superclass; |
1053 String superName = ""; | 1048 String superName = ""; |
1054 if (superclass != null) { | 1049 if (superclass != null) { |
1055 superName = namer.getName(superclass); | 1050 superName = namer.getName(superclass); |
1056 } | 1051 } |
1057 | 1052 |
1058 buffer.add('$classesCollector.$className$_=$_{'); | 1053 ClassBuilder builder = new ClassBuilder(); |
1059 emitClassConstructor(classElement, buffer); | 1054 |
1060 emitSuper(superName, buffer); | 1055 emitClassConstructor(classElement, builder); |
1061 emitClassFields(classElement, buffer, false, | 1056 emitSuper(superName, builder); |
1057 emitClassFields(classElement, builder, | |
1062 superClass: superName, classIsNative: false); | 1058 superClass: superName, classIsNative: false); |
1063 // TODO(floitsch): the emitInstanceMember should simply always emit a ',\n'. | 1059 emitClassGettersSetters(classElement, builder); |
1064 // That does currently not work because the native classes have a different | 1060 emitInstanceMembers(classElement, builder); |
1065 // syntax. | 1061 |
1066 emitClassGettersSetters(classElement, buffer, true); | 1062 js.Expression init = |
1067 emitInstanceMembers(classElement, buffer, true); | 1063 js.assign( |
1068 buffer.add('$n}$N$n'); | 1064 js.use(classesCollector).dot(className), |
1065 builder.toObjectInitializer()); | |
1066 buffer.add(js.prettyPrint(init, compiler)); | |
1067 buffer.add('$N$n'); | |
1069 } | 1068 } |
1070 | 1069 |
1071 bool get getterAndSetterCanBeImplementedByFieldSpec => true; | 1070 bool get getterAndSetterCanBeImplementedByFieldSpec => true; |
1072 | 1071 |
1073 void emitInterceptorMethods( | 1072 void emitInterceptorMethods(ClassBuilder builder) { |
1074 void defineInstanceMember(String name, StringBuffer memberBuffer)) { | |
1075 JavaScriptBackend backend = compiler.backend; | 1073 JavaScriptBackend backend = compiler.backend; |
1076 // Emit forwarders for the ObjectInterceptor class. We need to | 1074 // Emit forwarders for the ObjectInterceptor class. We need to |
1077 // emit all possible sends on intercepted methods. | 1075 // emit all possible sends on intercepted methods. |
1078 for (Selector selector in backend.usedInterceptors) { | 1076 for (Selector selector in backend.usedInterceptors) { |
1079 | 1077 |
1080 List<js.Parameter> parameters = <js.Parameter>[]; | 1078 List<js.Parameter> parameters = <js.Parameter>[]; |
1081 List<js.Expression> arguments = <js.Expression>[]; | 1079 List<js.Expression> arguments = <js.Expression>[]; |
1082 parameters.add(new js.Parameter('receiver')); | 1080 parameters.add(new js.Parameter('receiver')); |
1083 | 1081 |
1084 String name; | 1082 String name; |
(...skipping 13 matching lines...) Expand all Loading... | |
1098 } | 1096 } |
1099 } | 1097 } |
1100 js.Fun function = | 1098 js.Fun function = |
1101 new js.Fun(parameters, | 1099 new js.Fun(parameters, |
1102 new js.Block( | 1100 new js.Block( |
1103 <js.Statement>[ | 1101 <js.Statement>[ |
1104 new js.Return( | 1102 new js.Return( |
1105 new js.VariableUse('receiver') | 1103 new js.VariableUse('receiver') |
1106 .dot(name) | 1104 .dot(name) |
1107 .callWith(arguments))])); | 1105 .callWith(arguments))])); |
1108 | 1106 builder.addProperty(name, function); |
1109 CodeBuffer code = new CodeBuffer(); | |
1110 code.add(js.prettyPrint(function, compiler)); | |
1111 defineInstanceMember(name, code); | |
1112 } | 1107 } |
1113 } | 1108 } |
1114 | 1109 |
1115 Collection<Element> getTypedefChecksOn(DartType type) { | 1110 Collection<Element> getTypedefChecksOn(DartType type) { |
1116 return checkedTypedefs.filter((TypedefElement typedef) { | 1111 return checkedTypedefs.filter((TypedefElement typedef) { |
1117 FunctionType typedefType = | 1112 FunctionType typedefType = |
1118 typedef.computeType(compiler).unalias(compiler); | 1113 typedef.computeType(compiler).unalias(compiler); |
1119 return compiler.types.isSubtype(type, typedefType); | 1114 return compiler.types.isSubtype(type, typedefType); |
1120 }); | 1115 }); |
1121 } | 1116 } |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1267 } | 1262 } |
1268 | 1263 |
1269 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) { | 1264 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) { |
1270 if (needsDefineClass) { | 1265 if (needsDefineClass) { |
1271 buffer.add("$finishClassesName($classesCollector)$N"); | 1266 buffer.add("$finishClassesName($classesCollector)$N"); |
1272 // Reset the map. | 1267 // Reset the map. |
1273 buffer.add("$classesCollector$_=$_{}$N"); | 1268 buffer.add("$classesCollector$_=$_{}$N"); |
1274 } | 1269 } |
1275 } | 1270 } |
1276 | 1271 |
1277 void emitStaticFunctionWithNamer(CodeBuffer buffer, | 1272 void emitStaticFunction(CodeBuffer buffer, |
1278 Element element, | 1273 String name, |
1279 CodeBuffer functionBuffer, | 1274 js.Expression functionExpression) { |
1280 String functionNamer(Element element)) { | 1275 js.Expression assignment = |
1281 String functionName = functionNamer(element); | 1276 js.assign(js.use(isolateProperties).dot(name), functionExpression); |
1282 buffer.add('$isolateProperties.$functionName$_=$_'); | 1277 buffer.add(js.prettyPrint(assignment, compiler)); |
1283 buffer.add(functionBuffer); | |
1284 buffer.add('$N$n'); | 1278 buffer.add('$N$n'); |
1285 } | 1279 } |
1286 | 1280 |
1287 void emitStaticFunctions(CodeBuffer buffer) { | 1281 void emitStaticFunctions(CodeBuffer buffer) { |
1288 bool isStaticFunction(Element element) => | 1282 bool isStaticFunction(Element element) => |
1289 !element.isInstanceMember() && !element.isField(); | 1283 !element.isInstanceMember() && !element.isField(); |
1290 | 1284 |
1291 Collection<Element> elements = | 1285 Collection<Element> elements = |
1292 compiler.codegenWorld.generatedCode.keys.filter(isStaticFunction); | 1286 compiler.codegenWorld.generatedCode.keys.filter(isStaticFunction); |
1293 Set<Element> pendingElementsWithBailouts = | 1287 Set<Element> pendingElementsWithBailouts = |
1294 new Set<Element>.from( | 1288 new Set<Element>.from( |
1295 compiler.codegenWorld.generatedBailoutCode.keys.filter( | 1289 compiler.codegenWorld.generatedBailoutCode.keys.filter( |
1296 isStaticFunction)); | 1290 isStaticFunction)); |
1297 | 1291 |
1298 for (Element element in Elements.sortedByPosition(elements)) { | 1292 for (Element element in Elements.sortedByPosition(elements)) { |
1299 CodeBuffer code = compiler.codegenWorld.generatedCode[element]; | 1293 js.Expression code = compiler.codegenWorld.generatedCode[element]; |
1300 emitStaticFunctionWithNamer(buffer, element, code, namer.getName); | 1294 emitStaticFunction(buffer, namer.getName(element), code); |
1301 CodeBuffer bailoutCode = | 1295 js.Expression bailoutCode = |
1302 compiler.codegenWorld.generatedBailoutCode[element]; | 1296 compiler.codegenWorld.generatedBailoutCode[element]; |
1303 if (bailoutCode != null) { | 1297 if (bailoutCode != null) { |
1304 pendingElementsWithBailouts.remove(element); | 1298 pendingElementsWithBailouts.remove(element); |
1305 emitStaticFunctionWithNamer( | 1299 emitStaticFunction(buffer, namer.getBailoutName(element), bailoutCode); |
1306 buffer, element, bailoutCode, namer.getBailoutName); | |
1307 } | 1300 } |
1308 } | 1301 } |
1309 | 1302 |
1310 // Is it possible the primary function was inlined but the bailout was not? | 1303 // Is it possible the primary function was inlined but the bailout was not? |
1311 for (Element element in | 1304 for (Element element in |
1312 Elements.sortedByPosition(pendingElementsWithBailouts)) { | 1305 Elements.sortedByPosition(pendingElementsWithBailouts)) { |
1313 CodeBuffer bailoutCode = | 1306 CodeBuffer bailoutCode = |
1314 compiler.codegenWorld.generatedBailoutCode[element]; | 1307 compiler.codegenWorld.generatedBailoutCode[element]; |
1315 emitStaticFunctionWithNamer( | 1308 emitStaticFunction(buffer, namer.getBailoutName(element), bailoutCode); |
1316 buffer, element, bailoutCode, namer.getBailoutName); | |
1317 } | 1309 } |
1318 } | 1310 } |
1319 | 1311 |
1320 void emitStaticFunctionGetters(CodeBuffer buffer) { | 1312 void emitStaticFunctionGetters(CodeBuffer buffer) { |
1321 Set<FunctionElement> functionsNeedingGetter = | 1313 Set<FunctionElement> functionsNeedingGetter = |
1322 compiler.codegenWorld.staticFunctionsNeedingGetter; | 1314 compiler.codegenWorld.staticFunctionsNeedingGetter; |
1323 for (FunctionElement element in | 1315 for (FunctionElement element in functionsNeedingGetter) { |
1324 Elements.sortedByPosition(functionsNeedingGetter)) { | |
1325 // The static function does not have the correct name. Since | 1316 // The static function does not have the correct name. Since |
1326 // [addParameterStubs] use the name to create its stubs we simply | 1317 // [addParameterStubs] use the name to create its stubs we simply |
1327 // create a fake element with the correct name. | 1318 // create a fake element with the correct name. |
1328 // Note: the callElement will not have any enclosingElement. | 1319 // Note: the callElement will not have any enclosingElement. |
1329 FunctionElement callElement = | 1320 FunctionElement callElement = |
1330 new ClosureInvocationElement(namer.closureInvocationSelectorName, | 1321 new ClosureInvocationElement(namer.closureInvocationSelectorName, |
1331 element); | 1322 element); |
1332 String staticName = namer.getName(element); | 1323 String staticName = namer.getName(element); |
1333 String invocationName = namer.instanceMethodName(callElement); | 1324 String invocationName = namer.instanceMethodName(callElement); |
1334 String fieldAccess = '$isolateProperties.$staticName'; | 1325 String fieldAccess = '$isolateProperties.$staticName'; |
1335 buffer.add("$fieldAccess.$invocationName$_=$_$fieldAccess$N"); | 1326 buffer.add("$fieldAccess.$invocationName$_=$_$fieldAccess$N"); |
1336 addParameterStubs(callElement, (String name, CodeBuffer value) { | 1327 |
1337 buffer.add('$fieldAccess.$name$_=$_$value$N'); | 1328 addParameterStubs(callElement, (String name, js.Expression value) { |
1329 js.Expression assignment = | |
1330 js.assign( | |
1331 js.use(isolateProperties).dot(staticName).dot(name), | |
1332 value); | |
1333 buffer.add( | |
1334 js.prettyPrint(new js.ExpressionStatement(assignment), compiler)); | |
1335 buffer.add('$N'); | |
1338 }); | 1336 }); |
1337 | |
1339 // If a static function is used as a closure we need to add its name | 1338 // If a static function is used as a closure we need to add its name |
1340 // in case it is used in spawnFunction. | 1339 // in case it is used in spawnFunction. |
1341 String fieldName = namer.STATIC_CLOSURE_NAME_NAME; | 1340 String fieldName = namer.STATIC_CLOSURE_NAME_NAME; |
1342 buffer.add('$fieldAccess.$fieldName$_=$_"$staticName"$N'); | 1341 buffer.add('$fieldAccess.$fieldName$_=$_"$staticName"$N'); |
1343 getTypedefChecksOn(element.computeType(compiler)).forEach( | 1342 getTypedefChecksOn(element.computeType(compiler)).forEach( |
1344 (Element typedef) { | 1343 (Element typedef) { |
1345 String operator = namer.operatorIs(typedef); | 1344 String operator = namer.operatorIs(typedef); |
1346 buffer.add('$fieldAccess.$operator$_=${_}true$N'); | 1345 buffer.add('$fieldAccess.$operator$_=${_}true$N'); |
1347 } | 1346 } |
1348 ); | 1347 ); |
1349 } | 1348 } |
1350 } | 1349 } |
1351 | 1350 |
1352 void emitBoundClosureClassHeader(String mangledName, | 1351 void emitBoundClosureClassHeader(String mangledName, |
1353 String superName, | 1352 String superName, |
1354 List<String> fieldNames, | 1353 List<String> fieldNames, |
1355 CodeBuffer buffer) { | 1354 ClassBuilder builder) { |
1356 buffer.add('$classesCollector.$mangledName$_=$_' | 1355 builder.addProperty('', |
1357 '{"":"$superName;${Strings.join(fieldNames,',')}",'); | 1356 js.string("$superName;${Strings.join(fieldNames,',')}")); |
1358 } | 1357 } |
1359 | 1358 |
1360 /** | 1359 /** |
1361 * Documentation wanted -- johnniwinther | 1360 * Documentation wanted -- johnniwinther |
1362 * | 1361 * |
1363 * Invariant: [member] must be a declaration element. | 1362 * Invariant: [member] must be a declaration element. |
1364 */ | 1363 */ |
1365 void emitDynamicFunctionGetter(FunctionElement member, | 1364 void emitDynamicFunctionGetter(FunctionElement member, |
1366 DefineMemberFunction defineInstanceMember) { | 1365 DefineStubFunction defineStub) { |
1367 assert(invariant(member, member.isDeclaration)); | 1366 assert(invariant(member, member.isDeclaration)); |
1368 // For every method that has the same name as a property-get we create a | 1367 // For every method that has the same name as a property-get we create a |
1369 // getter that returns a bound closure. Say we have a class 'A' with method | 1368 // getter that returns a bound closure. Say we have a class 'A' with method |
1370 // 'foo' and somewhere in the code there is a dynamic property get of | 1369 // 'foo' and somewhere in the code there is a dynamic property get of |
1371 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): | 1370 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): |
1372 // | 1371 // |
1373 // class A { | 1372 // class A { |
1374 // foo(x, y, z) { ... } // Original function. | 1373 // foo(x, y, z) { ... } // Original function. |
1375 // get foo { return new BoundClosure499(this, "foo"); } | 1374 // get foo { return new BoundClosure499(this, "foo"); } |
1376 // } | 1375 // } |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1419 // Create a new closure class. | 1418 // Create a new closure class. |
1420 SourceString name = const SourceString("BoundClosure"); | 1419 SourceString name = const SourceString("BoundClosure"); |
1421 ClassElement closureClassElement = new ClosureClassElement( | 1420 ClassElement closureClassElement = new ClosureClassElement( |
1422 name, compiler, member, member.getCompilationUnit()); | 1421 name, compiler, member, member.getCompilationUnit()); |
1423 String mangledName = namer.getName(closureClassElement); | 1422 String mangledName = namer.getName(closureClassElement); |
1424 String superName = namer.getName(closureClassElement.superclass); | 1423 String superName = namer.getName(closureClassElement.superclass); |
1425 needsClosureClass = true; | 1424 needsClosureClass = true; |
1426 | 1425 |
1427 // Define the constructor with a name so that Object.toString can | 1426 // Define the constructor with a name so that Object.toString can |
1428 // find the class name of the closure class. | 1427 // find the class name of the closure class. |
1428 ClassBuilder boundClosureBuilder = new ClassBuilder(); | |
1429 emitBoundClosureClassHeader( | 1429 emitBoundClosureClassHeader( |
1430 mangledName, superName, fieldNames, boundClosureBuffer); | 1430 mangledName, superName, fieldNames, boundClosureBuilder); |
1431 // Now add the methods on the closure class. The instance method does not | 1431 // Now add the methods on the closure class. The instance method does not |
1432 // have the correct name. Since [addParameterStubs] use the name to create | 1432 // have the correct name. Since [addParameterStubs] use the name to create |
1433 // its stubs we simply create a fake element with the correct name. | 1433 // its stubs we simply create a fake element with the correct name. |
1434 // Note: the callElement will not have any enclosingElement. | 1434 // Note: the callElement will not have any enclosingElement. |
1435 FunctionElement callElement = | 1435 FunctionElement callElement = |
1436 new ClosureInvocationElement(namer.closureInvocationSelectorName, | 1436 new ClosureInvocationElement(namer.closureInvocationSelectorName, |
1437 member); | 1437 member); |
1438 | 1438 |
1439 String invocationName = namer.instanceMethodName(callElement); | 1439 String invocationName = namer.instanceMethodName(callElement); |
1440 | 1440 |
(...skipping 10 matching lines...) Expand all Loading... | |
1451 | 1451 |
1452 js.Expression fun = | 1452 js.Expression fun = |
1453 new js.Fun(parameters, | 1453 new js.Fun(parameters, |
1454 new js.Block( | 1454 new js.Block( |
1455 <js.Statement>[ | 1455 <js.Statement>[ |
1456 new js.Return( | 1456 new js.Return( |
1457 new js.PropertyAccess( | 1457 new js.PropertyAccess( |
1458 new js.This().dot(fieldNames[0]), | 1458 new js.This().dot(fieldNames[0]), |
1459 new js.This().dot(fieldNames[1])) | 1459 new js.This().dot(fieldNames[1])) |
1460 .callWith(arguments))])); | 1460 .callWith(arguments))])); |
1461 boundClosureBuilder.addProperty(invocationName, fun); | |
1461 | 1462 |
1462 boundClosureBuffer.add( | 1463 addParameterStubs(callElement, boundClosureBuilder.addProperty); |
1463 '$_$invocationName:$_${js.prettyPrint(fun,compiler)}'); | 1464 typedefChecks.forEach((Element typedef) { |
1464 | 1465 String operator = namer.operatorIs(typedef); |
1465 addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) { | 1466 boundClosureBuilder.addProperty(operator, new js.LiteralBool(true)); |
1466 boundClosureBuffer.add(',\n$_$stubName:$_$memberValue'); | |
1467 }); | 1467 }); |
1468 | 1468 |
1469 typedefChecks.forEach((Element typedef) { | 1469 js.Expression init = |
1470 String operator = namer.operatorIs(typedef); | 1470 js.assign( |
1471 boundClosureBuffer.add(',\n$_$operator$_:${_}true'); | 1471 js.use(classesCollector).dot(mangledName), |
1472 }); | 1472 boundClosureBuilder.toObjectInitializer()); |
1473 | 1473 boundClosureBuffer.add(js.prettyPrint(init, compiler)); |
1474 boundClosureBuffer.add("$n}$N"); | 1474 boundClosureBuffer.add("$N"); |
1475 | 1475 |
1476 closureClass = namer.isolateAccess(closureClassElement); | 1476 closureClass = namer.isolateAccess(closureClassElement); |
1477 | 1477 |
1478 // Cache it. | 1478 // Cache it. |
1479 if (canBeShared) { | 1479 if (canBeShared) { |
1480 cache[parameterCount] = closureClass; | 1480 cache[parameterCount] = closureClass; |
1481 } | 1481 } |
1482 } | 1482 } |
1483 | 1483 |
1484 // And finally the getter. | 1484 // And finally the getter. |
1485 String getterName = namer.getterName(member.getLibrary(), member.name); | 1485 String getterName = namer.getterName(member.getLibrary(), member.name); |
1486 String targetName = namer.instanceMethodName(member); | 1486 String targetName = namer.instanceMethodName(member); |
1487 | 1487 |
1488 List<js.Parameter> parameters = <js.Parameter>[]; | 1488 List<js.Parameter> parameters = <js.Parameter>[]; |
1489 List<js.Expression> arguments = <js.Expression>[]; | 1489 List<js.Expression> arguments = <js.Expression>[]; |
1490 arguments.add(new js.This()); | 1490 arguments.add(new js.This()); |
1491 arguments.add(new js.LiteralString("'$targetName'")); | 1491 arguments.add(js.string(targetName)); |
1492 if (inInterceptor) { | 1492 if (inInterceptor) { |
1493 parameters.add(new js.Parameter(extraArg)); | 1493 parameters.add(new js.Parameter(extraArg)); |
1494 arguments.add(new js.VariableUse(extraArg)); | 1494 arguments.add(new js.VariableUse(extraArg)); |
1495 } | 1495 } |
1496 | 1496 |
1497 js.Expression getterFunction = | 1497 js.Expression getterFunction = |
1498 new js.Fun(parameters, | 1498 new js.Fun(parameters, |
1499 new js.Block( | 1499 new js.Block( |
1500 <js.Statement>[ | 1500 <js.Statement>[ |
1501 new js.Return( | 1501 new js.Return( |
1502 new js.New( | 1502 new js.New( |
1503 new js.VariableUse(closureClass), | 1503 new js.VariableUse(closureClass), |
1504 arguments))])); | 1504 arguments))])); |
1505 | 1505 |
1506 CodeBuffer getterBuffer = new CodeBuffer(); | 1506 defineStub(getterName, getterFunction); |
1507 getterBuffer.add(js.prettyPrint(getterFunction, compiler)); | |
1508 defineInstanceMember(getterName, getterBuffer); | |
1509 } | 1507 } |
1510 | 1508 |
1511 /** | 1509 /** |
1512 * Documentation wanted -- johnniwinther | 1510 * Documentation wanted -- johnniwinther |
1513 * | 1511 * |
1514 * Invariant: [member] must be a declaration element. | 1512 * Invariant: [member] must be a declaration element. |
1515 */ | 1513 */ |
1516 void emitCallStubForGetter(Element member, | 1514 void emitCallStubForGetter(Element member, |
1517 Set<Selector> selectors, | 1515 Set<Selector> selectors, |
1518 DefineMemberFunction defineInstanceMember) { | 1516 DefineStubFunction defineStub) { |
1519 assert(invariant(member, member.isDeclaration)); | 1517 assert(invariant(member, member.isDeclaration)); |
1520 LibraryElement memberLibrary = member.getLibrary(); | 1518 LibraryElement memberLibrary = member.getLibrary(); |
1521 JavaScriptBackend backend = compiler.backend; | 1519 JavaScriptBackend backend = compiler.backend; |
1522 // If the class is an interceptor class, the stub gets the | 1520 // If the class is an interceptor class, the stub gets the |
1523 // receiver explicitely and we need to pass it to the getter call. | 1521 // receiver explicitely and we need to pass it to the getter call. |
1524 bool isInterceptorClass = | 1522 bool isInterceptorClass = |
1525 backend.isInterceptorClass(member.getEnclosingClass()); | 1523 backend.isInterceptorClass(member.getEnclosingClass()); |
1526 | 1524 |
1527 const String receiverArgumentName = r'$receiver'; | 1525 const String receiverArgumentName = r'$receiver'; |
1528 | 1526 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1564 } | 1562 } |
1565 | 1563 |
1566 js.Fun function = | 1564 js.Fun function = |
1567 new js.Fun(parameters, | 1565 new js.Fun(parameters, |
1568 new js.Block( | 1566 new js.Block( |
1569 <js.Statement>[ | 1567 <js.Statement>[ |
1570 new js.Return( | 1568 new js.Return( |
1571 buildGetter().dot(closureCallName) | 1569 buildGetter().dot(closureCallName) |
1572 .callWith(arguments))])); | 1570 .callWith(arguments))])); |
1573 | 1571 |
1574 CodeBuffer getterBuffer = new CodeBuffer(); | 1572 defineStub(invocationName, function); |
1575 getterBuffer.add(js.prettyPrint(function, compiler)); | |
1576 defineInstanceMember(invocationName, getterBuffer); | |
1577 } | 1573 } |
1578 } | 1574 } |
1579 } | 1575 } |
1580 | 1576 |
1581 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { | 1577 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { |
1582 ConstantHandler handler = compiler.constantHandler; | 1578 ConstantHandler handler = compiler.constantHandler; |
1583 List<VariableElement> staticNonFinalFields = | 1579 List<VariableElement> staticNonFinalFields = |
1584 handler.getStaticNonFinalFieldsForEmission(); | 1580 handler.getStaticNonFinalFieldsForEmission(); |
1585 for (Element element in staticNonFinalFields) { | 1581 for (Element element in staticNonFinalFields) { |
1586 compiler.withCurrentElement(element, () { | 1582 compiler.withCurrentElement(element, () { |
(...skipping 11 matching lines...) Expand all Loading... | |
1598 } | 1594 } |
1599 | 1595 |
1600 void emitLazilyInitializedStaticFields(CodeBuffer buffer) { | 1596 void emitLazilyInitializedStaticFields(CodeBuffer buffer) { |
1601 ConstantHandler handler = compiler.constantHandler; | 1597 ConstantHandler handler = compiler.constantHandler; |
1602 List<VariableElement> lazyFields = | 1598 List<VariableElement> lazyFields = |
1603 handler.getLazilyInitializedFieldsForEmission(); | 1599 handler.getLazilyInitializedFieldsForEmission(); |
1604 if (!lazyFields.isEmpty) { | 1600 if (!lazyFields.isEmpty) { |
1605 needsLazyInitializer = true; | 1601 needsLazyInitializer = true; |
1606 for (VariableElement element in lazyFields) { | 1602 for (VariableElement element in lazyFields) { |
1607 assert(compiler.codegenWorld.generatedBailoutCode[element] == null); | 1603 assert(compiler.codegenWorld.generatedBailoutCode[element] == null); |
1608 StringBuffer code = compiler.codegenWorld.generatedCode[element]; | 1604 js.Expression code = compiler.codegenWorld.generatedCode[element]; |
1609 assert(code != null); | 1605 assert(code != null); |
1610 // The code only computes the initial value. We build the lazy-check | 1606 // The code only computes the initial value. We build the lazy-check |
1611 // here: | 1607 // here: |
1612 // lazyInitializer(prototype, 'name', fieldName, getterName, initial); | 1608 // lazyInitializer(prototype, 'name', fieldName, getterName, initial); |
1613 // The name is used for error reporting. The 'initial' must be a | 1609 // The name is used for error reporting. The 'initial' must be a |
1614 // closure that constructs the initial value. | 1610 // closure that constructs the initial value. |
1615 buffer.add("$lazyInitializerName("); | 1611 buffer.add("$lazyInitializerName("); |
1616 buffer.add(isolateProperties); | 1612 buffer.add(isolateProperties); |
1617 buffer.add(",$_'"); | 1613 buffer.add(",$_'"); |
1618 buffer.add(element.name.slowToString()); | 1614 buffer.add(element.name.slowToString()); |
1619 buffer.add("',$_'"); | 1615 buffer.add("',$_'"); |
1620 buffer.add(namer.getName(element)); | 1616 buffer.add(namer.getName(element)); |
1621 buffer.add("',$_'"); | 1617 buffer.add("',$_'"); |
1622 buffer.add(namer.getLazyInitializerName(element)); | 1618 buffer.add(namer.getLazyInitializerName(element)); |
1623 buffer.add("',$_"); | 1619 buffer.add("',$_"); |
1624 buffer.add(code); | 1620 buffer.add(js.prettyPrint(code, compiler)); |
1625 emitLazyInitializedGetter(element, buffer); | 1621 emitLazyInitializedGetter(element, buffer); |
1626 buffer.add(")$N"); | 1622 buffer.add(")$N"); |
1627 } | 1623 } |
1628 } | 1624 } |
1629 } | 1625 } |
1630 | 1626 |
1631 void emitLazyInitializedGetter(VariableElement element, CodeBuffer buffer) { | 1627 void emitLazyInitializedGetter(VariableElement element, CodeBuffer buffer) { |
1632 // Nothing to do, the 'lazy' function will create the getter. | 1628 // Nothing to do, the 'lazy' function will create the getter. |
1633 } | 1629 } |
1634 | 1630 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1670 return list; | 1666 return list; |
1671 }; | 1667 }; |
1672 '''); | 1668 '''); |
1673 } | 1669 } |
1674 | 1670 |
1675 /** | 1671 /** |
1676 * Documentation wanted -- johnniwinther | 1672 * Documentation wanted -- johnniwinther |
1677 * | 1673 * |
1678 * Invariant: [member] must be a declaration element. | 1674 * Invariant: [member] must be a declaration element. |
1679 */ | 1675 */ |
1680 void emitExtraAccessors(Element member, | 1676 void emitExtraAccessors(Element member, ClassBuilder builder) { |
1681 DefineMemberFunction defineInstanceMember) { | |
1682 assert(invariant(member, member.isDeclaration)); | 1677 assert(invariant(member, member.isDeclaration)); |
1683 if (member.isGetter() || member.isField()) { | 1678 if (member.isGetter() || member.isField()) { |
1684 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 1679 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; |
1685 if (selectors != null && !selectors.isEmpty) { | 1680 if (selectors != null && !selectors.isEmpty) { |
1686 emitCallStubForGetter(member, selectors, defineInstanceMember); | 1681 emitCallStubForGetter(member, selectors, builder.addProperty); |
1687 } | 1682 } |
1688 } else if (member.isFunction()) { | 1683 } else if (member.isFunction()) { |
1689 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { | 1684 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { |
1690 emitDynamicFunctionGetter(member, defineInstanceMember); | 1685 emitDynamicFunctionGetter(member, builder.addProperty); |
1691 } | 1686 } |
1692 } | 1687 } |
1693 } | 1688 } |
1694 | 1689 |
1695 void emitNoSuchMethodHandlers(DefineMemberFunction defineInstanceMember) { | 1690 void emitNoSuchMethodHandlers(DefineStubFunction defineStub) { |
1696 // Do not generate no such method handlers if there is no class. | 1691 // Do not generate no such method handlers if there is no class. |
1697 if (compiler.codegenWorld.instantiatedClasses.isEmpty) return; | 1692 if (compiler.codegenWorld.instantiatedClasses.isEmpty) return; |
1698 | 1693 |
1699 String noSuchMethodName = namer.publicInstanceMethodNameByArity( | 1694 String noSuchMethodName = namer.publicInstanceMethodNameByArity( |
1700 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); | 1695 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); |
1701 | 1696 |
1702 Element createInvocationMirrorElement = | 1697 Element createInvocationMirrorElement = |
1703 compiler.findHelper(const SourceString("createInvocationMirror")); | 1698 compiler.findHelper(const SourceString("createInvocationMirror")); |
1704 String createInvocationMirrorName = | 1699 String createInvocationMirrorName = |
1705 namer.getName(createInvocationMirrorElement); | 1700 namer.getName(createInvocationMirrorElement); |
(...skipping 25 matching lines...) Expand all Loading... | |
1731 int type = selector.invocationMirrorKind; | 1726 int type = selector.invocationMirrorKind; |
1732 String methodName = selector.invocationMirrorMemberName; | 1727 String methodName = selector.invocationMirrorMemberName; |
1733 List<js.Parameter> parameters = <js.Parameter>[]; | 1728 List<js.Parameter> parameters = <js.Parameter>[]; |
1734 CodeBuffer args = new CodeBuffer(); | 1729 CodeBuffer args = new CodeBuffer(); |
1735 for (int i = 0; i < selector.argumentCount; i++) { | 1730 for (int i = 0; i < selector.argumentCount; i++) { |
1736 parameters.add(new js.Parameter('\$$i')); | 1731 parameters.add(new js.Parameter('\$$i')); |
1737 } | 1732 } |
1738 | 1733 |
1739 List<js.Expression> argNames = | 1734 List<js.Expression> argNames = |
1740 selector.getOrderedNamedArguments().map((SourceString name) => | 1735 selector.getOrderedNamedArguments().map((SourceString name) => |
1741 new js.LiteralString('"${name.slowToString()}"')); | 1736 js.string(name.slowToString())); |
1742 | 1737 |
1743 String internalName = namer.invocationMirrorInternalName(selector); | 1738 String internalName = namer.invocationMirrorInternalName(selector); |
1744 | 1739 |
1745 String createInvocationMirror = namer.getName( | 1740 String createInvocationMirror = namer.getName( |
1746 compiler.createInvocationMirrorElement); | 1741 compiler.createInvocationMirrorElement); |
1747 | 1742 |
1748 js.Expression expression = | 1743 js.Expression expression = |
1749 new js.This() | 1744 new js.This() |
1750 .dot(noSuchMethodName) | 1745 .dot(noSuchMethodName) |
1751 .callWith( | 1746 .callWith( |
1752 <js.Expression>[ | 1747 <js.Expression>[ |
1753 new js.VariableUse(namer.CURRENT_ISOLATE) | 1748 new js.VariableUse(namer.CURRENT_ISOLATE) |
1754 .dot(createInvocationMirror) | 1749 .dot(createInvocationMirror) |
1755 .callWith( | 1750 .callWith( |
1756 <js.Expression>[ | 1751 <js.Expression>[ |
1757 new js.LiteralString('"$methodName"'), | 1752 js.string(methodName), |
1758 new js.LiteralString('"$internalName"'), | 1753 js.string(internalName), |
1759 new js.LiteralNumber('$type'), | 1754 new js.LiteralNumber('$type'), |
1760 new js.ArrayInitializer.from( | 1755 new js.ArrayInitializer.from( |
1761 parameters.map((param) => | 1756 parameters.map((param) => js.use(param.name))), |
1762 new js.VariableUse(param.name))), | |
1763 new js.ArrayInitializer.from(argNames)])]); | 1757 new js.ArrayInitializer.from(argNames)])]); |
1764 js.Expression function = | 1758 js.Expression function = |
1765 new js.Fun(parameters, | 1759 new js.Fun(parameters, |
1766 new js.Block(<js.Statement>[new js.Return(expression)])); | 1760 new js.Block(<js.Statement>[new js.Return(expression)])); |
1767 return function; | 1761 return function; |
1768 } | 1762 } |
1769 | 1763 |
1770 void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) { | 1764 void addNoSuchMethodHandlers(SourceString ignore, Set<Selector> selectors) { |
1771 // Cache the object class and type. | 1765 // Cache the object class and type. |
1772 ClassElement objectClass = compiler.objectClass; | 1766 ClassElement objectClass = compiler.objectClass; |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1850 // bar through inheritance. | 1844 // bar through inheritance. |
1851 // | 1845 // |
1852 // If we're calling bar on an object of type A we do need the | 1846 // If we're calling bar on an object of type A we do need the |
1853 // handler because we may have to call B.noSuchMethod since B | 1847 // handler because we may have to call B.noSuchMethod since B |
1854 // does not implement bar. | 1848 // does not implement bar. |
1855 Set<ClassElement> holders = noSuchMethodHoldersFor(receiverType); | 1849 Set<ClassElement> holders = noSuchMethodHoldersFor(receiverType); |
1856 if (holders.every(hasMatchingMember)) continue; | 1850 if (holders.every(hasMatchingMember)) continue; |
1857 String jsName = namer.invocationMirrorInternalName(selector); | 1851 String jsName = namer.invocationMirrorInternalName(selector); |
1858 if (!addedJsNames.contains(jsName)) { | 1852 if (!addedJsNames.contains(jsName)) { |
1859 js.Expression method = generateMethod(jsName, selector); | 1853 js.Expression method = generateMethod(jsName, selector); |
1860 CodeBuffer jsCode = new CodeBuffer(); | 1854 defineStub(jsName, method); |
1861 jsCode.add(js.prettyPrint(method, compiler)); | |
1862 defineInstanceMember(jsName, jsCode); | |
1863 addedJsNames.add(jsName); | 1855 addedJsNames.add(jsName); |
1864 } | 1856 } |
1865 } | 1857 } |
1866 } | 1858 } |
1867 | 1859 |
1868 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); | 1860 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); |
1869 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); | 1861 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); |
1870 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); | 1862 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); |
1871 } | 1863 } |
1872 | 1864 |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2154 '${_}new ${namer.isolateName}()$N'); | 2146 '${_}new ${namer.isolateName}()$N'); |
2155 | 2147 |
2156 nativeEmitter.assembleCode(mainBuffer); | 2148 nativeEmitter.assembleCode(mainBuffer); |
2157 emitMain(mainBuffer); | 2149 emitMain(mainBuffer); |
2158 mainBuffer.add('function init()$_{\n'); | 2150 mainBuffer.add('function init()$_{\n'); |
2159 mainBuffer.add('$isolateProperties$_=$_{}$N'); | 2151 mainBuffer.add('$isolateProperties$_=$_{}$N'); |
2160 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); | 2152 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); |
2161 addLazyInitializerFunctionIfNecessary(mainBuffer); | 2153 addLazyInitializerFunctionIfNecessary(mainBuffer); |
2162 emitFinishIsolateConstructor(mainBuffer); | 2154 emitFinishIsolateConstructor(mainBuffer); |
2163 mainBuffer.add('}\n'); | 2155 mainBuffer.add('}\n'); |
2164 compiler.assembledCode = mainBuffer.toString(); | 2156 compiler.assembledCode = mainBuffer.getText(); |
2165 | 2157 |
2166 if (generateSourceMap) { | 2158 if (generateSourceMap) { |
2167 SourceFile compiledFile = new SourceFile(null, compiler.assembledCode); | 2159 SourceFile compiledFile = new SourceFile(null, compiler.assembledCode); |
2168 String sourceMap = buildSourceMap(mainBuffer, compiledFile); | 2160 String sourceMap = buildSourceMap(mainBuffer, compiledFile); |
2169 // TODO(podivilov): We should find a better way to return source maps to | 2161 // TODO(podivilov): We should find a better way to return source maps to |
2170 // compiler. Using diagnostic handler for that purpose is a temporary | 2162 // compiler. Using diagnostic handler for that purpose is a temporary |
2171 // hack. | 2163 // hack. |
2172 compiler.reportDiagnostic( | 2164 compiler.reportDiagnostic( |
2173 null, sourceMap, new api.Diagnostic(-1, 'source map')); | 2165 null, sourceMap, new api.Diagnostic(-1, 'source map')); |
2174 } | 2166 } |
2175 }); | 2167 }); |
2176 return compiler.assembledCode; | 2168 return compiler.assembledCode; |
2177 } | 2169 } |
2178 | 2170 |
2179 String buildSourceMap(CodeBuffer buffer, SourceFile compiledFile) { | 2171 String buildSourceMap(CodeBuffer buffer, SourceFile compiledFile) { |
2180 SourceMapBuilder sourceMapBuilder = new SourceMapBuilder(); | 2172 SourceMapBuilder sourceMapBuilder = new SourceMapBuilder(); |
2181 buffer.forEachSourceLocation(sourceMapBuilder.addMapping); | 2173 buffer.forEachSourceLocation(sourceMapBuilder.addMapping); |
2182 return sourceMapBuilder.build(compiledFile); | 2174 return sourceMapBuilder.build(compiledFile); |
2183 } | 2175 } |
2184 } | 2176 } |
2185 | 2177 |
2186 typedef void DefineMemberFunction(String invocationName, CodeBuffer definition); | |
2187 | |
2188 const String GENERATED_BY = """ | 2178 const String GENERATED_BY = """ |
2189 // Generated by dart2js, the Dart to JavaScript compiler. | 2179 // Generated by dart2js, the Dart to JavaScript compiler. |
2190 """; | 2180 """; |
2191 const String HOOKS_API_USAGE = """ | 2181 const String HOOKS_API_USAGE = """ |
2192 // The code supports the following hooks: | 2182 // The code supports the following hooks: |
2193 // dartPrint(message) - if this function is defined it is called | 2183 // dartPrint(message) - if this function is defined it is called |
2194 // instead of the Dart [print] method. | 2184 // instead of the Dart [print] method. |
2195 // dartMainRunner(main) - if this function is defined, the Dart [main] | 2185 // dartMainRunner(main) - if this function is defined, the Dart [main] |
2196 // method will not be invoked directly. | 2186 // method will not be invoked directly. |
2197 // Instead, a closure that will invoke [main] is | 2187 // Instead, a closure that will invoke [main] is |
2198 // passed to [dartMainRunner]. | 2188 // passed to [dartMainRunner]. |
2199 """; | 2189 """; |
OLD | NEW |