Chromium Code Reviews| 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 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 /** Is this value a constant expression? */ | 59 /** Is this value a constant expression? */ |
| 60 bool get isConst() => false; | 60 bool get isConst() => false; |
| 61 | 61 |
| 62 /** | |
| 63 * A canonicalized form of the code. Two const expressions that result in the | |
| 64 * same instance should have the same [canonicalCode]. | |
| 65 */ | |
| 66 String get canonicalCode() => null; | |
| 67 | |
| 68 /** If [isConst], the [EvaluatedValue] that defines this value. */ | 62 /** If [isConst], the [EvaluatedValue] that defines this value. */ |
| 69 EvaluatedValue get constValue() => null; | 63 EvaluatedValue get constValue() => null; |
| 70 | 64 |
| 71 // TODO(jimhug): Fix these names once get/set are truly pseudo-keywords. | 65 // TODO(jimhug): Fix these names once get/set are truly pseudo-keywords. |
| 72 // See issue #379. | 66 // See issue #379. |
| 73 Value get_(MethodGenerator context, String name, Node node) { | 67 Value get_(MethodGenerator context, String name, Node node) { |
| 74 final member = _resolveMember(context, name, node); | 68 final member = _resolveMember(context, name, node); |
| 75 if (member != null) { | 69 if (member != null) { |
| 76 return member._get(context, node, this); | 70 return member._get(context, node, this); |
| 77 } else { | 71 } else { |
| 78 return invokeNoSuchMethod(context, 'get:$name', node); | 72 return invokeNoSuchMethod(context, 'get:$name', node); |
| 79 } | 73 } |
| 80 } | 74 } |
| 81 | 75 |
| 82 Value set_(MethodGenerator context, String name, Node node, Value value, | 76 Value set_(MethodGenerator context, String name, Node node, Value value, |
| 83 [bool isDynamic=false]) { | 77 [bool isDynamic=false]) { |
| 84 | 78 |
| 85 final member = _resolveMember(context, name, node, isDynamic); | 79 final member = _resolveMember(context, name, node, isDynamic); |
| 86 if (member != null) { | 80 if (member != null) { |
| 87 return member._set(context, node, this, value, isDynamic); | 81 return member._set(context, node, this, value, isDynamic); |
| 88 } else { | 82 } else { |
| 89 return invokeNoSuchMethod(context, 'set:$name', node, | 83 return invokeNoSuchMethod(context, 'set:$name', node, |
| 90 new Arguments(null, [value])); | 84 new Arguments(null, [value])); |
| 91 } | 85 } |
| 92 } | 86 } |
| 93 | 87 |
| 88 Value binop(int kind, Value other, MethodGenerator context, var node) { | |
| 89 switch (kind) { | |
| 90 case TokenKind.AND: | |
| 91 case TokenKind.OR: | |
| 92 final code = '${code} ${node.op} ${other.code}'; | |
| 93 return new Value(world.nonNullBool, code, node.span); | |
| 94 // TODO(jimhug): Lot's to resolve here. | |
| 95 case TokenKind.EQ_STRICT: | |
| 96 return new Value(world.nonNullBool, '${code} == ${other.code}', | |
| 97 node.span); | |
| 98 case TokenKind.NE_STRICT: | |
| 99 return new Value(world.nonNullBool, '${code} != ${other.code}', | |
| 100 node.span); | |
| 101 } | |
| 102 | |
| 103 var name = kind == TokenKind.NE ? ':ne': TokenKind.binaryMethodName(kind); | |
| 104 return invoke(context, name, node, new Arguments(null, [other])); | |
| 105 } | |
| 106 | |
| 94 | 107 |
| 95 Value invoke(MethodGenerator context, String name, Node node, Arguments args, | 108 Value invoke(MethodGenerator context, String name, Node node, Arguments args, |
| 96 [bool isDynamic=false]) { | 109 [bool isDynamic=false]) { |
| 97 | 110 |
| 98 // TODO(jmesserly): it'd be nice to remove these special cases | 111 // TODO(jmesserly): it'd be nice to remove these special cases |
| 99 // We could create a :call in world members, and have that handle the | 112 // We could create a :call in world members, and have that handle the |
| 100 // canInvoke/Invoke logic. | 113 // canInvoke/Invoke logic. |
| 101 | 114 |
| 102 // Note: this check is a little different than the one in canInvoke, because | 115 // Note: this check is a little different than the one in canInvoke, because |
| 103 // sometimes we need to call dynamically even if we found the :call method | 116 // sometimes we need to call dynamically even if we found the :call method |
| (...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 418 if (toType.isVar) { | 431 if (toType.isVar) { |
| 419 world.error('can not resolve type', span); | 432 world.error('can not resolve type', span); |
| 420 } | 433 } |
| 421 | 434 |
| 422 String testCode = null; | 435 String testCode = null; |
| 423 if (toType.isVar || toType.isObject || toType is ParameterType) { | 436 if (toType.isVar || toType.isObject || toType is ParameterType) { |
| 424 // Note: everything is an Object, including null. | 437 // Note: everything is an Object, including null. |
| 425 if (needsTemp) { | 438 if (needsTemp) { |
| 426 return new Value(world.nonNullBool, '($code, true)', span); | 439 return new Value(world.nonNullBool, '($code, true)', span); |
| 427 } else { | 440 } else { |
| 428 return new EvaluatedValue(world.nonNullBool, true, 'true', null); | 441 // TODO(jimhug): Mark non-const? |
| 442 return Value.fromBool(true, span); | |
| 429 } | 443 } |
| 430 } | 444 } |
| 431 | 445 |
| 432 if (toType.library.isCore) { | 446 if (toType.library.isCore) { |
| 433 var typeofName = toType.typeofName; | 447 var typeofName = toType.typeofName; |
| 434 if (typeofName != null) { | 448 if (typeofName != null) { |
| 435 testCode = "(typeof($code) ${isTrue ? '==' : '!='} '$typeofName')"; | 449 testCode = "(typeof($code) ${isTrue ? '==' : '!='} '$typeofName')"; |
| 436 } | 450 } |
| 437 } | 451 } |
| 438 if (toType.isClass && toType is !ConcreteType | 452 if (toType.isClass && toType is !ConcreteType |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 502 '\$map(${Strings.join(names, ", ")})')); | 516 '\$map(${Strings.join(names, ", ")})')); |
| 503 }*/ | 517 }*/ |
| 504 | 518 |
| 505 // Finally, invoke noSuchMethod | 519 // Finally, invoke noSuchMethod |
| 506 return _resolveMember(context, 'noSuchMethod', node).invoke( | 520 return _resolveMember(context, 'noSuchMethod', node).invoke( |
| 507 context, node, this, new Arguments(null, noSuchArgs)); | 521 context, node, this, new Arguments(null, noSuchArgs)); |
| 508 } | 522 } |
| 509 | 523 |
| 510 | 524 |
| 511 static Value fromBool(bool value, SourceSpan span) { | 525 static Value fromBool(bool value, SourceSpan span) { |
| 512 return new EvaluatedValue(world.nonNullBool, value, value.toString(), | 526 return new BoolValue(value, true, span); |
| 513 span); | |
| 514 } | 527 } |
| 515 | 528 |
| 516 static Value fromInt(int value, SourceSpan span) { | 529 static Value fromInt(int value, SourceSpan span) { |
| 517 final strValue = value.toString(); | 530 return new IntValue(value, true, span); |
| 518 assert(strValue.indexOf('.') == -1); | |
| 519 return new EvaluatedValue(world.numType, value, strValue, span); | |
| 520 } | 531 } |
| 521 | 532 |
| 522 static Value fromDouble(double value, SourceSpan span) { | 533 static Value fromDouble(double value, SourceSpan span) { |
| 523 var strValue = value.toString(); | 534 return new DoubleValue(value, true, span); |
| 524 // Ensure that string version looks different from int | |
| 525 if (strValue.indexOf('.') == -1 && strValue.indexOf('e') == -1) { | |
| 526 strValue = strValue + '.0'; | |
| 527 } | |
| 528 return new EvaluatedValue(world.numType, value, strValue, span); | |
| 529 } | 535 } |
| 530 | 536 |
| 531 static Value fromString(String value, SourceSpan span) { | 537 static Value fromString(String value, SourceSpan span) { |
| 538 return new StringValue(value, true, span); | |
| 539 } | |
| 540 | |
| 541 static Value fromNull(SourceSpan span) { | |
| 542 return new NullValue(true, span); | |
| 543 } | |
| 544 } | |
| 545 | |
| 546 | |
| 547 // rename to PrimitiveValue | |
| 548 class EvaluatedValue extends Value implements Hashable { | |
| 549 /** Is this value treated as const by dart language? */ | |
| 550 bool isConst; | |
| 551 | |
| 552 EvaluatedValue(this.isConst, Type type, SourceSpan span): | |
| 553 super(type, '@@@', span, false); | |
| 554 | |
| 555 String get code() => '@@@'; | |
|
Jennifer Messerly
2012/01/09 19:06:54
Maybe this should include the type name? e.g. '@@@
jimhug
2012/01/09 21:34:30
Really, this should just throw - the point is to e
| |
| 556 | |
| 557 EvaluatedValue get constValue() => this; | |
| 558 | |
| 559 // TODO(jimhug): Using computed code here with caching is major perf fear. | |
| 560 int hashCode() => code.hashCode(); | |
| 561 | |
| 562 bool operator ==(var other) { | |
| 563 return other is EvaluatedValue && other.type == this.type && | |
| 564 other.code == this.code; | |
| 565 } | |
| 566 } | |
| 567 | |
| 568 class NullValue extends EvaluatedValue { | |
| 569 NullValue(bool isConst, SourceSpan span): | |
| 570 super(isConst, world.varType, span); | |
| 571 | |
| 572 get actualValue() => null; | |
| 573 | |
| 574 String get code() => 'null'; | |
| 575 | |
| 576 Value binop(int kind, var other, MethodGenerator context, var node) { | |
| 577 // TODO(jimhug): Support int/double better | |
| 578 if (other is! ListValue) return super.binop(kind, other, context, node); | |
| 579 | |
| 580 switch (kind) { | |
| 581 case TokenKind.EQ_STRICT: | |
| 582 return new BoolValue(type == other.type && code == other.code, | |
| 583 isConst && other.isConst, node.span); | |
| 584 case TokenKind.NE_STRICT: | |
| 585 return new BoolValue(type != other.type || code != other.code, | |
| 586 isConst && other.isConst, node.span); | |
| 587 } | |
| 588 | |
| 589 // TODO(jimhug): Will flesh out ops here! | |
| 590 return super.binop(kind, other, context, node); | |
| 591 } | |
| 592 } | |
| 593 | |
| 594 class BoolValue extends EvaluatedValue { | |
| 595 bool actualValue; | |
| 596 | |
| 597 BoolValue(this.actualValue, bool isConst, SourceSpan span): | |
| 598 super(isConst, world.nonNullBool, span); | |
| 599 | |
| 600 String get code() => actualValue ? 'true' : 'false'; | |
| 601 | |
| 602 Value binop(int kind, var other, MethodGenerator context, var node) { | |
| 603 if (other is! BoolValue) return super.binop(kind, other, context, node); | |
| 604 | |
| 605 switch (kind) { | |
| 606 case TokenKind.AND: | |
| 607 return new BoolValue(actualValue && other.actualValue, | |
| 608 isConst && other.isConst, node.span); | |
| 609 case TokenKind.OR: | |
| 610 return new BoolValue(actualValue || other.actualValue, | |
| 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 } | |
| 619 | |
| 620 // TODO(jimhug): Could handle more ops here, but does it matter? | |
| 621 return super.binop(kind, other, context, node); | |
| 622 } | |
| 623 } | |
| 624 | |
| 625 class IntValue extends EvaluatedValue { | |
| 626 int actualValue; | |
| 627 | |
| 628 IntValue(this.actualValue, bool isConst, SourceSpan span): | |
| 629 super(isConst, world.intType, span); | |
| 630 | |
| 631 // TODO(jimhug): Only add parens when needed. | |
| 632 String get code() => '(${actualValue})'; | |
| 633 | |
| 634 Value binop(int kind, var other, MethodGenerator context, var node) { | |
| 635 // TODO(jimhug): Support int/double better | |
| 636 if (other is! IntValue) return super.binop(kind, other, context, node); | |
| 637 | |
| 638 switch (kind) { | |
| 639 case TokenKind.EQ_STRICT: | |
| 640 return new BoolValue(actualValue == other.actualValue, | |
| 641 isConst && other.isConst, node.span); | |
| 642 case TokenKind.NE_STRICT: | |
| 643 return new BoolValue(actualValue != other.actualValue, | |
| 644 isConst && other.isConst, node.span); | |
| 645 } | |
| 646 | |
| 647 // TODO(jimhug): Will flesh out ops here! | |
| 648 return super.binop(kind, other, context, node); | |
| 649 } | |
| 650 } | |
| 651 | |
| 652 class DoubleValue extends EvaluatedValue { | |
| 653 double actualValue; | |
| 654 | |
| 655 DoubleValue(this.actualValue, bool isConst, SourceSpan span): | |
| 656 super(isConst, world.doubleType, span); | |
| 657 | |
| 658 String get code() => '(${actualValue})'; | |
| 659 | |
| 660 Value binop(int kind, var other, MethodGenerator context, var node) { | |
| 661 // TODO(jimhug): Support int/double better | |
| 662 if (other is! DoubleValue) return super.binop(kind, other, context, node); | |
| 663 | |
| 664 switch (kind) { | |
| 665 case TokenKind.EQ_STRICT: | |
| 666 return new BoolValue(actualValue == other.actualValue, | |
| 667 isConst && other.isConst, node.span); | |
| 668 case TokenKind.NE_STRICT: | |
| 669 return new BoolValue(actualValue != other.actualValue, | |
| 670 isConst && other.isConst, node.span); | |
| 671 } | |
| 672 | |
| 673 // TODO(jimhug): Will flesh out ops here! | |
| 674 return super.binop(kind, other, context, node); | |
| 675 } | |
| 676 } | |
| 677 | |
| 678 class StringValue extends EvaluatedValue { | |
| 679 String actualValue; | |
| 680 | |
| 681 StringValue(this.actualValue, bool isConst, SourceSpan span): | |
| 682 super(isConst, world.stringType, span); | |
| 683 | |
| 684 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); | |
| 687 | |
| 688 switch (kind) { | |
| 689 case TokenKind.EQ_STRICT: | |
| 690 return new BoolValue(actualValue == other.actualValue, | |
| 691 isConst && other.isConst, node.span); | |
| 692 case TokenKind.NE_STRICT: | |
| 693 return new BoolValue(actualValue != other.actualValue, | |
| 694 isConst && other.isConst, node.span); | |
| 695 } | |
| 696 | |
| 697 // TODO(jimhug): Will flesh out ops here! | |
| 698 return super.binop(kind, other, context, node); | |
| 699 } | |
| 700 | |
| 701 | |
| 702 // This is expensive and we may want to cache its value if called often | |
| 703 String get code() { | |
| 532 // TODO(jimhug): This could be much more efficient | 704 // TODO(jimhug): This could be much more efficient |
| 533 StringBuffer buf = new StringBuffer(); | 705 StringBuffer buf = new StringBuffer(); |
| 534 buf.add('"'); | 706 buf.add('"'); |
| 535 for (int i=0; i < value.length; i++) { | 707 for (int i=0; i < actualValue.length; i++) { |
| 536 var ch = value.charCodeAt(i); | 708 var ch = actualValue.charCodeAt(i); |
| 537 switch (ch) { | 709 switch (ch) { |
| 538 case 9/*'\t'*/: buf.add(@'\t'); break; | 710 case 9/*'\t'*/: buf.add(@'\t'); break; |
| 539 case 10/*'\n'*/: buf.add(@'\n'); break; | 711 case 10/*'\n'*/: buf.add(@'\n'); break; |
| 540 case 13/*'\r'*/: buf.add(@'\r'); break; | 712 case 13/*'\r'*/: buf.add(@'\r'); break; |
| 541 case 34/*"*/: buf.add(@'\"'); break; | 713 case 34/*"*/: buf.add(@'\"'); break; |
| 542 case 92/*\*/: buf.add(@'\\'); break; | 714 case 92/*\*/: buf.add(@'\\'); break; |
| 543 default: | 715 default: |
| 544 if (ch >= 32 && ch <= 126) { | 716 if (ch >= 32 && ch <= 126) { |
| 545 buf.add(value[i]); | 717 buf.add(actualValue[i]); |
| 546 } else { | 718 } else { |
| 547 final hex = ch.toRadixString(16); | 719 final hex = ch.toRadixString(16); |
| 548 switch (hex.length) { | 720 switch (hex.length) { |
| 549 case 1: buf.add(@'\x0'); buf.add(hex); break; | 721 case 1: buf.add(@'\x0'); buf.add(hex); break; |
| 550 case 2: buf.add(@'\x'); buf.add(hex); break; | 722 case 2: buf.add(@'\x'); buf.add(hex); break; |
| 551 case 3: buf.add(@'\u0'); buf.add(hex); break; | 723 case 3: buf.add(@'\u0'); buf.add(hex); break; |
| 552 case 4: buf.add(@'\u'); buf.add(hex); break; | 724 case 4: buf.add(@'\u'); buf.add(hex); break; |
| 553 default: | 725 default: |
| 554 world.internalError( | 726 world.internalError( |
| 555 'unicode values greater than 2 bytes not implemented'); | 727 'unicode values greater than 2 bytes not implemented'); |
| 556 break; | 728 break; |
| 557 } | 729 } |
| 558 } | 730 } |
| 559 break; | 731 break; |
| 560 } | 732 } |
| 561 } | 733 } |
| 562 buf.add('"'); | 734 buf.add('"'); |
| 563 | 735 return buf.toString(); |
| 564 return new EvaluatedValue(world.stringType, value, buf.toString(), span); | |
| 565 } | |
| 566 | |
| 567 static Value fromNull(SourceSpan span) { | |
| 568 return new EvaluatedValue(world.varType, null, 'null', span); | |
| 569 } | 736 } |
| 570 } | 737 } |
| 571 | 738 |
| 572 | 739 |
| 573 // TODO(jmesserly): the subtypes of Value require a lot of type checks and | 740 class ListValue extends EvaluatedValue { |
|
Jennifer Messerly
2012/01/09 19:06:54
awesome!
| |
| 574 // downcasts to use; can we make that cleaner? (search for ".dynamic") | 741 List<Value> values; |
| 575 | 742 |
| 576 /** A value that can has been evaluated statically. */ | 743 ListValue(this.values, bool isConst, Type type, SourceSpan span): |
| 577 class EvaluatedValue extends Value { | 744 super(isConst, type, span); |
| 578 | 745 |
| 579 var actualValue; | 746 String get code() { |
| 747 final buf = new StringBuffer(); | |
| 748 buf.add('['); | |
| 749 for (var i=0; i < values.length; i++) { | |
|
Jennifer Messerly
2012/01/09 19:06:54
nit: spacing on i=0;
jimhug
2012/01/09 21:34:30
Done.
| |
| 750 if (i > 0) buf.add(', '); | |
| 751 buf.add(values[i].code); | |
| 752 } | |
| 753 buf.add(']'); | |
| 754 var listCode = buf.toString(); | |
| 580 | 755 |
| 581 bool get isConst() => true; | 756 if (!isConst) return listCode; |
| 582 | 757 |
| 583 EvaluatedValue get constValue() => this; | 758 var v = new Value(world.listType, listCode, span); |
| 584 | 759 final immutableListCtor = world.immutableListType.getConstructor('from'); |
| 585 /** | 760 final result = immutableListCtor.invoke(null, null, |
| 586 * A canonicalized form of the code. Two const expressions that result in the | 761 new Value.type(v.type, span), new Arguments(null, [v])); |
| 587 * same instance should have the same [canonicalCode]. | 762 return result.code; |
| 588 */ | |
| 589 String canonicalCode; | |
| 590 | |
| 591 factory EvaluatedValue(Type type, actualValue, String canonicalCode, | |
| 592 SourceSpan span) { | |
| 593 return new EvaluatedValue._internal(type, actualValue, | |
| 594 canonicalCode, span, codeWithComments(canonicalCode, span)); | |
| 595 } | 763 } |
| 596 | 764 |
| 597 EvaluatedValue._internal(Type type, this.actualValue, this.canonicalCode, | 765 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 598 SourceSpan span, String code) | 766 // TODO(jimhug): Support int/double better |
| 599 : super(type, code, span, false); | 767 if (other is! ListValue) return super.binop(kind, other, context, node); |
| 600 | 768 |
| 601 static String codeWithComments(String canonicalCode, SourceSpan span) { | 769 switch (kind) { |
| 602 return canonicalCode; | 770 case TokenKind.EQ_STRICT: |
| 771 return new BoolValue(type == other.type && code == other.code, | |
| 772 isConst && other.isConst, node.span); | |
| 773 case TokenKind.NE_STRICT: | |
| 774 return new BoolValue(type != other.type || code != other.code, | |
| 775 isConst && other.isConst, node.span); | |
| 776 } | |
| 777 | |
| 778 return super.binop(kind, other, context, node); | |
| 779 } | |
| 780 | |
| 781 GlobalValue getGlobalValue() { | |
| 782 assert(isConst); | |
| 783 | |
| 784 return world.gen.globalForConst(this, values); | |
| 603 } | 785 } |
| 604 } | 786 } |
| 605 | 787 |
| 606 /** An evaluated constant list expression. */ | |
| 607 class ConstListValue extends EvaluatedValue { | |
| 608 List<EvaluatedValue> values; | |
| 609 | 788 |
| 610 factory ConstListValue(Type type, List<EvaluatedValue> values, | 789 class MapValue extends EvaluatedValue { |
| 611 String actualValue, String canonicalCode, SourceSpan span) { | 790 List<Value> values; |
| 612 return new ConstListValue._internal(type, values, actualValue, | 791 |
| 613 canonicalCode, span, codeWithComments(canonicalCode, span)); | 792 MapValue(this.values, bool isConst, Type type, SourceSpan span): |
| 793 super(isConst, type, span); | |
| 794 | |
| 795 String get code() { | |
| 796 // Cache? | |
| 797 var items = new ListValue(values, false, world.listType, span); | |
| 798 var tp = world.corelib.topType; | |
| 799 Member f = isConst ? tp.getMember('_constMap') : tp.getMember('_map'); | |
| 800 // TODO(jimhug): Clean up invoke signature | |
| 801 var value = f.invoke(null, null, new Value.type(tp, null), | |
| 802 new Arguments(null, [items])); | |
| 803 return value.code; | |
| 614 } | 804 } |
| 615 | 805 |
| 616 ConstListValue._internal(type, this.values, | 806 GlobalValue getGlobalValue() { |
| 617 actualValue, canonicalCode, span, code) : | 807 assert(isConst); |
| 618 super._internal(type, actualValue, canonicalCode, span, code); | 808 |
| 809 return world.gen.globalForConst(this, values); | |
| 810 } | |
| 811 | |
| 812 Value binop(int kind, var other, MethodGenerator context, var node) { | |
| 813 if (other is! MapValue) return super.binop(kind, other, context, node); | |
| 814 | |
| 815 switch (kind) { | |
| 816 case TokenKind.EQ_STRICT: | |
| 817 return new BoolValue(type == other.type && code == other.code, | |
| 818 isConst && other.isConst, node.span); | |
| 819 case TokenKind.NE_STRICT: | |
| 820 return new BoolValue(type != other.type || code != other.code, | |
| 821 isConst && other.isConst, node.span); | |
| 822 } | |
| 823 | |
| 824 return super.binop(kind, other, context, node); | |
| 825 } | |
| 619 } | 826 } |
| 620 | 827 |
| 621 /** An evaluated constant map expression. */ | |
| 622 class ConstMapValue extends EvaluatedValue { | |
| 623 Map<String, EvaluatedValue> values; | |
| 624 | 828 |
| 625 factory ConstMapValue(Type type, List<EvaluatedValue> keyValuePairs, | 829 class ObjectValue extends EvaluatedValue { |
| 626 String actualValue, String canonicalCode, SourceSpan span) { | 830 Map<String, Value> fields; |
| 627 final values = new Map<String, EvaluatedValue>(); | 831 String _code; |
| 628 for (int i = 0; i < keyValuePairs.length; i += 2) { | 832 |
| 629 values[keyValuePairs[i].actualValue] = keyValuePairs[i + 1]; | 833 ObjectValue(this.fields, bool isConst, Type type, this._code, SourceSpan span) : |
| 630 } | 834 super(isConst, type, span); |
| 631 return new ConstMapValue._internal(type, values, actualValue, | 835 |
| 632 canonicalCode, span, codeWithComments(canonicalCode, span)); | 836 String get code() { |
| 837 return _code; | |
| 633 } | 838 } |
| 634 | 839 |
| 635 ConstMapValue._internal(type, this.values, | 840 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 636 actualValue, canonicalCode, span, code) : | 841 if (other is! ObjectValue) return super.binop(kind, other, context, node); |
| 637 super._internal(type, actualValue, canonicalCode, span, code); | 842 |
| 843 switch (kind) { | |
| 844 case TokenKind.EQ_STRICT: | |
| 845 return new BoolValue(type == other.type && code == other.code, | |
| 846 isConst && other.isConst, node.span); | |
| 847 case TokenKind.NE_STRICT: | |
| 848 return new BoolValue(type != other.type || code != other.code, | |
| 849 isConst && other.isConst, node.span); | |
| 850 } | |
| 851 | |
| 852 return super.binop(kind, other, context, node); | |
| 853 } | |
| 854 | |
| 638 } | 855 } |
| 639 | 856 |
| 857 | |
| 640 /** An evaluated constant object expression. */ | 858 /** An evaluated constant object expression. */ |
| 859 /* | |
|
Jennifer Messerly
2012/01/09 19:06:54
remove?
jimhug
2012/01/09 21:34:30
Done.
| |
| 641 class ConstObjectValue extends EvaluatedValue { | 860 class ConstObjectValue extends EvaluatedValue { |
| 642 Map<String, EvaluatedValue> fields; | 861 Map<String, EvaluatedValue> fields; |
| 643 | 862 |
| 644 factory ConstObjectValue( | 863 factory ConstObjectValue( |
| 645 Type type, Map<String, EvaluatedValue> fields, | 864 Type type, Map<String, EvaluatedValue> fields, |
| 646 String canonicalCode, SourceSpan span) { | 865 String canonicalCode, SourceSpan span) { |
| 647 // compute a unique-string form used to index this value in the global const | 866 // compute a unique-string form used to index this value in the global const |
| 648 // map. This is used to ensure that multiple const object values are | 867 // map. This is used to ensure that multiple const object values are |
| 649 // equivalent if they have the same type name and values on each field. | 868 // equivalent if they have the same type name and values on each field. |
| 650 final fieldValues = []; | 869 final fieldValues = []; |
| 651 for (var f in fields.getKeys()) { | 870 for (var f in fields.getKeys()) { |
| 652 fieldValues.add('$f = ${fields[f].actualValue}'); | 871 fieldValues.add('$f = ${fields[f].actualValue}'); |
| 653 } | 872 } |
| 654 fieldValues.sort((a, b) => a.compareTo(b)); | 873 fieldValues.sort((a, b) => a.compareTo(b)); |
| 655 final actualValue = 'const ${type.jsname} [' | 874 final actualValue = 'const ${type.jsname} [' |
| 656 + Strings.join(fieldValues, ',') + ']'; | 875 + Strings.join(fieldValues, ',') + ']'; |
| 657 return new ConstObjectValue._internal(type, fields, actualValue, | 876 return new ConstObjectValue._internal(type, fields, actualValue, |
| 658 canonicalCode, span, codeWithComments(canonicalCode, span)); | 877 canonicalCode, span, codeWithComments(canonicalCode, span)); |
| 659 } | 878 } |
| 660 | 879 |
| 661 ConstObjectValue._internal(type, this.fields, | 880 ConstObjectValue._internal(type, this.fields, |
| 662 actualValue, canonicalCode, span, code) : | 881 actualValue, canonicalCode, span, code) : |
| 663 super._internal(type, actualValue, canonicalCode, span, code); | 882 super._internal(type, actualValue, canonicalCode, span, code); |
| 664 | |
| 665 } | 883 } |
| 884 */ | |
| 666 | 885 |
| 667 /** | 886 /** |
| 668 * A global value in the generated code, which corresponds to either a static | 887 * A global value in the generated code, which corresponds to either a static |
| 669 * field or a memoized const expressions. | 888 * field or a memoized const expressions. |
| 670 */ | 889 */ |
| 671 class GlobalValue extends Value implements Comparable { | 890 class GlobalValue extends Value implements Comparable { |
| 672 /** Static field definition (null for constant exp). */ | 891 /** Static field definition (null for constant exp). */ |
| 673 FieldMember field; | 892 FieldMember field; |
| 674 | 893 |
| 675 /** | 894 /** |
| 676 * When [this] represents a constant expression, the global variable name | 895 * When [this] represents a constant expression, the global variable name |
| 677 * generated for it. | 896 * generated for it. |
| 678 */ | 897 */ |
| 679 String name; | 898 String name; |
| 680 | 899 |
| 681 /** The value of the field or constant expression to declare. */ | 900 /** The value of the field or constant expression to declare. */ |
| 682 Value exp; | 901 Value exp; |
| 683 | 902 |
| 684 /** | |
| 685 * A canonicalized form of the code. Two const expressions that result in the | |
| 686 * same instance should have the same [canonicalCode]. | |
| 687 */ | |
| 688 String canonicalCode; | |
| 689 | 903 |
| 690 /** True for either cont expressions or a final static field. */ | 904 /** True for either cont expressions or a final static field. */ |
| 691 bool get isConst() => exp.isConst && (field == null || field.isFinal); | 905 bool get isConst() => exp.isConst && (field == null || field.isFinal); |
| 692 | 906 |
| 693 /** The actual constant value, when [isConst] is true. */ | 907 /** The actual constant value, when [isConst] is true. */ |
| 694 get actualValue() => exp.dynamic.actualValue; | 908 get actualValue() => exp.dynamic.actualValue; |
| 695 | 909 |
| 696 /** If [isConst], the [EvaluatedValue] that defines this value. */ | 910 /** If [isConst], the [EvaluatedValue] that defines this value. */ |
| 697 EvaluatedValue get constValue() => isConst ? exp.constValue : null; | 911 EvaluatedValue get constValue() => isConst ? exp.constValue : null; |
| 698 | 912 |
| 699 /** Other globals that should be defined before this global. */ | 913 /** Other globals that should be defined before this global. */ |
| 700 List<GlobalValue> dependencies; | 914 List<GlobalValue> dependencies; |
| 701 | 915 |
| 702 factory GlobalValue.fromStatic(field, Value exp, dependencies) { | |
| 703 var code = (exp.isConst ? exp.canonicalCode : exp.code); | |
| 704 var codeWithComment = '$code/*${field.declaringType.name}.${field.name}*/'; | |
| 705 return new GlobalValue( | |
| 706 exp.type, codeWithComment, field.isFinal, field, null, exp, | |
| 707 code, exp.span, dependencies.filter((d) => d is GlobalValue)); | |
| 708 } | |
| 709 | |
| 710 factory GlobalValue.fromConst(uniqueId, Value exp, dependencies) { | |
| 711 var name = "const\$$uniqueId"; | |
| 712 var codeWithComment = "$name/*${_escapeForComment(exp.span.text)}*/"; | |
| 713 return new GlobalValue( | |
| 714 exp.type, codeWithComment, true, null, name, exp, name, | |
| 715 exp.span, | |
| 716 dependencies.filter((d) => d is GlobalValue)); | |
| 717 } | |
| 718 | |
| 719 GlobalValue(Type type, String code, bool isConst, | 916 GlobalValue(Type type, String code, bool isConst, |
| 720 this.field, this.name, this.exp, this.canonicalCode, | 917 this.field, this.name, this.exp, |
| 721 SourceSpan span, List<GlobalValue> _dependencies) | 918 SourceSpan span, List<Value> _dependencies) |
| 722 : super(type, code, span, !isConst), dependencies = [] { | 919 : super(type, code, span, !isConst), dependencies = [] { |
| 723 // store transitive-dependencies so sorting algorithm works correctly. | 920 // store transitive-dependencies so sorting algorithm works correctly. |
| 724 for (final dep in _dependencies) { | 921 for (var dep in _dependencies) { |
| 725 dependencies.add(dep); | 922 if (dep is GlobalValue) { |
| 726 dependencies.addAll(dep.dependencies); | 923 dependencies.add(dep); |
| 924 dependencies.addAll(dep.dependencies); | |
| 925 } | |
| 727 } | 926 } |
| 728 } | 927 } |
| 729 | 928 |
| 730 int compareTo(GlobalValue other) { | 929 int compareTo(GlobalValue other) { |
| 731 // order by dependencies, o.w. by name | 930 // order by dependencies, o.w. by name |
| 732 if (other == this) { | 931 if (other == this) { |
| 733 return 0; | 932 return 0; |
| 734 } else if (dependencies.indexOf(other) >= 0) { | 933 } else if (dependencies.indexOf(other) >= 0) { |
| 735 return 1; | 934 return 1; |
| 736 } else if (other.dependencies.indexOf(this) >= 0) { | 935 } else if (other.dependencies.indexOf(this) >= 0) { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 799 } | 998 } |
| 800 | 999 |
| 801 _ensureCode(); | 1000 _ensureCode(); |
| 802 return null; | 1001 return null; |
| 803 } | 1002 } |
| 804 } | 1003 } |
| 805 | 1004 |
| 806 String _escapeForComment(String text) { | 1005 String _escapeForComment(String text) { |
| 807 return text.replaceAll('/*', '/ *').replaceAll('*/', '* /'); | 1006 return text.replaceAll('/*', '/ *').replaceAll('*/', '* /'); |
| 808 } | 1007 } |
| OLD | NEW |