OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |