| 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; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 } | 38 } |
| 39 // member.get_ returns null if no signatures match the given node. | 39 // member.get_ returns null if no signatures match the given node. |
| 40 if (member != null) { | 40 if (member != null) { |
| 41 return member; | 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 set_(MethodGenerator context, String name, Node node, Value value, |
| 48 [bool checked=true]) { | 48 [bool isDynamic=false]) { |
| 49 var member = _resolveMember(context, name, node); | 49 var member = _resolveMember(context, name, node); |
| 50 if (member != null) { | 50 if (member != null) { |
| 51 member = member.set_(context, node, this, value, checked); | 51 member = member.set_(context, node, this, value, isDynamic); |
| 52 } | 52 } |
| 53 // member.set_ returns null if no signatures match the given node. | 53 // member.set_ returns null if no signatures match the given node. |
| 54 if (member != null) { | 54 if (member != null) { |
| 55 return member; | 55 return member; |
| 56 } else { | 56 } else { |
| 57 return invokeNoSuchMethod(context, 'set:$name', node, | 57 return invokeNoSuchMethod(context, 'set:$name', node, |
| 58 new Arguments(null, [value])); | 58 new Arguments(null, [value])); |
| 59 } | 59 } |
| 60 } | 60 } |
| 61 | 61 |
| 62 invoke(MethodGenerator context, String name, Node node, Arguments args, | 62 invoke(MethodGenerator context, String name, Node node, Arguments args, |
| 63 [bool checked=true]) { | 63 [bool isDynamic=false]) { |
| 64 // TODO(jimhug): The != method is weird - understand it better. | 64 // TODO(jimhug): The != method is weird - understand it better. |
| 65 if (type.isVar && name == '\$ne') { | 65 if (type.isVar && name == '\$ne') { |
| 66 if (args.values.length != 1) { | 66 if (args.values.length != 1) { |
| 67 world.warning('wrong number of arguments for !=', node.span); | 67 world.warning('wrong number of arguments for !=', node.span); |
| 68 } | 68 } |
| 69 return new Value(null, '\$ne($code, ${args.values[0].code})'); | 69 return new Value(null, '\$ne($code, ${args.values[0].code})'); |
| 70 } | 70 } |
| 71 | 71 |
| 72 // TODO(jmesserly): it'd be nice to remove these special cases | 72 // TODO(jmesserly): it'd be nice to remove these special cases |
| 73 // 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 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 84 } | 84 } |
| 85 if (type.needsVarCall(args)) { | 85 if (type.needsVarCall(args)) { |
| 86 return _varCall(context, args); | 86 return _varCall(context, args); |
| 87 } | 87 } |
| 88 } | 88 } |
| 89 | 89 |
| 90 var member = _resolveMember(context, name, node); | 90 var member = _resolveMember(context, name, node); |
| 91 if (member == null) { | 91 if (member == null) { |
| 92 return invokeNoSuchMethod(context, name, node, args); | 92 return invokeNoSuchMethod(context, name, node, args); |
| 93 } else { | 93 } else { |
| 94 return member.invoke(context, node, this, args, checked); | 94 return member.invoke(context, node, this, args, isDynamic); |
| 95 } | 95 } |
| 96 } | 96 } |
| 97 | 97 |
| 98 bool canInvoke(MethodGenerator context, String name, Arguments args) { | 98 bool canInvoke(MethodGenerator context, String name, Arguments args) { |
| 99 // TODO(jimhug): The != method is weird - understand it better. | 99 // TODO(jimhug): The != method is weird - understand it better. |
| 100 if (type.isVar && name == '\$ne') { | 100 if (type.isVar && name == '\$ne') { |
| 101 return true; | 101 return true; |
| 102 } | 102 } |
| 103 | 103 |
| 104 if (type.isVarOrFunction && name == '\$call') { | 104 if (type.isVarOrFunction && name == '\$call') { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 156 } | 156 } |
| 157 | 157 |
| 158 /** Generate a call to an unknown function type. */ | 158 /** Generate a call to an unknown function type. */ |
| 159 Value _varCall(MethodGenerator context, Arguments args) { | 159 Value _varCall(MethodGenerator context, Arguments args) { |
| 160 var stub = world.functionType.getCallStub(args); | 160 var stub = world.functionType.getCallStub(args); |
| 161 return new Value(null, '$code.${stub.name}(${args.getCode()})'); | 161 return new Value(null, '$code.${stub.name}(${args.getCode()})'); |
| 162 } | 162 } |
| 163 | 163 |
| 164 /** | 164 /** |
| 165 * Assign or convert this value to another type. | 165 * Assign or convert this value to another type. |
| 166 * Right now we use this for converting between function types. In the future | 166 * This is used for converting between function types, and inserting type |
| 167 * we can use this for other kinds of type checks. | 167 * checks when --enable_type_checks is enabled. |
| 168 */ | 168 */ |
| 169 Value convertTo(MethodGenerator context, Type toType, Node node, | 169 Value convertTo(MethodGenerator context, Type toType, Node node, |
| 170 [bool checked=true]) { | 170 [bool isDynamic=false]) { |
| 171 |
| 172 // Check types if enabled, unless this is a dynamic operation |
| 173 bool checked = options.enableTypeChecks && !isDynamic; |
| 171 | 174 |
| 172 var callMethod = toType.getCallMethod(); | 175 var callMethod = toType.getCallMethod(); |
| 173 if (callMethod != null) { | 176 if (callMethod != null) { |
| 174 if (checked && options.enableTypeChecks && !toType.isAssignable(type)) { | 177 if (checked && !toType.isAssignable(type)) { |
| 175 convertWarning(toType, node); | 178 convertWarning(toType, node); |
| 176 } | 179 } |
| 177 | 180 |
| 178 // TODO(jmesserly): better error if passing a non-function to something | |
| 179 // that expects a function. | |
| 180 | |
| 181 int arity = callMethod.parameters.length; | 181 int arity = callMethod.parameters.length; |
| 182 var myCall = type.getCallMethod(); | 182 var myCall = type.getCallMethod(); |
| 183 if (myCall == null || myCall.parameters.length != arity) { | 183 if (myCall == null || myCall.parameters.length != arity) { |
| 184 final stub = world.functionType.getCallStub(new Arguments.bare(arity)); | 184 final stub = world.functionType.getCallStub(new Arguments.bare(arity)); |
| 185 return new Value(toType, 'to\$${stub.name}($code)'); | 185 return new Value(toType, 'to\$${stub.name}($code)'); |
| 186 } | 186 } |
| 187 } | 187 } |
| 188 | 188 |
| 189 // Don't add runtime asserts unless we have type checks turned on. |
| 189 if (!options.enableTypeChecks) { | 190 if (!options.enableTypeChecks) { |
| 190 return this; | 191 return this; |
| 191 } | 192 } |
| 192 | 193 |
| 193 if (type.isSubtypeOf(toType)) { | 194 if (type.isSubtypeOf(toType)) { |
| 194 return this; // widening conversion | 195 return this; // widening conversion |
| 195 } else if (checked && !toType.isSubtypeOf(type)) { | 196 } else if (checked && !toType.isSubtypeOf(type)) { |
| 196 // According to the static types, this conversion can't work. | 197 // According to the static types, this conversion can't work. |
| 197 convertWarning(toType, node); | 198 convertWarning(toType, node); |
| 198 } | 199 } |
| 199 | 200 |
| 200 // Add a runtime type check | 201 return _typeAssert(context, toType, node); |
| 202 } |
| 201 | 203 |
| 204 /** |
| 205 * Generates a run time type assertion for the given value. This works like |
| 206 * [instanceOf], but it allows null since Dart types are nullable. |
| 207 * Also it will throw a TypeError if it gets the wrong type. |
| 208 */ |
| 209 // TODO(jmesserly): this generated code is too verbose. |
| 210 Value _typeAssert(MethodGenerator context, Type toType, Node node) { |
| 211 if (toType is ParameterType) { |
| 212 ParameterType p = toType; |
| 213 toType = p.extendsType; |
| 214 } |
| 215 |
| 216 // TODO(jmesserly): I don't like the duplication with instanceOf |
| 202 var temp = context.getTemp(this); | 217 var temp = context.getTemp(this); |
| 203 var test = context.assignTemp(temp, this); | 218 String testCode; |
| 204 // TODO(jmesserly): this generates a second temp because of assignTemp | 219 if (toType.library.isCore && toType.typeofName != null) { |
| 205 // Also it generates an unecessary !!. | 220 testCode = "typeof(${temp.code}) == '${toType.typeofName}'"; |
| 206 test = test.instanceOf(context, toType, node.span); | 221 } else if (toType.isClass && toType is !ConcreteType) { |
| 222 toType.markUsed(); |
| 223 testCode = '${temp.code} instanceof ${toType.jsname}'; |
| 224 } else { |
| 225 toType.isTested = true; |
| 226 testCode = '${temp.code}.is\$${toType.jsname}'; |
| 227 } |
| 228 testCode = '(${context.assignTemp(temp, this).code} == null || $testCode)'; |
| 229 var test = new Value(world.boolType, testCode); |
| 207 | 230 |
| 208 // TODO(jmesserly): this generated code is too verbose | |
| 209 var err = world.corelib.types['TypeError']; | 231 var err = world.corelib.types['TypeError']; |
| 210 world.gen.genMethod(err.members['toString']); | 232 world.gen.genMethod(err.members['toString']); |
| 211 var args = new Arguments(null, [temp, | 233 var args = new Arguments(null, [temp, |
| 212 new Value(world.stringType, '"${toType.name}"')]); | 234 new Value(world.stringType, '"${toType.name}"')]); |
| 213 var typeErr = err.getConstructor('').invoke(context, node, null, args); | 235 var typeErr = err.getConstructor('').invoke(context, node, null, args); |
| 214 | 236 |
| 215 var result = new Value(toType, '(${test.code} ? ${temp.code} : ' | 237 var result = new Value(toType, '(${test.code} ? ${temp.code} : ' |
| 216 + '\$throw(${typeErr.code}))'); | 238 + '\$throw(${typeErr.code}))'); |
| 217 if (temp != this) context.freeTemp(temp); | 239 if (temp != this) context.freeTemp(temp); |
| 218 return result; | 240 return result; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 269 // The single ! here nicely converts undefined to false and function | 291 // The single ! here nicely converts undefined to false and function |
| 270 // to true. | 292 // to true. |
| 271 testCode = '!' + testCode; | 293 testCode = '!' + testCode; |
| 272 } | 294 } |
| 273 if (this != temp) context.freeTemp(temp); | 295 if (this != temp) context.freeTemp(temp); |
| 274 } | 296 } |
| 275 return new Value(world.boolType, testCode); | 297 return new Value(world.boolType, testCode); |
| 276 } | 298 } |
| 277 | 299 |
| 278 void convertWarning(Type toType, Node node) { | 300 void convertWarning(Type toType, Node node) { |
| 301 // TODO(jmesserly): better error messages for type conversion failures |
| 279 world.warning('type "${type.name}" is not assignable to "${toType.name}"', | 302 world.warning('type "${type.name}" is not assignable to "${toType.name}"', |
| 280 node.span); | 303 node.span); |
| 281 } | 304 } |
| 282 | 305 |
| 283 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, | 306 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, |
| 284 [Arguments args]) { | 307 [Arguments args]) { |
| 285 var pos = ''; | 308 var pos = ''; |
| 286 if (args != null) { | 309 if (args != null) { |
| 287 var argsCode = []; | 310 var argsCode = []; |
| 288 for (int i = 0; i < args.length; i++) { | 311 for (int i = 0; i < args.length; i++) { |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 494 return 1; | 517 return 1; |
| 495 } else if (name != null && other.name == null) { | 518 } else if (name != null && other.name == null) { |
| 496 return -1; | 519 return -1; |
| 497 } else if (name != null) { | 520 } else if (name != null) { |
| 498 return name.compareTo(other.name); | 521 return name.compareTo(other.name); |
| 499 } else { | 522 } else { |
| 500 return field.name.compareTo(other.field.name); | 523 return field.name.compareTo(other.field.name); |
| 501 } | 524 } |
| 502 } | 525 } |
| 503 } | 526 } |
| OLD | NEW |