| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 /** | 5 /** |
| 6 * Represents a meta-value for code generation. | 6 * Represents a meta-value for code generation. |
| 7 */ | 7 */ |
| 8 class Value { | 8 class Value { |
| 9 Type _type; | 9 Type _type; |
| 10 | 10 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 Value(this._type, this.code, this.span, [this.needsTemp = true]) { | 49 Value(this._type, this.code, this.span, [this.needsTemp = true]) { |
| 50 if (_type == null) world.internalError('type passed as null', span); | 50 if (_type == null) world.internalError('type passed as null', span); |
| 51 } | 51 } |
| 52 | 52 |
| 53 // TODO(jimhug): Replace with TypeValue. | 53 // TODO(jimhug): Replace with TypeValue. |
| 54 Value.type(this._type, this.span) | 54 Value.type(this._type, this.span) |
| 55 : code = null, needsTemp = false, isType = true { | 55 : code = null, needsTemp = false, isType = true { |
| 56 if (_type == null) world.internalError('type passed as null', span); | 56 if (_type == null) world.internalError('type passed as null', span); |
| 57 } | 57 } |
| 58 | 58 |
| 59 Value union(Value other) { |
| 60 // TODO(jimhug): What the hell is right here? |
| 61 if (this == other) return this; |
| 62 world.internalError('not yet ready for union'); |
| 63 } |
| 64 |
| 59 /** Is this value a constant expression? */ | 65 /** Is this value a constant expression? */ |
| 60 bool get isConst() => false; | 66 bool get isConst() => false; |
| 61 | 67 |
| 62 /** If [isConst], the [EvaluatedValue] that defines this value. */ | 68 /** If [isConst], the [EvaluatedValue] that defines this value. */ |
| 63 EvaluatedValue get constValue() => null; | 69 EvaluatedValue get constValue() => null; |
| 64 | 70 |
| 71 // TODO(jimhug): remove once type system works better. |
| 72 setField(Member field, Value value, [bool duringInit = false]) { } |
| 73 |
| 74 // Nothing to do in general? |
| 75 validateInitialized(SourceSpan span) { } |
| 76 |
| 65 // TODO(jimhug): Fix these names once get/set are truly pseudo-keywords. | 77 // TODO(jimhug): Fix these names once get/set are truly pseudo-keywords. |
| 66 // See issue #379. | 78 // See issue #379. |
| 67 Value get_(MethodGenerator context, String name, Node node) { | 79 Value get_(MethodGenerator context, String name, Node node) { |
| 68 final member = _resolveMember(context, name, node); | 80 final member = _resolveMember(context, name, node); |
| 69 if (member != null) { | 81 if (member != null) { |
| 70 return member._get(context, node, this); | 82 return member._get(context, node, this); |
| 71 } else { | 83 } else { |
| 72 return invokeNoSuchMethod(context, 'get:$name', node); | 84 return invokeNoSuchMethod(context, 'get:$name', node); |
| 73 } | 85 } |
| 74 } | 86 } |
| 75 | 87 |
| 76 Value set_(MethodGenerator context, String name, Node node, Value value, | 88 Value set_(MethodGenerator context, String name, Node node, Value value, |
| 77 [bool isDynamic=false]) { | 89 [bool isDynamic=false]) { |
| 78 | 90 |
| 79 final member = _resolveMember(context, name, node, isDynamic); | 91 final member = _resolveMember(context, name, node, isDynamic); |
| 80 if (member != null) { | 92 if (member != null) { |
| 81 return member._set(context, node, this, value, isDynamic); | 93 return member._set(context, node, this, value, isDynamic); |
| 82 } else { | 94 } else { |
| 83 return invokeNoSuchMethod(context, 'set:$name', node, | 95 return invokeNoSuchMethod(context, 'set:$name', node, |
| 84 new Arguments(null, [value])); | 96 new Arguments(null, [value])); |
| 85 } | 97 } |
| 86 } | 98 } |
| 87 | 99 |
| 100 Value unop(int kind, MethodGenerator context, var node) { |
| 101 switch (kind) { |
| 102 case TokenKind.NOT: |
| 103 // TODO(jimhug): Issue #359 seeks to clarify this behavior. |
| 104 var newVal = convertTo(context, world.nonNullBool); |
| 105 return new Value(newVal.type, '!${newVal.code}', node.span); |
| 106 case TokenKind.ADD: |
| 107 world.error('no unary add operator in dart', node.span); |
| 108 break; |
| 109 case TokenKind.SUB: |
| 110 return invoke(context, ':negate', node, Arguments.EMPTY); |
| 111 case TokenKind.BIT_NOT: |
| 112 return invoke(context, ':bit_not', node, Arguments.EMPTY); |
| 113 } |
| 114 world.internalError('unimplemented: ${node.op}', node.span); |
| 115 } |
| 116 |
| 88 Value binop(int kind, Value other, MethodGenerator context, var node) { | 117 Value binop(int kind, Value other, MethodGenerator context, var node) { |
| 89 switch (kind) { | 118 switch (kind) { |
| 90 case TokenKind.AND: | 119 case TokenKind.AND: |
| 91 case TokenKind.OR: | 120 case TokenKind.OR: |
| 92 final code = '${code} ${node.op} ${other.code}'; | 121 final code = '${code} ${node.op} ${other.code}'; |
| 93 return new Value(world.nonNullBool, code, node.span); | 122 return new Value(world.nonNullBool, code, node.span); |
| 94 // TODO(jimhug): Lot's to resolve here. | 123 // TODO(jimhug): Lot's to resolve here. |
| 95 case TokenKind.EQ_STRICT: | 124 case TokenKind.EQ_STRICT: |
| 96 return new Value(world.nonNullBool, '${code} == ${other.code}', | 125 return new Value(world.nonNullBool, '${code} == ${other.code}', |
| 97 node.span); | 126 node.span); |
| (...skipping 439 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 537 static Value fromString(String value, SourceSpan span) { | 566 static Value fromString(String value, SourceSpan span) { |
| 538 return new StringValue(value, true, span); | 567 return new StringValue(value, true, span); |
| 539 } | 568 } |
| 540 | 569 |
| 541 static Value fromNull(SourceSpan span) { | 570 static Value fromNull(SourceSpan span) { |
| 542 return new NullValue(true, span); | 571 return new NullValue(true, span); |
| 543 } | 572 } |
| 544 } | 573 } |
| 545 | 574 |
| 546 | 575 |
| 547 // rename to PrimitiveValue | 576 // TODO(jimhug): rename to PrimitiveValue and refactor further |
| 548 class EvaluatedValue extends Value implements Hashable { | 577 class EvaluatedValue extends Value implements Hashable { |
| 549 /** Is this value treated as const by dart language? */ | 578 /** Is this value treated as const by dart language? */ |
| 550 bool isConst; | 579 bool isConst; |
| 551 | 580 |
| 552 EvaluatedValue(this.isConst, Type type, SourceSpan span): | 581 EvaluatedValue(this.isConst, Type type, SourceSpan span): |
| 553 super(type, '@@@', span, false); | 582 super(type, '@@@', span, false); |
| 554 | 583 |
| 555 String get code() => '@@@'; | 584 String get code() => '@@@'; |
| 556 | 585 |
| 557 EvaluatedValue get constValue() => this; | 586 EvaluatedValue get constValue() => this; |
| 558 | 587 |
| 559 // TODO(jimhug): Using computed code here with caching is major perf fear. | 588 // TODO(jimhug): Using computed code here without caching is major fear. |
| 560 int hashCode() => code.hashCode(); | 589 int hashCode() => code.hashCode(); |
| 561 | 590 |
| 562 bool operator ==(var other) { | 591 bool operator ==(var other) { |
| 563 return other is EvaluatedValue && other.type == this.type && | 592 return other is EvaluatedValue && other.type == this.type && |
| 564 other.code == this.code; | 593 other.code == this.code; |
| 565 } | 594 } |
| 566 } | 595 } |
| 567 | 596 |
| 597 |
| 568 class NullValue extends EvaluatedValue { | 598 class NullValue extends EvaluatedValue { |
| 569 NullValue(bool isConst, SourceSpan span): | 599 NullValue(bool isConst, SourceSpan span): |
| 570 super(isConst, world.varType, span); | 600 super(isConst, world.varType, span); |
| 571 | 601 |
| 572 get actualValue() => null; | 602 get actualValue() => null; |
| 573 | 603 |
| 574 String get code() => 'null'; | 604 String get code() => 'null'; |
| 575 | 605 |
| 576 Value binop(int kind, var other, MethodGenerator context, var node) { | 606 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 577 // TODO(jimhug): Support int/double better | 607 if (other is! NullValue) return super.binop(kind, other, context, node); |
| 578 if (other is! ListValue) return super.binop(kind, other, context, node); | |
| 579 | 608 |
| 609 final c = isConst && other.isConst; |
| 610 final s = node.span; |
| 580 switch (kind) { | 611 switch (kind) { |
| 581 case TokenKind.EQ_STRICT: | 612 case TokenKind.EQ_STRICT: |
| 582 return new BoolValue(type == other.type && code == other.code, | 613 case TokenKind.EQ: |
| 583 isConst && other.isConst, node.span); | 614 return new BoolValue(true, c, s); |
| 584 case TokenKind.NE_STRICT: | 615 case TokenKind.NE_STRICT: |
| 585 return new BoolValue(type != other.type || code != other.code, | 616 case TokenKind.NE: |
| 586 isConst && other.isConst, node.span); | 617 return new BoolValue(false, c, s); |
| 587 } | 618 } |
| 588 | 619 |
| 589 // TODO(jimhug): Will flesh out ops here! | |
| 590 return super.binop(kind, other, context, node); | 620 return super.binop(kind, other, context, node); |
| 591 } | 621 } |
| 592 } | 622 } |
| 593 | 623 |
| 594 class BoolValue extends EvaluatedValue { | 624 class BoolValue extends EvaluatedValue { |
| 595 bool actualValue; | 625 bool actualValue; |
| 596 | 626 |
| 597 BoolValue(this.actualValue, bool isConst, SourceSpan span): | 627 BoolValue(this.actualValue, bool isConst, SourceSpan span): |
| 598 super(isConst, world.nonNullBool, span); | 628 super(isConst, world.nonNullBool, span); |
| 599 | 629 |
| 600 String get code() => actualValue ? 'true' : 'false'; | 630 String get code() => actualValue ? 'true' : 'false'; |
| 601 | 631 |
| 632 Value unop(int kind, MethodGenerator context, var node) { |
| 633 switch (kind) { |
| 634 case TokenKind.NOT: |
| 635 return new BoolValue(!actualValue, isConst, node.span); |
| 636 } |
| 637 return super.unop(kind, context, node); |
| 638 } |
| 639 |
| 602 Value binop(int kind, var other, MethodGenerator context, var node) { | 640 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 603 if (other is! BoolValue) return super.binop(kind, other, context, node); | 641 if (other is! BoolValue) return super.binop(kind, other, context, node); |
| 604 | 642 |
| 643 final c = isConst && other.isConst; |
| 644 final s = node.span; |
| 645 bool x = actualValue, y = other.actualValue; |
| 605 switch (kind) { | 646 switch (kind) { |
| 647 case TokenKind.EQ_STRICT: |
| 648 case TokenKind.EQ: |
| 649 return new BoolValue(x == y, c, s); |
| 650 case TokenKind.NE_STRICT: |
| 651 case TokenKind.NE: |
| 652 return new BoolValue(x != y, c, s); |
| 606 case TokenKind.AND: | 653 case TokenKind.AND: |
| 607 return new BoolValue(actualValue && other.actualValue, | 654 return new BoolValue(x && y, c, s); |
| 608 isConst && other.isConst, node.span); | |
| 609 case TokenKind.OR: | 655 case TokenKind.OR: |
| 610 return new BoolValue(actualValue || other.actualValue, | 656 return new BoolValue(x || y, c, s); |
| 611 isConst && other.isConst, node.span); | |
| 612 case TokenKind.EQ_STRICT: | |
| 613 return new BoolValue(actualValue == other.actualValue, | |
| 614 isConst && other.isConst, node.span); | |
| 615 case TokenKind.NE_STRICT: | |
| 616 return new BoolValue(actualValue != other.actualValue, | |
| 617 isConst && other.isConst, node.span); | |
| 618 } | 657 } |
| 619 | 658 |
| 620 // TODO(jimhug): Could handle more ops here, but does it matter? | |
| 621 return super.binop(kind, other, context, node); | 659 return super.binop(kind, other, context, node); |
| 622 } | 660 } |
| 623 } | 661 } |
| 624 | 662 |
| 625 class IntValue extends EvaluatedValue { | 663 class IntValue extends EvaluatedValue { |
| 626 int actualValue; | 664 int actualValue; |
| 627 | 665 |
| 628 IntValue(this.actualValue, bool isConst, SourceSpan span): | 666 IntValue(this.actualValue, bool isConst, SourceSpan span): |
| 629 super(isConst, world.intType, span); | 667 super(isConst, world.intType, span); |
| 630 | 668 |
| 631 // TODO(jimhug): Only add parens when needed. | 669 // TODO(jimhug): Only add parens when needed. |
| 632 String get code() => '(${actualValue})'; | 670 String get code() => '(${actualValue})'; |
| 633 | 671 |
| 672 Value unop(int kind, MethodGenerator context, var node) { |
| 673 switch (kind) { |
| 674 case TokenKind.ADD: |
| 675 // This is allowed on numeric constants only |
| 676 return new IntValue(actualValue, isConst, span); |
| 677 case TokenKind.SUB: |
| 678 return new IntValue(-actualValue, isConst, span); |
| 679 case TokenKind.BIT_NOT: |
| 680 return new IntValue(~actualValue, isConst, span); |
| 681 } |
| 682 return super.unop(kind, context, node); |
| 683 } |
| 684 |
| 685 |
| 634 Value binop(int kind, var other, MethodGenerator context, var node) { | 686 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 635 // TODO(jimhug): Support int/double better | 687 final c = isConst && other.isConst; |
| 636 if (other is! IntValue) return super.binop(kind, other, context, node); | 688 final s = node.span; |
| 689 if (other is IntValue) { |
| 690 int x = actualValue; |
| 691 int y = other.actualValue; |
| 692 switch (kind) { |
| 693 case TokenKind.EQ_STRICT: |
| 694 case TokenKind.EQ: |
| 695 return new BoolValue(x == y, c, s); |
| 696 case TokenKind.NE_STRICT: |
| 697 case TokenKind.NE: |
| 698 return new BoolValue(x != y, c, s); |
| 637 | 699 |
| 638 switch (kind) { | 700 case TokenKind.BIT_OR: |
| 639 case TokenKind.EQ_STRICT: | 701 return new IntValue(x | y, c, s); |
| 640 return new BoolValue(actualValue == other.actualValue, | 702 case TokenKind.BIT_XOR: |
| 641 isConst && other.isConst, node.span); | 703 return new IntValue(x ^ y, c, s); |
| 642 case TokenKind.NE_STRICT: | 704 case TokenKind.BIT_AND: |
| 643 return new BoolValue(actualValue != other.actualValue, | 705 return new IntValue(x & y, c, s); |
| 644 isConst && other.isConst, node.span); | 706 case TokenKind.SHL: |
| 707 return new IntValue(x << y, c, s); |
| 708 case TokenKind.SAR: |
| 709 return new IntValue(x >> y, c, s); |
| 710 case TokenKind.SHR: |
| 711 return new IntValue(x >>> y, c, s); |
| 712 case TokenKind.ADD: |
| 713 return new IntValue(x + y, c, s); |
| 714 case TokenKind.SUB: |
| 715 return new IntValue(x - y, c, s); |
| 716 case TokenKind.MUL: |
| 717 return new IntValue(x * y, c, s); |
| 718 case TokenKind.DIV: |
| 719 return new DoubleValue(x / y, c, s); |
| 720 case TokenKind.TRUNCDIV: |
| 721 return new IntValue(x ~/ y, c, s); |
| 722 case TokenKind.MOD: |
| 723 return new IntValue(x % y, c, s); |
| 724 case TokenKind.LT: |
| 725 return new BoolValue(x < y, c, s); |
| 726 case TokenKind.GT: |
| 727 return new BoolValue(x > y, c, s); |
| 728 case TokenKind.LTE: |
| 729 return new BoolValue(x <= y, c, s); |
| 730 case TokenKind.GTE: |
| 731 return new BoolValue(x >= y, c, s); |
| 732 } |
| 733 } else if (other is DoubleValue) { |
| 734 int x = actualValue; |
| 735 double y = other.actualValue; |
| 736 switch (kind) { |
| 737 case TokenKind.EQ_STRICT: |
| 738 case TokenKind.EQ: |
| 739 return new BoolValue(x == y, c, s); |
| 740 case TokenKind.NE_STRICT: |
| 741 case TokenKind.NE: |
| 742 return new BoolValue(x != y, c, s); |
| 743 |
| 744 case TokenKind.ADD: |
| 745 return new DoubleValue(x + y, c, s); |
| 746 case TokenKind.SUB: |
| 747 return new DoubleValue(x - y, c, s); |
| 748 case TokenKind.MUL: |
| 749 return new DoubleValue(x * y, c, s); |
| 750 case TokenKind.DIV: |
| 751 return new DoubleValue(x / y, c, s); |
| 752 case TokenKind.TRUNCDIV: |
| 753 // TODO(jimhug): I expected int, but corelib says double here... |
| 754 return new DoubleValue(x ~/ y, c, s); |
| 755 case TokenKind.MOD: |
| 756 return new DoubleValue(x % y, c, s); |
| 757 case TokenKind.LT: |
| 758 return new BoolValue(x < y, c, s); |
| 759 case TokenKind.GT: |
| 760 return new BoolValue(x > y, c, s); |
| 761 case TokenKind.LTE: |
| 762 return new BoolValue(x <= y, c, s); |
| 763 case TokenKind.GTE: |
| 764 return new BoolValue(x >= y, c, s); |
| 765 } |
| 645 } | 766 } |
| 646 | 767 |
| 647 // TODO(jimhug): Will flesh out ops here! | |
| 648 return super.binop(kind, other, context, node); | 768 return super.binop(kind, other, context, node); |
| 649 } | 769 } |
| 650 } | 770 } |
| 651 | 771 |
| 652 class DoubleValue extends EvaluatedValue { | 772 class DoubleValue extends EvaluatedValue { |
| 653 double actualValue; | 773 double actualValue; |
| 654 | 774 |
| 655 DoubleValue(this.actualValue, bool isConst, SourceSpan span): | 775 DoubleValue(this.actualValue, bool isConst, SourceSpan span): |
| 656 super(isConst, world.doubleType, span); | 776 super(isConst, world.doubleType, span); |
| 657 | 777 |
| 658 String get code() => '(${actualValue})'; | 778 String get code() => '(${actualValue})'; |
| 659 | 779 |
| 780 Value unop(int kind, MethodGenerator context, var node) { |
| 781 switch (kind) { |
| 782 case TokenKind.ADD: |
| 783 // This is allowed on numeric constants only |
| 784 return new DoubleValue(actualValue, isConst, span); |
| 785 case TokenKind.SUB: |
| 786 return new DoubleValue(-actualValue, isConst, span); |
| 787 } |
| 788 return super.unop(kind, context, node); |
| 789 } |
| 790 |
| 660 Value binop(int kind, var other, MethodGenerator context, var node) { | 791 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 661 // TODO(jimhug): Support int/double better | 792 final c = isConst && other.isConst; |
| 662 if (other is! DoubleValue) return super.binop(kind, other, context, node); | 793 final s = node.span; |
| 794 if (other is DoubleValue) { |
| 795 double x = actualValue; |
| 796 double y = other.actualValue; |
| 797 switch (kind) { |
| 798 case TokenKind.EQ_STRICT: |
| 799 case TokenKind.EQ: |
| 800 return new BoolValue(x == y, c, s); |
| 801 case TokenKind.NE_STRICT: |
| 802 case TokenKind.NE: |
| 803 return new BoolValue(x != y, c, s); |
| 663 | 804 |
| 664 switch (kind) { | 805 case TokenKind.ADD: |
| 665 case TokenKind.EQ_STRICT: | 806 return new DoubleValue(x + y, c, s); |
| 666 return new BoolValue(actualValue == other.actualValue, | 807 case TokenKind.SUB: |
| 667 isConst && other.isConst, node.span); | 808 return new DoubleValue(x - y, c, s); |
| 668 case TokenKind.NE_STRICT: | 809 case TokenKind.MUL: |
| 669 return new BoolValue(actualValue != other.actualValue, | 810 return new DoubleValue(x * y, c, s); |
| 670 isConst && other.isConst, node.span); | 811 case TokenKind.DIV: |
| 812 return new DoubleValue(x / y, c, s); |
| 813 case TokenKind.TRUNCDIV: |
| 814 // TODO(jimhug): I expected int, but corelib says double here... |
| 815 return new DoubleValue(x ~/ y, c, s); |
| 816 case TokenKind.MOD: |
| 817 return new DoubleValue(x % y, c, s); |
| 818 case TokenKind.LT: |
| 819 return new BoolValue(x < y, c, s); |
| 820 case TokenKind.GT: |
| 821 return new BoolValue(x > y, c, s); |
| 822 case TokenKind.LTE: |
| 823 return new BoolValue(x <= y, c, s); |
| 824 case TokenKind.GTE: |
| 825 return new BoolValue(x >= y, c, s); |
| 826 } |
| 827 } else if (other is IntValue) { |
| 828 double x = actualValue; |
| 829 int y = other.actualValue; |
| 830 switch (kind) { |
| 831 case TokenKind.EQ_STRICT: |
| 832 case TokenKind.EQ: |
| 833 return new BoolValue(x == y, c, s); |
| 834 case TokenKind.NE_STRICT: |
| 835 case TokenKind.NE: |
| 836 return new BoolValue(x != y, c, s); |
| 837 |
| 838 case TokenKind.ADD: |
| 839 return new DoubleValue(x + y, c, s); |
| 840 case TokenKind.SUB: |
| 841 return new DoubleValue(x - y, c, s); |
| 842 case TokenKind.MUL: |
| 843 return new DoubleValue(x * y, c, s); |
| 844 case TokenKind.DIV: |
| 845 return new DoubleValue(x / y, c, s); |
| 846 case TokenKind.TRUNCDIV: |
| 847 // TODO(jimhug): I expected int, but corelib says double here... |
| 848 return new DoubleValue(x ~/ y, c, s); |
| 849 case TokenKind.MOD: |
| 850 return new DoubleValue(x % y, c, s); |
| 851 case TokenKind.LT: |
| 852 return new BoolValue(x < y, c, s); |
| 853 case TokenKind.GT: |
| 854 return new BoolValue(x > y, c, s); |
| 855 case TokenKind.LTE: |
| 856 return new BoolValue(x <= y, c, s); |
| 857 case TokenKind.GTE: |
| 858 return new BoolValue(x >= y, c, s); |
| 859 } |
| 671 } | 860 } |
| 672 | 861 |
| 673 // TODO(jimhug): Will flesh out ops here! | |
| 674 return super.binop(kind, other, context, node); | 862 return super.binop(kind, other, context, node); |
| 675 } | 863 } |
| 676 } | 864 } |
| 677 | 865 |
| 678 class StringValue extends EvaluatedValue { | 866 class StringValue extends EvaluatedValue { |
| 679 String actualValue; | 867 String actualValue; |
| 680 | 868 |
| 681 StringValue(this.actualValue, bool isConst, SourceSpan span): | 869 StringValue(this.actualValue, bool isConst, SourceSpan span): |
| 682 super(isConst, world.stringType, span); | 870 super(isConst, world.stringType, span); |
| 683 | 871 |
| 684 Value binop(int kind, var other, MethodGenerator context, var node) { | 872 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 685 // TODO(jimhug): Support int/double better | |
| 686 if (other is! StringValue) return super.binop(kind, other, context, node); | 873 if (other is! StringValue) return super.binop(kind, other, context, node); |
| 687 | 874 |
| 875 final c = isConst && other.isConst; |
| 876 final s = node.span; |
| 877 String x = actualValue, y = other.actualValue; |
| 688 switch (kind) { | 878 switch (kind) { |
| 689 case TokenKind.EQ_STRICT: | 879 case TokenKind.EQ_STRICT: |
| 690 return new BoolValue(actualValue == other.actualValue, | 880 case TokenKind.EQ: |
| 691 isConst && other.isConst, node.span); | 881 return new BoolValue(x == y, c, s); |
| 692 case TokenKind.NE_STRICT: | 882 case TokenKind.NE_STRICT: |
| 693 return new BoolValue(actualValue != other.actualValue, | 883 case TokenKind.NE: |
| 694 isConst && other.isConst, node.span); | 884 return new BoolValue(x != y, c, s); |
| 885 case TokenKind.ADD: |
| 886 return new StringValue(x + y, c, s); |
| 695 } | 887 } |
| 696 | 888 |
| 697 // TODO(jimhug): Will flesh out ops here! | |
| 698 return super.binop(kind, other, context, node); | 889 return super.binop(kind, other, context, node); |
| 699 } | 890 } |
| 700 | 891 |
| 701 | 892 |
| 702 // This is expensive and we may want to cache its value if called often | 893 // This is expensive and we may want to cache its value if called often |
| 703 String get code() { | 894 String get code() { |
| 704 // TODO(jimhug): This could be much more efficient | 895 // TODO(jimhug): This could be much more efficient |
| 705 StringBuffer buf = new StringBuffer(); | 896 StringBuffer buf = new StringBuffer(); |
| 706 buf.add('"'); | 897 buf.add('"'); |
| 707 for (int i=0; i < actualValue.length; i++) { | 898 for (int i=0; i < actualValue.length; i++) { |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 820 return new BoolValue(type != other.type || code != other.code, | 1011 return new BoolValue(type != other.type || code != other.code, |
| 821 isConst && other.isConst, node.span); | 1012 isConst && other.isConst, node.span); |
| 822 } | 1013 } |
| 823 | 1014 |
| 824 return super.binop(kind, other, context, node); | 1015 return super.binop(kind, other, context, node); |
| 825 } | 1016 } |
| 826 } | 1017 } |
| 827 | 1018 |
| 828 | 1019 |
| 829 class ObjectValue extends EvaluatedValue { | 1020 class ObjectValue extends EvaluatedValue { |
| 830 Map<String, Value> fields; | 1021 Map<FieldMember, Value> fields; |
| 1022 bool seenNativeInitializer = false; |
| 1023 |
| 831 String _code; | 1024 String _code; |
| 832 | 1025 |
| 833 ObjectValue(this.fields, bool isConst, Type type, this._code, SourceSpan span)
: | 1026 ObjectValue(bool isConst, Type type, SourceSpan span): |
| 834 super(isConst, type, span); | 1027 fields = {}, super(isConst, type, span); |
| 835 | 1028 |
| 836 String get code() { | 1029 String get code() { |
| 1030 if (_code === null) validateInitialized(null); |
| 837 return _code; | 1031 return _code; |
| 838 } | 1032 } |
| 839 | 1033 |
| 1034 initFields() { |
| 1035 var allMembers = world.gen._orderValues(type.getAllMembers()); |
| 1036 for (var f in allMembers) { |
| 1037 if (f.isField && !f.isStatic && f.declaringType.isClass) { |
| 1038 fields[f] = f.computeValue(); |
| 1039 } |
| 1040 } |
| 1041 } |
| 1042 |
| 1043 setField(Member field, Value value, [bool duringInit = false]) { |
| 1044 var currentValue = fields[field]; |
| 1045 if (isConst && !value.isConst) { |
| 1046 world.error('used of non-const value in const intializer', value.span); |
| 1047 } |
| 1048 |
| 1049 if (currentValue === null) { |
| 1050 fields[field] = value; |
| 1051 if (field.isFinal && !duringInit) { |
| 1052 world.error('can not initialize final fields outside of initializer', |
| 1053 value.span); |
| 1054 } |
| 1055 } else { |
| 1056 // TODO(jimhug): Clarify spec on reinitializing fields with defaults. |
| 1057 if (field.isFinal && field.computeValue() === null) { |
| 1058 world.error('reassignment of field not allowed', value.span, |
| 1059 field.span); |
| 1060 } else { |
| 1061 fields[field] = value; //currentValue.union(value); |
| 1062 } |
| 1063 } |
| 1064 } |
| 1065 |
| 1066 validateInitialized(SourceSpan span) { |
| 1067 var buf = new StringBuffer(); |
| 1068 buf.add('Object.create('); |
| 1069 buf.add('${type.jsname}.prototype, '); |
| 1070 |
| 1071 buf.add('{'); |
| 1072 for (var field in fields.getKeys()) { |
| 1073 buf.add(field.name); |
| 1074 buf.add(': '); |
| 1075 buf.add('{"value": '); |
| 1076 if (fields[field] === null) { |
| 1077 world.error('Required field "${field.name}" was not initialized', |
| 1078 span, field.span); |
| 1079 buf.add('null'); |
| 1080 } else { |
| 1081 buf.add(fields[field].code); |
| 1082 } |
| 1083 buf.add(', writeable: false}, '); |
| 1084 } |
| 1085 buf.add('})'); |
| 1086 _code = buf.toString(); |
| 1087 } |
| 1088 |
| 840 Value binop(int kind, var other, MethodGenerator context, var node) { | 1089 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 841 if (other is! ObjectValue) return super.binop(kind, other, context, node); | 1090 if (other is! ObjectValue) return super.binop(kind, other, context, node); |
| 842 | 1091 |
| 843 switch (kind) { | 1092 switch (kind) { |
| 844 case TokenKind.EQ_STRICT: | 1093 case TokenKind.EQ_STRICT: |
| 1094 case TokenKind.EQ: |
| 845 return new BoolValue(type == other.type && code == other.code, | 1095 return new BoolValue(type == other.type && code == other.code, |
| 846 isConst && other.isConst, node.span); | 1096 isConst && other.isConst, node.span); |
| 847 case TokenKind.NE_STRICT: | 1097 case TokenKind.NE_STRICT: |
| 1098 case TokenKind.NE: |
| 848 return new BoolValue(type != other.type || code != other.code, | 1099 return new BoolValue(type != other.type || code != other.code, |
| 849 isConst && other.isConst, node.span); | 1100 isConst && other.isConst, node.span); |
| 850 } | 1101 } |
| 851 | 1102 |
| 852 return super.binop(kind, other, context, node); | 1103 return super.binop(kind, other, context, node); |
| 853 } | 1104 } |
| 854 | 1105 |
| 855 } | 1106 } |
| 856 | 1107 |
| 857 | 1108 |
| 858 /** An evaluated constant object expression. */ | |
| 859 /* | |
| 860 class ConstObjectValue extends EvaluatedValue { | |
| 861 Map<String, EvaluatedValue> fields; | |
| 862 | |
| 863 factory ConstObjectValue( | |
| 864 Type type, Map<String, EvaluatedValue> fields, | |
| 865 String canonicalCode, SourceSpan span) { | |
| 866 // compute a unique-string form used to index this value in the global const | |
| 867 // map. This is used to ensure that multiple const object values are | |
| 868 // equivalent if they have the same type name and values on each field. | |
| 869 final fieldValues = []; | |
| 870 for (var f in fields.getKeys()) { | |
| 871 fieldValues.add('$f = ${fields[f].actualValue}'); | |
| 872 } | |
| 873 fieldValues.sort((a, b) => a.compareTo(b)); | |
| 874 final actualValue = 'const ${type.jsname} [' | |
| 875 + Strings.join(fieldValues, ',') + ']'; | |
| 876 return new ConstObjectValue._internal(type, fields, actualValue, | |
| 877 canonicalCode, span, codeWithComments(canonicalCode, span)); | |
| 878 } | |
| 879 | |
| 880 ConstObjectValue._internal(type, this.fields, | |
| 881 actualValue, canonicalCode, span, code) : | |
| 882 super._internal(type, actualValue, canonicalCode, span, code); | |
| 883 } | |
| 884 */ | |
| 885 | |
| 886 /** | 1109 /** |
| 887 * A global value in the generated code, which corresponds to either a static | 1110 * A global value in the generated code, which corresponds to either a static |
| 888 * field or a memoized const expressions. | 1111 * field or a memoized const expressions. |
| 889 */ | 1112 */ |
| 890 class GlobalValue extends Value implements Comparable { | 1113 class GlobalValue extends Value implements Comparable { |
| 891 /** Static field definition (null for constant exp). */ | 1114 /** Static field definition (null for constant exp). */ |
| 892 FieldMember field; | 1115 FieldMember field; |
| 893 | 1116 |
| 894 /** | 1117 /** |
| 895 * When [this] represents a constant expression, the global variable name | 1118 * When [this] represents a constant expression, the global variable name |
| 896 * generated for it. | 1119 * generated for it. |
| 897 */ | 1120 */ |
| 898 String name; | 1121 String name; |
| 899 | 1122 |
| 900 /** The value of the field or constant expression to declare. */ | 1123 /** The value of the field or constant expression to declare. */ |
| 901 Value exp; | 1124 Value exp; |
| 902 | 1125 |
| 903 | |
| 904 /** True for either cont expressions or a final static field. */ | 1126 /** True for either cont expressions or a final static field. */ |
| 905 bool get isConst() => exp.isConst && (field == null || field.isFinal); | 1127 bool isConst; |
| 906 | 1128 |
| 907 /** The actual constant value, when [isConst] is true. */ | 1129 /** The actual constant value, when [isConst] is true. */ |
| 908 get actualValue() => exp.dynamic.actualValue; | 1130 get actualValue() => exp.dynamic.actualValue; |
| 909 | 1131 |
| 910 /** If [isConst], the [EvaluatedValue] that defines this value. */ | 1132 /** If [isConst], the [EvaluatedValue] that defines this value. */ |
| 911 EvaluatedValue get constValue() => isConst ? exp.constValue : null; | 1133 EvaluatedValue get constValue() => isConst ? exp.constValue : null; |
| 912 | 1134 |
| 913 /** Other globals that should be defined before this global. */ | 1135 /** Other globals that should be defined before this global. */ |
| 914 List<GlobalValue> dependencies; | 1136 List<GlobalValue> dependencies; |
| 915 | 1137 |
| 916 GlobalValue(Type type, String code, bool isConst, | 1138 GlobalValue(Type type, String code, bool isConst, |
| 917 this.field, this.name, this.exp, | 1139 this.field, this.name, this.exp, |
| 918 SourceSpan span, List<Value> _dependencies) | 1140 SourceSpan span, List<Value> _dependencies) |
| 919 : super(type, code, span, !isConst), dependencies = [] { | 1141 : super(type, code, span, !isConst), |
| 1142 dependencies = [], isConst = isConst { |
| 920 // store transitive-dependencies so sorting algorithm works correctly. | 1143 // store transitive-dependencies so sorting algorithm works correctly. |
| 921 for (var dep in _dependencies) { | 1144 for (var dep in _dependencies) { |
| 922 if (dep is GlobalValue) { | 1145 if (dep is GlobalValue) { |
| 923 dependencies.add(dep); | 1146 dependencies.add(dep); |
| 924 dependencies.addAll(dep.dependencies); | 1147 dependencies.addAll(dep.dependencies); |
| 925 } | 1148 } |
| 926 } | 1149 } |
| 927 } | 1150 } |
| 928 | 1151 |
| 929 int compareTo(GlobalValue other) { | 1152 int compareTo(GlobalValue other) { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 998 } | 1221 } |
| 999 | 1222 |
| 1000 _ensureCode(); | 1223 _ensureCode(); |
| 1001 return null; | 1224 return null; |
| 1002 } | 1225 } |
| 1003 } | 1226 } |
| 1004 | 1227 |
| 1005 String _escapeForComment(String text) { | 1228 String _escapeForComment(String text) { |
| 1006 return text.replaceAll('/*', '/ *').replaceAll('*/', '* /'); | 1229 return text.replaceAll('/*', '/ *').replaceAll('*/', '* /'); |
| 1007 } | 1230 } |
| OLD | NEW |