Chromium Code Reviews| 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 Member method; | 8 Member method; |
| 9 | 9 |
| 10 String name; | 10 String name; |
| (...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 361 world.error( | 361 world.error( |
| 362 'non constant static field must be initialized in functions', | 362 'non constant static field must be initialized in functions', |
| 363 value.span); | 363 value.span); |
| 364 } else { | 364 } else { |
| 365 world.error( | 365 world.error( |
| 366 'non constant field must be initialized in constructor', | 366 'non constant field must be initialized in constructor', |
| 367 value.span); | 367 value.span); |
| 368 } | 368 } |
| 369 } | 369 } |
| 370 | 370 |
| 371 | |
| 371 if (isStatic) { | 372 if (isStatic) { |
| 372 _computedValue = world.gen.globalForStaticField( | 373 if (isFinal && _computedValue.isConst) { |
| 373 this, _computedValue, [_computedValue]); | 374 ; // keep const as is here |
| 375 } else { | |
| 376 _computedValue = world.gen.globalForStaticField( | |
| 377 this, _computedValue, [_computedValue]); | |
| 378 } | |
| 374 } | 379 } |
| 375 _computing = false; | 380 _computing = false; |
| 376 } | 381 } |
| 377 return _computedValue; | 382 return _computedValue; |
| 378 } | 383 } |
| 379 | 384 |
| 380 Value _get(MethodGenerator context, Node node, Value target, | 385 Value _get(MethodGenerator context, Node node, Value target, |
| 381 [bool isDynamic=false]) { | 386 [bool isDynamic=false]) { |
| 382 if (isNative && returnType != null) { | 387 if (isNative && returnType != null) { |
| 383 returnType.markUsed(); | 388 returnType.markUsed(); |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 640 return new Value(inferredResult, ret.code, node.span); | 645 return new Value(inferredResult, ret.code, node.span); |
| 641 } | 646 } |
| 642 | 647 |
| 643 Value _set(MethodGenerator context, Node node, Value target, Value value, | 648 Value _set(MethodGenerator context, Node node, Value target, Value value, |
| 644 [bool isDynamic=false]) { | 649 [bool isDynamic=false]) { |
| 645 // TODO(jimhug): Check arg types in context of concrete type. | 650 // TODO(jimhug): Check arg types in context of concrete type. |
| 646 Value ret = baseMember._set(context, node, target, value, isDynamic); | 651 Value ret = baseMember._set(context, node, target, value, isDynamic); |
| 647 return new Value(returnType, ret.code, node.span); | 652 return new Value(returnType, ret.code, node.span); |
| 648 } | 653 } |
| 649 | 654 |
| 655 _evalConstConstructor(ObjectValue newObject, Arguments args) { | |
| 656 // TODO(jimhug): Concrete type probably matters somehow here | |
| 657 return baseMember.dynamic._evalConstConstructor(newObject, args); | |
| 658 } | |
| 659 | |
| 660 | |
| 650 Value invoke(MethodGenerator context, Node node, Value target, Arguments args, | 661 Value invoke(MethodGenerator context, Node node, Value target, Arguments args, |
| 651 [bool isDynamic=false]) { | 662 [bool isDynamic=false]) { |
| 652 // TODO(jimhug): Check arg types in context of concrete type. | 663 // TODO(jimhug): Check arg types in context of concrete type. |
| 653 Value ret = baseMember.invoke(context, node, target, args, isDynamic); | 664 Value ret = baseMember.invoke(context, node, target, args, isDynamic); |
| 654 var code = ret.code; | 665 var code = ret.code; |
| 655 if (isConstructor) { | 666 if (isConstructor) { |
| 656 // TODO(jimhug): Egregious hack - won't live through the year. | 667 // TODO(jimhug): Egregious hack - won't live through the year. |
| 657 code = code.replaceFirst( | 668 code = code.replaceFirst( |
| 658 declaringType.genericType.jsname, declaringType.jsname); | 669 declaringType.genericType.jsname, declaringType.jsname); |
| 659 } | 670 } |
| (...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1007 } | 1018 } |
| 1008 | 1019 |
| 1009 if (isFactory) { | 1020 if (isFactory) { |
| 1010 assert(target.isType); | 1021 assert(target.isType); |
| 1011 return new Value(target.type, '$generatedFactoryName($argsString)', | 1022 return new Value(target.type, '$generatedFactoryName($argsString)', |
| 1012 node !== null ? node.span : null); | 1023 node !== null ? node.span : null); |
| 1013 } | 1024 } |
| 1014 | 1025 |
| 1015 if (isStatic) { | 1026 if (isStatic) { |
| 1016 if (declaringType.isTop) { | 1027 if (declaringType.isTop) { |
| 1017 // TODO(jimhug): Explore moving libraries into their own namespaces | |
| 1018 return new Value(inferredResult, | 1028 return new Value(inferredResult, |
| 1019 '$jsname($argsString)', node !== null ? node.span : null); | 1029 '$jsname($argsString)', node !== null ? node.span : null); |
| 1020 } | 1030 } |
| 1021 return new Value(inferredResult, | 1031 return new Value(inferredResult, |
| 1022 '${declaringType.jsname}.$jsname($argsString)', node.span); | 1032 '${declaringType.jsname}.$jsname($argsString)', node.span); |
| 1023 } | 1033 } |
| 1024 | 1034 |
| 1025 var code = '${target.code}.$jsname($argsString)'; | |
| 1026 // optimize expressions which we know statically their value. | |
| 1027 // ???? | |
| 1028 /* | |
| 1029 if (target.isConst) { | |
| 1030 if (target is GlobalValue) { | |
| 1031 target = target.dynamic.exp; // TODO: an inline "cast" would be nice. | |
| 1032 } | |
| 1033 if (name == 'get:length') { | |
| 1034 if (target is ConstListValue || target is ConstMapValue) { | |
| 1035 code = '${target.dynamic.values.length}'; | |
| 1036 } | |
| 1037 } else if (name == 'isEmpty') { | |
| 1038 if (target is ConstListValue || target is ConstMapValue) { | |
| 1039 code = '${target.dynamic.values.isEmpty()}'; | |
| 1040 } | |
| 1041 } | |
| 1042 } | |
| 1043 */ | |
| 1044 | |
| 1045 // TODO(jmesserly): factor this better | 1035 // TODO(jmesserly): factor this better |
| 1046 if (name == 'get:typeName' && declaringType.library.isDom) { | 1036 if (name == 'get:typeName' && declaringType.library.isDom) { |
| 1047 world.gen.corejs.ensureTypeNameOf(); | 1037 world.gen.corejs.ensureTypeNameOf(); |
| 1048 } | 1038 } |
| 1049 | 1039 |
| 1040 var code = '${target.code}.$jsname($argsString)'; | |
| 1050 return new Value(inferredResult, code, node.span); | 1041 return new Value(inferredResult, code, node.span); |
| 1051 } | 1042 } |
| 1052 | 1043 |
| 1053 Value _invokeConstructor(MethodGenerator context, Node node, | 1044 Value _invokeConstructor(MethodGenerator context, Node node, |
| 1054 Value target, Arguments args, argsString) { | 1045 Value target, Arguments args, argsString) { |
| 1055 declaringType.markUsed(); | 1046 declaringType.markUsed(); |
| 1056 | 1047 |
| 1057 String ctor = constructorName; | 1048 String ctor = constructorName; |
| 1058 if (ctor != '') ctor = '.${ctor}\$ctor'; | 1049 if (ctor != '') ctor = '.${ctor}\$ctor'; |
| 1059 | 1050 |
| 1051 final span = node != null ? node.span : target.span; | |
| 1060 if (!target.isType) { | 1052 if (!target.isType) { |
| 1061 // initializer call to another constructor | 1053 // initializer call to another constructor |
| 1062 var code = '${declaringType.nativeName}${ctor}.call($argsString)'; | 1054 var code = '${declaringType.nativeName}${ctor}.call($argsString)'; |
| 1063 return new Value(target.type, code, node.span); | 1055 return new Value(target.type, code, span); |
| 1064 } else { | 1056 } else { |
| 1065 var code = 'new ${declaringType.nativeName}${ctor}($argsString)'; | 1057 // Start of abstract interpretation to replace const hacks goes here |
| 1066 | |
| 1067 // TODO(jmesserly): using the "node" here feels really hacky | 1058 // TODO(jmesserly): using the "node" here feels really hacky |
| 1068 if (isConst && node is NewExpression && node.dynamic.isConst) { | 1059 if (isConst && node is NewExpression && node.dynamic.isConst) { |
| 1069 return _invokeConstConstructor(node, code, target, args); | 1060 // !!!!!! Egregious hack !!!!!!! |
| 1061 if (isNative || declaringType.name == 'JSSyntaxRegExp') { | |
| 1062 // check that all args are const? | |
| 1063 var code = 'new ${declaringType.nativeName}${ctor}($argsString)'; | |
| 1064 return world.gen.globalForConst(new Value(target.type, code, span), | |
| 1065 [args.values]); | |
| 1066 } | |
| 1067 var newType = declaringType; | |
| 1068 var newObject = new ObjectValue(true, newType, span); | |
| 1069 newObject.initFields(); | |
| 1070 _evalConstConstructor(newObject, args); | |
| 1071 // ??? Does args.values include named args??? | |
|
Jennifer Messerly
2012/01/09 20:45:35
add a TODO here?
jimhug
2012/01/09 21:12:17
I've figured out the answer - so I'll remove the c
| |
| 1072 return world.gen.globalForConst(newObject, [args.values]); | |
| 1070 } else { | 1073 } else { |
| 1071 final span = node != null ? node.span : target.span; | 1074 var code = 'new ${declaringType.nativeName}${ctor}($argsString)'; |
| 1072 return new Value(target.type, code, span); | 1075 return new Value(target.type, code, span); |
| 1073 } | 1076 } |
| 1074 } | 1077 } |
| 1075 } | 1078 } |
| 1076 | 1079 |
| 1077 /** | 1080 _evalConstConstructor(Value newObject, Arguments args) { |
| 1078 * Special handling for const constructors so that so that: | 1081 declaringType.markUsed(); |
| 1079 * [: const B() === const B.a(0, 1) === const B.b(0) :] | 1082 var generator = new MethodGenerator(this, null); |
| 1080 * where: [: | 1083 generator.evalBody(newObject, args); |
| 1081 * class A { | |
| 1082 * final int x; | |
| 1083 * const A(this.x); | |
| 1084 * } | |
| 1085 * | |
| 1086 * class B { | |
| 1087 * final int y; | |
| 1088 * const B() : y = 0, super(1); | |
| 1089 * const B.a(this.y, x) : super(x); | |
| 1090 * const B.b(v) : this.a(v, 1); | |
| 1091 * } | |
| 1092 * :] | |
| 1093 */ | |
| 1094 Value _invokeConstConstructor( | |
| 1095 Node node, String code, Value target, Arguments args) { | |
| 1096 // TODO(jimhug): This should be low-hanging fruit for abstract eval! | |
| 1097 | |
| 1098 // Statically compute the actual value for every field in the const object. | |
| 1099 final fields = new Map<String, Value>(); | |
| 1100 | |
| 1101 // First deduce the value for fields initialized with the 'this.x' syntax. | |
| 1102 for (int i = 0; i < parameters.length; i++) { | |
| 1103 var param = parameters[i]; | |
| 1104 if (param.isInitializer) { | |
| 1105 var value = null; | |
| 1106 if (i < args.length) { | |
| 1107 value = args.values[i]; | |
| 1108 } else { // named arguments | |
| 1109 value = args.getValue(param.name); | |
| 1110 if (value == null) { | |
| 1111 value = param.value; | |
| 1112 } | |
| 1113 } | |
| 1114 fields[param.name] = value.constValue; | |
| 1115 } | |
| 1116 } | |
| 1117 | |
| 1118 // Then evaluate initializer expressions. | |
| 1119 if (definition.initializers != null) { | |
| 1120 // Introduce a temporary scope to evaluate initializers, which defines the | |
| 1121 // value for any formal argument as it's constant expression value. | |
| 1122 generator._pushBlock(); | |
| 1123 for (int j = 0; j < definition.formals.length; j++) { | |
| 1124 var name = definition.formals[j].name.name; | |
| 1125 var value = null; | |
| 1126 if (j < args.length) { | |
| 1127 value = args.values[j]; | |
| 1128 } else { // named arguments | |
| 1129 value = args.getValue(parameters[j].name); | |
| 1130 if (value == null) { | |
| 1131 value = parameters[j].value; | |
| 1132 } | |
| 1133 } | |
| 1134 generator._scope._vars[name] = value; | |
| 1135 } | |
| 1136 | |
| 1137 // TODO(jmesserly): unify with initializer code in gen.dart | |
| 1138 for (var init in definition.initializers) { | |
| 1139 if (init is CallExpression) { | |
| 1140 // Construct arguments to delegate and invoke it. | |
| 1141 var delegateArgs = generator._makeArgs(init.arguments); | |
| 1142 var value = initDelegate.invoke( | |
| 1143 generator, node, target, delegateArgs); | |
| 1144 if (init.target is ThisExpression) { | |
| 1145 // Redirection: Use directly the delegate result. E.g. so that | |
| 1146 // const B.b(0) === const B.a(0, 1) | |
| 1147 return value; | |
| 1148 } else { | |
| 1149 // Super-call: embed the value the super class fields. | |
| 1150 value = value.constValue; | |
| 1151 for (var fname in value.fields.getKeys()) { | |
| 1152 fields[fname] = value.fields[fname]; | |
| 1153 } | |
| 1154 } | |
| 1155 } else { | |
| 1156 // Normal field initializer assignment. | |
| 1157 BinaryExpression assign = init; | |
| 1158 var x = assign.x; // DotExpression or VarExpression | |
| 1159 var fname = x.name.name; | |
| 1160 var val = generator.visitValue(assign.y); | |
| 1161 if (!val.isConst) { | |
| 1162 world.error('invalid non-const initializer in const constructor', | |
| 1163 assign.y.span); | |
| 1164 } | |
| 1165 fields[fname] = val; | |
| 1166 } | |
| 1167 } | |
| 1168 | |
| 1169 generator._popBlock(); | |
| 1170 } | |
| 1171 | |
| 1172 // Add default values only if they weren't overriden in the constructor. | |
| 1173 for (var f in declaringType.members.getValues()) { | |
| 1174 if (f is FieldMember && !f.isStatic && !fields.containsKey(f.name)) { | |
| 1175 if (!f.isFinal) { | |
| 1176 world.error('const class "${declaringType.name}" has non-final ' | |
| 1177 + 'field "${f.name}"', f.span); | |
| 1178 } | |
| 1179 if (f.value != null) { | |
| 1180 fields[f.name] = f.computeValue(); | |
| 1181 } | |
| 1182 } | |
| 1183 } | |
| 1184 | |
| 1185 return world.gen.globalForConst( | |
| 1186 new ObjectValue(fields, true, target.type, code, node.span), | |
| 1187 args.values); | |
| 1188 } | 1084 } |
| 1189 | 1085 |
| 1190 | |
| 1191 Value _invokeBuiltin(MethodGenerator context, Node node, Value target, | 1086 Value _invokeBuiltin(MethodGenerator context, Node node, Value target, |
| 1192 Arguments args, argsCode, bool isDynamic) { | 1087 Arguments args, argsCode, bool isDynamic) { |
| 1193 var allConst = target.isConst && args.values.every((arg) => arg.isConst); | |
| 1194 | |
| 1195 // Handle some fast paths for Number, String, List and DOM. | 1088 // Handle some fast paths for Number, String, List and DOM. |
| 1196 if (declaringType.isNum) { | 1089 if (declaringType.isNum) { |
| 1197 // TODO(jimhug): This fails in bad ways when argsCode[1] is not num. | 1090 // TODO(jimhug): This fails in bad ways when argsCode[1] is not num. |
| 1198 // TODO(jimhug): What about null? | 1091 // TODO(jimhug): What about null? |
| 1199 if (!allConst) { | 1092 var code; |
| 1200 var code; | 1093 if (name == ':negate') { |
| 1201 if (name == ':negate') { | 1094 code = '-${target.code}'; |
| 1202 code = '-${target.code}'; | 1095 } else if (name == ':bit_not') { |
| 1203 } else if (name == ':bit_not') { | 1096 code = '~${target.code}'; |
| 1204 code = '~${target.code}'; | 1097 } else if (name == ':truncdiv' || name == ':mod') { |
| 1205 } else if (name == ':truncdiv' || name == ':mod') { | 1098 world.gen.corejs.useOperator(name); |
| 1206 world.gen.corejs.useOperator(name); | 1099 code = '$jsname(${target.code}, ${argsCode[0]})'; |
| 1207 code = '$jsname(${target.code}, ${argsCode[0]})'; | 1100 } else { |
| 1208 } else { | 1101 var op = TokenKind.rawOperatorFromMethod(name); |
| 1209 var op = TokenKind.rawOperatorFromMethod(name); | 1102 code = '${target.code} $op ${argsCode[0]}'; |
| 1210 code = '${target.code} $op ${argsCode[0]}'; | 1103 } |
| 1211 } | |
| 1212 | 1104 |
| 1213 return new Value(inferredResult, code, node.span); | 1105 return new Value(inferredResult, code, node.span); |
| 1214 } else { | |
| 1215 var value; | |
| 1216 num val0, val1, ival0, ival1; | |
| 1217 val0 = target.dynamic.actualValue; | |
| 1218 ival0 = val0.toInt(); | |
| 1219 if (args.values.length > 0) { | |
| 1220 val1 = args.values[0].dynamic.actualValue; | |
| 1221 ival1 = val1.toInt(); | |
| 1222 } | |
| 1223 switch (name) { | |
| 1224 case ':negate': value = -val0; break; | |
| 1225 case ':add': value = val0 + val1; break; | |
| 1226 case ':sub': value = val0 - val1; break; | |
| 1227 case ':mul': value = val0 * val1; break; | |
| 1228 case ':div': value = val0 / val1; break; | |
| 1229 case ':truncdiv': value = val0 ~/ val1; break; | |
| 1230 case ':mod': value = val0 % val1; break; | |
| 1231 case ':eq': value = val0 == val1; break; | |
| 1232 case ':lt': value = val0 < val1; break; | |
| 1233 case ':gt': value = val0 > val1; break; | |
| 1234 case ':lte': value = val0 <= val1; break; | |
| 1235 case ':gte': value = val0 >= val1; break; | |
| 1236 case ':ne': value = val0 != val1; break; | |
| 1237 | |
| 1238 // Note: unfortunatelly bit operations fail on doubles in dartvm | |
| 1239 case ':bit_not': value = (~ival0).toDouble(); break; | |
| 1240 case ':bit_or': value = (ival0 | ival1).toDouble(); break; | |
| 1241 case ':bit_xor': value = (ival0 ^ ival1).toDouble(); break; | |
| 1242 case ':bit_and': value = (ival0 & ival1).toDouble(); break; | |
| 1243 case ':shl': value = (ival0 << ival1).toDouble(); break; | |
| 1244 case ':sar': value = (ival0 >> ival1).toDouble(); break; | |
| 1245 case ':shr': value = (ival0 >>> ival1).toDouble(); break; | |
| 1246 } | |
| 1247 if (inferredResult.isInt) { | |
| 1248 return Value.fromInt(value.toInt(), node.span); | |
| 1249 } else if (inferredResult.isDouble) { | |
| 1250 return Value.fromDouble(value.toDouble(), node.span); | |
| 1251 } else if (inferredResult.isNum) { | |
| 1252 // TODO(jimhug): Number type system is flawed here... | |
| 1253 return Value.fromDouble(value.toDouble(), node.span); | |
| 1254 } else if (inferredResult.isBool) { | |
| 1255 return Value.fromBool(value, node.span); | |
| 1256 } else { | |
| 1257 world.internalError( | |
| 1258 'unsupported const result type "${inferredResult.name}"', | |
| 1259 node.span); | |
| 1260 } | |
| 1261 } | |
| 1262 } else if (declaringType.isString) { | 1106 } else if (declaringType.isString) { |
| 1263 if (name == ':index') { | 1107 if (name == ':index') { |
| 1264 // Note: this could technically propagate constness, but that's not | |
| 1265 // specified explicitly and the VM doesn't do that. | |
| 1266 return new Value(declaringType, '${target.code}[${argsCode[0]}]', | 1108 return new Value(declaringType, '${target.code}[${argsCode[0]}]', |
| 1267 node.span); | 1109 node.span); |
| 1268 } else if (name == ':add') { | 1110 } else if (name == ':add') { |
| 1269 if (allConst) { | |
| 1270 final value = target.dynamic.actualValue + | |
| 1271 args.values[0].dynamic.actualValue; | |
| 1272 return Value.fromString(value, node.span); | |
| 1273 } | |
| 1274 | |
| 1275 return new Value(declaringType, '${target.code} + ${argsCode[0]}', | 1111 return new Value(declaringType, '${target.code} + ${argsCode[0]}', |
| 1276 node.span); | 1112 node.span); |
| 1277 } | 1113 } |
| 1278 } else if (declaringType.isNative) { | 1114 } else if (declaringType.isNative) { |
| 1279 if (name == ':index') { | 1115 if (name == ':index') { |
| 1280 // Note: this could technically propagate constness, but that's not | |
| 1281 // specified explicitly and the VM doesn't do that. | |
| 1282 return | 1116 return |
| 1283 new Value(returnType, '${target.code}[${argsCode[0]}]', node.span); | 1117 new Value(returnType, '${target.code}[${argsCode[0]}]', node.span); |
| 1284 } else if (name == ':setindex') { | 1118 } else if (name == ':setindex') { |
| 1285 return new Value(returnType, | 1119 return new Value(returnType, |
| 1286 '${target.code}[${argsCode[0]}] = ${argsCode[1]}', node.span); | 1120 '${target.code}[${argsCode[0]}] = ${argsCode[1]}', node.span); |
| 1287 } | 1121 } |
| 1288 } | 1122 } |
| 1289 | 1123 |
| 1290 // TODO(jimhug): Optimize null on lhs as well. | 1124 // TODO(jimhug): Optimize null on lhs as well. |
| 1291 if (name == ':eq' || name == ':ne') { | 1125 if (name == ':eq' || name == ':ne') { |
| 1292 final op = name == ':eq' ? '==' : '!='; | 1126 final op = name == ':eq' ? '==' : '!='; |
| 1293 | 1127 |
| 1294 if (name == ':ne') { | 1128 if (name == ':ne') { |
| 1295 // Ensure == is generated. | 1129 // Ensure == is generated. |
| 1296 target.invoke(context, ':eq', node, args, isDynamic); | 1130 target.invoke(context, ':eq', node, args, isDynamic); |
| 1297 } | 1131 } |
| 1298 | 1132 |
| 1299 if (allConst) { | |
| 1300 var val0 = target.dynamic.actualValue; | |
| 1301 var val1 = args.values[0].dynamic.actualValue; | |
| 1302 var newVal = name == ':eq' ? val0 == val1 : val0 != val1; | |
| 1303 return Value.fromBool(newVal, node.span); | |
| 1304 } | |
| 1305 // Optimize test when null is on the rhs. | 1133 // Optimize test when null is on the rhs. |
| 1306 if (argsCode[0] == 'null') { | 1134 if (argsCode[0] == 'null') { |
| 1307 return new Value(inferredResult, '${target.code} $op null', node.span); | 1135 return new Value(inferredResult, '${target.code} $op null', node.span); |
| 1308 } else if (target.type.isNum || target.type.isString) { | 1136 } else if (target.type.isNum || target.type.isString) { |
| 1309 // TODO(jimhug): Maybe check rhs. | 1137 // TODO(jimhug): Maybe check rhs. |
| 1310 return new Value(inferredResult, '${target.code} $op ${argsCode[0]}', | 1138 return new Value(inferredResult, '${target.code} $op ${argsCode[0]}', |
| 1311 node.span); | 1139 node.span); |
| 1312 } | 1140 } |
| 1313 world.gen.corejs.useOperator(name); | 1141 world.gen.corejs.useOperator(name); |
| 1142 // TODO(jimhug): Should be able to use faster path sometimes here! | |
| 1314 return new Value(inferredResult, | 1143 return new Value(inferredResult, |
| 1315 '$jsname(${target.code}, ${argsCode[0]})', node.span); | 1144 '$jsname(${target.code}, ${argsCode[0]})', node.span); |
| 1316 } | 1145 } |
| 1317 | 1146 |
| 1318 if (isCallMethod) { | 1147 if (isCallMethod) { |
| 1319 declaringType.markUsed(); | 1148 declaringType.markUsed(); |
| 1320 return new Value(inferredResult, | 1149 return new Value(inferredResult, |
| 1321 '${target.code}(${Strings.join(argsCode, ", ")})', node.span); | 1150 '${target.code}(${Strings.join(argsCode, ", ")})', node.span); |
| 1322 } | 1151 } |
| 1323 | 1152 |
| 1324 if (name == ':index') { | 1153 if (name == ':index') { |
| 1325 world.gen.corejs.useIndex = true; | 1154 world.gen.corejs.useIndex = true; |
| 1326 } else if (name == ':setindex') { | 1155 } else if (name == ':setindex') { |
| 1327 world.gen.corejs.useSetIndex = true; | 1156 world.gen.corejs.useSetIndex = true; |
| 1328 } | 1157 } |
| 1329 | 1158 |
| 1330 // Fall back to normal method invocation. | 1159 // Fall back to normal method invocation. |
| 1331 var argsString = Strings.join(argsCode, ', '); | 1160 var argsString = Strings.join(argsCode, ', '); |
| 1332 return new Value(inferredResult, '${target.code}.$jsname($argsString)', | 1161 return new Value(inferredResult, '${target.code}.$jsname($argsString)', |
| 1333 node.span); | 1162 node.span); |
| 1334 } | 1163 } |
| 1335 | 1164 |
| 1336 resolve() { | 1165 resolve() { |
| 1337 // TODO(jimhug): cut-and-paste-and-edit from Field.resolve | 1166 // TODO(jimhug): work through side-by-side with spec |
| 1338 isStatic = declaringType.isTop; | 1167 isStatic = declaringType.isTop; |
| 1339 isConst = false; | 1168 isConst = false; |
| 1340 isFactory = false; | 1169 isFactory = false; |
| 1341 isAbstract = !declaringType.isClass; | 1170 isAbstract = !declaringType.isClass; |
| 1342 if (definition.modifiers != null) { | 1171 if (definition.modifiers != null) { |
| 1343 for (var mod in definition.modifiers) { | 1172 for (var mod in definition.modifiers) { |
| 1344 if (mod.kind == TokenKind.STATIC) { | 1173 if (mod.kind == TokenKind.STATIC) { |
| 1345 if (isStatic) { | 1174 if (isStatic) { |
| 1346 world.error('duplicate static modifier', mod.span); | 1175 world.error('duplicate static modifier', mod.span); |
| 1347 } | 1176 } |
| 1348 isStatic = true; | 1177 isStatic = true; |
| 1349 } else if (isConstructor && mod.kind == TokenKind.CONST) { | 1178 } else if (isConstructor && mod.kind == TokenKind.CONST) { |
| 1350 if (isConst) { | 1179 if (isConst) { |
| 1351 world.error('duplicate const modifier', mod.span); | 1180 world.error('duplicate const modifier', mod.span); |
| 1352 } | 1181 } |
| 1182 if (isFactory) { | |
| 1183 world.error('const factory not allowed', mod.span); | |
| 1184 } | |
| 1353 isConst = true; | 1185 isConst = true; |
| 1354 } else if (mod.kind == TokenKind.FACTORY) { | 1186 } else if (mod.kind == TokenKind.FACTORY) { |
| 1355 if (isFactory) { | 1187 if (isFactory) { |
| 1356 world.error('duplicate factory modifier', mod.span); | 1188 world.error('duplicate factory modifier', mod.span); |
| 1357 } | 1189 } |
| 1190 if (isConst) { | |
| 1191 world.error('const factory not allowed', mod.span); | |
| 1192 } | |
| 1193 if (isStatic) { | |
| 1194 world.error('static factory not allowed', mod.span); | |
| 1195 } | |
| 1358 isFactory = true; | 1196 isFactory = true; |
| 1359 } else if (mod.kind == TokenKind.ABSTRACT) { | 1197 } else if (mod.kind == TokenKind.ABSTRACT) { |
| 1360 if (isAbstract) { | 1198 if (isAbstract) { |
| 1361 if (declaringType.isClass) { | 1199 if (declaringType.isClass) { |
| 1362 world.error('duplicate abstract modifier', mod.span); | 1200 world.error('duplicate abstract modifier', mod.span); |
| 1363 } else if (!isCallMethod) { | 1201 } else if (!isCallMethod) { |
| 1364 world.error('abstract modifier not allowed on interface members', | 1202 world.error('abstract modifier not allowed on interface members', |
| 1365 mod.span); | 1203 mod.span); |
| 1366 } | 1204 } |
| 1367 } | 1205 } |
| (...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1752 } | 1590 } |
| 1753 | 1591 |
| 1754 void forEach(void f(Member member)) { | 1592 void forEach(void f(Member member)) { |
| 1755 factories.forEach((_, Map constructors) { | 1593 factories.forEach((_, Map constructors) { |
| 1756 constructors.forEach((_, Member member) { | 1594 constructors.forEach((_, Member member) { |
| 1757 f(member); | 1595 f(member); |
| 1758 }); | 1596 }); |
| 1759 }); | 1597 }); |
| 1760 } | 1598 } |
| 1761 } | 1599 } |
| OLD | NEW |