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)); |
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 .callOn([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> generateParameterStub( |
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).callOn(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, 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 .callOn([new js.VariableUse('this')]) |
| 308 .dot('hasOwnProperty') |
| 309 .callOn([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 .callOn( |
| 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 |