Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(262)

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1767803002: a few small refactorings to closure workarounds (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/closure/closure_type.dart ('k') | lib/src/codegen/js_metalet.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; 5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet;
6 6
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
8 import 'package:analyzer/dart/ast/token.dart'; 8 import 'package:analyzer/dart/ast/token.dart';
9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; 9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
10 import 'package:analyzer/src/generated/constant.dart'; 10 import 'package:analyzer/src/generated/constant.dart';
(...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 var supertype = element.supertype; 370 var supertype = element.supertype;
371 if (!supertype.isObject) { 371 if (!supertype.isObject) {
372 for (var ctor in element.constructors) { 372 for (var ctor in element.constructors) {
373 var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library); 373 var parentCtor = supertype.lookUpConstructor(ctor.name, ctor.library);
374 var fun = js.call('function() { super.#(...arguments); }', 374 var fun = js.call('function() { super.#(...arguments); }',
375 [_constructorName(parentCtor)]) as JS.Fun; 375 [_constructorName(parentCtor)]) as JS.Fun;
376 body.add(new JS.Method(_constructorName(ctor), fun)); 376 body.add(new JS.Method(_constructorName(ctor), fun));
377 } 377 }
378 } 378 }
379 379
380 var classExpr = new JS.ClassExpression( 380 var cls = _emitClassDeclaration(element, body);
381 new JS.Identifier(element.name), _classHeritage(element), body); 381 return _finishClassDef(element.type, cls);
382
383 return _finishClassDef(
384 element.type, _emitClassHeritageWorkaround(classExpr));
385 } 382 }
386 383
387 JS.Statement _emitJsType(String dartClassName, DartObject jsName) { 384 JS.Statement _emitJsType(String dartClassName, DartObject jsName) {
388 var jsTypeName = 385 var jsTypeName =
389 getConstantField(jsName, 'name', types.stringType)?.toStringValue(); 386 getConstantField(jsName, 'name', types.stringType)?.toStringValue();
390 387
391 if (jsTypeName != null && jsTypeName != dartClassName) { 388 if (jsTypeName != null && jsTypeName != dartClassName) {
392 // We export the JS type as if it was a Dart type. For example this allows 389 // We export the JS type as if it was a Dart type. For example this allows
393 // `dom.InputElement` to actually be HTMLInputElement. 390 // `dom.InputElement` to actually be HTMLInputElement.
394 // TODO(jmesserly): if we had the JS name on the Element, we could just 391 // TODO(jmesserly): if we had the JS name on the Element, we could just
(...skipping 19 matching lines...) Expand all
414 for (var member in node.members) { 411 for (var member in node.members) {
415 if (member is ConstructorDeclaration) { 412 if (member is ConstructorDeclaration) {
416 ctors.add(member); 413 ctors.add(member);
417 } else if (member is FieldDeclaration) { 414 } else if (member is FieldDeclaration) {
418 (member.isStatic ? staticFields : fields).add(member); 415 (member.isStatic ? staticFields : fields).add(member);
419 } else if (member is MethodDeclaration) { 416 } else if (member is MethodDeclaration) {
420 methods.add(member); 417 methods.add(member);
421 } 418 }
422 } 419 }
423 420
424 var classExpr = new JS.ClassExpression(new JS.Identifier(type.name), 421 var allFields = new List.from(fields)..addAll(staticFields);
425 _classHeritage(classElem), _emitClassMethods(node, ctors, fields), 422
426 typeParams: _emitTypeFormals(classElem.typeParameters), 423 var classDecl = _emitClassDeclaration(
427 fields: 424 classElem, _emitClassMethods(node, ctors, fields),
428 _emitFieldDeclarations(classElem, fields, staticFields).toList()); 425 fields: allFields);
429 426
430 String jsPeerName; 427 String jsPeerName;
431 var jsPeer = findAnnotation(classElem, isJsPeerInterface); 428 var jsPeer = findAnnotation(classElem, isJsPeerInterface);
432 // Only look at "Native" annotations on registered extension types. 429 // Only look at "Native" annotations on registered extension types.
433 // E.g., we're current ignoring the ones in dart:html. 430 // E.g., we're current ignoring the ones in dart:html.
434 if (jsPeer == null && _extensionTypes.contains(classElem)) { 431 if (jsPeer == null && _extensionTypes.contains(classElem)) {
435 jsPeer = findAnnotation(classElem, isNativeAnnotation); 432 jsPeer = findAnnotation(classElem, isNativeAnnotation);
436 } 433 }
437 if (jsPeer != null) { 434 if (jsPeer != null) {
438 jsPeerName = 435 jsPeerName =
439 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); 436 getConstantField(jsPeer, 'name', types.stringType)?.toStringValue();
440 if (jsPeerName.contains(',')) { 437 if (jsPeerName.contains(',')) {
441 jsPeerName = jsPeerName.split(',')[0]; 438 jsPeerName = jsPeerName.split(',')[0];
442 } 439 }
443 } 440 }
444 441
445 var body = _finishClassMembers(classElem, classExpr, ctors, fields, 442 var body = _finishClassMembers(classElem, classDecl, ctors, fields,
446 staticFields, methods, node.metadata, jsPeerName); 443 staticFields, methods, node.metadata, jsPeerName);
447 444
448 var result = _finishClassDef(type, body); 445 var result = _finishClassDef(type, body);
449 446
450 if (jsPeerName != null) { 447 if (jsPeerName != null) {
451 // This class isn't allowed to be lazy, because we need to set up 448 // This class isn't allowed to be lazy, because we need to set up
452 // the native JS type eagerly at this point. 449 // the native JS type eagerly at this point.
453 // If we wanted to support laziness, we could defer the hookup until 450 // If we wanted to support laziness, we could defer the hookup until
454 // the end of the Dart library cycle load. 451 // the end of the Dart library cycle load.
455 assert(_loader.isLoaded(classElem)); 452 assert(_loader.isLoaded(classElem));
456 453
457 // TODO(jmesserly): this copies the dynamic members. 454 // TODO(jmesserly): this copies the dynamic members.
458 // Probably fine for objects coming from JS, but not if we actually 455 // Probably fine for objects coming from JS, but not if we actually
459 // want to support construction of instances with generic types other 456 // want to support construction of instances with generic types other
460 // than dynamic. See issue #154 for Array and List<E> related bug. 457 // than dynamic. See issue #154 for Array and List<E> related bug.
461 var copyMembers = js.statement( 458 var copyMembers = js.statement(
462 'dart.registerExtension(dart.global.#, #);', 459 'dart.registerExtension(dart.global.#, #);',
463 [_propertyName(jsPeerName), classElem.name]); 460 [_propertyName(jsPeerName), classElem.name]);
464 return _statement([result, copyMembers]); 461 return _statement([result, copyMembers]);
465 } 462 }
466 return result; 463 return result;
467 } 464 }
468 465
469 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) { 466 List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) {
470 if (!options.closure) return null;
471 return typeFormals 467 return typeFormals
472 .map((t) => new JS.Identifier(t.name)) 468 .map((t) => new JS.Identifier(t.name))
473 .toList(growable: false); 469 .toList(growable: false);
474 } 470 }
475 471
476 /// Emit field declarations for TypeScript & Closure's ES6_TYPED 472 /// Emits a field declaration for TypeScript & Closure's ES6_TYPED
477 /// (e.g. `class Foo { i: string; }`) 473 /// (e.g. `class Foo { i: string; }`)
478 Iterable<JS.VariableDeclarationList> _emitFieldDeclarations( 474 JS.VariableDeclarationList _emitTypeScriptField(FieldDeclaration field) {
479 ClassElement classElem, 475 return new JS.VariableDeclarationList(
480 List<FieldDeclaration> fields, 476 field.isStatic ? 'static' : null,
481 List<FieldDeclaration> staticFields) sync* { 477 field.fields.variables
482 if (!options.closure) return; 478 .map((decl) => new JS.VariableInitialization(
483 479 new JS.Identifier(
484 makeInitialization(VariableDeclaration decl) => 480 // TODO(ochafik): use a refactored _emitMemberName instead.
485 new JS.VariableInitialization( 481 decl.name.name,
486 new JS.Identifier( 482 type: emitTypeRef(decl.element.type)),
487 // TODO(ochafik): use a refactored _emitMemberName instead. 483 null))
488 decl.name.name, 484 .toList(growable: false));
489 type: emitTypeRef(decl.element.type)),
490 null);
491
492 for (var field in fields) {
493 yield new JS.VariableDeclarationList(
494 null, field.fields.variables.map(makeInitialization).toList());
495 }
496 for (var field in staticFields) {
497 yield new JS.VariableDeclarationList(
498 'static', field.fields.variables.map(makeInitialization).toList());
499 }
500 } 485 }
501 486
502 @override 487 @override
503 JS.Statement visitEnumDeclaration(EnumDeclaration node) { 488 JS.Statement visitEnumDeclaration(EnumDeclaration node) {
504 var element = node.element; 489 var element = node.element;
505 var type = element.type; 490 var type = element.type;
506 var name = js.string(type.name); 491 var name = js.string(type.name);
507 var id = new JS.Identifier(type.name); 492 var id = new JS.Identifier(type.name);
508 493
509 // Generate a class per section 13 of the spec. 494 // Generate a class per section 13 of the spec.
(...skipping 10 matching lines...) Expand all
520 for (var i = 0; i < fields.length; ++i) { 505 for (var i = 0; i < fields.length; ++i) {
521 properties.add(new JS.Property( 506 properties.add(new JS.Property(
522 js.number(i), js.string('${type.name}.${fields[i].name}'))); 507 js.number(i), js.string('${type.name}.${fields[i].name}')));
523 } 508 }
524 var nameMap = new JS.ObjectInitializer(properties, multiline: true); 509 var nameMap = new JS.ObjectInitializer(properties, multiline: true);
525 var toStringF = new JS.Method(js.string('toString'), 510 var toStringF = new JS.Method(js.string('toString'),
526 js.call('function() { return #[this.index]; }', nameMap) as JS.Fun); 511 js.call('function() { return #[this.index]; }', nameMap) as JS.Fun);
527 512
528 // Create enum class 513 // Create enum class
529 var classExpr = new JS.ClassExpression( 514 var classExpr = new JS.ClassExpression(
530 id, _classHeritage(element), [constructor, toStringF]); 515 id, _emitClassHeritage(element), [constructor, toStringF]);
531 var result = <JS.Statement>[js.statement('#', classExpr)]; 516 var result = <JS.Statement>[js.statement('#', classExpr)];
532 517
533 // Create static fields for each enum value 518 // Create static fields for each enum value
534 for (var i = 0; i < fields.length; ++i) { 519 for (var i = 0; i < fields.length; ++i) {
535 result.add(js.statement('#.# = dart.const(new #(#));', 520 result.add(js.statement('#.# = dart.const(new #(#));',
536 [id, fields[i].name, id, js.number(i)])); 521 [id, fields[i].name, id, js.number(i)]));
537 } 522 }
538 523
539 // Create static values list 524 // Create static values list
540 var values = new JS.ArrayInitializer(new List<JS.Expression>.from( 525 var values = new JS.ArrayInitializer(new List<JS.Expression>.from(
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
601 var typeElement = typeArg.element; 586 var typeElement = typeArg.element;
602 // FIXME(vsm): This does not track mutual recursive dependences. 587 // FIXME(vsm): This does not track mutual recursive dependences.
603 if (current == typeElement || _deferIfNeeded(typeArg, current)) { 588 if (current == typeElement || _deferIfNeeded(typeArg, current)) {
604 return true; 589 return true;
605 } 590 }
606 } 591 }
607 } 592 }
608 return false; 593 return false;
609 } 594 }
610 595
611 JS.Expression _classHeritage(ClassElement element) { 596 JS.Statement _emitClassDeclaration(
597 ClassElement element, List<JS.Method> methods,
598 {List<FieldDeclaration> fields}) {
599 String name = element.name;
600 var heritage = _emitClassHeritage(element);
601 var typeParams = _emitTypeFormals(element.typeParameters);
602 var jsFields = fields?.map(_emitTypeScriptField)?.toList();
603
604 // Workaround for Closure: super classes must be qualified paths.
605 // TODO(jmesserly): is there a bug filed? we need to get out of the business
606 // of working around bugs in other transpilers...
607 JS.Statement workaroundSuper = null;
608 if (options.closure && heritage is JS.Call) {
609 var superVar = new JS.TemporaryId('$name\$super');
610 workaroundSuper = js.statement('const # = #;', [superVar, heritage]);
611 heritage = superVar;
612 }
613 var decl = new JS.ClassDeclaration(new JS.ClassExpression(
614 new JS.Identifier(name), heritage, methods,
615 typeParams: typeParams, fields: jsFields));
616 if (workaroundSuper != null) {
617 return new JS.Block([workaroundSuper, decl]);
618 }
619 return decl;
620 }
621
622 JS.Expression _emitClassHeritage(ClassElement element) {
612 var type = element.type; 623 var type = element.type;
613 if (type.isObject) return null; 624 if (type.isObject) return null;
614 625
615 // Assume we can load eagerly, until proven otherwise. 626 // Assume we can load eagerly, until proven otherwise.
616 _loader.startTopLevel(element); 627 _loader.startTopLevel(element);
617 628
618 // Find the super type 629 // Find the super type
619 JS.Expression heritage; 630 JS.Expression heritage;
620 var supertype = type.superclass; 631 var supertype = type.superclass;
621 if (_deferIfNeeded(supertype, element)) { 632 if (_deferIfNeeded(supertype, element)) {
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
751 JS.Expression _instantiateAnnotation(Annotation node) { 762 JS.Expression _instantiateAnnotation(Annotation node) {
752 var element = node.element; 763 var element = node.element;
753 if (element is ConstructorElement) { 764 if (element is ConstructorElement) {
754 return _emitInstanceCreationExpression(element, element.returnType, 765 return _emitInstanceCreationExpression(element, element.returnType,
755 node.constructorName, node.arguments, true); 766 node.constructorName, node.arguments, true);
756 } else { 767 } else {
757 return _visit(node.name); 768 return _visit(node.name);
758 } 769 }
759 } 770 }
760 771
761 _isQualifiedPath(JS.Expression node) =>
762 node is JS.Identifier ||
763 node is JS.PropertyAccess &&
764 _isQualifiedPath(node.receiver) &&
765 node.selector is JS.LiteralString;
766
767 /// Workaround for Closure: super classes must be qualified paths.
768 JS.Statement _emitClassHeritageWorkaround(JS.ClassExpression cls) {
769 if (options.closure &&
770 cls.heritage != null &&
771 !_isQualifiedPath(cls.heritage)) {
772 var superVar = new JS.TemporaryId(cls.name.name + r'$super');
773 return _statement([
774 js.statement('const # = #;', [superVar, cls.heritage]),
775 new JS.ClassDeclaration(new JS.ClassExpression(
776 cls.name, superVar, cls.methods,
777 typeParams: cls.typeParams, fields: cls.fields))
778 ]);
779 }
780 return new JS.ClassDeclaration(cls);
781 }
782
783 /// Emit class members that need to come after the class declaration, such 772 /// Emit class members that need to come after the class declaration, such
784 /// as static fields. See [_emitClassMethods] for things that are emitted 773 /// as static fields. See [_emitClassMethods] for things that are emitted
785 /// inside the ES6 `class { ... }` node. 774 /// inside the ES6 `class { ... }` node.
786 JS.Statement _finishClassMembers( 775 JS.Statement _finishClassMembers(
787 ClassElement classElem, 776 ClassElement classElem,
788 JS.ClassExpression cls, 777 JS.Statement classDecl,
789 List<ConstructorDeclaration> ctors, 778 List<ConstructorDeclaration> ctors,
790 List<FieldDeclaration> fields, 779 List<FieldDeclaration> fields,
791 List<FieldDeclaration> staticFields, 780 List<FieldDeclaration> staticFields,
792 List<MethodDeclaration> methods, 781 List<MethodDeclaration> methods,
793 List<Annotation> metadata, 782 List<Annotation> metadata,
794 String jsPeerName) { 783 String jsPeerName) {
795 var name = classElem.name; 784 var name = classElem.name;
796 var body = <JS.Statement>[]; 785 var body = <JS.Statement>[];
797 786
798 if (_extensionTypes.contains(classElem)) { 787 if (_extensionTypes.contains(classElem)) {
(...skipping 12 matching lines...) Expand all
811 } 800 }
812 } 801 }
813 } 802 }
814 } 803 }
815 if (dartxNames.isNotEmpty) { 804 if (dartxNames.isNotEmpty) {
816 body.add(js.statement('dart.defineExtensionNames(#)', 805 body.add(js.statement('dart.defineExtensionNames(#)',
817 [new JS.ArrayInitializer(dartxNames, multiline: true)])); 806 [new JS.ArrayInitializer(dartxNames, multiline: true)]));
818 } 807 }
819 } 808 }
820 809
821 body.add(_emitClassHeritageWorkaround(cls)); 810 body.add(classDecl);
822 811
823 // TODO(jmesserly): we should really just extend native Array. 812 // TODO(jmesserly): we should really just extend native Array.
824 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { 813 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) {
825 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', 814 body.add(js.statement('dart.setBaseClass(#, dart.global.#);',
826 [classElem.name, _propertyName(jsPeerName)])); 815 [classElem.name, _propertyName(jsPeerName)]));
827 } 816 }
828 817
829 // Deferred Superclass 818 // Deferred Superclass
830 if (_hasDeferredSupertype.contains(classElem)) { 819 if (_hasDeferredSupertype.contains(classElem)) {
831 body.add(js.statement('#.prototype.__proto__ = #.prototype;', 820 body.add(js.statement('#.prototype.__proto__ = #.prototype;',
(...skipping 2660 matching lines...) Expand 10 before | Expand all | Expand 10 after
3492 3481
3493 _visit(AstNode node) { 3482 _visit(AstNode node) {
3494 if (node == null) return null; 3483 if (node == null) return null;
3495 var result = node.accept(this); 3484 var result = node.accept(this);
3496 if (result is JS.Node) result = annotate(result, node); 3485 if (result is JS.Node) result = annotate(result, node);
3497 return result; 3486 return result;
3498 } 3487 }
3499 3488
3500 // TODO(jmesserly): this will need to be a generic method, if we ever want to 3489 // TODO(jmesserly): this will need to be a generic method, if we ever want to
3501 // self-host strong mode. 3490 // self-host strong mode.
3502 List /*<T>*/ _visitList /*<T>*/ (Iterable<AstNode> nodes) { 3491 List/*<T>*/ _visitList/*<T>*/(Iterable<AstNode> nodes) {
3503 if (nodes == null) return null; 3492 if (nodes == null) return null;
3504 var result = /*<T>*/ []; 3493 var result = /*<T>*/ [];
3505 for (var node in nodes) result.add(_visit(node)); 3494 for (var node in nodes) result.add(_visit(node));
3506 return result; 3495 return result;
3507 } 3496 }
3508 3497
3509 /// Visits a list of expressions, creating a comma expression if needed in JS. 3498 /// Visits a list of expressions, creating a comma expression if needed in JS.
3510 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { 3499 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) {
3511 if (nodes == null || nodes.isEmpty) return null; 3500 if (nodes == null || nodes.isEmpty) return null;
3512 return new JS.Expression.binary( 3501 return new JS.Expression.binary(
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after
3813 var rules = new StrongTypeSystemImpl(); 3802 var rules = new StrongTypeSystemImpl();
3814 var codegen = 3803 var codegen =
3815 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); 3804 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields);
3816 var module = codegen.emitLibrary(unit); 3805 var module = codegen.emitLibrary(unit);
3817 var out = compiler.getOutputPath(library.source.uri); 3806 var out = compiler.getOutputPath(library.source.uri);
3818 var flags = compiler.options; 3807 var flags = compiler.options;
3819 var serverUri = flags.serverMode 3808 var serverUri = flags.serverMode
3820 ? Uri.parse('http://${flags.host}:${flags.port}/') 3809 ? Uri.parse('http://${flags.host}:${flags.port}/')
3821 : null; 3810 : null;
3822 return writeJsLibrary(module, out, compiler.inputBaseDir, serverUri, 3811 return writeJsLibrary(module, out, compiler.inputBaseDir, serverUri,
3812 emitTypes: options.closure,
3823 emitSourceMaps: options.emitSourceMaps, 3813 emitSourceMaps: options.emitSourceMaps,
3824 fileSystem: compiler.fileSystem); 3814 fileSystem: compiler.fileSystem);
3825 } 3815 }
3826 } 3816 }
3827 3817
3828 /// Choose a canonical name from the library element. 3818 /// Choose a canonical name from the library element.
3829 /// This never uses the library's name (the identifier in the `library` 3819 /// This never uses the library's name (the identifier in the `library`
3830 /// declaration) as it doesn't have any meaningful rules enforced. 3820 /// declaration) as it doesn't have any meaningful rules enforced.
3831 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); 3821 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library);
3832 3822
(...skipping 10 matching lines...) Expand all
3843 3833
3844 /// A special kind of element created by the compiler, signifying a temporary 3834 /// A special kind of element created by the compiler, signifying a temporary
3845 /// variable. These objects use instance equality, and should be shared 3835 /// variable. These objects use instance equality, and should be shared
3846 /// everywhere in the tree where they are treated as the same variable. 3836 /// everywhere in the tree where they are treated as the same variable.
3847 class TemporaryVariableElement extends LocalVariableElementImpl { 3837 class TemporaryVariableElement extends LocalVariableElementImpl {
3848 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3838 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
3849 3839
3850 int get hashCode => identityHashCode(this); 3840 int get hashCode => identityHashCode(this);
3851 bool operator ==(Object other) => identical(this, other); 3841 bool operator ==(Object other) => identical(this, other);
3852 } 3842 }
OLDNEW
« no previous file with comments | « lib/src/closure/closure_type.dart ('k') | lib/src/codegen/js_metalet.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698