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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
53 String get defineClassName() | 53 String get defineClassName() |
54 => '${namer.ISOLATE}.\$defineClass'; | 54 => '${namer.ISOLATE}.\$defineClass'; |
55 String get finishClassesName() | 55 String get finishClassesName() |
56 => '${namer.ISOLATE}.\$finishClasses'; | 56 => '${namer.ISOLATE}.\$finishClasses'; |
57 String get finishIsolateConstructorName() | 57 String get finishIsolateConstructorName() |
58 => '${namer.ISOLATE}.\$finishIsolateConstructor'; | 58 => '${namer.ISOLATE}.\$finishIsolateConstructor'; |
59 String get pendingClassesName() | 59 String get pendingClassesName() |
60 => '${namer.ISOLATE}.\$pendingClasses'; | 60 => '${namer.ISOLATE}.\$pendingClasses'; |
61 String get isolatePropertiesName() | 61 String get isolatePropertiesName() |
62 => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}'; | 62 => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}'; |
63 String get supportsProtoName() | |
64 => 'supportsProto'; | |
63 | 65 |
64 final String GETTER_SUFFIX = "?"; | 66 final String GETTER_SUFFIX = "?"; |
65 final String SETTER_SUFFIX = "!"; | 67 final String SETTER_SUFFIX = "!"; |
66 final String GETTER_SETTER_SUFFIX = "="; | 68 final String GETTER_SETTER_SUFFIX = "="; |
67 | 69 |
68 String get generateGetterSetterFunction() { | 70 String get generateGetterSetterFunction() { |
69 return """ | 71 return """ |
70 function(field, prototype) { | 72 function(field, prototype) { |
71 var len = field.length; | 73 var len = field.length; |
72 var lastChar = field[len - 1]; | 74 var lastChar = field[len - 1]; |
73 var needsGetter = lastChar == '$GETTER_SUFFIX' || lastChar == '$GETTER_SETTER_ SUFFIX'; | 75 var needsGetter = lastChar == '$GETTER_SUFFIX' || lastChar == '$GETTER_SETTER_ SUFFIX'; |
74 var needsSetter = lastChar == '$SETTER_SUFFIX' || lastChar == '$GETTER_SETTER_ SUFFIX'; | 76 var needsSetter = lastChar == '$SETTER_SUFFIX' || lastChar == '$GETTER_SETTER_ SUFFIX'; |
75 if (needsGetter || needsSetter) field = field.substring(0, len - 1); | 77 if (needsGetter || needsSetter) field = field.substring(0, len - 1); |
76 if (needsGetter) { | 78 if (needsGetter) { |
77 var getterString = "return this." + field + ";"; | 79 var getterString = "return this." + field + ";"; |
80 """ /* The supportsProtoCheck below depends on the getter/setter convention. | |
81 When changing here, update the protoCheck too. */ """ | |
78 prototype["get\$" + field] = new Function(getterString); | 82 prototype["get\$" + field] = new Function(getterString); |
79 } | 83 } |
80 if (needsSetter) { | 84 if (needsSetter) { |
81 var setterString = "this." + field + " = v;"; | 85 var setterString = "this." + field + " = v;"; |
82 prototype["set\$" + field] = new Function("v", setterString); | 86 prototype["set\$" + field] = new Function("v", setterString); |
83 } | 87 } |
84 return field; | 88 return field; |
85 }"""; | 89 }"""; |
86 } | 90 } |
87 | 91 |
88 String get defineClassFunction() { | 92 String get defineClassFunction() { |
89 // First the class name, then the super class name, followed by the fields | 93 // First the class name, then the super class name, followed by the fields |
90 // (in an array) and the members (inside an Object literal). | 94 // (in an array) and the members (inside an Object literal). |
91 // The caller can also pass in the constructor as a function if needed. | 95 // The caller can also pass in the constructor as a function if needed. |
92 // | 96 // |
93 // Example: | 97 // Example: |
94 // defineClass("A", "B", ["x", "y"], { | 98 // defineClass("A", "B", ["x", "y"], { |
95 // foo$1: function(y) { | 99 // foo$1: function(y) { |
96 // print(this.x + y); | 100 // print(this.x + y); |
97 // }, | 101 // }, |
98 // bar$2: function(t, v) { | 102 // bar$2: function(t, v) { |
99 // this.x = t - v; | 103 // this.x = t - v; |
100 // }, | 104 // }, |
101 // }); | 105 // }); |
102 return """ | 106 return """ |
103 function(cls, superclass, fields, prototype) { | 107 function(cls, fields, prototype) { |
104 var generateGetterSetter = $generateGetterSetterFunction; | 108 var generateGetterSetter = $generateGetterSetterFunction; |
105 var constructor; | 109 var constructor; |
106 if (typeof fields == 'function') { | 110 if (typeof fields == 'function') { |
107 constructor = fields; | 111 constructor = fields; |
108 } else { | 112 } else { |
109 var str = "function " + cls + "("; | 113 var str = "function " + cls + "("; |
110 var body = ""; | 114 var body = ""; |
111 for (var i = 0; i < fields.length; i++) { | 115 for (var i = 0; i < fields.length; i++) { |
112 if (i != 0) str += ", "; | 116 if (i != 0) str += ", "; |
113 var field = fields[i]; | 117 var field = fields[i]; |
114 field = generateGetterSetter(field, prototype); | 118 field = generateGetterSetter(field, prototype); |
115 str += field; | 119 str += field; |
116 body += "this." + field + " = " + field + ";\\n"; | 120 body += "this." + field + " = " + field + ";\\n"; |
117 } | 121 } |
118 str += ") {" + body + "}\\n"; | 122 str += ") {" + body + "}\\n"; |
119 str += "return " + cls + ";"; | 123 str += "return " + cls + ";"; |
120 constructor = new Function(str)(); | 124 constructor = new Function(str)(); |
121 } | 125 } |
122 $isolatePropertiesName[cls] = constructor; | |
123 constructor.prototype = prototype; | 126 constructor.prototype = prototype; |
124 if (superclass !== "") { | 127 return constructor; |
125 $pendingClassesName[cls] = superclass; | 128 }"""; |
126 } | 129 } |
127 }"""; | 130 |
131 /** Needs defineClass to be defined. */ | |
132 String get protoSupportCheck() { | |
133 // On | |
vsm
2012/07/18 05:33:31
Delete last line.
floitsch
2012/07/18 12:19:33
Done.
| |
134 // On Firefox and Webkit browsers we can manipulate the __proto__ | |
135 // directly. Opera claims to have __proto__ support, but it is buggy. | |
136 // So we have to do more checks. | |
137 // If the browser does not support __proto__ we need to instantiate an | |
138 // object with the correct (internal) prototype set up correctly, and then | |
139 // copy the members. | |
140 | |
141 return ''' | |
142 var $supportsProtoName = false; | |
143 var tmp = $defineClassName('c', ['f?'], {}).prototype; | |
144 if (tmp.__proto__) { | |
145 tmp.__proto__ = {}; | |
146 if (typeof tmp.get\$f !== "undefined") $supportsProtoName = true; | |
147 } | |
148 '''; | |
128 } | 149 } |
129 | 150 |
130 String get finishClassesFunction() { | 151 String get finishClassesFunction() { |
131 // 'defineClass' does not require the classes to be constructed in order. | 152 // 'defineClass' does not require the classes to be constructed in order. |
132 // Classes are initially just stored in the 'pendingClasses' field. | 153 // Classes are initially just stored in the 'pendingClasses' field. |
133 // 'finishClasses' takes all pending classes and sets up the prototype. | 154 // 'finishClasses' takes all pending classes and sets up the prototype. |
134 // Once set up, the constructors prototype field satisfy: | 155 // Once set up, the constructors prototype field satisfy: |
135 // - it contains all (local) members. | 156 // - it contains all (local) members. |
136 // - its internal prototype (__proto__) points to the superclass' | 157 // - its internal prototype (__proto__) points to the superclass' |
137 // prototype field. | 158 // prototype field. |
138 // - the prototype's constructor field points to the JavaScript | 159 // - the prototype's constructor field points to the JavaScript |
139 // constructor. | 160 // constructor. |
140 // For engines where we have access to the '__proto__' we can manipulate | 161 // For engines where we have access to the '__proto__' we can manipulate |
141 // the object literal directly. For other engines we have to create a new | 162 // the object literal directly. For other engines we have to create a new |
142 // object and copy over the members. | 163 // object and copy over the members. |
143 return ''' | 164 return ''' |
144 function(collectedClasses) { | 165 function(collectedClasses) { |
145 for (var collected in collectedClasses) { | 166 for (var cls in collectedClasses) { |
146 if (Object.prototype.hasOwnProperty.call(collectedClasses, collected)) { | 167 if (Object.prototype.hasOwnProperty.call(collectedClasses, cls)) { |
147 var desc = collectedClasses[collected]; | 168 var desc = collectedClasses[cls]; |
148 $defineClassName(collected, desc.super, desc[''], desc); | 169 $isolatePropertiesName[cls] = $defineClassName(cls, desc[''], desc); |
170 if (desc['super'] !== "") $pendingClassesName[cls] = desc['super']; | |
149 } | 171 } |
150 } | 172 } |
151 var pendingClasses = $pendingClassesName; | 173 var pendingClasses = $pendingClassesName; |
152 '''/* FinishClasses can be called multiple times. This means that we need to | 174 '''/* FinishClasses can be called multiple times. This means that we need to |
153 clear the pendingClasses property. */''' | 175 clear the pendingClasses property. */''' |
154 $pendingClassesName = {}; | 176 $pendingClassesName = {}; |
155 var finishedClasses = {}; | 177 var finishedClasses = {}; |
156 function finishClass(cls) { | 178 function finishClass(cls) { |
157 if (finishedClasses[cls]) return; | 179 if (finishedClasses[cls]) return; |
158 finishedClasses[cls] = true; | 180 finishedClasses[cls] = true; |
159 var superclass = pendingClasses[cls]; | 181 var superclass = pendingClasses[cls]; |
160 '''/* The superclass is only false (empty string) for Dart's Object class. */''' | 182 '''/* The superclass is only false (empty string) for Dart's Object class. */''' |
161 if (!superclass) return; | 183 if (!superclass) return; |
162 finishClass(superclass); | 184 finishClass(superclass); |
163 var constructor = $isolatePropertiesName[cls]; | 185 var constructor = $isolatePropertiesName[cls]; |
164 var superConstructor = $isolatePropertiesName[superclass]; | 186 var superConstructor = $isolatePropertiesName[superclass]; |
165 var prototype = constructor.prototype; | 187 var prototype = constructor.prototype; |
166 if (prototype.__proto__) { | 188 if ($supportsProtoName) { |
167 '''/* On Firefox and Webkit browsers we can manipulate the __proto__ | |
168 directly. */''' | |
169 prototype.__proto__ = superConstructor.prototype; | 189 prototype.__proto__ = superConstructor.prototype; |
170 prototype.constructor = constructor; | 190 prototype.constructor = constructor; |
171 } else { | 191 } else { |
172 '''/* On the remaining browsers we need to instantiate an object with the | |
173 correct (internal) prototype set up correctly, and then copy the | |
174 members. */''' | |
175 function tmp() {}; | 192 function tmp() {}; |
176 tmp.prototype = superConstructor.prototype; | 193 tmp.prototype = superConstructor.prototype; |
177 var newPrototype = new tmp(); | 194 var newPrototype = new tmp(); |
178 constructor.prototype = newPrototype; | 195 constructor.prototype = newPrototype; |
179 newPrototype.constructor = constructor; | 196 newPrototype.constructor = constructor; |
180 '''/* Opera does not support 'getOwnPropertyNames'. Therefore we use | 197 '''/* Opera does not support 'getOwnPropertyNames'. Therefore we use |
181 hosOwnProperty instead. */''' | 198 hosOwnProperty instead. */''' |
182 var hasOwnProperty = Object.prototype.hasOwnProperty; | 199 var hasOwnProperty = Object.prototype.hasOwnProperty; |
183 for (var member in prototype) { | 200 for (var member in prototype) { |
184 if (member == '' || member == 'super') continue; | 201 if (member == '' || member == 'super') continue; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
230 isolatePrototype.constructor = newIsolate; | 247 isolatePrototype.constructor = newIsolate; |
231 newIsolate.${namer.ISOLATE_PROPERTIES} = isolateProperties; | 248 newIsolate.${namer.ISOLATE_PROPERTIES} = isolateProperties; |
232 return newIsolate; | 249 return newIsolate; |
233 }"""; | 250 }"""; |
234 } | 251 } |
235 | 252 |
236 void addDefineClassAndFinishClassFunctionsIfNecessary(CodeBuffer buffer) { | 253 void addDefineClassAndFinishClassFunctionsIfNecessary(CodeBuffer buffer) { |
237 if (needsDefineClass) { | 254 if (needsDefineClass) { |
238 String isolate = namer.ISOLATE; | 255 String isolate = namer.ISOLATE; |
239 buffer.add("$defineClassName = $defineClassFunction;\n"); | 256 buffer.add("$defineClassName = $defineClassFunction;\n"); |
257 buffer.add(protoSupportCheck); | |
240 buffer.add("$pendingClassesName = {};\n"); | 258 buffer.add("$pendingClassesName = {};\n"); |
241 buffer.add("$finishClassesName = $finishClassesFunction;\n"); | 259 buffer.add("$finishClassesName = $finishClassesFunction;\n"); |
242 } | 260 } |
243 } | 261 } |
244 | 262 |
245 void emitFinishIsolateConstructor(CodeBuffer buffer) { | 263 void emitFinishIsolateConstructor(CodeBuffer buffer) { |
246 String name = finishIsolateConstructorName; | 264 String name = finishIsolateConstructorName; |
247 String value = finishIsolateConstructorFunction; | 265 String value = finishIsolateConstructorFunction; |
248 buffer.add("$name = $value;\n"); | 266 buffer.add("$name = $value;\n"); |
249 } | 267 } |
(...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
704 // Create a new closure class. | 722 // Create a new closure class. |
705 SourceString name = const SourceString("BoundClosure"); | 723 SourceString name = const SourceString("BoundClosure"); |
706 ClassElement closureClassElement = | 724 ClassElement closureClassElement = |
707 new ClosureClassElement(name, compiler, member.getCompilationUnit()); | 725 new ClosureClassElement(name, compiler, member.getCompilationUnit()); |
708 String mangledName = namer.getName(closureClassElement); | 726 String mangledName = namer.getName(closureClassElement); |
709 String superName = namer.getName(closureClassElement.superclass); | 727 String superName = namer.getName(closureClassElement.superclass); |
710 needsClosureClass = true; | 728 needsClosureClass = true; |
711 | 729 |
712 // Define the constructor with a name so that Object.toString can | 730 // Define the constructor with a name so that Object.toString can |
713 // find the class name of the closure class. | 731 // find the class name of the closure class. |
714 boundClosureBuffer.add("$defineClassName('$mangledName', '$superName', "); | 732 boundClosureBuffer.add(""" |
715 boundClosureBuffer.add("['self', 'target'], {\n"); | 733 $classesCollector.$mangledName = {'': |
716 | 734 ['self', 'target'], |
735 'super': '$superName', | |
736 """); | |
717 // Now add the methods on the closure class. The instance method does not | 737 // Now add the methods on the closure class. The instance method does not |
718 // have the correct name. Since [addParameterStubs] use the name to create | 738 // have the correct name. Since [addParameterStubs] use the name to create |
719 // its stubs we simply create a fake element with the correct name. | 739 // its stubs we simply create a fake element with the correct name. |
720 // Note: the callElement will not have any enclosingElement. | 740 // Note: the callElement will not have any enclosingElement. |
721 FunctionElement callElement = | 741 FunctionElement callElement = |
722 new ClosureInvocationElement(namer.CLOSURE_INVOCATION_NAME, member); | 742 new ClosureInvocationElement(namer.CLOSURE_INVOCATION_NAME, member); |
723 | 743 |
724 String invocationName = | 744 String invocationName = |
725 namer.instanceMethodName(member.getLibrary(), | 745 namer.instanceMethodName(member.getLibrary(), |
726 callElement.name, parameterCount); | 746 callElement.name, parameterCount); |
727 List<String> arguments = new List<String>(parameterCount); | 747 List<String> arguments = new List<String>(parameterCount); |
728 for (int i = 0; i < parameterCount; i++) { | 748 for (int i = 0; i < parameterCount; i++) { |
729 arguments[i] = "p$i"; | 749 arguments[i] = "p$i"; |
730 } | 750 } |
731 String joinedArgs = Strings.join(arguments, ", "); | 751 String joinedArgs = Strings.join(arguments, ", "); |
732 boundClosureBuffer.add( | 752 boundClosureBuffer.add( |
733 "$invocationName: function($joinedArgs) {"); | 753 "$invocationName: function($joinedArgs) {"); |
734 boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);"); | 754 boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);"); |
735 boundClosureBuffer.add(" }"); | 755 boundClosureBuffer.add(" }"); |
736 addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) { | 756 addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) { |
737 boundClosureBuffer.add(',\n $stubName: $memberValue'); | 757 boundClosureBuffer.add(',\n $stubName: $memberValue'); |
738 }); | 758 }); |
739 boundClosureBuffer.add("\n});\n"); | 759 boundClosureBuffer.add("\n};\n"); |
740 | 760 |
741 closureClass = namer.isolateAccess(closureClassElement); | 761 closureClass = namer.isolateAccess(closureClassElement); |
742 | 762 |
743 // Cache it. | 763 // Cache it. |
744 if (!hasOptionalParameters) { | 764 if (!hasOptionalParameters) { |
745 boundClosureCache[parameterCount] = closureClass; | 765 boundClosureCache[parameterCount] = closureClass; |
746 } | 766 } |
747 } | 767 } |
748 | 768 |
749 // And finally the getter. | 769 // And finally the getter. |
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1033 // the classesCollector variable. | 1053 // the classesCollector variable. |
1034 classesCollector = 'classesCollector should not be used from now on'; | 1054 classesCollector = 'classesCollector should not be used from now on'; |
1035 | 1055 |
1036 emitFinishIsolateConstructorInvocation(mainBuffer); | 1056 emitFinishIsolateConstructorInvocation(mainBuffer); |
1037 mainBuffer.add( | 1057 mainBuffer.add( |
1038 'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n'); | 1058 'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n'); |
1039 | 1059 |
1040 nativeEmitter.assembleCode(mainBuffer); | 1060 nativeEmitter.assembleCode(mainBuffer); |
1041 emitMain(mainBuffer); | 1061 emitMain(mainBuffer); |
1042 mainBuffer.add('function init() {\n'); | 1062 mainBuffer.add('function init() {\n'); |
1043 mainBuffer.add(' $isolateProperties = {};\n'); | 1063 mainBuffer.add('$isolateProperties = {};\n'); |
1044 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); | 1064 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); |
1045 emitFinishIsolateConstructor(mainBuffer); | 1065 emitFinishIsolateConstructor(mainBuffer); |
1046 mainBuffer.add('}\n'); | 1066 mainBuffer.add('}\n'); |
1047 compiler.assembledCode = mainBuffer.toString(); | 1067 compiler.assembledCode = mainBuffer.toString(); |
1048 | 1068 |
1049 if (generateSourceMap) { | 1069 if (generateSourceMap) { |
1050 SourceFile compiledFile = new SourceFile(null, compiler.assembledCode); | 1070 SourceFile compiledFile = new SourceFile(null, compiler.assembledCode); |
1051 String sourceMap = sourceMapBuilder.build(compiledFile); | 1071 String sourceMap = sourceMapBuilder.build(compiledFile); |
1052 // TODO(podivilov): We should find a better way to return source maps to | 1072 // TODO(podivilov): We should find a better way to return source maps to |
1053 // compiler. Using diagnostic handler for that purpose is a temporary | 1073 // compiler. Using diagnostic handler for that purpose is a temporary |
(...skipping 13 matching lines...) Expand all Loading... | |
1067 sourceName = token.slowToString(); | 1087 sourceName = token.slowToString(); |
1068 } | 1088 } |
1069 int totalOffset = bufferOffset + offset; | 1089 int totalOffset = bufferOffset + offset; |
1070 sourceMapBuilder.addMapping( | 1090 sourceMapBuilder.addMapping( |
1071 sourceFile, token.charOffset, sourceName, totalOffset); | 1091 sourceFile, token.charOffset, sourceName, totalOffset); |
1072 }); | 1092 }); |
1073 } | 1093 } |
1074 } | 1094 } |
1075 | 1095 |
1076 typedef void DefineMemberFunction(String invocationName, CodeBuffer definition); | 1096 typedef void DefineMemberFunction(String invocationName, CodeBuffer definition); |
OLD | NEW |