| 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 part of _js_helper; | |
| 6 | |
| 7 setRuntimeTypeInfo(target, typeInfo) { | |
| 8 assert(typeInfo == null || typeInfo is JSArray); | |
| 9 // We have to check for null because factories may return null. | |
| 10 if (target != null) JS('var', r'#.$builtinTypeInfo = #', target, typeInfo); | |
| 11 } | |
| 12 | |
| 13 getRuntimeTypeInfo(target) { | |
| 14 if (target == null) return null; | |
| 15 return JS('var', r'#.$builtinTypeInfo', target); | |
| 16 } | |
| 17 | |
| 18 getRuntimeTypeArgument(target, substitution, index) { | |
| 19 var arguments = substitute(substitution, getRuntimeTypeInfo(target)); | |
| 20 return (arguments == null) ? null : getField(arguments, index); | |
| 21 } | |
| 22 | |
| 23 class TypeImpl implements Type { | |
| 24 final String _typeName; | |
| 25 | |
| 26 TypeImpl(this._typeName); | |
| 27 | |
| 28 toString() => _typeName; | |
| 29 | |
| 30 // TODO(ahe): This is a poor hashCode as it collides with its name. | |
| 31 int get hashCode => _typeName.hashCode; | |
| 32 | |
| 33 bool operator ==(other) { | |
| 34 return (other is TypeImpl) && _typeName == other._typeName; | |
| 35 } | |
| 36 } | |
| 37 | |
| 38 String getClassName(var object) { | |
| 39 return JS('String', r'#.constructor.builtin$cls', getInterceptor(object)); | |
| 40 } | |
| 41 | |
| 42 String getRuntimeTypeAsString(List runtimeType) { | |
| 43 String className = getConstructorName(runtimeType[0]); | |
| 44 return '$className${joinArguments(runtimeType, 1)}'; | |
| 45 } | |
| 46 | |
| 47 String getConstructorName(type) => JS('String', r'#.builtin$cls', type); | |
| 48 | |
| 49 String runtimeTypeToString(type) { | |
| 50 if (type == null) { | |
| 51 return 'dynamic'; | |
| 52 } else if (type is JSArray) { | |
| 53 // A list representing a type with arguments. | |
| 54 return getRuntimeTypeAsString(type); | |
| 55 } else { | |
| 56 // A reference to the constructor. | |
| 57 return getConstructorName(type); | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 String joinArguments(var types, int startIndex) { | |
| 62 if (types == null) return ''; | |
| 63 bool firstArgument = true; | |
| 64 bool allDynamic = true; | |
| 65 StringBuffer buffer = new StringBuffer(); | |
| 66 for (int index = startIndex; index < types.length; index++) { | |
| 67 if (firstArgument) { | |
| 68 firstArgument = false; | |
| 69 } else { | |
| 70 buffer.write(', '); | |
| 71 } | |
| 72 var argument = types[index]; | |
| 73 if (argument != null) { | |
| 74 allDynamic = false; | |
| 75 } | |
| 76 buffer.write(runtimeTypeToString(argument)); | |
| 77 } | |
| 78 return allDynamic ? '' : '<$buffer>'; | |
| 79 } | |
| 80 | |
| 81 String getRuntimeTypeString(var object) { | |
| 82 String className = object is JSArray ? 'List' : getClassName(object); | |
| 83 var typeInfo = JS('var', r'#.$builtinTypeInfo', object); | |
| 84 return "$className${joinArguments(typeInfo, 0)}"; | |
| 85 } | |
| 86 | |
| 87 Type getRuntimeType(var object) { | |
| 88 String type = getRuntimeTypeString(object); | |
| 89 return new TypeImpl(type); | |
| 90 } | |
| 91 | |
| 92 bool isJsFunction(var o) => JS('bool', r'typeof # == "function"', o); | |
| 93 | |
| 94 Object invoke(function, arguments) { | |
| 95 return JS('var', r'#.apply(null, #)', function, arguments); | |
| 96 } | |
| 97 | |
| 98 Object call(target, name) => JS('var', r'#[#]()', target, name); | |
| 99 | |
| 100 substitute(var substitution, var arguments) { | |
| 101 if (substitution is JSArray) { | |
| 102 arguments = substitution; | |
| 103 } else if (isJsFunction(substitution)) { | |
| 104 arguments = invoke(substitution, arguments); | |
| 105 } | |
| 106 return arguments; | |
| 107 } | |
| 108 | |
| 109 /** | |
| 110 * Perform a type check with arguments on the Dart object [object]. | |
| 111 * | |
| 112 * Parameters: | |
| 113 * - [isField]: the name of the flag/function to check if the object | |
| 114 * is of the correct class. | |
| 115 * - [checks]: the (JavaScript) list of type representations for the | |
| 116 * arguments to check against. | |
| 117 * - [asField]: the name of the function that transforms the type | |
| 118 * arguments of [objects] to an instance of the class that we check | |
| 119 * against. | |
| 120 */ | |
| 121 bool checkSubtype(Object object, String isField, List checks, String asField) { | |
| 122 if (object == null) return false; | |
| 123 var arguments = getRuntimeTypeInfo(object); | |
| 124 // Interceptor is needed for JSArray and native classes. | |
| 125 // TODO(sra): It could be a more specialized interceptor since [object] is not | |
| 126 // `null` or a primitive. | |
| 127 // TODO(9586): Move type info for static functions onto an interceptor. | |
| 128 var interceptor = getInterceptor(object); | |
| 129 bool isSubclass = getField(interceptor, isField); | |
| 130 // When we read the field and it is not there, [isSubclass] will be [:null:]. | |
| 131 if (isSubclass == null || !isSubclass) return false; | |
| 132 // Should the asField function be passed the receiver? | |
| 133 var substitution = getField(interceptor, asField); | |
| 134 return checkArguments(substitution, arguments, checks); | |
| 135 } | |
| 136 | |
| 137 String computeTypeName(String isField, List arguments) { | |
| 138 // Shorten the field name to the class name and append the textual | |
| 139 // representation of the type arguments. | |
| 140 int prefixLength = JS_OPERATOR_IS_PREFIX().length; | |
| 141 return Primitives.formatType(isField.substring(prefixLength, isField.length), | |
| 142 arguments); | |
| 143 } | |
| 144 | |
| 145 Object subtypeCast(Object object, String isField, List checks, String asField) { | |
| 146 if (object != null && !checkSubtype(object, isField, checks, asField)) { | |
| 147 String actualType = Primitives.objectTypeName(object); | |
| 148 String typeName = computeTypeName(isField, checks); | |
| 149 throw new CastErrorImplementation(object, typeName); | |
| 150 } | |
| 151 return object; | |
| 152 } | |
| 153 | |
| 154 Object assertSubtype(Object object, String isField, List checks, | |
| 155 String asField) { | |
| 156 if (object != null && !checkSubtype(object, isField, checks, asField)) { | |
| 157 String typeName = computeTypeName(isField, checks); | |
| 158 throw new TypeErrorImplementation(object, typeName); | |
| 159 } | |
| 160 return object; | |
| 161 } | |
| 162 | |
| 163 /** | |
| 164 * Check that the types in the list [arguments] are subtypes of the types in | |
| 165 * list [checks] (at the respective positions), possibly applying [substitution] | |
| 166 * to the arguments before the check. | |
| 167 * | |
| 168 * See [:RuntimeTypes.getSubtypeSubstitution:] for a description of the possible | |
| 169 * values for [substitution]. | |
| 170 */ | |
| 171 bool checkArguments(var substitution, var arguments, var checks) { | |
| 172 return areSubtypes(substitute(substitution, arguments), checks); | |
| 173 } | |
| 174 | |
| 175 bool areSubtypes(List s, List t) { | |
| 176 // [:null:] means a raw type. | |
| 177 if (s == null || t == null) return true; | |
| 178 | |
| 179 assert(s is JSArray); | |
| 180 assert(t is JSArray); | |
| 181 assert(s.length == t.length); | |
| 182 | |
| 183 int len = s.length; | |
| 184 for (int i = 0; i < len; i++) { | |
| 185 if (!isSubtype(s[i], t[i])) { | |
| 186 return false; | |
| 187 } | |
| 188 } | |
| 189 return true; | |
| 190 } | |
| 191 | |
| 192 getArguments(var type) { | |
| 193 return type is JSArray ? JS('var', r'#.slice(1)', type) : null; | |
| 194 } | |
| 195 | |
| 196 getField(var object, var name) => JS('var', r'#[#]', object, name); | |
| 197 | |
| 198 bool isSubtypeOfNull(type) { | |
| 199 // `null` means `dynamic`. | |
| 200 return type == null || getConstructorName(type) == JS_OBJECT_CLASS_NAME(); | |
| 201 } | |
| 202 | |
| 203 /** | |
| 204 * Tests whether the Dart object [o] is a subtype of the runtime type | |
| 205 * representation [t], which is a type representation as described in the | |
| 206 * comment on [isSubtype]. | |
| 207 */ | |
| 208 bool checkSubtypeOfRuntimeType(Object o, var t) { | |
| 209 if (JS('bool', '# == null', o)) return isSubtypeOfNull(t); | |
| 210 if (JS('bool', '# == null', t)) return true; | |
| 211 // Get the runtime type information from the object here, because we may | |
| 212 // overwrite o with the interceptor below. | |
| 213 var rti = getRuntimeTypeInfo(o); | |
| 214 o = getInterceptor(o); | |
| 215 // We can use the object as its own type representation because we install | |
| 216 // the subtype flags and the substitution on the prototype, so they are | |
| 217 // properties of the object in JS. | |
| 218 var type; | |
| 219 if (JS('bool', '# != null', rti)) { | |
| 220 // If the type has type variables (that is, [:rti != null:]), make a copy of | |
| 221 // the type arguments and insert [o] in the first position to create a | |
| 222 // compound type representation. | |
| 223 type = JS('List', '#.slice()', rti); | |
| 224 JS('', '#.splice(0, 0, #)', type, o); | |
| 225 } else { | |
| 226 // Use the object as representation of the raw type. | |
| 227 type = o; | |
| 228 } | |
| 229 return isSubtype(type, t); | |
| 230 } | |
| 231 | |
| 232 Object subtypeOfRuntimeTypeCast(Object object, var type) { | |
| 233 if (object != null && !checkSubtypeOfRuntimeType(object, type)) { | |
| 234 String actualType = Primitives.objectTypeName(object); | |
| 235 throw new CastErrorImplementation(actualType, runtimeTypeToString(type)); | |
| 236 } | |
| 237 return object; | |
| 238 } | |
| 239 | |
| 240 Object assertSubtypeOfRuntimeType(Object object, var type) { | |
| 241 if (object != null && !checkSubtypeOfRuntimeType(object, type)) { | |
| 242 throw new TypeErrorImplementation(object, runtimeTypeToString(type)); | |
| 243 } | |
| 244 return object; | |
| 245 } | |
| 246 | |
| 247 /** | |
| 248 * Check whether the type represented by [s] is a subtype of the type | |
| 249 * represented by [t]. | |
| 250 * | |
| 251 * Type representations can be: | |
| 252 * 1) a JavaScript constructor for a class C: the represented type is the raw | |
| 253 * type C. | |
| 254 * 2) a Dart object: this is the interceptor instance for a native type. | |
| 255 * 3) a JavaScript object: this represents a class for which there is no | |
| 256 * JavaScript constructor, because it is only used in type arguments or it | |
| 257 * is native. The represented type is the raw type of this class. | |
| 258 * 4) a JavaScript array: the first entry is of type 1, 2 or 3 and contains the | |
| 259 * subtyping flags and the substitution of the type and the rest of the | |
| 260 * array are the type arguments. | |
| 261 * 5) [:null:]: the dynamic type. | |
| 262 */ | |
| 263 bool isSubtype(var s, var t) { | |
| 264 // If either type is dynamic, [s] is a subtype of [t]. | |
| 265 if (JS('bool', '# == null', s) || JS('bool', '# == null', t)) return true; | |
| 266 // Subtyping is reflexive. | |
| 267 if (JS('bool', '# === #', s, t)) return true; | |
| 268 // Get the object describing the class and check for the subtyping flag | |
| 269 // constructed from the type of [t]. | |
| 270 var typeOfS = s is JSArray ? s[0] : s; | |
| 271 var typeOfT = t is JSArray ? t[0] : t; | |
| 272 // TODO(johnniwinther): replace this with the real function subtype test. | |
| 273 if (JS('bool', '#.func', s) == true || JS('bool', '#.func', t) == true ) { | |
| 274 return true; | |
| 275 } | |
| 276 // Check for a subtyping flag. | |
| 277 var test = '${JS_OPERATOR_IS_PREFIX()}${runtimeTypeToString(typeOfT)}'; | |
| 278 if (getField(typeOfS, test) == null) return false; | |
| 279 // Get the necessary substitution of the type arguments, if there is one. | |
| 280 var substitution; | |
| 281 if (JS('bool', '# !== #', typeOfT, typeOfS)) { | |
| 282 var field = '${JS_OPERATOR_AS_PREFIX()}${runtimeTypeToString(typeOfT)}'; | |
| 283 substitution = getField(typeOfS, field); | |
| 284 } | |
| 285 // The class of [s] is a subclass of the class of [t]. If [s] has no type | |
| 286 // arguments and no substitution, it is used as raw type. If [t] has no | |
| 287 // type arguments, it used as a raw type. In both cases, [s] is a subtype | |
| 288 // of [t]. | |
| 289 if ((s is! JSArray && JS('bool', '# == null', substitution)) || | |
| 290 t is! JSArray) { | |
| 291 return true; | |
| 292 } | |
| 293 // Recursively check the type arguments. | |
| 294 return checkArguments(substitution, getArguments(s), getArguments(t)); | |
| 295 } | |
| 296 | |
| 297 createRuntimeType(String name) => new TypeImpl(name); | |
| OLD | NEW |