| 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 | |
| 54 // TODO(jmesserly): more work is needed to make unifying all kinds of Values | 50 // TODO(jmesserly): more work is needed to make unifying all kinds of Values |
| 55 // work properly. | 51 // work properly. |
| 56 static Value union(Value x, Value y) { | 52 static Value union(Value x, Value y) { |
| 57 if (y === null || x == y) return x; | 53 if (y === null || x == y) return x; |
| 58 if (x === null) return y; | 54 if (x === null) return y; |
| 59 | 55 |
| 60 var ret = x._tryUnion(y); | 56 var ret = x._tryUnion(y); |
| 61 if (ret != null) return ret; | 57 if (ret != null) return ret; |
| 62 | 58 |
| 63 // TODO(jmesserly): might want to call a _tryUnionReversed here. | 59 // TODO(jmesserly): might want to call a _tryUnionReversed here. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 86 Value get_(MethodGenerator context, String name, Node node) { | 82 Value get_(MethodGenerator context, String name, Node node) { |
| 87 final member = _resolveMember(context, name, node); | 83 final member = _resolveMember(context, name, node); |
| 88 if (member != null) { | 84 if (member != null) { |
| 89 return member._get(context, node, this); | 85 return member._get(context, node, this); |
| 90 } else { | 86 } else { |
| 91 return invokeNoSuchMethod(context, 'get:$name', node); | 87 return invokeNoSuchMethod(context, 'get:$name', node); |
| 92 } | 88 } |
| 93 } | 89 } |
| 94 | 90 |
| 95 Value set_(MethodGenerator context, String name, Node node, Value value, | 91 Value set_(MethodGenerator context, String name, Node node, Value value, |
| 96 [bool isDynamic=false, int kind=0, int returnKind=ReturnKind.IGNORE]) { | 92 [bool isDynamic=false]) { |
| 93 |
| 97 final member = _resolveMember(context, name, node, isDynamic); | 94 final member = _resolveMember(context, name, node, isDynamic); |
| 98 if (member != null) { | 95 if (member != null) { |
| 99 var thisValue = this; | 96 return member._set(context, node, this, value, isDynamic); |
| 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 } | |
| 128 } else { | 97 } else { |
| 129 // TODO(jimhug): Need to support += and noSuchMethod better. | |
| 130 return invokeNoSuchMethod(context, 'set:$name', node, | 98 return invokeNoSuchMethod(context, 'set:$name', node, |
| 131 new Arguments(null, [value])); | 99 new Arguments(null, [value])); |
| 132 } | 100 } |
| 133 } | 101 } |
| 134 | 102 |
| 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 | |
| 191 Value unop(int kind, MethodGenerator context, var node) { | 103 Value unop(int kind, MethodGenerator context, var node) { |
| 192 switch (kind) { | 104 switch (kind) { |
| 193 case TokenKind.NOT: | 105 case TokenKind.NOT: |
| 194 // TODO(jimhug): Issue #359 seeks to clarify this behavior. | 106 // TODO(jimhug): Issue #359 seeks to clarify this behavior. |
| 195 var newVal = convertTo(context, world.nonNullBool); | 107 var newVal = convertTo(context, world.nonNullBool); |
| 196 return new Value(newVal.type, '!${newVal.code}', node.span); | 108 return new Value(newVal.type, '!${newVal.code}', node.span); |
| 197 case TokenKind.ADD: | 109 case TokenKind.ADD: |
| 198 world.error('no unary add operator in dart', node.span); | 110 world.error('no unary add operator in dart', node.span); |
| 199 break; | 111 break; |
| 200 case TokenKind.SUB: | 112 case TokenKind.SUB: |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 var member = _resolveMember(context, name, null, isDynamic:true); | 174 var member = _resolveMember(context, name, null, isDynamic:true); |
| 263 return member != null && member.canInvoke(context, args); | 175 return member != null && member.canInvoke(context, args); |
| 264 } | 176 } |
| 265 | 177 |
| 266 /** | 178 /** |
| 267 * True if this class (or some related class that is not Object) overrides | 179 * True if this class (or some related class that is not Object) overrides |
| 268 * noSuchMethod. If it does we suppress warnings about unknown members. | 180 * noSuchMethod. If it does we suppress warnings about unknown members. |
| 269 */ | 181 */ |
| 270 // TODO(jmesserly): should we be doing this? | 182 // TODO(jmesserly): should we be doing this? |
| 271 bool _hasOverriddenNoSuchMethod() { | 183 bool _hasOverriddenNoSuchMethod() { |
| 272 var m = type.getMember('noSuchMethod'); | 184 if (isSuper) { |
| 273 return m != null && !m.declaringType.isObject; | 185 var m = staticType.getMember('noSuchMethod'); |
| 274 } | 186 return m != null && !m.declaringType.isObject; |
| 275 | 187 } else { |
| 276 // TODO(jimhug): Handle more precise types here, i.e. consts or closed... | 188 var m = staticType.resolveMember('noSuchMethod'); |
| 277 bool get isPreciseType() => isSuper || isType; | 189 return m != null && m.members.length > 1; |
| 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 } | |
| 296 } | 190 } |
| 297 } | 191 } |
| 298 | 192 |
| 299 | 193 _tryResolveMember(MethodGenerator context, Type resolvetype, String name) { |
| 300 | 194 if (isSuper) { |
| 301 MemberSet _tryResolveMember(MethodGenerator context, String name, bool isDynam
ic, Node node) { | 195 return resolvetype.getMember(name); |
| 302 var member = type.getMember(name); | |
| 303 if (member == null) { | |
| 304 _missingMemberError(context, name, isDynamic, node); | |
| 305 return null; | |
| 306 } else { | 196 } else { |
| 307 if (isType && !member.isStatic) { | 197 return resolvetype.resolveMember(name); |
| 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; | |
| 319 } | 198 } |
| 320 } | 199 } |
| 321 | 200 |
| 322 // TODO(jmesserly): until reified generics are fixed, treat ParameterType as | 201 // TODO(jmesserly): until reified generics are fixed, treat ParameterType as |
| 323 // "var". | 202 // "var". |
| 324 bool _isVarOrParameterType(Type t) => t.isVar || t is ParameterType; | 203 bool _isVarOrParameterType(Type t) => t.isVar || t is ParameterType; |
| 325 | 204 |
| 326 bool _shouldBindDynamically() { | 205 bool _shouldBindDynamically() { |
| 327 return _isVarOrParameterType(type) || options.forceDynamic && !isConst; | 206 return _isVarOrParameterType(type) || options.forceDynamic && !isConst; |
| 328 } | 207 } |
| 329 | 208 |
| 330 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) | 209 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) |
| 331 MemberSet _resolveMember(MethodGenerator context, String name, Node node, | 210 _resolveMember(MethodGenerator context, String name, Node node, |
| 332 [bool isDynamic=false]) { | 211 [bool isDynamic=false]) { |
| 333 var member = null; | 212 |
| 213 // TODO(jmesserly): this has gotten ugly again. |
| 214 var member; |
| 334 if (!_shouldBindDynamically()) { | 215 if (!_shouldBindDynamically()) { |
| 335 member = _tryResolveMember(context, name, isDynamic, node); | 216 member = _tryResolveMember(context, type, name); |
| 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 } |
| 336 } | 241 } |
| 337 | 242 |
| 338 // Fall back to a dynamic operation for instance members | 243 // Fall back to a dynamic operation for instance members |
| 339 if (member == null && !isSuper && !isType) { | 244 if (member == null && !isSuper && !isType) { |
| 340 member = context.findMembers(name); | 245 member = context.findMembers(name); |
| 341 if (member == null && !isDynamic) { | 246 if (member == null && !isDynamic) { |
| 342 var where = 'the world'; | 247 var where = 'the world'; |
| 343 if (name.startsWith('_')) { | 248 if (name.startsWith('_')) { |
| 344 where = 'library "${context.library.name}"'; | 249 where = 'library "${context.library.name}"'; |
| 345 } | 250 } |
| (...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 638 } | 543 } |
| 639 | 544 |
| 640 void convertWarning(Type toType) { | 545 void convertWarning(Type toType) { |
| 641 // TODO(jmesserly): better error messages for type conversion failures | 546 // TODO(jmesserly): better error messages for type conversion failures |
| 642 world.warning('type "${type.name}" is not assignable to "${toType.name}"', | 547 world.warning('type "${type.name}" is not assignable to "${toType.name}"', |
| 643 span); | 548 span); |
| 644 } | 549 } |
| 645 | 550 |
| 646 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, | 551 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, |
| 647 [Arguments args]) { | 552 [Arguments args]) { |
| 648 if (isType) { | |
| 649 world.error('member lookup failed for "$name"', node.span); | |
| 650 } | |
| 651 | |
| 652 var pos = ''; | 553 var pos = ''; |
| 653 if (args != null) { | 554 if (args != null) { |
| 654 var argsCode = []; | 555 var argsCode = []; |
| 655 for (int i = 0; i < args.length; i++) { | 556 for (int i = 0; i < args.length; i++) { |
| 656 argsCode.add(args.values[i].code); | 557 argsCode.add(args.values[i].code); |
| 657 } | 558 } |
| 658 pos = Strings.join(argsCode, ", "); // don't remove trailing nulls | 559 pos = Strings.join(argsCode, ", "); // don't remove trailing nulls |
| 659 } | 560 } |
| 660 final noSuchArgs = [ | 561 final noSuchArgs = [ |
| 661 new Value(world.stringType, '"$name"', node.span), | 562 new Value(world.stringType, '"$name"', node.span), |
| (...skipping 666 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1328 bool get needsTemp() => false; | 1229 bool get needsTemp() => false; |
| 1329 bool _shouldBindDynamically() => false; | 1230 bool _shouldBindDynamically() => false; |
| 1330 | 1231 |
| 1331 String get code() => _code; | 1232 String get code() => _code; |
| 1332 | 1233 |
| 1333 // TODO(jimhug): Lazy initialization here is weird! | 1234 // TODO(jimhug): Lazy initialization here is weird! |
| 1334 void _ensureCode() { | 1235 void _ensureCode() { |
| 1335 if (_code === null) _code = isType ? type.jsname : home._makeThisCode(); | 1236 if (_code === null) _code = isType ? type.jsname : home._makeThisCode(); |
| 1336 } | 1237 } |
| 1337 | 1238 |
| 1338 MemberSet _tryResolveMember(MethodGenerator context, String name, bool isDynam
ic, Node node) { | 1239 _tryResolveMember(MethodGenerator context, Type resolveType, String name) { |
| 1339 assert(context == home); | 1240 assert(context == home); |
| 1340 | 1241 |
| 1341 // TODO(jimhug): Confirm this matches final resolution of issue 641. | 1242 // First look for members directly defined on my resolveType. |
| 1342 var member = type.getMember(name); | 1243 var member = resolveType.resolveMember(name); |
| 1343 if (member == null || member.declaringType != type) { | 1244 if (member != null) { |
| 1344 var libMember = home.library.lookup(name, span); | 1245 if (options.forceDynamic && !member.isStatic) { |
| 1345 if (libMember !== null) { | 1246 member = context.findMembers(name); |
| 1346 return libMember.preciseMemberSet; | |
| 1347 } | 1247 } |
| 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; |
| 1348 } | 1256 } |
| 1349 | 1257 |
| 1350 _ensureCode(); | 1258 _ensureCode(); |
| 1351 return super._tryResolveMember(context, name, isDynamic, node); | 1259 return null; |
| 1352 } | 1260 } |
| 1353 } | 1261 } |
| 1354 | 1262 |
| 1355 /** A reference to 'super'. */ | 1263 /** A reference to 'super'. */ |
| 1356 // TODO(jmesserly): override resolveMember to clean up the one on Value | 1264 // TODO(jmesserly): override resolveMember to clean up the one on Value |
| 1357 class SuperValue extends Value { | 1265 class SuperValue extends Value { |
| 1358 SuperValue(Type parentType, SourceSpan span): | 1266 SuperValue(Type parentType, SourceSpan span): |
| 1359 super(parentType, 'this', span); | 1267 super(parentType, 'this', span); |
| 1360 | 1268 |
| 1361 bool get needsTemp() => false; | 1269 bool get needsTemp() => false; |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1466 } | 1374 } |
| 1467 return super.unop(kind, context, node); | 1375 return super.unop(kind, context, node); |
| 1468 } | 1376 } |
| 1469 Value binop(int kind, var other, MethodGenerator context, var node) { | 1377 Value binop(int kind, var other, MethodGenerator context, var node) { |
| 1470 if (value != null) { | 1378 if (value != null) { |
| 1471 return replaceValue(value.binop(kind, _unwrap(other), context, node)); | 1379 return replaceValue(value.binop(kind, _unwrap(other), context, node)); |
| 1472 } | 1380 } |
| 1473 return super.binop(kind, other, context, node); | 1381 return super.binop(kind, other, context, node); |
| 1474 } | 1382 } |
| 1475 } | 1383 } |
| OLD | NEW |