| 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 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 140 listener.handleIdentifier(token); | 140 listener.handleIdentifier(token); |
| 141 token = token.next; | 141 token = token.next; |
| 142 if (token.kind === STRING_TOKEN) { | 142 if (token.kind === STRING_TOKEN) { |
| 143 listener.beginLiteralString(token); | 143 listener.beginLiteralString(token); |
| 144 listener.endLiteralString(0); | 144 listener.endLiteralString(0); |
| 145 LiteralString str = listener.popNode(); | 145 LiteralString str = listener.popNode(); |
| 146 listener.pushNode(new NodeList.singleton(str)); | 146 listener.pushNode(new NodeList.singleton(str)); |
| 147 listener.endSend(token); | 147 listener.endSend(token); |
| 148 token = token.next; | 148 token = token.next; |
| 149 // If this native method is just redirecting to another method, | 149 // If this native method is just redirecting to another method, |
| 150 // we add a return node to match the SSA builder expactations. | 150 // we add a return node to match the SSA builder expectations. |
| 151 if (nativeRedirectionRegExp.hasMatch(str.dartString.slowToString())) { | 151 if (nativeRedirectionRegExp.hasMatch(str.dartString.slowToString())) { |
| 152 listener.endReturnStatement(true, begin, token); | 152 listener.endReturnStatement(true, begin, token); |
| 153 } else { | 153 } else { |
| 154 listener.endExpressionStatement(token); | 154 listener.endExpressionStatement(token); |
| 155 } | 155 } |
| 156 } else { | 156 } else { |
| 157 listener.pushNode(new NodeList.empty()); | 157 listener.pushNode(new NodeList.empty()); |
| 158 listener.endSend(token); | 158 listener.endSend(token); |
| 159 listener.endReturnStatement(true, begin, token); | 159 listener.endReturnStatement(true, begin, token); |
| 160 } | 160 } |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 249 receiver = '#.'; | 249 receiver = '#.'; |
| 250 inputs.add(builder.localsHandler.readThis()); | 250 inputs.add(builder.localsHandler.readThis()); |
| 251 } | 251 } |
| 252 parameters.forEachParameter((Element parameter) { | 252 parameters.forEachParameter((Element parameter) { |
| 253 Type type = parameter.computeType(compiler); | 253 Type type = parameter.computeType(compiler); |
| 254 HInstruction input = builder.localsHandler.readLocal(parameter); | 254 HInstruction input = builder.localsHandler.readLocal(parameter); |
| 255 if (type is FunctionType) input = convertDartClosure(parameter); | 255 if (type is FunctionType) input = convertDartClosure(parameter); |
| 256 inputs.add(input); | 256 inputs.add(input); |
| 257 arguments.add('#'); | 257 arguments.add('#'); |
| 258 }); | 258 }); |
| 259 |
| 259 String foreignParameters = Strings.join(arguments, ','); | 260 String foreignParameters = Strings.join(arguments, ','); |
| 260 | |
| 261 String dartMethodName; | |
| 262 String nativeMethodCall; | 261 String nativeMethodCall; |
| 263 | |
| 264 if (element.kind == ElementKind.FUNCTION) { | 262 if (element.kind == ElementKind.FUNCTION) { |
| 265 dartMethodName = builder.compiler.namer.instanceMethodName( | |
| 266 element.getLibrary(), element.name, parameters.parameterCount); | |
| 267 nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)'; | 263 nativeMethodCall = '$receiver$nativeMethodName($foreignParameters)'; |
| 268 } else if (element.kind == ElementKind.GETTER) { | 264 } else if (element.kind == ElementKind.GETTER) { |
| 269 dartMethodName = builder.compiler.namer.getterName( | |
| 270 element.getLibrary(), element.name); | |
| 271 nativeMethodCall = '$receiver$nativeMethodName'; | 265 nativeMethodCall = '$receiver$nativeMethodName'; |
| 272 } else if (element.kind == ElementKind.SETTER) { | 266 } else if (element.kind == ElementKind.SETTER) { |
| 273 dartMethodName = builder.compiler.namer.setterName( | |
| 274 element.getLibrary(), element.name); | |
| 275 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; | 267 nativeMethodCall = '$receiver$nativeMethodName = $foreignParameters'; |
| 276 } else { | 268 } else { |
| 277 builder.compiler.internalError('unexpected kind: "${element.kind}"', | 269 builder.compiler.internalError('unexpected kind: "${element.kind}"', |
| 278 element: element); | 270 element: element); |
| 279 } | 271 } |
| 280 | 272 |
| 281 HInstruction thenInstruction; | 273 DartString jsCode = new DartString.literal(nativeMethodCall); |
| 282 void visitThen() { | 274 builder.push( |
| 283 DartString jsCode = new DartString.literal(nativeMethodCall); | 275 new HForeign(jsCode, const LiteralDartString('Object'), inputs)); |
| 284 thenInstruction = | |
| 285 new HForeign(jsCode, const LiteralDartString('Object'), inputs); | |
| 286 builder.add(thenInstruction); | |
| 287 } | |
| 288 | |
| 289 bool isNativeLiteral = false; | |
| 290 bool isOverridden = false; | |
| 291 NativeEmitter nativeEmitter = builder.compiler.emitter.nativeEmitter; | |
| 292 if (element.enclosingElement.kind == ElementKind.CLASS) { | |
| 293 ClassElement classElement = element.enclosingElement; | |
| 294 String nativeName = classElement.nativeName.slowToString(); | |
| 295 isNativeLiteral = nativeEmitter.isNativeLiteral(nativeName); | |
| 296 isOverridden = isOverriddenMethod(element, classElement, nativeEmitter); | |
| 297 } | |
| 298 if (!element.isInstanceMember() || isNativeLiteral || !isOverridden) { | |
| 299 // We generate a direct call to the native method. | |
| 300 visitThen(); | |
| 301 builder.stack.add(thenInstruction); | |
| 302 } else { | |
| 303 // Record that this method is overridden. In case of optional | |
| 304 // arguments, the emitter will generate stubs to handle them, | |
| 305 // and needs to know if the method is overridden. | |
| 306 nativeEmitter.overriddenMethods.add(element); | |
| 307 | |
| 308 // If the method is an instance method that is overridden, we | |
| 309 // generate the following code: | |
| 310 // function(params) { | |
| 311 // return Object.getPrototypeOf(this).hasOwnProperty(dartMethodName)) | |
| 312 // ? this.methodName(params) | |
| 313 // : Object.prototype.methodName.call(this, params); | |
| 314 // } | |
| 315 // | |
| 316 // The property check at the beginning is to make sure we won't | |
| 317 // call the method from the super class, in case the prototype of | |
| 318 // 'this' does not have the method yet. | |
| 319 HInstruction elseInstruction; | |
| 320 void visitElse() { | |
| 321 String params = arguments.isEmpty() ? '' : ', $foreignParameters'; | |
| 322 DartString jsCode = new DartString.literal( | |
| 323 'Object.prototype.$dartMethodName.call(#$params)'); | |
| 324 elseInstruction = | |
| 325 new HForeign(jsCode, const LiteralDartString('Object'), inputs); | |
| 326 builder.add(elseInstruction); | |
| 327 } | |
| 328 | |
| 329 HConstant constant = builder.graph.addConstantString( | |
| 330 new DartString.literal('$dartMethodName')); | |
| 331 DartString jsCode = new DartString.literal( | |
| 332 'Object.getPrototypeOf(#).hasOwnProperty(#)'); | |
| 333 builder.push(new HForeign( | |
| 334 jsCode, const LiteralDartString('Object'), | |
| 335 <HInstruction>[builder.localsHandler.readThis(), constant])); | |
| 336 | |
| 337 builder.handleIf(visitThen, visitElse); | |
| 338 | |
| 339 HPhi phi = new HPhi.manyInputs( | |
| 340 null, <HInstruction>[thenInstruction, elseInstruction]); | |
| 341 builder.current.addPhi(phi); | |
| 342 builder.stack.add(phi); | |
| 343 } | |
| 344 } else { | 276 } else { |
| 345 // This is JS code written in a Dart file with the construct | 277 // This is JS code written in a Dart file with the construct |
| 346 // native """ ... """;. It does not work well with mangling, | 278 // native """ ... """;. It does not work well with mangling, |
| 347 // but there should currently be no clash between leg mangling | 279 // but there should currently be no clash between leg mangling |
| 348 // and the library where this construct is being used. This | 280 // and the library where this construct is being used. This |
| 349 // mangling problem will go away once we switch these libraries | 281 // mangling problem will go away once we switch these libraries |
| 350 // to use Leg's 'JS' function. | 282 // to use Leg's 'JS' function. |
| 351 parameters.forEachParameter((Element parameter) { | 283 parameters.forEachParameter((Element parameter) { |
| 352 Type type = parameter.computeType(compiler); | 284 Type type = parameter.computeType(compiler); |
| 353 if (type is FunctionType) { | 285 if (type is FunctionType) { |
| 354 HInstruction jsClosure = convertDartClosure(parameter); | 286 HInstruction jsClosure = convertDartClosure(parameter); |
| 355 // Because the JS code references the argument name directly, | 287 // Because the JS code references the argument name directly, |
| 356 // we must keep the name and assign the JS closure to it. | 288 // we must keep the name and assign the JS closure to it. |
| 357 builder.add(new HForeign( | 289 builder.add(new HForeign( |
| 358 new DartString.literal('${parameter.name.slowToString()} = #'), | 290 new DartString.literal('${parameter.name.slowToString()} = #'), |
| 359 const LiteralDartString('void'), | 291 const LiteralDartString('void'), |
| 360 <HInstruction>[jsClosure])); | 292 <HInstruction>[jsClosure])); |
| 361 } | 293 } |
| 362 }); | 294 }); |
| 363 LiteralString jsCode = node.arguments.head; | 295 LiteralString jsCode = node.arguments.head; |
| 364 builder.push(new HForeign(jsCode.dartString, | 296 builder.push(new HForeign(jsCode.dartString, |
| 365 const LiteralDartString('Object'), | 297 const LiteralDartString('Object'), |
| 366 <HInstruction>[])); | 298 <HInstruction>[])); |
| 367 } | 299 } |
| 368 } | 300 } |
| 301 |
| 302 void generateMethodWithPrototypeCheckForElement(Compiler compiler, |
| 303 StringBuffer buffer, |
| 304 FunctionElement element, |
| 305 String code, |
| 306 String parameters) { |
| 307 String methodName; |
| 308 Namer namer = compiler.namer; |
| 309 if (element.kind == ElementKind.FUNCTION) { |
| 310 FunctionParameters parameters = element.computeParameters(compiler); |
| 311 methodName = namer.instanceMethodName( |
| 312 element.getLibrary(), element.name, parameters.parameterCount); |
| 313 } else if (element.kind == ElementKind.GETTER) { |
| 314 methodName = namer.getterName(element.getLibrary(), element.name); |
| 315 } else if (element.kind == ElementKind.SETTER) { |
| 316 methodName = namer.setterName(element.getLibrary(), element.name); |
| 317 } else { |
| 318 compiler.internalError('unexpected kind: "${element.kind}"', |
| 319 element: element); |
| 320 } |
| 321 |
| 322 generateMethodWithPrototypeCheck( |
| 323 compiler, buffer, methodName, code, parameters); |
| 324 } |
| 325 |
| 326 |
| 327 // If a method is overridden, we must check if the prototype of |
| 328 // 'this' has the method available. Otherwise, we may end up |
| 329 // calling the method from the super class. If the method is not |
| 330 // available, we make a direct call to Object.prototype.$methodName. |
| 331 // This method will patch the prototype of 'this' to the real method. |
| 332 void generateMethodWithPrototypeCheck(Compiler compiler, |
| 333 StringBuffer buffer, |
| 334 String methodName, |
| 335 String code, |
| 336 String parameters) { |
| 337 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); |
| 338 buffer.add("('$methodName')) {\n"); |
| 339 buffer.add(" $code"); |
| 340 buffer.add(" } else {\n"); |
| 341 buffer.add(" return Object.prototype.$methodName.call(this"); |
| 342 buffer.add(parameters == '' ? '' : ', $parameters'); |
| 343 buffer.add(");\n"); |
| 344 buffer.add(" }\n"); |
| 345 } |
| OLD | NEW |