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