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

Side by Side Diff: frog/value.dart

Issue 8523012: first stage of Value cleanups (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: rebased Created 9 years, 1 month 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
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 * Represents a meta-value for code generation. 6 * Represents a meta-value for code generation.
7 */ 7 */
8 class Value { 8 class Value {
9 /** The [Type] of the [Value]. */ 9 /** The [Type] of the [Value]. */
10 Type type; 10 Type type;
11 11
12 /** The code to generate this value. */ 12 /** The code to generate this value. */
13 String code; 13 String code;
14 14
15 /** The source location that created this value for error messages. */
16 SourceSpan span;
Jennifer Messerly 2011/11/10 22:50:39 this makes me happy. I bet we can start slowly rat
17
15 /** Is this a reference to super? */ 18 /** Is this a reference to super? */
16 bool isSuper; 19 bool isSuper;
17 20
18 /** Is this a pretend first-class type? */ 21 /** Is this a pretend first-class type? */
19 bool isType; 22 bool isType;
20 23
21 /** If we reference this value multiple times, do we need a temp? */ 24 /** If we reference this value multiple times, do we need a temp? */
22 bool needsTemp; 25 bool needsTemp;
23 26
24 Value(this.type, this.code, 27 Value(this.type, this.code, this.span,
25 // TODO(sigmund): reorder, so that needsTemp comes first. 28 // TODO(sigmund): reorder, so that needsTemp comes first.
26 [this.isSuper = false, this.needsTemp = true, this.isType = false]) { 29 [this.isSuper = false, this.needsTemp = true, this.isType = false]) {
27 if (type == null) type = world.varType; 30 if (type == null) type = world.varType;
28 } 31 }
29 32
30 /** Is this value a constant expression? */ 33 /** Is this value a constant expression? */
31 bool get isConst() => false; 34 bool get isConst() => false;
32 35
33 // TODO(jimhug): These three methods are still a little too similar for me. 36 // TODO(jimhug): Fix these names once get/set are truly pseudo-keywords.
34 get_(MethodGenerator context, String name, Node node) { 37 // See issue #379.
35 var member = _resolveMember(context, name, node); 38 Value get_(MethodGenerator context, String name, Node node) {
39 final member = _resolveMember(context, name, node);
36 if (member != null) { 40 if (member != null) {
37 member = member.get_(context, node, this); 41 return member._get(context, node, this);
38 }
39 // member.get_ returns null if no signatures match the given node.
40 if (member != null) {
41 return member;
42 } else { 42 } else {
43 return invokeNoSuchMethod(context, 'get:$name', node); 43 return invokeNoSuchMethod(context, 'get:$name', node);
44 } 44 }
45 } 45 }
46 46
47 set_(MethodGenerator context, String name, Node node, Value value, 47 Value set_(MethodGenerator context, String name, Node node, Value value,
48 [bool isDynamic=false]) { 48 [bool isDynamic=false]) {
49 var member = _resolveMember(context, name, node, isDynamic); 49
50 final member = _resolveMember(context, name, node, isDynamic);
50 if (member != null) { 51 if (member != null) {
51 member = member.set_(context, node, this, value, isDynamic); 52 return member._set(context, node, this, value, isDynamic);
52 }
53 // member.set_ returns null if no signatures match the given node.
54 if (member != null) {
55 return member;
56 } else { 53 } else {
57 return invokeNoSuchMethod(context, 'set:$name', node, 54 return invokeNoSuchMethod(context, 'set:$name', node,
58 new Arguments(null, [value])); 55 new Arguments(null, [value]));
59 } 56 }
60 } 57 }
61 58
62 invoke(MethodGenerator context, String name, Node node, Arguments args, 59
60
61 Value invoke(MethodGenerator context, String name, Node node, Arguments args,
63 [bool isDynamic=false]) { 62 [bool isDynamic=false]) {
64 // TODO(jimhug): The != method is weird - understand it better. 63 // TODO(jimhug): The != method is weird - understand it better.
65 if (type.isVar && name == '\$ne') { 64 if (type.isVar && name == '\$ne') {
66 if (args.values.length != 1) { 65 if (args.values.length != 1) {
67 world.warning('wrong number of arguments for !=', node.span); 66 world.warning('wrong number of arguments for !=', node.span);
68 } 67 }
69 world.gen.corejs.useOperator('\$ne'); 68 world.gen.corejs.useOperator('\$ne');
70 return new Value(null, '\$ne($code, ${args.values[0].code})'); 69 return new Value(null, '\$ne($code, ${args.values[0].code})', node.span);
71 } 70 }
72 71
73 // TODO(jmesserly): it'd be nice to remove these special cases 72 // TODO(jmesserly): it'd be nice to remove these special cases
74 // We could create a $call (and $ne) in world members, and have 73 // We could create a $call (and $ne) in world members, and have
75 // those guys handle the canInvoke/Invoke logic. 74 // those guys handle the canInvoke/Invoke logic.
76 75
77 // Note: this check is a little different than the one in canInvoke, because 76 // Note: this check is a little different than the one in canInvoke, because
78 // sometimes we need to call dynamically even if we found the $call method 77 // sometimes we need to call dynamically even if we found the $call method
79 // statically. 78 // statically.
80 79
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 // TODO(jmesserly): should we be doing this? 116 // TODO(jmesserly): should we be doing this?
118 bool _hasOverriddenNoSuchMethod() { 117 bool _hasOverriddenNoSuchMethod() {
119 if (isSuper) { 118 if (isSuper) {
120 var m = type.getMember('noSuchMethod'); 119 var m = type.getMember('noSuchMethod');
121 return m != null && !m.declaringType.isObject; 120 return m != null && !m.declaringType.isObject;
122 } else { 121 } else {
123 return type.resolveMember('noSuchMethod').members.length > 1; 122 return type.resolveMember('noSuchMethod').members.length > 1;
124 } 123 }
125 } 124 }
126 125
126 _tryResolveMember(MethodGenerator context, String name) {
Jennifer Messerly 2011/11/10 22:50:39 funny! I had it factored this way at one point, bu
127 if (isSuper) {
128 return type.getMember(name);
129 } else {
130 return type.resolveMember(name);
131 }
132 }
133
127 // TODO(jimhug): Better type here - currently is union(Member, MemberSet) 134 // TODO(jimhug): Better type here - currently is union(Member, MemberSet)
128 _resolveMember(MethodGenerator context, String name, Node node, 135 _resolveMember(MethodGenerator context, String name, Node node,
129 [bool isDynamic=false]) { 136 [bool isDynamic=false]) {
130 137
131 // TODO(jmesserly): until reified generic lists are fixed, treat 138 // TODO(jmesserly): until reified generic lists are fixed, treat
132 // ParameterType as "var". 139 // ParameterType as "var".
133 var member; 140 var member;
134 if (!type.isVar && type is! ParameterType) { 141 if (!type.isVar && type is! ParameterType) {
135 if (isSuper) { 142 member = _tryResolveMember(context, name);
136 member = type.getMember(name);
137 } else {
138 member = type.resolveMember(name);
139 }
140 143
141 if (member != null && isType && !member.isStatic) { 144 if (member != null && isType && !member.isStatic) {
142 if (!isDynamic) { 145 if (!isDynamic) {
143 world.error('can not refer to instance member as static', node.span); 146 world.error('can not refer to instance member as static', node.span);
144 } 147 }
145 return null; 148 return null;
146 } 149 }
147 150
148 if (member == null && !isDynamic && !_hasOverriddenNoSuchMethod()) { 151 if (member == null && !isDynamic && !_hasOverriddenNoSuchMethod()) {
149 var typeName = type.name == null ? type.library.name : type.name; 152 var typeName = type.name == null ? type.library.name : type.name;
(...skipping 23 matching lines...) Expand all
173 world.error('Types are not first class', span); 176 world.error('Types are not first class', span);
174 } 177 }
175 } 178 }
176 179
177 /** Generate a call to an unknown function type. */ 180 /** Generate a call to an unknown function type. */
178 Value _varCall(MethodGenerator context, Arguments args) { 181 Value _varCall(MethodGenerator context, Arguments args) {
179 // TODO(jmesserly): calls to unknown functions will bypass type checks, 182 // TODO(jmesserly): calls to unknown functions will bypass type checks,
180 // which normally happen on the caller side, or in the generated stub for 183 // which normally happen on the caller side, or in the generated stub for
181 // dynamic method calls. What should we do? 184 // dynamic method calls. What should we do?
182 var stub = world.functionType.getCallStub(args); 185 var stub = world.functionType.getCallStub(args);
183 return new Value(null, '$code.${stub.name}(${args.getCode()})'); 186 return new Value(null, '$code.${stub.name}(${args.getCode()})', span);
184 } 187 }
185 188
186 /** True if convertTo would generate a conversion. */ 189 /** True if convertTo would generate a conversion. */
187 // TODO(jmesserly): I don't like how this is coupled to convertTo. 190 // TODO(jmesserly): I don't like how this is coupled to convertTo.
188 bool needsConversion(Type toType) { 191 bool needsConversion(Type toType) {
189 var callMethod = toType.getCallMethod(); 192 var callMethod = toType.getCallMethod();
190 if (callMethod != null) { 193 if (callMethod != null) {
191 int arity = callMethod.parameters.length; 194 int arity = callMethod.parameters.length;
192 var myCall = type.getCallMethod(); 195 var myCall = type.getCallMethod();
193 if (myCall == null || myCall.parameters.length != arity) { 196 if (myCall == null || myCall.parameters.length != arity) {
(...skipping 27 matching lines...) Expand all
221 var callMethod = toType.getCallMethod(); 224 var callMethod = toType.getCallMethod();
222 if (callMethod != null) { 225 if (callMethod != null) {
223 if (checked && !toType.isAssignable(type)) { 226 if (checked && !toType.isAssignable(type)) {
224 convertWarning(toType, node); 227 convertWarning(toType, node);
225 } 228 }
226 229
227 int arity = callMethod.parameters.length; 230 int arity = callMethod.parameters.length;
228 var myCall = type.getCallMethod(); 231 var myCall = type.getCallMethod();
229 if (myCall == null || myCall.parameters.length != arity) { 232 if (myCall == null || myCall.parameters.length != arity) {
230 final stub = world.functionType.getCallStub(new Arguments.bare(arity)); 233 final stub = world.functionType.getCallStub(new Arguments.bare(arity));
231 var val = new Value(toType, 'to\$${stub.name}($code)'); 234 var val = new Value(toType, 'to\$${stub.name}($code)', node.span);
232 return _isDomCallback(toType) && !_isDomCallback(type) ? 235 return _isDomCallback(toType) && !_isDomCallback(type) ?
233 val._wrapDomCallback(toType, arity) : val; 236 val._wrapDomCallback(toType, arity) : val;
234 } else if (_isDomCallback(toType) && !_isDomCallback(type)) { 237 } else if (_isDomCallback(toType) && !_isDomCallback(type)) {
235 return _wrapDomCallback(toType, arity); 238 return _wrapDomCallback(toType, arity);
236 } 239 }
237 } 240 }
238 241
239 // If we're assigning from a var, pretend it's Object for the purpose of 242 // If we're assigning from a var, pretend it's Object for the purpose of
240 // runtime checks. 243 // runtime checks.
241 244
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 convertWarning(world.boolType, node); 281 convertWarning(world.boolType, node);
279 } 282 }
280 if (!options.enableTypeChecks) { 283 if (!options.enableTypeChecks) {
281 return this; 284 return this;
282 } else { 285 } else {
283 // TODO(jmesserly): this is hacky. 286 // TODO(jmesserly): this is hacky.
284 if (code.startsWith('\$notnull_bool')) { 287 if (code.startsWith('\$notnull_bool')) {
285 return this; 288 return this;
286 } else { 289 } else {
287 world.gen.corejs.useNotNullBool = true; 290 world.gen.corejs.useNotNullBool = true;
288 return new Value(world.boolType, '\$notnull_bool($code)'); 291 return new Value(world.boolType, '\$notnull_bool($code)', span);
289 } 292 }
290 } 293 }
291 } 294 }
292 295
293 bool _isDomCallback(toType) { 296 bool _isDomCallback(toType) {
294 return (toType.definition is FunctionTypeDefinition 297 return (toType.definition is FunctionTypeDefinition
295 && toType.library == world.dom); 298 && toType.library == world.dom);
296 } 299 }
297 300
298 Value _wrapDomCallback(Type toType, int arity) { 301 Value _wrapDomCallback(Type toType, int arity) {
299 return new Value(toType, '\$wrap_call\$$arity($code)'); 302 return new Value(toType, '\$wrap_call\$$arity($code)', span);
300 } 303 }
301 304
302 /** 305 /**
303 * Generates a run time type assertion for the given value. This works like 306 * Generates a run time type assertion for the given value. This works like
304 * [instanceOf], but it allows null since Dart types are nullable. 307 * [instanceOf], but it allows null since Dart types are nullable.
305 * Also it will throw a TypeError if it gets the wrong type. 308 * Also it will throw a TypeError if it gets the wrong type.
306 */ 309 */
307 Value _typeAssert(MethodGenerator context, Type toType, Node node) { 310 Value _typeAssert(MethodGenerator context, Type toType, Node node) {
308 if (toType is ParameterType) { 311 if (toType is ParameterType) {
309 ParameterType p = toType; 312 ParameterType p = toType;
(...skipping 27 matching lines...) Expand all
337 } else { 340 } else {
338 toType.isTested = true; 341 toType.isTested = true;
339 342
340 // If we track nullability, we could simplify this check. 343 // If we track nullability, we could simplify this check.
341 var temp = context.getTemp(this); 344 var temp = context.getTemp(this);
342 check = '(${context.assignTemp(temp, this).code} &&'; 345 check = '(${context.assignTemp(temp, this).code} &&';
343 check += ' ${temp.code}.is\$${toType.jsname}())'; 346 check += ' ${temp.code}.is\$${toType.jsname}())';
344 if (this != temp) context.freeTemp(temp); 347 if (this != temp) context.freeTemp(temp);
345 } 348 }
346 349
347 return new Value(toType, check); 350 return new Value(toType, check, span);
348 } 351 }
349 352
350 /** 353 /**
351 * Test to see if value is an instance of this type. 354 * Test to see if value is an instance of this type.
352 * 355 *
353 * - If a primitive type, then uses the JavaScript typeof. 356 * - If a primitive type, then uses the JavaScript typeof.
354 * - If it's a non-generic class, use instanceof. 357 * - If it's a non-generic class, use instanceof.
355 * - Otherwise add a fake member to test for. This value is generated 358 * - Otherwise add a fake member to test for. This value is generated
356 * as a function so that it can be called for a runtime failure. 359 * as a function so that it can be called for a runtime failure.
357 */ 360 */
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 // Add !! to convert to boolean. 397 // Add !! to convert to boolean.
395 // TODO(jimhug): only do this if needed 398 // TODO(jimhug): only do this if needed
396 testCode = '!!' + testCode; 399 testCode = '!!' + testCode;
397 } else { 400 } else {
398 // The single ! here nicely converts undefined to false and function 401 // The single ! here nicely converts undefined to false and function
399 // to true. 402 // to true.
400 testCode = '!' + testCode; 403 testCode = '!' + testCode;
401 } 404 }
402 if (this != temp) context.freeTemp(temp); 405 if (this != temp) context.freeTemp(temp);
403 } 406 }
404 return new Value(world.boolType, testCode); 407 return new Value(world.boolType, testCode, span);
405 } 408 }
406 409
407 void convertWarning(Type toType, Node node) { 410 void convertWarning(Type toType, Node node) {
408 // TODO(jmesserly): better error messages for type conversion failures 411 // TODO(jmesserly): better error messages for type conversion failures
409 world.warning('type "${type.name}" is not assignable to "${toType.name}"', 412 world.warning('type "${type.name}" is not assignable to "${toType.name}"',
410 node.span); 413 node.span);
411 } 414 }
412 415
413 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, 416 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node,
414 [Arguments args]) { 417 [Arguments args]) {
415 var pos = ''; 418 var pos = '';
416 if (args != null) { 419 if (args != null) {
417 var argsCode = []; 420 var argsCode = [];
418 for (int i = 0; i < args.length; i++) { 421 for (int i = 0; i < args.length; i++) {
419 argsCode.add(args.values[i].code); 422 argsCode.add(args.values[i].code);
420 } 423 }
421 pos = Strings.join(argsCode, ", "); // don't remove trailing nulls 424 pos = Strings.join(argsCode, ", "); // don't remove trailing nulls
422 } 425 }
423 final noSuchArgs = [ 426 final noSuchArgs = [
424 new Value(world.stringType, '"$name"'), 427 new Value(world.stringType, '"$name"', node.span),
425 new Value(world.listType, '[$pos]')]; 428 new Value(world.listType, '[$pos]', node.span)];
426 429
427 // TODO(jmesserly): should be passing names but that breaks tests. Oh well. 430 // TODO(jmesserly): should be passing names but that breaks tests. Oh well.
428 /*if (args != null && args.hasNames) { 431 /*if (args != null && args.hasNames) {
429 var names = []; 432 var names = [];
430 for (int i = args.bareCount; i < args.length; i++) { 433 for (int i = args.bareCount; i < args.length; i++) {
431 names.add('"${args.getName(i)}", ${args.values[i].code}'); 434 names.add('"${args.getName(i)}", ${args.values[i].code}');
432 } 435 }
433 noSuchArgs.add(new Value(world.gen.useMapFactory(), 436 noSuchArgs.add(new Value(world.gen.useMapFactory(),
434 '\$map(${Strings.join(names, ", ")})')); 437 '\$map(${Strings.join(names, ", ")})'));
435 }*/ 438 }*/
436 439
437 // Finally, invoke noSuchMethod 440 // Finally, invoke noSuchMethod
438 return _resolveMember(context, 'noSuchMethod', node).invoke( 441 return _resolveMember(context, 'noSuchMethod', node).invoke(
439 context, node, this, new Arguments(null, noSuchArgs)); 442 context, node, this, new Arguments(null, noSuchArgs));
440 } 443 }
441 444
442 Value invokeSpecial(String name, Arguments args, Type returnType) { 445 Value invokeSpecial(String name, Arguments args, Type returnType) {
443 assert(name.startsWith('\$')); 446 assert(name.startsWith('\$'));
444 assert(!args.hasNames); 447 assert(!args.hasNames);
445 // TODO(jimhug): We need to do this a little bit more like get and set on 448 // TODO(jimhug): We need to do this a little bit more like get and set on
446 // properties. We should check the set of members for something 449 // properties. We should check the set of members for something
447 // like "requiresNativeIndexer" and "requiresDartIndexer" to 450 // like "requiresNativeIndexer" and "requiresDartIndexer" to
448 // decide on a strategy. 451 // decide on a strategy.
449 452
450 var argsString = args.getCode(); 453 var argsString = args.getCode();
451 // Most operator calls need to be emitted as function calls, so we don't 454 // Most operator calls need to be emitted as function calls, so we don't
452 // box numbers accidentally. Indexing is the exception. 455 // box numbers accidentally. Indexing is the exception.
453 if (name == '\$index' || name == '\$setindex') { 456 if (name == '\$index' || name == '\$setindex') {
454 return new Value(returnType, '$code.$name($argsString)'); 457 return new Value(returnType, '$code.$name($argsString)', span);
455 } else { 458 } else {
456 if (argsString.length > 0) argsString = ', $argsString'; 459 if (argsString.length > 0) argsString = ', $argsString';
457 world.gen.corejs.useOperator(name); 460 world.gen.corejs.useOperator(name);
458 return new Value(returnType, '$name($code$argsString)'); 461 return new Value(returnType, '$name($code$argsString)', span);
459 } 462 }
460 } 463 }
461 } 464 }
462 465
463 // TODO(jmesserly): the subtypes of Value require a lot of type checks and 466 // TODO(jmesserly): the subtypes of Value require a lot of type checks and
464 // downcasts to use; can we make that cleaner? (search for ".dynamic") 467 // downcasts to use; can we make that cleaner? (search for ".dynamic")
465 468
466 /** A value that can has been evaluated statically. */ 469 /** A value that can has been evaluated statically. */
467 class EvaluatedValue extends Value { 470 class EvaluatedValue extends Value {
468 471
469 var actualValue; 472 var actualValue;
470 473
471 bool get isConst() => true; 474 bool get isConst() => true;
472 475
473 /** 476 /**
474 * A canonicalized form of the code. Two const expressions that result in the 477 * A canonicalized form of the code. Two const expressions that result in the
475 * same instance should have the same [canonicalCode]. 478 * same instance should have the same [canonicalCode].
476 */ 479 */
477 String canonicalCode; 480 String canonicalCode;
478 481
479 /** Original span where this evaluated expression came from. */ 482 factory EvaluatedValue(Type type, actualValue, String canonicalCode,
480 SourceSpan original; 483 SourceSpan span) {
481
482 factory EvaluatedValue(type, actualValue, canonicalCode, original) {
483 return new EvaluatedValue._internal(type, actualValue, 484 return new EvaluatedValue._internal(type, actualValue,
484 canonicalCode, original, codeWithComments(canonicalCode, original)); 485 canonicalCode, span, codeWithComments(canonicalCode, span));
485 } 486 }
486 487
487 EvaluatedValue._internal( 488 EvaluatedValue._internal(Type type, this.actualValue, this.canonicalCode,
488 type, this.actualValue, this.canonicalCode, this.original, code) 489 SourceSpan span, String code)
489 : super(type, code, false, false, false); 490 : super(type, code, span, false, false, false);
490 491
491 static String codeWithComments(String canonicalCode, SourceSpan original) { 492 static String codeWithComments(String canonicalCode, SourceSpan span) {
492 return (original != null && original.text != canonicalCode) 493 return (span != null && span.text != canonicalCode)
493 ? '$canonicalCode/*${original.text}*/' : canonicalCode; 494 ? '$canonicalCode/*${span.text}*/' : canonicalCode;
494 } 495 }
495 } 496 }
496 497
497 /** An evaluated constant list expression. */ 498 /** An evaluated constant list expression. */
498 class ConstListValue extends EvaluatedValue { 499 class ConstListValue extends EvaluatedValue {
499 List<EvaluatedValue> values; 500 List<EvaluatedValue> values;
500 501
501 factory ConstListValue(Type type, List<EvaluatedValue> values, 502 factory ConstListValue(Type type, List<EvaluatedValue> values,
502 String actualValue, String canonicalCode, SourceSpan original) { 503 String actualValue, String canonicalCode, SourceSpan span) {
503 return new ConstListValue._internal(type, values, actualValue, 504 return new ConstListValue._internal(type, values, actualValue,
504 canonicalCode, original, codeWithComments(canonicalCode, original)); 505 canonicalCode, span, codeWithComments(canonicalCode, span));
505 } 506 }
506 507
507 ConstListValue._internal(type, this.values, 508 ConstListValue._internal(type, this.values,
508 actualValue, canonicalCode, original, code) : 509 actualValue, canonicalCode, span, code) :
509 super._internal(type, actualValue, canonicalCode, original, code); 510 super._internal(type, actualValue, canonicalCode, span, code);
510 } 511 }
511 512
512 /** An evaluated constant map expression. */ 513 /** An evaluated constant map expression. */
513 class ConstMapValue extends EvaluatedValue { 514 class ConstMapValue extends EvaluatedValue {
514 Map<String, EvaluatedValue> values; 515 Map<String, EvaluatedValue> values;
515 516
516 factory ConstMapValue(Type type, List<EvaluatedValue> keyValuePairs, 517 factory ConstMapValue(Type type, List<EvaluatedValue> keyValuePairs,
517 String actualValue, String canonicalCode, SourceSpan original) { 518 String actualValue, String canonicalCode, SourceSpan span) {
518 final values = new Map<String, EvaluatedValue>(); 519 final values = new Map<String, EvaluatedValue>();
519 for (int i = 0; i < keyValuePairs.length; i += 2) { 520 for (int i = 0; i < keyValuePairs.length; i += 2) {
520 values[keyValuePairs[i].actualValue] = keyValuePairs[i + 1]; 521 values[keyValuePairs[i].actualValue] = keyValuePairs[i + 1];
521 } 522 }
522 return new ConstMapValue._internal(type, values, actualValue, 523 return new ConstMapValue._internal(type, values, actualValue,
523 canonicalCode, original, codeWithComments(canonicalCode, original)); 524 canonicalCode, span, codeWithComments(canonicalCode, span));
524 } 525 }
525 526
526 ConstMapValue._internal(type, this.values, 527 ConstMapValue._internal(type, this.values,
527 actualValue, canonicalCode, original, code) : 528 actualValue, canonicalCode, span, code) :
528 super._internal(type, actualValue, canonicalCode, original, code); 529 super._internal(type, actualValue, canonicalCode, span, code);
529 } 530 }
530 531
531 /** An evaluated constant object expression. */ 532 /** An evaluated constant object expression. */
532 class ConstObjectValue extends EvaluatedValue { 533 class ConstObjectValue extends EvaluatedValue {
533 Map<String, EvaluatedValue> fields; 534 Map<String, EvaluatedValue> fields;
534 535
535 factory ConstObjectValue( 536 factory ConstObjectValue(
536 Type type, Map<String, EvaluatedValue> fields, 537 Type type, Map<String, EvaluatedValue> fields,
537 String canonicalCode, SourceSpan original) { 538 String canonicalCode, SourceSpan span) {
538 // compute a unique-string form used to index this value in the global const 539 // compute a unique-string form used to index this value in the global const
539 // map. This is used to ensure that multiple const object values are 540 // map. This is used to ensure that multiple const object values are
540 // equivalent if they have the same type name and values on each field. 541 // equivalent if they have the same type name and values on each field.
541 final fieldValues = []; 542 final fieldValues = [];
542 for (var f in fields.getKeys()) { 543 for (var f in fields.getKeys()) {
543 fieldValues.add('$f = ${fields[f].actualValue}'); 544 fieldValues.add('$f = ${fields[f].actualValue}');
544 } 545 }
545 fieldValues.sort((a, b) => a.compareTo(b)); 546 fieldValues.sort((a, b) => a.compareTo(b));
546 final actualValue = 'const ${type.jsname} [' 547 final actualValue = 'const ${type.jsname} ['
547 + Strings.join(fieldValues, ',') + ']'; 548 + Strings.join(fieldValues, ',') + ']';
548 return new ConstObjectValue._internal(type, fields, actualValue, 549 return new ConstObjectValue._internal(type, fields, actualValue,
549 canonicalCode, original, codeWithComments(canonicalCode, original)); 550 canonicalCode, span, codeWithComments(canonicalCode, span));
550 } 551 }
551 552
552 ConstObjectValue._internal(type, this.fields, 553 ConstObjectValue._internal(type, this.fields,
553 actualValue, canonicalCode, original, code) : 554 actualValue, canonicalCode, span, code) :
554 super._internal(type, actualValue, canonicalCode, original, code); 555 super._internal(type, actualValue, canonicalCode, span, code);
555 556
556 } 557 }
557 558
558 /** 559 /**
559 * A global value in the generated code, which corresponds to either a static 560 * A global value in the generated code, which corresponds to either a static
560 * field or a memoized const expressions. 561 * field or a memoized const expressions.
561 */ 562 */
562 class GlobalValue extends Value implements Comparable { 563 class GlobalValue extends Value implements Comparable {
563 /** Static field definition (null for constant exp). */ 564 /** Static field definition (null for constant exp). */
564 FieldMember field; 565 FieldMember field;
565 566
566 /** 567 /**
567 * When [this] represents a constant expression, the global variable name 568 * When [this] represents a constant expression, the global variable name
568 * generated for it. 569 * generated for it.
569 */ 570 */
570 String name; 571 String name;
571 572
572 /** The value of the field or constant expression to declare. */ 573 /** The value of the field or constant expression to declare. */
573 Value exp; 574 Value exp;
574 575
575 /** 576 /**
576 * A canonicalized form of the code. Two const expressions that result in the 577 * A canonicalized form of the code. Two const expressions that result in the
577 * same instance should have the same [canonicalCode]. 578 * same instance should have the same [canonicalCode].
578 */ 579 */
579 String canonicalCode; 580 String canonicalCode;
580 581
581 /** Original span where this value came from. */
582 SourceSpan original;
583
584 /** True for either cont expressions or a final static field. */ 582 /** True for either cont expressions or a final static field. */
585 bool get isConst() => exp.isConst && (field == null || field.isFinal); 583 bool get isConst() => exp.isConst && (field == null || field.isFinal);
586 584
587 /** The actual constant value, if [isConst] is true. */ 585 /** The actual constant value, if [isConst] is true. */
588 get actualValue() => exp.dynamic.actualValue; 586 get actualValue() => exp.dynamic.actualValue;
589 587
590 /** Other globals that should be defined before this global. */ 588 /** Other globals that should be defined before this global. */
591 List<GlobalValue> dependencies; 589 List<GlobalValue> dependencies;
592 590
593 factory GlobalValue.fromStatic(field, exp, dependencies) { 591 factory GlobalValue.fromStatic(field, Value exp, dependencies) {
594 var code = (exp.isConst ? exp.canonicalCode : exp.code); 592 var code = (exp.isConst ? exp.canonicalCode : exp.code);
595 var codeWithComment = '$code/*${field.declaringType.name}.${field.name}*/'; 593 var codeWithComment = '$code/*${field.declaringType.name}.${field.name}*/';
596 return new GlobalValue( 594 return new GlobalValue(
597 exp.type, codeWithComment, field.isFinal, field, null, exp, 595 exp.type, codeWithComment, field.isFinal, field, null, exp,
598 code, null, dependencies.filter((d) => d is GlobalValue)); 596 code, exp.span, dependencies.filter((d) => d is GlobalValue));
599 } 597 }
600 598
601 factory GlobalValue.fromConst(uniqueId, exp, dependencies) { 599 factory GlobalValue.fromConst(uniqueId, Value exp, dependencies) {
602 var name = "const\$$uniqueId"; 600 var name = "const\$$uniqueId";
603 var codeWithComment = "$name/*${exp.original.text}*/"; 601 var codeWithComment = "$name/*${exp.span.text}*/";
604 return new GlobalValue( 602 return new GlobalValue(
605 exp.type, codeWithComment, true, null, name, exp, name, 603 exp.type, codeWithComment, true, null, name, exp, name,
606 exp.original, 604 exp.span,
607 dependencies.filter((d) => d is GlobalValue)); 605 dependencies.filter((d) => d is GlobalValue));
608 } 606 }
609 607
610 GlobalValue(type, code, isConst, 608 GlobalValue(Type type, String code, bool isConst,
611 this.field, this.name, this.exp, this.canonicalCode, 609 this.field, this.name, this.exp, this.canonicalCode,
612 this.original, this.dependencies) 610 SourceSpan span, this.dependencies)
613 : super(type, code, false, !isConst, false); 611 : super(type, code, span, false, !isConst, false);
614 612
615 int compareTo(GlobalValue other) { 613 int compareTo(GlobalValue other) {
616 // order by dependencies, o.w. by name 614 // order by dependencies, o.w. by name
617 if (other == this) { 615 if (other == this) {
618 return 0; 616 return 0;
619 } else if (dependencies.indexOf(other, 0) >= 0) { 617 } else if (dependencies.indexOf(other, 0) >= 0) {
620 return 1; 618 return 1;
621 } else if (other.dependencies.indexOf(this, 0) >= 0) { 619 } else if (other.dependencies.indexOf(this, 0) >= 0) {
622 return -1; 620 return -1;
623 } else if (dependencies.length > other.dependencies.length) { 621 } else if (dependencies.length > other.dependencies.length) {
624 return 1; 622 return 1;
625 } else if (dependencies.length < other.dependencies.length) { 623 } else if (dependencies.length < other.dependencies.length) {
626 return -1; 624 return -1;
627 } else if (name == null && other.name != null) { 625 } else if (name == null && other.name != null) {
628 return 1; 626 return 1;
629 } else if (name != null && other.name == null) { 627 } else if (name != null && other.name == null) {
630 return -1; 628 return -1;
631 } else if (name != null) { 629 } else if (name != null) {
632 return name.compareTo(other.name); 630 return name.compareTo(other.name);
633 } else { 631 } else {
634 return field.name.compareTo(other.field.name); 632 return field.name.compareTo(other.field.name);
635 } 633 }
636 } 634 }
637 } 635 }
636
637 /**
638 * Represents the hidden or implicit value in a bare reference like 'a'.
639 * This could be this, the current type, or the current library for purposes
640 * of resolving members.
641 */
642 class BareValue extends Value {
Jennifer Messerly 2011/11/10 22:50:39 +1
643 MethodGenerator home;
644
645 BareValue(this.home, MethodGenerator outermost, SourceSpan span):
646 super(outermost.method.declaringType, null, span, false, false,
647 outermost.isStatic);
648
649 _tryResolveMember(MethodGenerator context, String name) {
650 assert(context == home);
651
652 // First look for members directly defined on my type.
653 var member = type.resolveMember(name);
654 if (member != null) {
655 assert(code == null);
656 // TODO(jimhug): Lazy initialization here is weird!
657 if (isType) {
658 code = type.jsname;
659 } else {
660 code = home._makeThisCode();
661 }
662 return member;
663 }
664
665 // Then look for members in my library.
666 member = home.library.lookup(name, span);
667 if (member != null) {
668 return member;
669 }
670
671 return null;
672 }
673 }
OLDNEW
« frog/member.dart ('K') | « frog/member.dart ('k') | frog/var_member.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698