OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 interface CallingContext { |
| 7 MemberSet findMembers(String name); |
| 8 CounterLog get counters(); |
| 9 Library get library(); |
| 10 bool get isStatic(); |
| 11 MethodMember get method(); |
| 12 |
| 13 bool get needsCode(); |
| 14 bool get showWarnings(); |
| 15 |
| 16 // Hopefully remove the 5 members below that are only used for code gen. |
| 17 String _makeThisCode(); |
| 18 |
| 19 Value getTemp(Value value); |
| 20 VariableValue forceTemp(Value value); |
| 21 Value assignTemp(Value tmp, Value v); |
| 22 void freeTemp(VariableValue value); |
| 23 } |
| 24 |
| 25 // TODO(jimhug): Value needs better separation into three parts: |
| 26 // 1. Static analysis |
| 27 // 2. Type inferred abstract interpretation analysis |
| 28 // 3. Actual code generation |
| 29 /** |
| 30 * This subtype of value is the one and only version used for static |
| 31 * analysis. It has no code and its type is always the static type. |
| 32 */ |
| 33 class PureStaticValue extends Value { |
| 34 bool isConst; |
| 35 bool isType; |
| 36 |
| 37 // TODO(jimhug): Can we remove span? |
| 38 PureStaticValue(Type type, SourceSpan span, |
| 39 [this.isConst = false, this.isType = false]): |
| 40 super(type, null, span); |
| 41 |
| 42 Member getMem(CallingContext context, String name, Node node) { |
| 43 var member = type.getMember(name); |
| 44 |
| 45 if (member == null) { |
| 46 world.warning('can not find "$name" on "${type.name}"', node.span); |
| 47 } |
| 48 |
| 49 if (isType && !member.isStatic) { |
| 50 world.error('can not refer to instance member as static', node.span); |
| 51 } |
| 52 |
| 53 return member; |
| 54 } |
| 55 |
| 56 Value get_(CallingContext context, String name, Node node) { |
| 57 if (type.isVar) return new PureStaticValue(world.varType, node.span); |
| 58 var member = getMem(context, name, node); |
| 59 if (member == null) return new PureStaticValue(world.varType, node.span); |
| 60 |
| 61 return member._get(context, node, this); |
| 62 } |
| 63 |
| 64 Value set_(CallingContext context, String name, Node node, Value value, |
| 65 [int kind=0, int returnKind=ReturnKind.IGNORE]) { |
| 66 if (type.isVar) return new PureStaticValue(world.varType, node.span); |
| 67 |
| 68 var member = getMem(context, name, node); |
| 69 if (member != null) { |
| 70 member._set(context, node, this, value); |
| 71 } |
| 72 return new PureStaticValue(value.type, node.span); |
| 73 } |
| 74 |
| 75 Value setIndex(CallingContext context, Value index, Node node, Value value, |
| 76 [int kind=0, int returnKind=ReturnKind.IGNORE]) { |
| 77 return invoke(context, ':setindex', node, |
| 78 new Arguments(null, [index, value])); |
| 79 } |
| 80 |
| 81 Value unop(int kind, CallingContext context, var node) { |
| 82 switch (kind) { |
| 83 case TokenKind.NOT: |
| 84 // TODO(jimhug): Issue #359 seeks to clarify this behavior. |
| 85 // ?var newVal = convertTo(context, world.nonNullBool); |
| 86 return new PureStaticValue(world.boolType, node.span); |
| 87 case TokenKind.ADD: |
| 88 if (!isConst && !type.isNum) { |
| 89 world.error('no unary add operator in dart', node.span); |
| 90 } |
| 91 return new PureStaticValue(world.numType, node.span); |
| 92 case TokenKind.SUB: |
| 93 return invoke(context, ':negate', node, Arguments.EMPTY); |
| 94 case TokenKind.BIT_NOT: |
| 95 return invoke(context, ':bit_not', node, Arguments.EMPTY); |
| 96 } |
| 97 world.internalError('unimplemented: ${node.op}', node.span); |
| 98 } |
| 99 |
| 100 Value binop(int kind, Value other, CallingContext context, var node) { |
| 101 var isConst = isConst && other.isConst; |
| 102 |
| 103 |
| 104 switch (kind) { |
| 105 case TokenKind.AND: |
| 106 case TokenKind.OR: |
| 107 return new PureStaticValue(world.boolType, node.span, isConst); |
| 108 case TokenKind.EQ_STRICT: |
| 109 return new PureStaticValue(world.boolType, node.span, isConst); |
| 110 case TokenKind.NE_STRICT: |
| 111 return new PureStaticValue(world.boolType, node.span, isConst); |
| 112 } |
| 113 |
| 114 var name = kind == TokenKind.NE ? ':ne': TokenKind.binaryMethodName(kind); |
| 115 var ret = invoke(context, name, node, new Arguments(null, [other])); |
| 116 if (isConst) { |
| 117 ret = new PureStaticValue(ret.type, node.span, isConst); |
| 118 } |
| 119 return ret; |
| 120 } |
| 121 |
| 122 |
| 123 Value invoke(CallingContext context, String name, Node node, |
| 124 Arguments args) { |
| 125 if (type.isVar) return new PureStaticValue(world.varType, node.span); |
| 126 if (type.isFunction && name == ':call') { |
| 127 return new PureStaticValue(world.varType, node.span); |
| 128 } |
| 129 |
| 130 var member = getMem(context, name, node); |
| 131 if (member == null) return new PureStaticValue(world.varType, node.span); |
| 132 |
| 133 return member.invoke(context, node, this, args); |
| 134 } |
| 135 } |
| 136 |
| 137 |
6 /** | 138 /** |
7 * Represents a meta-value for code generation. | 139 * Represents a meta-value for code generation. |
8 */ | 140 */ |
9 class Value { | 141 class Value { |
10 /** The inferred (i.e. most precise) [Type] of the [Value]. */ | 142 /** The inferred (i.e. most precise) [Type] of the [Value]. */ |
11 final Type type; | 143 final Type type; |
12 | 144 |
13 /** The javascript code to generate this value. */ | 145 /** The javascript code to generate this value. */ |
14 final String code; | 146 final String code; |
15 | 147 |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
76 Value _tryUnion(Value right) => null; | 208 Value _tryUnion(Value right) => null; |
77 | 209 |
78 // TODO(jimhug): remove once type system works better. | 210 // TODO(jimhug): remove once type system works better. |
79 setField(Member field, Value value, [bool duringInit = false]) { } | 211 setField(Member field, Value value, [bool duringInit = false]) { } |
80 | 212 |
81 // Nothing to do in general? | 213 // Nothing to do in general? |
82 validateInitialized(SourceSpan span) { } | 214 validateInitialized(SourceSpan span) { } |
83 | 215 |
84 // TODO(jimhug): Fix these names once get/set are truly pseudo-keywords. | 216 // TODO(jimhug): Fix these names once get/set are truly pseudo-keywords. |
85 // See issue #379. | 217 // See issue #379. |
86 Value get_(MethodGenerator context, String name, Node node) { | 218 Value get_(CallingContext context, String name, Node node) { |
87 final member = _resolveMember(context, name, node); | 219 final member = _resolveMember(context, name, node); |
88 if (member != null) { | 220 if (member != null) { |
89 return member._get(context, node, this); | 221 return member._get(context, node, this); |
90 } else { | 222 } else { |
91 return invokeNoSuchMethod(context, 'get:$name', node); | 223 return invokeNoSuchMethod(context, 'get:$name', node); |
92 } | 224 } |
93 } | 225 } |
94 | 226 |
95 Value set_(MethodGenerator context, String name, Node node, Value value, | 227 Value set_(CallingContext context, String name, Node node, Value value, |
96 [bool isDynamic=false, int kind=0, int returnKind=ReturnKind.IGNORE]) { | 228 [int kind=0, int returnKind=ReturnKind.IGNORE]) { |
97 final member = _resolveMember(context, name, node, isDynamic); | 229 final member = _resolveMember(context, name, node); |
98 if (member != null) { | 230 if (member != null) { |
99 var thisValue = this; | 231 var thisValue = this; |
100 var thisTmp = null; | 232 var thisTmp = null; |
101 var retTmp = null; | 233 var retTmp = null; |
102 if (kind != 0) { | 234 if (kind != 0) { |
103 // TODO(jimhug): Very special number optimizations will go here... | 235 // TODO(jimhug): Very special number optimizations will go here... |
104 thisTmp = context.getTemp(thisValue); | 236 thisTmp = context.getTemp(thisValue); |
105 thisValue = context.assignTemp(thisTmp, thisValue); | 237 thisValue = context.assignTemp(thisTmp, thisValue); |
106 var lhs = member._get(context, node, thisTmp); | 238 var lhs = member._get(context, node, thisTmp); |
107 if (returnKind == ReturnKind.PRE) { | 239 if (returnKind == ReturnKind.PRE) { |
108 retTmp = context.forceTemp(lhs); | 240 retTmp = context.forceTemp(lhs); |
109 lhs = context.assignTemp(retTmp, lhs); | 241 lhs = context.assignTemp(retTmp, lhs); |
110 } | 242 } |
111 value = lhs.binop(kind, value, context, node); | 243 value = lhs.binop(kind, value, context, node); |
112 } | 244 } |
113 | 245 |
114 if (returnKind == ReturnKind.POST) { | 246 if (returnKind == ReturnKind.POST) { |
115 // TODO(jimhug): Optimize this away when native JS is detected. | 247 // TODO(jimhug): Optimize this away when native JS is detected. |
116 retTmp = context.forceTemp(value); | 248 retTmp = context.forceTemp(value); |
117 value = context.assignTemp(retTmp, value); | 249 value = context.assignTemp(retTmp, value); |
118 } | 250 } |
119 | 251 |
120 var ret = member._set(context, node, thisValue, value, isDynamic); | 252 var ret = member._set(context, node, thisValue, value); |
121 if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp); | 253 if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp); |
122 if (retTmp != null) { | 254 if (retTmp != null) { |
123 context.freeTemp(retTmp); | 255 context.freeTemp(retTmp); |
124 return Value.comma(ret, retTmp); | 256 return Value.comma(ret, retTmp); |
125 } else { | 257 } else { |
126 return ret; | 258 return ret; |
127 } | 259 } |
128 } else { | 260 } else { |
129 // TODO(jimhug): Need to support += and noSuchMethod better. | 261 // TODO(jimhug): Need to support += and noSuchMethod better. |
130 return invokeNoSuchMethod(context, 'set:$name', node, | 262 return invokeNoSuchMethod(context, 'set:$name', node, |
131 new Arguments(null, [value])); | 263 new Arguments(null, [value])); |
132 } | 264 } |
133 } | 265 } |
134 | 266 |
135 // TODO(jimhug): This method body has too much in common with set_ above. | 267 // TODO(jimhug): This method body has too much in common with set_ above. |
136 Value setIndex(MethodGenerator context, Value index, Node node, Value value, | 268 Value setIndex(CallingContext context, Value index, Node node, Value value, |
137 [bool isDynamic=false, int kind=0, int returnKind=ReturnKind.IGNORE]) { | 269 [int kind=0, int returnKind=ReturnKind.IGNORE]) { |
138 final member = _resolveMember(context, ':setindex', node, isDynamic); | 270 final member = _resolveMember(context, ':setindex', node); |
139 if (member != null) { | 271 if (member != null) { |
140 var thisValue = this; | 272 var thisValue = this; |
141 var indexValue = index; | 273 var indexValue = index; |
142 var thisTmp = null; | 274 var thisTmp = null; |
143 var indexTmp = null; | 275 var indexTmp = null; |
144 var retTmp = null; | 276 var retTmp = null; |
145 if (returnKind == ReturnKind.POST) { | 277 if (returnKind == ReturnKind.POST) { |
146 // TODO(jimhug): Optimize this away when native JS works. | 278 // TODO(jimhug): Optimize this away when native JS works. |
147 retTmp = context.forceTemp(value); | 279 retTmp = context.forceTemp(value); |
148 } | 280 } |
(...skipping 13 matching lines...) Expand all Loading... |
162 if (returnKind == ReturnKind.PRE) { | 294 if (returnKind == ReturnKind.PRE) { |
163 lhs = context.assignTemp(retTmp, lhs); | 295 lhs = context.assignTemp(retTmp, lhs); |
164 } | 296 } |
165 value = lhs.binop(kind, value, context, node); | 297 value = lhs.binop(kind, value, context, node); |
166 } | 298 } |
167 if (returnKind == ReturnKind.POST) { | 299 if (returnKind == ReturnKind.POST) { |
168 value = context.assignTemp(retTmp, value); | 300 value = context.assignTemp(retTmp, value); |
169 } | 301 } |
170 | 302 |
171 var ret = member.invoke(context, node, thisValue, | 303 var ret = member.invoke(context, node, thisValue, |
172 new Arguments(null, [indexValue, value]), isDynamic); | 304 new Arguments(null, [indexValue, value])); |
173 if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp); | 305 if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp); |
174 if (indexTmp != null && indexTmp != index) context.freeTemp(indexTmp); | 306 if (indexTmp != null && indexTmp != index) context.freeTemp(indexTmp); |
175 if (retTmp != null) { | 307 if (retTmp != null) { |
176 context.freeTemp(retTmp); | 308 context.freeTemp(retTmp); |
177 return Value.comma(ret, retTmp); | 309 return Value.comma(ret, retTmp); |
178 } else { | 310 } else { |
179 return ret; | 311 return ret; |
180 } | 312 } |
181 } else { | 313 } else { |
182 // TODO(jimhug): Need to support += and noSuchMethod better. | 314 // TODO(jimhug): Need to support += and noSuchMethod better. |
183 return invokeNoSuchMethod(context, ':index', node, | 315 return invokeNoSuchMethod(context, ':index', node, |
184 new Arguments(null, [index, value])); | 316 new Arguments(null, [index, value])); |
185 } | 317 } |
186 } | 318 } |
187 | 319 |
188 //Value getIndex(MethodGenerator context, Value index, var node) { | 320 //Value getIndex(CallingContext context, Value index, var node) { |
189 //} | 321 //} |
190 | 322 |
191 Value unop(int kind, MethodGenerator context, var node) { | 323 Value unop(int kind, CallingContext context, var node) { |
192 switch (kind) { | 324 switch (kind) { |
193 case TokenKind.NOT: | 325 case TokenKind.NOT: |
194 // TODO(jimhug): Issue #359 seeks to clarify this behavior. | 326 // TODO(jimhug): Issue #359 seeks to clarify this behavior. |
195 var newVal = convertTo(context, world.nonNullBool); | 327 var newVal = convertTo(context, world.nonNullBool); |
196 return new Value(newVal.type, '!${newVal.code}', node.span); | 328 return new Value(newVal.type, '!${newVal.code}', node.span); |
197 case TokenKind.ADD: | 329 case TokenKind.ADD: |
198 world.error('no unary add operator in dart', node.span); | 330 world.error('no unary add operator in dart', node.span); |
199 break; | 331 break; |
200 case TokenKind.SUB: | 332 case TokenKind.SUB: |
201 return invoke(context, ':negate', node, Arguments.EMPTY); | 333 return invoke(context, ':negate', node, Arguments.EMPTY); |
202 case TokenKind.BIT_NOT: | 334 case TokenKind.BIT_NOT: |
203 return invoke(context, ':bit_not', node, Arguments.EMPTY); | 335 return invoke(context, ':bit_not', node, Arguments.EMPTY); |
204 } | 336 } |
205 world.internalError('unimplemented: ${node.op}', node.span); | 337 world.internalError('unimplemented: ${node.op}', node.span); |
206 } | 338 } |
207 | 339 |
208 Value binop(int kind, Value other, MethodGenerator context, var node) { | 340 bool _mayOverrideEqual() { |
| 341 // TODO(jimhug): Need to check subtypes as well |
| 342 return type.isVar || type.isObject || |
| 343 !type.getMember(':eq').declaringType.isObject; |
| 344 } |
| 345 |
| 346 Value binop(int kind, Value other, CallingContext context, var node) { |
209 switch (kind) { | 347 switch (kind) { |
210 case TokenKind.AND: | 348 case TokenKind.AND: |
211 case TokenKind.OR: | 349 case TokenKind.OR: |
212 final code = '${code} ${node.op} ${other.code}'; | 350 final code = '${code} ${node.op} ${other.code}'; |
213 return new Value(world.nonNullBool, code, node.span); | 351 return new Value(world.nonNullBool, code, node.span); |
214 // TODO(jimhug): Lot's to resolve here. | 352 // TODO(jimhug): Wrong on primitives, need new fix for null == undefined |
215 case TokenKind.EQ_STRICT: | 353 case TokenKind.EQ_STRICT: |
216 return new Value(world.nonNullBool, '${code} == ${other.code}', | 354 return new Value(world.nonNullBool, '${code} == ${other.code}', |
217 node.span); | 355 node.span); |
218 case TokenKind.NE_STRICT: | 356 case TokenKind.NE_STRICT: |
219 return new Value(world.nonNullBool, '${code} != ${other.code}', | 357 return new Value(world.nonNullBool, '${code} != ${other.code}', |
220 node.span); | 358 node.span); |
| 359 |
| 360 case TokenKind.EQ: |
| 361 if (other.code == 'null') { |
| 362 if (!_mayOverrideEqual()) { |
| 363 return new Value(world.nonNullBool, '${code} == ${other.code}', |
| 364 node.span); |
| 365 } |
| 366 } else if (code == 'null') { |
| 367 return new Value(world.nonNullBool, '${code} == ${other.code}', |
| 368 node.span); |
| 369 } |
| 370 break; |
| 371 case TokenKind.NE: |
| 372 if (other.code == 'null') { |
| 373 if (!_mayOverrideEqual()) { |
| 374 return new Value(world.nonNullBool, '${code} != ${other.code}', |
| 375 node.span); |
| 376 } |
| 377 } else if (code == 'null') { |
| 378 return new Value(world.nonNullBool, '${code} != ${other.code}', |
| 379 node.span); |
| 380 } |
| 381 break; |
| 382 |
221 } | 383 } |
222 | 384 |
223 var name = kind == TokenKind.NE ? ':ne': TokenKind.binaryMethodName(kind); | 385 var name = kind == TokenKind.NE ? ':ne': TokenKind.binaryMethodName(kind); |
224 return invoke(context, name, node, new Arguments(null, [other])); | 386 return invoke(context, name, node, new Arguments(null, [other])); |
225 } | 387 } |
226 | 388 |
227 | 389 |
228 Value invoke(MethodGenerator context, String name, Node node, Arguments args, | 390 Value invoke(CallingContext context, String name, Node node, |
229 [bool isDynamic=false]) { | 391 Arguments args) { |
230 | |
231 // TODO(jmesserly): it'd be nice to remove these special cases | 392 // TODO(jmesserly): it'd be nice to remove these special cases |
232 // We could create a :call in world members, and have that handle the | 393 // We could create a :call in world members, and have that handle the |
233 // canInvoke/Invoke logic. | 394 // canInvoke/Invoke logic. |
234 | 395 |
235 // Note: this check is a little different than the one in canInvoke, because | 396 // Note: this check is a little different than the one in canInvoke, because |
236 // sometimes we need to call dynamically even if we found the :call method | 397 // sometimes we need to call dynamically even if we found the :call method |
237 // statically. | 398 // statically. |
238 | 399 |
239 if (name == ':call') { | 400 if (name == ':call') { |
240 if (isType) { | 401 if (isType) { |
241 world.error('must use "new" or "const" to construct a new instance', | 402 world.error('must use "new" or "const" to construct a new instance', |
242 node.span); | 403 node.span); |
243 } | 404 } |
244 if (type.needsVarCall(args)) { | 405 if (type.needsVarCall(args)) { |
245 return _varCall(context, node, args); | 406 return _varCall(context, node, args); |
246 } | 407 } |
247 } | 408 } |
248 | 409 |
249 var member = _resolveMember(context, name, node, isDynamic); | 410 var member = _resolveMember(context, name, node); |
250 if (member == null) { | 411 if (member == null) { |
251 return invokeNoSuchMethod(context, name, node, args); | 412 return invokeNoSuchMethod(context, name, node, args); |
252 } else { | 413 } else { |
253 return member.invoke(context, node, this, args, isDynamic); | 414 return member.invoke(context, node, this, args); |
254 } | 415 } |
255 } | 416 } |
256 | 417 |
257 bool canInvoke(MethodGenerator context, String name, Arguments args) { | |
258 if (type.isVarOrFunction && name == ':call') { | |
259 return true; | |
260 } | |
261 | |
262 var member = _resolveMember(context, name, null, isDynamic:true); | |
263 return member != null && member.canInvoke(context, args); | |
264 } | |
265 | |
266 /** | 418 /** |
267 * True if this class (or some related class that is not Object) overrides | 419 * True if this class (or some related class that is not Object) overrides |
268 * noSuchMethod. If it does we suppress warnings about unknown members. | 420 * noSuchMethod. If it does we suppress warnings about unknown members. |
269 */ | 421 */ |
270 // TODO(jmesserly): should we be doing this? | 422 // TODO(jmesserly): should we be doing this? |
271 bool _hasOverriddenNoSuchMethod() { | 423 bool _hasOverriddenNoSuchMethod() { |
272 var m = type.getMember('noSuchMethod'); | 424 var m = type.getMember('noSuchMethod'); |
273 return m != null && !m.declaringType.isObject; | 425 return m != null && !m.declaringType.isObject; |
274 } | 426 } |
275 | 427 |
276 // TODO(jimhug): Handle more precise types here, i.e. consts or closed... | 428 // TODO(jimhug): Handle more precise types here, i.e. consts or closed... |
277 bool get isPreciseType() => isSuper || isType; | 429 bool get isPreciseType() => isSuper || isType; |
278 | 430 |
279 void _missingMemberError(MethodGenerator context, String name, bool isDynamic,
Node node) { | 431 void _missingMemberError(CallingContext context, String name, Node node) { |
280 bool onStaticType = false; | 432 bool onStaticType = false; |
281 if (type != staticType) { | 433 if (type != staticType) { |
282 onStaticType = staticType.getMember(name) !== null; | 434 onStaticType = staticType.getMember(name) !== null; |
283 } | 435 } |
284 | 436 |
285 if (!onStaticType && !isDynamic && | 437 if (!onStaticType && context.showWarnings && |
286 !_isVarOrParameterType(staticType) && !_hasOverriddenNoSuchMethod()) { | 438 !_isVarOrParameterType(staticType) && !_hasOverriddenNoSuchMethod()) { |
287 // warn if the member was not found, or error if it is a static lookup. | 439 // warn if the member was not found, or error if it is a static lookup. |
288 var typeName = staticType.name; | 440 var typeName = staticType.name; |
289 if (typeName == null) typeName = staticType.library.name; | 441 if (typeName == null) typeName = staticType.library.name; |
290 var message = 'can not resolve "$name" on "${typeName}"'; | 442 var message = 'can not resolve "$name" on "${typeName}"'; |
291 if (isType) { | 443 if (isType) { |
292 world.error(message, node.span); | 444 world.error(message, node.span); |
293 } else { | 445 } else { |
294 world.warning(message, node.span); | 446 world.warning(message, node.span); |
295 } | 447 } |
296 } | 448 } |
297 } | 449 } |
298 | 450 |
299 | 451 |
300 | 452 |
301 MemberSet _tryResolveMember(MethodGenerator context, String name, bool isDynam
ic, Node node) { | 453 MemberSet _tryResolveMember(CallingContext context, String name, Node node) { |
302 var member = type.getMember(name); | 454 var member = type.getMember(name); |
303 if (member == null) { | 455 if (member == null) { |
304 _missingMemberError(context, name, isDynamic, node); | 456 _missingMemberError(context, name, node); |
305 return null; | 457 return null; |
306 } else { | 458 } else { |
307 if (isType && !member.isStatic) { | 459 if (isType && !member.isStatic && context.showWarnings) { |
308 if (!isDynamic) { | 460 world.error('can not refer to instance member as static', node.span); |
309 world.error('can not refer to instance member as static', node.span); | |
310 } | |
311 return null; | 461 return null; |
312 } | 462 } |
313 } | 463 } |
314 | 464 |
315 if (isPreciseType || member.isStatic) { | 465 if (isPreciseType || member.isStatic) { |
316 return member.preciseMemberSet; | 466 return member.preciseMemberSet; |
317 } else { | 467 } else { |
318 return member.potentialMemberSet; | 468 return member.potentialMemberSet; |
319 } | 469 } |
320 } | 470 } |
321 | 471 |
322 // TODO(jmesserly): until reified generics are fixed, treat ParameterType as | 472 // TODO(jmesserly): until reified generics are fixed, treat ParameterType as |
323 // "var". | 473 // "var". |
324 bool _isVarOrParameterType(Type t) => t.isVar || t is ParameterType; | 474 bool _isVarOrParameterType(Type t) => t.isVar || t is ParameterType; |
325 | 475 |
326 bool _shouldBindDynamically() { | 476 bool _shouldBindDynamically() { |
327 return _isVarOrParameterType(type) || options.forceDynamic && !isConst; | 477 return _isVarOrParameterType(type) || options.forceDynamic && !isConst; |
328 } | 478 } |
329 | 479 |
330 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) | 480 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) |
331 MemberSet _resolveMember(MethodGenerator context, String name, Node node, | 481 MemberSet _resolveMember(CallingContext context, String name, Node node) { |
332 [bool isDynamic=false]) { | |
333 var member = null; | 482 var member = null; |
334 if (!_shouldBindDynamically()) { | 483 if (!_shouldBindDynamically()) { |
335 member = _tryResolveMember(context, name, isDynamic, node); | 484 member = _tryResolveMember(context, name, node); |
336 } | 485 } |
337 | 486 |
338 // Fall back to a dynamic operation for instance members | 487 // Fall back to a dynamic operation for instance members |
339 if (member == null && !isSuper && !isType) { | 488 if (member == null && !isSuper && !isType) { |
340 member = context.findMembers(name); | 489 member = context.findMembers(name); |
341 if (member == null && !isDynamic) { | 490 if (member == null && context.showWarnings) { |
342 var where = 'the world'; | 491 var where = 'the world'; |
343 if (name.startsWith('_')) { | 492 if (name.startsWith('_')) { |
344 where = 'library "${context.library.name}"'; | 493 where = 'library "${context.library.name}"'; |
345 } | 494 } |
346 world.warning('$name is not defined anywhere in $where.', | 495 world.warning('$name is not defined anywhere in $where.', |
347 node.span); | 496 node.span); |
348 } | 497 } |
349 } | 498 } |
350 | 499 |
351 return member; | 500 return member; |
352 } | 501 } |
353 | 502 |
354 checkFirstClass(SourceSpan span) { | 503 checkFirstClass(SourceSpan span) { |
355 if (isType) { | 504 if (isType) { |
356 world.error('Types are not first class', span); | 505 world.error('Types are not first class', span); |
357 } | 506 } |
358 } | 507 } |
359 | 508 |
360 /** Generate a call to an unknown function type. */ | 509 /** Generate a call to an unknown function type. */ |
361 Value _varCall(MethodGenerator context, Node node, Arguments args) { | 510 Value _varCall(CallingContext context, Node node, Arguments args) { |
362 // TODO(jmesserly): calls to unknown functions will bypass type checks, | 511 // TODO(jmesserly): calls to unknown functions will bypass type checks, |
363 // which normally happen on the caller side, or in the generated stub for | 512 // which normally happen on the caller side, or in the generated stub for |
364 // dynamic method calls. What should we do? | 513 // dynamic method calls. What should we do? |
365 var stub = world.functionType.getCallStub(args); | 514 var stub = world.functionType.getCallStub(args); |
366 return stub.invoke(context, node, this, args); | 515 return stub.invoke(context, node, this, args); |
367 } | 516 } |
368 | 517 |
369 /** True if convertTo would generate a conversion. */ | 518 /** True if convertTo would generate a conversion. */ |
370 bool needsConversion(Type toType) { | 519 bool needsConversion(Type toType) { |
371 var c = convertTo(null, toType, isDynamic:true); | 520 var c = convertTo(null, toType); |
372 return c == null || code != c.code; | 521 return c == null || code != c.code; |
373 } | 522 } |
374 | 523 |
375 /** | 524 /** |
376 * Assign or convert this value to another type. | 525 * Assign or convert this value to another type. |
377 * This is used for converting between function types, inserting type | 526 * This is used for converting between function types, inserting type |
378 * checks when --enable_type_checks is enabled, and wrapping callback | 527 * checks when --enable_type_checks is enabled, and wrapping callback |
379 * functions passed to the dom so we can restore their isolate context. | 528 * functions passed to the dom so we can restore their isolate context. |
380 */ | 529 */ |
381 Value convertTo(MethodGenerator context, Type toType, | 530 Value convertTo(CallingContext context, Type toType) { |
382 [bool isDynamic=false]) { | |
383 | 531 |
384 // Issue type warnings unless we are processing a dynamic operation. | 532 // Issue type warnings unless we are processing a dynamic operation. |
385 bool checked = !isDynamic; | 533 bool checked = context != null && context.showWarnings; |
386 | 534 |
387 var callMethod = toType.getCallMethod(); | 535 var callMethod = toType.getCallMethod(); |
388 if (callMethod != null) { | 536 if (callMethod != null) { |
389 if (checked && !toType.isAssignable(type)) { | 537 if (checked && !toType.isAssignable(type)) { |
390 convertWarning(toType); | 538 convertWarning(toType); |
391 } | 539 } |
392 | 540 |
393 return _maybeWrapFunction(toType, callMethod); | 541 return _maybeWrapFunction(toType, callMethod); |
394 } | 542 } |
395 | 543 |
(...skipping 17 matching lines...) Expand all Loading... |
413 return changeStaticType(toType); | 561 return changeStaticType(toType); |
414 } | 562 } |
415 | 563 |
416 if (checked && !toType.isSubtypeOf(type)) { | 564 if (checked && !toType.isSubtypeOf(type)) { |
417 // According to the static types, this conversion can't work. | 565 // According to the static types, this conversion can't work. |
418 convertWarning(toType); | 566 convertWarning(toType); |
419 } | 567 } |
420 | 568 |
421 // Generate a runtime checks if they're turned on, otherwise skip it. | 569 // Generate a runtime checks if they're turned on, otherwise skip it. |
422 if (options.enableTypeChecks) { | 570 if (options.enableTypeChecks) { |
423 if (context == null && isDynamic) { | 571 if (context == null) { |
424 // If we're called from needsConversion, we don't need a context. | 572 // If we're called from needsConversion, we don't need a context. |
425 // Just return null so it knows a conversion is required. | 573 // Just return null so it knows a conversion is required. |
426 return null; | 574 return null; |
427 } | 575 } |
428 return _typeAssert(context, toType, isDynamic); | 576 return _typeAssert(context, toType); |
429 } else { | 577 } else { |
430 return changeStaticType(toType); | 578 return changeStaticType(toType); |
431 } | 579 } |
432 } | 580 } |
433 | 581 |
434 // TODO(jmesserly): I think we can replace our usage of the "isDynamic" flag | |
435 // by changing the static type of the target to "Dynamic" instead. | |
436 changeStaticType(Type toType) { | 582 changeStaticType(Type toType) { |
437 // Ensure that we return something with the right type for inference | 583 // Ensure that we return something with the right type for inference |
438 // purposes. | 584 // purposes. |
439 return (toType == type) ? this : new ConvertedValue(this, toType); | 585 return (toType == type) ? this : new ConvertedValue(this, toType); |
440 } | 586 } |
441 | 587 |
442 /** | 588 /** |
443 * Wraps a function with a conversion, so it can be called directly from | 589 * Wraps a function with a conversion, so it can be called directly from |
444 * Dart or JS code with the proper arity. We avoid the wrapping if the target | 590 * Dart or JS code with the proper arity. We avoid the wrapping if the target |
445 * function has the same arity. | 591 * function has the same arity. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 | 623 |
478 if (result === this) result = changeStaticType(toType); | 624 if (result === this) result = changeStaticType(toType); |
479 return result; | 625 return result; |
480 } | 626 } |
481 | 627 |
482 /** | 628 /** |
483 * Generates a run time type assertion for the given value. This works like | 629 * Generates a run time type assertion for the given value. This works like |
484 * [instanceOf], but it allows null since Dart types are nullable. | 630 * [instanceOf], but it allows null since Dart types are nullable. |
485 * Also it will throw a TypeError if it gets the wrong type. | 631 * Also it will throw a TypeError if it gets the wrong type. |
486 */ | 632 */ |
487 Value _typeAssert(MethodGenerator context, Type toType, bool isDynamic) { | 633 Value _typeAssert(CallingContext context, Type toType) { |
488 if (toType is ParameterType) { | 634 if (toType is ParameterType) { |
489 ParameterType p = toType; | 635 ParameterType p = toType; |
490 toType = p.extendsType; | 636 toType = p.extendsType; |
491 } | 637 } |
492 | 638 |
493 if (toType.isObject || toType.isVar) { | 639 if (toType.isObject || toType.isVar) { |
494 world.internalError( | 640 world.internalError( |
495 'We thought ${type.name} is not a subtype of ${toType.name}?'); | 641 'We thought ${type.name} is not a subtype of ${toType.name}?'); |
496 } | 642 } |
497 | 643 |
498 // Prevent a stack overflow when forceDynamic and type checks are both | 644 // Prevent a stack overflow when forceDynamic and type checks are both |
499 // enabled. forceDynamic would cause the TypeError constructor to type check | 645 // enabled. forceDynamic would cause the TypeError constructor to type check |
500 // its arguments, which in turn invokes the TypeError constructor, ad | 646 // its arguments, which in turn invokes the TypeError constructor, ad |
501 // infinitum. | 647 // infinitum. |
502 String throwTypeError(String paramName) => world.withoutForceDynamic(() { | 648 String throwTypeError(String paramName) => world.withoutForceDynamic(() { |
503 final typeErrorCtor = world.typeErrorType.getConstructor('_internal'); | 649 final typeErrorCtor = world.typeErrorType.getConstructor('_internal'); |
504 world.gen.corejs.ensureTypeNameOf(); | 650 world.gen.corejs.ensureTypeNameOf(); |
505 final result = typeErrorCtor.invoke(context, null, | 651 final result = typeErrorCtor.invoke(context, null, |
506 new TypeValue(world.typeErrorType, null), | 652 new TypeValue(world.typeErrorType, null), |
507 new Arguments(null, [ | 653 new Arguments(null, [ |
508 new Value(world.objectType, paramName, null), | 654 new Value(world.objectType, paramName, null), |
509 new Value(world.stringType, '"${toType.name}"', null)]), | 655 new Value(world.stringType, '"${toType.name}"', null)])); |
510 isDynamic); | |
511 world.gen.corejs.useThrow = true; | 656 world.gen.corejs.useThrow = true; |
512 return '\$throw(${result.code})'; | 657 return '\$throw(${result.code})'; |
513 }); | 658 }); |
514 | 659 |
515 // TODO(jmesserly): better assert for integers? | 660 // TODO(jmesserly): better assert for integers? |
516 if (toType.isNum) toType = world.numType; | 661 if (toType.isNum) toType = world.numType; |
517 | 662 |
518 // Generate a check like these: | 663 // Generate a check like these: |
519 // obj && obj.is$TypeName() | 664 // obj && obj.is$TypeName() |
520 // $assert_int(obj) | 665 // $assert_int(obj) |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 } | 713 } |
569 | 714 |
570 /** | 715 /** |
571 * Test to see if value is an instance of this type. | 716 * Test to see if value is an instance of this type. |
572 * | 717 * |
573 * - If a primitive type, then uses the JavaScript typeof. | 718 * - If a primitive type, then uses the JavaScript typeof. |
574 * - If it's a non-generic class, use instanceof. | 719 * - If it's a non-generic class, use instanceof. |
575 * - Otherwise add a fake member to test for. This value is generated | 720 * - Otherwise add a fake member to test for. This value is generated |
576 * as a function so that it can be called for a runtime failure. | 721 * as a function so that it can be called for a runtime failure. |
577 */ | 722 */ |
578 Value instanceOf(MethodGenerator context, Type toType, SourceSpan span, | 723 Value instanceOf(CallingContext context, Type toType, SourceSpan span, |
579 [bool isTrue=true, bool forceCheck=false]) { | 724 [bool isTrue=true, bool forceCheck=false]) { |
580 // TODO(jimhug): Optimize away tests that will always pass unless | 725 // TODO(jimhug): Optimize away tests that will always pass unless |
581 // forceCheck is true. | 726 // forceCheck is true. |
582 | 727 |
583 if (toType.isVar) { | 728 if (toType.isVar) { |
584 world.error('can not resolve type', span); | 729 world.error('can not resolve type', span); |
585 } | 730 } |
586 | 731 |
587 String testCode = null; | 732 String testCode = null; |
588 if (toType.isVar || toType.isObject || toType is ParameterType) { | 733 if (toType.isVar || toType.isObject || toType is ParameterType) { |
589 // Note: everything is an Object, including null. | 734 // Note: everything is an Object, including null. |
590 if (needsTemp) { | 735 if (needsTemp) { |
591 return new Value(world.nonNullBool, '($code, true)', span); | 736 return new Value(world.nonNullBool, '($code, true)', span); |
592 } else { | 737 } else { |
593 // TODO(jimhug): Mark non-const? | 738 // TODO(jimhug): Mark non-const? |
594 return Value.fromBool(true, span); | 739 return Value.fromBool(true, span); |
595 } | 740 } |
596 } | 741 } |
597 | 742 |
598 if (toType.library.isCore) { | 743 if (toType.library.isCore) { |
599 var typeofName = toType.typeofName; | 744 var typeofName = toType.typeofName; |
600 if (typeofName != null) { | 745 if (typeofName != null) { |
601 testCode = "(typeof($code) ${isTrue ? '==' : '!='} '$typeofName')"; | 746 testCode = "(typeof($code) ${isTrue ? '==' : '!='} '$typeofName')"; |
602 } | 747 } |
603 } | 748 } |
604 if (toType.isClass && toType is !ConcreteType | 749 |
605 && !toType.isHiddenNativeType) { | 750 if (toType.isClass |
| 751 && !toType.isHiddenNativeType && !toType.isConcreteGeneric) { |
606 toType.markUsed(); | 752 toType.markUsed(); |
607 testCode = '($code instanceof ${toType.jsname})'; | 753 testCode = '($code instanceof ${toType.jsname})'; |
608 if (!isTrue) { | 754 if (!isTrue) { |
609 testCode = '!' + testCode; | 755 testCode = '!' + testCode; |
610 } | 756 } |
611 } | 757 } |
612 if (testCode == null) { | 758 if (testCode == null) { |
613 toType.isTested = true; | 759 toType.isTested = true; |
614 | 760 |
615 // If we track nullability, we could simplify this check. | 761 // If we track nullability, we could simplify this check. |
(...skipping 21 matching lines...) Expand all Loading... |
637 } | 783 } |
638 return new Value(world.nonNullBool, testCode, span); | 784 return new Value(world.nonNullBool, testCode, span); |
639 } | 785 } |
640 | 786 |
641 void convertWarning(Type toType) { | 787 void convertWarning(Type toType) { |
642 // TODO(jmesserly): better error messages for type conversion failures | 788 // TODO(jmesserly): better error messages for type conversion failures |
643 world.warning('type "${type.name}" is not assignable to "${toType.name}"', | 789 world.warning('type "${type.name}" is not assignable to "${toType.name}"', |
644 span); | 790 span); |
645 } | 791 } |
646 | 792 |
647 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, | 793 Value invokeNoSuchMethod(CallingContext context, String name, Node node, |
648 [Arguments args]) { | 794 [Arguments args]) { |
649 if (isType) { | 795 if (isType) { |
650 world.error('member lookup failed for "$name"', node.span); | 796 world.error('member lookup failed for "$name"', node.span); |
651 } | 797 } |
652 | 798 |
653 var pos = ''; | 799 var pos = ''; |
654 if (args != null) { | 800 if (args != null) { |
655 var argsCode = []; | 801 var argsCode = []; |
656 for (int i = 0; i < args.length; i++) { | 802 for (int i = 0; i < args.length; i++) { |
657 argsCode.add(args.values[i].code); | 803 argsCode.add(args.values[i].code); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
728 | 874 |
729 | 875 |
730 class NullValue extends EvaluatedValue { | 876 class NullValue extends EvaluatedValue { |
731 NullValue(bool isConst, SourceSpan span): | 877 NullValue(bool isConst, SourceSpan span): |
732 super(isConst, world.varType, span); | 878 super(isConst, world.varType, span); |
733 | 879 |
734 get actualValue() => null; | 880 get actualValue() => null; |
735 | 881 |
736 String get code() => 'null'; | 882 String get code() => 'null'; |
737 | 883 |
738 Value binop(int kind, var other, MethodGenerator context, var node) { | 884 Value binop(int kind, var other, CallingContext context, var node) { |
739 if (other is! NullValue) return super.binop(kind, other, context, node); | 885 if (other is! NullValue) return super.binop(kind, other, context, node); |
740 | 886 |
741 final c = isConst && other.isConst; | 887 final c = isConst && other.isConst; |
742 final s = node.span; | 888 final s = node.span; |
743 switch (kind) { | 889 switch (kind) { |
744 case TokenKind.EQ_STRICT: | 890 case TokenKind.EQ_STRICT: |
745 case TokenKind.EQ: | 891 case TokenKind.EQ: |
746 return new BoolValue(true, c, s); | 892 return new BoolValue(true, c, s); |
747 case TokenKind.NE_STRICT: | 893 case TokenKind.NE_STRICT: |
748 case TokenKind.NE: | 894 case TokenKind.NE: |
749 return new BoolValue(false, c, s); | 895 return new BoolValue(false, c, s); |
750 } | 896 } |
751 | 897 |
752 return super.binop(kind, other, context, node); | 898 return super.binop(kind, other, context, node); |
753 } | 899 } |
754 } | 900 } |
755 | 901 |
756 class BoolValue extends EvaluatedValue { | 902 class BoolValue extends EvaluatedValue { |
757 final bool actualValue; | 903 final bool actualValue; |
758 | 904 |
759 BoolValue(this.actualValue, bool isConst, SourceSpan span): | 905 BoolValue(this.actualValue, bool isConst, SourceSpan span): |
760 super(isConst, world.nonNullBool, span); | 906 super(isConst, world.nonNullBool, span); |
761 | 907 |
762 String get code() => actualValue ? 'true' : 'false'; | 908 String get code() => actualValue ? 'true' : 'false'; |
763 | 909 |
764 Value unop(int kind, MethodGenerator context, var node) { | 910 Value unop(int kind, CallingContext context, var node) { |
765 switch (kind) { | 911 switch (kind) { |
766 case TokenKind.NOT: | 912 case TokenKind.NOT: |
767 return new BoolValue(!actualValue, isConst, node.span); | 913 return new BoolValue(!actualValue, isConst, node.span); |
768 } | 914 } |
769 return super.unop(kind, context, node); | 915 return super.unop(kind, context, node); |
770 } | 916 } |
771 | 917 |
772 Value binop(int kind, var other, MethodGenerator context, var node) { | 918 Value binop(int kind, var other, CallingContext context, var node) { |
773 if (other is! BoolValue) return super.binop(kind, other, context, node); | 919 if (other is! BoolValue) return super.binop(kind, other, context, node); |
774 | 920 |
775 final c = isConst && other.isConst; | 921 final c = isConst && other.isConst; |
776 final s = node.span; | 922 final s = node.span; |
777 bool x = actualValue, y = other.actualValue; | 923 bool x = actualValue, y = other.actualValue; |
778 switch (kind) { | 924 switch (kind) { |
779 case TokenKind.EQ_STRICT: | 925 case TokenKind.EQ_STRICT: |
780 case TokenKind.EQ: | 926 case TokenKind.EQ: |
781 return new BoolValue(x == y, c, s); | 927 return new BoolValue(x == y, c, s); |
782 case TokenKind.NE_STRICT: | 928 case TokenKind.NE_STRICT: |
(...skipping 11 matching lines...) Expand all Loading... |
794 | 940 |
795 class IntValue extends EvaluatedValue { | 941 class IntValue extends EvaluatedValue { |
796 final int actualValue; | 942 final int actualValue; |
797 | 943 |
798 IntValue(this.actualValue, bool isConst, SourceSpan span): | 944 IntValue(this.actualValue, bool isConst, SourceSpan span): |
799 super(isConst, world.intType, span); | 945 super(isConst, world.intType, span); |
800 | 946 |
801 // TODO(jimhug): Only add parens when needed. | 947 // TODO(jimhug): Only add parens when needed. |
802 String get code() => '(${actualValue})'; | 948 String get code() => '(${actualValue})'; |
803 | 949 |
804 Value unop(int kind, MethodGenerator context, var node) { | 950 Value unop(int kind, CallingContext context, var node) { |
805 switch (kind) { | 951 switch (kind) { |
806 case TokenKind.ADD: | 952 case TokenKind.ADD: |
807 // This is allowed on numeric constants only | 953 // This is allowed on numeric constants only |
808 return new IntValue(actualValue, isConst, span); | 954 return new IntValue(actualValue, isConst, span); |
809 case TokenKind.SUB: | 955 case TokenKind.SUB: |
810 return new IntValue(-actualValue, isConst, span); | 956 return new IntValue(-actualValue, isConst, span); |
811 case TokenKind.BIT_NOT: | 957 case TokenKind.BIT_NOT: |
812 return new IntValue(~actualValue, isConst, span); | 958 return new IntValue(~actualValue, isConst, span); |
813 } | 959 } |
814 return super.unop(kind, context, node); | 960 return super.unop(kind, context, node); |
815 } | 961 } |
816 | 962 |
817 | 963 |
818 Value binop(int kind, var other, MethodGenerator context, var node) { | 964 Value binop(int kind, var other, CallingContext context, var node) { |
819 final c = isConst && other.isConst; | 965 final c = isConst && other.isConst; |
820 final s = node.span; | 966 final s = node.span; |
821 if (other is IntValue) { | 967 if (other is IntValue) { |
822 int x = actualValue; | 968 int x = actualValue; |
823 int y = other.actualValue; | 969 int y = other.actualValue; |
824 switch (kind) { | 970 switch (kind) { |
825 case TokenKind.EQ_STRICT: | 971 case TokenKind.EQ_STRICT: |
826 case TokenKind.EQ: | 972 case TokenKind.EQ: |
827 return new BoolValue(x == y, c, s); | 973 return new BoolValue(x == y, c, s); |
828 case TokenKind.NE_STRICT: | 974 case TokenKind.NE_STRICT: |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
900 } | 1046 } |
901 | 1047 |
902 class DoubleValue extends EvaluatedValue { | 1048 class DoubleValue extends EvaluatedValue { |
903 final double actualValue; | 1049 final double actualValue; |
904 | 1050 |
905 DoubleValue(this.actualValue, bool isConst, SourceSpan span): | 1051 DoubleValue(this.actualValue, bool isConst, SourceSpan span): |
906 super(isConst, world.doubleType, span); | 1052 super(isConst, world.doubleType, span); |
907 | 1053 |
908 String get code() => '(${actualValue})'; | 1054 String get code() => '(${actualValue})'; |
909 | 1055 |
910 Value unop(int kind, MethodGenerator context, var node) { | 1056 Value unop(int kind, CallingContext context, var node) { |
911 switch (kind) { | 1057 switch (kind) { |
912 case TokenKind.ADD: | 1058 case TokenKind.ADD: |
913 // This is allowed on numeric constants only | 1059 // This is allowed on numeric constants only |
914 return new DoubleValue(actualValue, isConst, span); | 1060 return new DoubleValue(actualValue, isConst, span); |
915 case TokenKind.SUB: | 1061 case TokenKind.SUB: |
916 return new DoubleValue(-actualValue, isConst, span); | 1062 return new DoubleValue(-actualValue, isConst, span); |
917 } | 1063 } |
918 return super.unop(kind, context, node); | 1064 return super.unop(kind, context, node); |
919 } | 1065 } |
920 | 1066 |
921 Value binop(int kind, var other, MethodGenerator context, var node) { | 1067 Value binop(int kind, var other, CallingContext context, var node) { |
922 final c = isConst && other.isConst; | 1068 final c = isConst && other.isConst; |
923 final s = node.span; | 1069 final s = node.span; |
924 if (other is DoubleValue) { | 1070 if (other is DoubleValue) { |
925 double x = actualValue; | 1071 double x = actualValue; |
926 double y = other.actualValue; | 1072 double y = other.actualValue; |
927 switch (kind) { | 1073 switch (kind) { |
928 case TokenKind.EQ_STRICT: | 1074 case TokenKind.EQ_STRICT: |
929 case TokenKind.EQ: | 1075 case TokenKind.EQ: |
930 return new BoolValue(x == y, c, s); | 1076 return new BoolValue(x == y, c, s); |
931 case TokenKind.NE_STRICT: | 1077 case TokenKind.NE_STRICT: |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
992 return super.binop(kind, other, context, node); | 1138 return super.binop(kind, other, context, node); |
993 } | 1139 } |
994 } | 1140 } |
995 | 1141 |
996 class StringValue extends EvaluatedValue { | 1142 class StringValue extends EvaluatedValue { |
997 final String actualValue; | 1143 final String actualValue; |
998 | 1144 |
999 StringValue(this.actualValue, bool isConst, SourceSpan span): | 1145 StringValue(this.actualValue, bool isConst, SourceSpan span): |
1000 super(isConst, world.stringType, span); | 1146 super(isConst, world.stringType, span); |
1001 | 1147 |
1002 Value binop(int kind, var other, MethodGenerator context, var node) { | 1148 Value binop(int kind, var other, CallingContext context, var node) { |
1003 if (other is! StringValue) return super.binop(kind, other, context, node); | 1149 if (other is! StringValue) return super.binop(kind, other, context, node); |
1004 | 1150 |
1005 final c = isConst && other.isConst; | 1151 final c = isConst && other.isConst; |
1006 final s = node.span; | 1152 final s = node.span; |
1007 String x = actualValue, y = other.actualValue; | 1153 String x = actualValue, y = other.actualValue; |
1008 switch (kind) { | 1154 switch (kind) { |
1009 case TokenKind.EQ_STRICT: | 1155 case TokenKind.EQ_STRICT: |
1010 case TokenKind.EQ: | 1156 case TokenKind.EQ: |
1011 return new BoolValue(x == y, c, s); | 1157 return new BoolValue(x == y, c, s); |
1012 case TokenKind.NE_STRICT: | 1158 case TokenKind.NE_STRICT: |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1071 if (i > 0) buf.add(', '); | 1217 if (i > 0) buf.add(', '); |
1072 buf.add(values[i].code); | 1218 buf.add(values[i].code); |
1073 } | 1219 } |
1074 buf.add(']'); | 1220 buf.add(']'); |
1075 var listCode = buf.toString(); | 1221 var listCode = buf.toString(); |
1076 | 1222 |
1077 if (!isConst) return listCode; | 1223 if (!isConst) return listCode; |
1078 | 1224 |
1079 var v = new Value(world.listType, listCode, span); | 1225 var v = new Value(world.listType, listCode, span); |
1080 final immutableListCtor = world.immutableListType.getConstructor('from'); | 1226 final immutableListCtor = world.immutableListType.getConstructor('from'); |
1081 final result = immutableListCtor.invoke(null, null, | 1227 final result = immutableListCtor.invoke(world.gen.mainContext, null, |
1082 new TypeValue(v.type, span), new Arguments(null, [v])); | 1228 new TypeValue(v.type, span), new Arguments(null, [v])); |
1083 return result.code; | 1229 return result.code; |
1084 } | 1230 } |
1085 | 1231 |
1086 Value binop(int kind, var other, MethodGenerator context, var node) { | 1232 Value binop(int kind, var other, CallingContext context, var node) { |
1087 // TODO(jimhug): Support int/double better | 1233 // TODO(jimhug): Support int/double better |
1088 if (other is! ListValue) return super.binop(kind, other, context, node); | 1234 if (other is! ListValue) return super.binop(kind, other, context, node); |
1089 | 1235 |
1090 switch (kind) { | 1236 switch (kind) { |
1091 case TokenKind.EQ_STRICT: | 1237 case TokenKind.EQ_STRICT: |
1092 return new BoolValue(type == other.type && code == other.code, | 1238 return new BoolValue(type == other.type && code == other.code, |
1093 isConst && other.isConst, node.span); | 1239 isConst && other.isConst, node.span); |
1094 case TokenKind.NE_STRICT: | 1240 case TokenKind.NE_STRICT: |
1095 return new BoolValue(type != other.type || code != other.code, | 1241 return new BoolValue(type != other.type || code != other.code, |
1096 isConst && other.isConst, node.span); | 1242 isConst && other.isConst, node.span); |
(...skipping 15 matching lines...) Expand all Loading... |
1112 | 1258 |
1113 MapValue(this.values, bool isConst, Type type, SourceSpan span): | 1259 MapValue(this.values, bool isConst, Type type, SourceSpan span): |
1114 super(isConst, type, span); | 1260 super(isConst, type, span); |
1115 | 1261 |
1116 String get code() { | 1262 String get code() { |
1117 // Cache? | 1263 // Cache? |
1118 var items = new ListValue(values, false, world.listType, span); | 1264 var items = new ListValue(values, false, world.listType, span); |
1119 var tp = world.coreimpl.topType; | 1265 var tp = world.coreimpl.topType; |
1120 Member f = isConst ? tp.getMember('_constMap') : tp.getMember('_map'); | 1266 Member f = isConst ? tp.getMember('_constMap') : tp.getMember('_map'); |
1121 // TODO(jimhug): Clean up invoke signature | 1267 // TODO(jimhug): Clean up invoke signature |
1122 var value = f.invoke(null, null, new TypeValue(tp, null), | 1268 var value = f.invoke(world.gen.mainContext, null, new TypeValue(tp, null), |
1123 new Arguments(null, [items])); | 1269 new Arguments(null, [items])); |
1124 return value.code; | 1270 return value.code; |
1125 } | 1271 } |
1126 | 1272 |
1127 GlobalValue getGlobalValue() { | 1273 GlobalValue getGlobalValue() { |
1128 assert(isConst); | 1274 assert(isConst); |
1129 | 1275 |
1130 return world.gen.globalForConst(this, values); | 1276 return world.gen.globalForConst(this, values); |
1131 } | 1277 } |
1132 | 1278 |
1133 Value binop(int kind, var other, MethodGenerator context, var node) { | 1279 Value binop(int kind, var other, CallingContext context, var node) { |
1134 if (other is! MapValue) return super.binop(kind, other, context, node); | 1280 if (other is! MapValue) return super.binop(kind, other, context, node); |
1135 | 1281 |
1136 switch (kind) { | 1282 switch (kind) { |
1137 case TokenKind.EQ_STRICT: | 1283 case TokenKind.EQ_STRICT: |
1138 return new BoolValue(type == other.type && code == other.code, | 1284 return new BoolValue(type == other.type && code == other.code, |
1139 isConst && other.isConst, node.span); | 1285 isConst && other.isConst, node.span); |
1140 case TokenKind.NE_STRICT: | 1286 case TokenKind.NE_STRICT: |
1141 return new BoolValue(type != other.type || code != other.code, | 1287 return new BoolValue(type != other.type || code != other.code, |
1142 isConst && other.isConst, node.span); | 1288 isConst && other.isConst, node.span); |
1143 } | 1289 } |
(...skipping 11 matching lines...) Expand all Loading... |
1155 | 1301 |
1156 ObjectValue(bool isConst, Type type, SourceSpan span): | 1302 ObjectValue(bool isConst, Type type, SourceSpan span): |
1157 fields = {}, super(isConst, type, span); | 1303 fields = {}, super(isConst, type, span); |
1158 | 1304 |
1159 String get code() { | 1305 String get code() { |
1160 if (_code === null) validateInitialized(null); | 1306 if (_code === null) validateInitialized(null); |
1161 return _code; | 1307 return _code; |
1162 } | 1308 } |
1163 | 1309 |
1164 initFields() { | 1310 initFields() { |
1165 var allMembers = world.gen._orderValues(type.getAllMembers()); | 1311 var allMembers = world.gen._orderValues(type.genericType.getAllMembers()); |
1166 for (var f in allMembers) { | 1312 for (var f in allMembers) { |
1167 if (f.isField && !f.isStatic && f.declaringType.isClass) { | 1313 if (f.isField && !f.isStatic && f.declaringType.isClass) { |
1168 fields[f] = f.computeValue(); | 1314 fields[f] = f.computeValue(); |
1169 } | 1315 } |
1170 } | 1316 } |
1171 } | 1317 } |
1172 | 1318 |
1173 setField(Member field, Value value, [bool duringInit = false]) { | 1319 setField(Member field, Value value, [bool duringInit = false]) { |
1174 // Unpack constant values | 1320 // Unpack constant values |
1175 if (value.isConst && value is VariableValue) { | 1321 if (value.isConst && value is VariableValue) { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1213 buf.add('null'); | 1359 buf.add('null'); |
1214 } else { | 1360 } else { |
1215 buf.add(fields[field].code); | 1361 buf.add(fields[field].code); |
1216 } | 1362 } |
1217 buf.add(', writeable: false}, '); | 1363 buf.add(', writeable: false}, '); |
1218 } | 1364 } |
1219 buf.add('})'); | 1365 buf.add('})'); |
1220 _code = buf.toString(); | 1366 _code = buf.toString(); |
1221 } | 1367 } |
1222 | 1368 |
1223 Value binop(int kind, var other, MethodGenerator context, var node) { | 1369 Value binop(int kind, var other, CallingContext context, var node) { |
1224 if (other is! ObjectValue) return super.binop(kind, other, context, node); | 1370 if (other is! ObjectValue) return super.binop(kind, other, context, node); |
1225 | 1371 |
1226 switch (kind) { | 1372 switch (kind) { |
1227 case TokenKind.EQ_STRICT: | 1373 case TokenKind.EQ_STRICT: |
1228 case TokenKind.EQ: | 1374 case TokenKind.EQ: |
1229 return new BoolValue(type == other.type && code == other.code, | 1375 return new BoolValue(type == other.type && code == other.code, |
1230 isConst && other.isConst, node.span); | 1376 isConst && other.isConst, node.span); |
1231 case TokenKind.NE_STRICT: | 1377 case TokenKind.NE_STRICT: |
1232 case TokenKind.NE: | 1378 case TokenKind.NE: |
1233 return new BoolValue(type != other.type || code != other.code, | 1379 return new BoolValue(type != other.type || code != other.code, |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1311 } | 1457 } |
1312 } | 1458 } |
1313 | 1459 |
1314 /** | 1460 /** |
1315 * Represents the hidden or implicit value in a bare reference like 'a'. | 1461 * Represents the hidden or implicit value in a bare reference like 'a'. |
1316 * This could be this, the current type, or the current library for purposes | 1462 * This could be this, the current type, or the current library for purposes |
1317 * of resolving members. | 1463 * of resolving members. |
1318 */ | 1464 */ |
1319 class BareValue extends Value { | 1465 class BareValue extends Value { |
1320 final bool isType; | 1466 final bool isType; |
1321 final MethodGenerator home; | 1467 final CallingContext home; |
1322 | 1468 |
1323 String _code; | 1469 String _code; |
1324 | 1470 |
1325 BareValue(this.home, MethodGenerator outermost, SourceSpan span): | 1471 BareValue(this.home, CallingContext outermost, SourceSpan span): |
1326 isType = outermost.isStatic, | 1472 isType = outermost.isStatic, |
1327 super(outermost.method.declaringType, null, span); | 1473 super(outermost.method.declaringType, null, span); |
1328 | 1474 |
1329 bool get needsTemp() => false; | 1475 bool get needsTemp() => false; |
1330 bool _shouldBindDynamically() => false; | 1476 bool _shouldBindDynamically() => false; |
1331 | 1477 |
1332 String get code() => _code; | 1478 String get code() => _code; |
1333 | 1479 |
1334 // TODO(jimhug): Lazy initialization here is weird! | 1480 // TODO(jimhug): Lazy initialization here is weird! |
1335 void _ensureCode() { | 1481 void _ensureCode() { |
1336 if (_code === null) _code = isType ? type.jsname : home._makeThisCode(); | 1482 if (_code === null) _code = isType ? type.jsname : home._makeThisCode(); |
1337 } | 1483 } |
1338 | 1484 |
1339 MemberSet _tryResolveMember(MethodGenerator context, String name, bool isDynam
ic, Node node) { | 1485 MemberSet _tryResolveMember(CallingContext context, String name, Node node) { |
1340 assert(context == home); | 1486 assert(context == home); |
1341 | 1487 |
1342 // TODO(jimhug): Confirm this matches final resolution of issue 641. | 1488 // TODO(jimhug): Confirm this matches final resolution of issue 641. |
1343 var member = type.getMember(name); | 1489 var member = type.getMember(name); |
1344 if (member == null || member.declaringType != type) { | 1490 if (member == null || member.declaringType != type) { |
1345 var libMember = home.library.lookup(name, span); | 1491 var libMember = home.library.lookup(name, span); |
1346 if (libMember !== null) { | 1492 if (libMember !== null) { |
1347 return libMember.preciseMemberSet; | 1493 return libMember.preciseMemberSet; |
1348 } | 1494 } |
1349 } | 1495 } |
1350 | 1496 |
1351 _ensureCode(); | 1497 _ensureCode(); |
1352 return super._tryResolveMember(context, name, isDynamic, node); | 1498 return super._tryResolveMember(context, name, node); |
1353 } | 1499 } |
1354 } | 1500 } |
1355 | 1501 |
1356 /** A reference to 'super'. */ | 1502 /** A reference to 'super'. */ |
1357 // TODO(jmesserly): override resolveMember to clean up the one on Value | 1503 // TODO(jmesserly): override resolveMember to clean up the one on Value |
1358 class SuperValue extends Value { | 1504 class SuperValue extends Value { |
1359 SuperValue(Type parentType, SourceSpan span): | 1505 SuperValue(Type parentType, SourceSpan span): |
1360 super(parentType, 'this', span); | 1506 super(parentType, 'this', span); |
1361 | 1507 |
1362 bool get needsTemp() => false; | 1508 bool get needsTemp() => false; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1404 } | 1550 } |
1405 | 1551 |
1406 bool get needsTemp() => value.needsTemp; | 1552 bool get needsTemp() => value.needsTemp; |
1407 bool get isFinal() => value.isFinal; | 1553 bool get isFinal() => value.isFinal; |
1408 bool get isConst() => value.isConst; | 1554 bool get isConst() => value.isConst; |
1409 | 1555 |
1410 // TODO(jmesserly): preserve the staticType? | 1556 // TODO(jmesserly): preserve the staticType? |
1411 Value _tryUnion(Value right) => Value.union(value, right); | 1557 Value _tryUnion(Value right) => Value.union(value, right); |
1412 | 1558 |
1413 // TODO(jmessery): override get/set/invoke/unop/binop? | 1559 // TODO(jmessery): override get/set/invoke/unop/binop? |
1414 // (The tricky part is we want them to use our staticType for warning | |
1415 // purposes. It's like the whole ConcreteType/ConcreteMember issues...) | |
1416 } | 1560 } |
1417 | 1561 |
1418 /** | 1562 /** |
1419 * A value that represents a variable or parameter. The [assigned] value can be | 1563 * A value that represents a variable or parameter. The [assigned] value can be |
1420 * mutated when the variable is assigned to a new Value. | 1564 * mutated when the variable is assigned to a new Value. |
1421 */ | 1565 */ |
1422 class VariableValue extends Value { | 1566 class VariableValue extends Value { |
1423 final bool isFinal; | 1567 final bool isFinal; |
1424 final Value value; | 1568 final Value value; |
1425 | 1569 |
(...skipping 28 matching lines...) Expand all Loading... |
1454 Type get staticType() => super.type; | 1598 Type get staticType() => super.type; |
1455 bool get isConst() => value !== null ? value.isConst : false; | 1599 bool get isConst() => value !== null ? value.isConst : false; |
1456 | 1600 |
1457 // TODO(jmesserly): we could use this for checking uninitialized values | 1601 // TODO(jmesserly): we could use this for checking uninitialized values |
1458 bool get isInitialized() => value != null; | 1602 bool get isInitialized() => value != null; |
1459 | 1603 |
1460 VariableValue replaceValue(Value v) => | 1604 VariableValue replaceValue(Value v) => |
1461 new VariableValue(staticType, code, span, isFinal, v); | 1605 new VariableValue(staticType, code, span, isFinal, v); |
1462 | 1606 |
1463 // TODO(jmesserly): anything else to override? | 1607 // TODO(jmesserly): anything else to override? |
1464 Value unop(int kind, MethodGenerator context, var node) { | 1608 Value unop(int kind, CallingContext context, var node) { |
1465 if (value != null) { | 1609 if (value != null) { |
1466 return replaceValue(value.unop(kind, context, node)); | 1610 return replaceValue(value.unop(kind, context, node)); |
1467 } | 1611 } |
1468 return super.unop(kind, context, node); | 1612 return super.unop(kind, context, node); |
1469 } | 1613 } |
1470 Value binop(int kind, var other, MethodGenerator context, var node) { | 1614 Value binop(int kind, var other, CallingContext context, var node) { |
1471 if (value != null) { | 1615 if (value != null) { |
1472 return replaceValue(value.binop(kind, _unwrap(other), context, node)); | 1616 return replaceValue(value.binop(kind, _unwrap(other), context, node)); |
1473 } | 1617 } |
1474 return super.binop(kind, other, context, node); | 1618 return super.binop(kind, other, context, node); |
1475 } | 1619 } |
1476 } | 1620 } |
OLD | NEW |