| 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 #import('dart:uri'); | 6 #import('dart:uri'); |
| 7 #import('leg.dart'); | 7 #import('leg.dart'); |
| 8 #import('elements/elements.dart'); | 8 #import('elements/elements.dart'); |
| 9 #import('scanner/scannerlib.dart'); | 9 #import('scanner/scannerlib.dart'); |
| 10 #import('ssa/ssa.dart'); | 10 #import('ssa/ssa.dart'); |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 } | 73 } |
| 74 | 74 |
| 75 void maybeEnableNative(Compiler compiler, | 75 void maybeEnableNative(Compiler compiler, |
| 76 LibraryElement library, | 76 LibraryElement library, |
| 77 Uri uri) { | 77 Uri uri) { |
| 78 String libraryName = uri.toString(); | 78 String libraryName = uri.toString(); |
| 79 if (library.script.name.contains('dart/tests/compiler/dart2js_native') | 79 if (library.script.name.contains('dart/tests/compiler/dart2js_native') |
| 80 || libraryName == 'dart:dom_deprecated' | 80 || libraryName == 'dart:dom_deprecated' |
| 81 || libraryName == 'dart:isolate' | 81 || libraryName == 'dart:isolate' |
| 82 || libraryName == 'dart:html') { | 82 || libraryName == 'dart:html') { |
| 83 library.define(new ForeignElement( | |
| 84 const SourceString('native'), library), compiler); | |
| 85 library.canUseNative = true; | 83 library.canUseNative = true; |
| 86 if (compiler.jsIndexingBehaviorInterface !== null) { | 84 if (compiler.jsIndexingBehaviorInterface !== null) { |
| 87 library.define(compiler.jsIndexingBehaviorInterface, compiler); | 85 library.define(compiler.jsIndexingBehaviorInterface, compiler); |
| 88 } | 86 } |
| 89 } | 87 } |
| 90 | 88 |
| 91 // Additionaly, if this is a test, we allow access to foreign functions. | 89 // Additionaly, if this is a test, we allow access to foreign functions. |
| 92 if (library.script.name.contains('dart/tests/compiler/dart2js_native')) { | 90 if (library.script.name.contains('dart/tests/compiler/dart2js_native')) { |
| 93 library.define(compiler.findHelper(const SourceString('JS')), compiler); | 91 library.define(compiler.findHelper(const SourceString('JS')), compiler); |
| 94 } | 92 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 checkAllowedLibrary(listener, token); | 132 checkAllowedLibrary(listener, token); |
| 135 token = token.next; | 133 token = token.next; |
| 136 if (token.kind !== STRING_TOKEN) { | 134 if (token.kind !== STRING_TOKEN) { |
| 137 listener.unexpected(token); | 135 listener.unexpected(token); |
| 138 } else { | 136 } else { |
| 139 token = token.next; | 137 token = token.next; |
| 140 } | 138 } |
| 141 return token; | 139 return token; |
| 142 } | 140 } |
| 143 | 141 |
| 144 RegExp nativeRedirectionRegExp = const RegExp(@'^[a-zA-Z][a-zA-Z_$0-9]*$'); | |
| 145 | |
| 146 Token handleNativeFunctionBody(ElementListener listener, Token token) { | 142 Token handleNativeFunctionBody(ElementListener listener, Token token) { |
| 147 checkAllowedLibrary(listener, token); | 143 checkAllowedLibrary(listener, token); |
| 148 Token begin = token; | 144 Token begin = token; |
| 149 listener.beginExpressionStatement(token); | 145 listener.beginReturnStatement(token); |
| 150 listener.handleIdentifier(token); | |
| 151 token = token.next; | 146 token = token.next; |
| 147 bool hasExpression = false; |
| 152 if (token.kind === STRING_TOKEN) { | 148 if (token.kind === STRING_TOKEN) { |
| 149 hasExpression = true; |
| 153 listener.beginLiteralString(token); | 150 listener.beginLiteralString(token); |
| 154 listener.endLiteralString(0); | 151 listener.endLiteralString(0); |
| 155 LiteralString str = listener.popNode(); | |
| 156 listener.pushNode(new NodeList.singleton(str)); | |
| 157 listener.endSend(token); | |
| 158 token = token.next; | 152 token = token.next; |
| 159 // If this native method is just redirecting to another method, | |
| 160 // we add a return node to match the SSA builder expectations. | |
| 161 if (nativeRedirectionRegExp.hasMatch(str.dartString.slowToString())) { | |
| 162 listener.endReturnStatement(true, begin, token); | |
| 163 } else { | |
| 164 listener.endExpressionStatement(token); | |
| 165 } | |
| 166 } else { | |
| 167 listener.pushNode(new NodeList.empty()); | |
| 168 listener.endSend(token); | |
| 169 listener.endReturnStatement(true, begin, token); | |
| 170 } | 153 } |
| 171 listener.endFunctionBody(1, begin, token); | 154 listener.endReturnStatement(hasExpression, begin, token); |
| 172 // TODO(ngeoffray): expect a ';'. | 155 // TODO(ngeoffray): expect a ';'. |
| 156 // Currently there are method with both native marker and Dart body. |
| 173 return token.next; | 157 return token.next; |
| 174 } | 158 } |
| 175 | 159 |
| 176 SourceString checkForNativeClass(ElementListener listener) { | 160 SourceString checkForNativeClass(ElementListener listener) { |
| 177 SourceString nativeName; | 161 SourceString nativeName; |
| 178 Node node = listener.nodes.head; | 162 Node node = listener.nodes.head; |
| 179 if (node != null | 163 if (node != null |
| 180 && node.asIdentifier() != null | 164 && node.asIdentifier() != null |
| 181 && node.asIdentifier().source.stringValue == 'native') { | 165 && node.asIdentifier().source.stringValue == 'native') { |
| 182 nativeName = node.asIdentifier().token.next.value; | 166 nativeName = node.asIdentifier().token.next.value; |
| 183 listener.popNode(); | 167 listener.popNode(); |
| 184 } | 168 } |
| 185 return nativeName; | 169 return nativeName; |
| 186 } | 170 } |
| 187 | 171 |
| 188 bool isOverriddenMethod(FunctionElement element, | 172 bool isOverriddenMethod(FunctionElement element, |
| 189 ClassElement cls, | 173 ClassElement cls, |
| 190 NativeEmitter nativeEmitter) { | 174 NativeEmitter nativeEmitter) { |
| 191 List<ClassElement> subtypes = nativeEmitter.subtypes[cls]; | 175 List<ClassElement> subtypes = nativeEmitter.subtypes[cls]; |
| 192 if (subtypes == null) return false; | 176 if (subtypes == null) return false; |
| 193 for (ClassElement subtype in subtypes) { | 177 for (ClassElement subtype in subtypes) { |
| 194 if (subtype.lookupLocalMember(element.name) != null) return true; | 178 if (subtype.lookupLocalMember(element.name) != null) return true; |
| 195 } | 179 } |
| 196 return false; | 180 return false; |
| 197 } | 181 } |
| 198 | 182 |
| 199 void handleSsaNative(SsaBuilder builder, Send node) { | 183 void handleSsaNative(SsaBuilder builder, Expression nativeBody) { |
| 200 Compiler compiler = builder.compiler; | 184 Compiler compiler = builder.compiler; |
| 201 FunctionElement element = builder.work.element; | 185 FunctionElement element = builder.work.element; |
| 202 element.setNative(); | 186 element.setNative(); |
| 203 NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; | 187 NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; |
| 204 // If what we're compiling is a getter named 'typeName' and the native | 188 // If what we're compiling is a getter named 'typeName' and the native |
| 205 // class is named 'DOMType', we generate a call to the typeNameOf | 189 // class is named 'DOMType', we generate a call to the typeNameOf |
| 206 // function attached on the isolate. | 190 // function attached on the isolate. |
| 207 // The DOM classes assume that their 'typeName' property, which is | 191 // The DOM classes assume that their 'typeName' property, which is |
| 208 // not a JS property on the DOM types, returns the type name. | 192 // not a JS property on the DOM types, returns the type name. |
| 209 if (element.name == const SourceString('typeName') | 193 if (element.name == const SourceString('typeName') |
| 210 && element.isGetter() | 194 && element.isGetter() |
| 211 && nativeEmitter.toNativeName(element.enclosingElement) == 'DOMType') { | 195 && nativeEmitter.toNativeName(element.enclosingElement) == 'DOMType') { |
| 212 Element methodElement = | 196 Element methodElement = |
| 213 compiler.findHelper(const SourceString('getTypeNameOf')); | 197 compiler.findHelper(const SourceString('getTypeNameOf')); |
| 214 HStatic method = new HStatic(methodElement); | 198 HStatic method = new HStatic(methodElement); |
| 215 builder.add(method); | 199 builder.add(method); |
| 216 builder.push(new HInvokeStatic(Selector.INVOCATION_1, | 200 builder.push(new HInvokeStatic(Selector.INVOCATION_1, |
| 217 <HInstruction>[method, builder.localsHandler.readThis()])); | 201 <HInstruction>[method, builder.localsHandler.readThis()])); |
| 218 return; | 202 builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); |
| 219 } | 203 } |
| 220 | 204 |
| 221 HInstruction convertDartClosure(Element parameter, FunctionType type) { | 205 HInstruction convertDartClosure(Element parameter, FunctionType type) { |
| 222 HInstruction local = builder.localsHandler.readLocal(parameter); | 206 HInstruction local = builder.localsHandler.readLocal(parameter); |
| 223 // TODO(ngeoffray): For static methods, we could pass a method with a | 207 // TODO(ngeoffray): For static methods, we could pass a method with a |
| 224 // defined arity. | 208 // defined arity. |
| 225 builder.push(new HStatic(builder.interceptors.getClosureConverter())); | 209 builder.push(new HStatic(builder.interceptors.getClosureConverter())); |
| 226 HInstruction arity = builder.graph.addConstantInt(type.computeArity()); | 210 HInstruction arity = builder.graph.addConstantInt(type.computeArity()); |
| 227 List<HInstruction> callInputs = <HInstruction>[builder.pop(), local, arity]; | 211 List<HInstruction> callInputs = <HInstruction>[builder.pop(), local, arity]; |
| 228 HInstruction closure = new HInvokeStatic(Selector.INVOCATION_1, callInputs); | 212 HInstruction closure = new HInvokeStatic(Selector.INVOCATION_1, callInputs); |
| 229 builder.add(closure); | 213 builder.add(closure); |
| 230 return closure; | 214 return closure; |
| 231 } | 215 } |
| 232 | 216 |
| 233 | |
| 234 // Check which pattern this native method follows: | 217 // Check which pattern this native method follows: |
| 235 // 1) foo() native; hasBody = false, isRedirecting = false | 218 // 1) foo() native; hasBody = false, isRedirecting = false |
| 236 // 2) foo() native "bar"; hasBody = false, isRedirecting = true | 219 // 2) foo() native "bar"; hasBody = false, isRedirecting = true |
| 237 // 3) foo() native "return 42"; hasBody = true, isRedirecting = false | 220 // 3) foo() native "return 42"; hasBody = true, isRedirecting = false |
| 221 RegExp nativeRedirectionRegExp = const RegExp(@'^[a-zA-Z][a-zA-Z_$0-9]*$'); |
| 238 bool hasBody = false; | 222 bool hasBody = false; |
| 239 bool isRedirecting = false; | 223 bool isRedirecting = false; |
| 240 String nativeMethodName = element.name.slowToString(); | 224 String nativeMethodName = element.name.slowToString(); |
| 241 if (!node.arguments.isEmpty()) { | 225 if (nativeBody !== null) { |
| 242 if (!node.arguments.tail.isEmpty()) { | 226 LiteralString jsCode = nativeBody.asLiteralString(); |
| 243 builder.compiler.cancel('More than one argument to native'); | |
| 244 } | |
| 245 LiteralString jsCode = node.arguments.head; | |
| 246 String str = jsCode.dartString.slowToString(); | 227 String str = jsCode.dartString.slowToString(); |
| 247 if (nativeRedirectionRegExp.hasMatch(str)) { | 228 if (nativeRedirectionRegExp.hasMatch(str)) { |
| 248 nativeMethodName = str; | 229 nativeMethodName = str; |
| 249 isRedirecting = true; | 230 isRedirecting = true; |
| 250 nativeEmitter.addRedirectingMethod(element, nativeMethodName); | 231 nativeEmitter.addRedirectingMethod(element, nativeMethodName); |
| 251 } else { | 232 } else { |
| 252 hasBody = true; | 233 hasBody = true; |
| 253 } | 234 } |
| 254 } | 235 } |
| 255 | 236 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 283 } else if (element.kind == ElementKind.SETTER) { | 264 } else if (element.kind == ElementKind.SETTER) { |
| 284 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; | 265 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; |
| 285 } else { | 266 } else { |
| 286 builder.compiler.internalError('unexpected kind: "${element.kind}"', | 267 builder.compiler.internalError('unexpected kind: "${element.kind}"', |
| 287 element: element); | 268 element: element); |
| 288 } | 269 } |
| 289 | 270 |
| 290 DartString jsCode = new DartString.literal(nativeMethodCall); | 271 DartString jsCode = new DartString.literal(nativeMethodCall); |
| 291 builder.push( | 272 builder.push( |
| 292 new HForeign(jsCode, const LiteralDartString('Object'), inputs)); | 273 new HForeign(jsCode, const LiteralDartString('Object'), inputs)); |
| 274 builder.close(new HReturn(builder.pop())).addSuccessor(builder.graph.exit); |
| 293 } else { | 275 } else { |
| 294 // This is JS code written in a Dart file with the construct | 276 // This is JS code written in a Dart file with the construct |
| 295 // native """ ... """;. It does not work well with mangling, | 277 // native """ ... """;. It does not work well with mangling, |
| 296 // but there should currently be no clash between leg mangling | 278 // but there should currently be no clash between leg mangling |
| 297 // and the library where this construct is being used. This | 279 // and the library where this construct is being used. This |
| 298 // mangling problem will go away once we switch these libraries | 280 // mangling problem will go away once we switch these libraries |
| 299 // to use Leg's 'JS' function. | 281 // to use Leg's 'JS' function. |
| 300 parameters.forEachParameter((Element parameter) { | 282 parameters.forEachParameter((Element parameter) { |
| 301 Type type = parameter.computeType(compiler); | 283 Type type = parameter.computeType(compiler); |
| 302 if (type is FunctionType) { | 284 if (type is FunctionType) { |
| 303 HInstruction jsClosure = convertDartClosure(parameter, type); | 285 HInstruction jsClosure = convertDartClosure(parameter, type); |
| 304 // Because the JS code references the argument name directly, | 286 // Because the JS code references the argument name directly, |
| 305 // we must keep the name and assign the JS closure to it. | 287 // we must keep the name and assign the JS closure to it. |
| 306 builder.add(new HForeign( | 288 builder.add(new HForeign( |
| 307 new DartString.literal('${parameter.name.slowToString()} = #'), | 289 new DartString.literal('${parameter.name.slowToString()} = #'), |
| 308 const LiteralDartString('void'), | 290 const LiteralDartString('void'), |
| 309 <HInstruction>[jsClosure])); | 291 <HInstruction>[jsClosure])); |
| 310 } | 292 } |
| 311 }); | 293 }); |
| 312 LiteralString jsCode = node.arguments.head; | 294 LiteralString jsCode = nativeBody.asLiteralString(); |
| 313 builder.push(new HForeign(jsCode.dartString, | 295 builder.push(new HForeign(jsCode.dartString, |
| 314 const LiteralDartString('Object'), | 296 const LiteralDartString('Object'), |
| 315 <HInstruction>[])); | 297 <HInstruction>[])); |
| 316 } | 298 } |
| 317 } | 299 } |
| 318 | 300 |
| 319 void generateMethodWithPrototypeCheckForElement(Compiler compiler, | 301 void generateMethodWithPrototypeCheckForElement(Compiler compiler, |
| 320 StringBuffer buffer, | 302 StringBuffer buffer, |
| 321 FunctionElement element, | 303 FunctionElement element, |
| 322 String code, | 304 String code, |
| (...skipping 30 matching lines...) Expand all Loading... |
| 353 String parameters) { | 335 String parameters) { |
| 354 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); | 336 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); |
| 355 buffer.add("('$methodName')) {\n"); | 337 buffer.add("('$methodName')) {\n"); |
| 356 buffer.add(" $code"); | 338 buffer.add(" $code"); |
| 357 buffer.add(" } else {\n"); | 339 buffer.add(" } else {\n"); |
| 358 buffer.add(" return Object.prototype.$methodName.call(this"); | 340 buffer.add(" return Object.prototype.$methodName.call(this"); |
| 359 buffer.add(parameters == '' ? '' : ', $parameters'); | 341 buffer.add(parameters == '' ? '' : ', $parameters'); |
| 360 buffer.add(");\n"); | 342 buffer.add(");\n"); |
| 361 buffer.add(" }\n"); | 343 buffer.add(" }\n"); |
| 362 } | 344 } |
| OLD | NEW |