Chromium Code Reviews| 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); | |
|
Anton Muhin
2012/06/18 10:04:52
now we treat all natives as returns, even though t
sra1
2012/06/20 18:29:05
I'm not sure that makes sense; some of the native
Anton Muhin
2012/06/20 18:51:49
Stephen, both Peter and me are well aware of this
| |
| 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 /* Fails thanks to lib/isolate/frog/isolateimpl.dart:143 --- native method w/ Dart body. |
|
Anton Muhin
2012/06/18 10:04:52
is it valid Dart? I mean code in isolateimpl.dart
ahe
2012/06/18 10:37:39
No. Nothing about "native" is valid Dart.
sra1
2012/06/20 18:29:05
Native methods with both a string JavaScript body
Anton Muhin
2012/06/20 18:51:49
Thanks a lot for clarifications. I wonder if it w
| |
| 156 if (';' !== token.stringValue) { | |
| 157 return listener.expected(';', token); | |
| 158 } | |
| 159 */ | |
| 173 return token.next; | 160 return token.next; |
| 174 } | 161 } |
| 175 | 162 |
| 176 SourceString checkForNativeClass(ElementListener listener) { | 163 SourceString checkForNativeClass(ElementListener listener) { |
| 177 SourceString nativeName; | 164 SourceString nativeName; |
| 178 Node node = listener.nodes.head; | 165 Node node = listener.nodes.head; |
| 179 if (node != null | 166 if (node != null |
| 180 && node.asIdentifier() != null | 167 && node.asIdentifier() != null |
| 181 && node.asIdentifier().source.stringValue == 'native') { | 168 && node.asIdentifier().source.stringValue == 'native') { |
| 182 nativeName = node.asIdentifier().token.next.value; | 169 nativeName = node.asIdentifier().token.next.value; |
| 183 listener.popNode(); | 170 listener.popNode(); |
| 184 } | 171 } |
| 185 return nativeName; | 172 return nativeName; |
| 186 } | 173 } |
| 187 | 174 |
| 188 bool isOverriddenMethod(FunctionElement element, | 175 bool isOverriddenMethod(FunctionElement element, |
| 189 ClassElement cls, | 176 ClassElement cls, |
| 190 NativeEmitter nativeEmitter) { | 177 NativeEmitter nativeEmitter) { |
| 191 List<ClassElement> subtypes = nativeEmitter.subtypes[cls]; | 178 List<ClassElement> subtypes = nativeEmitter.subtypes[cls]; |
| 192 if (subtypes == null) return false; | 179 if (subtypes == null) return false; |
| 193 for (ClassElement subtype in subtypes) { | 180 for (ClassElement subtype in subtypes) { |
| 194 if (subtype.lookupLocalMember(element.name) != null) return true; | 181 if (subtype.lookupLocalMember(element.name) != null) return true; |
| 195 } | 182 } |
| 196 return false; | 183 return false; |
| 197 } | 184 } |
| 198 | 185 |
| 199 void handleSsaNative(SsaBuilder builder, Send node) { | 186 void handleSsaNative(SsaBuilder builder, Expression nativeBody) { |
| 200 Compiler compiler = builder.compiler; | 187 Compiler compiler = builder.compiler; |
| 201 FunctionElement element = builder.work.element; | 188 FunctionElement element = builder.work.element; |
| 202 element.setNative(); | 189 element.setNative(); |
| 203 NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; | 190 NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; |
| 204 // If what we're compiling is a getter named 'typeName' and the native | 191 // 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 | 192 // class is named 'DOMType', we generate a call to the typeNameOf |
| 206 // function attached on the isolate. | 193 // function attached on the isolate. |
| 207 // The DOM classes assume that their 'typeName' property, which is | 194 // The DOM classes assume that their 'typeName' property, which is |
| 208 // not a JS property on the DOM types, returns the type name. | 195 // not a JS property on the DOM types, returns the type name. |
| 209 if (element.name == const SourceString('typeName') | 196 if (element.name == const SourceString('typeName') |
| 210 && element.isGetter() | 197 && element.isGetter() |
| 211 && nativeEmitter.toNativeName(element.enclosingElement) == 'DOMType') { | 198 && nativeEmitter.toNativeName(element.enclosingElement) == 'DOMType') { |
| 212 Element methodElement = | 199 Element methodElement = |
| 213 compiler.findHelper(const SourceString('getTypeNameOf')); | 200 compiler.findHelper(const SourceString('getTypeNameOf')); |
| 214 HStatic method = new HStatic(methodElement); | 201 HStatic method = new HStatic(methodElement); |
| 215 builder.add(method); | 202 builder.add(method); |
| 216 builder.push(new HInvokeStatic(Selector.INVOCATION_1, | 203 builder.push(new HInvokeStatic(Selector.INVOCATION_1, |
| 217 <HInstruction>[method, builder.localsHandler.readThis()])); | 204 <HInstruction>[method, builder.localsHandler.readThis()])); |
| 218 return; | 205 return true; |
| 219 } | 206 } |
| 220 | 207 |
| 221 HInstruction convertDartClosure(Element parameter, FunctionType type) { | 208 HInstruction convertDartClosure(Element parameter, FunctionType type) { |
| 222 HInstruction local = builder.localsHandler.readLocal(parameter); | 209 HInstruction local = builder.localsHandler.readLocal(parameter); |
| 223 // TODO(ngeoffray): For static methods, we could pass a method with a | 210 // TODO(ngeoffray): For static methods, we could pass a method with a |
| 224 // defined arity. | 211 // defined arity. |
| 225 builder.push(new HStatic(builder.interceptors.getClosureConverter())); | 212 builder.push(new HStatic(builder.interceptors.getClosureConverter())); |
| 226 HInstruction arity = builder.graph.addConstantInt(type.computeArity()); | 213 HInstruction arity = builder.graph.addConstantInt(type.computeArity()); |
| 227 List<HInstruction> callInputs = <HInstruction>[builder.pop(), local, arity]; | 214 List<HInstruction> callInputs = <HInstruction>[builder.pop(), local, arity]; |
| 228 HInstruction closure = new HInvokeStatic(Selector.INVOCATION_1, callInputs); | 215 HInstruction closure = new HInvokeStatic(Selector.INVOCATION_1, callInputs); |
| 229 builder.add(closure); | 216 builder.add(closure); |
| 230 return closure; | 217 return closure; |
| 231 } | 218 } |
| 232 | 219 |
| 233 | |
| 234 // Check which pattern this native method follows: | 220 // Check which pattern this native method follows: |
| 235 // 1) foo() native; hasBody = false, isRedirecting = false | 221 // 1) foo() native; hasBody = false, isRedirecting = false |
| 236 // 2) foo() native "bar"; hasBody = false, isRedirecting = true | 222 // 2) foo() native "bar"; hasBody = false, isRedirecting = true |
| 237 // 3) foo() native "return 42"; hasBody = true, isRedirecting = false | 223 // 3) foo() native "return 42"; hasBody = true, isRedirecting = false |
| 224 RegExp nativeRedirectionRegExp = const RegExp(@'^[a-zA-Z][a-zA-Z_$0-9]*$'); | |
| 238 bool hasBody = false; | 225 bool hasBody = false; |
| 239 bool isRedirecting = false; | 226 bool isRedirecting = false; |
| 240 String nativeMethodName = element.name.slowToString(); | 227 String nativeMethodName = element.name.slowToString(); |
| 241 if (!node.arguments.isEmpty()) { | 228 if (nativeBody !== null) { |
| 242 if (!node.arguments.tail.isEmpty()) { | 229 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(); | 230 String str = jsCode.dartString.slowToString(); |
| 247 if (nativeRedirectionRegExp.hasMatch(str)) { | 231 if (nativeRedirectionRegExp.hasMatch(str)) { |
| 248 nativeMethodName = str; | 232 nativeMethodName = str; |
| 249 isRedirecting = true; | 233 isRedirecting = true; |
| 250 nativeEmitter.addRedirectingMethod(element, nativeMethodName); | 234 nativeEmitter.addRedirectingMethod(element, nativeMethodName); |
| 251 } else { | 235 } else { |
| 252 hasBody = true; | 236 hasBody = true; |
| 253 } | 237 } |
| 254 } | 238 } |
| 255 | 239 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 283 } else if (element.kind == ElementKind.SETTER) { | 267 } else if (element.kind == ElementKind.SETTER) { |
| 284 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; | 268 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; |
| 285 } else { | 269 } else { |
| 286 builder.compiler.internalError('unexpected kind: "${element.kind}"', | 270 builder.compiler.internalError('unexpected kind: "${element.kind}"', |
| 287 element: element); | 271 element: element); |
| 288 } | 272 } |
| 289 | 273 |
| 290 DartString jsCode = new DartString.literal(nativeMethodCall); | 274 DartString jsCode = new DartString.literal(nativeMethodCall); |
| 291 builder.push( | 275 builder.push( |
| 292 new HForeign(jsCode, const LiteralDartString('Object'), inputs)); | 276 new HForeign(jsCode, const LiteralDartString('Object'), inputs)); |
| 277 return true; | |
| 293 } else { | 278 } else { |
| 294 // This is JS code written in a Dart file with the construct | 279 // This is JS code written in a Dart file with the construct |
| 295 // native """ ... """;. It does not work well with mangling, | 280 // native """ ... """;. It does not work well with mangling, |
| 296 // but there should currently be no clash between leg mangling | 281 // but there should currently be no clash between leg mangling |
| 297 // and the library where this construct is being used. This | 282 // and the library where this construct is being used. This |
| 298 // mangling problem will go away once we switch these libraries | 283 // mangling problem will go away once we switch these libraries |
| 299 // to use Leg's 'JS' function. | 284 // to use Leg's 'JS' function. |
| 300 parameters.forEachParameter((Element parameter) { | 285 parameters.forEachParameter((Element parameter) { |
| 301 Type type = parameter.computeType(compiler); | 286 Type type = parameter.computeType(compiler); |
| 302 if (type is FunctionType) { | 287 if (type is FunctionType) { |
| 303 HInstruction jsClosure = convertDartClosure(parameter, type); | 288 HInstruction jsClosure = convertDartClosure(parameter, type); |
| 304 // Because the JS code references the argument name directly, | 289 // Because the JS code references the argument name directly, |
| 305 // we must keep the name and assign the JS closure to it. | 290 // we must keep the name and assign the JS closure to it. |
| 306 builder.add(new HForeign( | 291 builder.add(new HForeign( |
| 307 new DartString.literal('${parameter.name.slowToString()} = #'), | 292 new DartString.literal('${parameter.name.slowToString()} = #'), |
| 308 const LiteralDartString('void'), | 293 const LiteralDartString('void'), |
| 309 <HInstruction>[jsClosure])); | 294 <HInstruction>[jsClosure])); |
| 310 } | 295 } |
| 311 }); | 296 }); |
| 312 LiteralString jsCode = node.arguments.head; | 297 LiteralString jsCode = nativeBody.asLiteralString(); |
| 313 builder.push(new HForeign(jsCode.dartString, | 298 builder.push(new HForeign(jsCode.dartString, |
| 314 const LiteralDartString('Object'), | 299 const LiteralDartString('Object'), |
| 315 <HInstruction>[])); | 300 <HInstruction>[])); |
| 301 return false; | |
| 316 } | 302 } |
| 317 } | 303 } |
| 318 | 304 |
| 319 void generateMethodWithPrototypeCheckForElement(Compiler compiler, | 305 void generateMethodWithPrototypeCheckForElement(Compiler compiler, |
| 320 StringBuffer buffer, | 306 StringBuffer buffer, |
| 321 FunctionElement element, | 307 FunctionElement element, |
| 322 String code, | 308 String code, |
| 323 String parameters) { | 309 String parameters) { |
| 324 String methodName; | 310 String methodName; |
| 325 Namer namer = compiler.namer; | 311 Namer namer = compiler.namer; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 353 String parameters) { | 339 String parameters) { |
| 354 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); | 340 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); |
| 355 buffer.add("('$methodName')) {\n"); | 341 buffer.add("('$methodName')) {\n"); |
| 356 buffer.add(" $code"); | 342 buffer.add(" $code"); |
| 357 buffer.add(" } else {\n"); | 343 buffer.add(" } else {\n"); |
| 358 buffer.add(" return Object.prototype.$methodName.call(this"); | 344 buffer.add(" return Object.prototype.$methodName.call(this"); |
| 359 buffer.add(parameters == '' ? '' : ', $parameters'); | 345 buffer.add(parameters == '' ? '' : ', $parameters'); |
| 360 buffer.add(");\n"); | 346 buffer.add(");\n"); |
| 361 buffer.add(" }\n"); | 347 buffer.add(" }\n"); |
| 362 } | 348 } |
| OLD | NEW |