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 part of js_backend; | 5 part of js_backend; |
6 | 6 |
7 class NativeEmitter { | 7 class NativeEmitter { |
8 | 8 |
9 CodeEmitterTask emitter; | 9 CodeEmitterTask emitter; |
10 CodeBuffer nativeBuffer; | 10 CodeBuffer nativeBuffer; |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
199 nativeBuffer.add('\n});\n\n'); | 199 nativeBuffer.add('\n});\n\n'); |
200 | 200 |
201 classesWithDynamicDispatch.add(classElement); | 201 classesWithDynamicDispatch.add(classElement); |
202 } | 202 } |
203 | 203 |
204 List<ClassElement> getDirectSubclasses(ClassElement cls) { | 204 List<ClassElement> getDirectSubclasses(ClassElement cls) { |
205 List<ClassElement> result = directSubtypes[cls]; | 205 List<ClassElement> result = directSubtypes[cls]; |
206 return result == null ? const<ClassElement>[] : result; | 206 return result == null ? const<ClassElement>[] : result; |
207 } | 207 } |
208 | 208 |
209 void potentiallyConvertDartClosuresToJs(CodeBuffer code, | 209 void potentiallyConvertDartClosuresToJs(List<js.Statement> statements, |
210 FunctionElement member, | 210 FunctionElement member, |
211 List<String> argumentsBuffer) { | 211 List<js.Parameter> stubParameters) { |
212 FunctionSignature parameters = member.computeSignature(compiler); | 212 FunctionSignature parameters = member.computeSignature(compiler); |
213 Element converter = | 213 Element converter = |
214 compiler.findHelper(const SourceString('convertDartClosureToJS')); | 214 compiler.findHelper(const SourceString('convertDartClosureToJS')); |
215 String closureConverter = backend.namer.isolateAccess(converter); | 215 String closureConverter = backend.namer.isolateAccess(converter); |
216 Set<String> stubParameterNames = new Set<String>.from( | |
217 stubParameters.map((param) => param.name)); | |
ngeoffray
2012/11/30 09:01:56
I would to this by hand to keep the order and then
| |
216 parameters.forEachParameter((Element parameter) { | 218 parameters.forEachParameter((Element parameter) { |
217 String name = parameter.name.slowToString(); | 219 String name = parameter.name.slowToString(); |
218 // If [name] is not in [argumentsBuffer], then the parameter is | 220 // If [name] is not in [stubParameters], then the parameter is an optional |
219 // an optional parameter that was not provided for that stub. | 221 // parameter that was not provided for this stub. |
220 if (argumentsBuffer.indexOf(name) == -1) return; | 222 for (js.Parameter stubParameter in stubParameters) { |
221 DartType type = parameter.computeType(compiler).unalias(compiler); | 223 if (stubParameter.name == name) { |
222 if (type is FunctionType) { | 224 DartType type = parameter.computeType(compiler).unalias(compiler); |
223 // The parameter type is a function type either directly or through | 225 if (type is FunctionType) { |
224 // typedef(s). | 226 // The parameter type is a function type either directly or through |
225 int arity = type.computeArity(); | 227 // typedef(s). |
226 code.add(' $name = $closureConverter($name, $arity);\n'); | 228 int arity = type.computeArity(); |
229 | |
230 statements.add( | |
231 new js.ExpressionStatement( | |
232 new js.Assignment( | |
233 new js.VariableUse(name), | |
234 new js.VariableUse(closureConverter) | |
235 .callWith([new js.VariableUse(name), | |
236 new js.LiteralNumber('$arity')])))); | |
237 break; | |
238 } | |
239 } | |
227 } | 240 } |
228 }); | 241 }); |
229 } | 242 } |
230 | 243 |
231 String generateParameterStub(Element member, | 244 List<js.Statement> generateParameterStubStatements( |
232 String invocationName, | 245 Element member, |
233 String stubParameters, | 246 String invocationName, |
234 List<String> argumentsBuffer, | 247 List<js.Parameter> stubParameters, |
235 int indexOfLastOptionalArgumentInParameters, | 248 List<js.Expression> argumentsBuffer, |
236 CodeBuffer buffer) { | 249 int indexOfLastOptionalArgumentInParameters) { |
237 // The target JS function may check arguments.length so we need to | 250 // The target JS function may check arguments.length so we need to |
238 // make sure not to pass any unspecified optional arguments to it. | 251 // make sure not to pass any unspecified optional arguments to it. |
239 // For example, for the following Dart method: | 252 // For example, for the following Dart method: |
240 // foo([x, y, z]); | 253 // foo([x, y, z]); |
241 // The call: | 254 // The call: |
242 // foo(y: 1) | 255 // foo(y: 1) |
243 // must be turned into a JS call to: | 256 // must be turned into a JS call to: |
244 // foo(null, y). | 257 // foo(null, y). |
245 | 258 |
246 List<String> nativeArgumentsBuffer = argumentsBuffer.getRange( | 259 ClassElement classElement = member.enclosingElement; |
247 0, indexOfLastOptionalArgumentInParameters + 1); | 260 String nativeTagInfo = classElement.nativeName.slowToString(); |
248 | 261 |
249 ClassElement classElement = member.enclosingElement; | 262 List<js.Statement> statements = <js.Statement>[]; |
250 String nativeName = classElement.nativeName.slowToString(); | 263 potentiallyConvertDartClosuresToJs(statements, member, stubParameters); |
251 String nativeArguments = Strings.join(nativeArgumentsBuffer, ","); | |
252 | 264 |
253 CodeBuffer code = new CodeBuffer(); | 265 String target; |
254 potentiallyConvertDartClosuresToJs(code, member, argumentsBuffer); | 266 List<js.Expression> arguments; |
255 | 267 |
256 if (!nativeMethods.contains(member)) { | 268 if (!nativeMethods.contains(member)) { |
257 // When calling a method that has a native body, we call it | 269 // When calling a method that has a native body, we call it with our |
258 // with our calling conventions. | 270 // calling conventions. |
259 String arguments = Strings.join(argumentsBuffer, ","); | 271 target = backend.namer.getName(member); |
260 code.add(' return this.${backend.namer.getName(member)}($arguments)'); | 272 arguments = argumentsBuffer; |
261 } else { | 273 } else { |
262 // When calling a JS method, we call it with the native name. | 274 // When calling a JS method, we call it with the native name, and only the |
263 String name = redirectingMethods[member]; | 275 // arguments up until the last one provided. |
264 if (name == null) name = member.name.slowToString(); | 276 target = redirectingMethods[member]; |
265 code.add(' return this.$name($nativeArguments);'); | 277 if (target == null) target = member.name.slowToString(); |
278 arguments = argumentsBuffer.getRange( | |
279 0, indexOfLastOptionalArgumentInParameters + 1); | |
280 } | |
281 statements.add( | |
282 new js.Return( | |
283 new js.VariableUse('this').dot(target).callWith(arguments))); | |
284 | |
285 if (isNativeLiteral(nativeTagInfo) || !overriddenMethods.contains(member)) { | |
286 // Call the method directly. | |
287 return statements; | |
288 } else { | |
289 return <js.Statement>[ | |
290 generateMethodBodyWithPrototypeCheck( | |
291 invocationName, new js.Block(statements), stubParameters)]; | |
292 } | |
293 } | |
294 | |
295 // If a method is overridden, we must check if the prototype of 'this' has the | |
296 // method available. Otherwise, we may end up calling the method from the | |
297 // super class. If the method is not available, we make a direct call to | |
298 // Object.prototype.$methodName. This method will patch the prototype of | |
299 // 'this' to the real method. | |
300 js.Statement generateMethodBodyWithPrototypeCheck( | |
301 String methodName, | |
302 js.Statement body, | |
303 List<js.Parameter> parameters) { | |
304 return new js.If( | |
305 new js.VariableUse('Object') | |
306 .dot('getPrototypeOf') | |
307 .callWith([new js.VariableUse('this')]) | |
308 .dot('hasOwnProperty') | |
309 .callWith([new js.LiteralString("'$methodName'")]), | |
310 body, | |
311 new js.Block( | |
312 <js.Statement>[ | |
313 new js.Return( | |
314 new js.VariableUse('Object') | |
315 .dot('prototype').dot(methodName).dot('call') | |
316 .callWith( | |
317 <js.Expression>[new js.VariableUse('this')] | |
318 ..addAll(parameters.map((param) => | |
319 new js.VariableUse(param.name))))) | |
320 ])); | |
321 } | |
322 | |
323 js.Block generateMethodBodyWithPrototypeCheckForElement( | |
324 FunctionElement element, | |
325 js.Block body, | |
326 List<js.Parameter> parameters) { | |
327 String methodName; | |
328 Namer namer = backend.namer; | |
329 if (element.kind == ElementKind.FUNCTION) { | |
330 methodName = namer.instanceMethodName(element); | |
331 } else if (element.kind == ElementKind.GETTER) { | |
332 methodName = namer.getterName(element.getLibrary(), element.name); | |
333 } else if (element.kind == ElementKind.SETTER) { | |
334 methodName = namer.setterName(element.getLibrary(), element.name); | |
335 } else { | |
336 compiler.internalError('unexpected kind: "${element.kind}"', | |
337 element: element); | |
266 } | 338 } |
267 | 339 |
268 if (isNativeLiteral(nativeName) || !overriddenMethods.contains(member)) { | 340 return new js.Block( |
269 // Call the method directly. | 341 [generateMethodBodyWithPrototypeCheck(methodName, body, parameters)]); |
270 buffer.add(code.toString()); | |
271 } else { | |
272 native.generateMethodWithPrototypeCheck( | |
273 compiler, buffer, invocationName, code.toString(), stubParameters); | |
274 } | |
275 } | 342 } |
276 | 343 |
344 | |
277 void emitDynamicDispatchMetadata() { | 345 void emitDynamicDispatchMetadata() { |
278 if (classesWithDynamicDispatch.isEmpty) return; | 346 if (classesWithDynamicDispatch.isEmpty) return; |
279 int length = classesWithDynamicDispatch.length; | 347 int length = classesWithDynamicDispatch.length; |
280 nativeBuffer.add('// $length dynamic classes.\n'); | 348 nativeBuffer.add('// $length dynamic classes.\n'); |
281 | 349 |
282 // Build a pre-order traversal over all the classes and their subclasses. | 350 // Build a pre-order traversal over all the classes and their subclasses. |
283 Set<ClassElement> seen = new Set<ClassElement>(); | 351 Set<ClassElement> seen = new Set<ClassElement>(); |
284 List<ClassElement> classes = <ClassElement>[]; | 352 List<ClassElement> classes = <ClassElement>[]; |
285 void visit(ClassElement cls) { | 353 void visit(ClassElement cls) { |
286 if (seen.contains(cls)) return; | 354 if (seen.contains(cls)) return; |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
506 if (!first) targetBuffer.add(",\n"); | 574 if (!first) targetBuffer.add(",\n"); |
507 targetBuffer.add(" $name: $function"); | 575 targetBuffer.add(" $name: $function"); |
508 first = false; | 576 first = false; |
509 }); | 577 }); |
510 targetBuffer.add("\n});\n\n"); | 578 targetBuffer.add("\n});\n\n"); |
511 } | 579 } |
512 targetBuffer.add(nativeBuffer); | 580 targetBuffer.add(nativeBuffer); |
513 targetBuffer.add('\n'); | 581 targetBuffer.add('\n'); |
514 } | 582 } |
515 } | 583 } |
OLD | NEW |