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 |