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 class NativeEmitter { | 7 class NativeEmitter { |
8 | 8 |
9 CodeEmitterTask emitter; | 9 CodeEmitterTask emitter; |
10 CodeBuffer nativeBuffer; | 10 CodeBuffer nativeBuffer; |
(...skipping 13 matching lines...) Expand all Loading... |
24 | 24 |
25 // Caches the native methods that are overridden by a native class. | 25 // Caches the native methods that are overridden by a native class. |
26 // Note that the method that overrides does not have to be native: | 26 // Note that the method that overrides does not have to be native: |
27 // it's the overridden method that must make sure it will dispatch | 27 // it's the overridden method that must make sure it will dispatch |
28 // to its subclass if it sees an instance whose class is a subclass. | 28 // to its subclass if it sees an instance whose class is a subclass. |
29 Set<FunctionElement> overriddenMethods; | 29 Set<FunctionElement> overriddenMethods; |
30 | 30 |
31 // Caches the methods that have a native body. | 31 // Caches the methods that have a native body. |
32 Set<FunctionElement> nativeMethods; | 32 Set<FunctionElement> nativeMethods; |
33 | 33 |
| 34 // Caches the methods that redirect to a JS method. |
| 35 Map<FunctionElement, String> redirectingMethods; |
| 36 |
34 // Do we need the native emitter to take care of handling | 37 // Do we need the native emitter to take care of handling |
35 // noSuchMethod for us? This flag is set to true in the emitter if | 38 // noSuchMethod for us? This flag is set to true in the emitter if |
36 // it finds any native class that needs noSuchMethod handling. | 39 // it finds any native class that needs noSuchMethod handling. |
37 bool handleNoSuchMethod = false; | 40 bool handleNoSuchMethod = false; |
38 | 41 |
39 NativeEmitter(this.emitter) | 42 NativeEmitter(this.emitter) |
40 : classesWithDynamicDispatch = new Set<ClassElement>(), | 43 : classesWithDynamicDispatch = new Set<ClassElement>(), |
41 nativeClasses = new Set<ClassElement>(), | 44 nativeClasses = new Set<ClassElement>(), |
42 subtypes = new Map<ClassElement, List<ClassElement>>(), | 45 subtypes = new Map<ClassElement, List<ClassElement>>(), |
43 directSubtypes = new Map<ClassElement, List<ClassElement>>(), | 46 directSubtypes = new Map<ClassElement, List<ClassElement>>(), |
44 overriddenMethods = new Set<FunctionElement>(), | 47 overriddenMethods = new Set<FunctionElement>(), |
45 nativeMethods = new Set<FunctionElement>(), | 48 nativeMethods = new Set<FunctionElement>(), |
| 49 redirectingMethods = new Map<FunctionElement, String>(), |
46 nativeBuffer = new CodeBuffer(); | 50 nativeBuffer = new CodeBuffer(); |
47 | 51 |
48 Compiler get compiler => emitter.compiler; | 52 Compiler get compiler => emitter.compiler; |
49 JavaScriptBackend get backend => compiler.backend; | 53 JavaScriptBackend get backend => compiler.backend; |
50 | 54 |
| 55 void addRedirectingMethod(FunctionElement element, String name) { |
| 56 redirectingMethods[element] = name; |
| 57 } |
| 58 |
51 String get dynamicName { | 59 String get dynamicName { |
52 Element element = compiler.findHelper( | 60 Element element = compiler.findHelper( |
53 const SourceString('dynamicFunction')); | 61 const SourceString('dynamicFunction')); |
54 return backend.namer.isolateAccess(element); | 62 return backend.namer.isolateAccess(element); |
55 } | 63 } |
56 | 64 |
57 String get dynamicSetMetadataName { | 65 String get dynamicSetMetadataName { |
58 Element element = compiler.findHelper( | 66 Element element = compiler.findHelper( |
59 const SourceString('dynamicSetMetadata')); | 67 const SourceString('dynamicSetMetadata')); |
60 return backend.namer.isolateAccess(element); | 68 return backend.namer.isolateAccess(element); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 if (method !== '') { | 108 if (method !== '') { |
101 if (hasOwnProperty.call(desc, method)) { | 109 if (hasOwnProperty.call(desc, method)) { |
102 $dynamicName(method)[cls] = desc[method]; | 110 $dynamicName(method)[cls] = desc[method]; |
103 } | 111 } |
104 } | 112 } |
105 } | 113 } |
106 }"""; | 114 }"""; |
107 } | 115 } |
108 | 116 |
109 void generateNativeLiteral(ClassElement classElement) { | 117 void generateNativeLiteral(ClassElement classElement) { |
110 String quotedNative = classElement.nativeTagInfo.slowToString(); | 118 String quotedNative = classElement.nativeName.slowToString(); |
111 String nativeCode = quotedNative.substring(2, quotedNative.length - 1); | 119 String nativeCode = quotedNative.substring(2, quotedNative.length - 1); |
112 String className = backend.namer.getName(classElement); | 120 String className = backend.namer.getName(classElement); |
113 nativeBuffer.add(className); | 121 nativeBuffer.add(className); |
114 nativeBuffer.add(' = '); | 122 nativeBuffer.add(' = '); |
115 nativeBuffer.add(nativeCode); | 123 nativeBuffer.add(nativeCode); |
116 nativeBuffer.add(';\n'); | 124 nativeBuffer.add(';\n'); |
117 | 125 |
118 void defineInstanceMember(String name, CodeBuffer value) { | 126 void defineInstanceMember(String name, CodeBuffer value) { |
119 nativeBuffer.add("$className.$name = $value;\n"); | 127 nativeBuffer.add("$className.$name = $value;\n"); |
120 } | 128 } |
121 | 129 |
122 classElement.implementation.forEachMember((_, Element member) { | 130 classElement.implementation.forEachMember((_, Element member) { |
123 if (member.isInstanceMember()) { | 131 if (member.isInstanceMember()) { |
124 emitter.addInstanceMember(member, defineInstanceMember); | 132 emitter.addInstanceMember(member, defineInstanceMember); |
125 } | 133 } |
126 }); | 134 }); |
127 } | 135 } |
128 | 136 |
129 bool isNativeLiteral(String quotedName) { | 137 bool isNativeLiteral(String quotedName) { |
130 return identical(quotedName[1], '='); | 138 return identical(quotedName[1], '='); |
131 } | 139 } |
132 | 140 |
133 bool isNativeGlobal(String quotedName) { | 141 bool isNativeGlobal(String quotedName) { |
134 return identical(quotedName[1], '@'); | 142 return identical(quotedName[1], '@'); |
135 } | 143 } |
136 | 144 |
137 String toNativeTag(ClassElement cls) { | 145 String toNativeName(ClassElement cls) { |
138 String quotedName = cls.nativeTagInfo.slowToString(); | 146 String quotedName = cls.nativeName.slowToString(); |
139 if (isNativeGlobal(quotedName)) { | 147 if (isNativeGlobal(quotedName)) { |
140 // Global object, just be like the other types for now. | 148 // Global object, just be like the other types for now. |
141 return quotedName.substring(3, quotedName.length - 1); | 149 return quotedName.substring(3, quotedName.length - 1); |
142 } else { | 150 } else { |
143 return quotedName.substring(2, quotedName.length - 1); | 151 return quotedName.substring(2, quotedName.length - 1); |
144 } | 152 } |
145 } | 153 } |
146 | 154 |
147 void generateNativeClass(ClassElement classElement) { | 155 void generateNativeClass(ClassElement classElement) { |
148 nativeClasses.add(classElement); | 156 nativeClasses.add(classElement); |
149 | 157 |
150 assert(classElement.backendMembers.isEmpty); | 158 assert(classElement.backendMembers.isEmpty); |
151 String quotedName = classElement.nativeTagInfo.slowToString(); | 159 String quotedName = classElement.nativeName.slowToString(); |
152 if (isNativeLiteral(quotedName)) { | 160 if (isNativeLiteral(quotedName)) { |
153 generateNativeLiteral(classElement); | 161 generateNativeLiteral(classElement); |
154 // The native literal kind needs to be dealt with specially when | 162 // The native literal kind needs to be dealt with specially when |
155 // generating code for it. | 163 // generating code for it. |
156 return; | 164 return; |
157 } | 165 } |
158 | 166 |
159 CodeBuffer fieldBuffer = new CodeBuffer(); | 167 CodeBuffer fieldBuffer = new CodeBuffer(); |
160 CodeBuffer getterSetterBuffer = new CodeBuffer(); | 168 CodeBuffer getterSetterBuffer = new CodeBuffer(); |
161 CodeBuffer methodBuffer = new CodeBuffer(); | 169 CodeBuffer methodBuffer = new CodeBuffer(); |
162 | 170 |
163 emitter.emitClassFields(classElement, fieldBuffer, false); | 171 emitter.emitClassFields(classElement, fieldBuffer, false); |
164 emitter.emitClassGettersSetters(classElement, getterSetterBuffer, false); | 172 emitter.emitClassGettersSetters(classElement, getterSetterBuffer, false); |
165 emitter.emitInstanceMembers(classElement, methodBuffer, false); | 173 emitter.emitInstanceMembers(classElement, methodBuffer, false); |
166 | 174 |
167 if (methodBuffer.isEmpty | 175 if (methodBuffer.isEmpty |
168 && fieldBuffer.isEmpty | 176 && fieldBuffer.isEmpty |
169 && getterSetterBuffer.isEmpty) { | 177 && getterSetterBuffer.isEmpty) { |
170 return; | 178 return; |
171 } | 179 } |
172 | 180 |
173 String nativeTag = toNativeTag(classElement); | 181 String nativeName = toNativeName(classElement); |
174 nativeBuffer.add("$defineNativeClassName('$nativeTag', "); | 182 nativeBuffer.add("$defineNativeClassName('$nativeName', "); |
175 nativeBuffer.add('{'); | 183 nativeBuffer.add('{'); |
176 bool firstInMap = true; | 184 bool firstInMap = true; |
177 if (!fieldBuffer.isEmpty) { | 185 if (!fieldBuffer.isEmpty) { |
178 firstInMap = false; | 186 firstInMap = false; |
179 nativeBuffer.add(fieldBuffer); | 187 nativeBuffer.add(fieldBuffer); |
180 } | 188 } |
181 if (!getterSetterBuffer.isEmpty) { | 189 if (!getterSetterBuffer.isEmpty) { |
182 if (!firstInMap) nativeBuffer.add(","); | 190 if (!firstInMap) nativeBuffer.add(","); |
183 firstInMap = false; | 191 firstInMap = false; |
184 nativeBuffer.add("\n "); | 192 nativeBuffer.add("\n "); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 // foo([x, y, z]); | 240 // foo([x, y, z]); |
233 // The call: | 241 // The call: |
234 // foo(y: 1) | 242 // foo(y: 1) |
235 // must be turned into a JS call to: | 243 // must be turned into a JS call to: |
236 // foo(null, y). | 244 // foo(null, y). |
237 | 245 |
238 List<String> nativeArgumentsBuffer = argumentsBuffer.getRange( | 246 List<String> nativeArgumentsBuffer = argumentsBuffer.getRange( |
239 0, indexOfLastOptionalArgumentInParameters + 1); | 247 0, indexOfLastOptionalArgumentInParameters + 1); |
240 | 248 |
241 ClassElement classElement = member.enclosingElement; | 249 ClassElement classElement = member.enclosingElement; |
242 String nativeTagInfo = classElement.nativeTagInfo.slowToString(); | 250 String nativeName = classElement.nativeName.slowToString(); |
243 String nativeArguments = Strings.join(nativeArgumentsBuffer, ","); | 251 String nativeArguments = Strings.join(nativeArgumentsBuffer, ","); |
244 | 252 |
245 CodeBuffer code = new CodeBuffer(); | 253 CodeBuffer code = new CodeBuffer(); |
246 potentiallyConvertDartClosuresToJs(code, member, argumentsBuffer); | 254 potentiallyConvertDartClosuresToJs(code, member, argumentsBuffer); |
247 | 255 |
248 if (!nativeMethods.contains(member)) { | 256 if (!nativeMethods.contains(member)) { |
249 // When calling a method that has a native body, we call it | 257 // When calling a method that has a native body, we call it |
250 // with our calling conventions. | 258 // with our calling conventions. |
251 String arguments = Strings.join(argumentsBuffer, ","); | 259 String arguments = Strings.join(argumentsBuffer, ","); |
252 code.add(' return this.${backend.namer.getName(member)}($arguments)'); | 260 code.add(' return this.${backend.namer.getName(member)}($arguments)'); |
253 } else { | 261 } else { |
254 // When calling a JS method, we call it with the native name. | 262 // When calling a JS method, we call it with the native name. |
255 String name = member.nativeName(); | 263 String name = redirectingMethods[member]; |
| 264 if (name == null) name = member.name.slowToString(); |
256 code.add(' return this.$name($nativeArguments);'); | 265 code.add(' return this.$name($nativeArguments);'); |
257 } | 266 } |
258 | 267 |
259 if (isNativeLiteral(nativeTagInfo) || !overriddenMethods.contains(member)) { | 268 if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) { |
260 // Call the method directly. | 269 // Call the method directly. |
261 buffer.add(code.toString()); | 270 buffer.add(code.toString()); |
262 } else { | 271 } else { |
263 native.generateMethodWithPrototypeCheck( | 272 native.generateMethodWithPrototypeCheck( |
264 compiler, buffer, invocationName, code.toString(), stubParameters); | 273 compiler, buffer, invocationName, code.toString(), stubParameters); |
265 } | 274 } |
266 } | 275 } |
267 | 276 |
268 void emitDynamicDispatchMetadata() { | 277 void emitDynamicDispatchMetadata() { |
269 if (classesWithDynamicDispatch.isEmpty) return; | 278 if (classesWithDynamicDispatch.isEmpty) return; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 // Expression to compute tags string for a class. The expression will | 320 // Expression to compute tags string for a class. The expression will |
312 // initially be a string or expression building a string, but may be | 321 // initially be a string or expression building a string, but may be |
313 // replaced with a variable reference to the common substring. | 322 // replaced with a variable reference to the common substring. |
314 Map<ClassElement, js.Expression> tagDefns = | 323 Map<ClassElement, js.Expression> tagDefns = |
315 new Map<ClassElement, js.Expression>(); | 324 new Map<ClassElement, js.Expression>(); |
316 | 325 |
317 js.Expression makeExpression(ClassElement classElement) { | 326 js.Expression makeExpression(ClassElement classElement) { |
318 // Expression fragments for this set of cls keys. | 327 // Expression fragments for this set of cls keys. |
319 List<js.Expression> expressions = <js.Expression>[]; | 328 List<js.Expression> expressions = <js.Expression>[]; |
320 // TODO: Remove if cls is abstract. | 329 // TODO: Remove if cls is abstract. |
321 List<String> subtags = [toNativeTag(classElement)]; | 330 List<String> subtags = [toNativeName(classElement)]; |
322 void walk(ClassElement cls) { | 331 void walk(ClassElement cls) { |
323 for (final ClassElement subclass in getDirectSubclasses(cls)) { | 332 for (final ClassElement subclass in getDirectSubclasses(cls)) { |
324 ClassElement tag = subclass; | 333 ClassElement tag = subclass; |
325 js.Expression existing = tagDefns[tag]; | 334 js.Expression existing = tagDefns[tag]; |
326 if (existing == null) { | 335 if (existing == null) { |
327 // [subclass] is still within the subtree between dispatch classes. | 336 // [subclass] is still within the subtree between dispatch classes. |
328 subtags.add(toNativeTag(tag)); | 337 subtags.add(toNativeName(tag)); |
329 walk(subclass); | 338 walk(subclass); |
330 } else { | 339 } else { |
331 // [subclass] is one of the preorderDispatchClasses, so CSE this | 340 // [subclass] is one of the preorderDispatchClasses, so CSE this |
332 // reference with the previous reference. | 341 // reference with the previous reference. |
333 if (existing is js.VariableUse && | 342 if (existing is js.VariableUse && |
334 varDefns.containsKey(existing.name)) { | 343 varDefns.containsKey(existing.name)) { |
335 // We end up here if the subclasses have a DAG structure. We | 344 // We end up here if the subclasses have a DAG structure. We |
336 // don't have DAGs yet, but if the dispatch is used for mixins | 345 // don't have DAGs yet, but if the dispatch is used for mixins |
337 // that will be a possibility. | 346 // that will be a possibility. |
338 // Re-use the previously created temporary variable. | 347 // Re-use the previously created temporary variable. |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
388 } | 397 } |
389 | 398 |
390 // [table] is a list of lists, each inner list of the form: | 399 // [table] is a list of lists, each inner list of the form: |
391 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag] | 400 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag] |
392 // E.g. | 401 // E.g. |
393 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...] | 402 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...] |
394 js.Expression table = | 403 js.Expression table = |
395 new js.ArrayInitializer.from( | 404 new js.ArrayInitializer.from( |
396 preorderDispatchClasses.map((cls) => | 405 preorderDispatchClasses.map((cls) => |
397 new js.ArrayInitializer.from([ | 406 new js.ArrayInitializer.from([ |
398 new js.LiteralString("'${toNativeTag(cls)}'"), | 407 new js.LiteralString("'${toNativeName(cls)}'"), |
399 tagDefns[cls]]))); | 408 tagDefns[cls]]))); |
400 | 409 |
401 // $.dynamicSetMetadata(table); | 410 // $.dynamicSetMetadata(table); |
402 statements.add( | 411 statements.add( |
403 new js.ExpressionStatement( | 412 new js.ExpressionStatement( |
404 new js.Call( | 413 new js.Call( |
405 new js.VariableUse(dynamicSetMetadataName), | 414 new js.VariableUse(dynamicSetMetadataName), |
406 [table]))); | 415 [table]))); |
407 | 416 |
408 // (function(){statements})(); | 417 // (function(){statements})(); |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
497 if (!first) targetBuffer.add(",\n"); | 506 if (!first) targetBuffer.add(",\n"); |
498 targetBuffer.add(" $name: $function"); | 507 targetBuffer.add(" $name: $function"); |
499 first = false; | 508 first = false; |
500 }); | 509 }); |
501 targetBuffer.add("\n});\n\n"); | 510 targetBuffer.add("\n});\n\n"); |
502 } | 511 } |
503 targetBuffer.add(nativeBuffer); | 512 targetBuffer.add(nativeBuffer); |
504 targetBuffer.add('\n'); | 513 targetBuffer.add('\n'); |
505 } | 514 } |
506 } | 515 } |
OLD | NEW |