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 Firefox and Webkit browsers we can manipulate the __proto__ |
| 134 // directly. Opera claims to have __proto__ support, but it is buggy. |
| 135 // So we have to do more checks. |
| 136 // If the browser does not support __proto__ we need to instantiate an |
| 137 // object with the correct (internal) prototype set up correctly, and then |
| 138 // copy the members. |
| 139 |
| 140 return ''' |
| 141 var $supportsProtoName = false; |
| 142 var tmp = $defineClassName('c', ['f?'], {}).prototype; |
| 143 if (tmp.__proto__) { |
| 144 tmp.__proto__ = {}; |
| 145 if (typeof tmp.get\$f !== "undefined") $supportsProtoName = true; |
| 146 } |
| 147 '''; |
128 } | 148 } |
129 | 149 |
130 String get finishClassesFunction() { | 150 String get finishClassesFunction() { |
131 // 'defineClass' does not require the classes to be constructed in order. | 151 // 'defineClass' does not require the classes to be constructed in order. |
132 // Classes are initially just stored in the 'pendingClasses' field. | 152 // Classes are initially just stored in the 'pendingClasses' field. |
133 // 'finishClasses' takes all pending classes and sets up the prototype. | 153 // 'finishClasses' takes all pending classes and sets up the prototype. |
134 // Once set up, the constructors prototype field satisfy: | 154 // Once set up, the constructors prototype field satisfy: |
135 // - it contains all (local) members. | 155 // - it contains all (local) members. |
136 // - its internal prototype (__proto__) points to the superclass' | 156 // - its internal prototype (__proto__) points to the superclass' |
137 // prototype field. | 157 // prototype field. |
138 // - the prototype's constructor field points to the JavaScript | 158 // - the prototype's constructor field points to the JavaScript |
139 // constructor. | 159 // constructor. |
140 // For engines where we have access to the '__proto__' we can manipulate | 160 // 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 | 161 // the object literal directly. For other engines we have to create a new |
142 // object and copy over the members. | 162 // object and copy over the members. |
143 return ''' | 163 return ''' |
144 function(collectedClasses) { | 164 function(collectedClasses) { |
145 for (var collected in collectedClasses) { | 165 for (var cls in collectedClasses) { |
146 if (Object.prototype.hasOwnProperty.call(collectedClasses, collected)) { | 166 if (Object.prototype.hasOwnProperty.call(collectedClasses, cls)) { |
147 var desc = collectedClasses[collected]; | 167 var desc = collectedClasses[cls]; |
148 $defineClassName(collected, desc.super, desc[''], desc); | 168 $isolatePropertiesName[cls] = $defineClassName(cls, desc[''], desc); |
| 169 if (desc['super'] !== "") $pendingClassesName[cls] = desc['super']; |
149 } | 170 } |
150 } | 171 } |
151 var pendingClasses = $pendingClassesName; | 172 var pendingClasses = $pendingClassesName; |
152 '''/* FinishClasses can be called multiple times. This means that we need to | 173 '''/* FinishClasses can be called multiple times. This means that we need to |
153 clear the pendingClasses property. */''' | 174 clear the pendingClasses property. */''' |
154 $pendingClassesName = {}; | 175 $pendingClassesName = {}; |
155 var finishedClasses = {}; | 176 var finishedClasses = {}; |
156 function finishClass(cls) { | 177 function finishClass(cls) { |
157 if (finishedClasses[cls]) return; | 178 if (finishedClasses[cls]) return; |
158 finishedClasses[cls] = true; | 179 finishedClasses[cls] = true; |
159 var superclass = pendingClasses[cls]; | 180 var superclass = pendingClasses[cls]; |
160 '''/* The superclass is only false (empty string) for Dart's Object class. */''' | 181 '''/* The superclass is only false (empty string) for Dart's Object class. */''' |
161 if (!superclass) return; | 182 if (!superclass) return; |
162 finishClass(superclass); | 183 finishClass(superclass); |
163 var constructor = $isolatePropertiesName[cls]; | 184 var constructor = $isolatePropertiesName[cls]; |
164 var superConstructor = $isolatePropertiesName[superclass]; | 185 var superConstructor = $isolatePropertiesName[superclass]; |
165 var prototype = constructor.prototype; | 186 var prototype = constructor.prototype; |
166 if (prototype.__proto__) { | 187 if ($supportsProtoName) { |
167 '''/* On Firefox and Webkit browsers we can manipulate the __proto__ | |
168 directly. */''' | |
169 prototype.__proto__ = superConstructor.prototype; | 188 prototype.__proto__ = superConstructor.prototype; |
170 prototype.constructor = constructor; | 189 prototype.constructor = constructor; |
171 } else { | 190 } 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() {}; | 191 function tmp() {}; |
176 tmp.prototype = superConstructor.prototype; | 192 tmp.prototype = superConstructor.prototype; |
177 var newPrototype = new tmp(); | 193 var newPrototype = new tmp(); |
178 constructor.prototype = newPrototype; | 194 constructor.prototype = newPrototype; |
179 newPrototype.constructor = constructor; | 195 newPrototype.constructor = constructor; |
180 '''/* Opera does not support 'getOwnPropertyNames'. Therefore we use | 196 '''/* Opera does not support 'getOwnPropertyNames'. Therefore we use |
181 hosOwnProperty instead. */''' | 197 hosOwnProperty instead. */''' |
182 var hasOwnProperty = Object.prototype.hasOwnProperty; | 198 var hasOwnProperty = Object.prototype.hasOwnProperty; |
183 for (var member in prototype) { | 199 for (var member in prototype) { |
184 if (member == '' || member == 'super') continue; | 200 if (member == '' || member == 'super') continue; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 isolatePrototype.constructor = newIsolate; | 246 isolatePrototype.constructor = newIsolate; |
231 newIsolate.${namer.ISOLATE_PROPERTIES} = isolateProperties; | 247 newIsolate.${namer.ISOLATE_PROPERTIES} = isolateProperties; |
232 return newIsolate; | 248 return newIsolate; |
233 }"""; | 249 }"""; |
234 } | 250 } |
235 | 251 |
236 void addDefineClassAndFinishClassFunctionsIfNecessary(CodeBuffer buffer) { | 252 void addDefineClassAndFinishClassFunctionsIfNecessary(CodeBuffer buffer) { |
237 if (needsDefineClass) { | 253 if (needsDefineClass) { |
238 String isolate = namer.ISOLATE; | 254 String isolate = namer.ISOLATE; |
239 buffer.add("$defineClassName = $defineClassFunction;\n"); | 255 buffer.add("$defineClassName = $defineClassFunction;\n"); |
| 256 buffer.add(protoSupportCheck); |
240 buffer.add("$pendingClassesName = {};\n"); | 257 buffer.add("$pendingClassesName = {};\n"); |
241 buffer.add("$finishClassesName = $finishClassesFunction;\n"); | 258 buffer.add("$finishClassesName = $finishClassesFunction;\n"); |
242 } | 259 } |
243 } | 260 } |
244 | 261 |
245 void emitFinishIsolateConstructor(CodeBuffer buffer) { | 262 void emitFinishIsolateConstructor(CodeBuffer buffer) { |
246 String name = finishIsolateConstructorName; | 263 String name = finishIsolateConstructorName; |
247 String value = finishIsolateConstructorFunction; | 264 String value = finishIsolateConstructorFunction; |
248 buffer.add("$name = $value;\n"); | 265 buffer.add("$name = $value;\n"); |
249 } | 266 } |
(...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
704 // Create a new closure class. | 721 // Create a new closure class. |
705 SourceString name = const SourceString("BoundClosure"); | 722 SourceString name = const SourceString("BoundClosure"); |
706 ClassElement closureClassElement = | 723 ClassElement closureClassElement = |
707 new ClosureClassElement(name, compiler, member.getCompilationUnit()); | 724 new ClosureClassElement(name, compiler, member.getCompilationUnit()); |
708 String mangledName = namer.getName(closureClassElement); | 725 String mangledName = namer.getName(closureClassElement); |
709 String superName = namer.getName(closureClassElement.superclass); | 726 String superName = namer.getName(closureClassElement.superclass); |
710 needsClosureClass = true; | 727 needsClosureClass = true; |
711 | 728 |
712 // Define the constructor with a name so that Object.toString can | 729 // Define the constructor with a name so that Object.toString can |
713 // find the class name of the closure class. | 730 // find the class name of the closure class. |
714 boundClosureBuffer.add("$defineClassName('$mangledName', '$superName', "); | 731 boundClosureBuffer.add(""" |
715 boundClosureBuffer.add("['self', 'target'], {\n"); | 732 $classesCollector.$mangledName = {'': |
716 | 733 ['self', 'target'], |
| 734 'super': '$superName', |
| 735 """); |
717 // Now add the methods on the closure class. The instance method does not | 736 // 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 | 737 // have the correct name. Since [addParameterStubs] use the name to create |
719 // its stubs we simply create a fake element with the correct name. | 738 // its stubs we simply create a fake element with the correct name. |
720 // Note: the callElement will not have any enclosingElement. | 739 // Note: the callElement will not have any enclosingElement. |
721 FunctionElement callElement = | 740 FunctionElement callElement = |
722 new ClosureInvocationElement(namer.CLOSURE_INVOCATION_NAME, member); | 741 new ClosureInvocationElement(namer.CLOSURE_INVOCATION_NAME, member); |
723 | 742 |
724 String invocationName = | 743 String invocationName = |
725 namer.instanceMethodName(member.getLibrary(), | 744 namer.instanceMethodName(member.getLibrary(), |
726 callElement.name, parameterCount); | 745 callElement.name, parameterCount); |
727 List<String> arguments = new List<String>(parameterCount); | 746 List<String> arguments = new List<String>(parameterCount); |
728 for (int i = 0; i < parameterCount; i++) { | 747 for (int i = 0; i < parameterCount; i++) { |
729 arguments[i] = "p$i"; | 748 arguments[i] = "p$i"; |
730 } | 749 } |
731 String joinedArgs = Strings.join(arguments, ", "); | 750 String joinedArgs = Strings.join(arguments, ", "); |
732 boundClosureBuffer.add( | 751 boundClosureBuffer.add( |
733 "$invocationName: function($joinedArgs) {"); | 752 "$invocationName: function($joinedArgs) {"); |
734 boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);"); | 753 boundClosureBuffer.add(" return this.self[this.target]($joinedArgs);"); |
735 boundClosureBuffer.add(" }"); | 754 boundClosureBuffer.add(" }"); |
736 addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) { | 755 addParameterStubs(callElement, (String stubName, CodeBuffer memberValue) { |
737 boundClosureBuffer.add(',\n $stubName: $memberValue'); | 756 boundClosureBuffer.add(',\n $stubName: $memberValue'); |
738 }); | 757 }); |
739 boundClosureBuffer.add("\n});\n"); | 758 boundClosureBuffer.add("\n};\n"); |
740 | 759 |
741 closureClass = namer.isolateAccess(closureClassElement); | 760 closureClass = namer.isolateAccess(closureClassElement); |
742 | 761 |
743 // Cache it. | 762 // Cache it. |
744 if (!hasOptionalParameters) { | 763 if (!hasOptionalParameters) { |
745 boundClosureCache[parameterCount] = closureClass; | 764 boundClosureCache[parameterCount] = closureClass; |
746 } | 765 } |
747 } | 766 } |
748 | 767 |
749 // And finally the getter. | 768 // And finally the getter. |
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1033 // the classesCollector variable. | 1052 // the classesCollector variable. |
1034 classesCollector = 'classesCollector should not be used from now on'; | 1053 classesCollector = 'classesCollector should not be used from now on'; |
1035 | 1054 |
1036 emitFinishIsolateConstructorInvocation(mainBuffer); | 1055 emitFinishIsolateConstructorInvocation(mainBuffer); |
1037 mainBuffer.add( | 1056 mainBuffer.add( |
1038 'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n'); | 1057 'var ${namer.CURRENT_ISOLATE} = new ${namer.ISOLATE}();\n'); |
1039 | 1058 |
1040 nativeEmitter.assembleCode(mainBuffer); | 1059 nativeEmitter.assembleCode(mainBuffer); |
1041 emitMain(mainBuffer); | 1060 emitMain(mainBuffer); |
1042 mainBuffer.add('function init() {\n'); | 1061 mainBuffer.add('function init() {\n'); |
1043 mainBuffer.add(' $isolateProperties = {};\n'); | 1062 mainBuffer.add('$isolateProperties = {};\n'); |
1044 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); | 1063 addDefineClassAndFinishClassFunctionsIfNecessary(mainBuffer); |
1045 emitFinishIsolateConstructor(mainBuffer); | 1064 emitFinishIsolateConstructor(mainBuffer); |
1046 mainBuffer.add('}\n'); | 1065 mainBuffer.add('}\n'); |
1047 compiler.assembledCode = mainBuffer.toString(); | 1066 compiler.assembledCode = mainBuffer.toString(); |
1048 | 1067 |
1049 if (generateSourceMap) { | 1068 if (generateSourceMap) { |
1050 SourceFile compiledFile = new SourceFile(null, compiler.assembledCode); | 1069 SourceFile compiledFile = new SourceFile(null, compiler.assembledCode); |
1051 String sourceMap = sourceMapBuilder.build(compiledFile); | 1070 String sourceMap = sourceMapBuilder.build(compiledFile); |
1052 // TODO(podivilov): We should find a better way to return source maps to | 1071 // TODO(podivilov): We should find a better way to return source maps to |
1053 // compiler. Using diagnostic handler for that purpose is a temporary | 1072 // compiler. Using diagnostic handler for that purpose is a temporary |
(...skipping 13 matching lines...) Expand all Loading... |
1067 sourceName = token.slowToString(); | 1086 sourceName = token.slowToString(); |
1068 } | 1087 } |
1069 int totalOffset = bufferOffset + offset; | 1088 int totalOffset = bufferOffset + offset; |
1070 sourceMapBuilder.addMapping( | 1089 sourceMapBuilder.addMapping( |
1071 sourceFile, token.charOffset, sourceName, totalOffset); | 1090 sourceFile, token.charOffset, sourceName, totalOffset); |
1072 }); | 1091 }); |
1073 } | 1092 } |
1074 } | 1093 } |
1075 | 1094 |
1076 typedef void DefineMemberFunction(String invocationName, CodeBuffer definition); | 1095 typedef void DefineMemberFunction(String invocationName, CodeBuffer definition); |
OLD | NEW |