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 |