| 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 |