| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| /** |
| * This part contains helpers for supporting runtime type information. |
| * |
| * The helper use a mixture of Dart and JavaScript objects. To indicate which is |
| * used where we adopt the scheme of using explicit type annotation for Dart |
| * objects and 'var' or omitted return type for JavaScript objects. |
| * |
| * Since bool, int, and String values are represented by the same JavaScript |
| * primitives, type annotations are used for these types in all cases. |
| * |
| * Several methods use a common JavaScript encoding of runtime type information. |
| * This encoding is referred to as the type representation which is one of |
| * these: |
| * 1) a JavaScript constructor for a class C: the represented type is the raw |
| * type C. |
| * 2) a Dart object: this is the interceptor instance for a native type. |
| * 3) a JavaScript object: this represents a class for which there is no |
| * JavaScript constructor, because it is only used in type arguments or it |
| * is native. The represented type is the raw type of this class. |
| * 4) a JavaScript array: the first entry is of type 1, 2 or 3 and contains the |
| * subtyping flags and the substitution of the type and the rest of the |
| * array are the type arguments. |
| * 5) `null`: the dynamic type. |
| * |
| * |
| * To check subtype relations between generic classes we use a JavaScript |
| * expression that describes the necessary substitution for type arguments. |
| * Such a substitution expresssion can be: |
| * 1) `null`, if no substituted check is necessary, because the |
| * type variables are the same or there are no type variables in the class |
| * that is checked for. |
| * 2) A list expression describing the type arguments to be used in the |
| * subtype check, if the type arguments to be used in the check do not |
| * depend on the type arguments of the object. |
| * 3) A function mapping the type variables of the object to be checked to |
| * a list expression. |
| */ |
| |
| part of _js_helper; |
| |
| Type createRuntimeType(String name) => new TypeImpl(name); |
| |
| class TypeImpl implements Type { |
| final String _typeName; |
| String _unmangledName; |
| |
| TypeImpl(this._typeName); |
| |
| String toString() { |
| if (_unmangledName != null) return _unmangledName; |
| String unmangledName = unmangleGlobalNameIfPreservedAnyways(_typeName); |
| // TODO(ahe): Handle type arguments. |
| if (unmangledName == null) unmangledName = _typeName; |
| return _unmangledName = unmangledName; |
| } |
| |
| // TODO(ahe): This is a poor hashCode as it collides with its name. |
| int get hashCode => _typeName.hashCode; |
| |
| bool operator ==(other) { |
| return (other is TypeImpl) && _typeName == other._typeName; |
| } |
| } |
| |
| /** |
| * Represents a type variable. |
| * |
| * This class holds the information needed when reflecting on generic classes |
| * and their members. |
| */ |
| class TypeVariable { |
| final Type owner; |
| final String name; |
| final int bound; |
| |
| const TypeVariable(this.owner, this.name, this.bound); |
| } |
| |
| getMangledTypeName(TypeImpl type) => type._typeName; |
| |
| /** |
| * Sets the runtime type information on [target]. [typeInfo] is a type |
| * representation of type 4 or 5, that is, either a JavaScript array or |
| * [:null:]. |
| */ |
| Object setRuntimeTypeInfo(Object target, var typeInfo) { |
| assert(isNull(typeInfo) || isJsArray(typeInfo)); |
| // We have to check for null because factories may return null. |
| if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); |
| return target; |
| } |
| |
| /** |
| * Returns the runtime type information of [target]. The returned value is a |
| * list of type representations for the type arguments. |
| */ |
| getRuntimeTypeInfo(Object target) { |
| if (target == null) return null; |
| return JS('var', r'#.$builtinTypeInfo', target); |
| } |
| |
| /** |
| * Returns the type arguments of [target] as an instance of [substitutionName]. |
| */ |
| getRuntimeTypeArguments(target, substitutionName) { |
| var substitution = |
| getField(target, '${JS_OPERATOR_AS_PREFIX()}$substitutionName'); |
| return substitute(substitution, getRuntimeTypeInfo(target)); |
| } |
| |
| /** |
| * Returns the [index]th type argument of [target] as an instance of |
| * [substitutionName]. |
| */ |
| @NoThrows() @NoSideEffects() @NoInline() |
| getRuntimeTypeArgument(Object target, String substitutionName, int index) { |
| var arguments = getRuntimeTypeArguments(target, substitutionName); |
| return isNull(arguments) ? null : getIndex(arguments, index); |
| } |
| |
| @NoThrows() @NoSideEffects() @NoInline() |
| getTypeArgumentByIndex(Object target, int index) { |
| var rti = getRuntimeTypeInfo(target); |
| return isNull(rti) ? null : getIndex(rti, index); |
| } |
| |
| void copyTypeArguments(Object source, Object target) { |
| JS('var', r'#.$builtinTypeInfo = #.$builtinTypeInfo', target, source); |
| } |
| |
| /** |
| * Retrieves the class name from type information stored on the constructor |
| * of [object]. |
| */ |
| String getClassName(var object) { |
| return JS('String', r'#.constructor.builtin$cls', getInterceptor(object)); |
| } |
| |
| /** |
| * Creates the string representation for the type representation [runtimeType] |
| * of type 4, the JavaScript array, where the first element represents the class |
| * and the remaining elements represent the type arguments. |
| */ |
| String getRuntimeTypeAsString(var runtimeType, {String onTypeVariable(int i)}) { |
| assert(isJsArray(runtimeType)); |
| String className = getConstructorName(getIndex(runtimeType, 0)); |
| return '$className' |
| '${joinArguments(runtimeType, 1, onTypeVariable: onTypeVariable)}'; |
| } |
| |
| /** |
| * Retrieves the class name from type information stored on the constructor |
| * [type]. |
| */ |
| String getConstructorName(var type) => JS('String', r'#.builtin$cls', type); |
| |
| /** |
| * Returns a human-readable representation of the type representation [type]. |
| */ |
| String runtimeTypeToString(var type, {String onTypeVariable(int i)}) { |
| if (isNull(type)) { |
| return 'dynamic'; |
| } else if (isJsArray(type)) { |
| // A list representing a type with arguments. |
| return getRuntimeTypeAsString(type, onTypeVariable: onTypeVariable); |
| } else if (isJsFunction(type)) { |
| // A reference to the constructor. |
| return getConstructorName(type); |
| } else if (type is int) { |
| if (onTypeVariable == null) { |
| return type.toString(); |
| } else { |
| return onTypeVariable(type); |
| } |
| } else { |
| // TODO(ahe): Handle function types, and be sure to always return a string. |
| return null; |
| } |
| } |
| |
| /** |
| * Creates a comma-separated string of human-readable representations of the |
| * type representations in the JavaScript array [types] starting at index |
| * [startIndex]. |
| */ |
| String joinArguments(var types, int startIndex, |
| {String onTypeVariable(int i)}) { |
| if (isNull(types)) return ''; |
| assert(isJsArray(types)); |
| bool firstArgument = true; |
| bool allDynamic = true; |
| StringBuffer buffer = new StringBuffer(); |
| for (int index = startIndex; index < getLength(types); index++) { |
| if (firstArgument) { |
| firstArgument = false; |
| } else { |
| buffer.write(', '); |
| } |
| var argument = getIndex(types, index); |
| if (argument != null) { |
| allDynamic = false; |
| } |
| buffer.write(runtimeTypeToString(argument, onTypeVariable: onTypeVariable)); |
| } |
| return allDynamic ? '' : '<$buffer>'; |
| } |
| |
| /** |
| * Returns a human-readable representation of the type of [object]. |
| */ |
| String getRuntimeTypeString(var object) { |
| String className = isJsArray(object) ? 'List' : getClassName(object); |
| var typeInfo = JS('var', r'#.$builtinTypeInfo', object); |
| return "$className${joinArguments(typeInfo, 0)}"; |
| } |
| |
| Type getRuntimeType(var object) { |
| String type = getRuntimeTypeString(object); |
| return new TypeImpl(type); |
| } |
| |
| /** |
| * Applies the [substitution] on the [arguments]. |
| * |
| * See the comment in the beginning of this file for a description of the |
| * possible values for [substitution]. |
| */ |
| substitute(var substitution, var arguments) { |
| assert(isNull(substitution) || |
| isJsArray(substitution) || |
| isJsFunction(substitution)); |
| assert(isNull(arguments) || isJsArray(arguments)); |
| if (isJsArray(substitution)) { |
| arguments = substitution; |
| } else if (isJsFunction(substitution)) { |
| substitution = invoke(substitution, arguments); |
| if (isJsArray(substitution)) { |
| arguments = substitution; |
| } else if (isJsFunction(substitution)) { |
| // TODO(johnniwinther): Check if this is still needed. |
| arguments = invoke(substitution, arguments); |
| } |
| } |
| return arguments; |
| } |
| |
| /** |
| * Perform a type check with arguments on the Dart object [object]. |
| * |
| * Parameters: |
| * - [isField]: the name of the flag/function to check if the object |
| * is of the correct class. |
| * - [checks]: the (JavaScript) list of type representations for the |
| * arguments to check against. |
| * - [asField]: the name of the function that transforms the type |
| * arguments of [objects] to an instance of the class that we check |
| * against. |
| */ |
| bool checkSubtype(Object object, String isField, List checks, String asField) { |
| if (object == null) return false; |
| var arguments = getRuntimeTypeInfo(object); |
| // Interceptor is needed for JSArray and native classes. |
| // TODO(sra): It could be a more specialized interceptor since [object] is not |
| // `null` or a primitive. |
| // TODO(9586): Move type info for static functions onto an interceptor. |
| var interceptor = getInterceptor(object); |
| var isSubclass = getField(interceptor, isField); |
| // When we read the field and it is not there, [isSubclass] will be [:null:]. |
| if (isNull(isSubclass)) return false; |
| // Should the asField function be passed the receiver? |
| var substitution = getField(interceptor, asField); |
| return checkArguments(substitution, arguments, checks); |
| } |
| |
| String computeTypeName(String isField, List arguments) { |
| // Shorten the field name to the class name and append the textual |
| // representation of the type arguments. |
| int prefixLength = JS_OPERATOR_IS_PREFIX().length; |
| return Primitives.formatType(isField.substring(prefixLength, isField.length), |
| arguments); |
| } |
| |
| Object subtypeCast(Object object, String isField, List checks, String asField) { |
| if (object != null && !checkSubtype(object, isField, checks, asField)) { |
| String actualType = Primitives.objectTypeName(object); |
| String typeName = computeTypeName(isField, checks); |
| // TODO(johnniwinther): Move type lookup to [CastErrorImplementation] to |
| // align with [TypeErrorImplementation]. |
| throw new CastErrorImplementation(actualType, typeName); |
| } |
| return object; |
| } |
| |
| Object assertSubtype(Object object, String isField, List checks, |
| String asField) { |
| if (object != null && !checkSubtype(object, isField, checks, asField)) { |
| String typeName = computeTypeName(isField, checks); |
| throw new TypeErrorImplementation(object, typeName); |
| } |
| return object; |
| } |
| |
| /// Checks that the type represented by [subtype] is a subtype of [supertype]. |
| /// If not a type error with [message] is thrown. |
| assertIsSubtype(var subtype, var supertype, String message) { |
| if (!isSubtype(subtype, supertype)) { |
| throwTypeError(message); |
| } |
| } |
| |
| throwTypeError(message) { |
| throw new TypeErrorImplementation.fromMessage(message); |
| } |
| |
| /** |
| * Check that the types in the list [arguments] are subtypes of the types in |
| * list [checks] (at the respective positions), possibly applying [substitution] |
| * to the arguments before the check. |
| * |
| * See the comment in the beginning of this file for a description of the |
| * possible values for [substitution]. |
| */ |
| bool checkArguments(var substitution, var arguments, var checks) { |
| return areSubtypes(substitute(substitution, arguments), checks); |
| } |
| |
| /** |
| * Checks whether the types of [s] are all subtypes of the types of [t]. |
| * |
| * [s] and [t] are either [:null:] or JavaScript arrays of type representations, |
| * A [:null:] argument is interpreted as the arguments of a raw type, that is a |
| * list of [:dynamic:]. If [s] and [t] are JavaScript arrays they must be of the |
| * same length. |
| * |
| * See the comment in the beginning of this file for a description of type |
| * representations. |
| */ |
| bool areSubtypes(var s, var t) { |
| // [:null:] means a raw type. |
| if (isNull(s) || isNull(t)) return true; |
| |
| assert(isJsArray(s)); |
| assert(isJsArray(t)); |
| assert(getLength(s) == getLength(t)); |
| |
| int len = getLength(s); |
| for (int i = 0; i < len; i++) { |
| if (!isSubtype(getIndex(s, i), getIndex(t, i))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Computes the signature by applying the type arguments of [context] as an |
| * instance of [contextName] to the signature function [signature]. |
| */ |
| computeSignature(var signature, var context, var contextName) { |
| var typeArguments = getRuntimeTypeArguments(context, contextName); |
| return invokeOn(signature, context, typeArguments); |
| } |
| |
| /** |
| * Returns [:true:] if the runtime type representation [type] is a supertype of |
| * [:Null:]. |
| */ |
| bool isSupertypeOfNull(var type) { |
| // `null` means `dynamic`. |
| return isNull(type) || getConstructorName(type) == JS_OBJECT_CLASS_NAME() |
| || getConstructorName(type) == JS_NULL_CLASS_NAME(); |
| } |
| |
| /** |
| * Tests whether the Dart object [o] is a subtype of the runtime type |
| * representation [t]. |
| * |
| * See the comment in the beginning of this file for a description of type |
| * representations. |
| */ |
| bool checkSubtypeOfRuntimeType(o, t) { |
| if (isNull(o)) return isSupertypeOfNull(t); |
| if (isNull(t)) return true; |
| // Get the runtime type information from the object here, because we may |
| // overwrite o with the interceptor below. |
| var rti = getRuntimeTypeInfo(o); |
| o = getInterceptor(o); |
| // We can use the object as its own type representation because we install |
| // the subtype flags and the substitution on the prototype, so they are |
| // properties of the object in JS. |
| var type; |
| if (isNotNull(rti)) { |
| // If the type has type variables (that is, [:rti != null:]), make a copy of |
| // the type arguments and insert [o] in the first position to create a |
| // compound type representation. |
| type = JS('JSExtendableArray', '#.slice()', rti); |
| JS('', '#.splice(0, 0, #)', type, o); |
| } else { |
| // Use the object as representation of the raw type. |
| type = o; |
| } |
| return isSubtype(type, t); |
| } |
| |
| Object subtypeOfRuntimeTypeCast(Object object, var type) { |
| if (object != null && !checkSubtypeOfRuntimeType(object, type)) { |
| String actualType = Primitives.objectTypeName(object); |
| throw new CastErrorImplementation(actualType, runtimeTypeToString(type)); |
| } |
| return object; |
| } |
| |
| Object assertSubtypeOfRuntimeType(Object object, var type) { |
| if (object != null && !checkSubtypeOfRuntimeType(object, type)) { |
| throw new TypeErrorImplementation(object, runtimeTypeToString(type)); |
| } |
| return object; |
| } |
| |
| /** |
| * Extracts the type arguments from a type representation. The result is a |
| * JavaScript array or [:null:]. |
| */ |
| getArguments(var type) { |
| return isJsArray(type) ? JS('var', r'#.slice(1)', type) : null; |
| } |
| |
| /** |
| * Checks whether the type represented by the type representation [s] is a |
| * subtype of the type represented by the type representation [t]. |
| * |
| * See the comment in the beginning of this file for a description of type |
| * representations. |
| */ |
| bool isSubtype(var s, var t) { |
| // Subtyping is reflexive. |
| if (isIdentical(s, t)) return true; |
| // If either type is dynamic, [s] is a subtype of [t]. |
| if (isNull(s) || isNull(t)) return true; |
| if (hasField(t, '${JS_FUNCTION_TYPE_TAG()}')) { |
| if (hasNoField(s, '${JS_FUNCTION_TYPE_TAG()}')) { |
| var signatureName = |
| '${JS_OPERATOR_IS_PREFIX()}_${getField(t, JS_FUNCTION_TYPE_TAG())}'; |
| if (hasField(s, signatureName)) return true; |
| var targetSignatureFunction = getField(s, '${JS_SIGNATURE_NAME()}'); |
| if (isNull(targetSignatureFunction)) return false; |
| s = invokeOn(targetSignatureFunction, s, null); |
| } |
| return isFunctionSubtype(s, t); |
| } |
| // Check function types against the Function class. |
| if (getConstructorName(t) == JS_FUNCTION_CLASS_NAME() && |
| hasField(s, '${JS_FUNCTION_TYPE_TAG()}')) { |
| return true; |
| } |
| // Get the object describing the class and check for the subtyping flag |
| // constructed from the type of [t]. |
| var typeOfS = isJsArray(s) ? getIndex(s, 0) : s; |
| var typeOfT = isJsArray(t) ? getIndex(t, 0) : t; |
| // Check for a subtyping flag. |
| var name = runtimeTypeToString(typeOfT); |
| // Get the necessary substitution of the type arguments, if there is one. |
| var substitution; |
| if (isNotIdentical(typeOfT, typeOfS)) { |
| var test = '${JS_OPERATOR_IS_PREFIX()}${name}'; |
| if (hasNoField(typeOfS, test)) return false; |
| var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; |
| substitution = getField(typeOfS, field); |
| } |
| // The class of [s] is a subclass of the class of [t]. If [s] has no type |
| // arguments and no substitution, it is used as raw type. If [t] has no |
| // type arguments, it used as a raw type. In both cases, [s] is a subtype |
| // of [t]. |
| if ((!isJsArray(s) && isNull(substitution)) || !isJsArray(t)) { |
| return true; |
| } |
| // Recursively check the type arguments. |
| return checkArguments(substitution, getArguments(s), getArguments(t)); |
| } |
| |
| bool isAssignable(var s, var t) { |
| return isSubtype(s, t) || isSubtype(t, s); |
| } |
| |
| /** |
| * If [allowShorter] is [:true:], [t] is allowed to be shorter than [s]. |
| */ |
| bool areAssignable(List s, List t, bool allowShorter) { |
| // Both lists are empty and thus equal. |
| if (isNull(t) && isNull(s)) return true; |
| // [t] is empty (and [s] is not) => only OK if [allowShorter]. |
| if (isNull(t)) return allowShorter; |
| // [s] is empty (and [t] is not) => [s] is not longer or equal to [t]. |
| if (isNull(s)) return false; |
| |
| assert(isJsArray(s)); |
| assert(isJsArray(t)); |
| |
| int sLength = getLength(s); |
| int tLength = getLength(t); |
| if (allowShorter) { |
| if (sLength < tLength) return false; |
| } else { |
| if (sLength != tLength) return false; |
| } |
| |
| for (int i = 0; i < tLength; i++) { |
| if (!isAssignable(getIndex(s, i), getIndex(t, i))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool areAssignableMaps(var s, var t) { |
| if (isNull(t)) return true; |
| if (isNull(s)) return false; |
| |
| assert(isJsObject(s)); |
| assert(isJsObject(t)); |
| |
| List names = |
| JSArray.markFixedList(JS('', 'Object.getOwnPropertyNames(#)', t)); |
| for (int i = 0; i < names.length; i++) { |
| var name = names[i]; |
| if (JS('bool', '!Object.hasOwnProperty.call(#, #)', s, name)) { |
| return false; |
| } |
| var tType = JS('', '#[#]', t, name); |
| var sType = JS('', '#[#]', s, name); |
| if (!isAssignable(tType, sType)) return false; |
| } |
| return true; |
| } |
| |
| bool isFunctionSubtype(var s, var t) { |
| assert(hasField(t, '${JS_FUNCTION_TYPE_TAG()}')); |
| if (hasNoField(s, '${JS_FUNCTION_TYPE_TAG()}')) return false; |
| if (hasField(s, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) { |
| if (hasNoField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}') && |
| hasField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}')) { |
| return false; |
| } |
| } else if (hasNoField(t, '${JS_FUNCTION_TYPE_VOID_RETURN_TAG()}')) { |
| var sReturnType = getField(s, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}'); |
| var tReturnType = getField(t, '${JS_FUNCTION_TYPE_RETURN_TYPE_TAG()}'); |
| if (!isAssignable(sReturnType, tReturnType)) return false; |
| } |
| var sParameterTypes = |
| getField(s, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}'); |
| var tParameterTypes = |
| getField(t, '${JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG()}'); |
| |
| var sOptionalParameterTypes = |
| getField(s, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}'); |
| var tOptionalParameterTypes = |
| getField(t, '${JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG()}'); |
| |
| int sParametersLen = |
| isNotNull(sParameterTypes) ? getLength(sParameterTypes) : 0; |
| int tParametersLen = |
| isNotNull(tParameterTypes) ? getLength(tParameterTypes) : 0; |
| |
| int sOptionalParametersLen = isNotNull(sOptionalParameterTypes) |
| ? getLength(sOptionalParameterTypes) : 0; |
| int tOptionalParametersLen = isNotNull(tOptionalParameterTypes) |
| ? getLength(tOptionalParameterTypes) : 0; |
| |
| if (sParametersLen > tParametersLen) { |
| // Too many required parameters in [s]. |
| return false; |
| } |
| if (sParametersLen + sOptionalParametersLen < |
| tParametersLen + tOptionalParametersLen) { |
| // Too few required and optional parameters in [s]. |
| return false; |
| } |
| if (sParametersLen == tParametersLen) { |
| // Simple case: Same number of required parameters. |
| if (!areAssignable(sParameterTypes, tParameterTypes, false)) return false; |
| if (!areAssignable(sOptionalParameterTypes, |
| tOptionalParameterTypes, true)) { |
| return false; |
| } |
| } else { |
| // Complex case: Optional parameters of [s] for required parameters of [t]. |
| int pos = 0; |
| // Check all required parameters of [s]. |
| for (; pos < sParametersLen; pos++) { |
| if (!isAssignable(getIndex(sParameterTypes, pos), |
| getIndex(tParameterTypes, pos))) { |
| return false; |
| } |
| } |
| int sPos = 0; |
| int tPos = pos; |
| // Check the remaining parameters of [t] with the first optional parameters |
| // of [s]. |
| for (; tPos < tParametersLen ; sPos++, tPos++) { |
| if (!isAssignable(getIndex(sOptionalParameterTypes, sPos), |
| getIndex(tParameterTypes, tPos))) { |
| return false; |
| } |
| } |
| tPos = 0; |
| // Check the optional parameters of [t] with the remaining optional |
| // parameters of [s]: |
| for (; tPos < tOptionalParametersLen ; sPos++, tPos++) { |
| if (!isAssignable(getIndex(sOptionalParameterTypes, sPos), |
| getIndex(tOptionalParameterTypes, tPos))) { |
| return false; |
| } |
| } |
| } |
| |
| var sNamedParameters = |
| getField(s, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}'); |
| var tNamedParameters = |
| getField(t, '${JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG()}'); |
| return areAssignableMaps(sNamedParameters, tNamedParameters); |
| } |
| |
| /** |
| * Calls the JavaScript [function] with the [arguments] with the global scope |
| * as the [:this:] context. |
| */ |
| invoke(var function, var arguments) => invokeOn(function, null, arguments); |
| |
| /** |
| * Calls the JavaScript [function] with the [arguments] with [receiver] as the |
| * [:this:] context. |
| */ |
| Object invokeOn(function, receiver, arguments) { |
| assert(isJsFunction(function)); |
| assert(isNull(arguments) || isJsArray(arguments)); |
| return JS('var', r'#.apply(#, #)', function, receiver, arguments); |
| } |
| |
| /// Calls the property [name] on the JavaScript [object]. |
| call(var object, String name) => JS('var', r'#[#]()', object, name); |
| |
| /// Returns the property [name] of the JavaScript object [object]. |
| getField(var object, String name) => JS('var', r'#[#]', object, name); |
| |
| /// Returns the property [index] of the JavaScript array [array]. |
| getIndex(var array, int index) { |
| assert(isJsArray(array)); |
| return JS('var', r'#[#]', array, index); |
| } |
| |
| /// Returns the length of the JavaScript array [array]. |
| int getLength(var array) { |
| assert(isJsArray(array)); |
| return JS('int', r'#.length', array); |
| } |
| |
| /// Returns whether [value] is a JavaScript array. |
| bool isJsArray(var value) { |
| return value is JSArray; |
| } |
| |
| hasField(var object, var name) => JS('bool', r'# in #', name, object); |
| |
| hasNoField(var object, var name) => !hasField(object, name); |
| |
| /// Returns [:true:] if [o] is a JavaScript function. |
| bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o); |
| |
| /// Returns [:true:] if [o] is a JavaScript object. |
| bool isJsObject(var o) => JS('bool', r"typeof # == 'object'", o); |
| |
| /** |
| * Returns [:true:] if [o] is equal to [:null:], that is either [:null:] or |
| * [:undefined:]. We use this helper to avoid generating code under the invalid |
| * assumption that [o] is a Dart value. |
| */ |
| bool isNull(var o) => JS('bool', '# == null', o); |
| |
| /** |
| * Returns [:true:] if [o] is not equal to [:null:], that is neither [:null:] |
| * nor [:undefined:]. We use this helper to avoid generating code under the |
| * invalid assumption that [o] is a Dart value. |
| */ |
| bool isNotNull(var o) => JS('bool', '# != null', o); |
| |
| /** |
| * Returns [:true:] if the JavaScript values [s] and [t] are identical. We use |
| * this helper to avoid generating code under the invalid assumption that [s] |
| * and [t] are Dart values. |
| */ |
| bool isIdentical(var s, var t) => JS('bool', '# === #', s, t); |
| |
| /** |
| * Returns [:true:] if the JavaScript values [s] and [t] are not identical. We |
| * use this helper to avoid generating code under the invalid assumption that |
| * [s] and [t] are Dart values. |
| */ |
| bool isNotIdentical(var s, var t) => JS('bool', '# !== #', s, t); |