| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011, 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 // The following methods are used to handle type information | |
| 6 // | |
| 7 | |
| 8 /** | |
| 9 * @constructor | |
| 10 * @param {string} classkey | |
| 11 * @param {string=} typekey | |
| 12 * @param {Array.<RTT>=} typeargs | |
| 13 * @param {RTT} returnType | |
| 14 * @param {bool} functionType | |
| 15 * @param {string=} named optional | |
| 16 */ | |
| 17 function RTT(classkey, typekey, typeargs, returnType, functionType, named) { | |
| 18 this.classKey = classkey; | |
| 19 this.typeKey = typekey ? typekey : classkey; | |
| 20 this.typeArgs = typeargs; | |
| 21 this.returnType = returnType; // key for the return type | |
| 22 this.named = named; | |
| 23 this.implementedTypes = {}; | |
| 24 this.functionType = functionType; | |
| 25 // Add self | |
| 26 this.implementedTypes[classkey] = this; | |
| 27 // Add Object | |
| 28 if (!functionType && classkey != $cls('Object')) { | |
| 29 this.implementedTypes[$cls('Object')] = RTT.objectType; | |
| 30 } | |
| 31 } | |
| 32 | |
| 33 /** @type {Object.<string, Object>} */ | |
| 34 RTT.types = {}; | |
| 35 | |
| 36 /** @return {string} */ | |
| 37 RTT.prototype.toString = function() { return this.typeKey; } | |
| 38 | |
| 39 /** | |
| 40 * @param {*} value | |
| 41 * @return {boolean} Whether this type is implemented by the value | |
| 42 */ | |
| 43 RTT.prototype.implementedBy = function(value){ | |
| 44 return (value == null) ? RTT.nullInstanceOf(this) : | |
| 45 this.functionType ? this.implementedByTypeFunc(value) : | |
| 46 this.implementedByType(RTT.getTypeInfo(value)); | |
| 47 }; | |
| 48 | |
| 49 /** | |
| 50 * A helper function for safely looking up a value | |
| 51 * in a Object used as a map. | |
| 52 * @param {Object.<*>} map | |
| 53 * @param {srting} key | |
| 54 * @return {*} the value or null; | |
| 55 */ | |
| 56 function $mapLookup(map, key) { | |
| 57 return map.hasOwnProperty(key) ? map[key] : null; | |
| 58 } | |
| 59 | |
| 60 RTT.prototype.implementedByTypeSwitch = function(value){ | |
| 61 return this.functionType ? this.implementedByTypeFunc(value) : | |
| 62 this.implementedByType(value); | |
| 63 }; | |
| 64 | |
| 65 /** | |
| 66 * @param {!RTT} other | |
| 67 * @return {boolean} Whether this type is implement by other | |
| 68 */ | |
| 69 RTT.prototype.implementedByType = function(otherType) { | |
| 70 if (otherType === this || otherType === RTT.dynamicType.$lookupRTT(null,otherT
ype.named)) { | |
| 71 return true; | |
| 72 } | |
| 73 var targetTypeInfo = $mapLookup(otherType.implementedTypes, this.classKey); | |
| 74 if (targetTypeInfo == null) { | |
| 75 return false; | |
| 76 } | |
| 77 if (targetTypeInfo.typeArgs && this.typeArgs) { | |
| 78 for(var i = this.typeArgs.length - 1; i >= 0; i--) { | |
| 79 if (!this.typeArgs[i].implementedByTypeSwitch(targetTypeInfo.typeArgs[i]))
{ | |
| 80 return false; | |
| 81 } | |
| 82 } | |
| 83 } | |
| 84 return true; | |
| 85 }; | |
| 86 | |
| 87 /** | |
| 88 * @param {!RTT} other | |
| 89 * @return {boolean} Whether this type is assignable by other | |
| 90 */ | |
| 91 RTT.prototype.assignableByType = function(otherType) { | |
| 92 if (otherType === this || otherType === RTT.dynamicType.$lookupRTT(null,otherT
ype.named) | |
| 93 || this === RTT.dynamicType.$lookupRTT(null,this.named)) { | |
| 94 return true; | |
| 95 } | |
| 96 var targetTypeInfo = $mapLookup(otherType.implementedTypes, this.classKey); | |
| 97 if (targetTypeInfo == null) { | |
| 98 targetTypeInfo = $mapLookup(this.implementedTypes, otherType.classKey); | |
| 99 if (targetTypeInfo == null) { | |
| 100 return false; | |
| 101 } | |
| 102 } | |
| 103 if (targetTypeInfo.typeArgs && this.typeArgs) { | |
| 104 for(var i = this.typeArgs.length - 1; i >= 0; i--) { | |
| 105 if (!this.typeArgs[i].assignableByType(targetTypeInfo.typeArgs[i])) { | |
| 106 return false; | |
| 107 } | |
| 108 } | |
| 109 } | |
| 110 return true; | |
| 111 }; | |
| 112 | |
| 113 | |
| 114 /** | |
| 115 * @param {!RTT} other | |
| 116 * @return {boolean} Whether this type is implemented by other | |
| 117 */ | |
| 118 RTT.prototype.implementedByTypeFunc = function(otherType) { | |
| 119 if (otherType.$lookupRTT) { | |
| 120 otherType = otherType.$lookupRTT(); | |
| 121 } else if (!(otherType instanceof RTT)) { | |
| 122 return false; | |
| 123 } | |
| 124 if (otherType === this || otherType === RTT.dynamicType.$lookupRTT(null,otherT
ype.named)) { | |
| 125 return true; | |
| 126 } | |
| 127 var props = Object.getOwnPropertyNames(otherType.implementedTypes); | |
| 128 NEXT_PROPERTY: for (var i = 0 ; i < props.length; i++) { | |
| 129 var mapped = otherType.implementedTypes[props[i]]; | |
| 130 if (mapped.returnType && this.returnType && | |
| 131 !this.returnType.assignableByType(mapped.returnType)) { | |
| 132 continue; | |
| 133 } | |
| 134 if (mapped.typeArgs && this.typeArgs) { | |
| 135 if (mapped.typeArgs.length < this.typeArgs.length) { | |
| 136 continue; | |
| 137 } | |
| 138 var named = false; | |
| 139 var x; | |
| 140 for (x = 0; x < this.typeArgs.length; x++) { | |
| 141 if (this.typeArgs[x].named || mapped.typeArgs[x].named) { | |
| 142 named = true; | |
| 143 break; | |
| 144 } | |
| 145 if (!this.typeArgs[x].assignableByType(mapped.typeArgs[x])) { | |
| 146 continue NEXT_PROPERTY; | |
| 147 } | |
| 148 } | |
| 149 if (!named && x < this.typeArgs.length) { | |
| 150 continue NEXT_PROPERTY; | |
| 151 } | |
| 152 for (; x < this.typeArgs.length; x++) { | |
| 153 if (!this.typeArgs[x].assignableByType(mapped.typeArgs[x]) | |
| 154 || !(this.typeArgs[x].named === mapped.typeArgs[x].named)) { | |
| 155 continue NEXT_PROPERTY; | |
| 156 } | |
| 157 } | |
| 158 } else if (mapped.typeArgs || this.typeArgs) { | |
| 159 continue NEXT_PROPERTY; | |
| 160 } | |
| 161 return true; | |
| 162 } | |
| 163 return false; | |
| 164 }; | |
| 165 | |
| 166 /** | |
| 167 * @return {string} the class name associated with this type | |
| 168 */ | |
| 169 RTT.prototype.getClassName = function() { | |
| 170 var name = this.classKey; | |
| 171 if (name.substr(0, 4) == "cls:") { | |
| 172 name = name.substr(4); | |
| 173 } | |
| 174 if (name.substr(-5) == "$Dart") { | |
| 175 name = name.substr(0, name.length - 5); | |
| 176 } | |
| 177 return name; | |
| 178 } | |
| 179 | |
| 180 /** | |
| 181 * @param {RTT} | |
| 182 * @return {boolean} | |
| 183 */ | |
| 184 RTT.nullInstanceOf = function(type) { | |
| 185 return type === RTT.objectType || type === RTT.dynamicType.$lookupRTT(null,typ
e.named); | |
| 186 }; | |
| 187 | |
| 188 /** | |
| 189 * @param {*} value The value to retrieve type information for | |
| 190 * @return {RTT} | |
| 191 */ | |
| 192 RTT.getNativeTypeInfo = function(value) { | |
| 193 if (value instanceof Array) return Array.$lookupRTT(); | |
| 194 switch (typeof value) { | |
| 195 case 'string': return String.$lookupRTT(); | |
| 196 case 'number': return Number.$lookupRTT(); | |
| 197 case 'boolean': return Boolean.$lookupRTT(); | |
| 198 } | |
| 199 return RTT.placeholderType; | |
| 200 }; | |
| 201 | |
| 202 /** | |
| 203 * @param {string} name | |
| 204 * @param {function(RTT,Array.<RTT>)=} implementsSupplier | |
| 205 * @param {Array.<RTT>=} typeArgs | |
| 206 * @param {string} named optional value | |
| 207 * @return {RTT} The RTT information object | |
| 208 */ | |
| 209 RTT.create = function(name, implementsSupplier, typeArgs, named) { | |
| 210 if (name == $cls("Object") && !named) return RTT.objectType; | |
| 211 var typekey = RTT.getTypeKey(name, typeArgs, null, named); | |
| 212 var rtt = $mapLookup(RTT.types, typekey); | |
| 213 if (rtt) { | |
| 214 return rtt; | |
| 215 } | |
| 216 var classkey = RTT.getTypeKey(name); | |
| 217 rtt = new RTT(classkey, typekey, typeArgs, null, false, named); | |
| 218 RTT.types[typekey] = rtt; | |
| 219 if (implementsSupplier) { | |
| 220 implementsSupplier(rtt, typeArgs); | |
| 221 } | |
| 222 return rtt; | |
| 223 }; | |
| 224 | |
| 225 /** | |
| 226 * @param {Array.<RTT>=} typeArgs | |
| 227 * @param {<RTT>=} returnType (if defined) | |
| 228 * @param {string} named optional value | |
| 229 * @return {RTT} The RTT information object | |
| 230 */ | |
| 231 RTT.createFunction = function(typeArgs, returnType, named) { | |
| 232 var name = $cls("Function$Dart"); | |
| 233 var typekey = RTT.getTypeKey(name, typeArgs, returnType, named); | |
| 234 var rtt = $mapLookup(RTT.types, typekey); | |
| 235 if (rtt) { | |
| 236 return rtt; | |
| 237 } | |
| 238 var classkey = RTT.getTypeKey(name); | |
| 239 rtt = new RTT(classkey, typekey, typeArgs, returnType, true, named); | |
| 240 RTT.types[typekey] = rtt; | |
| 241 return rtt; | |
| 242 }; | |
| 243 | |
| 244 /** | |
| 245 * @param {RTT} old | |
| 246 * @param {string} named optional | |
| 247 * @return {RTT} The RTT information object with named | |
| 248 */ | |
| 249 RTT.clone = function(old, named) { | |
| 250 var name = old.getClassName(); | |
| 251 var typekey = RTT.getTypeKey(name, old.typeArgs, old.returnType, named); | |
| 252 var rtt = $mapLookup(RTT.types, typekey); | |
| 253 if (rtt) { | |
| 254 return rtt; | |
| 255 } | |
| 256 var classkey = RTT.getTypeKey(name); | |
| 257 rtt = new RTT(classkey, typekey, old.typeArgs, old.returnType, old.functionTyp
e, named); | |
| 258 RTT.types[typekey] = rtt; | |
| 259 rtt.implementedTypes = old.implementedTypes | |
| 260 return rtt; | |
| 261 }; | |
| 262 | |
| 263 /** | |
| 264 * @param {string} classkey | |
| 265 * @param {Array.<(RTT|string)>=} typeargs | |
| 266 * @param {string} returntype | |
| 267 * @param {string=} named optional | |
| 268 * @return {string} | |
| 269 */ | |
| 270 RTT.getTypeKey = function(classkey, typeargs, returntype, named) { | |
| 271 var key = classkey; | |
| 272 if (named) { | |
| 273 key += ":" + named; | |
| 274 } | |
| 275 if (typeargs) { | |
| 276 key += "<" + typeargs.join(",") + ">"; | |
| 277 } | |
| 278 if (returntype) { | |
| 279 key += "-><" + returntype + ">"; | |
| 280 } | |
| 281 return key; | |
| 282 }; | |
| 283 | |
| 284 /** | |
| 285 * @return {*} value | |
| 286 * @return {RTT} return the RTT information object for the value | |
| 287 */ | |
| 288 RTT.getTypeInfo = function(value) { | |
| 289 return (value.$typeInfo) ? value.$typeInfo : RTT.getNativeTypeInfo(value); | |
| 290 }; | |
| 291 | |
| 292 /** | |
| 293 * @param {Object} o | |
| 294 * @param {RTT} rtt | |
| 295 * Sets the RTT on the object and returns the object itself. | |
| 296 */ | |
| 297 RTT.setTypeInfo = function(o, rtt) { | |
| 298 o.$typeInfo = rtt; | |
| 299 return o; | |
| 300 }; | |
| 301 | |
| 302 /** | |
| 303 * @param {Object} o | |
| 304 * Removes any RTT from the object and returns the object itself. | |
| 305 */ | |
| 306 RTT.removeTypeInfo = function(o) { | |
| 307 o.$typeInfo = null; | |
| 308 return o; | |
| 309 }; | |
| 310 | |
| 311 /** | |
| 312 * The typeArg array is optional | |
| 313 * @param {Array.<RTT>=} typeArgs | |
| 314 * @param {number} i | |
| 315 * @param {string=} named optional | |
| 316 * @return {RTT} | |
| 317 */ | |
| 318 RTT.getTypeArg = function(typeArgs, i, named) { | |
| 319 if (typeArgs) { | |
| 320 if (typeArgs.length > i) { | |
| 321 if (named && named != typeArgs[i].named) { | |
| 322 return RTT.clone(typeArgs[i], named); | |
| 323 } | |
| 324 return typeArgs[i]; | |
| 325 } else { | |
| 326 throw new Error("Missing type arg"); | |
| 327 } | |
| 328 } | |
| 329 return RTT.dynamicType.$lookupRTT(null,named); | |
| 330 }; | |
| 331 | |
| 332 /** | |
| 333 * The typeArg array is optional | |
| 334 * @param {*} o | |
| 335 * @param {string} classkey | |
| 336 * @return {Array.<RTT>} | |
| 337 */ | |
| 338 RTT.getTypeArgsFor = function(o, classkey) { | |
| 339 var rtt = $mapLookup(RTT.getTypeInfo(o).implementedTypes, classkey); | |
| 340 if (!rtt) { | |
| 341 throw new Error("internal error: can not find " + | |
| 342 classkey + " in " + JSON.stringify(o)); | |
| 343 } | |
| 344 return rtt.typeArgs; | |
| 345 }; | |
| 346 | |
| 347 // Base types for runtime type information | |
| 348 | |
| 349 /** @type {!RTT} */ | |
| 350 function ImplementsAll(name,named) { | |
| 351 var typeKey = RTT.getTypeKey(name, null, null, named); | |
| 352 RTT.call(this,name,typeKey,null,null,null,named); | |
| 353 } | |
| 354 $inherits(ImplementsAll,RTT); | |
| 355 ImplementsAll.prototype.implementedBy = function(o) {return true}; | |
| 356 ImplementsAll.prototype.implementedByType = function(o) {return true}; | |
| 357 | |
| 358 RTT.objectType = new ImplementsAll($cls('Object')); | |
| 359 RTT.placeholderType = new ImplementsAll($cls('::')); | |
| 360 | |
| 361 function ImplementsDynamic(named) { | |
| 362 ImplementsAll.call(this,$cls('Dynamic'),named); | |
| 363 } | |
| 364 $inherits(ImplementsDynamic,ImplementsAll); | |
| 365 ImplementsDynamic.prototype.$lookupRTT = function(typeArgs, named) { | |
| 366 var typekey = RTT.getTypeKey($cls('Dynamic'), null, null, named); | |
| 367 var rtt = $mapLookup(RTT.types, typekey); | |
| 368 if (rtt) { | |
| 369 return rtt; | |
| 370 } | |
| 371 rtt = new ImplementsDynamic(named); | |
| 372 RTT.types[typekey] = rtt; | |
| 373 return rtt; | |
| 374 } | |
| 375 RTT.dynamicType = ImplementsDynamic.prototype.$lookupRTT(); | |
| 376 | |
| 377 /** | |
| 378 * Checks that a value is assignable to an expected type, and either returns tha
t | |
| 379 * value if it is, or else throws a TypeMismatchException. | |
| 380 * | |
| 381 * @param {!RTT} the expected type | |
| 382 * @param {*} the value to check | |
| 383 * @return {*} the value | |
| 384 */ | |
| 385 function $chk(rtt, value) { | |
| 386 // null can be assigned to any type | |
| 387 if (value == $Dart$Null || rtt.implementedBy(value)) { | |
| 388 return value; | |
| 389 } | |
| 390 $te(rtt, value); | |
| 391 } | |
| 392 | |
| 393 /** | |
| 394 * Throw a TypeError. See core.dart for the ExceptionHelper class. | |
| 395 * | |
| 396 * @param {!RTT} the expected type | |
| 397 * @param {*) the value that failed | |
| 398 */ | |
| 399 function $te(rtt, value) { | |
| 400 var srcType = RTT.getTypeInfo(value).getClassName(); | |
| 401 var dstType = rtt.getClassName(); | |
| 402 var e = native_ExceptionHelper_createTypeError(srcType, dstType); | |
| 403 $Dart$ThrowException(e); | |
| 404 } | |
| 405 | |
| 406 // Setup the Function object | |
| 407 Function.prototype.$implements$Function$Dart = 1; | |
| 408 RTT.setTypeInfo(Function.prototype, RTT.create($cls('Function$Dart'))); | |
| 409 | |
| 410 /** | |
| 411 * @param {string} cls | |
| 412 * @return {string} | |
| 413 * @consistentIdGenerator | |
| 414 */ | |
| 415 function $cls(cls) { | |
| 416 return "cls:" + cls; | |
| 417 } | |
| 418 | |
| 419 /** | |
| 420 * @param {*} o | |
| 421 * @return {boolean} | |
| 422 */ | |
| 423 function $isBool(o) { | |
| 424 return typeof o == 'boolean'; | |
| 425 } | |
| 426 | |
| 427 /** | |
| 428 * @param {*} o | |
| 429 * @return {boolean} | |
| 430 */ | |
| 431 function $isNum(o) { | |
| 432 return typeof o == 'number'; | |
| 433 } | |
| 434 | |
| 435 /** | |
| 436 * @param {*} o | |
| 437 * @return {boolean} | |
| 438 */ | |
| 439 function $isString(o) { | |
| 440 return typeof o == 'string'; | |
| 441 } | |
| OLD | NEW |