| 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 dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
| 6 | 6 |
| 7 class NativeEmitter { | 7 class NativeEmitter { |
| 8 | |
| 9 // TODO(floitsch): the native-emitter should not know about ClassBuilders. | 8 // TODO(floitsch): the native-emitter should not know about ClassBuilders. |
| 10 final Map<Element, full_js_emitter.ClassBuilder> cachedBuilders; | 9 final Map<Element, full_js_emitter.ClassBuilder> cachedBuilders; |
| 11 | 10 |
| 12 final CodeEmitterTask emitterTask; | 11 final CodeEmitterTask emitterTask; |
| 13 | 12 |
| 14 // Whether the application contains native classes. | 13 // Whether the application contains native classes. |
| 15 bool hasNativeClasses = false; | 14 bool hasNativeClasses = false; |
| 16 | 15 |
| 17 // Caches the native subtypes of a native class. | 16 // Caches the native subtypes of a native class. |
| 18 Map<ClassElement, List<ClassElement>> subtypes; | 17 Map<ClassElement, List<ClassElement>> subtypes; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 64 * | 63 * |
| 65 * [classes] contains native classes, mixin applications, and user subclasses | 64 * [classes] contains native classes, mixin applications, and user subclasses |
| 66 * of native classes. | 65 * of native classes. |
| 67 * | 66 * |
| 68 * [interceptorClassesNeededByConstants] contains the interceptors that are | 67 * [interceptorClassesNeededByConstants] contains the interceptors that are |
| 69 * referenced by constants. | 68 * referenced by constants. |
| 70 * | 69 * |
| 71 * [classesModifiedByEmitRTISupport] contains the list of classes that must | 70 * [classesModifiedByEmitRTISupport] contains the list of classes that must |
| 72 * exist, because runtime-type support adds information to the class. | 71 * exist, because runtime-type support adds information to the class. |
| 73 */ | 72 */ |
| 74 Set<Class> prepareNativeClasses(List<Class> classes, | 73 Set<Class> prepareNativeClasses( |
| 74 List<Class> classes, |
| 75 Set<ClassElement> interceptorClassesNeededByConstants, | 75 Set<ClassElement> interceptorClassesNeededByConstants, |
| 76 Set<ClassElement> classesModifiedByEmitRTISupport) { | 76 Set<ClassElement> classesModifiedByEmitRTISupport) { |
| 77 assert(classes.every((Class cls) => cls != null)); | 77 assert(classes.every((Class cls) => cls != null)); |
| 78 | 78 |
| 79 hasNativeClasses = classes.isNotEmpty; | 79 hasNativeClasses = classes.isNotEmpty; |
| 80 | 80 |
| 81 // Compute a pre-order traversal of the subclass forest. We actually want a | 81 // Compute a pre-order traversal of the subclass forest. We actually want a |
| 82 // post-order traversal but it is easier to compute the pre-order and use it | 82 // post-order traversal but it is easier to compute the pre-order and use it |
| 83 // in reverse. | 83 // in reverse. |
| 84 List<Class> preOrder = <Class>[]; | 84 List<Class> preOrder = <Class>[]; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 } else if (interceptorClassesNeededByConstants.contains(classElement)) { | 130 } else if (interceptorClassesNeededByConstants.contains(classElement)) { |
| 131 needed = true; | 131 needed = true; |
| 132 } else if (classesModifiedByEmitRTISupport.contains(classElement)) { | 132 } else if (classesModifiedByEmitRTISupport.contains(classElement)) { |
| 133 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer | 133 // TODO(9556): Remove this test when [emitRuntimeTypeSupport] no longer |
| 134 // adds information to a class prototype or constructor. | 134 // adds information to a class prototype or constructor. |
| 135 needed = true; | 135 needed = true; |
| 136 } else if (extensionPoints.containsKey(cls)) { | 136 } else if (extensionPoints.containsKey(cls)) { |
| 137 needed = true; | 137 needed = true; |
| 138 } | 138 } |
| 139 if (backend.isJsInterop(classElement)) { | 139 if (backend.isJsInterop(classElement)) { |
| 140 needed = true; // TODO(jacobr): we don't need all interop classes. | 140 needed = true; // TODO(jacobr): we don't need all interop classes. |
| 141 } else if (cls.isNative && | 141 } else if (cls.isNative && |
| 142 backend.nativeData.hasNativeTagsForcedNonLeaf(classElement)) { | 142 backend.nativeData.hasNativeTagsForcedNonLeaf(classElement)) { |
| 143 needed = true; | 143 needed = true; |
| 144 nonLeafClasses.add(cls); | 144 nonLeafClasses.add(cls); |
| 145 } | 145 } |
| 146 | 146 |
| 147 if (needed || neededClasses.contains(cls)) { | 147 if (needed || neededClasses.contains(cls)) { |
| 148 neededClasses.add(cls); | 148 neededClasses.add(cls); |
| 149 neededClasses.add(cls.superclass); | 149 neededClasses.add(cls.superclass); |
| 150 nonLeafClasses.add(cls.superclass); | 150 nonLeafClasses.add(cls.superclass); |
| 151 } | 151 } |
| 152 } | 152 } |
| 153 | 153 |
| 154 // Collect all the tags that map to each native class. | 154 // Collect all the tags that map to each native class. |
| 155 | 155 |
| 156 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); | 156 Map<Class, Set<String>> leafTags = new Map<Class, Set<String>>(); |
| 157 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); | 157 Map<Class, Set<String>> nonleafTags = new Map<Class, Set<String>>(); |
| 158 | 158 |
| 159 for (Class cls in classes) { | 159 for (Class cls in classes) { |
| 160 if (!cls.isNative) continue; | 160 if (!cls.isNative) continue; |
| 161 if (backend.isJsInterop(cls.element)) continue; | 161 if (backend.isJsInterop(cls.element)) continue; |
| 162 List<String> nativeTags = | 162 List<String> nativeTags = |
| 163 backend.nativeData.getNativeTagsOfClass(cls.element); | 163 backend.nativeData.getNativeTagsOfClass(cls.element); |
| 164 | 164 |
| 165 if (nonLeafClasses.contains(cls) || | 165 if (nonLeafClasses.contains(cls) || extensionPoints.containsKey(cls)) { |
| 166 extensionPoints.containsKey(cls)) { | |
| 167 nonleafTags | 166 nonleafTags |
| 168 .putIfAbsent(cls, () => new Set<String>()) | 167 .putIfAbsent(cls, () => new Set<String>()) |
| 169 .addAll(nativeTags); | 168 .addAll(nativeTags); |
| 170 } else { | 169 } else { |
| 171 Class sufficingInterceptor = cls; | 170 Class sufficingInterceptor = cls; |
| 172 while (!neededClasses.contains(sufficingInterceptor)) { | 171 while (!neededClasses.contains(sufficingInterceptor)) { |
| 173 sufficingInterceptor = sufficingInterceptor.superclass; | 172 sufficingInterceptor = sufficingInterceptor.superclass; |
| 174 } | 173 } |
| 175 if (sufficingInterceptor == objectClass) { | 174 if (sufficingInterceptor == objectClass) { |
| 176 sufficingInterceptor = jsInterceptorClass; | 175 sufficingInterceptor = jsInterceptorClass; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 Class nativeAncestorOf(Class cls) { | 229 Class nativeAncestorOf(Class cls) { |
| 231 return nativeSuperclassOf(cls.superclass); | 230 return nativeSuperclassOf(cls.superclass); |
| 232 } | 231 } |
| 233 | 232 |
| 234 Map<Class, List<Class>> map = new Map<Class, List<Class>>(); | 233 Map<Class, List<Class>> map = new Map<Class, List<Class>>(); |
| 235 | 234 |
| 236 for (Class cls in classes) { | 235 for (Class cls in classes) { |
| 237 if (cls.isNative) continue; | 236 if (cls.isNative) continue; |
| 238 Class nativeAncestor = nativeAncestorOf(cls); | 237 Class nativeAncestor = nativeAncestorOf(cls); |
| 239 if (nativeAncestor != null) { | 238 if (nativeAncestor != null) { |
| 240 map | 239 map.putIfAbsent(nativeAncestor, () => <Class>[]).add(cls); |
| 241 .putIfAbsent(nativeAncestor, () => <Class>[]) | |
| 242 .add(cls); | |
| 243 } | 240 } |
| 244 } | 241 } |
| 245 return map; | 242 return map; |
| 246 } | 243 } |
| 247 | 244 |
| 248 bool isTrivialClass(Class cls) { | 245 bool isTrivialClass(Class cls) { |
| 249 bool needsAccessor(Field field) { | 246 bool needsAccessor(Field field) { |
| 250 return field.needsGetter || | 247 return field.needsGetter || |
| 251 field.needsUncheckedSetter || | 248 field.needsUncheckedSetter || |
| 252 field.needsCheckedSetter; | 249 field.needsCheckedSetter; |
| 253 } | 250 } |
| 254 | 251 |
| 255 return | 252 return cls.methods.isEmpty && |
| 256 cls.methods.isEmpty && | |
| 257 cls.isChecks.isEmpty && | 253 cls.isChecks.isEmpty && |
| 258 cls.callStubs.isEmpty && | 254 cls.callStubs.isEmpty && |
| 259 !cls.superclass.isMixinApplication && | 255 !cls.superclass.isMixinApplication && |
| 260 !cls.fields.any(needsAccessor); | 256 !cls.fields.any(needsAccessor); |
| 261 } | 257 } |
| 262 | 258 |
| 263 void potentiallyConvertDartClosuresToJs( | 259 void potentiallyConvertDartClosuresToJs(List<jsAst.Statement> statements, |
| 264 List<jsAst.Statement> statements, | 260 FunctionElement member, List<jsAst.Parameter> stubParameters) { |
| 265 FunctionElement member, | |
| 266 List<jsAst.Parameter> stubParameters) { | |
| 267 FunctionSignature parameters = member.functionSignature; | 261 FunctionSignature parameters = member.functionSignature; |
| 268 Element converter = helpers.closureConverter; | 262 Element converter = helpers.closureConverter; |
| 269 jsAst.Expression closureConverter = | 263 jsAst.Expression closureConverter = |
| 270 emitterTask.staticFunctionAccess(converter); | 264 emitterTask.staticFunctionAccess(converter); |
| 271 parameters.forEachParameter((ParameterElement parameter) { | 265 parameters.forEachParameter((ParameterElement parameter) { |
| 272 String name = parameter.name; | 266 String name = parameter.name; |
| 273 // If [name] is not in [stubParameters], then the parameter is an optional | 267 // If [name] is not in [stubParameters], then the parameter is an optional |
| 274 // parameter that was not provided for this stub. | 268 // parameter that was not provided for this stub. |
| 275 for (jsAst.Parameter stubParameter in stubParameters) { | 269 for (jsAst.Parameter stubParameter in stubParameters) { |
| 276 if (stubParameter.name == name) { | 270 if (stubParameter.name == name) { |
| 277 DartType type = parameter.type.unaliased; | 271 DartType type = parameter.type.unaliased; |
| 278 if (type is FunctionType) { | 272 if (type is FunctionType) { |
| 279 // The parameter type is a function type either directly or through | 273 // The parameter type is a function type either directly or through |
| 280 // typedef(s). | 274 // typedef(s). |
| 281 FunctionType functionType = type; | 275 FunctionType functionType = type; |
| 282 int arity = functionType.computeArity(); | 276 int arity = functionType.computeArity(); |
| 283 statements.add( | 277 statements.add(js |
| 284 js.statement('# = #(#, $arity)', | 278 .statement('# = #(#, $arity)', [name, closureConverter, name])); |
| 285 [name, closureConverter, name])); | |
| 286 break; | 279 break; |
| 287 } | 280 } |
| 288 } | 281 } |
| 289 } | 282 } |
| 290 }); | 283 }); |
| 291 } | 284 } |
| 292 | 285 |
| 293 List<jsAst.Statement> generateParameterStubStatements( | 286 List<jsAst.Statement> generateParameterStubStatements( |
| 294 FunctionElement member, | 287 FunctionElement member, |
| 295 bool isInterceptedMethod, | 288 bool isInterceptedMethod, |
| (...skipping 17 matching lines...) Expand all Loading... |
| 313 jsAst.Expression receiver; | 306 jsAst.Expression receiver; |
| 314 List<jsAst.Expression> arguments; | 307 List<jsAst.Expression> arguments; |
| 315 | 308 |
| 316 assert(invariant(member, nativeMethods.contains(member))); | 309 assert(invariant(member, nativeMethods.contains(member))); |
| 317 // When calling a JS method, we call it with the native name, and only the | 310 // When calling a JS method, we call it with the native name, and only the |
| 318 // arguments up until the last one provided. | 311 // arguments up until the last one provided. |
| 319 target = backend.nativeData.getFixedBackendName(member); | 312 target = backend.nativeData.getFixedBackendName(member); |
| 320 | 313 |
| 321 if (isInterceptedMethod) { | 314 if (isInterceptedMethod) { |
| 322 receiver = argumentsBuffer[0]; | 315 receiver = argumentsBuffer[0]; |
| 323 arguments = argumentsBuffer.sublist(1, | 316 arguments = argumentsBuffer.sublist( |
| 324 indexOfLastOptionalArgumentInParameters + 1); | 317 1, indexOfLastOptionalArgumentInParameters + 1); |
| 325 } else { | 318 } else { |
| 326 // Native methods that are not intercepted must be static. | 319 // Native methods that are not intercepted must be static. |
| 327 assert(invariant(member, member.isStatic)); | 320 assert(invariant(member, member.isStatic)); |
| 328 arguments = argumentsBuffer.sublist(0, | 321 arguments = argumentsBuffer.sublist( |
| 329 indexOfLastOptionalArgumentInParameters + 1); | 322 0, indexOfLastOptionalArgumentInParameters + 1); |
| 330 if (backend.isJsInterop(member)) { | 323 if (backend.isJsInterop(member)) { |
| 331 // fixedBackendPath is allowed to have the form foo.bar.baz for | 324 // fixedBackendPath is allowed to have the form foo.bar.baz for |
| 332 // interop. This template is uncached to avoid possibly running out of | 325 // interop. This template is uncached to avoid possibly running out of |
| 333 // memory when Dart2Js is run in server mode. In reality the risk of | 326 // memory when Dart2Js is run in server mode. In reality the risk of |
| 334 // caching these templates causing an issue is very low as each class | 327 // caching these templates causing an issue is very low as each class |
| 335 // and library that uses typed JavaScript interop will create only 1 | 328 // and library that uses typed JavaScript interop will create only 1 |
| 336 // unique template. | 329 // unique template. |
| 337 receiver = js.uncachedExpressionTemplate( | 330 receiver = js |
| 338 backend.namer.fixedBackendPath(member)).instantiate([]); | 331 .uncachedExpressionTemplate(backend.namer.fixedBackendPath(member)) |
| 332 .instantiate([]); |
| 339 } else { | 333 } else { |
| 340 receiver = js('this'); | 334 receiver = js('this'); |
| 341 } | 335 } |
| 342 } | 336 } |
| 343 statements.add( | 337 statements |
| 344 js.statement('return #.#(#)', [receiver, target, arguments])); | 338 .add(js.statement('return #.#(#)', [receiver, target, arguments])); |
| 345 | 339 |
| 346 return statements; | 340 return statements; |
| 347 } | 341 } |
| 348 | 342 |
| 349 bool isSupertypeOfNativeClass(ClassElement element) { | 343 bool isSupertypeOfNativeClass(ClassElement element) { |
| 350 if (backend.classesMixedIntoInterceptedClasses.contains(element)) { | 344 if (backend.classesMixedIntoInterceptedClasses.contains(element)) { |
| 351 return true; | 345 return true; |
| 352 } | 346 } |
| 353 | 347 |
| 354 return subtypes[element] != null; | 348 return subtypes[element] != null; |
| 355 } | 349 } |
| 356 | 350 |
| 357 bool requiresNativeIsCheck(Element element) { | 351 bool requiresNativeIsCheck(Element element) { |
| 358 // TODO(sra): Remove this function. It determines if a native type may | 352 // TODO(sra): Remove this function. It determines if a native type may |
| 359 // satisfy a check against [element], in which case an interceptor must be | 353 // satisfy a check against [element], in which case an interceptor must be |
| 360 // used. We should also use an interceptor if the check can't be satisfied | 354 // used. We should also use an interceptor if the check can't be satisfied |
| 361 // by a native class in case we get a native instance that tries to spoof | 355 // by a native class in case we get a native instance that tries to spoof |
| 362 // the type info. i.e the criteria for whether or not to use an interceptor | 356 // the type info. i.e the criteria for whether or not to use an interceptor |
| 363 // is whether the receiver can be native, not the type of the test. | 357 // is whether the receiver can be native, not the type of the test. |
| 364 if (element == null || !element.isClass) return false; | 358 if (element == null || !element.isClass) return false; |
| 365 ClassElement cls = element; | 359 ClassElement cls = element; |
| 366 if (backend.isNativeOrExtendsNative(cls)) return true; | 360 if (backend.isNativeOrExtendsNative(cls)) return true; |
| 367 return isSupertypeOfNativeClass(element); | 361 return isSupertypeOfNativeClass(element); |
| 368 } | 362 } |
| 369 } | 363 } |
| OLD | NEW |