OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, 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 /// This library defines the association between runtime objects and |
| 6 /// runtime types. |
| 7 |
| 8 library dart._rtti; |
| 9 import 'dart:_foreign_helper' show JS, rest; |
| 10 import 'dart:_utils' as utils; |
| 11 import 'dart:_types' show definiteFunctionType, dynamicR, bottom, jsobject; |
| 12 |
| 13 final defineLazyProperty = JS('', '${utils.defineLazyProperty}'); |
| 14 final defineProperty = JS('', 'Object.defineProperty'); |
| 15 |
| 16 /// |
| 17 /// Runtime type information. This module defines the mapping from |
| 18 /// runtime objects to their runtime type information. See the types |
| 19 /// module for the definition of how type information is represented. |
| 20 /// |
| 21 /// Runtime objects fall into four main categories: |
| 22 /// |
| 23 /// - Things represented by javascript primitives, such as |
| 24 /// null, numbers, booleans, strings, and symbols. For these |
| 25 /// we map directly from the javascript type (given by typeof) |
| 26 /// to the appropriate class type from core, which serves as their |
| 27 /// rtti. |
| 28 /// |
| 29 /// - Functions, which are represented by javascript functions. |
| 30 /// Representations of Dart functions always have a |
| 31 /// _runtimeType property attached to them with the appropriate |
| 32 /// rtti. |
| 33 /// |
| 34 /// - Objects (instances) which are represented by instances of |
| 35 /// javascript (ES6) classes. Their types are given by their |
| 36 /// classes, and the rtti is accessed by projecting out their |
| 37 /// constructor field. |
| 38 /// |
| 39 /// - Types objects, which are represented as described in the types |
| 40 /// module. Types always have a _runtimeType property attached to |
| 41 /// them with the appropriate rtti. The rtti for these is always |
| 42 /// core.Type. TODO(leafp): consider the possibility that we can |
| 43 /// reliably recognize type objects and map directly to core.Type |
| 44 /// rather than attaching this property everywhere. |
| 45 /// |
| 46 /// |
| 47 |
| 48 /// |
| 49 ///Tag a closure with a type, using one of three forms: |
| 50 /// dart.fn(cls) marks cls has having no optional or named |
| 51 /// parameters, with all argument and return types as dynamic |
| 52 /// dart.fn(cls, func) marks cls with the lazily computed |
| 53 /// runtime type as computed by func() |
| 54 /// dart.fn(cls, rType, argsT, extras) marks cls as having the |
| 55 /// runtime type dart.functionType(rType, argsT, extras) |
| 56 /// |
| 57 /// Note that since we are producing a type for a concrete function, |
| 58 /// it is sound to use the definite arrow type. |
| 59 /// |
| 60 fn(closure, @rest args) => JS('', '''(() => { |
| 61 // Closure and a lazy type constructor |
| 62 if ($args.length == 1) { |
| 63 $defineLazyProperty($closure, $_runtimeType, {get : $args[0]}); |
| 64 return $closure; |
| 65 } |
| 66 let t; |
| 67 if ($args.length == 0) { |
| 68 // No type arguments, it's all dynamic |
| 69 t = $definiteFunctionType( |
| 70 $dynamicR, Array($closure.length).fill($dynamicR)); |
| 71 } else { |
| 72 // We're passed the piecewise components of the function type, |
| 73 // construct it. |
| 74 t = $definiteFunctionType.apply(null, $args); |
| 75 } |
| 76 tag($closure, t); |
| 77 return $closure; |
| 78 })()'''); |
| 79 |
| 80 // TODO(vsm): How should we encode the runtime type? |
| 81 final _runtimeType = JS('', 'Symbol("_runtimeType")'); |
| 82 |
| 83 checkPrimitiveType(obj) => JS('', '''(() => { |
| 84 switch (typeof $obj) { |
| 85 case "undefined": |
| 86 return $Null; |
| 87 case "number": |
| 88 return Math.floor($obj) == $obj ? $int : $double; |
| 89 case "boolean": |
| 90 return $bool; |
| 91 case "string": |
| 92 return $String; |
| 93 case "symbol": |
| 94 return Symbol; |
| 95 } |
| 96 // Undefined is handled above. For historical reasons, |
| 97 // typeof null == "object" in JS. |
| 98 if ($obj === null) return $Null; |
| 99 return null; |
| 100 })()'''); |
| 101 |
| 102 runtimeType(obj) => JS('', '''(() => { |
| 103 let result = $checkPrimitiveType($obj); |
| 104 if (result !== null) return result; |
| 105 return $obj.runtimeType; |
| 106 })()'''); |
| 107 |
| 108 getFunctionType(obj) => JS('', '''(() => { |
| 109 // TODO(vsm): Encode this properly on the function for Dart-generated code. |
| 110 let args = Array($obj.length).fill($dynamicR); |
| 111 return $definiteFunctionType($bottom, args); |
| 112 })()'''); |
| 113 |
| 114 /// |
| 115 /// Returns the runtime type of obj. This is the same as `obj.realRuntimeType` |
| 116 /// but will not call an overridden getter. |
| 117 /// |
| 118 /// Currently this will return null for non-Dart objects. |
| 119 /// |
| 120 realRuntimeType(obj) => JS('', '''(() => { |
| 121 let result = $checkPrimitiveType($obj); |
| 122 if (result !== null) return result; |
| 123 // TODO(vsm): Should we treat Dart and JS objects differently here? |
| 124 // E.g., we can check if obj instanceof core.Object to differentiate. |
| 125 result = $obj[$_runtimeType]; |
| 126 if (result) return result; |
| 127 result = $obj.constructor; |
| 128 if (result == Function) { |
| 129 // An undecorated Function should have come from |
| 130 // JavaScript. Treat as untyped. |
| 131 return $jsobject; |
| 132 } |
| 133 return result; |
| 134 })()'''); |
| 135 |
| 136 LazyTagged(infoFn) => JS('', '''(() => { |
| 137 class _Tagged { |
| 138 get [$_runtimeType]() {return $infoFn();} |
| 139 } |
| 140 return _Tagged; |
| 141 })()'''); |
| 142 |
| 143 read(value) => JS('', '''(() => { |
| 144 return $value[$_runtimeType]; |
| 145 })()'''); |
| 146 |
| 147 tag(value, info) => JS('', '''(() => { |
| 148 $value[$_runtimeType] = $info; |
| 149 })()'''); |
| 150 |
| 151 tagComputed(value, compute) => JS('', '''(() => { |
| 152 $defineProperty($value, $_runtimeType, { get: $compute }); |
| 153 })()'''); |
| 154 |
| 155 tagMemoized(value, compute) => JS('', '''(() => { |
| 156 let cache = null; |
| 157 function getter() { |
| 158 if ($compute == null) return cache; |
| 159 cache = $compute(); |
| 160 $compute = null; |
| 161 return cache; |
| 162 } |
| 163 $tagComputed($value, getter); |
| 164 })()'''); |
OLD | NEW |