| 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 /** | 6 /** |
| 7 * Represents a meta-value for code generation. | 7 * Represents a meta-value for code generation. |
| 8 */ | 8 */ |
| 9 class Value { | 9 class Value { |
| 10 /** The inferred (i.e. most precise) [Type] of the [Value]. */ | 10 /** The inferred (i.e. most precise) [Type] of the [Value]. */ |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 * The statically declared [Type] of the [Value]. This type determines which | 40 * The statically declared [Type] of the [Value]. This type determines which |
| 41 * kind of static type warnings are issued. It's also the type that is used | 41 * kind of static type warnings are issued. It's also the type that is used |
| 42 * for generating type assertions (i.e. given `Foo x; ...; x = expr;`, | 42 * for generating type assertions (i.e. given `Foo x; ...; x = expr;`, |
| 43 * expr will be checked against "Foo" regardless of the inferred type of `x`). | 43 * expr will be checked against "Foo" regardless of the inferred type of `x`). |
| 44 */ | 44 */ |
| 45 Type get staticType() => type; | 45 Type get staticType() => type; |
| 46 | 46 |
| 47 /** If [isConst], the [EvaluatedValue] that defines this value. */ | 47 /** If [isConst], the [EvaluatedValue] that defines this value. */ |
| 48 EvaluatedValue get constValue() => null; | 48 EvaluatedValue get constValue() => null; |
| 49 | 49 |
| 50 static Value comma(Value x, Value y) { |
| 51 return new Value(y.type, '(${x.code}, ${y.code})', null); |
| 52 } |
| 53 |
| 50 // TODO(jmesserly): more work is needed to make unifying all kinds of Values | 54 // TODO(jmesserly): more work is needed to make unifying all kinds of Values |
| 51 // work properly. | 55 // work properly. |
| 52 static Value union(Value x, Value y) { | 56 static Value union(Value x, Value y) { |
| 53 if (y === null || x == y) return x; | 57 if (y === null || x == y) return x; |
| 54 if (x === null) return y; | 58 if (x === null) return y; |
| 55 | 59 |
| 56 var ret = x._tryUnion(y); | 60 var ret = x._tryUnion(y); |
| 57 if (ret != null) return ret; | 61 if (ret != null) return ret; |
| 58 | 62 |
| 59 // TODO(jmesserly): might want to call a _tryUnionReversed here. | 63 // TODO(jmesserly): might want to call a _tryUnionReversed here. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 82 Value get_(MethodGenerator context, String name, Node node) { | 86 Value get_(MethodGenerator context, String name, Node node) { |
| 83 final member = _resolveMember(context, name, node); | 87 final member = _resolveMember(context, name, node); |
| 84 if (member != null) { | 88 if (member != null) { |
| 85 return member._get(context, node, this); | 89 return member._get(context, node, this); |
| 86 } else { | 90 } else { |
| 87 return invokeNoSuchMethod(context, 'get:$name', node); | 91 return invokeNoSuchMethod(context, 'get:$name', node); |
| 88 } | 92 } |
| 89 } | 93 } |
| 90 | 94 |
| 91 Value set_(MethodGenerator context, String name, Node node, Value value, | 95 Value set_(MethodGenerator context, String name, Node node, Value value, |
| 92 [bool isDynamic=false]) { | 96 [bool isDynamic=false, int kind=0, int returnKind=ReturnKind.IGNORE]) { |
| 93 | |
| 94 final member = _resolveMember(context, name, node, isDynamic); | 97 final member = _resolveMember(context, name, node, isDynamic); |
| 95 if (member != null) { | 98 if (member != null) { |
| 96 return member._set(context, node, this, value, isDynamic); | 99 var thisValue = this; |
| 100 var thisTmp = null; |
| 101 var retTmp = null; |
| 102 if (kind != 0) { |
| 103 // TODO(jimhug): Very special number optimizations will go here... |
| 104 thisTmp = context.getTemp(thisValue); |
| 105 thisValue = context.assignTemp(thisTmp, thisValue); |
| 106 var lhs = member._get(context, node, thisTmp); |
| 107 if (returnKind == ReturnKind.PRE) { |
| 108 retTmp = context.forceTemp(lhs); |
| 109 lhs = context.assignTemp(retTmp, lhs); |
| 110 } |
| 111 value = lhs.binop(kind, value, context, node); |
| 112 } |
| 113 |
| 114 if (returnKind == ReturnKind.POST) { |
| 115 // TODO(jimhug): Optimize this away when native JS is detected. |
| 116 retTmp = context.forceTemp(value); |
| 117 value = context.assignTemp(retTmp, value); |
| 118 } |
| 119 |
| 120 var ret = member._set(context, node, thisValue, value, isDynamic); |
| 121 if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp); |
| 122 if (retTmp != null) { |
| 123 context.freeTemp(retTmp); |
| 124 return Value.comma(ret, retTmp); |
| 125 } else { |
| 126 return ret; |
| 127 } |
| 97 } else { | 128 } else { |
| 129 // TODO(jimhug): Need to support += and noSuchMethod better. |
| 98 return invokeNoSuchMethod(context, 'set:$name', node, | 130 return invokeNoSuchMethod(context, 'set:$name', node, |
| 99 new Arguments(null, [value])); | 131 new Arguments(null, [value])); |
| 100 } | 132 } |
| 101 } | 133 } |
| 102 | 134 |
| 135 // TODO(jimhug): This method body has too much in common with set_ above. |
| 136 Value setIndex(MethodGenerator context, Value index, Node node, Value value, |
| 137 [bool isDynamic=false, int kind=0, int returnKind=ReturnKind.IGNORE]) { |
| 138 final member = _resolveMember(context, ':setindex', node, isDynamic); |
| 139 if (member != null) { |
| 140 var thisValue = this; |
| 141 var indexValue = index; |
| 142 var thisTmp = null; |
| 143 var indexTmp = null; |
| 144 var retTmp = null; |
| 145 if (returnKind == ReturnKind.POST) { |
| 146 // TODO(jimhug): Optimize this away when native JS works. |
| 147 retTmp = context.forceTemp(value); |
| 148 } |
| 149 if (kind != 0) { |
| 150 // TODO(jimhug): Very special number optimizations will go here... |
| 151 thisTmp = context.getTemp(this); |
| 152 indexTmp = context.getTemp(index); |
| 153 thisValue = context.assignTemp(thisTmp, thisValue); |
| 154 indexValue = context.assignTemp(indexTmp, indexValue); |
| 155 |
| 156 if (returnKind == ReturnKind.PRE) { |
| 157 retTmp = context.forceTemp(value); |
| 158 } |
| 159 |
| 160 var lhs = thisTmp.invoke(context, ':index', node, |
| 161 new Arguments(null, [indexTmp])); |
| 162 if (returnKind == ReturnKind.PRE) { |
| 163 lhs = context.assignTemp(retTmp, lhs); |
| 164 } |
| 165 value = lhs.binop(kind, value, context, node); |
| 166 } |
| 167 if (returnKind == ReturnKind.POST) { |
| 168 value = context.assignTemp(retTmp, value); |
| 169 } |
| 170 |
| 171 var ret = member.invoke(context, node, thisValue, |
| 172 new Arguments(null, [indexValue, value]), isDynamic); |
| 173 if (thisTmp != null && thisTmp != this) context.freeTemp(thisTmp); |
| 174 if (indexTmp != null && indexTmp != index) context.freeTemp(indexTmp); |
| 175 if (retTmp != null) { |
| 176 context.freeTemp(retTmp); |
| 177 return Value.comma(ret, retTmp); |
| 178 } else { |
| 179 return ret; |
| 180 } |
| 181 } else { |
| 182 // TODO(jimhug): Need to support += and noSuchMethod better. |
| 183 return invokeNoSuchMethod(context, ':index', node, |
| 184 new Arguments(null, [index, value])); |
| 185 } |
| 186 } |
| 187 |
| 188 //Value getIndex(MethodGenerator context, Value index, var node) { |
| 189 //} |
| 190 |
| 103 Value unop(int kind, MethodGenerator context, var node) { | 191 Value unop(int kind, MethodGenerator context, var node) { |
| 104 switch (kind) { | 192 switch (kind) { |
| 105 case TokenKind.NOT: | 193 case TokenKind.NOT: |
| 106 // TODO(jimhug): Issue #359 seeks to clarify this behavior. | 194 // TODO(jimhug): Issue #359 seeks to clarify this behavior. |
| 107 var newVal = convertTo(context, world.nonNullBool); | 195 var newVal = convertTo(context, world.nonNullBool); |
| 108 return new Value(newVal.type, '!${newVal.code}', node.span); | 196 return new Value(newVal.type, '!${newVal.code}', node.span); |
| 109 case TokenKind.ADD: | 197 case TokenKind.ADD: |
| 110 world.error('no unary add operator in dart', node.span); | 198 world.error('no unary add operator in dart', node.span); |
| 111 break; | 199 break; |
| 112 case TokenKind.SUB: | 200 case TokenKind.SUB: |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 var member = _resolveMember(context, name, null, isDynamic:true); | 262 var member = _resolveMember(context, name, null, isDynamic:true); |
| 175 return member != null && member.canInvoke(context, args); | 263 return member != null && member.canInvoke(context, args); |
| 176 } | 264 } |
| 177 | 265 |
| 178 /** | 266 /** |
| 179 * True if this class (or some related class that is not Object) overrides | 267 * True if this class (or some related class that is not Object) overrides |
| 180 * noSuchMethod. If it does we suppress warnings about unknown members. | 268 * noSuchMethod. If it does we suppress warnings about unknown members. |
| 181 */ | 269 */ |
| 182 // TODO(jmesserly): should we be doing this? | 270 // TODO(jmesserly): should we be doing this? |
| 183 bool _hasOverriddenNoSuchMethod() { | 271 bool _hasOverriddenNoSuchMethod() { |
| 184 if (isSuper) { | 272 var m = type.getMember('noSuchMethod'); |
| 185 var m = staticType.getMember('noSuchMethod'); | 273 return m != null && !m.declaringType.isObject; |
| 186 return m != null && !m.declaringType.isObject; | 274 } |
| 187 } else { | 275 |
| 188 var m = staticType.resolveMember('noSuchMethod'); | 276 // TODO(jimhug): Handle more precise types here, i.e. consts or closed... |
| 189 return m != null && m.members.length > 1; | 277 bool get isPreciseType() => isSuper || isType; |
| 278 |
| 279 void _missingMemberError(MethodGenerator context, String name, bool isDynamic,
Node node) { |
| 280 bool onStaticType = false; |
| 281 if (type != staticType) { |
| 282 onStaticType = staticType.getMember(name) !== null; |
| 283 } |
| 284 |
| 285 if (!onStaticType && !isDynamic && |
| 286 !_isVarOrParameterType(staticType) && !_hasOverriddenNoSuchMethod()) { |
| 287 // warn if the member was not found, or error if it is a static lookup. |
| 288 var typeName = staticType.name; |
| 289 if (typeName == null) typeName = staticType.library.name; |
| 290 var message = 'can not resolve "$name" on "${typeName}"'; |
| 291 if (isType) { |
| 292 world.error(message, node.span); |
| 293 } else { |
| 294 world.warning(message, node.span); |
| 295 } |
| 190 } | 296 } |
| 191 } | 297 } |
| 192 | 298 |
| 193 _tryResolveMember(MethodGenerator context, Type resolvetype, String name) { | 299 |
| 194 if (isSuper) { | 300 |
| 195 return resolvetype.getMember(name); | 301 MemberSet _tryResolveMember(MethodGenerator context, String name, bool isDynam
ic, Node node) { |
| 302 var member = type.getMember(name); |
| 303 if (member == null) { |
| 304 _missingMemberError(context, name, isDynamic, node); |
| 305 return null; |
| 196 } else { | 306 } else { |
| 197 return resolvetype.resolveMember(name); | 307 if (isType && !member.isStatic) { |
| 308 if (!isDynamic) { |
| 309 world.error('can not refer to instance member as static', node.span); |
| 310 } |
| 311 return null; |
| 312 } |
| 313 } |
| 314 |
| 315 if (isPreciseType || member.isStatic) { |
| 316 return member.preciseMemberSet; |
| 317 } else { |
| 318 return member.potentialMemberSet; |
| 198 } | 319 } |
| 199 } | 320 } |
| 200 | 321 |
| 201 // TODO(jmesserly): until reified generics are fixed, treat ParameterType as | 322 // TODO(jmesserly): until reified generics are fixed, treat ParameterType as |
| 202 // "var". | 323 // "var". |
| 203 bool _isVarOrParameterType(Type t) => t.isVar || t is ParameterType; | 324 bool _isVarOrParameterType(Type t) => t.isVar || t is ParameterType; |
| 204 | 325 |
| 205 bool _shouldBindDynamically() { | 326 bool _shouldBindDynamically() { |
| 206 return _isVarOrParameterType(type) || options.forceDynamic && !isConst; | 327 return _isVarOrParameterType(type) || options.forceDynamic && !isConst; |
| 207 } | 328 } |
| 208 | 329 |
| 209 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) | 330 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) |
| 210 _resolveMember(MethodGenerator context, String name, Node node, | 331 MemberSet _resolveMember(MethodGenerator context, String name, Node node, |
| 211 [bool isDynamic=false]) { | 332 [bool isDynamic=false]) { |
| 212 | 333 var member = null; |
| 213 // TODO(jmesserly): this has gotten ugly again. | |
| 214 var member; | |
| 215 if (!_shouldBindDynamically()) { | 334 if (!_shouldBindDynamically()) { |
| 216 member = _tryResolveMember(context, type, name); | 335 member = _tryResolveMember(context, name, isDynamic, node); |
| 217 | |
| 218 if (member == null && type != staticType) { | |
| 219 member = _tryResolveMember(context, staticType, name); | |
| 220 } | |
| 221 | |
| 222 if (member != null && isType && !member.isStatic) { | |
| 223 if (!isDynamic) { | |
| 224 world.error('can not refer to instance member as static', node.span); | |
| 225 } | |
| 226 return null; | |
| 227 } | |
| 228 | |
| 229 if (member == null && !isDynamic && | |
| 230 !_isVarOrParameterType(staticType) && !_hasOverriddenNoSuchMethod()) { | |
| 231 // warn if the member was not found, or error if it is a static lookup. | |
| 232 var typeName = staticType.name; | |
| 233 if (typeName == null) typeName = staticType.library.name; | |
| 234 var message = 'can not resolve "$name" on "${typeName}"'; | |
| 235 if (isType) { | |
| 236 world.error(message, node.span); | |
| 237 } else { | |
| 238 world.warning(message, node.span); | |
| 239 } | |
| 240 } | |
| 241 } | 336 } |
| 242 | 337 |
| 243 // Fall back to a dynamic operation for instance members | 338 // Fall back to a dynamic operation for instance members |
| 244 if (member == null && !isSuper && !isType) { | 339 if (member == null && !isSuper && !isType) { |
| 245 member = context.findMembers(name); | 340 member = context.findMembers(name); |
| 246 if (member == null && !isDynamic) { | 341 if (member == null && !isDynamic) { |
| 247 var where = 'the world'; | 342 var where = 'the world'; |
| 248 if (name.startsWith('_')) { | 343 if (name.startsWith('_')) { |
| 249 where = 'library "${context.library.name}"'; | 344 where = 'library "${context.library.name}"'; |
| 250 } | 345 } |
| (...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 543 } | 638 } |
| 544 | 639 |
| 545 void convertWarning(Type toType) { | 640 void convertWarning(Type toType) { |
| 546 // TODO(jmesserly): better error messages for type conversion failures | 641 // TODO(jmesserly): better error messages for type conversion failures |
| 547 world.warning('type "${type.name}" is not assignable to "${toType.name}"', | 642 world.warning('type "${type.name}" is not assignable to "${toType.name}"', |
| 548 span); | 643 span); |
| 549 } | 644 } |
| 550 | 645 |
| 551 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, | 646 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, |
| 552 [Arguments args]) { | 647 [Arguments args]) { |
| 648 if (isType) { |
| 649 world.error('member lookup failed for "$name"', node.span); |
| 650 } |
| 651 |
| 553 var pos = ''; | 652 var pos = ''; |
| 554 if (args != null) { | 653 if (args != null) { |
| 555 var argsCode = []; | 654 var argsCode = []; |
| 556 for (int i = 0; i < args.length; i++) { | 655 for (int i = 0; i < args.length; i++) { |
| 557 argsCode.add(args.values[i].code); | 656 argsCode.add(args.values[i].code); |
| 558 } | 657 } |
| 559 pos = Strings.join(argsCode, ", "); // don't remove trailing nulls | 658 pos = Strings.join(argsCode, ", "); // don't remove trailing nulls |
| 560 } | 659 } |
| 561 final noSuchArgs = [ | 660 final noSuchArgs = [ |
| 562 new Value(world.stringType, '"$name"', node.span), | 661 new Value(world.stringType, '"$name"', node.span), |
| (...skipping 666 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1229 bool get needsTemp() => false; | 1328 bool get needsTemp() => false; |
| 1230 bool _shouldBindDynamically() => false; | 1329 bool _shouldBindDynamically() => false; |
| 1231 | 1330 |
| 1232 String get code() => _code; | 1331 String get code() => _code; |
| 1233 | 1332 |
| 1234 // TODO(jimhug): Lazy initialization here is weird! | 1333 // TODO(jimhug): Lazy initialization here is weird! |
| 1235 void _ensureCode() { | 1334 void _ensureCode() { |
| 1236 if (_code === null) _code = isType ? type.jsname : home._makeThisCode(); | 1335 if (_code === null) _code = isType ? type.jsname : home._makeThisCode(); |
| 1237 } | 1336 } |
| 1238 | 1337 |
| 1239 _tryResolveMember(MethodGenerator context, Type resolveType, String name) { | 1338 MemberSet _tryResolveMember(MethodGenerator context, String name, bool isDynam
ic, Node node) { |
| 1240 assert(context == home); | 1339 assert(context == home); |
| 1241 | 1340 |
| 1242 // First look for members directly defined on my resolveType. | 1341 // TODO(jimhug): Confirm this matches final resolution of issue 641. |
| 1243 var member = resolveType.resolveMember(name); | 1342 var member = type.getMember(name); |
| 1244 if (member != null) { | 1343 if (member == null || member.declaringType != type) { |
| 1245 if (options.forceDynamic && !member.isStatic) { | 1344 var libMember = home.library.lookup(name, span); |
| 1246 member = context.findMembers(name); | 1345 if (libMember !== null) { |
| 1346 return libMember.preciseMemberSet; |
| 1247 } | 1347 } |
| 1248 _ensureCode(); | |
| 1249 return member; | |
| 1250 } | |
| 1251 | |
| 1252 // Then look for members in my library. | |
| 1253 member = home.library.lookup(name, span); | |
| 1254 if (member != null) { | |
| 1255 return member; | |
| 1256 } | 1348 } |
| 1257 | 1349 |
| 1258 _ensureCode(); | 1350 _ensureCode(); |
| 1259 return null; | 1351 return super._tryResolveMember(context, name, isDynamic, node); |
| 1260 } | 1352 } |
| 1261 } | 1353 } |
| 1262 | 1354 |
| 1263 /** A reference to 'super'. */ | 1355 /** A reference to 'super'. */ |
| 1264 // TODO(jmesserly): override resolveMember to clean up the one on Value | 1356 // TODO(jmesserly): override resolveMember to clean up the one on Value |
| 1265 class SuperValue extends Value { | 1357 class SuperValue extends Value { |
| 1266 SuperValue(Type parentType, SourceSpan span): | 1358 SuperValue(Type parentType, SourceSpan span): |
| 1267 super(parentType, 'this', span); | 1359 super(parentType, 'this', span); |
| 1268 | 1360 |
| 1269 bool get needsTemp() => false; | 1361 bool get needsTemp() => false; |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1374 } | 1466 } |
| 1375 return super.unop(kind, context, node); | 1467 return super.unop(kind, context, node); |
| 1376 } | 1468 } |
| 1377 Value binop(int kind, var other, MethodGenerator context, var node) { | 1469 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 1378 if (value != null) { | 1470 if (value != null) { |
| 1379 return replaceValue(value.binop(kind, _unwrap(other), context, node)); | 1471 return replaceValue(value.binop(kind, _unwrap(other), context, node)); |
| 1380 } | 1472 } |
| 1381 return super.binop(kind, other, context, node); | 1473 return super.binop(kind, other, context, node); |
| 1382 } | 1474 } |
| 1383 } | 1475 } |
| OLD | NEW |