| 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 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 final int bound; | 76 final int bound; |
| 77 | 77 |
| 78 const TypeVariable(this.owner, this.name, this.bound); | 78 const TypeVariable(this.owner, this.name, this.bound); |
| 79 } | 79 } |
| 80 | 80 |
| 81 getMangledTypeName(TypeImpl type) => type._typeName; | 81 getMangledTypeName(TypeImpl type) => type._typeName; |
| 82 | 82 |
| 83 /** | 83 /** |
| 84 * Sets the runtime type information on [target]. [typeInfo] is a type | 84 * Sets the runtime type information on [target]. [typeInfo] is a type |
| 85 * representation of type 4 or 5, that is, either a JavaScript array or | 85 * representation of type 4 or 5, that is, either a JavaScript array or |
| 86 * [:null:]. | 86 * `null`. |
| 87 */ | 87 */ |
| 88 Object setRuntimeTypeInfo(Object target, var typeInfo) { | 88 Object setRuntimeTypeInfo(Object target, var typeInfo) { |
| 89 assert(isNull(typeInfo) || isJsArray(typeInfo)); | 89 assert(typeInfo == null || isJsArray(typeInfo)); |
| 90 // We have to check for null because factories may return null. | 90 // We have to check for null because factories may return null. |
| 91 if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); | 91 if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); |
| 92 return target; | 92 return target; |
| 93 } | 93 } |
| 94 | 94 |
| 95 /** | 95 /** |
| 96 * Returns the runtime type information of [target]. The returned value is a | 96 * Returns the runtime type information of [target]. The returned value is a |
| 97 * list of type representations for the type arguments. | 97 * list of type representations for the type arguments. |
| 98 */ | 98 */ |
| 99 getRuntimeTypeInfo(Object target) { | 99 getRuntimeTypeInfo(Object target) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 110 return substitute(substitution, getRuntimeTypeInfo(target)); | 110 return substitute(substitution, getRuntimeTypeInfo(target)); |
| 111 } | 111 } |
| 112 | 112 |
| 113 /** | 113 /** |
| 114 * Returns the [index]th type argument of [target] as an instance of | 114 * Returns the [index]th type argument of [target] as an instance of |
| 115 * [substitutionName]. | 115 * [substitutionName]. |
| 116 */ | 116 */ |
| 117 @NoThrows() @NoSideEffects() @NoInline() | 117 @NoThrows() @NoSideEffects() @NoInline() |
| 118 getRuntimeTypeArgument(Object target, String substitutionName, int index) { | 118 getRuntimeTypeArgument(Object target, String substitutionName, int index) { |
| 119 var arguments = getRuntimeTypeArguments(target, substitutionName); | 119 var arguments = getRuntimeTypeArguments(target, substitutionName); |
| 120 return isNull(arguments) ? null : getIndex(arguments, index); | 120 return arguments == null ? null : getIndex(arguments, index); |
| 121 } | 121 } |
| 122 | 122 |
| 123 @NoThrows() @NoSideEffects() @NoInline() | 123 @NoThrows() @NoSideEffects() @NoInline() |
| 124 getTypeArgumentByIndex(Object target, int index) { | 124 getTypeArgumentByIndex(Object target, int index) { |
| 125 var rti = getRuntimeTypeInfo(target); | 125 var rti = getRuntimeTypeInfo(target); |
| 126 return isNull(rti) ? null : getIndex(rti, index); | 126 return rti == null ? null : getIndex(rti, index); |
| 127 } | 127 } |
| 128 | 128 |
| 129 void copyTypeArguments(Object source, Object target) { | 129 void copyTypeArguments(Object source, Object target) { |
| 130 JS('var', r'#.$builtinTypeInfo = #.$builtinTypeInfo', target, source); | 130 JS('var', r'#.$builtinTypeInfo = #.$builtinTypeInfo', target, source); |
| 131 } | 131 } |
| 132 | 132 |
| 133 /** | 133 /** |
| 134 * Retrieves the class name from type information stored on the constructor | 134 * Retrieves the class name from type information stored on the constructor |
| 135 * of [object]. | 135 * of [object]. |
| 136 */ | 136 */ |
| (...skipping 16 matching lines...) Expand all Loading... |
| 153 /** | 153 /** |
| 154 * Retrieves the class name from type information stored on the constructor | 154 * Retrieves the class name from type information stored on the constructor |
| 155 * [type]. | 155 * [type]. |
| 156 */ | 156 */ |
| 157 String getConstructorName(var type) => JS('String', r'#.builtin$cls', type); | 157 String getConstructorName(var type) => JS('String', r'#.builtin$cls', type); |
| 158 | 158 |
| 159 /** | 159 /** |
| 160 * Returns a human-readable representation of the type representation [type]. | 160 * Returns a human-readable representation of the type representation [type]. |
| 161 */ | 161 */ |
| 162 String runtimeTypeToString(var type, {String onTypeVariable(int i)}) { | 162 String runtimeTypeToString(var type, {String onTypeVariable(int i)}) { |
| 163 if (isNull(type)) { | 163 if (type == null) { |
| 164 return 'dynamic'; | 164 return 'dynamic'; |
| 165 } else if (isJsArray(type)) { | 165 } else if (isJsArray(type)) { |
| 166 // A list representing a type with arguments. | 166 // A list representing a type with arguments. |
| 167 return getRuntimeTypeAsString(type, onTypeVariable: onTypeVariable); | 167 return getRuntimeTypeAsString(type, onTypeVariable: onTypeVariable); |
| 168 } else if (isJsFunction(type)) { | 168 } else if (isJsFunction(type)) { |
| 169 // A reference to the constructor. | 169 // A reference to the constructor. |
| 170 return getConstructorName(type); | 170 return getConstructorName(type); |
| 171 } else if (type is int) { | 171 } else if (type is int) { |
| 172 if (onTypeVariable == null) { | 172 if (onTypeVariable == null) { |
| 173 return type.toString(); | 173 return type.toString(); |
| 174 } else { | 174 } else { |
| 175 return onTypeVariable(type); | 175 return onTypeVariable(type); |
| 176 } | 176 } |
| 177 } else { | 177 } else { |
| 178 // TODO(ahe): Handle function types, and be sure to always return a string. | 178 // TODO(ahe): Handle function types, and be sure to always return a string. |
| 179 return null; | 179 return null; |
| 180 } | 180 } |
| 181 } | 181 } |
| 182 | 182 |
| 183 /** | 183 /** |
| 184 * Creates a comma-separated string of human-readable representations of the | 184 * Creates a comma-separated string of human-readable representations of the |
| 185 * type representations in the JavaScript array [types] starting at index | 185 * type representations in the JavaScript array [types] starting at index |
| 186 * [startIndex]. | 186 * [startIndex]. |
| 187 */ | 187 */ |
| 188 String joinArguments(var types, int startIndex, | 188 String joinArguments(var types, int startIndex, |
| 189 {String onTypeVariable(int i)}) { | 189 {String onTypeVariable(int i)}) { |
| 190 if (isNull(types)) return ''; | 190 if (types == null) return ''; |
| 191 assert(isJsArray(types)); | 191 assert(isJsArray(types)); |
| 192 bool firstArgument = true; | 192 bool firstArgument = true; |
| 193 bool allDynamic = true; | 193 bool allDynamic = true; |
| 194 StringBuffer buffer = new StringBuffer(); | 194 StringBuffer buffer = new StringBuffer(); |
| 195 for (int index = startIndex; index < getLength(types); index++) { | 195 for (int index = startIndex; index < getLength(types); index++) { |
| 196 if (firstArgument) { | 196 if (firstArgument) { |
| 197 firstArgument = false; | 197 firstArgument = false; |
| 198 } else { | 198 } else { |
| 199 buffer.write(', '); | 199 buffer.write(', '); |
| 200 } | 200 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 224 return new TypeImpl(type); | 224 return new TypeImpl(type); |
| 225 } | 225 } |
| 226 | 226 |
| 227 /** | 227 /** |
| 228 * Applies the [substitution] on the [arguments]. | 228 * Applies the [substitution] on the [arguments]. |
| 229 * | 229 * |
| 230 * See the comment in the beginning of this file for a description of the | 230 * See the comment in the beginning of this file for a description of the |
| 231 * possible values for [substitution]. | 231 * possible values for [substitution]. |
| 232 */ | 232 */ |
| 233 substitute(var substitution, var arguments) { | 233 substitute(var substitution, var arguments) { |
| 234 assert(isNull(substitution) || | 234 assert(substitution == null || |
| 235 isJsFunction(substitution)); | 235 isJsFunction(substitution)); |
| 236 assert(isNull(arguments) || isJsArray(arguments)); | 236 assert(arguments == null || isJsArray(arguments)); |
| 237 if (isJsFunction(substitution)) { | 237 if (isJsFunction(substitution)) { |
| 238 substitution = invoke(substitution, arguments); | 238 substitution = invoke(substitution, arguments); |
| 239 if (isJsArray(substitution)) { | 239 if (isJsArray(substitution)) { |
| 240 arguments = substitution; | 240 arguments = substitution; |
| 241 } else if (isJsFunction(substitution)) { | 241 } else if (isJsFunction(substitution)) { |
| 242 // TODO(johnniwinther): Check if this is still needed. | 242 // TODO(johnniwinther): Check if this is still needed. |
| 243 arguments = invoke(substitution, arguments); | 243 arguments = invoke(substitution, arguments); |
| 244 } | 244 } |
| 245 } | 245 } |
| 246 return arguments; | 246 return arguments; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 260 */ | 260 */ |
| 261 bool checkSubtype(Object object, String isField, List checks, String asField) { | 261 bool checkSubtype(Object object, String isField, List checks, String asField) { |
| 262 if (object == null) return false; | 262 if (object == null) return false; |
| 263 var arguments = getRuntimeTypeInfo(object); | 263 var arguments = getRuntimeTypeInfo(object); |
| 264 // Interceptor is needed for JSArray and native classes. | 264 // Interceptor is needed for JSArray and native classes. |
| 265 // TODO(sra): It could be a more specialized interceptor since [object] is not | 265 // TODO(sra): It could be a more specialized interceptor since [object] is not |
| 266 // `null` or a primitive. | 266 // `null` or a primitive. |
| 267 // TODO(9586): Move type info for static functions onto an interceptor. | 267 // TODO(9586): Move type info for static functions onto an interceptor. |
| 268 var interceptor = getInterceptor(object); | 268 var interceptor = getInterceptor(object); |
| 269 var isSubclass = getField(interceptor, isField); | 269 var isSubclass = getField(interceptor, isField); |
| 270 // When we read the field and it is not there, [isSubclass] will be [:null:]. | 270 // When we read the field and it is not there, [isSubclass] will be `null`. |
| 271 if (isNull(isSubclass)) return false; | 271 if (isSubclass == null) return false; |
| 272 // Should the asField function be passed the receiver? | 272 // Should the asField function be passed the receiver? |
| 273 var substitution = getField(interceptor, asField); | 273 var substitution = getField(interceptor, asField); |
| 274 return checkArguments(substitution, arguments, checks); | 274 return checkArguments(substitution, arguments, checks); |
| 275 } | 275 } |
| 276 | 276 |
| 277 /// Returns the field's type name. | 277 /// Returns the field's type name. |
| 278 /// | 278 /// |
| 279 /// In minified mode, uses the unminified names if available. | 279 /// In minified mode, uses the unminified names if available. |
| 280 String computeTypeName(String isField, List arguments) { | 280 String computeTypeName(String isField, List arguments) { |
| 281 // Shorten the field name to the class name and append the textual | 281 // Shorten the field name to the class name and append the textual |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 325 * See the comment in the beginning of this file for a description of the | 325 * See the comment in the beginning of this file for a description of the |
| 326 * possible values for [substitution]. | 326 * possible values for [substitution]. |
| 327 */ | 327 */ |
| 328 bool checkArguments(var substitution, var arguments, var checks) { | 328 bool checkArguments(var substitution, var arguments, var checks) { |
| 329 return areSubtypes(substitute(substitution, arguments), checks); | 329 return areSubtypes(substitute(substitution, arguments), checks); |
| 330 } | 330 } |
| 331 | 331 |
| 332 /** | 332 /** |
| 333 * Checks whether the types of [s] are all subtypes of the types of [t]. | 333 * Checks whether the types of [s] are all subtypes of the types of [t]. |
| 334 * | 334 * |
| 335 * [s] and [t] are either [:null:] or JavaScript arrays of type representations, | 335 * [s] and [t] are either `null` or JavaScript arrays of type representations, |
| 336 * A [:null:] argument is interpreted as the arguments of a raw type, that is a | 336 * A `null` argument is interpreted as the arguments of a raw type, that is a |
| 337 * list of [:dynamic:]. If [s] and [t] are JavaScript arrays they must be of the | 337 * list of `dynamic`. If [s] and [t] are JavaScript arrays they must be of the |
| 338 * same length. | 338 * same length. |
| 339 * | 339 * |
| 340 * See the comment in the beginning of this file for a description of type | 340 * See the comment in the beginning of this file for a description of type |
| 341 * representations. | 341 * representations. |
| 342 */ | 342 */ |
| 343 bool areSubtypes(var s, var t) { | 343 bool areSubtypes(var s, var t) { |
| 344 // [:null:] means a raw type. | 344 // `null` means a raw type. |
| 345 if (isNull(s) || isNull(t)) return true; | 345 if (s == null || t == null) return true; |
| 346 | 346 |
| 347 assert(isJsArray(s)); | 347 assert(isJsArray(s)); |
| 348 assert(isJsArray(t)); | 348 assert(isJsArray(t)); |
| 349 assert(getLength(s) == getLength(t)); | 349 assert(getLength(s) == getLength(t)); |
| 350 | 350 |
| 351 int len = getLength(s); | 351 int len = getLength(s); |
| 352 for (int i = 0; i < len; i++) { | 352 for (int i = 0; i < len; i++) { |
| 353 if (!isSubtype(getIndex(s, i), getIndex(t, i))) { | 353 if (!isSubtype(getIndex(s, i), getIndex(t, i))) { |
| 354 return false; | 354 return false; |
| 355 } | 355 } |
| 356 } | 356 } |
| 357 return true; | 357 return true; |
| 358 } | 358 } |
| 359 | 359 |
| 360 /** | 360 /** |
| 361 * Computes the signature by applying the type arguments of [context] as an | 361 * Computes the signature by applying the type arguments of [context] as an |
| 362 * instance of [contextName] to the signature function [signature]. | 362 * instance of [contextName] to the signature function [signature]. |
| 363 */ | 363 */ |
| 364 computeSignature(var signature, var context, var contextName) { | 364 computeSignature(var signature, var context, var contextName) { |
| 365 var typeArguments = getRuntimeTypeArguments(context, contextName); | 365 var typeArguments = getRuntimeTypeArguments(context, contextName); |
| 366 return invokeOn(signature, context, typeArguments); | 366 return invokeOn(signature, context, typeArguments); |
| 367 } | 367 } |
| 368 | 368 |
| 369 /** | 369 /** |
| 370 * Returns [:true:] if the runtime type representation [type] is a supertype of | 370 * Returns `true` if the runtime type representation [type] is a supertype of |
| 371 * [:Null:]. | 371 * [Null]. |
| 372 */ | 372 */ |
| 373 bool isSupertypeOfNull(var type) { | 373 bool isSupertypeOfNull(var type) { |
| 374 // `null` means `dynamic`. | 374 // `null` means `dynamic`. |
| 375 return isNull(type) || getConstructorName(type) == JS_OBJECT_CLASS_NAME() | 375 return type == null || getConstructorName(type) == JS_OBJECT_CLASS_NAME() |
| 376 || getConstructorName(type) == JS_NULL_CLASS_NAME(); | 376 || getConstructorName(type) == JS_NULL_CLASS_NAME(); |
| 377 } | 377 } |
| 378 | 378 |
| 379 /** | 379 /** |
| 380 * Tests whether the Dart object [o] is a subtype of the runtime type | 380 * Tests whether the Dart object [o] is a subtype of the runtime type |
| 381 * representation [t]. | 381 * representation [t]. |
| 382 * | 382 * |
| 383 * See the comment in the beginning of this file for a description of type | 383 * See the comment in the beginning of this file for a description of type |
| 384 * representations. | 384 * representations. |
| 385 */ | 385 */ |
| 386 bool checkSubtypeOfRuntimeType(o, t) { | 386 bool checkSubtypeOfRuntimeType(o, t) { |
| 387 if (isNull(o)) return isSupertypeOfNull(t); | 387 if (o == null) return isSupertypeOfNull(t); |
| 388 if (isNull(t)) return true; | 388 if (t == null) return true; |
| 389 // Get the runtime type information from the object here, because we may | 389 // Get the runtime type information from the object here, because we may |
| 390 // overwrite o with the interceptor below. | 390 // overwrite o with the interceptor below. |
| 391 var rti = getRuntimeTypeInfo(o); | 391 var rti = getRuntimeTypeInfo(o); |
| 392 o = getInterceptor(o); | 392 o = getInterceptor(o); |
| 393 var type = JS('', '#.constructor', o); | 393 var type = JS('', '#.constructor', o); |
| 394 if (isNotNull(rti)) { | 394 if (rti != null) { |
| 395 // If the type has type variables (that is, [:rti != null:]), make a copy of | 395 // If the type has type variables (that is, `rti != null`), make a copy of |
| 396 // the type arguments and insert [o] in the first position to create a | 396 // the type arguments and insert [o] in the first position to create a |
| 397 // compound type representation. | 397 // compound type representation. |
| 398 rti = JS('JSExtendableArray', '#.slice()', rti); // Make a copy. | 398 rti = JS('JSExtendableArray', '#.slice()', rti); // Make a copy. |
| 399 JS('', '#.splice(0, 0, #)', rti, type); // Insert type at position 0. | 399 JS('', '#.splice(0, 0, #)', rti, type); // Insert type at position 0. |
| 400 type = rti; | 400 type = rti; |
| 401 } else if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { | 401 } else if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { |
| 402 // Functions are treated specially and have their type information stored | 402 // Functions are treated specially and have their type information stored |
| 403 // directly in the instance. | 403 // directly in the instance. |
| 404 var signatureName = | 404 var signatureName = |
| 405 '${JS_OPERATOR_IS_PREFIX()}_${getField(t, JS_FUNCTION_TYPE_TAG())}'; | 405 '${JS_OPERATOR_IS_PREFIX()}_${getField(t, JS_FUNCTION_TYPE_TAG())}'; |
| 406 if (hasField(o, signatureName)) return true; | 406 if (hasField(o, signatureName)) return true; |
| 407 var targetSignatureFunction = getField(o, '${JS_SIGNATURE_NAME()}'); | 407 var targetSignatureFunction = getField(o, '${JS_SIGNATURE_NAME()}'); |
| 408 if (isNull(targetSignatureFunction)) return false; | 408 if (targetSignatureFunction == null) return false; |
| 409 type = invokeOn(targetSignatureFunction, o, null); | 409 type = invokeOn(targetSignatureFunction, o, null); |
| 410 return isFunctionSubtype(type, t); | 410 return isFunctionSubtype(type, t); |
| 411 } | 411 } |
| 412 return isSubtype(type, t); | 412 return isSubtype(type, t); |
| 413 } | 413 } |
| 414 | 414 |
| 415 Object subtypeOfRuntimeTypeCast(Object object, var type) { | 415 Object subtypeOfRuntimeTypeCast(Object object, var type) { |
| 416 if (object != null && !checkSubtypeOfRuntimeType(object, type)) { | 416 if (object != null && !checkSubtypeOfRuntimeType(object, type)) { |
| 417 String actualType = Primitives.objectTypeName(object); | 417 String actualType = Primitives.objectTypeName(object); |
| 418 throw new CastErrorImplementation(actualType, runtimeTypeToString(type)); | 418 throw new CastErrorImplementation(actualType, runtimeTypeToString(type)); |
| 419 } | 419 } |
| 420 return object; | 420 return object; |
| 421 } | 421 } |
| 422 | 422 |
| 423 Object assertSubtypeOfRuntimeType(Object object, var type) { | 423 Object assertSubtypeOfRuntimeType(Object object, var type) { |
| 424 if (object != null && !checkSubtypeOfRuntimeType(object, type)) { | 424 if (object != null && !checkSubtypeOfRuntimeType(object, type)) { |
| 425 throw new TypeErrorImplementation(object, runtimeTypeToString(type)); | 425 throw new TypeErrorImplementation(object, runtimeTypeToString(type)); |
| 426 } | 426 } |
| 427 return object; | 427 return object; |
| 428 } | 428 } |
| 429 | 429 |
| 430 /** | 430 /** |
| 431 * Extracts the type arguments from a type representation. The result is a | 431 * Extracts the type arguments from a type representation. The result is a |
| 432 * JavaScript array or [:null:]. | 432 * JavaScript array or `null`. |
| 433 */ | 433 */ |
| 434 getArguments(var type) { | 434 getArguments(var type) { |
| 435 return isJsArray(type) ? JS('var', r'#.slice(1)', type) : null; | 435 return isJsArray(type) ? JS('var', r'#.slice(1)', type) : null; |
| 436 } | 436 } |
| 437 | 437 |
| 438 /** | 438 /** |
| 439 * Checks whether the type represented by the type representation [s] is a | 439 * Checks whether the type represented by the type representation [s] is a |
| 440 * subtype of the type represented by the type representation [t]. | 440 * subtype of the type represented by the type representation [t]. |
| 441 * | 441 * |
| 442 * See the comment in the beginning of this file for a description of type | 442 * See the comment in the beginning of this file for a description of type |
| 443 * representations. | 443 * representations. |
| 444 * | 444 * |
| 445 * The arguments [s] and [t] must be types, usually represented by the | 445 * The arguments [s] and [t] must be types, usually represented by the |
| 446 * constructor of the class, or an array (for generic types). | 446 * constructor of the class, or an array (for generic types). |
| 447 */ | 447 */ |
| 448 bool isSubtype(var s, var t) { | 448 bool isSubtype(var s, var t) { |
| 449 // Subtyping is reflexive. | 449 // Subtyping is reflexive. |
| 450 if (isIdentical(s, t)) return true; | 450 if (isIdentical(s, t)) return true; |
| 451 // If either type is dynamic, [s] is a subtype of [t]. | 451 // If either type is dynamic, [s] is a subtype of [t]. |
| 452 if (isNull(s) || isNull(t)) return true; | 452 if (s == null || t == null) return true; |
| 453 if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { | 453 if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { |
| 454 return isFunctionSubtype(s, t); | 454 return isFunctionSubtype(s, t); |
| 455 } | 455 } |
| 456 // Check function types against the Function class. | 456 // Check function types against the Function class. |
| 457 if (hasField(s, '${JS_FUNCTION_TYPE_TAG()}')) { | 457 if (hasField(s, '${JS_FUNCTION_TYPE_TAG()}')) { |
| 458 return getConstructorName(t) == JS_FUNCTION_CLASS_NAME(); | 458 return getConstructorName(t) == JS_FUNCTION_CLASS_NAME(); |
| 459 } | 459 } |
| 460 | 460 |
| 461 // Get the object describing the class and check for the subtyping flag | 461 // Get the object describing the class and check for the subtyping flag |
| 462 // constructed from the type of [t]. | 462 // constructed from the type of [t]. |
| 463 var typeOfS = isJsArray(s) ? getIndex(s, 0) : s; | 463 var typeOfS = isJsArray(s) ? getIndex(s, 0) : s; |
| 464 var typeOfT = isJsArray(t) ? getIndex(t, 0) : t; | 464 var typeOfT = isJsArray(t) ? getIndex(t, 0) : t; |
| 465 // Check for a subtyping flag. | 465 // Check for a subtyping flag. |
| 466 var name = runtimeTypeToString(typeOfT); | 466 var name = runtimeTypeToString(typeOfT); |
| 467 // Get the necessary substitution of the type arguments, if there is one. | 467 // Get the necessary substitution of the type arguments, if there is one. |
| 468 var substitution; | 468 var substitution; |
| 469 if (isNotIdentical(typeOfT, typeOfS)) { | 469 if (isNotIdentical(typeOfT, typeOfS)) { |
| 470 var test = '${JS_OPERATOR_IS_PREFIX()}${name}'; | 470 var test = '${JS_OPERATOR_IS_PREFIX()}${name}'; |
| 471 var typeOfSPrototype = JS('', '#.prototype', typeOfS); | 471 var typeOfSPrototype = JS('', '#.prototype', typeOfS); |
| 472 if (hasNoField(typeOfSPrototype, test)) return false; | 472 if (hasNoField(typeOfSPrototype, test)) return false; |
| 473 var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; | 473 var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; |
| 474 substitution = getField(typeOfSPrototype, field); | 474 substitution = getField(typeOfSPrototype, field); |
| 475 } | 475 } |
| 476 // 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 |
| 477 // 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 |
| 478 // 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 |
| 479 // of [t]. | 479 // of [t]. |
| 480 if ((!isJsArray(s) && isNull(substitution)) || !isJsArray(t)) { | 480 if ((!isJsArray(s) && substitution == null) || !isJsArray(t)) { |
| 481 return true; | 481 return true; |
| 482 } | 482 } |
| 483 // Recursively check the type arguments. | 483 // Recursively check the type arguments. |
| 484 return checkArguments(substitution, getArguments(s), getArguments(t)); | 484 return checkArguments(substitution, getArguments(s), getArguments(t)); |
| 485 } | 485 } |
| 486 | 486 |
| 487 bool isAssignable(var s, var t) { | 487 bool isAssignable(var s, var t) { |
| 488 return isSubtype(s, t) || isSubtype(t, s); | 488 return isSubtype(s, t) || isSubtype(t, s); |
| 489 } | 489 } |
| 490 | 490 |
| 491 /** | 491 /** |
| 492 * If [allowShorter] is [:true:], [t] is allowed to be shorter than [s]. | 492 * If [allowShorter] is `true`, [t] is allowed to be shorter than [s]. |
| 493 */ | 493 */ |
| 494 bool areAssignable(List s, List t, bool allowShorter) { | 494 bool areAssignable(List s, List t, bool allowShorter) { |
| 495 // Both lists are empty and thus equal. | 495 // Both lists are empty and thus equal. |
| 496 if (isNull(t) && isNull(s)) return true; | 496 if (t ==null && s == null) return true; |
| 497 // [t] is empty (and [s] is not) => only OK if [allowShorter]. | 497 // [t] is empty (and [s] is not) => only OK if [allowShorter]. |
| 498 if (isNull(t)) return allowShorter; | 498 if (t == null) return allowShorter; |
| 499 // [s] is empty (and [t] is not) => [s] is not longer or equal to [t]. | 499 // [s] is empty (and [t] is not) => [s] is not longer or equal to [t]. |
| 500 if (isNull(s)) return false; | 500 if (s == null) return false; |
| 501 | 501 |
| 502 assert(isJsArray(s)); | 502 assert(isJsArray(s)); |
| 503 assert(isJsArray(t)); | 503 assert(isJsArray(t)); |
| 504 | 504 |
| 505 int sLength = getLength(s); | 505 int sLength = getLength(s); |
| 506 int tLength = getLength(t); | 506 int tLength = getLength(t); |
| 507 if (allowShorter) { | 507 if (allowShorter) { |
| 508 if (sLength < tLength) return false; | 508 if (sLength < tLength) return false; |
| 509 } else { | 509 } else { |
| 510 if (sLength != tLength) return false; | 510 if (sLength != tLength) return false; |
| 511 } | 511 } |
| 512 | 512 |
| 513 for (int i = 0; i < tLength; i++) { | 513 for (int i = 0; i < tLength; i++) { |
| 514 if (!isAssignable(getIndex(s, i), getIndex(t, i))) { | 514 if (!isAssignable(getIndex(s, i), getIndex(t, i))) { |
| 515 return false; | 515 return false; |
| 516 } | 516 } |
| 517 } | 517 } |
| 518 return true; | 518 return true; |
| 519 } | 519 } |
| 520 | 520 |
| 521 bool areAssignableMaps(var s, var t) { | 521 bool areAssignableMaps(var s, var t) { |
| 522 if (isNull(t)) return true; | 522 if (t == null) return true; |
| 523 if (isNull(s)) return false; | 523 if (s == null) return false; |
| 524 | 524 |
| 525 assert(isJsObject(s)); | 525 assert(isJsObject(s)); |
| 526 assert(isJsObject(t)); | 526 assert(isJsObject(t)); |
| 527 | 527 |
| 528 List names = | 528 List names = |
| 529 JSArray.markFixedList(JS('', 'Object.getOwnPropertyNames(#)', t)); | 529 JSArray.markFixedList(JS('', 'Object.getOwnPropertyNames(#)', t)); |
| 530 for (int i = 0; i < names.length; i++) { | 530 for (int i = 0; i < names.length; i++) { |
| 531 var name = names[i]; | 531 var name = names[i]; |
| 532 if (JS('bool', '!Object.hasOwnProperty.call(#, #)', s, name)) { | 532 if (JS('bool', '!Object.hasOwnProperty.call(#, #)', s, name)) { |
| 533 return false; | 533 return false; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 555 var sParameterTypes = | 555 var sParameterTypes = |
| 556 getField(s, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}'); | 556 getField(s, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}'); |
| 557 var tParameterTypes = | 557 var tParameterTypes = |
| 558 getField(t, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}'); | 558 getField(t, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}'); |
| 559 | 559 |
| 560 var sOptionalParameterTypes = | 560 var sOptionalParameterTypes = |
| 561 getField(s, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}'); | 561 getField(s, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}'); |
| 562 var tOptionalParameterTypes = | 562 var tOptionalParameterTypes = |
| 563 getField(t, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}'); | 563 getField(t, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}'); |
| 564 | 564 |
| 565 int sParametersLen = | 565 int sParametersLen = sParameterTypes != null ? getLength(sParameterTypes) : 0; |
| 566 isNotNull(sParameterTypes) ? getLength(sParameterTypes) : 0; | 566 int tParametersLen = tParameterTypes != null ? getLength(tParameterTypes) : 0; |
| 567 int tParametersLen = | |
| 568 isNotNull(tParameterTypes) ? getLength(tParameterTypes) : 0; | |
| 569 | 567 |
| 570 int sOptionalParametersLen = isNotNull(sOptionalParameterTypes) | 568 int sOptionalParametersLen = |
| 571 ? getLength(sOptionalParameterTypes) : 0; | 569 sOptionalParameterTypes != null ? getLength(sOptionalParameterTypes) : 0; |
| 572 int tOptionalParametersLen = isNotNull(tOptionalParameterTypes) | 570 int tOptionalParametersLen = |
| 573 ? getLength(tOptionalParameterTypes) : 0; | 571 tOptionalParameterTypes != null ? getLength(tOptionalParameterTypes) : 0; |
| 574 | 572 |
| 575 if (sParametersLen > tParametersLen) { | 573 if (sParametersLen > tParametersLen) { |
| 576 // Too many required parameters in [s]. | 574 // Too many required parameters in [s]. |
| 577 return false; | 575 return false; |
| 578 } | 576 } |
| 579 if (sParametersLen + sOptionalParametersLen < | 577 if (sParametersLen + sOptionalParametersLen < |
| 580 tParametersLen + tOptionalParametersLen) { | 578 tParametersLen + tOptionalParametersLen) { |
| 581 // Too few required and optional parameters in [s]. | 579 // Too few required and optional parameters in [s]. |
| 582 return false; | 580 return false; |
| 583 } | 581 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 621 | 619 |
| 622 var sNamedParameters = | 620 var sNamedParameters = |
| 623 getField(s, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}'); | 621 getField(s, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}'); |
| 624 var tNamedParameters = | 622 var tNamedParameters = |
| 625 getField(t, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}'); | 623 getField(t, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}'); |
| 626 return areAssignableMaps(sNamedParameters, tNamedParameters); | 624 return areAssignableMaps(sNamedParameters, tNamedParameters); |
| 627 } | 625 } |
| 628 | 626 |
| 629 /** | 627 /** |
| 630 * Calls the JavaScript [function] with the [arguments] with the global scope | 628 * Calls the JavaScript [function] with the [arguments] with the global scope |
| 631 * as the [:this:] context. | 629 * as the `this` context. |
| 632 */ | 630 */ |
| 633 invoke(var function, var arguments) => invokeOn(function, null, arguments); | 631 invoke(var function, var arguments) => invokeOn(function, null, arguments); |
| 634 | 632 |
| 635 /** | 633 /** |
| 636 * Calls the JavaScript [function] with the [arguments] with [receiver] as the | 634 * Calls the JavaScript [function] with the [arguments] with [receiver] as the |
| 637 * [:this:] context. | 635 * `this` context. |
| 638 */ | 636 */ |
| 639 Object invokeOn(function, receiver, arguments) { | 637 Object invokeOn(function, receiver, arguments) { |
| 640 assert(isJsFunction(function)); | 638 assert(isJsFunction(function)); |
| 641 assert(isNull(arguments) || isJsArray(arguments)); | 639 assert(arguments == null || isJsArray(arguments)); |
| 642 return JS('var', r'#.apply(#, #)', function, receiver, arguments); | 640 return JS('var', r'#.apply(#, #)', function, receiver, arguments); |
| 643 } | 641 } |
| 644 | 642 |
| 645 /// Calls the property [name] on the JavaScript [object]. | 643 /// Calls the property [name] on the JavaScript [object]. |
| 646 call(var object, String name) => JS('var', r'#[#]()', object, name); | 644 call(var object, String name) => JS('var', r'#[#]()', object, name); |
| 647 | 645 |
| 648 /// Returns the property [name] of the JavaScript object [object]. | 646 /// Returns the property [name] of the JavaScript object [object]. |
| 649 getField(var object, String name) => JS('var', r'#[#]', object, name); | 647 getField(var object, String name) => JS('var', r'#[#]', object, name); |
| 650 | 648 |
| 651 /// Returns the property [index] of the JavaScript array [array]. | 649 /// Returns the property [index] of the JavaScript array [array]. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 662 | 660 |
| 663 /// Returns whether [value] is a JavaScript array. | 661 /// Returns whether [value] is a JavaScript array. |
| 664 bool isJsArray(var value) { | 662 bool isJsArray(var value) { |
| 665 return value is JSArray; | 663 return value is JSArray; |
| 666 } | 664 } |
| 667 | 665 |
| 668 hasField(var object, var name) => JS('bool', r'# in #', name, object); | 666 hasField(var object, var name) => JS('bool', r'# in #', name, object); |
| 669 | 667 |
| 670 hasNoField(var object, var name) => !hasField(object, name); | 668 hasNoField(var object, var name) => !hasField(object, name); |
| 671 | 669 |
| 672 /// Returns [:true:] if [o] is a JavaScript function. | 670 /// Returns `true` if [o] is a JavaScript function. |
| 673 bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o); | 671 bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o); |
| 674 | 672 |
| 675 /// Returns [:true:] if [o] is a JavaScript object. | 673 /// Returns `true` if [o] is a JavaScript object. |
| 676 bool isJsObject(var o) => JS('bool', r"typeof # == 'object'", o); | 674 bool isJsObject(var o) => JS('bool', r"typeof # == 'object'", o); |
| 677 | 675 |
| 678 /** | 676 /** |
| 679 * Returns [:true:] if [o] is equal to [:null:], that is either [:null:] or | 677 * Returns `true` if the JavaScript values [s] and [t] are identical. We use |
| 680 * [:undefined:]. We use this helper to avoid generating code under the invalid | 678 * this helper instead of [identical] because `identical` needs to merge |
| 681 * assumption that [o] is a Dart value. | 679 * `null` and `undefined` (which we can avoid). |
| 682 */ | |
| 683 bool isNull(var o) => JS('bool', '# == null', o); | |
| 684 | |
| 685 /** | |
| 686 * Returns [:true:] if [o] is not equal to [:null:], that is neither [:null:] | |
| 687 * nor [:undefined:]. We use this helper to avoid generating code under the | |
| 688 * invalid assumption that [o] is a Dart value. | |
| 689 */ | |
| 690 bool isNotNull(var o) => JS('bool', '# != null', o); | |
| 691 | |
| 692 /** | |
| 693 * Returns [:true:] if the JavaScript values [s] and [t] are identical. We use | |
| 694 * this helper to avoid generating code under the invalid assumption that [s] | |
| 695 * and [t] are Dart values. | |
| 696 */ | 680 */ |
| 697 bool isIdentical(var s, var t) => JS('bool', '# === #', s, t); | 681 bool isIdentical(var s, var t) => JS('bool', '# === #', s, t); |
| 698 | 682 |
| 699 /** | 683 /** |
| 700 * Returns [:true:] if the JavaScript values [s] and [t] are not identical. We | 684 * Returns `true` if the JavaScript values [s] and [t] are not identical. We use |
| 701 * use this helper to avoid generating code under the invalid assumption that | 685 * this helper instead of [identical] because `identical` needs to merge |
| 702 * [s] and [t] are Dart values. | 686 * `null` and `undefined` (which we can avoid). |
| 703 */ | 687 */ |
| 704 bool isNotIdentical(var s, var t) => JS('bool', '# !== #', s, t); | 688 bool isNotIdentical(var s, var t) => JS('bool', '# !== #', s, t); |
| OLD | NEW |