Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1001)

Side by Side Diff: sdk/lib/_internal/compiler/js_lib/js_rti.dart

Issue 1212513002: sdk files reorganization to make dart2js a proper package (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: renamed Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698