| 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 /** The [Type] of the [Value]. */ | 9 /** The [Type] of the [Value]. */ |
| 10 Type type; | 10 Type type; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 /** If we reference this value multiple times, do we need a temp? */ | 21 /** If we reference this value multiple times, do we need a temp? */ |
| 22 bool needsTemp; | 22 bool needsTemp; |
| 23 | 23 |
| 24 Value(this.type, this.code, | 24 Value(this.type, this.code, |
| 25 // TODO(sigmund): reorder, so that needsTemp comes first. | 25 // TODO(sigmund): reorder, so that needsTemp comes first. |
| 26 [this.isSuper = false, this.needsTemp = true, this.isType = false]) { | 26 [this.isSuper = false, this.needsTemp = true, this.isType = false]) { |
| 27 if (type == null) type = world.varType; | 27 if (type == null) type = world.varType; |
| 28 } | 28 } |
| 29 | 29 |
| 30 /** Is this value a constant expression? */ | 30 /** Is this value a constant expression? */ |
| 31 bool get isConst() => false; | 31 bool get isConst () => false; |
| 32 | 32 |
| 33 // TODO(jimhug): These three methods are still a little too similar for me. | 33 // TODO(jimhug): These three methods are still a little too similar for me. |
| 34 get_(MethodGenerator context, String name, Node node) { | 34 get_(MethodGenerator context, String name, Node node) { |
| 35 var member = _resolveMember(context, name, node); | 35 var member = _resolveMember(context, name, node); |
| 36 if (member != null) { | 36 if (member != null) { |
| 37 member = member.get_(context, node, this); | 37 member = member.get_(context, node, this); |
| 38 } | 38 } |
| 39 // member.get_ returns null if no signatures match the given node. | 39 // member.get_ returns null if no signatures match the given node. |
| 40 if (member != null) { | 40 if (member != null) { |
| 41 return member; | 41 return member; |
| 42 } else { | 42 } else { |
| 43 return invokeNoSuchMethod(context, 'get:$name', node); | 43 return invokeNoSuchMethod(context, 'get:$name', node); |
| 44 } | 44 } |
| 45 } | 45 } |
| 46 | 46 |
| 47 set_(MethodGenerator context, String name, Node node, Value value, | 47 set_(MethodGenerator context, String name, Node node, Value value, |
| 48 [bool isDynamic=false]) { | 48 [bool isDynamic=false]) { |
| 49 var member = _resolveMember(context, name, node, isDynamic); | 49 var member = _resolveMember(context, name, node); |
| 50 if (member != null) { | 50 if (member != null) { |
| 51 member = member.set_(context, node, this, value, isDynamic); | 51 member = member.set_(context, node, this, value, isDynamic); |
| 52 } | 52 } |
| 53 // member.set_ returns null if no signatures match the given node. | 53 // member.set_ returns null if no signatures match the given node. |
| 54 if (member != null) { | 54 if (member != null) { |
| 55 return member; | 55 return member; |
| 56 } else { | 56 } else { |
| 57 return invokeNoSuchMethod(context, 'set:$name', node, | 57 return invokeNoSuchMethod(context, 'set:$name', node, |
| 58 new Arguments(null, [value])); | 58 new Arguments(null, [value])); |
| 59 } | 59 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 81 if (name == '\$call') { | 81 if (name == '\$call') { |
| 82 if (isType) { | 82 if (isType) { |
| 83 world.error('must use "new" or "const" to construct a new instance', | 83 world.error('must use "new" or "const" to construct a new instance', |
| 84 node.span); | 84 node.span); |
| 85 } | 85 } |
| 86 if (type.needsVarCall(args)) { | 86 if (type.needsVarCall(args)) { |
| 87 return _varCall(context, args); | 87 return _varCall(context, args); |
| 88 } | 88 } |
| 89 } | 89 } |
| 90 | 90 |
| 91 var member = _resolveMember(context, name, node, isDynamic); | 91 var member = _resolveMember(context, name, node); |
| 92 if (member == null) { | 92 if (member == null) { |
| 93 return invokeNoSuchMethod(context, name, node, args); | 93 return invokeNoSuchMethod(context, name, node, args); |
| 94 } else { | 94 } else { |
| 95 return member.invoke(context, node, this, args, isDynamic); | 95 return member.invoke(context, node, this, args, isDynamic); |
| 96 } | 96 } |
| 97 } | 97 } |
| 98 | 98 |
| 99 bool canInvoke(MethodGenerator context, String name, Arguments args) { | 99 bool canInvoke(MethodGenerator context, String name, Arguments args) { |
| 100 // TODO(jimhug): The != method is weird - understand it better. | 100 // TODO(jimhug): The != method is weird - understand it better. |
| 101 if (type.isVar && name == '\$ne') { | 101 if (type.isVar && name == '\$ne') { |
| 102 return true; | 102 return true; |
| 103 } | 103 } |
| 104 | 104 |
| 105 if (type.isVarOrFunction && name == '\$call') { | 105 if (type.isVarOrFunction && name == '\$call') { |
| 106 return true; | 106 return true; |
| 107 } | 107 } |
| 108 | 108 |
| 109 var member = _resolveMember(context, name, null, isDynamic:true); | 109 var member = _tryResolveMember(context, name); |
| 110 return member != null && member.canInvoke(context, args); | 110 return member != null && member.canInvoke(context, args); |
| 111 } | 111 } |
| 112 | 112 |
| 113 /** | 113 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) |
| 114 * True if this class (or some related class that is not Object) overrides | 114 _tryResolveMember(MethodGenerator context, String name) { |
| 115 * noSuchMethod. If it does we suppress warnings about unknown members. | 115 var member = null; |
| 116 */ | 116 if (!type.isVar) { |
| 117 // TODO(jmesserly): should we be doing this? | 117 if (isSuper) { |
| 118 bool _hasOverriddenNoSuchMethod() { | 118 return type.getMember(name); |
| 119 if (isSuper) { | 119 } else { |
| 120 var m = type.getMember('noSuchMethod'); | 120 member = type.resolveMember(name); |
| 121 return m != null && !m.declaringType.isObject; | 121 } |
| 122 } |
| 123 |
| 124 if (member == null) { |
| 125 // TODO(jmesserly): shouldn't look in world except for "var" |
| 126 member = context.findMembers(name); |
| 127 } |
| 128 return member; |
| 129 } |
| 130 |
| 131 _resolveMember(MethodGenerator context, String name, Node node) { |
| 132 var member = _tryResolveMember(context, name); |
| 133 if (member != null) { |
| 134 if (isType && !member.isStatic) { |
| 135 world.error('can not refer to instance member as static', node.span); |
| 136 } |
| 137 return member; |
| 122 } else { | 138 } else { |
| 123 return type.resolveMember('noSuchMethod').members.length > 1; | 139 // TODO(jmesserly): we suppress warnings if someone has overridden |
| 140 // noSuchMethod, and we know it will call their version. Is that right? |
| 141 if (_tryResolveMember(context, 'noSuchMethod').members.length > 1) { |
| 142 return null; |
| 143 } |
| 144 |
| 145 var typeName = type.name == null ? type.library.name : type.name; |
| 146 var message = 'can not resolve "$name" on "${typeName}"'; |
| 147 if (isType) { |
| 148 world.error(message, node.span); |
| 149 } else { |
| 150 world.warning(message, node.span); |
| 151 } |
| 152 // TODO(jmesserly): isn't this condition always true if we got here? |
| 153 if (context.findMembers(name) == null) { |
| 154 world.warning('$name is not defined anywhere in the world.', node.span); |
| 155 } |
| 156 return null; |
| 124 } | 157 } |
| 125 } | 158 } |
| 126 | 159 |
| 127 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) | |
| 128 _resolveMember(MethodGenerator context, String name, Node node, | |
| 129 [bool isDynamic=false]) { | |
| 130 | |
| 131 // TODO(jmesserly): until reified generic lists are fixed, treat | |
| 132 // ParameterType as "var". | |
| 133 var member; | |
| 134 if (!type.isVar && type is! ParameterType) { | |
| 135 if (isSuper) { | |
| 136 member = type.getMember(name); | |
| 137 } else { | |
| 138 member = type.resolveMember(name); | |
| 139 } | |
| 140 | |
| 141 if (member != null && isType && !member.isStatic) { | |
| 142 if (!isDynamic) { | |
| 143 world.error('can not refer to instance member as static', node.span); | |
| 144 } | |
| 145 return null; | |
| 146 } | |
| 147 | |
| 148 if (member == null && !isDynamic && !_hasOverriddenNoSuchMethod()) { | |
| 149 var typeName = type.name == null ? type.library.name : type.name; | |
| 150 var message = 'can not resolve "$name" on "${typeName}"'; | |
| 151 if (isType) { | |
| 152 world.error(message, node.span); | |
| 153 } else { | |
| 154 world.warning(message, node.span); | |
| 155 } | |
| 156 } | |
| 157 } | |
| 158 | |
| 159 // Fall back to a dynamic operation for instance members | |
| 160 if (member == null && !isSuper && !isType) { | |
| 161 member = context.findMembers(name); | |
| 162 if (member == null && !isDynamic) { | |
| 163 world.warning('$name is not defined anywhere in the world.', | |
| 164 node.span); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 return member; | |
| 169 } | |
| 170 | |
| 171 checkFirstClass(SourceSpan span) { | 160 checkFirstClass(SourceSpan span) { |
| 172 if (isType) { | 161 if (isType) { |
| 173 world.error('Types are not first class', span); | 162 world.error('Types are not first class', span); |
| 174 } | 163 } |
| 175 } | 164 } |
| 176 | 165 |
| 177 /** Generate a call to an unknown function type. */ | 166 /** Generate a call to an unknown function type. */ |
| 178 Value _varCall(MethodGenerator context, Arguments args) { | 167 Value _varCall(MethodGenerator context, Arguments args) { |
| 179 // TODO(jmesserly): calls to unknown functions will bypass type checks, | 168 // TODO(jmesserly): calls to unknown functions will bypass type checks, |
| 180 // which normally happen on the caller side, or in the generated stub for | 169 // which normally happen on the caller side, or in the generated stub for |
| (...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 428 /*if (args != null && args.hasNames) { | 417 /*if (args != null && args.hasNames) { |
| 429 var names = []; | 418 var names = []; |
| 430 for (int i = args.bareCount; i < args.length; i++) { | 419 for (int i = args.bareCount; i < args.length; i++) { |
| 431 names.add('"${args.getName(i)}", ${args.values[i].code}'); | 420 names.add('"${args.getName(i)}", ${args.values[i].code}'); |
| 432 } | 421 } |
| 433 noSuchArgs.add(new Value(world.gen.useMapFactory(), | 422 noSuchArgs.add(new Value(world.gen.useMapFactory(), |
| 434 '\$map(${Strings.join(names, ", ")})')); | 423 '\$map(${Strings.join(names, ", ")})')); |
| 435 }*/ | 424 }*/ |
| 436 | 425 |
| 437 // Finally, invoke noSuchMethod | 426 // Finally, invoke noSuchMethod |
| 438 return _resolveMember(context, 'noSuchMethod', node).invoke( | 427 return _tryResolveMember(context, 'noSuchMethod').invoke( |
| 439 context, node, this, new Arguments(null, noSuchArgs)); | 428 context, node, this, new Arguments(null, noSuchArgs)); |
| 440 } | 429 } |
| 441 | 430 |
| 442 Value invokeSpecial(String name, Arguments args, Type returnType) { | 431 Value invokeSpecial(String name, Arguments args, Type returnType) { |
| 443 assert(name.startsWith('\$')); | 432 assert(name.startsWith('\$')); |
| 444 assert(!args.hasNames); | 433 assert(!args.hasNames); |
| 445 // TODO(jimhug): We need to do this a little bit more like get and set on | 434 // TODO(jimhug): We need to do this a little bit more like get and set on |
| 446 // properties. We should check the set of members for something | 435 // properties. We should check the set of members for something |
| 447 // like "requiresNativeIndexer" and "requiresDartIndexer" to | 436 // like "requiresNativeIndexer" and "requiresDartIndexer" to |
| 448 // decide on a strategy. | 437 // decide on a strategy. |
| 449 | 438 |
| 450 var argsString = args.getCode(); | 439 var argsString = args.getCode(); |
| 451 // Most operator calls need to be emitted as function calls, so we don't | 440 // Most operator calls need to be emitted as function calls, so we don't |
| 452 // box numbers accidentally. Indexing is the exception. | 441 // box numbers accidentally. Indexing is the exception. |
| 453 if (name == '\$index' || name == '\$setindex') { | 442 if (name == '\$index' || name == '\$setindex') { |
| 454 return new Value(returnType, '$code.$name($argsString)'); | 443 return new Value(returnType, '$code.$name($argsString)'); |
| 455 } else { | 444 } else { |
| 456 if (argsString.length > 0) argsString = ', $argsString'; | 445 if (argsString.length > 0) argsString = ', $argsString'; |
| 457 world.gen.corejs.useOperator(name); | 446 world.gen.corejs.useOperator(name); |
| 458 return new Value(returnType, '$name($code$argsString)'); | 447 return new Value(returnType, '$name($code$argsString)'); |
| 459 } | 448 } |
| 460 } | 449 } |
| 461 } | 450 } |
| 462 | 451 |
| 463 // TODO(jmesserly): the subtypes of Value require a lot of type checks and | |
| 464 // downcasts to use; can we make that cleaner? (search for ".dynamic") | |
| 465 | |
| 466 /** A value that can has been evaluated statically. */ | 452 /** A value that can has been evaluated statically. */ |
| 467 class EvaluatedValue extends Value { | 453 class EvaluatedValue extends Value { |
| 468 | 454 |
| 469 var actualValue; | 455 var actualValue; |
| 470 | 456 |
| 471 bool get isConst() => true; | 457 bool get isConst() => true; |
| 472 | 458 |
| 473 /** | 459 /** |
| 474 * A canonicalized form of the code. Two const expressions that result in the | 460 * A canonicalized form of the code. Two const expressions that result in the |
| 475 * same instance should have the same [canonicalCode]. | 461 * same instance should have the same [canonicalCode]. |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 628 return 1; | 614 return 1; |
| 629 } else if (name != null && other.name == null) { | 615 } else if (name != null && other.name == null) { |
| 630 return -1; | 616 return -1; |
| 631 } else if (name != null) { | 617 } else if (name != null) { |
| 632 return name.compareTo(other.name); | 618 return name.compareTo(other.name); |
| 633 } else { | 619 } else { |
| 634 return field.name.compareTo(other.field.name); | 620 return field.name.compareTo(other.field.name); |
| 635 } | 621 } |
| 636 } | 622 } |
| 637 } | 623 } |
| OLD | NEW |