| OLD | NEW | 
 | (Empty) | 
|    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 |  | 
|    3 // BSD-style license that can be found in the LICENSE file. |  | 
|    4  |  | 
|    5 /** |  | 
|    6  * This part contains helpers for supporting runtime type information. |  | 
|    7  * |  | 
|    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 |  | 
|   10  * objects and 'var' or omitted return type for JavaScript objects. |  | 
|   11  * |  | 
|   12  * Since bool, int, and String values are represented by the same JavaScript |  | 
|   13  * primitives, type annotations are used for these types in all cases. |  | 
|   14  * |  | 
|   15  * Several methods use a common JavaScript encoding of runtime type information. |  | 
|   16  * This encoding is referred to as the type representation which is one of |  | 
|   17  * these: |  | 
|   18  *  1) a JavaScript constructor for a class C: the represented type is the raw |  | 
|   19  *     type C. |  | 
|   20  *  2) a JavaScript array: the first entry is of type 1 and contains the |  | 
|   21  *     subtyping flags and the substitution of the type and the rest of the |  | 
|   22  *     array are the type arguments. |  | 
|   23  *  3) `null`: the dynamic type. |  | 
|   24  *  4) a JavaScript object representing the function type. For instance, it has |  | 
|   25  *     the form  {ret: rti, args: [rti], opt: [rti], named: {name: rti}} for a |  | 
|   26  *     function with a return type, regular, optional and named arguments. |  | 
|   27  * |  | 
|   28  * To check subtype relations between generic classes we use a JavaScript |  | 
|   29  * expression that describes the necessary substitution for type arguments. |  | 
|   30  * Such a substitution expresssion can be: |  | 
|   31  *  1) `null`, if no substituted check is necessary, because the |  | 
|   32  *     type variables are the same or there are no type variables in the class |  | 
|   33  *     that is checked for. |  | 
|   34  *  2) A list expression describing the type arguments to be used in the |  | 
|   35  *     subtype check, if the type arguments to be used in the check do not |  | 
|   36  *     depend on the type arguments of the object. |  | 
|   37  *  3) A function mapping the type variables of the object to be checked to |  | 
|   38  *     a list expression. The function may also return null, which is equivalent |  | 
|   39  *     to an array containing only null values. |  | 
|   40  */ |  | 
|   41  |  | 
|   42 part of _js_helper; |  | 
|   43  |  | 
|   44 Type createRuntimeType(String name) => new TypeImpl(name); |  | 
|   45  |  | 
|   46 class TypeImpl implements Type { |  | 
|   47   final String _typeName; |  | 
|   48   String _unmangledName; |  | 
|   49  |  | 
|   50   TypeImpl(this._typeName); |  | 
|   51  |  | 
|   52   String toString() { |  | 
|   53     if (_unmangledName != null) return _unmangledName; |  | 
|   54     String unmangledName = |  | 
|   55         unmangleAllIdentifiersIfPreservedAnyways(_typeName); |  | 
|   56     return _unmangledName = unmangledName; |  | 
|   57   } |  | 
|   58  |  | 
|   59   // TODO(ahe): This is a poor hashCode as it collides with its name. |  | 
|   60   int get hashCode => _typeName.hashCode; |  | 
|   61  |  | 
|   62   bool operator ==(other) { |  | 
|   63     return  (other is TypeImpl) && _typeName == other._typeName; |  | 
|   64   } |  | 
|   65 } |  | 
|   66  |  | 
|   67 /** |  | 
|   68  * Represents a type variable. |  | 
|   69  * |  | 
|   70  * This class holds the information needed when reflecting on generic classes |  | 
|   71  * and their members. |  | 
|   72  */ |  | 
|   73 class TypeVariable { |  | 
|   74   final Type owner; |  | 
|   75   final String name; |  | 
|   76   final int bound; |  | 
|   77  |  | 
|   78   const TypeVariable(this.owner, this.name, this.bound); |  | 
|   79 } |  | 
|   80  |  | 
|   81 getMangledTypeName(TypeImpl type) => type._typeName; |  | 
|   82  |  | 
|   83 /** |  | 
|   84  * Sets the runtime type information on [target]. [rti] is a type |  | 
|   85  * representation of type 4 or 5, that is, either a JavaScript array or |  | 
|   86  * `null`. |  | 
|   87  */ |  | 
|   88 Object setRuntimeTypeInfo(Object target, var rti) { |  | 
|   89   assert(rti == null || isJsArray(rti)); |  | 
|   90   // We have to check for null because factories may return null. |  | 
|   91   if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, rti); |  | 
|   92   return target; |  | 
|   93 } |  | 
|   94  |  | 
|   95 /** |  | 
|   96  * Returns the runtime type information of [target]. The returned value is a |  | 
|   97  * list of type representations for the type arguments. |  | 
|   98  */ |  | 
|   99 getRuntimeTypeInfo(Object target) { |  | 
|  100   if (target == null) return null; |  | 
|  101   return JS('var', r'#.$builtinTypeInfo', target); |  | 
|  102 } |  | 
|  103  |  | 
|  104 /** |  | 
|  105  * Returns the type arguments of [target] as an instance of [substitutionName]. |  | 
|  106  */ |  | 
|  107 getRuntimeTypeArguments(target, substitutionName) { |  | 
|  108   var substitution = getField(target, |  | 
|  109       '${JS_GET_NAME(JsGetName.OPERATOR_AS_PREFIX)}$substitutionName'); |  | 
|  110   return substitute(substitution, getRuntimeTypeInfo(target)); |  | 
|  111 } |  | 
|  112  |  | 
|  113 /** |  | 
|  114  * Returns the [index]th type argument of [target] as an instance of |  | 
|  115  * [substitutionName]. |  | 
|  116  */ |  | 
|  117 @NoThrows() @NoSideEffects() @NoInline() |  | 
|  118 getRuntimeTypeArgument(Object target, String substitutionName, int index) { |  | 
|  119   var arguments = getRuntimeTypeArguments(target, substitutionName); |  | 
|  120   return arguments == null ? null : getIndex(arguments, index); |  | 
|  121 } |  | 
|  122  |  | 
|  123 @NoThrows() @NoSideEffects() @NoInline() |  | 
|  124 getTypeArgumentByIndex(Object target, int index) { |  | 
|  125   var rti = getRuntimeTypeInfo(target); |  | 
|  126   return rti == null ? null : getIndex(rti, index); |  | 
|  127 } |  | 
|  128  |  | 
|  129 void copyTypeArguments(Object source, Object target) { |  | 
|  130   JS('var', r'#.$builtinTypeInfo = #.$builtinTypeInfo', target, source); |  | 
|  131 } |  | 
|  132  |  | 
|  133 /** |  | 
|  134  * Retrieves the class name from type information stored on the constructor |  | 
|  135  * of [object]. |  | 
|  136  */ |  | 
|  137 String getClassName(var object) { |  | 
|  138   return rawRtiToJsConstructorName(getRawRuntimeType(getInterceptor(object))); |  | 
|  139 } |  | 
|  140  |  | 
|  141 /** |  | 
|  142  * Creates the string representation for the type representation [rti] |  | 
|  143  * of type 4, the JavaScript array, where the first element represents the class |  | 
|  144  * and the remaining elements represent the type arguments. |  | 
|  145  */ |  | 
|  146 String getRuntimeTypeAsString(var rti, {String onTypeVariable(int i)}) { |  | 
|  147   assert(isJsArray(rti)); |  | 
|  148   String className = rawRtiToJsConstructorName(getIndex(rti, 0)); |  | 
|  149   return '$className${joinArguments(rti, 1, onTypeVariable: onTypeVariable)}'; |  | 
|  150 } |  | 
|  151  |  | 
|  152 /** |  | 
|  153  * Returns a human-readable representation of the type representation [rti]. |  | 
|  154  */ |  | 
|  155 String runtimeTypeToString(var rti, {String onTypeVariable(int i)}) { |  | 
|  156   if (rti == null) { |  | 
|  157     return 'dynamic'; |  | 
|  158   } else if (isJsArray(rti)) { |  | 
|  159     // A list representing a type with arguments. |  | 
|  160     return getRuntimeTypeAsString(rti, onTypeVariable: onTypeVariable); |  | 
|  161   } else if (isJsFunction(rti)) { |  | 
|  162     // A reference to the constructor. |  | 
|  163     return rawRtiToJsConstructorName(rti); |  | 
|  164   } else if (rti is int) { |  | 
|  165     if (onTypeVariable == null) { |  | 
|  166       return rti.toString(); |  | 
|  167     } else { |  | 
|  168       return onTypeVariable(rti); |  | 
|  169     } |  | 
|  170   } else { |  | 
|  171     // TODO(ahe): Handle function types, and be sure to always return a string. |  | 
|  172     return null; |  | 
|  173   } |  | 
|  174 } |  | 
|  175  |  | 
|  176 /** |  | 
|  177  * Creates a comma-separated string of human-readable representations of the |  | 
|  178  * type representations in the JavaScript array [types] starting at index |  | 
|  179  * [startIndex]. |  | 
|  180  */ |  | 
|  181 String joinArguments(var types, int startIndex, |  | 
|  182                      {String onTypeVariable(int i)}) { |  | 
|  183   if (types == null) return ''; |  | 
|  184   assert(isJsArray(types)); |  | 
|  185   bool firstArgument = true; |  | 
|  186   bool allDynamic = true; |  | 
|  187   StringBuffer buffer = new StringBuffer(); |  | 
|  188   for (int index = startIndex; index < getLength(types); index++) { |  | 
|  189     if (firstArgument) { |  | 
|  190       firstArgument = false; |  | 
|  191     } else { |  | 
|  192       buffer.write(', '); |  | 
|  193     } |  | 
|  194     var argument = getIndex(types, index); |  | 
|  195     if (argument != null) { |  | 
|  196       allDynamic = false; |  | 
|  197     } |  | 
|  198     buffer.write(runtimeTypeToString(argument, onTypeVariable: onTypeVariable)); |  | 
|  199   } |  | 
|  200   return allDynamic ? '' : '<$buffer>'; |  | 
|  201 } |  | 
|  202  |  | 
|  203 /** |  | 
|  204  * Returns a human-readable representation of the type of [object]. |  | 
|  205  * |  | 
|  206  * In minified mode does *not* use unminified identifiers (even when present). |  | 
|  207  */ |  | 
|  208 String getRuntimeTypeString(var object) { |  | 
|  209   String className = getClassName(object); |  | 
|  210   if (object == null) return className; |  | 
|  211   var rti = JS('var', r'#.$builtinTypeInfo', object); |  | 
|  212   return "$className${joinArguments(rti, 0)}"; |  | 
|  213 } |  | 
|  214  |  | 
|  215 Type getRuntimeType(var object) { |  | 
|  216   String type = getRuntimeTypeString(object); |  | 
|  217   return new TypeImpl(type); |  | 
|  218 } |  | 
|  219  |  | 
|  220 /** |  | 
|  221  * Applies the [substitution] on the [arguments]. |  | 
|  222  * |  | 
|  223  * See the comment in the beginning of this file for a description of the |  | 
|  224  * possible values for [substitution]. |  | 
|  225  */ |  | 
|  226 substitute(var substitution, var arguments) { |  | 
|  227   assert(substitution == null || |  | 
|  228          isJsFunction(substitution)); |  | 
|  229   assert(arguments == null || isJsArray(arguments)); |  | 
|  230   if (isJsFunction(substitution)) { |  | 
|  231     substitution = invoke(substitution, arguments); |  | 
|  232     if (substitution == null || isJsArray(substitution)) { |  | 
|  233       arguments = substitution; |  | 
|  234     } else if (isJsFunction(substitution)) { |  | 
|  235       // TODO(johnniwinther): Check if this is still needed. |  | 
|  236       arguments = invoke(substitution, arguments); |  | 
|  237     } |  | 
|  238   } |  | 
|  239   return arguments; |  | 
|  240 } |  | 
|  241  |  | 
|  242 /** |  | 
|  243  * Perform a type check with arguments on the Dart object [object]. |  | 
|  244  * |  | 
|  245  * Parameters: |  | 
|  246  * - [isField]: the name of the flag/function to check if the object |  | 
|  247  *   is of the correct class. |  | 
|  248  * - [checks]: the (JavaScript) list of type representations for the |  | 
|  249  *   arguments to check against. |  | 
|  250  * - [asField]: the name of the function that transforms the type |  | 
|  251  *   arguments of [objects] to an instance of the class that we check |  | 
|  252  *   against. |  | 
|  253  */ |  | 
|  254 bool checkSubtype(Object object, String isField, List checks, String asField) { |  | 
|  255   if (object == null) return false; |  | 
|  256   var arguments = getRuntimeTypeInfo(object); |  | 
|  257   // Interceptor is needed for JSArray and native classes. |  | 
|  258   // TODO(sra): It could be a more specialized interceptor since [object] is not |  | 
|  259   // `null` or a primitive. |  | 
|  260   // TODO(9586): Move type info for static functions onto an interceptor. |  | 
|  261   var interceptor = getInterceptor(object); |  | 
|  262   var isSubclass = getField(interceptor, isField); |  | 
|  263   // When we read the field and it is not there, [isSubclass] will be `null`. |  | 
|  264   if (isSubclass == null) return false; |  | 
|  265   // Should the asField function be passed the receiver? |  | 
|  266   var substitution = getField(interceptor, asField); |  | 
|  267   return checkArguments(substitution, arguments, checks); |  | 
|  268 } |  | 
|  269  |  | 
|  270 /// Returns the field's type name. |  | 
|  271 /// |  | 
|  272 /// In minified mode, uses the unminified names if available. |  | 
|  273 String computeTypeName(String isField, List arguments) { |  | 
|  274   // Extract the class name from the is field and append the textual |  | 
|  275   // representation of the type arguments. |  | 
|  276   return Primitives.formatType(isCheckPropertyToJsConstructorName(isField), |  | 
|  277                                arguments); |  | 
|  278 } |  | 
|  279  |  | 
|  280 Object subtypeCast(Object object, String isField, List checks, String asField) { |  | 
|  281   if (object != null && !checkSubtype(object, isField, checks, asField)) { |  | 
|  282     String actualType = Primitives.objectTypeName(object); |  | 
|  283     String typeName = computeTypeName(isField, checks); |  | 
|  284     // TODO(johnniwinther): Move type lookup to [CastErrorImplementation] to |  | 
|  285     // align with [TypeErrorImplementation]. |  | 
|  286     throw new CastErrorImplementation(actualType, typeName); |  | 
|  287   } |  | 
|  288   return object; |  | 
|  289 } |  | 
|  290  |  | 
|  291 Object assertSubtype(Object object, String isField, List checks, |  | 
|  292                      String asField) { |  | 
|  293   if (object != null && !checkSubtype(object, isField, checks, asField)) { |  | 
|  294     String typeName = computeTypeName(isField, checks); |  | 
|  295     throw new TypeErrorImplementation(object, typeName); |  | 
|  296   } |  | 
|  297   return object; |  | 
|  298 } |  | 
|  299  |  | 
|  300 /// Checks that the type represented by [subtype] is a subtype of [supertype]. |  | 
|  301 /// If not a type error with [message] is thrown. |  | 
|  302 assertIsSubtype(var subtype, var supertype, String message) { |  | 
|  303   if (!isSubtype(subtype, supertype)) { |  | 
|  304     throwTypeError(message); |  | 
|  305   } |  | 
|  306 } |  | 
|  307  |  | 
|  308 throwTypeError(message) { |  | 
|  309   throw new TypeErrorImplementation.fromMessage(message); |  | 
|  310 } |  | 
|  311  |  | 
|  312 /** |  | 
|  313  * Check that the types in the list [arguments] are subtypes of the types in |  | 
|  314  * list [checks] (at the respective positions), possibly applying [substitution] |  | 
|  315  * to the arguments before the check. |  | 
|  316  * |  | 
|  317  * See the comment in the beginning of this file for a description of the |  | 
|  318  * possible values for [substitution]. |  | 
|  319  */ |  | 
|  320 bool checkArguments(var substitution, var arguments, var checks) { |  | 
|  321   return areSubtypes(substitute(substitution, arguments), checks); |  | 
|  322 } |  | 
|  323  |  | 
|  324 /** |  | 
|  325  * Checks whether the types of [s] are all subtypes of the types of [t]. |  | 
|  326  * |  | 
|  327  * [s] and [t] are either `null` or JavaScript arrays of type representations, |  | 
|  328  * A `null` argument is interpreted as the arguments of a raw type, that is a |  | 
|  329  * list of `dynamic`. If [s] and [t] are JavaScript arrays they must be of the |  | 
|  330  * same length. |  | 
|  331  * |  | 
|  332  * See the comment in the beginning of this file for a description of type |  | 
|  333  * representations. |  | 
|  334  */ |  | 
|  335 bool areSubtypes(var s, var t) { |  | 
|  336   // `null` means a raw type. |  | 
|  337   if (s == null || t == null) return true; |  | 
|  338  |  | 
|  339   assert(isJsArray(s)); |  | 
|  340   assert(isJsArray(t)); |  | 
|  341   assert(getLength(s) == getLength(t)); |  | 
|  342  |  | 
|  343   int len = getLength(s); |  | 
|  344   for (int i = 0; i < len; i++) { |  | 
|  345     if (!isSubtype(getIndex(s, i), getIndex(t, i))) { |  | 
|  346       return false; |  | 
|  347     } |  | 
|  348   } |  | 
|  349   return true; |  | 
|  350 } |  | 
|  351  |  | 
|  352 /** |  | 
|  353  * Computes the signature by applying the type arguments of [context] as an |  | 
|  354  * instance of [contextName] to the signature function [signature]. |  | 
|  355  */ |  | 
|  356 computeSignature(var signature, var context, var contextName) { |  | 
|  357   var typeArguments = getRuntimeTypeArguments(context, contextName); |  | 
|  358   return invokeOn(signature, context, typeArguments); |  | 
|  359 } |  | 
|  360  |  | 
|  361 /** |  | 
|  362  * Returns `true` if the runtime type representation [type] is a supertype of |  | 
|  363  * [Null]. |  | 
|  364  */ |  | 
|  365 bool isSupertypeOfNull(var type) { |  | 
|  366   // `null` means `dynamic`. |  | 
|  367   return type == null || isDartObjectTypeRti(type) || isNullTypeRti(type); |  | 
|  368 } |  | 
|  369  |  | 
|  370 /** |  | 
|  371  * Tests whether the Dart object [o] is a subtype of the runtime type |  | 
|  372  * representation [t]. |  | 
|  373  * |  | 
|  374  * See the comment in the beginning of this file for a description of type |  | 
|  375  * representations. |  | 
|  376  */ |  | 
|  377 bool checkSubtypeOfRuntimeType(o, t) { |  | 
|  378   if (o == null) return isSupertypeOfNull(t); |  | 
|  379   if (t == null) return true; |  | 
|  380   // Get the runtime type information from the object here, because we may |  | 
|  381   // overwrite o with the interceptor below. |  | 
|  382   var rti = getRuntimeTypeInfo(o); |  | 
|  383   o = getInterceptor(o); |  | 
|  384   var type = getRawRuntimeType(o); |  | 
|  385   if (rti != null) { |  | 
|  386     // If the type has type variables (that is, `rti != null`), make a copy of |  | 
|  387     // the type arguments and insert [o] in the first position to create a |  | 
|  388     // compound type representation. |  | 
|  389     rti = JS('JSExtendableArray', '#.slice()', rti);  // Make a copy. |  | 
|  390     JS('', '#.splice(0, 0, #)', rti, type);  // Insert type at position 0. |  | 
|  391     type = rti; |  | 
|  392   } |  | 
|  393   if (isDartFunctionType(t)) { |  | 
|  394     // Functions are treated specially and have their type information stored |  | 
|  395     // directly in the instance. |  | 
|  396     var targetSignatureFunction = |  | 
|  397         getField(o, '${JS_GET_NAME(JsGetName.SIGNATURE_NAME)}'); |  | 
|  398     if (targetSignatureFunction == null) return false; |  | 
|  399     type = invokeOn(targetSignatureFunction, o, null); |  | 
|  400     return isFunctionSubtype(type, t); |  | 
|  401   } |  | 
|  402   return isSubtype(type, t); |  | 
|  403 } |  | 
|  404  |  | 
|  405 Object subtypeOfRuntimeTypeCast(Object object, var type) { |  | 
|  406   if (object != null && !checkSubtypeOfRuntimeType(object, type)) { |  | 
|  407     String actualType = Primitives.objectTypeName(object); |  | 
|  408     throw new CastErrorImplementation(actualType, runtimeTypeToString(type)); |  | 
|  409   } |  | 
|  410   return object; |  | 
|  411 } |  | 
|  412  |  | 
|  413 Object assertSubtypeOfRuntimeType(Object object, var type) { |  | 
|  414   if (object != null && !checkSubtypeOfRuntimeType(object, type)) { |  | 
|  415     throw new TypeErrorImplementation(object, runtimeTypeToString(type)); |  | 
|  416   } |  | 
|  417   return object; |  | 
|  418 } |  | 
|  419  |  | 
|  420 /** |  | 
|  421  * Extracts the type arguments from a type representation. The result is a |  | 
|  422  * JavaScript array or `null`. |  | 
|  423  */ |  | 
|  424 getArguments(var type) { |  | 
|  425   return isJsArray(type) ? JS('var', r'#.slice(1)', type) : null; |  | 
|  426 } |  | 
|  427  |  | 
|  428 /** |  | 
|  429  * Checks whether the type represented by the type representation [s] is a |  | 
|  430  * subtype of the type represented by the type representation [t]. |  | 
|  431  * |  | 
|  432  * See the comment in the beginning of this file for a description of type |  | 
|  433  * representations. |  | 
|  434  * |  | 
|  435  * The arguments [s] and [t] must be types, usually represented by the |  | 
|  436  * constructor of the class, or an array (for generic types). |  | 
|  437  */ |  | 
|  438 bool isSubtype(var s, var t) { |  | 
|  439   // Subtyping is reflexive. |  | 
|  440   if (isIdentical(s, t)) return true; |  | 
|  441   // If either type is dynamic, [s] is a subtype of [t]. |  | 
|  442   if (s == null || t == null) return true; |  | 
|  443   if (isDartFunctionType(t)) { |  | 
|  444     return isFunctionSubtype(s, t); |  | 
|  445   } |  | 
|  446   // Check function types against the Function class. |  | 
|  447   if (isDartFunctionType(s)) { |  | 
|  448     return isDartFunctionTypeRti(t); |  | 
|  449   } |  | 
|  450  |  | 
|  451   // Get the object describing the class and check for the subtyping flag |  | 
|  452   // constructed from the type of [t]. |  | 
|  453   var typeOfS = isJsArray(s) ? getIndex(s, 0) : s; |  | 
|  454   var typeOfT = isJsArray(t) ? getIndex(t, 0) : t; |  | 
|  455  |  | 
|  456   // Check for a subtyping flag. |  | 
|  457   // Get the necessary substitution of the type arguments, if there is one. |  | 
|  458   var substitution; |  | 
|  459   if (isNotIdentical(typeOfT, typeOfS)) { |  | 
|  460     if (!builtinIsSubtype(typeOfS, runtimeTypeToString(typeOfT))) { |  | 
|  461       return false; |  | 
|  462     } |  | 
|  463     var typeOfSPrototype = JS('', '#.prototype', typeOfS); |  | 
|  464     var field = '${JS_GET_NAME(JsGetName.OPERATOR_AS_PREFIX)}' |  | 
|  465         '${runtimeTypeToString(typeOfT)}'; |  | 
|  466     substitution = getField(typeOfSPrototype, field); |  | 
|  467   } |  | 
|  468   // The class of [s] is a subclass of the class of [t].  If [s] has no type |  | 
|  469   // arguments and no substitution, it is used as raw type.  If [t] has no |  | 
|  470   // type arguments, it used as a raw type.  In both cases, [s] is a subtype |  | 
|  471   // of [t]. |  | 
|  472   if ((!isJsArray(s) && substitution == null) || !isJsArray(t)) { |  | 
|  473     return true; |  | 
|  474   } |  | 
|  475   // Recursively check the type arguments. |  | 
|  476   return checkArguments(substitution, getArguments(s), getArguments(t)); |  | 
|  477 } |  | 
|  478  |  | 
|  479 bool isAssignable(var s, var t) { |  | 
|  480   return isSubtype(s, t) || isSubtype(t, s); |  | 
|  481 } |  | 
|  482  |  | 
|  483 /** |  | 
|  484  * If [allowShorter] is `true`, [t] is allowed to be shorter than [s]. |  | 
|  485  */ |  | 
|  486 bool areAssignable(List s, List t, bool allowShorter) { |  | 
|  487   // Both lists are empty and thus equal. |  | 
|  488   if (t ==null && s == null) return true; |  | 
|  489   // [t] is empty (and [s] is not) => only OK if [allowShorter]. |  | 
|  490   if (t == null) return allowShorter; |  | 
|  491   // [s] is empty (and [t] is not) => [s] is not longer or equal to [t]. |  | 
|  492   if (s == null) return false; |  | 
|  493  |  | 
|  494   assert(isJsArray(s)); |  | 
|  495   assert(isJsArray(t)); |  | 
|  496  |  | 
|  497   int sLength = getLength(s); |  | 
|  498   int tLength = getLength(t); |  | 
|  499   if (allowShorter) { |  | 
|  500     if (sLength < tLength) return false; |  | 
|  501   } else { |  | 
|  502     if (sLength != tLength) return false; |  | 
|  503   } |  | 
|  504  |  | 
|  505   for (int i = 0; i < tLength; i++) { |  | 
|  506     if (!isAssignable(getIndex(s, i), getIndex(t, i))) { |  | 
|  507       return false; |  | 
|  508     } |  | 
|  509   } |  | 
|  510   return true; |  | 
|  511 } |  | 
|  512  |  | 
|  513 bool areAssignableMaps(var s, var t) { |  | 
|  514   if (t == null) return true; |  | 
|  515   if (s == null) return false; |  | 
|  516  |  | 
|  517   assert(isJsObject(s)); |  | 
|  518   assert(isJsObject(t)); |  | 
|  519  |  | 
|  520   List names = |  | 
|  521       JSArray.markFixedList(JS('', 'Object.getOwnPropertyNames(#)', t)); |  | 
|  522   for (int i = 0; i < names.length; i++) { |  | 
|  523     var name = names[i]; |  | 
|  524     if (JS('bool', '!Object.hasOwnProperty.call(#, #)', s, name)) { |  | 
|  525       return false; |  | 
|  526     } |  | 
|  527     var tType = JS('', '#[#]', t, name); |  | 
|  528     var sType = JS('', '#[#]', s, name); |  | 
|  529     if (!isAssignable(tType, sType)) return false; |  | 
|  530   } |  | 
|  531   return true; |  | 
|  532 } |  | 
|  533  |  | 
|  534 bool isFunctionSubtype(var s, var t) { |  | 
|  535   assert(isDartFunctionType(t)); |  | 
|  536   if (!isDartFunctionType(s)) return false; |  | 
|  537   var voidReturnTag = JS_GET_NAME(JsGetName.FUNCTION_TYPE_VOID_RETURN_TAG); |  | 
|  538   var returnTypeTag = JS_GET_NAME(JsGetName.FUNCTION_TYPE_RETURN_TYPE_TAG); |  | 
|  539   if (hasField(s, voidReturnTag)) { |  | 
|  540     if (hasNoField(t, voidReturnTag) && hasField(t, returnTypeTag)) { |  | 
|  541       return false; |  | 
|  542     } |  | 
|  543   } else if (hasNoField(t, voidReturnTag)) { |  | 
|  544     var sReturnType = getField(s, returnTypeTag); |  | 
|  545     var tReturnType = getField(t, returnTypeTag); |  | 
|  546     if (!isAssignable(sReturnType, tReturnType)) return false; |  | 
|  547   } |  | 
|  548   var requiredParametersTag = |  | 
|  549       JS_GET_NAME(JsGetName.FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG); |  | 
|  550   var sParameterTypes = getField(s, requiredParametersTag); |  | 
|  551   var tParameterTypes = getField(t, requiredParametersTag); |  | 
|  552  |  | 
|  553   var optionalParametersTag = |  | 
|  554       JS_GET_NAME(JsGetName.FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG); |  | 
|  555   var sOptionalParameterTypes = getField(s, optionalParametersTag); |  | 
|  556   var tOptionalParameterTypes = getField(t, optionalParametersTag); |  | 
|  557  |  | 
|  558   int sParametersLen = sParameterTypes != null ? getLength(sParameterTypes) : 0; |  | 
|  559   int tParametersLen = tParameterTypes != null ? getLength(tParameterTypes) : 0; |  | 
|  560  |  | 
|  561   int sOptionalParametersLen = |  | 
|  562       sOptionalParameterTypes != null ? getLength(sOptionalParameterTypes) : 0; |  | 
|  563   int tOptionalParametersLen = |  | 
|  564       tOptionalParameterTypes != null ? getLength(tOptionalParameterTypes) : 0; |  | 
|  565  |  | 
|  566   if (sParametersLen > tParametersLen) { |  | 
|  567     // Too many required parameters in [s]. |  | 
|  568     return false; |  | 
|  569   } |  | 
|  570   if (sParametersLen + sOptionalParametersLen < |  | 
|  571       tParametersLen + tOptionalParametersLen) { |  | 
|  572     // Too few required and optional parameters in [s]. |  | 
|  573     return false; |  | 
|  574   } |  | 
|  575   if (sParametersLen == tParametersLen) { |  | 
|  576     // Simple case: Same number of required parameters. |  | 
|  577     if (!areAssignable(sParameterTypes, tParameterTypes, false)) return false; |  | 
|  578     if (!areAssignable(sOptionalParameterTypes, |  | 
|  579                        tOptionalParameterTypes, true)) { |  | 
|  580       return false; |  | 
|  581     } |  | 
|  582   } else { |  | 
|  583     // Complex case: Optional parameters of [s] for required parameters of [t]. |  | 
|  584     int pos = 0; |  | 
|  585     // Check all required parameters of [s]. |  | 
|  586     for (; pos < sParametersLen; pos++) { |  | 
|  587       if (!isAssignable(getIndex(sParameterTypes, pos), |  | 
|  588                         getIndex(tParameterTypes, pos))) { |  | 
|  589         return false; |  | 
|  590       } |  | 
|  591     } |  | 
|  592     int sPos = 0; |  | 
|  593     int tPos = pos; |  | 
|  594     // Check the remaining parameters of [t] with the first optional parameters |  | 
|  595     // of [s]. |  | 
|  596     for (; tPos < tParametersLen ; sPos++, tPos++) { |  | 
|  597       if (!isAssignable(getIndex(sOptionalParameterTypes, sPos), |  | 
|  598                         getIndex(tParameterTypes, tPos))) { |  | 
|  599         return false; |  | 
|  600       } |  | 
|  601     } |  | 
|  602     tPos = 0; |  | 
|  603     // Check the optional parameters of [t] with the remaining optional |  | 
|  604     // parameters of [s]: |  | 
|  605     for (; tPos < tOptionalParametersLen ; sPos++, tPos++) { |  | 
|  606       if (!isAssignable(getIndex(sOptionalParameterTypes, sPos), |  | 
|  607                         getIndex(tOptionalParameterTypes, tPos))) { |  | 
|  608         return false; |  | 
|  609       } |  | 
|  610     } |  | 
|  611   } |  | 
|  612  |  | 
|  613   var namedParametersTag = |  | 
|  614       JS_GET_NAME(JsGetName.FUNCTION_TYPE_NAMED_PARAMETERS_TAG); |  | 
|  615   var sNamedParameters = getField(s, namedParametersTag); |  | 
|  616   var tNamedParameters = getField(t, namedParametersTag); |  | 
|  617   return areAssignableMaps(sNamedParameters, tNamedParameters); |  | 
|  618 } |  | 
|  619  |  | 
|  620 /** |  | 
|  621  * Calls the JavaScript [function] with the [arguments] with the global scope |  | 
|  622  * as the `this` context. |  | 
|  623  */ |  | 
|  624 invoke(var function, var arguments) => invokeOn(function, null, arguments); |  | 
|  625  |  | 
|  626 /** |  | 
|  627  * Calls the JavaScript [function] with the [arguments] with [receiver] as the |  | 
|  628  * `this` context. |  | 
|  629  */ |  | 
|  630 Object invokeOn(function, receiver, arguments) { |  | 
|  631   assert(isJsFunction(function)); |  | 
|  632   assert(arguments == null || isJsArray(arguments)); |  | 
|  633   return JS('var', r'#.apply(#, #)', function, receiver, arguments); |  | 
|  634 } |  | 
|  635  |  | 
|  636 /// Calls the property [name] on the JavaScript [object]. |  | 
|  637 call(var object, String name) => JS('var', r'#[#]()', object, name); |  | 
|  638  |  | 
|  639 /// Returns the property [name] of the JavaScript object [object]. |  | 
|  640 getField(var object, String name) => JS('var', r'#[#]', object, name); |  | 
|  641  |  | 
|  642 /// Returns the property [index] of the JavaScript array [array]. |  | 
|  643 getIndex(var array, int index) { |  | 
|  644   assert(isJsArray(array)); |  | 
|  645   return JS('var', r'#[#]', array, index); |  | 
|  646 } |  | 
|  647  |  | 
|  648 /// Returns the length of the JavaScript array [array]. |  | 
|  649 int getLength(var array) { |  | 
|  650   assert(isJsArray(array)); |  | 
|  651   return JS('int', r'#.length', array); |  | 
|  652 } |  | 
|  653  |  | 
|  654 /// Returns whether [value] is a JavaScript array. |  | 
|  655 bool isJsArray(var value) { |  | 
|  656   return value is JSArray; |  | 
|  657 } |  | 
|  658  |  | 
|  659 hasField(var object, var name) => JS('bool', r'# in #', name, object); |  | 
|  660  |  | 
|  661 hasNoField(var object, var name) => !hasField(object, name); |  | 
|  662  |  | 
|  663 /// Returns `true` if [o] is a JavaScript function. |  | 
|  664 bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o); |  | 
|  665  |  | 
|  666 /// Returns `true` if [o] is a JavaScript object. |  | 
|  667 bool isJsObject(var o) => JS('bool', r"typeof # == 'object'", o); |  | 
|  668  |  | 
|  669 /** |  | 
|  670  * Returns `true` if the JavaScript values [s] and [t] are identical. We use |  | 
|  671  * this helper instead of [identical] because `identical` needs to merge |  | 
|  672  * `null` and `undefined` (which we can avoid). |  | 
|  673  */ |  | 
|  674 bool isIdentical(var s, var t) => JS('bool', '# === #', s, t); |  | 
|  675  |  | 
|  676 /** |  | 
|  677  * Returns `true` if the JavaScript values [s] and [t] are not identical. We use |  | 
|  678  * this helper instead of [identical] because `identical` needs to merge |  | 
|  679  * `null` and `undefined` (which we can avoid). |  | 
|  680  */ |  | 
|  681 bool isNotIdentical(var s, var t) => JS('bool', '# !== #', s, t); |  | 
| OLD | NEW |