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 |