| 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 class NativeEmitter { | 5 class NativeEmitter { |
| 6 | 6 |
| 7 CodeEmitterTask emitter; | 7 CodeEmitterTask emitter; |
| 8 CodeBuffer nativeBuffer; | 8 CodeBuffer nativeBuffer; |
| 9 | 9 |
| 10 // Classes that participate in dynamic dispatch. These are the | 10 // Classes that participate in dynamic dispatch. These are the |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 : classesWithDynamicDispatch = new Set<ClassElement>(), | 41 : classesWithDynamicDispatch = new Set<ClassElement>(), |
| 42 nativeClasses = new Set<ClassElement>(), | 42 nativeClasses = new Set<ClassElement>(), |
| 43 subtypes = new Map<ClassElement, List<ClassElement>>(), | 43 subtypes = new Map<ClassElement, List<ClassElement>>(), |
| 44 directSubtypes = new Map<ClassElement, List<ClassElement>>(), | 44 directSubtypes = new Map<ClassElement, List<ClassElement>>(), |
| 45 overriddenMethods = new Set<FunctionElement>(), | 45 overriddenMethods = new Set<FunctionElement>(), |
| 46 nativeMethods = new Set<FunctionElement>(), | 46 nativeMethods = new Set<FunctionElement>(), |
| 47 redirectingMethods = new Map<FunctionElement, String>(), | 47 redirectingMethods = new Map<FunctionElement, String>(), |
| 48 nativeBuffer = new CodeBuffer(); | 48 nativeBuffer = new CodeBuffer(); |
| 49 | 49 |
| 50 Compiler get compiler => emitter.compiler; | 50 Compiler get compiler => emitter.compiler; |
| 51 JavaScriptBackend get backend => compiler.backend; |
| 51 | 52 |
| 52 void addRedirectingMethod(FunctionElement element, String name) { | 53 void addRedirectingMethod(FunctionElement element, String name) { |
| 53 redirectingMethods[element] = name; | 54 redirectingMethods[element] = name; |
| 54 } | 55 } |
| 55 | 56 |
| 56 String get dynamicName { | 57 String get dynamicName { |
| 57 Element element = compiler.findHelper( | 58 Element element = compiler.findHelper( |
| 58 const SourceString('dynamicFunction')); | 59 const SourceString('dynamicFunction')); |
| 59 return compiler.namer.isolateAccess(element); | 60 return backend.namer.isolateAccess(element); |
| 60 } | 61 } |
| 61 | 62 |
| 62 String get dynamicSetMetadataName { | 63 String get dynamicSetMetadataName { |
| 63 Element element = compiler.findHelper( | 64 Element element = compiler.findHelper( |
| 64 const SourceString('dynamicSetMetadata')); | 65 const SourceString('dynamicSetMetadata')); |
| 65 return compiler.namer.isolateAccess(element); | 66 return backend.namer.isolateAccess(element); |
| 66 } | 67 } |
| 67 | 68 |
| 68 String get typeNameOfName { | 69 String get typeNameOfName { |
| 69 Element element = compiler.findHelper( | 70 Element element = compiler.findHelper( |
| 70 const SourceString('getTypeNameOf')); | 71 const SourceString('getTypeNameOf')); |
| 71 return compiler.namer.isolateAccess(element); | 72 return backend.namer.isolateAccess(element); |
| 72 } | 73 } |
| 73 | 74 |
| 74 String get defPropName { | 75 String get defPropName { |
| 75 Element element = compiler.findHelper( | 76 Element element = compiler.findHelper( |
| 76 const SourceString('defineProperty')); | 77 const SourceString('defineProperty')); |
| 77 return compiler.namer.isolateAccess(element); | 78 return backend.namer.isolateAccess(element); |
| 78 } | 79 } |
| 79 | 80 |
| 80 String get toStringHelperName { | 81 String get toStringHelperName { |
| 81 Element element = compiler.findHelper( | 82 Element element = compiler.findHelper( |
| 82 const SourceString('toStringForNativeObject')); | 83 const SourceString('toStringForNativeObject')); |
| 83 return compiler.namer.isolateAccess(element); | 84 return backend.namer.isolateAccess(element); |
| 84 } | 85 } |
| 85 | 86 |
| 86 String get defineNativeClassName | 87 String get defineNativeClassName |
| 87 => '${compiler.namer.CURRENT_ISOLATE}.\$defineNativeClass'; | 88 => '${backend.namer.CURRENT_ISOLATE}.\$defineNativeClass'; |
| 88 | 89 |
| 89 String get defineNativeClassFunction { | 90 String get defineNativeClassFunction { |
| 90 return """ | 91 return """ |
| 91 function(cls, fields, methods) { | 92 function(cls, fields, methods) { |
| 92 var generateGetterSetter = ${emitter.generateGetterSetterFunction}; | 93 var generateGetterSetter = ${emitter.generateGetterSetterFunction}; |
| 93 for (var i = 0; i < fields.length; i++) { | 94 for (var i = 0; i < fields.length; i++) { |
| 94 generateGetterSetter(fields[i], methods); | 95 generateGetterSetter(fields[i], methods); |
| 95 } | 96 } |
| 96 for (var method in methods) { | 97 for (var method in methods) { |
| 97 $dynamicName(method)[cls] = methods[method]; | 98 $dynamicName(method)[cls] = methods[method]; |
| 98 } | 99 } |
| 99 }"""; | 100 }"""; |
| 100 } | 101 } |
| 101 | 102 |
| 102 void generateNativeLiteral(ClassElement classElement) { | 103 void generateNativeLiteral(ClassElement classElement) { |
| 103 String quotedNative = classElement.nativeName.slowToString(); | 104 String quotedNative = classElement.nativeName.slowToString(); |
| 104 String nativeCode = quotedNative.substring(2, quotedNative.length - 1); | 105 String nativeCode = quotedNative.substring(2, quotedNative.length - 1); |
| 105 String className = compiler.namer.getName(classElement); | 106 String className = backend.namer.getName(classElement); |
| 106 nativeBuffer.add(className); | 107 nativeBuffer.add(className); |
| 107 nativeBuffer.add(' = '); | 108 nativeBuffer.add(' = '); |
| 108 nativeBuffer.add(nativeCode); | 109 nativeBuffer.add(nativeCode); |
| 109 nativeBuffer.add(';\n'); | 110 nativeBuffer.add(';\n'); |
| 110 | 111 |
| 111 void defineInstanceMember(String name, CodeBuffer value) { | 112 void defineInstanceMember(String name, CodeBuffer value) { |
| 112 nativeBuffer.add("$className.$name = $value;\n"); | 113 nativeBuffer.add("$className.$name = $value;\n"); |
| 113 } | 114 } |
| 114 | 115 |
| 115 for (Element member in classElement.localMembers) { | 116 for (Element member in classElement.localMembers) { |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 171 List<ClassElement> result = directSubtypes[cls]; | 172 List<ClassElement> result = directSubtypes[cls]; |
| 172 return result === null ? const<ClassElement>[] : result; | 173 return result === null ? const<ClassElement>[] : result; |
| 173 } | 174 } |
| 174 | 175 |
| 175 void potentiallyConvertDartClosuresToJs(CodeBuffer code, | 176 void potentiallyConvertDartClosuresToJs(CodeBuffer code, |
| 176 FunctionElement member, | 177 FunctionElement member, |
| 177 List<String> argumentsBuffer) { | 178 List<String> argumentsBuffer) { |
| 178 FunctionSignature parameters = member.computeSignature(compiler); | 179 FunctionSignature parameters = member.computeSignature(compiler); |
| 179 Element converter = | 180 Element converter = |
| 180 compiler.findHelper(const SourceString('convertDartClosureToJS')); | 181 compiler.findHelper(const SourceString('convertDartClosureToJS')); |
| 181 String closureConverter = compiler.namer.isolateAccess(converter); | 182 String closureConverter = backend.namer.isolateAccess(converter); |
| 182 parameters.forEachParameter((Element parameter) { | 183 parameters.forEachParameter((Element parameter) { |
| 183 String name = parameter.name.slowToString(); | 184 String name = parameter.name.slowToString(); |
| 184 // If [name] is not in [argumentsBuffer], then the parameter is | 185 // If [name] is not in [argumentsBuffer], then the parameter is |
| 185 // an optional parameter that was not provided for that stub. | 186 // an optional parameter that was not provided for that stub. |
| 186 if (argumentsBuffer.indexOf(name) == -1) return; | 187 if (argumentsBuffer.indexOf(name) == -1) return; |
| 187 DartType type = parameter.computeType(compiler).unalias(compiler); | 188 DartType type = parameter.computeType(compiler).unalias(compiler); |
| 188 if (type is FunctionType) { | 189 if (type is FunctionType) { |
| 189 // The parameter type is a function type either directly or through | 190 // The parameter type is a function type either directly or through |
| 190 // typedef(s). | 191 // typedef(s). |
| 191 int arity = type.computeArity(); | 192 int arity = type.computeArity(); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 216 String nativeName = classElement.nativeName.slowToString(); | 217 String nativeName = classElement.nativeName.slowToString(); |
| 217 String nativeArguments = Strings.join(nativeArgumentsBuffer, ","); | 218 String nativeArguments = Strings.join(nativeArgumentsBuffer, ","); |
| 218 | 219 |
| 219 CodeBuffer code = new CodeBuffer(); | 220 CodeBuffer code = new CodeBuffer(); |
| 220 potentiallyConvertDartClosuresToJs(code, member, argumentsBuffer); | 221 potentiallyConvertDartClosuresToJs(code, member, argumentsBuffer); |
| 221 | 222 |
| 222 if (!nativeMethods.contains(member)) { | 223 if (!nativeMethods.contains(member)) { |
| 223 // When calling a method that has a native body, we call it | 224 // When calling a method that has a native body, we call it |
| 224 // with our calling conventions. | 225 // with our calling conventions. |
| 225 String arguments = Strings.join(argumentsBuffer, ","); | 226 String arguments = Strings.join(argumentsBuffer, ","); |
| 226 code.add(' return this.${compiler.namer.getName(member)}($arguments)'); | 227 code.add(' return this.${backend.namer.getName(member)}($arguments)'); |
| 227 } else { | 228 } else { |
| 228 // When calling a JS method, we call it with the native name. | 229 // When calling a JS method, we call it with the native name. |
| 229 String name = redirectingMethods[member]; | 230 String name = redirectingMethods[member]; |
| 230 if (name === null) name = member.name.slowToString(); | 231 if (name === null) name = member.name.slowToString(); |
| 231 code.add(' return this.$name($nativeArguments);'); | 232 code.add(' return this.$name($nativeArguments);'); |
| 232 } | 233 } |
| 233 | 234 |
| 234 if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) { | 235 if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) { |
| 235 // Call the method directly. | 236 // Call the method directly. |
| 236 buffer.add(code.toString()); | 237 buffer.add(code.toString()); |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 bool requiresNativeIsCheck(Element element) { | 379 bool requiresNativeIsCheck(Element element) { |
| 379 if (!element.isClass()) return false; | 380 if (!element.isClass()) return false; |
| 380 ClassElement cls = element; | 381 ClassElement cls = element; |
| 381 if (cls.isNative()) return true; | 382 if (cls.isNative()) return true; |
| 382 return isSupertypeOfNativeClass(element); | 383 return isSupertypeOfNativeClass(element); |
| 383 } | 384 } |
| 384 | 385 |
| 385 void emitIsChecks(Map<String, String> objectProperties) { | 386 void emitIsChecks(Map<String, String> objectProperties) { |
| 386 for (Element type in compiler.codegenWorld.isChecks) { | 387 for (Element type in compiler.codegenWorld.isChecks) { |
| 387 if (!requiresNativeIsCheck(type)) continue; | 388 if (!requiresNativeIsCheck(type)) continue; |
| 388 String name = compiler.namer.operatorIs(type); | 389 String name = backend.namer.operatorIs(type); |
| 389 objectProperties[name] = 'function() { return false; }'; | 390 objectProperties[name] = 'function() { return false; }'; |
| 390 } | 391 } |
| 391 } | 392 } |
| 392 | 393 |
| 393 void assembleCode(CodeBuffer targetBuffer) { | 394 void assembleCode(CodeBuffer targetBuffer) { |
| 394 if (nativeClasses.isEmpty()) return; | 395 if (nativeClasses.isEmpty()) return; |
| 395 emitDynamicDispatchMetadata(); | 396 emitDynamicDispatchMetadata(); |
| 396 targetBuffer.add('$defineNativeClassName = ' | 397 targetBuffer.add('$defineNativeClassName = ' |
| 397 '$defineNativeClassFunction;\n\n'); | 398 '$defineNativeClassFunction;\n\n'); |
| 398 | 399 |
| 399 // Because of native classes, we have to generate some is checks | 400 // Because of native classes, we have to generate some is checks |
| 400 // by calling a method, instead of accessing a property. So we | 401 // by calling a method, instead of accessing a property. So we |
| 401 // attach to the JS Object prototype these methods that return | 402 // attach to the JS Object prototype these methods that return |
| 402 // false, and will be overridden by subclasses when they have to | 403 // false, and will be overridden by subclasses when they have to |
| 403 // return true. | 404 // return true. |
| 404 Map<String, String> objectProperties = new Map<String, String>(); | 405 Map<String, String> objectProperties = new Map<String, String>(); |
| 405 emitIsChecks(objectProperties); | 406 emitIsChecks(objectProperties); |
| 406 | 407 |
| 407 // In order to have the toString method on every native class, | 408 // In order to have the toString method on every native class, |
| 408 // we must patch the JS Object prototype with a helper method. | 409 // we must patch the JS Object prototype with a helper method. |
| 409 String toStringName = compiler.namer.instanceMethodName( | 410 String toStringName = backend.namer.instanceMethodName( |
| 410 null, const SourceString('toString'), 0); | 411 null, const SourceString('toString'), 0); |
| 411 objectProperties[toStringName] = | 412 objectProperties[toStringName] = |
| 412 'function() { return $toStringHelperName(this); }'; | 413 'function() { return $toStringHelperName(this); }'; |
| 413 | 414 |
| 414 // If the native emitter has been asked to take care of the | 415 // If the native emitter has been asked to take care of the |
| 415 // noSuchMethod handlers, we do that now. | 416 // noSuchMethod handlers, we do that now. |
| 416 if (handleNoSuchMethod) { | 417 if (handleNoSuchMethod) { |
| 417 emitter.emitNoSuchMethodHandlers((String name, CodeBuffer buffer) { | 418 emitter.emitNoSuchMethodHandlers((String name, CodeBuffer buffer) { |
| 418 objectProperties[name] = buffer.toString(); | 419 objectProperties[name] = buffer.toString(); |
| 419 }); | 420 }); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 432 if (!first) targetBuffer.add(",\n"); | 433 if (!first) targetBuffer.add(",\n"); |
| 433 targetBuffer.add(" $name: $function"); | 434 targetBuffer.add(" $name: $function"); |
| 434 first = false; | 435 first = false; |
| 435 }); | 436 }); |
| 436 targetBuffer.add("\n});\n\n"); | 437 targetBuffer.add("\n});\n\n"); |
| 437 } | 438 } |
| 438 targetBuffer.add('$nativeBuffer'); | 439 targetBuffer.add('$nativeBuffer'); |
| 439 targetBuffer.add('\n'); | 440 targetBuffer.add('\n'); |
| 440 } | 441 } |
| 441 } | 442 } |
| OLD | NEW |