| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** A formal parameter to a [Method]. */ | 5 /** A formal parameter to a [Method]. */ |
| 6 class Parameter { | 6 class Parameter { |
| 7 FormalNode definition; | 7 FormalNode definition; |
| 8 | 8 |
| 9 String name; | 9 String name; |
| 10 Type type; | 10 Type type; |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 void providePropertySyntax() => | 132 void providePropertySyntax() => |
| 133 world.internalError('can not be property', span); | 133 world.internalError('can not be property', span); |
| 134 | 134 |
| 135 Definition get initDelegate() { | 135 Definition get initDelegate() { |
| 136 world.internalError('cannot have initializers', span); | 136 world.internalError('cannot have initializers', span); |
| 137 } | 137 } |
| 138 Definition set initDelegate(ctor) { | 138 Definition set initDelegate(ctor) { |
| 139 world.internalError('cannot have initializers', span); | 139 world.internalError('cannot have initializers', span); |
| 140 } | 140 } |
| 141 | 141 |
| 142 /** |
| 143 * The inferred returnType. Right now this is just used to track |
| 144 * non-nullable bools. |
| 145 */ |
| 146 Type get inferredResult() { |
| 147 var t = returnType; |
| 148 if (t.isBool && (library.isCore || library.isCoreImpl)) { |
| 149 // We trust our core libraries not to return null from bools. |
| 150 // I hope this trust is well placed! |
| 151 return world.nonNullBool; |
| 152 } |
| 153 return t; |
| 154 } |
| 155 |
| 142 Definition get definition() => null; | 156 Definition get definition() => null; |
| 143 | 157 |
| 144 List<Parameter> get parameters() => []; | 158 List<Parameter> get parameters() => []; |
| 145 | 159 |
| 146 // TODO(jmesserly): isDynamic isn't a great name for this, something better? | 160 // TODO(jmesserly): isDynamic isn't a great name for this, something better? |
| 147 abstract Value _get(MethodGenerator context, Node node, Value target, | 161 abstract Value _get(MethodGenerator context, Node node, Value target, |
| 148 [bool isDynamic]); | 162 [bool isDynamic]); |
| 149 | 163 |
| 150 abstract Value _set(MethodGenerator context, Node node, Value target, | 164 abstract Value _set(MethodGenerator context, Node node, Value target, |
| 151 Value value, [bool isDynamic]); | 165 Value value, [bool isDynamic]); |
| (...skipping 414 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 566 var type = baseMember.resolveType(node, isRequired); | 580 var type = baseMember.resolveType(node, isRequired); |
| 567 return type.resolveTypeParams(declaringType); | 581 return type.resolveTypeParams(declaringType); |
| 568 } | 582 } |
| 569 | 583 |
| 570 // TODO(jimhug): Add support for type params. | 584 // TODO(jimhug): Add support for type params. |
| 571 bool override(Member other) => baseMember.override(other); | 585 bool override(Member other) => baseMember.override(other); |
| 572 | 586 |
| 573 Value _get(MethodGenerator context, Node node, Value target, | 587 Value _get(MethodGenerator context, Node node, Value target, |
| 574 [bool isDynamic=false]) { | 588 [bool isDynamic=false]) { |
| 575 Value ret = baseMember._get(context, node, target, isDynamic); | 589 Value ret = baseMember._get(context, node, target, isDynamic); |
| 576 return new Value(returnType, ret.code, node.span); | 590 return new Value(inferredResult, ret.code, node.span); |
| 577 } | 591 } |
| 578 | 592 |
| 579 Value _set(MethodGenerator context, Node node, Value target, Value value, | 593 Value _set(MethodGenerator context, Node node, Value target, Value value, |
| 580 [bool isDynamic=false]) { | 594 [bool isDynamic=false]) { |
| 581 // TODO(jimhug): Check arg types in context of concrete type. | 595 // TODO(jimhug): Check arg types in context of concrete type. |
| 582 Value ret = baseMember._set(context, node, target, value, isDynamic); | 596 Value ret = baseMember._set(context, node, target, value, isDynamic); |
| 583 return new Value(returnType, ret.code, node.span); | 597 return new Value(returnType, ret.code, node.span); |
| 584 } | 598 } |
| 585 | 599 |
| 586 Value invoke(MethodGenerator context, Node node, Value target, Arguments args, | 600 Value invoke(MethodGenerator context, Node node, Value target, Arguments args, |
| 587 [bool isDynamic=false]) { | 601 [bool isDynamic=false]) { |
| 588 // TODO(jimhug): Check arg types in context of concrete type. | 602 // TODO(jimhug): Check arg types in context of concrete type. |
| 589 Value ret = baseMember.invoke(context, node, target, args, isDynamic); | 603 Value ret = baseMember.invoke(context, node, target, args, isDynamic); |
| 590 var code = ret.code; | 604 var code = ret.code; |
| 591 if (isConstructor) { | 605 if (isConstructor) { |
| 592 // TODO(jimhug): Egregious hack - won't live through the weekend. | 606 // TODO(jimhug): Egregious hack - won't live through the weekend. |
| 593 code = code.replaceFirst( | 607 code = code.replaceFirst( |
| 594 declaringType.genericType.jsname, declaringType.jsname); | 608 declaringType.genericType.jsname, declaringType.jsname); |
| 595 } | 609 } |
| 596 declaringType.genMethod(this); | 610 declaringType.genMethod(this); |
| 597 return new Value(returnType, code, node.span); | 611 return new Value(inferredResult, code, node.span); |
| 598 } | 612 } |
| 599 } | 613 } |
| 600 | 614 |
| 601 | 615 |
| 602 /** Represents a Dart method or top-level function. */ | 616 /** Represents a Dart method or top-level function. */ |
| 603 class MethodMember extends Member { | 617 class MethodMember extends Member { |
| 604 FunctionDefinition definition; | 618 FunctionDefinition definition; |
| 605 Type returnType; | 619 Type returnType; |
| 606 List<Parameter> parameters; | 620 List<Parameter> parameters; |
| 607 | 621 |
| (...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 899 } | 913 } |
| 900 | 914 |
| 901 var argsString = Strings.join(argsCode, ', '); | 915 var argsString = Strings.join(argsCode, ', '); |
| 902 | 916 |
| 903 if (isConstructor) { | 917 if (isConstructor) { |
| 904 return _invokeConstructor(context, node, target, args, argsString); | 918 return _invokeConstructor(context, node, target, args, argsString); |
| 905 } | 919 } |
| 906 | 920 |
| 907 // TODO(jimhug): target really shouldn't ever be null... | 921 // TODO(jimhug): target really shouldn't ever be null... |
| 908 if (target != null && target.isSuper) { | 922 if (target != null && target.isSuper) { |
| 909 return new Value(returnType, | 923 return new Value(inferredResult, |
| 910 '${declaringType.jsname}.prototype.$jsname.call($argsString)', | 924 '${declaringType.jsname}.prototype.$jsname.call($argsString)', |
| 911 node.span); | 925 node.span); |
| 912 } | 926 } |
| 913 | 927 |
| 914 if (name.startsWith('\$')) { | 928 if (name.startsWith('\$')) { |
| 915 return _invokeBuiltin(context, node, target, args, argsCode); | 929 return _invokeBuiltin(context, node, target, args, argsCode, isDynamic); |
| 916 } | 930 } |
| 917 | 931 |
| 918 if (isFactory) { | 932 if (isFactory) { |
| 919 return new Value(returnType, '$generatedFactoryName($argsString)', | 933 return new Value(inferredResult, '$generatedFactoryName($argsString)', |
| 920 node.span); | 934 node.span); |
| 921 } | 935 } |
| 922 | 936 |
| 923 if (isStatic) { | 937 if (isStatic) { |
| 924 if (declaringType.isTop) { | 938 if (declaringType.isTop) { |
| 925 // TODO(jimhug): Explore moving libraries into their own namespaces | 939 // TODO(jimhug): Explore moving libraries into their own namespaces |
| 926 return new Value(returnType, '$jsname($argsString)', node != null ? node
.span : node); | 940 return new Value(inferredResult, '$jsname($argsString)', node != null ?
node.span : node); |
| 927 } | 941 } |
| 928 return new Value(returnType, | 942 return new Value(inferredResult, |
| 929 '${declaringType.jsname}.$jsname($argsString)', node.span); | 943 '${declaringType.jsname}.$jsname($argsString)', node.span); |
| 930 } | 944 } |
| 931 | 945 |
| 932 var code = '${target.code}.$jsname($argsString)'; | 946 var code = '${target.code}.$jsname($argsString)'; |
| 933 // optimize expressions which we know statically their value. | 947 // optimize expressions which we know statically their value. |
| 934 if (target.isConst) { | 948 if (target.isConst) { |
| 935 if (target is GlobalValue) { | 949 if (target is GlobalValue) { |
| 936 target = target.dynamic.exp; // TODO: an inline "cast" would be nice. | 950 target = target.dynamic.exp; // TODO: an inline "cast" would be nice. |
| 937 } | 951 } |
| 938 if (name == 'get\$length') { | 952 if (name == 'get\$length') { |
| 939 if (target is ConstListValue || target is ConstMapValue) { | 953 if (target is ConstListValue || target is ConstMapValue) { |
| 940 code = '${target.dynamic.values.length}'; | 954 code = '${target.dynamic.values.length}'; |
| 941 } | 955 } |
| 942 } else if (name == 'isEmpty') { | 956 } else if (name == 'isEmpty') { |
| 943 if (target is ConstListValue || target is ConstMapValue) { | 957 if (target is ConstListValue || target is ConstMapValue) { |
| 944 code = '${target.dynamic.values.isEmpty()}'; | 958 code = '${target.dynamic.values.isEmpty()}'; |
| 945 } | 959 } |
| 946 } | 960 } |
| 947 } | 961 } |
| 948 | 962 |
| 949 // TODO(jmesserly): factor this better | 963 // TODO(jmesserly): factor this better |
| 950 if (name == 'get\$typeName' && declaringType.library == world.dom) { | 964 if (name == 'get\$typeName' && declaringType.library == world.dom) { |
| 951 world.gen.corejs.useTypeNameOf = true; | 965 world.gen.corejs.useTypeNameOf = true; |
| 952 } | 966 } |
| 953 | 967 |
| 954 return new Value(returnType, code, node.span); | 968 return new Value(inferredResult, code, node.span); |
| 955 } | 969 } |
| 956 | 970 |
| 957 Value _invokeConstructor(MethodGenerator context, Node node, | 971 Value _invokeConstructor(MethodGenerator context, Node node, |
| 958 Value target, Arguments args, argsString) { | 972 Value target, Arguments args, argsString) { |
| 959 declaringType.markUsed(); | 973 declaringType.markUsed(); |
| 960 | 974 |
| 961 if (target != null) { | 975 if (target != null) { |
| 962 // initializer call to another constructor | 976 // initializer call to another constructor |
| 963 var code = (constructorName != '') | 977 var code = (constructorName != '') |
| 964 ? '${declaringType.jsname}.${constructorName}\$ctor.call($argsString)' | 978 ? '${declaringType.jsname}.${constructorName}\$ctor.call($argsString)' |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1075 } | 1089 } |
| 1076 } | 1090 } |
| 1077 | 1091 |
| 1078 return world.gen.globalForConst( | 1092 return world.gen.globalForConst( |
| 1079 new ConstObjectValue(declaringType, fields, code, node.span), | 1093 new ConstObjectValue(declaringType, fields, code, node.span), |
| 1080 args.values); | 1094 args.values); |
| 1081 } | 1095 } |
| 1082 | 1096 |
| 1083 | 1097 |
| 1084 Value _invokeBuiltin(MethodGenerator context, Node node, Value target, | 1098 Value _invokeBuiltin(MethodGenerator context, Node node, Value target, |
| 1085 Arguments args, argsCode) { | 1099 Arguments args, argsCode, bool isDynamic) { |
| 1086 var allConst = target.isConst && args.values.every((arg) => arg.isConst); | 1100 var allConst = target.isConst && args.values.every((arg) => arg.isConst); |
| 1101 |
| 1087 // Handle some fast paths for Number, String, List and DOM. | 1102 // Handle some fast paths for Number, String, List and DOM. |
| 1088 if (declaringType.isNum) { | 1103 if (declaringType.isNum) { |
| 1089 // TODO(jimhug): This fails in bad ways when argsCode[1] is not num. | 1104 // TODO(jimhug): This fails in bad ways when argsCode[1] is not num. |
| 1090 // TODO(jimhug): What about null? | 1105 // TODO(jimhug): What about null? |
| 1091 if (!allConst) { | 1106 if (!allConst) { |
| 1092 var code; | 1107 var code; |
| 1093 if (name == '\$negate') { | 1108 if (name == '\$negate') { |
| 1094 code = '-${target.code}'; | 1109 code = '-${target.code}'; |
| 1095 } else if (name == '\$bit_not') { | 1110 } else if (name == '\$bit_not') { |
| 1096 code = '~${target.code}'; | 1111 code = '~${target.code}'; |
| 1097 } else if (name == '\$truncdiv' || name == '\$mod') { | 1112 } else if (name == '\$truncdiv' || name == '\$mod') { |
| 1098 world.gen.corejs.useOperator(name); | 1113 world.gen.corejs.useOperator(name); |
| 1099 code = '$name(${target.code}, ${argsCode[0]})'; | 1114 code = '$name(${target.code}, ${argsCode[0]})'; |
| 1100 } else { | 1115 } else { |
| 1101 var op = TokenKind.rawOperatorFromMethod(name); | 1116 var op = TokenKind.rawOperatorFromMethod(name); |
| 1102 code = '${target.code} $op ${argsCode[0]}'; | 1117 code = '${target.code} $op ${argsCode[0]}'; |
| 1103 } | 1118 } |
| 1104 | 1119 |
| 1105 return new Value(returnType, code, node.span); | 1120 return new Value(inferredResult, code, node.span); |
| 1106 } else { | 1121 } else { |
| 1107 var value; | 1122 var value; |
| 1108 num val0, val1, ival0, ival1; | 1123 num val0, val1, ival0, ival1; |
| 1109 val0 = target.dynamic.actualValue; | 1124 val0 = target.dynamic.actualValue; |
| 1110 ival0 = val0.toInt(); | 1125 ival0 = val0.toInt(); |
| 1111 if (args.values.length > 0) { | 1126 if (args.values.length > 0) { |
| 1112 val1 = args.values[0].dynamic.actualValue; | 1127 val1 = args.values[0].dynamic.actualValue; |
| 1113 ival1 = val1.toInt(); | 1128 ival1 = val1.toInt(); |
| 1114 } | 1129 } |
| 1115 switch (name) { | 1130 switch (name) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1129 | 1144 |
| 1130 // Note: unfortunatelly bit operations fail on doubles in dartvm | 1145 // Note: unfortunatelly bit operations fail on doubles in dartvm |
| 1131 case '\$bit_not': value = (~ival0).toDouble(); break; | 1146 case '\$bit_not': value = (~ival0).toDouble(); break; |
| 1132 case '\$bit_or': value = (ival0 | ival1).toDouble(); break; | 1147 case '\$bit_or': value = (ival0 | ival1).toDouble(); break; |
| 1133 case '\$bit_xor': value = (ival0 ^ ival1).toDouble(); break; | 1148 case '\$bit_xor': value = (ival0 ^ ival1).toDouble(); break; |
| 1134 case '\$bit_and': value = (ival0 & ival1).toDouble(); break; | 1149 case '\$bit_and': value = (ival0 & ival1).toDouble(); break; |
| 1135 case '\$shl': value = (ival0 << ival1).toDouble(); break; | 1150 case '\$shl': value = (ival0 << ival1).toDouble(); break; |
| 1136 case '\$sar': value = (ival0 >> ival1).toDouble(); break; | 1151 case '\$sar': value = (ival0 >> ival1).toDouble(); break; |
| 1137 case '\$shr': value = (ival0 >>> ival1).toDouble(); break; | 1152 case '\$shr': value = (ival0 >>> ival1).toDouble(); break; |
| 1138 } | 1153 } |
| 1139 return new EvaluatedValue(returnType, value, "$value", node.span); | 1154 return new EvaluatedValue(inferredResult, value, "$value", node.span); |
| 1140 } | 1155 } |
| 1141 } else if (declaringType.isString) { | 1156 } else if (declaringType.isString) { |
| 1142 if (name == '\$index') { | 1157 if (name == '\$index') { |
| 1143 // Note: this could technically propagate constness, but that's not | 1158 // Note: this could technically propagate constness, but that's not |
| 1144 // specified explicitly and the VM doesn't do that. | 1159 // specified explicitly and the VM doesn't do that. |
| 1145 return new Value(declaringType, '${target.code}[${argsCode[0]}]', | 1160 return new Value(declaringType, '${target.code}[${argsCode[0]}]', |
| 1146 node.span); | 1161 node.span); |
| 1147 } else if (name == '\$add') { | 1162 } else if (name == '\$add') { |
| 1148 if (allConst) { | 1163 if (allConst) { |
| 1149 var val0 = target.dynamic.actualValue; | 1164 var val0 = target.dynamic.actualValue; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1169 return new Value(returnType, '${target.code}[${argsCode[0]}]', node.span
); | 1184 return new Value(returnType, '${target.code}[${argsCode[0]}]', node.span
); |
| 1170 } else if (name == '\$setindex') { | 1185 } else if (name == '\$setindex') { |
| 1171 return new Value(returnType, | 1186 return new Value(returnType, |
| 1172 '${target.code}[${argsCode[0]}] = ${argsCode[1]}', node.span); | 1187 '${target.code}[${argsCode[0]}] = ${argsCode[1]}', node.span); |
| 1173 } | 1188 } |
| 1174 } | 1189 } |
| 1175 | 1190 |
| 1176 // TODO(jimhug): Optimize null on lhs as well. | 1191 // TODO(jimhug): Optimize null on lhs as well. |
| 1177 if (name == '\$eq' || name == '\$ne') { | 1192 if (name == '\$eq' || name == '\$ne') { |
| 1178 final op = name == '\$eq' ? '==' : '!='; | 1193 final op = name == '\$eq' ? '==' : '!='; |
| 1194 |
| 1195 if (name == '\$ne') { |
| 1196 // Ensure == is generated. |
| 1197 target.invoke(context, '\$eq', node, args, isDynamic); |
| 1198 } |
| 1199 |
| 1179 if (allConst) { | 1200 if (allConst) { |
| 1180 var val0 = target.dynamic.actualValue; | 1201 var val0 = target.dynamic.actualValue; |
| 1181 var val1 = args.values[0].dynamic.actualValue; | 1202 var val1 = args.values[0].dynamic.actualValue; |
| 1182 var newVal = name == '\$eq' ? val0 == val1 : val0 != val1; | 1203 var newVal = name == '\$eq' ? val0 == val1 : val0 != val1; |
| 1183 return new EvaluatedValue(world.boolType, | 1204 return new EvaluatedValue(world.nonNullBool, |
| 1184 newVal, "$newVal", node.span); | 1205 newVal, "$newVal", node.span); |
| 1185 } | 1206 } |
| 1186 // Optimize test when null is on the rhs. | 1207 // Optimize test when null is on the rhs. |
| 1187 if (argsCode[0] == 'null') { | 1208 if (argsCode[0] == 'null') { |
| 1188 return new Value(returnType, '${target.code} $op null', node.span); | 1209 return new Value(inferredResult, '${target.code} $op null', node.span); |
| 1189 } else if (target.type.isNum || target.type.isString) { | 1210 } else if (target.type.isNum || target.type.isString) { |
| 1190 // TODO(jimhug): Maybe check rhs. | 1211 // TODO(jimhug): Maybe check rhs. |
| 1191 return new Value(returnType, '${target.code} $op ${argsCode[0]}', | 1212 return new Value(inferredResult, '${target.code} $op ${argsCode[0]}', |
| 1192 node.span); | 1213 node.span); |
| 1193 } | 1214 } |
| 1194 world.gen.corejs.useOperator(name); | 1215 world.gen.corejs.useOperator(name); |
| 1195 return new Value(returnType, '$name(${target.code}, ${argsCode[0]})', | 1216 return new Value(inferredResult, '$name(${target.code}, ${argsCode[0]})', |
| 1196 node.span); | 1217 node.span); |
| 1197 } | 1218 } |
| 1198 | 1219 |
| 1199 if (name == '\$call') { | 1220 if (name == '\$call') { |
| 1200 declaringType.markUsed(); | 1221 declaringType.markUsed(); |
| 1201 return new Value(returnType, | 1222 return new Value(inferredResult, |
| 1202 '${target.code}(${Strings.join(argsCode, ", ")})', node.span); | 1223 '${target.code}(${Strings.join(argsCode, ", ")})', node.span); |
| 1203 } | 1224 } |
| 1204 | 1225 |
| 1205 if (name == '\$index') { | 1226 if (name == '\$index') { |
| 1206 world.gen.corejs.useIndex = true; | 1227 world.gen.corejs.useIndex = true; |
| 1207 } else if (name == '\$setindex') { | 1228 } else if (name == '\$setindex') { |
| 1208 world.gen.corejs.useSetIndex = true; | 1229 world.gen.corejs.useSetIndex = true; |
| 1209 } | 1230 } |
| 1210 | 1231 |
| 1211 // Fall back to normal method invocation. | 1232 // Fall back to normal method invocation. |
| 1212 var argsString = Strings.join(argsCode, ', '); | 1233 var argsString = Strings.join(argsCode, ', '); |
| 1213 return new Value(returnType, '${target.code}.$jsname($argsString)', | 1234 return new Value(inferredResult, '${target.code}.$jsname($argsString)', |
| 1214 node.span); | 1235 node.span); |
| 1215 } | 1236 } |
| 1216 | 1237 |
| 1217 | 1238 |
| 1218 resolve(Type inType) { | 1239 resolve(Type inType) { |
| 1219 // TODO(jimhug): cut-and-paste-and-edit from Field.resolve | 1240 // TODO(jimhug): cut-and-paste-and-edit from Field.resolve |
| 1220 isStatic = inType.isTop; | 1241 isStatic = inType.isTop; |
| 1221 isConst = false; | 1242 isConst = false; |
| 1222 isFactory = false; | 1243 isFactory = false; |
| 1223 isAbstract = !declaringType.isClass; | 1244 isAbstract = !declaringType.isClass; |
| (...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1578 } | 1599 } |
| 1579 | 1600 |
| 1580 void forEach(void f(Member member)) { | 1601 void forEach(void f(Member member)) { |
| 1581 factories.forEach((_, Map constructors) { | 1602 factories.forEach((_, Map constructors) { |
| 1582 constructors.forEach((_, Member member) { | 1603 constructors.forEach((_, Member member) { |
| 1583 f(member); | 1604 f(member); |
| 1584 }); | 1605 }); |
| 1585 }); | 1606 }); |
| 1586 } | 1607 } |
| 1587 } | 1608 } |
| OLD | NEW |