Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Side by Side Diff: frog/gen.dart

Issue 9146001: continued cleanup of initialization and Value (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: touchups to comments Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | frog/member.dart » ('j') | frog/member.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /** 5 /**
6 * Top level generator object for writing code and keeping track of 6 * Top level generator object for writing code and keeping track of
7 * dependencies. 7 * dependencies.
8 * 8 *
9 * Should have two compilation models, but only one implemented so far. 9 * Should have two compilation models, but only one implemented so far.
10 * 10 *
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 var key = "${field.declaringType.jsname}.${field.jsname}"; 137 var key = "${field.declaringType.jsname}.${field.jsname}";
138 var ret = globals[key]; 138 var ret = globals[key];
139 if (ret === null) { 139 if (ret === null) {
140 ret = new GlobalValue(exp.type, exp.code, field.isFinal, field, null, 140 ret = new GlobalValue(exp.type, exp.code, field.isFinal, field, null,
141 exp, exp.span, dependencies); 141 exp, exp.span, dependencies);
142 globals[key] = ret; 142 globals[key] = ret;
143 } 143 }
144 return ret; 144 return ret;
145 } 145 }
146 146
147 GlobalValue globalForConst(EvaluatedValue exp, List<Value> dependencies) { 147 GlobalValue globalForConst(Value exp, List<Value> dependencies) {
148 // Include type name to ensure unique constants - this matches 148 // Include type name to ensure unique constants - this matches
149 // the code above that includes the type name for static fields. 149 // the code above that includes the type name for static fields.
150 var key = exp.type.jsname + ':' + exp.code; 150 var key = exp.type.jsname + ':' + exp.code;
151 var ret = globals[key]; 151 var ret = globals[key];
152 if (ret === null) { 152 if (ret === null) {
153 var name = "const\$${globals.length}"; 153 // another egregious hack!!!
154 var ns = globals.length.toString();
155 while (ns.length < 4) ns = '0' + ns;
156 var name = "const\$${ns}";
154 ret = new GlobalValue(exp.type, name, true, null, name, exp, 157 ret = new GlobalValue(exp.type, name, true, null, name, exp,
155 exp.span, dependencies); 158 exp.span, dependencies);
156 globals[key] = ret; 159 globals[key] = ret;
157 } 160 }
158 assert(ret.type == exp.type); 161 assert(ret.type == exp.type);
159 return ret; 162 return ret;
160 } 163 }
161 164
162 writeTypes(Library lib) { 165 writeTypes(Library lib) {
163 if (lib.isWritten) return; 166 if (lib.isWritten) return;
(...skipping 758 matching lines...) Expand 10 before | Expand all | Expand 10 after
922 925
923 void freeTemp(Value value) { 926 void freeTemp(Value value) {
924 if (_usedTemps.remove(value.code)) { 927 if (_usedTemps.remove(value.code)) {
925 _freeTemps.add(value.code); 928 _freeTemps.add(value.code);
926 } else { 929 } else {
927 world.internalError( 930 world.internalError(
928 'tried to free unused value or non-temp "${value.code}"'); 931 'tried to free unused value or non-temp "${value.code}"');
929 } 932 }
930 } 933 }
931 934
935 /*
936 run1(Value thisValue, Arguments args) {
937 // Use some sort of key to do a lookup
938
939
940 }*/
941
932 run() { 942 run() {
933 if (method.isGenerated) return; 943 if (method.isGenerated) return;
934 944
935 // This avoids any attempts to infer across recursion. 945 // This avoids any attempts to infer across recursion.
936 method.isGenerated = true; 946 method.isGenerated = true;
937 method.generator = this; 947 method.generator = this;
938 948
939 writeBody(); 949 // Create most generic possible call for this method.
950 var thisObject;
951 if (method.isConstructor) {
952 thisObject = new ObjectValue(false, method.declaringType, method.span);
953 thisObject.initFields();
954 } else {
955 thisObject = new Value(method.declaringType, 'this', null);
956 }
957 var values = [];
958 for (var p in method.parameters) {
959 values.add(new Value(p.type, p.name, null));
960 }
961 var args = new Arguments(null, values);
962
963 evalBody(thisObject, args);
940 964
941 if (method.definition.nativeBody != null) { 965 if (method.definition.nativeBody != null) {
942 // Throw away the code--it was just used for tree shaking purposes. 966 // Throw away the code--it was just used for tree shaking purposes.
943 writer = new CodeWriter(); 967 writer = new CodeWriter();
944 if (method.definition.nativeBody == '') { 968 if (method.definition.nativeBody == '') {
945 method.generator = null; 969 method.generator = null;
946 } else { 970 } else {
947 _paramCode = map(method.parameters, (p) => p.name); 971 _paramCode = map(method.parameters, (p) => p.name);
948 writer.write(method.definition.nativeBody); 972 writer.write(method.definition.nativeBody);
949 } 973 }
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
1080 } 1104 }
1081 1105
1082 optNames.addAll(optValues); 1106 optNames.addAll(optValues);
1083 var optional = "['" + Strings.join(optNames, "', '") + "']"; 1107 var optional = "['" + Strings.join(optNames, "', '") + "']";
1084 defWriter.writeln('${start}${meth.jsname}.\$optional = $optional'); 1108 defWriter.writeln('${start}${meth.jsname}.\$optional = $optional');
1085 } 1109 }
1086 } 1110 }
1087 } 1111 }
1088 } 1112 }
1089 1113
1090 writeBody() { 1114 _initField(ObjectValue newObject, String name, Value value, SourceSpan span) {
1091 var initializers = null; 1115 var field = method.declaringType.getMember(name);
1092 var initializedFields = null; // to check that final fields are initialized 1116 if (field == null) {
1093 var allMembers = null; 1117 world.error('bad initializer - no matching field', span);
1094 if (method.isConstructor) { 1118 }
1095 initializers = []; 1119 if (!field.isField) {
1096 initializedFields = new Set(); 1120 world.error('"this.${name}" does not refer to a field', span);
1097 allMembers = world.gen._orderValues(method.declaringType.getAllMembers()); 1121 }
1098 for (var f in allMembers) { 1122 return newObject.setField(field, value, duringInit: true);
1099 if (f.isField && !f.isStatic) { 1123 }
1100 var cv = f.computeValue(); 1124
1101 if (cv != null) { 1125 evalBody(Value newObject, Arguments args) {
1102 initializers.add('this.${f.jsname} = ${cv.code}'); 1126 bool fieldsSet = false;
1103 initializedFields.add(f.name); 1127 if (method.isNative && method.isConstructor && newObject is ObjectValue) {
1104 } 1128 newObject.dynamic.seenNativeInitializer = true;
Jennifer Messerly 2012/01/09 20:45:35 I see you've embraced the Dart "cast" syntax ;)
1129 }
1130 // Collects parameters for writing signature in the future.
1131 _paramCode = [];
1132 for (int i = 0; i < method.parameters.length; i++) {
1133 var p = method.parameters[i];
1134 Value currentArg = null;
1135 // TODO(jimhug): bareCount is O(N)
Jennifer Messerly 2012/01/09 20:45:35 It gets cached though. So it's amortized O(1)
jimhug 2012/01/09 21:12:17 I'd missed that! Thanks, I'll remove the comment.
1136 if (i < args.bareCount) {
1137 currentArg = args.values[i];
1138 } else {
1139 // Handle named or missing arguments
1140 currentArg = args.getValue(p.name);
1141 if (currentArg === null) {
1142 // Ensure default value for param has been generated
1143 p.genValue(method, method.generator);
1144 currentArg = p.value;
1145 }
1146 }
1147
1148 if (p.isInitializer) {
1149 _paramCode.add(p.name);
1150 fieldsSet = true;
1151 _initField(newObject, p.name, currentArg, p.definition.span);
1152 } else {
1153 var paramValue = _scope.declareParameter(p);
1154 _paramCode.add(paramValue.code);
1155 if (newObject != null && newObject.isConst) {
1156 _scope._vars[p.name] = currentArg;
1105 } 1157 }
1106 } 1158 }
1107 } 1159 }
1108 1160
1109 // Collects parameters for writing signature in the future. 1161 var initializerCall = null;
1110 _paramCode = []; 1162 final declaredInitializers = method.definition.initializers;
1111 for (var p in method.parameters) { 1163 if (declaredInitializers != null) {
1112 if (initializers != null && p.isInitializer) { 1164 for (var init in declaredInitializers) {
1113 var field = method.declaringType.getMember(p.name); 1165 if (init is CallExpression) {
1114 if (field == null) { 1166 if (initializerCall != null) {
1115 world.error('bad this parameter - no matching field', 1167 world.error('only one initializer redirecting call is allowed',
1116 p.definition.span); 1168 init.span);
1169 }
1170 initializerCall = init;
1171 } else if (init is BinaryExpression
1172 && TokenKind.kindFromAssign(init.op.kind) == 0) {
1173 var left = init.x;
1174 if (!(left is DotExpression && left.self is ThisExpression
1175 || left is VarExpression)) {
1176 world.error('invalid left side of initializer', left.span);
1177 continue;
1178 }
1179 // TODO(jmesserly): eval right side of initializers in static
1180 // context, so "this." is not in scope
1181 var initValue = visitValue(init.y);
1182 fieldsSet = true;
1183 _initField(newObject, left.name.name, initValue, left.span);
1184 } else {
1185 world.error('invalid initializer', init.span);
1117 } 1186 }
1118 if (!field.isField) {
1119 world.error('"this.${p.name}" does not refer to a field',
1120 p.definition.span);
1121 }
1122 var paramValue = new Value(field.returnType, p.name,
1123 p.definition.span, false);
1124 _paramCode.add(paramValue.code);
1125
1126 initializers.add('this.${field.jsname} = ${paramValue.code};');
1127 initializedFields.add(p.name);
1128 } else {
1129 var paramValue = _scope.declareParameter(p);
1130 _paramCode.add(paramValue.code);
1131 } 1187 }
1132 } 1188 }
1133 1189
1134 var body = method.definition.body;
1135
1136 if (body == null && !method.isConstructor && !method.isNative) {
1137 world.error('unexpected empty body for ${method.name}',
1138 method.definition.span);
1139 }
1140
1141 var initializerCall = null;
1142 final declaredInitializers = method.definition.initializers;
1143 if (initializers != null) {
1144 for (var i in initializers) {
1145 writer.writeln(i);
1146 }
1147 if (declaredInitializers != null) {
1148 for (var init in declaredInitializers) {
1149 // TODO(jmesserly): eval right side of initializers in static context,
1150 // so "this." is not in scope
1151 // TODO(jmesserly): this has diverged from code in member.dart,
1152 // _invokeConstConstructor. Need to unify these paths.
1153 // TODO(jimhug): Lot's of correctness to verify here.
1154 if (init is CallExpression) {
1155 if (initializerCall != null) {
1156 world.error('only one initializer redirecting call is allowed',
1157 init.span);
1158 }
1159 initializerCall = init;
1160 } else if (init is BinaryExpression
1161 && TokenKind.kindFromAssign(init.op.kind) == 0) {
1162
1163 var left = init.x;
1164 if (!(left is DotExpression && left.self is ThisExpression
1165 || left is VarExpression)) {
1166 world.error('invalid left side of initializer', left.span);
1167 continue;
1168 }
1169
1170 var f = method.declaringType.getMember(left.name.name);
1171 if (f == null) {
1172 world.error('bad initializer - no matching field', left.span);
1173 continue;
1174 } else if (!f.isField) {
1175 world.error('"${left.name.name}" does not refer to a field',
1176 left.span);
1177 continue;
1178 }
1179
1180 initializedFields.add(f.name);
1181 writer.writeln('this.${f.jsname} = ${visitValue(init.y).code};');
1182 } else {
1183 world.error('invalid initializer', init.span);
1184 }
1185 }
1186 }
1187 writer.comment('// Initializers done');
1188 }
1189
1190 if (method.isConstructor && initializerCall == null && !method.isNative) { 1190 if (method.isConstructor && initializerCall == null && !method.isNative) {
1191 var parentType = method.declaringType.parent; 1191 var parentType = method.declaringType.parent;
1192 if (parentType != null && !parentType.isObject) { 1192 if (parentType != null && !parentType.isObject) {
1193 // TODO(jmesserly): we could omit this if all supertypes are using 1193 // TODO(jmesserly): we could omit this if all supertypes are using
1194 // default constructors. 1194 // default constructors.
1195 initializerCall = new CallExpression( 1195 initializerCall = new CallExpression(
1196 new SuperExpression(method.span), [], method.span); 1196 new SuperExpression(method.span), [], method.span);
1197 } 1197 }
1198 } 1198 }
1199 1199
1200 if (initializerCall != null) { 1200 if (method.isConstructor && newObject is ObjectValue) {
1201 var target = _writeInitializerCall(initializerCall); 1201 var fields = newObject.dynamic.fields;
1202 if (!target.isSuper) { 1202 for (var field in fields.getKeys()) {
1203 // when calling another constructor on the same class 1203 var value = fields[field];
1204 // no other initialization is allowed 1204 if (value !== null) {
1205 if (initializers.length > 0) { 1205 writer.writeln('this.${field.jsname} = ${value.code};');
1206 for (var p in method.parameters) {
1207 if (p.isInitializer) {
1208 world.error(
1209 'no initialization allowed on redirecting constructors',
1210 p.definition.span);
1211 break;
1212 }
1213 }
1214 }
1215 if (declaredInitializers != null && declaredInitializers.length > 1) {
1216 var init = declaredInitializers[0] == initializerCall
1217 ? declaredInitializers[1] : declaredInitializers[0];
1218 world.error(
1219 'no initialization allowed on redirecting constructors',
1220 init.span);
1221 }
1222 initializedFields = null;
1223 }
1224 }
1225
1226 // check that initialization was correct
1227 if (initializedFields != null) {
1228 for (var member in allMembers) {
1229 if (member.isField && member.isFinal && !member.isStatic
1230 && !method.isNative && !initializedFields.contains(member.name)) {
1231 world.error('Field "${member.name}" is final and was not initialized',
1232 method.definition.span);
1233 } 1206 }
1234 } 1207 }
1235 } 1208 }
1236 1209
1237 visitStatementsInBlock(body); 1210 // TODO(jimhug): Doing this call last does not match spec.
1211 if (initializerCall != null) {
1212 evalInitializerCall(newObject, initializerCall, fieldsSet);
1213 }
1214
1215 if (method.isConstructor && newObject !== null && newObject.isConst) {
1216 newObject.validateInitialized(method.span);
1217 } else if (method.isConstructor) {
1218 var fields = newObject.dynamic.fields;
1219 for (var field in fields.getKeys()) {
1220 var value = fields[field];
1221 if (value === null && field.isFinal &&
1222 field.declaringType == method.declaringType &&
1223 !newObject.dynamic.seenNativeInitializer) {
1224 world.error('uninitialized final field "${field.name}"',
1225 field.span, method.span);
1226 }
1227 }
1228 }
1229
1230 var body = method.definition.body;
1231
1232 if (body === null) {
1233 // TODO(jimhug): Move check into resolve on method.
1234 if (!method.isConstructor && !method.isNative) {
1235 world.error('unexpected empty body for ${method.name}',
1236 method.definition.span);
1237 }
1238 } else {
1239 visitStatementsInBlock(body);
1240 }
1238 } 1241 }
1239 1242
1240 /** 1243 evalInitializerCall(ObjectValue newObject, CallExpression node,
1241 * Calls another constructor (super, super.name, this, this.name). Returns 1244 [bool fieldsSet = false]) {
1242 * the value of the target expression.
1243 */
1244 Value _writeInitializerCall(CallExpression node) {
1245 String contructorName = ''; 1245 String contructorName = '';
1246 var targetExp = node.target; 1246 var targetExp = node.target;
1247 if (targetExp is DotExpression) { 1247 if (targetExp is DotExpression) {
1248 DotExpression dot = targetExp; 1248 DotExpression dot = targetExp;
1249 targetExp = dot.self; 1249 targetExp = dot.self;
1250 contructorName = dot.name.name; 1250 contructorName = dot.name.name;
1251 } 1251 }
1252 1252
1253 Type targetType = null;
1253 var target = null; 1254 var target = null;
1254 if (targetExp is SuperExpression) { 1255 if (targetExp is SuperExpression) {
1256 targetType = method.declaringType.parent;
1255 target = _makeSuperValue(targetExp); 1257 target = _makeSuperValue(targetExp);
1256 } else if (targetExp is ThisExpression) { 1258 } else if (targetExp is ThisExpression) {
1259 targetType = method.declaringType;
1257 target = _makeThisValue(targetExp); 1260 target = _makeThisValue(targetExp);
1261 if (fieldsSet) {
1262 world.error('no initialization allowed with redirecting constructor',
1263 node.span);
1264 }
1258 } else { 1265 } else {
1259 world.error('bad call in initializers', node.span); 1266 world.error('bad call in initializers', node.span);
1260 } 1267 }
1261 target.allowDynamic = false; 1268 target.allowDynamic = false;
1262 1269
1263 var m = target.type.getConstructor(contructorName); 1270
1271 var m = targetType.getConstructor(contructorName);
1264 if (m == null) { 1272 if (m == null) {
1265 world.error('no matching constructor for ${target.type.name}', node.span); 1273 world.error('no matching constructor for ${targetType.name}', node.span);
1266 } 1274 }
1267 1275
1276 // TODO(jimhug): Replace with more generic recursion detection
1268 method.initDelegate = m; 1277 method.initDelegate = m;
1269 // check no cycles in in initialization: 1278 // check no cycles in in initialization:
1270 var other = m; 1279 var other = m;
1271 while (other != null) { 1280 while (other != null) {
1272 if (other == method) { 1281 if (other == method) {
1273 world.error('initialization cycle', node.span); 1282 world.error('initialization cycle', node.span);
1274 break; 1283 break;
1275 } 1284 }
1276 other = other.initDelegate; 1285 other = other.initDelegate;
1277 } 1286 }
1278 1287
1279 // move this all to happen when new first constuctor is walked. 1288 var newArgs = _makeArgs(node.arguments);
1289 // ???? wacky stuff ????
1280 world.gen.genMethod(m); 1290 world.gen.genMethod(m);
1281 var value = m.invoke(this, node, target, _makeArgs(node.arguments)); 1291
1282 if (target.type != world.objectType) { 1292 m._evalConstConstructor(newObject, newArgs);
1283 // No need to actually call Object's empty super constructor. 1293
1284 writer.writeln('${value.code};'); 1294 if (!newObject.isConst) {
1295 var value = m.invoke(this, node, target, newArgs);
1296 if (target.type != world.objectType) {
1297 // No need to actually call Object's empty super constructor.
1298 writer.writeln('${value.code};');
1299 }
1285 } 1300 }
1286 return target;
1287 } 1301 }
1288 1302
1289 _makeArgs(List<ArgumentNode> arguments) { 1303 _makeArgs(List<ArgumentNode> arguments) {
1290 var args = []; 1304 var args = [];
1291 bool seenLabel = false; 1305 bool seenLabel = false;
1292 for (var arg in arguments) { 1306 for (var arg in arguments) {
1293 if (arg.label != null) { 1307 if (arg.label != null) {
1294 seenLabel = true; 1308 seenLabel = true;
1295 } else if (seenLabel) { 1309 } else if (seenLabel) {
1296 // TODO(jimhug): Move this into parser? 1310 // TODO(jimhug): Move this into parser?
(...skipping 881 matching lines...) Expand 10 before | Expand all | Expand 10 after
2178 var kind = (TokenKind.INCR == node.op.kind ? 2192 var kind = (TokenKind.INCR == node.op.kind ?
2179 TokenKind.ADD : TokenKind.SUB); 2193 TokenKind.ADD : TokenKind.SUB);
2180 // TODO(jimhug): Shouldn't need a full-expression here. 2194 // TODO(jimhug): Shouldn't need a full-expression here.
2181 var operand = new LiteralExpression(Value.fromInt(1, node.span), 2195 var operand = new LiteralExpression(Value.fromInt(1, node.span),
2182 node.span); 2196 node.span);
2183 2197
2184 var assignValue = _visitAssign(kind, node.self, operand, node, null); 2198 var assignValue = _visitAssign(kind, node.self, operand, node, null);
2185 return new Value(assignValue.type, '(${assignValue.code})', 2199 return new Value(assignValue.type, '(${assignValue.code})',
2186 node.span); 2200 node.span);
2187 } 2201 }
2188 case TokenKind.NOT:
2189 // TODO(jimhug): Issue #359 seeks to clarify this behavior.
2190 if (value.type.isBool && value.isConst) {
2191 var newVal = !value.actualValue;
2192 return Value.fromBool(newVal, node.span);
2193 } else {
2194 var newVal = value.convertTo(this, world.nonNullBool);
2195 return new Value(newVal.type, '!${newVal.code}', node.span);
2196 }
2197
2198 case TokenKind.ADD:
2199 // TODO(jimhug): Issue #359 seeks to clarify this behavior.
2200 return value.convertTo(this, world.numType);
2201
2202 case TokenKind.SUB:
2203 case TokenKind.BIT_NOT:
2204 if (node.op.kind == TokenKind.BIT_NOT) {
2205 return value.invoke(this, ':bit_not', node, Arguments.EMPTY);
2206 } else if (node.op.kind == TokenKind.SUB) {
2207 return value.invoke(this, ':negate', node, Arguments.EMPTY);
2208 } else {
2209 world.internalError('unimplemented: unary ${node.op}',
2210 node.span);
2211 }
2212 default:
2213 world.internalError('unimplemented: ${node.op}', node.span);
2214 } 2202 }
2203 return value.unop(node.op.kind, this, node);
Jennifer Messerly 2012/01/09 20:45:35 so nice
2215 } 2204 }
2216 2205
2217 visitDeclaredIdentifier(DeclaredIdentifier node) { 2206 visitDeclaredIdentifier(DeclaredIdentifier node) {
2218 world.error('Expected expression', node.span); 2207 world.error('Expected expression', node.span);
2219 } 2208 }
2220 2209
2221 visitAwaitExpression(AwaitExpression node) { 2210 visitAwaitExpression(AwaitExpression node) {
2222 world.internalError( 2211 world.internalError(
2223 'Await expressions should have been eliminated before code generation', 2212 'Await expressions should have been eliminated before code generation',
2224 node.span); 2213 node.span);
(...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after
2609 result.add(new Value(world.varType, '\$$i', null, /*needsTemp:*/false)); 2598 result.add(new Value(world.varType, '\$$i', null, /*needsTemp:*/false));
2610 } 2599 }
2611 for (int i = bareCount; i < length; i++) { 2600 for (int i = bareCount; i < length; i++) {
2612 var name = getName(i); 2601 var name = getName(i);
2613 if (name == null) name = '\$$i'; 2602 if (name == null) name = '\$$i';
2614 result.add(new Value(world.varType, name, null, /*needsTemp:*/false)); 2603 result.add(new Value(world.varType, name, null, /*needsTemp:*/false));
2615 } 2604 }
2616 return new Arguments(nodes, result); 2605 return new Arguments(nodes, result);
2617 } 2606 }
2618 } 2607 }
OLDNEW
« no previous file with comments | « no previous file | frog/member.dart » ('j') | frog/member.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698