OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 * This part contains helpers for supporting runtime type information. | 6 * This part contains helpers for supporting runtime type information. |
7 * | 7 * |
8 * The helper use a mixture of Dart and JavaScript objects. To indicate which is | 8 * The helper use a mixture of Dart and JavaScript objects. To indicate which is |
9 * used where we adopt the scheme of using explicit type annotation for Dart | 9 * used where we adopt the scheme of using explicit type annotation for Dart |
10 * objects and 'var' or omitted return type for JavaScript objects. | 10 * objects and 'var' or omitted return type for JavaScript objects. |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
127 | 127 |
128 void copyTypeArguments(Object source, Object target) { | 128 void copyTypeArguments(Object source, Object target) { |
129 JS('var', r'#.$builtinTypeInfo = #.$builtinTypeInfo', target, source); | 129 JS('var', r'#.$builtinTypeInfo = #.$builtinTypeInfo', target, source); |
130 } | 130 } |
131 | 131 |
132 /** | 132 /** |
133 * Retrieves the class name from type information stored on the constructor | 133 * Retrieves the class name from type information stored on the constructor |
134 * of [object]. | 134 * of [object]. |
135 */ | 135 */ |
136 String getClassName(var object) { | 136 String getClassName(var object) { |
137 return JS('String', r'#.constructor.builtin$cls', getInterceptor(object)); | 137 return getDartTypeName(getRawRuntimeType(object)); |
138 } | 138 } |
139 | 139 |
140 /** | 140 /** |
141 * Creates the string representation for the type representation [runtimeType] | 141 * Creates the string representation for the type representation [runtimeType] |
142 * of type 4, the JavaScript array, where the first element represents the class | 142 * of type 4, the JavaScript array, where the first element represents the class |
143 * and the remaining elements represent the type arguments. | 143 * and the remaining elements represent the type arguments. |
144 */ | 144 */ |
145 String getRuntimeTypeAsString(var runtimeType, {String onTypeVariable(int i)}) { | 145 String getRuntimeTypeAsString(var runtimeType, {String onTypeVariable(int i)}) { |
146 assert(isJsArray(runtimeType)); | 146 assert(isJsArray(runtimeType)); |
147 String className = getConstructorName(getIndex(runtimeType, 0)); | 147 String className = getDartTypeName(getIndex(runtimeType, 0)); |
148 return '$className' | 148 return '$className' |
149 '${joinArguments(runtimeType, 1, onTypeVariable: onTypeVariable)}'; | 149 '${joinArguments(runtimeType, 1, onTypeVariable: onTypeVariable)}'; |
150 } | 150 } |
151 | 151 |
152 /** | 152 /** |
153 * Retrieves the class name from type information stored on the constructor | |
154 * [type]. | |
155 */ | |
156 String getConstructorName(var type) => JS('String', r'#.builtin$cls', type); | |
157 | |
158 /** | |
159 * Returns a human-readable representation of the type representation [type]. | 153 * Returns a human-readable representation of the type representation [type]. |
160 */ | 154 */ |
161 String runtimeTypeToString(var type, {String onTypeVariable(int i)}) { | 155 String runtimeTypeToString(var type, {String onTypeVariable(int i)}) { |
162 if (type == null) { | 156 if (type == null) { |
163 return 'dynamic'; | 157 return 'dynamic'; |
164 } else if (isJsArray(type)) { | 158 } else if (isJsArray(type)) { |
165 // A list representing a type with arguments. | 159 // A list representing a type with arguments. |
166 return getRuntimeTypeAsString(type, onTypeVariable: onTypeVariable); | 160 return getRuntimeTypeAsString(type, onTypeVariable: onTypeVariable); |
167 } else if (isJsFunction(type)) { | 161 } else if (isJsFunction(type)) { |
168 // A reference to the constructor. | 162 // A reference to the constructor. |
169 return getConstructorName(type); | 163 return getDartTypeName(type); |
170 } else if (type is int) { | 164 } else if (type is int) { |
171 if (onTypeVariable == null) { | 165 if (onTypeVariable == null) { |
172 return type.toString(); | 166 return type.toString(); |
173 } else { | 167 } else { |
174 return onTypeVariable(type); | 168 return onTypeVariable(type); |
175 } | 169 } |
176 } else { | 170 } else { |
177 // TODO(ahe): Handle function types, and be sure to always return a string. | 171 // TODO(ahe): Handle function types, and be sure to always return a string. |
178 return null; | 172 return null; |
179 } | 173 } |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 var substitution = getField(interceptor, asField); | 266 var substitution = getField(interceptor, asField); |
273 return checkArguments(substitution, arguments, checks); | 267 return checkArguments(substitution, arguments, checks); |
274 } | 268 } |
275 | 269 |
276 /// Returns the field's type name. | 270 /// Returns the field's type name. |
277 /// | 271 /// |
278 /// In minified mode, uses the unminified names if available. | 272 /// In minified mode, uses the unminified names if available. |
279 String computeTypeName(String isField, List arguments) { | 273 String computeTypeName(String isField, List arguments) { |
280 // Shorten the field name to the class name and append the textual | 274 // Shorten the field name to the class name and append the textual |
281 // representation of the type arguments. | 275 // representation of the type arguments. |
| 276 // TODO(floitsch): change this to: |
| 277 // String className = JS_BUILTIN('depends:none;effects:none;returns:String', |
| 278 // JsBuiltin.classNameFroIsCheckProperty, |
| 279 // isField); |
282 int prefixLength = JS_OPERATOR_IS_PREFIX().length; | 280 int prefixLength = JS_OPERATOR_IS_PREFIX().length; |
283 return Primitives.formatType(isField.substring(prefixLength, isField.length), | 281 return Primitives.formatType(isField.substring(prefixLength, isField.length), |
284 arguments); | 282 arguments); |
285 } | 283 } |
286 | 284 |
287 Object subtypeCast(Object object, String isField, List checks, String asField) { | 285 Object subtypeCast(Object object, String isField, List checks, String asField) { |
288 if (object != null && !checkSubtype(object, isField, checks, asField)) { | 286 if (object != null && !checkSubtype(object, isField, checks, asField)) { |
289 String actualType = Primitives.objectTypeName(object); | 287 String actualType = Primitives.objectTypeName(object); |
290 String typeName = computeTypeName(isField, checks); | 288 String typeName = computeTypeName(isField, checks); |
291 // TODO(johnniwinther): Move type lookup to [CastErrorImplementation] to | 289 // TODO(johnniwinther): Move type lookup to [CastErrorImplementation] to |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 var typeArguments = getRuntimeTypeArguments(context, contextName); | 362 var typeArguments = getRuntimeTypeArguments(context, contextName); |
365 return invokeOn(signature, context, typeArguments); | 363 return invokeOn(signature, context, typeArguments); |
366 } | 364 } |
367 | 365 |
368 /** | 366 /** |
369 * Returns `true` if the runtime type representation [type] is a supertype of | 367 * Returns `true` if the runtime type representation [type] is a supertype of |
370 * [Null]. | 368 * [Null]. |
371 */ | 369 */ |
372 bool isSupertypeOfNull(var type) { | 370 bool isSupertypeOfNull(var type) { |
373 // `null` means `dynamic`. | 371 // `null` means `dynamic`. |
374 return type == null || getConstructorName(type) == JS_OBJECT_CLASS_NAME() | 372 return type == null || getDartTypeName(type) == JS_OBJECT_CLASS_NAME() |
375 || getConstructorName(type) == JS_NULL_CLASS_NAME(); | 373 || getDartTypeName(type) == JS_NULL_CLASS_NAME(); |
376 } | 374 } |
377 | 375 |
378 /** | 376 /** |
379 * Tests whether the Dart object [o] is a subtype of the runtime type | 377 * Tests whether the Dart object [o] is a subtype of the runtime type |
380 * representation [t]. | 378 * representation [t]. |
381 * | 379 * |
382 * See the comment in the beginning of this file for a description of type | 380 * See the comment in the beginning of this file for a description of type |
383 * representations. | 381 * representations. |
384 */ | 382 */ |
385 bool checkSubtypeOfRuntimeType(o, t) { | 383 bool checkSubtypeOfRuntimeType(o, t) { |
386 if (o == null) return isSupertypeOfNull(t); | 384 if (o == null) return isSupertypeOfNull(t); |
387 if (t == null) return true; | 385 if (t == null) return true; |
388 // Get the runtime type information from the object here, because we may | 386 // Get the runtime type information from the object here, because we may |
389 // overwrite o with the interceptor below. | 387 // overwrite o with the interceptor below. |
390 var rti = getRuntimeTypeInfo(o); | 388 var rti = getRuntimeTypeInfo(o); |
391 o = getInterceptor(o); | 389 o = getInterceptor(o); |
392 var type = JS('', '#.constructor', o); | 390 var type = getRawRuntimeType(o); |
393 if (rti != null) { | 391 if (rti != null) { |
394 // If the type has type variables (that is, `rti != null`), make a copy of | 392 // If the type has type variables (that is, `rti != null`), make a copy of |
395 // the type arguments and insert [o] in the first position to create a | 393 // the type arguments and insert [o] in the first position to create a |
396 // compound type representation. | 394 // compound type representation. |
397 rti = JS('JSExtendableArray', '#.slice()', rti); // Make a copy. | 395 rti = JS('JSExtendableArray', '#.slice()', rti); // Make a copy. |
398 JS('', '#.splice(0, 0, #)', rti, type); // Insert type at position 0. | 396 JS('', '#.splice(0, 0, #)', rti, type); // Insert type at position 0. |
399 type = rti; | 397 type = rti; |
400 } else if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { | 398 } else if (isDartFunctionType(t)) { |
401 // Functions are treated specially and have their type information stored | 399 // Functions are treated specially and have their type information stored |
402 // directly in the instance. | 400 // directly in the instance. |
403 var signatureName = | |
404 '${JS_OPERATOR_IS_PREFIX()}_${getField(t, JS_FUNCTION_TYPE_TAG())}'; | |
405 if (hasField(o, signatureName)) return true; | |
406 var targetSignatureFunction = getField(o, '${JS_SIGNATURE_NAME()}'); | 401 var targetSignatureFunction = getField(o, '${JS_SIGNATURE_NAME()}'); |
407 if (targetSignatureFunction == null) return false; | 402 if (targetSignatureFunction == null) return false; |
408 type = invokeOn(targetSignatureFunction, o, null); | 403 type = invokeOn(targetSignatureFunction, o, null); |
409 return isFunctionSubtype(type, t); | 404 return isFunctionSubtype(type, t); |
410 } | 405 } |
411 return isSubtype(type, t); | 406 return isSubtype(type, t); |
412 } | 407 } |
413 | 408 |
414 Object subtypeOfRuntimeTypeCast(Object object, var type) { | 409 Object subtypeOfRuntimeTypeCast(Object object, var type) { |
415 if (object != null && !checkSubtypeOfRuntimeType(object, type)) { | 410 if (object != null && !checkSubtypeOfRuntimeType(object, type)) { |
(...skipping 26 matching lines...) Expand all Loading... |
442 * representations. | 437 * representations. |
443 * | 438 * |
444 * The arguments [s] and [t] must be types, usually represented by the | 439 * The arguments [s] and [t] must be types, usually represented by the |
445 * constructor of the class, or an array (for generic types). | 440 * constructor of the class, or an array (for generic types). |
446 */ | 441 */ |
447 bool isSubtype(var s, var t) { | 442 bool isSubtype(var s, var t) { |
448 // Subtyping is reflexive. | 443 // Subtyping is reflexive. |
449 if (isIdentical(s, t)) return true; | 444 if (isIdentical(s, t)) return true; |
450 // If either type is dynamic, [s] is a subtype of [t]. | 445 // If either type is dynamic, [s] is a subtype of [t]. |
451 if (s == null || t == null) return true; | 446 if (s == null || t == null) return true; |
452 if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { | 447 if (isDartFunctionType(t)) { |
453 return isFunctionSubtype(s, t); | 448 return isFunctionSubtype(s, t); |
454 } | 449 } |
455 // Check function types against the Function class. | 450 // Check function types against the Function class. |
456 if (hasField(s, '${JS_FUNCTION_TYPE_TAG()}')) { | 451 if (isDartFunctionType(s)) { |
457 return getConstructorName(t) == JS_FUNCTION_CLASS_NAME(); | 452 return isDartFunctionTypeLiteral(t); |
458 } | 453 } |
459 | 454 |
460 // Get the object describing the class and check for the subtyping flag | 455 // Get the object describing the class and check for the subtyping flag |
461 // constructed from the type of [t]. | 456 // constructed from the type of [t]. |
462 var typeOfS = isJsArray(s) ? getIndex(s, 0) : s; | 457 var typeOfS = isJsArray(s) ? getIndex(s, 0) : s; |
463 var typeOfT = isJsArray(t) ? getIndex(t, 0) : t; | 458 var typeOfT = isJsArray(t) ? getIndex(t, 0) : t; |
464 // Check for a subtyping flag. | 459 // Check for a subtyping flag. |
465 var name = runtimeTypeToString(typeOfT); | 460 var name = runtimeTypeToString(typeOfT); |
466 // Get the necessary substitution of the type arguments, if there is one. | 461 // Get the necessary substitution of the type arguments, if there is one. |
467 var substitution; | 462 var substitution; |
468 if (isNotIdentical(typeOfT, typeOfS)) { | 463 if (isNotIdentical(typeOfT, typeOfS)) { |
| 464 // TODO(floitsch): change this to: |
| 465 // if (!JS_BUILTIN('depends:none;effects:none;returns:bool', |
| 466 // JsBuiltin.implementsType, |
| 467 // typeOfSPrototype, name)) { |
| 468 // return false; |
| 469 // } |
469 var test = '${JS_OPERATOR_IS_PREFIX()}${name}'; | 470 var test = '${JS_OPERATOR_IS_PREFIX()}${name}'; |
470 var typeOfSPrototype = JS('', '#.prototype', typeOfS); | 471 var typeOfSPrototype = JS('', '#.prototype', typeOfS); |
471 if (hasNoField(typeOfSPrototype, test)) return false; | 472 if (hasNoField(typeOfSPrototype, test)) return false; |
472 var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; | 473 var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; |
473 substitution = getField(typeOfSPrototype, field); | 474 substitution = getField(typeOfSPrototype, field); |
474 } | 475 } |
475 // The class of [s] is a subclass of the class of [t]. If [s] has no type | 476 // The class of [s] is a subclass of the class of [t]. If [s] has no type |
476 // arguments and no substitution, it is used as raw type. If [t] has no | 477 // arguments and no substitution, it is used as raw type. If [t] has no |
477 // type arguments, it used as a raw type. In both cases, [s] is a subtype | 478 // type arguments, it used as a raw type. In both cases, [s] is a subtype |
478 // of [t]. | 479 // of [t]. |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
532 return false; | 533 return false; |
533 } | 534 } |
534 var tType = JS('', '#[#]', t, name); | 535 var tType = JS('', '#[#]', t, name); |
535 var sType = JS('', '#[#]', s, name); | 536 var sType = JS('', '#[#]', s, name); |
536 if (!isAssignable(tType, sType)) return false; | 537 if (!isAssignable(tType, sType)) return false; |
537 } | 538 } |
538 return true; | 539 return true; |
539 } | 540 } |
540 | 541 |
541 bool isFunctionSubtype(var s, var t) { | 542 bool isFunctionSubtype(var s, var t) { |
542 assert(hasField(t, '${JS_FUNCTION_TYPE_TAG()}')); | 543 assert(isDartFunctionType(t)); |
543 if (hasNoField(s, '${JS_FUNCTION_TYPE_TAG()}')) return false; | 544 if (!isDartFunctionType(s)) return false; |
544 if (hasField(s, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) { | 545 if (hasField(s, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) { |
545 if (hasNoField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}') && | 546 if (hasNoField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}') && |
546 hasField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}')) { | 547 hasField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}')) { |
547 return false; | 548 return false; |
548 } | 549 } |
549 } else if (hasNoField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) { | 550 } else if (hasNoField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) { |
550 var sReturnType = getField(s, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}'); | 551 var sReturnType = getField(s, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}'); |
551 var tReturnType = getField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}'); | 552 var tReturnType = getField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}'); |
552 if (!isAssignable(sReturnType, tReturnType)) return false; | 553 if (!isAssignable(sReturnType, tReturnType)) return false; |
553 } | 554 } |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
678 * `null` and `undefined` (which we can avoid). | 679 * `null` and `undefined` (which we can avoid). |
679 */ | 680 */ |
680 bool isIdentical(var s, var t) => JS('bool', '# === #', s, t); | 681 bool isIdentical(var s, var t) => JS('bool', '# === #', s, t); |
681 | 682 |
682 /** | 683 /** |
683 * Returns `true` if the JavaScript values [s] and [t] are not identical. We use | 684 * Returns `true` if the JavaScript values [s] and [t] are not identical. We use |
684 * this helper instead of [identical] because `identical` needs to merge | 685 * this helper instead of [identical] because `identical` needs to merge |
685 * `null` and `undefined` (which we can avoid). | 686 * `null` and `undefined` (which we can avoid). |
686 */ | 687 */ |
687 bool isNotIdentical(var s, var t) => JS('bool', '# !== #', s, t); | 688 bool isNotIdentical(var s, var t) => JS('bool', '# !== #', s, t); |
OLD | NEW |