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; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 60 return member._set(context, node, this, value, isDynamic); | 60 return member._set(context, node, this, value, isDynamic); |
| 61 } else { | 61 } else { |
| 62 return invokeNoSuchMethod(context, 'set:$name', node, | 62 return invokeNoSuchMethod(context, 'set:$name', node, |
| 63 new Arguments(null, [value])); | 63 new Arguments(null, [value])); |
| 64 } | 64 } |
| 65 } | 65 } |
| 66 | 66 |
| 67 | 67 |
| 68 Value invoke(MethodGenerator context, String name, Node node, Arguments args, | 68 Value invoke(MethodGenerator context, String name, Node node, Arguments args, |
| 69 [bool isDynamic=false]) { | 69 [bool isDynamic=false]) { |
| 70 // TODO(jimhug): The != method is weird - understand it better. | 70 // TODO(jmesserly): try to get rid of this code path. We're generating a |
| 71 // synthetic != on Object (see DefinedType._createNotEqualMember) already. | |
| 72 // So it should be pretty easy to make this go away. | |
|
jimhug
2011/11/12 00:26:36
+1
| |
| 71 if (_typeIsVarOrParameterType && name == '\$ne') { | 73 if (_typeIsVarOrParameterType && name == '\$ne') { |
| 72 if (args.values.length != 1) { | 74 if (args.values.length != 1) { |
| 73 world.warning('wrong number of arguments for !=', node.span); | 75 world.warning('wrong number of arguments for !=', node.span); |
| 74 } | 76 } |
| 77 // Ensure the == operator is generated, and get its type | |
| 78 var eq = invoke(context, '\$eq', node, args, isDynamic); | |
| 75 world.gen.corejs.useOperator('\$ne'); | 79 world.gen.corejs.useOperator('\$ne'); |
| 76 return new Value(world.varType, '\$ne($code, ${args.values[0].code})', | 80 return new Value(eq.type, '\$ne($code, ${args.values[0].code})', |
| 77 node.span); | 81 node.span); |
| 78 } | 82 } |
| 79 | 83 |
| 80 // TODO(jmesserly): it'd be nice to remove these special cases | 84 // TODO(jmesserly): it'd be nice to remove these special cases |
| 81 // We could create a $call (and $ne) in world members, and have | 85 // We could create a $call (and $ne) in world members, and have |
| 82 // those guys handle the canInvoke/Invoke logic. | 86 // those guys handle the canInvoke/Invoke logic. |
| 83 | 87 |
| 84 // Note: this check is a little different than the one in canInvoke, because | 88 // Note: this check is a little different than the one in canInvoke, because |
| 85 // sometimes we need to call dynamically even if we found the $call method | 89 // sometimes we need to call dynamically even if we found the $call method |
| 86 // statically. | 90 // statically. |
| 87 | 91 |
| (...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 259 } | 263 } |
| 260 | 264 |
| 261 // TODO(jmesserly): remove the special case for "num" when our num handling | 265 // TODO(jmesserly): remove the special case for "num" when our num handling |
| 262 // is better. | 266 // is better. |
| 263 bool bothNum = type.isNum && toType.isNum; | 267 bool bothNum = type.isNum && toType.isNum; |
| 264 if (!checked || fromType.isSubtypeOf(toType) || bothNum) { | 268 if (!checked || fromType.isSubtypeOf(toType) || bothNum) { |
| 265 // No checks needed for a widening conversion. | 269 // No checks needed for a widening conversion. |
| 266 return this; | 270 return this; |
| 267 } | 271 } |
| 268 | 272 |
| 269 if (!toType.isSubtypeOf(type)) { | 273 if (checked && !toType.isSubtypeOf(type)) { |
| 270 // According to the static types, this conversion can't work. | 274 // According to the static types, this conversion can't work. |
| 271 convertWarning(toType, node); | 275 convertWarning(toType, node); |
| 272 } | 276 } |
| 273 | 277 |
| 274 // Generate a runtime checks if they're turned on, otherwise skip it. | 278 // Generate a runtime checks if they're turned on, otherwise skip it. |
| 275 if (options.enableTypeChecks) { | 279 if (options.enableTypeChecks) { |
| 276 return _typeAssert(context, toType, node); | 280 return _typeAssert(context, toType, node); |
| 277 } else { | 281 } else { |
| 278 return this; | 282 return this; |
| 279 } | 283 } |
| 280 } | 284 } |
| 281 | 285 |
| 282 // TODO(jmesserly): this generates an unnecessary check for the 90% | |
| 283 // case where the thing passed in was a non-overloaded == or != expression | |
| 284 // We'll want to eliminate these, probably by tracking non-null bools in the | |
| 285 // type system. | |
| 286 // This matches the interesting Boolean Conversion section of the spec. | |
| 287 Value convertToNonNullBool(MethodGenerator context, Node node) { | |
| 288 if (!type.isAssignable(world.boolType)) { | |
| 289 convertWarning(world.boolType, node); | |
| 290 } | |
| 291 if (!options.enableTypeChecks) { | |
| 292 // TODO(jimhug): If type != world.boolType, this should return | |
| 293 // this.code === true according to the spec. | |
| 294 return this; | |
| 295 } else { | |
| 296 // TODO(jmesserly): this is hacky. | |
| 297 if (code.startsWith('\$notnull_bool')) { | |
| 298 return this; | |
| 299 } else { | |
| 300 world.gen.corejs.useNotNullBool = true; | |
| 301 return new Value(world.boolType, '\$notnull_bool($code)', span); | |
| 302 } | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 bool _isDomCallback(toType) { | 286 bool _isDomCallback(toType) { |
| 307 return (toType.definition is FunctionTypeDefinition | 287 return (toType.definition is FunctionTypeDefinition |
| 308 && toType.library == world.dom); | 288 && toType.library == world.dom); |
| 309 } | 289 } |
| 310 | 290 |
| 311 Value _wrapDomCallback(Type toType, int arity) { | 291 Value _wrapDomCallback(Type toType, int arity) { |
| 312 return new Value(toType, '\$wrap_call\$$arity($code)', span); | 292 return new Value(toType, '\$wrap_call\$$arity($code)', span); |
| 313 } | 293 } |
| 314 | 294 |
| 315 /** | 295 /** |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 338 // TypeError. Alternatively we could define fallbacks on Object that throw. | 318 // TypeError. Alternatively we could define fallbacks on Object that throw. |
| 339 String check; | 319 String check; |
| 340 if (toType.isVoid) { | 320 if (toType.isVoid) { |
| 341 check = '\$assert_void($code)'; | 321 check = '\$assert_void($code)'; |
| 342 if (toType.typeCheckCode == null) { | 322 if (toType.typeCheckCode == null) { |
| 343 toType.typeCheckCode = ''' | 323 toType.typeCheckCode = ''' |
| 344 function \$assert_void(x) { | 324 function \$assert_void(x) { |
| 345 return x == null ? x : x.is\$void(); // throws TypeError | 325 return x == null ? x : x.is\$void(); // throws TypeError |
| 346 }'''; | 326 }'''; |
| 347 } | 327 } |
| 328 } else if (toType == world.nonNullBool) { | |
| 329 // This could be made less of a special case | |
| 330 world.gen.corejs.useNotNullBool = true; | |
| 331 check = '\$notnull_bool($code)'; | |
| 332 | |
| 348 } else if (toType.library.isCore && toType.typeofName != null) { | 333 } else if (toType.library.isCore && toType.typeofName != null) { |
| 349 check = '\$assert_${toType.name}($code)'; | 334 check = '\$assert_${toType.name}($code)'; |
| 350 | 335 |
| 351 if (toType.typeCheckCode == null) { | 336 if (toType.typeCheckCode == null) { |
| 352 toType.typeCheckCode = ''' | 337 toType.typeCheckCode = ''' |
| 353 function \$assert_${toType.name}(x) { | 338 function \$assert_${toType.name}(x) { |
| 354 if (x == null || typeof(x) == "${toType.typeofName}") return x; | 339 if (x == null || typeof(x) == "${toType.typeofName}") return x; |
| 355 throw new TypeError("'" + x + "' is not a ${toType.name}."); | 340 throw new TypeError("'" + x + "' is not a ${toType.name}."); |
| 356 }'''; | 341 }'''; |
| 357 } | 342 } |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 375 * - If it's a non-generic class, use instanceof. | 360 * - If it's a non-generic class, use instanceof. |
| 376 * - Otherwise add a fake member to test for. This value is generated | 361 * - Otherwise add a fake member to test for. This value is generated |
| 377 * as a function so that it can be called for a runtime failure. | 362 * as a function so that it can be called for a runtime failure. |
| 378 */ | 363 */ |
| 379 Value instanceOf(MethodGenerator context, Type toType, SourceSpan span, | 364 Value instanceOf(MethodGenerator context, Type toType, SourceSpan span, |
| 380 [bool isTrue=true, bool forceCheck=false]) { | 365 [bool isTrue=true, bool forceCheck=false]) { |
| 381 // TODO(jimhug): Optimize away tests that will always pass unless | 366 // TODO(jimhug): Optimize away tests that will always pass unless |
| 382 // forceCheck is true. | 367 // forceCheck is true. |
| 383 if (toType.isVar) { | 368 if (toType.isVar) { |
| 384 world.error('can not resolve type', span); | 369 world.error('can not resolve type', span); |
| 385 return new EvaluatedValue(world.boolType, true, 'true', null); | 370 return new EvaluatedValue(world.nonNullBool, true, 'true', null); |
| 386 } | 371 } |
| 387 | 372 |
| 388 if (toType is ParameterType) { | 373 if (toType is ParameterType) { |
| 389 return new EvaluatedValue(world.boolType, true, 'true', null); | 374 return new EvaluatedValue(world.nonNullBool, true, 'true', null); |
| 390 } | 375 } |
| 391 | 376 |
| 392 String testCode = null; | 377 String testCode = null; |
| 393 if (toType.library.isCore) { | 378 if (toType.library.isCore) { |
| 394 var typeofName = toType.typeofName; | 379 var typeofName = toType.typeofName; |
| 395 if (typeofName != null) { | 380 if (typeofName != null) { |
| 396 testCode = "(typeof($code) ${isTrue ? '==' : '!='} '$typeofName')"; | 381 testCode = "(typeof($code) ${isTrue ? '==' : '!='} '$typeofName')"; |
| 397 } | 382 } |
| 398 } | 383 } |
| 399 if (toType.isClass && toType is !ConcreteType) { | 384 if (toType.isClass && toType is !ConcreteType) { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 415 // Add !! to convert to boolean. | 400 // Add !! to convert to boolean. |
| 416 // TODO(jimhug): only do this if needed | 401 // TODO(jimhug): only do this if needed |
| 417 testCode = '!!' + testCode; | 402 testCode = '!!' + testCode; |
| 418 } else { | 403 } else { |
| 419 // The single ! here nicely converts undefined to false and function | 404 // The single ! here nicely converts undefined to false and function |
| 420 // to true. | 405 // to true. |
| 421 testCode = '!' + testCode; | 406 testCode = '!' + testCode; |
| 422 } | 407 } |
| 423 if (this != temp) context.freeTemp(temp); | 408 if (this != temp) context.freeTemp(temp); |
| 424 } | 409 } |
| 425 return new Value(world.boolType, testCode, span); | 410 return new Value(world.nonNullBool, testCode, span); |
| 426 } | 411 } |
| 427 | 412 |
| 428 void convertWarning(Type toType, Node node) { | 413 void convertWarning(Type toType, Node node) { |
| 429 // TODO(jmesserly): better error messages for type conversion failures | 414 // TODO(jmesserly): better error messages for type conversion failures |
| 430 world.warning('type "${type.name}" is not assignable to "${toType.name}"', | 415 world.warning('type "${type.name}" is not assignable to "${toType.name}"', |
| 431 node.span); | 416 node.span); |
| 432 } | 417 } |
| 433 | 418 |
| 434 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, | 419 Value invokeNoSuchMethod(MethodGenerator context, String name, Node node, |
| 435 [Arguments args]) { | 420 [Arguments args]) { |
| (...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 683 | 668 |
| 684 // Then look for members in my library. | 669 // Then look for members in my library. |
| 685 member = home.library.lookup(name, span); | 670 member = home.library.lookup(name, span); |
| 686 if (member != null) { | 671 if (member != null) { |
| 687 return member; | 672 return member; |
| 688 } | 673 } |
| 689 | 674 |
| 690 return null; | 675 return null; |
| 691 } | 676 } |
| 692 } | 677 } |
| OLD | NEW |