| 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 library native; | 5 library native; |
| 6 | 6 |
| 7 import 'dart:collection' show Queue; | 7 import 'dart:collection' show Queue; |
| 8 import 'dart2jslib.dart'; | 8 import 'dart2jslib.dart'; |
| 9 import 'dart_types.dart'; | 9 import 'dart_types.dart'; |
| 10 import 'elements/elements.dart'; | 10 import 'elements/elements.dart'; |
| (...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 281 ClassElement get annotationJsNameClass { | 281 ClassElement get annotationJsNameClass { |
| 282 findAnnotationClasses(); | 282 findAnnotationClasses(); |
| 283 return _annotationJsNameClass; | 283 return _annotationJsNameClass; |
| 284 } | 284 } |
| 285 | 285 |
| 286 void findAnnotationClasses() { | 286 void findAnnotationClasses() { |
| 287 if (_annotationCreatesClass != null) return; | 287 if (_annotationCreatesClass != null) return; |
| 288 ClassElement find(name) { | 288 ClassElement find(name) { |
| 289 Element e = compiler.findHelper(name); | 289 Element e = compiler.findHelper(name); |
| 290 if (e == null || e is! ClassElement) { | 290 if (e == null || e is! ClassElement) { |
| 291 compiler.cancel("Could not find implementation class '${name}'"); | 291 compiler.internalError(NO_LOCATION_SPANNABLE, |
| 292 "Could not find implementation class '${name}'."); |
| 292 } | 293 } |
| 293 return e; | 294 return e; |
| 294 } | 295 } |
| 295 _annotationCreatesClass = find('Creates'); | 296 _annotationCreatesClass = find('Creates'); |
| 296 _annotationReturnsClass = find('Returns'); | 297 _annotationReturnsClass = find('Returns'); |
| 297 _annotationJsNameClass = find('JSName'); | 298 _annotationJsNameClass = find('JSName'); |
| 298 } | 299 } |
| 299 | 300 |
| 300 /// Returns the JSName annotation string or `null` if no JSName annotation is | 301 /// Returns the JSName annotation string or `null` if no JSName annotation is |
| 301 /// present. | 302 /// present. |
| 302 String findJsNameFromAnnotation(Element element) { | 303 String findJsNameFromAnnotation(Element element) { |
| 303 String name = null; | 304 String name = null; |
| 304 ClassElement annotationClass = annotationJsNameClass; | 305 ClassElement annotationClass = annotationJsNameClass; |
| 305 for (Link<MetadataAnnotation> link = element.metadata; | 306 for (Link<MetadataAnnotation> link = element.metadata; |
| 306 !link.isEmpty; | 307 !link.isEmpty; |
| 307 link = link.tail) { | 308 link = link.tail) { |
| 308 MetadataAnnotation annotation = link.head.ensureResolved(compiler); | 309 MetadataAnnotation annotation = link.head.ensureResolved(compiler); |
| 309 var value = annotation.value; | 310 var value = annotation.value; |
| 310 if (value is! ConstructedConstant) continue; | 311 if (value is! ConstructedConstant) continue; |
| 311 if (value.type is! InterfaceType) continue; | 312 if (value.type is! InterfaceType) continue; |
| 312 if (!identical(value.type.element, annotationClass)) continue; | 313 if (!identical(value.type.element, annotationClass)) continue; |
| 313 | 314 |
| 314 var fields = value.fields; | 315 var fields = value.fields; |
| 315 // TODO(sra): Better validation of the constant. | 316 // TODO(sra): Better validation of the constant. |
| 316 if (fields.length != 1 || fields[0] is! StringConstant) { | 317 if (fields.length != 1 || fields[0] is! StringConstant) { |
| 317 PartialMetadataAnnotation partial = annotation; | 318 PartialMetadataAnnotation partial = annotation; |
| 318 compiler.cancel( | 319 compiler.internalError(annotation, |
| 319 'Annotations needs one string: ${partial.parseNode(compiler)}'); | 320 'Annotations needs one string: ${partial.parseNode(compiler)}'); |
| 320 } | 321 } |
| 321 String specString = fields[0].toDartString().slowToString(); | 322 String specString = fields[0].toDartString().slowToString(); |
| 322 if (name == null) { | 323 if (name == null) { |
| 323 name = specString; | 324 name = specString; |
| 324 } else { | 325 } else { |
| 325 PartialMetadataAnnotation partial = annotation; | 326 PartialMetadataAnnotation partial = annotation; |
| 326 compiler.cancel( | 327 compiler.internalError(annotation, |
| 327 'Too many JSName annotations: ${partial.parseNode(compiler)}'); | 328 'Too many JSName annotations: ${partial.parseNode(compiler)}'); |
| 328 } | 329 } |
| 329 } | 330 } |
| 330 return name; | 331 return name; |
| 331 } | 332 } |
| 332 | 333 |
| 333 enqueueClass(ClassElement classElement, cause) { | 334 enqueueClass(ClassElement classElement, cause) { |
| 334 assert(unusedClasses.contains(classElement)); | 335 assert(unusedClasses.contains(classElement)); |
| 335 unusedClasses.remove(classElement); | 336 unusedClasses.remove(classElement); |
| 336 pendingClasses.add(classElement); | 337 pendingClasses.add(classElement); |
| (...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 757 | 758 |
| 758 static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) { | 759 static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) { |
| 759 // The first argument of a JS-call is a string encoding various attributes | 760 // The first argument of a JS-call is a string encoding various attributes |
| 760 // of the code. | 761 // of the code. |
| 761 // | 762 // |
| 762 // 'Type1|Type2'. A union type. | 763 // 'Type1|Type2'. A union type. |
| 763 // '=Object'. A JavaScript Object, no subtype. | 764 // '=Object'. A JavaScript Object, no subtype. |
| 764 | 765 |
| 765 var argNodes = jsCall.arguments; | 766 var argNodes = jsCall.arguments; |
| 766 if (argNodes.isEmpty) { | 767 if (argNodes.isEmpty) { |
| 767 compiler.cancel("JS expression has no type", node: jsCall); | 768 compiler.internalError(jsCall, "JS expression has no type."); |
| 768 } | 769 } |
| 769 | 770 |
| 770 var code = argNodes.tail.head; | 771 var code = argNodes.tail.head; |
| 771 if (code is !StringNode || code.isInterpolation) { | 772 if (code is !StringNode || code.isInterpolation) { |
| 772 compiler.cancel('JS code must be a string literal', node: code); | 773 compiler.internalError(code, 'JS code must be a string literal.'); |
| 773 } | 774 } |
| 774 | 775 |
| 775 LiteralString specLiteral = argNodes.head.asLiteralString(); | 776 LiteralString specLiteral = argNodes.head.asLiteralString(); |
| 776 if (specLiteral == null) { | 777 if (specLiteral == null) { |
| 777 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It | 778 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It |
| 778 // is not very satisfactory because it does not work for void, dynamic. | 779 // is not very satisfactory because it does not work for void, dynamic. |
| 779 compiler.cancel("Unexpected JS first argument", node: argNodes.head); | 780 compiler.internalError(argNodes.head, "Unexpected JS first argument."); |
| 780 } | 781 } |
| 781 | 782 |
| 782 var behavior = new NativeBehavior(); | 783 var behavior = new NativeBehavior(); |
| 783 behavior.codeAst = js.js.parseForeignJS(code.dartString.slowToString()); | 784 behavior.codeAst = js.js.parseForeignJS(code.dartString.slowToString()); |
| 784 new SideEffectsVisitor(behavior.sideEffects).visit(behavior.codeAst); | 785 new SideEffectsVisitor(behavior.sideEffects).visit(behavior.codeAst); |
| 785 | 786 |
| 786 String specString = specLiteral.dartString.slowToString(); | 787 String specString = specLiteral.dartString.slowToString(); |
| 787 // Various things that are not in fact types. | 788 // Various things that are not in fact types. |
| 788 if (specString == 'void') return behavior; | 789 if (specString == 'void') return behavior; |
| 789 if (specString == '' || specString == 'var') { | 790 if (specString == '' || specString == 'var') { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 884 MetadataAnnotation annotation = link.head.ensureResolved(compiler); | 885 MetadataAnnotation annotation = link.head.ensureResolved(compiler); |
| 885 var value = annotation.value; | 886 var value = annotation.value; |
| 886 if (value is! ConstructedConstant) continue; | 887 if (value is! ConstructedConstant) continue; |
| 887 if (value.type is! InterfaceType) continue; | 888 if (value.type is! InterfaceType) continue; |
| 888 if (!identical(value.type.element, annotationClass)) continue; | 889 if (!identical(value.type.element, annotationClass)) continue; |
| 889 | 890 |
| 890 var fields = value.fields; | 891 var fields = value.fields; |
| 891 // TODO(sra): Better validation of the constant. | 892 // TODO(sra): Better validation of the constant. |
| 892 if (fields.length != 1 || fields[0] is! StringConstant) { | 893 if (fields.length != 1 || fields[0] is! StringConstant) { |
| 893 PartialMetadataAnnotation partial = annotation; | 894 PartialMetadataAnnotation partial = annotation; |
| 894 compiler.cancel( | 895 compiler.internalError(annotation, |
| 895 'Annotations needs one string: ${partial.parseNode(compiler)}'); | 896 'Annotations needs one string: ${partial.parseNode(compiler)}'); |
| 896 } | 897 } |
| 897 String specString = fields[0].toDartString().slowToString(); | 898 String specString = fields[0].toDartString().slowToString(); |
| 898 for (final typeString in specString.split('|')) { | 899 for (final typeString in specString.split('|')) { |
| 899 var type = _parseType(typeString, compiler, lookup, annotation); | 900 var type = _parseType(typeString, compiler, lookup, annotation); |
| 900 if (types == null) types = []; | 901 if (types == null) types = []; |
| 901 types.add(type); | 902 types.add(type); |
| 902 } | 903 } |
| 903 } | 904 } |
| 904 return types; | 905 return types; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 943 lookup(name), locationNodeOrElement) { | 944 lookup(name), locationNodeOrElement) { |
| 944 if (typeString == '=Object') return SpecialType.JsObject; | 945 if (typeString == '=Object') return SpecialType.JsObject; |
| 945 if (typeString == 'dynamic') { | 946 if (typeString == 'dynamic') { |
| 946 return compiler.types.dynamicType; | 947 return compiler.types.dynamicType; |
| 947 } | 948 } |
| 948 DartType type = lookup(typeString); | 949 DartType type = lookup(typeString); |
| 949 if (type != null) return type; | 950 if (type != null) return type; |
| 950 | 951 |
| 951 int index = typeString.indexOf('<'); | 952 int index = typeString.indexOf('<'); |
| 952 if (index < 1) { | 953 if (index < 1) { |
| 953 compiler.cancel("Type '$typeString' not found", | 954 compiler.internalError( |
| 954 node: _errorNode(locationNodeOrElement, compiler)); | 955 _errorNode(locationNodeOrElement, compiler), |
| 956 "Type '$typeString' not found."); |
| 955 } | 957 } |
| 956 type = lookup(typeString.substring(0, index)); | 958 type = lookup(typeString.substring(0, index)); |
| 957 if (type != null) { | 959 if (type != null) { |
| 958 // TODO(sra): Parse type parameters. | 960 // TODO(sra): Parse type parameters. |
| 959 return type; | 961 return type; |
| 960 } | 962 } |
| 961 compiler.cancel("Type '$typeString' not found", | 963 compiler.internalError( |
| 962 node: _errorNode(locationNodeOrElement, compiler)); | 964 _errorNode(locationNodeOrElement, compiler), |
| 965 "Type '$typeString' not found."); |
| 963 } | 966 } |
| 964 | 967 |
| 965 static _errorNode(locationNodeOrElement, compiler) { | 968 static _errorNode(locationNodeOrElement, compiler) { |
| 966 if (locationNodeOrElement is Node) return locationNodeOrElement; | 969 if (locationNodeOrElement is Node) return locationNodeOrElement; |
| 967 return locationNodeOrElement.parseNode(compiler); | 970 return locationNodeOrElement.parseNode(compiler); |
| 968 } | 971 } |
| 969 } | 972 } |
| 970 | 973 |
| 971 void checkAllowedLibrary(ElementListener listener, Token token) { | 974 void checkAllowedLibrary(ElementListener listener, Token token) { |
| 972 LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary(); | 975 LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary(); |
| 973 if (!currentLibrary.canUseNative) { | 976 if (!currentLibrary.canUseNative) { |
| 974 listener.recoverableError("Unexpected token", token: token); | 977 listener.recoverableError(token, "Unexpected token"); |
| 975 } | 978 } |
| 976 } | 979 } |
| 977 | 980 |
| 978 Token handleNativeBlockToSkip(Listener listener, Token token) { | 981 Token handleNativeBlockToSkip(Listener listener, Token token) { |
| 979 checkAllowedLibrary(listener, token); | 982 checkAllowedLibrary(listener, token); |
| 980 token = token.next; | 983 token = token.next; |
| 981 if (identical(token.kind, STRING_TOKEN)) { | 984 if (identical(token.kind, STRING_TOKEN)) { |
| 982 token = token.next; | 985 token = token.next; |
| 983 } | 986 } |
| 984 if (identical(token.stringValue, '{')) { | 987 if (identical(token.stringValue, '{')) { |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1073 // No longer supported, this is now done with @JSName('foo') and case 1. | 1076 // No longer supported, this is now done with @JSName('foo') and case 1. |
| 1074 // 3) foo() native "return 42"; | 1077 // 3) foo() native "return 42"; |
| 1075 // hasBody = true | 1078 // hasBody = true |
| 1076 bool hasBody = false; | 1079 bool hasBody = false; |
| 1077 assert(element.isNative()); | 1080 assert(element.isNative()); |
| 1078 String nativeMethodName = element.fixedBackendName(); | 1081 String nativeMethodName = element.fixedBackendName(); |
| 1079 if (nativeBody != null) { | 1082 if (nativeBody != null) { |
| 1080 LiteralString jsCode = nativeBody.asLiteralString(); | 1083 LiteralString jsCode = nativeBody.asLiteralString(); |
| 1081 String str = jsCode.dartString.slowToString(); | 1084 String str = jsCode.dartString.slowToString(); |
| 1082 if (nativeRedirectionRegExp.hasMatch(str)) { | 1085 if (nativeRedirectionRegExp.hasMatch(str)) { |
| 1083 compiler.cancel("Deprecated syntax, use @JSName('name') instead.", | 1086 compiler.internalError( |
| 1084 node: nativeBody); | 1087 nativeBody, "Deprecated syntax, use @JSName('name') instead."); |
| 1085 } | 1088 } |
| 1086 hasBody = true; | 1089 hasBody = true; |
| 1087 } | 1090 } |
| 1088 | 1091 |
| 1089 if (!hasBody) { | 1092 if (!hasBody) { |
| 1090 nativeEmitter.nativeMethods.add(element); | 1093 nativeEmitter.nativeMethods.add(element); |
| 1091 } | 1094 } |
| 1092 | 1095 |
| 1093 FunctionSignature parameters = element.functionSignature; | 1096 FunctionSignature parameters = element.functionSignature; |
| 1094 if (!hasBody) { | 1097 if (!hasBody) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1113 | 1116 |
| 1114 String foreignParameters = arguments.join(','); | 1117 String foreignParameters = arguments.join(','); |
| 1115 String nativeMethodCall; | 1118 String nativeMethodCall; |
| 1116 if (element.kind == ElementKind.FUNCTION) { | 1119 if (element.kind == ElementKind.FUNCTION) { |
| 1117 nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)'; | 1120 nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)'; |
| 1118 } else if (element.kind == ElementKind.GETTER) { | 1121 } else if (element.kind == ElementKind.GETTER) { |
| 1119 nativeMethodCall = '$receiver$nativeMethodName'; | 1122 nativeMethodCall = '$receiver$nativeMethodName'; |
| 1120 } else if (element.kind == ElementKind.SETTER) { | 1123 } else if (element.kind == ElementKind.SETTER) { |
| 1121 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; | 1124 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; |
| 1122 } else { | 1125 } else { |
| 1123 builder.compiler.internalError('unexpected kind: "${element.kind}"', | 1126 builder.compiler.internalError(element, |
| 1124 element: element); | 1127 'Unexpected kind: "${element.kind}".'); |
| 1125 } | 1128 } |
| 1126 | 1129 |
| 1127 builder.push(new HForeign(js.js(nativeMethodCall), backend.dynamicType, | 1130 builder.push(new HForeign(js.js(nativeMethodCall), backend.dynamicType, |
| 1128 inputs, effects: new SideEffects())); | 1131 inputs, effects: new SideEffects())); |
| 1129 builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); | 1132 builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); |
| 1130 } else { | 1133 } else { |
| 1131 if (parameters.parameterCount != 0) { | 1134 if (parameters.parameterCount != 0) { |
| 1132 compiler.cancel( | 1135 compiler.internalError(nativeBody, |
| 1133 'native "..." syntax is restricted to functions with zero parameters', | 1136 'native "..." syntax is restricted to ' |
| 1134 node: nativeBody); | 1137 'functions with zero parameters.'); |
| 1135 } | 1138 } |
| 1136 LiteralString jsCode = nativeBody.asLiteralString(); | 1139 LiteralString jsCode = nativeBody.asLiteralString(); |
| 1137 builder.push(new HForeign.statement( | 1140 builder.push(new HForeign.statement( |
| 1138 new js.LiteralStatement(jsCode.dartString.slowToString()), | 1141 new js.LiteralStatement(jsCode.dartString.slowToString()), |
| 1139 <HInstruction>[], | 1142 <HInstruction>[], |
| 1140 new SideEffects(), | 1143 new SideEffects(), |
| 1141 null, | 1144 null, |
| 1142 backend.dynamicType)); | 1145 backend.dynamicType)); |
| 1143 } | 1146 } |
| 1144 } | 1147 } |
| OLD | NEW |