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 |