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 /** | 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } | |
| OLD | NEW |