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 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 // An empty native class may be omitted since the superclass methods can be | 150 // An empty native class may be omitted since the superclass methods can be |
151 // located via the dispatch metadata. | 151 // located via the dispatch metadata. |
152 // TODO(sra): Also need to check there are no subclasses that will reference | 152 // TODO(sra): Also need to check there are no subclasses that will reference |
153 // this class. | 153 // this class. |
154 // bool hasOnlyGeneratedFields = builder.properties.length == 1; | 154 // bool hasOnlyGeneratedFields = builder.properties.length == 1; |
155 // if (hasOnlyGeneratedFields == 1 && !hasFields) return; | 155 // if (hasOnlyGeneratedFields == 1 && !hasFields) return; |
156 | 156 |
157 // Define interceptor class for [classElement]. | 157 // Define interceptor class for [classElement]. |
158 String className = backend.namer.getName(classElement); | 158 String className = backend.namer.getName(classElement); |
159 jsAst.Expression init = | 159 jsAst.Expression init = |
160 js[emitter.classesCollector][className].assign( | 160 js(emitter.classesCollector)[className].assign( |
161 builder.toObjectInitializer()); | 161 builder.toObjectInitializer()); |
162 mainBuffer.write(jsAst.prettyPrint(init, compiler)); | 162 mainBuffer.write(jsAst.prettyPrint(init, compiler)); |
163 mainBuffer.write('$N$n'); | 163 mainBuffer.write('$N$n'); |
164 | 164 |
165 emitter.needsDefineClass = true; | 165 emitter.needsDefineClass = true; |
166 | 166 |
167 // Define dispatch for [classElement]. | 167 // Define dispatch for [classElement]. |
168 String nativeTag = toNativeTag(classElement); | 168 String nativeTag = toNativeTag(classElement); |
169 String definer = directSubtypes[classElement] == null | 169 String definer = directSubtypes[classElement] == null |
170 ? defineNativeMethodsName | 170 ? defineNativeMethodsName |
171 : defineNativeMethodsNonleafName; | 171 : defineNativeMethodsNonleafName; |
172 | 172 |
173 // TODO(sra): Fix DOM generation. There is a missing proto in the picture | 173 // TODO(sra): Fix DOM generation. There is a missing proto in the picture |
174 // the DOM gives of the proto chain. We might need an annotation. | 174 // the DOM gives of the proto chain. We might need an annotation. |
175 if (nativeTag == 'HTMLElement') definer = defineNativeMethodsNonleafName; | 175 if (nativeTag == 'HTMLElement') definer = defineNativeMethodsNonleafName; |
176 | 176 |
177 jsAst.Expression definition = | 177 jsAst.Expression definition = |
178 js[definer]( | 178 js(definer)( |
179 [js.string(nativeTag), | 179 [jsBuilder.string(nativeTag), |
180 js[backend.namer.isolateAccess(classElement)]]); | 180 js(backend.namer.isolateAccess(classElement))]); |
181 | 181 |
182 nativeBuffer.add(jsAst.prettyPrint(definition, compiler)); | 182 nativeBuffer.add(jsAst.prettyPrint(definition, compiler)); |
183 nativeBuffer.add('$N$n'); | 183 nativeBuffer.add('$N$n'); |
184 | 184 |
185 classesWithDynamicDispatch.add(classElement); | 185 classesWithDynamicDispatch.add(classElement); |
186 } | 186 } |
187 | 187 |
188 void finishGenerateNativeClasses() { | 188 void finishGenerateNativeClasses() { |
189 // TODO(sra): Put specialized version of getNativeMethods on | 189 // TODO(sra): Put specialized version of getNativeMethods on |
190 // `Object.prototype` to avoid checking in `getInterceptor` and | 190 // `Object.prototype` to avoid checking in `getInterceptor` and |
191 // specializations. | 191 // specializations. |
192 | 192 |
193 // jsAst.Expression call = js[defineNativeMethodsFinishName]([]); | 193 // jsAst.Expression call = js(defineNativeMethodsFinishName)([]); |
194 // nativeBuffer.add(jsAst.prettyPrint(call, compiler)); | 194 // nativeBuffer.add(jsAst.prettyPrint(call, compiler)); |
195 // nativeBuffer.add('$N$n'); | 195 // nativeBuffer.add('$N$n'); |
196 } | 196 } |
197 | 197 |
198 List<ClassElement> getDirectSubclasses(ClassElement cls) { | 198 List<ClassElement> getDirectSubclasses(ClassElement cls) { |
199 List<ClassElement> result = directSubtypes[cls]; | 199 List<ClassElement> result = directSubtypes[cls]; |
200 return result == null ? const<ClassElement>[] : result; | 200 return result == null ? const<ClassElement>[] : result; |
201 } | 201 } |
202 | 202 |
203 void potentiallyConvertDartClosuresToJs( | 203 void potentiallyConvertDartClosuresToJs( |
(...skipping 13 matching lines...) Expand all Loading... |
217 for (jsAst.Parameter stubParameter in stubParameters) { | 217 for (jsAst.Parameter stubParameter in stubParameters) { |
218 if (stubParameter.name == name) { | 218 if (stubParameter.name == name) { |
219 DartType type = parameter.computeType(compiler).unalias(compiler); | 219 DartType type = parameter.computeType(compiler).unalias(compiler); |
220 if (type is FunctionType) { | 220 if (type is FunctionType) { |
221 // The parameter type is a function type either directly or through | 221 // The parameter type is a function type either directly or through |
222 // typedef(s). | 222 // typedef(s). |
223 int arity = type.computeArity(); | 223 int arity = type.computeArity(); |
224 | 224 |
225 statements.add( | 225 statements.add( |
226 new jsAst.ExpressionStatement( | 226 new jsAst.ExpressionStatement( |
227 js.assign( | 227 jsBuilder.assign( |
228 js[name], | 228 js(name), |
229 js[closureConverter]( | 229 js(closureConverter)( |
230 [js[name], | 230 [js(name), |
231 new jsAst.LiteralNumber('$arity')])))); | 231 new jsAst.LiteralNumber('$arity')])))); |
232 break; | 232 break; |
233 } | 233 } |
234 } | 234 } |
235 } | 235 } |
236 }); | 236 }); |
237 } | 237 } |
238 | 238 |
239 List<jsAst.Statement> generateParameterStubStatements( | 239 List<jsAst.Statement> generateParameterStubStatements( |
240 Element member, | 240 Element member, |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
372 tagDefns[tag] = new jsAst.VariableUse(varName); | 372 tagDefns[tag] = new jsAst.VariableUse(varName); |
373 expressions.add(new jsAst.VariableUse(varName)); | 373 expressions.add(new jsAst.VariableUse(varName)); |
374 } | 374 } |
375 } | 375 } |
376 } | 376 } |
377 } | 377 } |
378 walk(classElement); | 378 walk(classElement); |
379 | 379 |
380 if (!subtags.isEmpty) { | 380 if (!subtags.isEmpty) { |
381 subtags.sort(); | 381 subtags.sort(); |
382 expressions.add(js.string(subtags.join('|'))); | 382 expressions.add(jsBuilder.string(subtags.join('|'))); |
383 } | 383 } |
384 jsAst.Expression expression; | 384 jsAst.Expression expression; |
385 if (expressions.length == 1) { | 385 if (expressions.length == 1) { |
386 expression = expressions[0]; | 386 expression = expressions[0]; |
387 } else { | 387 } else { |
388 jsAst.Expression array = new jsAst.ArrayInitializer.from(expressions); | 388 jsAst.Expression array = new jsAst.ArrayInitializer.from(expressions); |
389 expression = array['join']([js.string('|')]); | 389 expression = array['join']([jsBuilder.string('|')]); |
390 } | 390 } |
391 return expression; | 391 return expression; |
392 } | 392 } |
393 | 393 |
394 for (final ClassElement classElement in preorderDispatchClasses) { | 394 for (final ClassElement classElement in preorderDispatchClasses) { |
395 tagDefns[classElement] = makeExpression(classElement); | 395 tagDefns[classElement] = makeExpression(classElement); |
396 } | 396 } |
397 | 397 |
398 // Write out a thunk that builds the metadata. | 398 // Write out a thunk that builds the metadata. |
399 if (!tagDefns.isEmpty) { | 399 if (!tagDefns.isEmpty) { |
(...skipping 14 matching lines...) Expand all Loading... |
414 } | 414 } |
415 | 415 |
416 // [table] is a list of lists, each inner list of the form: | 416 // [table] is a list of lists, each inner list of the form: |
417 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag] | 417 // [dynamic-dispatch-tag, tags-of-classes-implementing-dispatch-tag] |
418 // E.g. | 418 // E.g. |
419 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...] | 419 // [['Node', 'Text|HTMLElement|HTMLDivElement|...'], ...] |
420 jsAst.Expression table = | 420 jsAst.Expression table = |
421 new jsAst.ArrayInitializer.from( | 421 new jsAst.ArrayInitializer.from( |
422 preorderDispatchClasses.map((cls) => | 422 preorderDispatchClasses.map((cls) => |
423 new jsAst.ArrayInitializer.from([ | 423 new jsAst.ArrayInitializer.from([ |
424 js.string(toNativeTag(cls)), | 424 jsBuilder.string(toNativeTag(cls)), |
425 tagDefns[cls]]))); | 425 tagDefns[cls]]))); |
426 | 426 |
427 // $.dynamicSetMetadata(table); | 427 // $.dynamicSetMetadata(table); |
428 statements.add( | 428 statements.add( |
429 new jsAst.ExpressionStatement( | 429 new jsAst.ExpressionStatement( |
430 new jsAst.Call( | 430 new jsAst.Call( |
431 new jsAst.VariableUse(dynamicSetMetadataName), | 431 new jsAst.VariableUse(dynamicSetMetadataName), |
432 [table]))); | 432 [table]))); |
433 | 433 |
434 // (function(){statements})(); | 434 // (function(){statements})(); |
(...skipping 30 matching lines...) Expand all Loading... |
465 if (!element.isClass()) return false; | 465 if (!element.isClass()) return false; |
466 ClassElement cls = element; | 466 ClassElement cls = element; |
467 if (cls.isNative()) return true; | 467 if (cls.isNative()) return true; |
468 return isSupertypeOfNativeClass(element); | 468 return isSupertypeOfNativeClass(element); |
469 } | 469 } |
470 | 470 |
471 void assembleCode(CodeBuffer targetBuffer) { | 471 void assembleCode(CodeBuffer targetBuffer) { |
472 List<jsAst.Property> objectProperties = <jsAst.Property>[]; | 472 List<jsAst.Property> objectProperties = <jsAst.Property>[]; |
473 | 473 |
474 void addProperty(String name, jsAst.Expression value) { | 474 void addProperty(String name, jsAst.Expression value) { |
475 objectProperties.add(new jsAst.Property(js.string(name), value)); | 475 objectProperties.add(new jsAst.Property(jsBuilder.string(name), value)); |
476 } | 476 } |
477 | 477 |
478 // Because of native classes, we have to generate some is checks | 478 // Because of native classes, we have to generate some is checks |
479 // by calling a method, instead of accessing a property. So we | 479 // by calling a method, instead of accessing a property. So we |
480 // attach to the JS Object prototype these methods that return | 480 // attach to the JS Object prototype these methods that return |
481 // false, and will be overridden by subclasses when they have to | 481 // false, and will be overridden by subclasses when they have to |
482 // return true. | 482 // return true. |
483 void emitIsChecks() { | 483 void emitIsChecks() { |
484 for (ClassElement element in | 484 for (ClassElement element in |
485 Elements.sortedByPosition(emitter.checkedClasses)) { | 485 Elements.sortedByPosition(emitter.checkedClasses)) { |
486 if (!requiresNativeIsCheck(element)) continue; | 486 if (!requiresNativeIsCheck(element)) continue; |
487 if (element.isObject(compiler)) continue; | 487 if (element.isObject(compiler)) continue; |
488 // Add function for the is-test. | 488 // Add function for the is-test. |
489 String name = backend.namer.operatorIs(element); | 489 String name = backend.namer.operatorIs(element); |
490 addProperty(name, | 490 addProperty(name, |
491 js.fun([], js.return_(js['false']))); | 491 jsBuilder.fun([], jsBuilder.return_(js('false')))); |
492 // Add a function for the (trivial) substitution. | 492 // Add a function for the (trivial) substitution. |
493 addProperty(backend.namer.substitutionName(element), | 493 addProperty(backend.namer.substitutionName(element), |
494 js.fun([], js.return_(js['null']))); | 494 jsBuilder.fun([], jsBuilder.return_(js('null')))); |
495 } | 495 } |
496 } | 496 } |
497 emitIsChecks(); | 497 emitIsChecks(); |
498 | 498 |
499 jsAst.Expression makeCallOnThis(String functionName) { | 499 jsAst.Expression makeCallOnThis(String functionName) { |
500 // Because we know the function is intercepted, we need an extra | 500 // Because we know the function is intercepted, we need an extra |
501 // parameter. | 501 // parameter. |
502 return js.fun(['_'], js.return_(js['$functionName(this)'])); | 502 return jsBuilder.fun(['_'], jsBuilder.return_(js('$functionName(this)'))); |
503 } | 503 } |
504 | 504 |
505 if (!nativeClasses.isEmpty) { | 505 if (!nativeClasses.isEmpty) { |
506 emitDynamicDispatchMetadata(); | 506 emitDynamicDispatchMetadata(); |
507 | 507 |
508 // In order to have the toString method on every native class, | 508 // In order to have the toString method on every native class, |
509 // we must patch the JS Object prototype with a helper method. | 509 // we must patch the JS Object prototype with a helper method. |
510 String toStringName = backend.namer.publicInstanceMethodNameByArity( | 510 String toStringName = backend.namer.publicInstanceMethodNameByArity( |
511 const SourceString('toString'), 0); | 511 const SourceString('toString'), 0); |
512 addProperty(toStringName, makeCallOnThis(toStringHelperName)); | 512 addProperty(toStringName, makeCallOnThis(toStringHelperName)); |
513 | 513 |
514 // Same as above, but for hashCode. | 514 // Same as above, but for hashCode. |
515 String hashCodeName = | 515 String hashCodeName = |
516 backend.namer.publicGetterName(const SourceString('hashCode')); | 516 backend.namer.publicGetterName(const SourceString('hashCode')); |
517 addProperty(hashCodeName, makeCallOnThis(hashCodeHelperName)); | 517 addProperty(hashCodeName, makeCallOnThis(hashCodeHelperName)); |
518 | 518 |
519 // Same as above, but for operator==. | 519 // Same as above, but for operator==. |
520 String equalsName = backend.namer.publicInstanceMethodNameByArity( | 520 String equalsName = backend.namer.publicInstanceMethodNameByArity( |
521 const SourceString('=='), 1); | 521 const SourceString('=='), 1); |
522 // Because we know the function is intercepted, we need an extra | 522 // Because we know the function is intercepted, we need an extra |
523 // parameter. | 523 // parameter. |
524 addProperty(equalsName, js.fun(['_', 'a'], | 524 addProperty(equalsName, jsBuilder.fun(['_', 'a'], |
525 js.return_(js['this === a']))); | 525 jsBuilder.return_(js('this === a')))); |
526 | 526 |
527 // If the native emitter has been asked to take care of the | 527 // If the native emitter has been asked to take care of the |
528 // noSuchMethod handlers, we do that now. | 528 // noSuchMethod handlers, we do that now. |
529 if (handleNoSuchMethod) { | 529 if (handleNoSuchMethod) { |
530 emitter.emitNoSuchMethodHandlers(addProperty); | 530 emitter.emitNoSuchMethodHandlers(addProperty); |
531 } | 531 } |
532 } | 532 } |
533 | 533 |
534 // If we have any properties to add to Object.prototype, we run | 534 // If we have any properties to add to Object.prototype, we run |
535 // through them and add them using defineProperty. | 535 // through them and add them using defineProperty. |
536 if (!objectProperties.isEmpty) { | 536 if (!objectProperties.isEmpty) { |
537 jsAst.Expression init = | 537 jsAst.Expression init = |
538 js.fun(['table'], | 538 jsBuilder.fun(['table'], |
539 new jsAst.ForIn( | 539 new jsAst.ForIn( |
540 new jsAst.VariableDeclarationList( | 540 new jsAst.VariableDeclarationList( |
541 [new jsAst.VariableInitialization( | 541 [new jsAst.VariableInitialization( |
542 new jsAst.VariableDeclaration('key'), | 542 new jsAst.VariableDeclaration('key'), |
543 null)]), | 543 null)]), |
544 js['table'], | 544 js('table'), |
545 new jsAst.ExpressionStatement( | 545 new jsAst.ExpressionStatement( |
546 js['$defPropName(Object.prototype, key, table[key])'])))( | 546 js('$defPropName(Object.prototype, key, table[key])'))))( |
547 new jsAst.ObjectInitializer(objectProperties)); | 547 new jsAst.ObjectInitializer(objectProperties)); |
548 | 548 |
549 if (emitter.compiler.enableMinification) targetBuffer.add(';'); | 549 if (emitter.compiler.enableMinification) targetBuffer.add(';'); |
550 targetBuffer.add(jsAst.prettyPrint( | 550 targetBuffer.add(jsAst.prettyPrint( |
551 new jsAst.ExpressionStatement(init), compiler)); | 551 new jsAst.ExpressionStatement(init), compiler)); |
552 targetBuffer.add('\n'); | 552 targetBuffer.add('\n'); |
553 } | 553 } |
554 | 554 |
555 targetBuffer.add(nativeBuffer); | 555 targetBuffer.add(nativeBuffer); |
556 targetBuffer.add('\n'); | 556 targetBuffer.add('\n'); |
557 } | 557 } |
558 } | 558 } |
OLD | NEW |