| 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 |