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 library dev_compiler.runtime.dart_runtime; | |
6 | |
7 import 'dart:mirrors'; | |
8 | |
9 import 'package:dev_compiler/config.dart'; | |
10 | |
11 dynamic dload(dynamic obj, String field) { | |
12 var symbol = new Symbol(field); | |
13 var mirror = reflect(obj); | |
14 // TODO(vsm): Does this create an NSM? | |
15 var fieldMirror = mirror.getField(symbol); | |
16 return fieldMirror.reflectee; | |
17 } | |
18 | |
19 dynamic dinvokef(dynamic f, List args) { | |
20 // TODO(vsm): Support named arguments. | |
21 assert(f is Function); | |
22 return Function.apply(f, args); | |
23 } | |
24 | |
25 // A workaround to manufacture a generic Type object inline. | |
26 // We use mirrors to extract type T given a TypeFunction<T>. | |
27 // E.g., Map<String, String> is not a valid literal in Dart. | |
28 // Instead, use: type((Map<String, String> _) {}); | |
29 // See bug: https://code.google.com/p/dart/issues/detail?id=11923 | |
30 typedef TypeFunction<T>(T x); | |
31 | |
32 Type type(TypeFunction f) { | |
33 ClosureMirror cm = reflect(f); | |
34 MethodMirror mm = cm.function; | |
35 ParameterMirror pm = mm.parameters[0]; | |
36 TypeMirror tm = pm.type; | |
37 return tm.reflectedType; | |
38 } | |
39 | |
40 dynamic cast(dynamic obj, Type staticType) { | |
41 // This is our 'as' equivalent. | |
42 if (obj == null) { | |
43 // A null can be cast only to non-primitive types. | |
44 if (!isPrimitiveType(staticType)) return null; | |
45 } else { | |
46 // For non-null values, val is T => val as T succeeds. | |
47 if (instanceOf(obj, staticType)) return obj; | |
48 } | |
49 // TODO(vsm): Add message. | |
50 throw new CastError(); | |
51 } | |
52 | |
53 bool instanceOf(dynamic obj, Type staticType) { | |
54 // This is our 'is' equivalent. | |
55 Type runtimeType = obj.runtimeType; | |
56 return _isSubType(reflectType(runtimeType), reflectType(staticType)); | |
57 } | |
58 | |
59 bool isGroundType(Type type) { | |
60 // These are types allowed in is / as expressions. | |
61 final mirror = reflectType(type); | |
62 return _isGroundTypeMirror(mirror); | |
63 } | |
64 | |
65 final _primitiveMap = { | |
66 'int': int, | |
67 'double': double, | |
68 'num': num, | |
69 'bool': bool, | |
70 'String': String, | |
71 }; | |
72 | |
73 // TODO(vsm): Make this configurable? Using default settings for now. | |
74 final _typeOptions = new TypeOptions(); | |
75 | |
76 Set<Type> _primitives = () { | |
77 var types = _typeOptions.nonnullableTypes; | |
78 var set = new Set<Type>.from(types.map((t) => _primitiveMap[t])); | |
79 return set; | |
80 }(); | |
81 | |
82 bool isPrimitiveType(Type t) { | |
83 return _primitives.contains(t); | |
84 } | |
85 | |
86 class Arity { | |
87 final int normal; | |
88 final int optionalPositional; | |
89 | |
90 Arity._internal(this.normal, this.optionalPositional); | |
91 | |
92 int get min => normal; | |
93 int get max => normal + optionalPositional; | |
94 } | |
95 | |
96 Arity getArity(Function f) { | |
97 final FunctionTypeMirror mirror = reflectType(f.runtimeType); | |
98 final parameters = mirror.parameters; | |
99 int normal = 0; | |
100 int optionalPositional = 0; | |
101 for (var parameter in parameters) { | |
102 if (parameter.isNamed) { | |
103 // Ignore named parameters - these cannot be passed positionally. | |
104 } else if (parameter.isOptional) { | |
105 optionalPositional++; | |
106 } else { | |
107 normal++; | |
108 } | |
109 } | |
110 return new Arity._internal(normal, optionalPositional); | |
111 } | |
112 | |
113 bool _isFunctionSubType(TypeMirror ret1, List<ParameterMirror> params1, | |
114 TypeMirror ret2, List<ParameterMirror> params2) { | |
115 if (!_isSubType(ret1, ret2)) { | |
116 // Covariant return types | |
117 // Note, void (which can only appear as a return type) is effectively | |
118 // treated as dynamic. If the base return type is void, we allow any | |
119 // subtype return type. | |
120 // E.g., we allow: | |
121 // () -> int <: () -> void | |
122 if (ret2.simpleName != const Symbol('void')) { | |
123 return false; | |
124 } | |
125 } | |
126 | |
127 if (params1.length < params2.length) { | |
128 return false; | |
129 } | |
130 | |
131 for (int i = 0; i < params2.length; ++i) { | |
132 ParameterMirror p1 = params1[i]; | |
133 ParameterMirror p2 = params2[i]; | |
134 | |
135 // Contravariant parameter types. | |
136 if (!_isSubType(p2.type, p1.type, dynamicIsBottom: true)) { | |
137 return false; | |
138 } | |
139 | |
140 // Optional parameters. | |
141 if (p2.isOptional) { | |
142 // If the base param is optional, the sub param must be optional: | |
143 if (!p1.isOptional) return false; | |
144 if (!p2.isNamed) { | |
145 // either neither are named or | |
146 if (p1.isNamed) return false; | |
147 } else { | |
148 // both are named with the same name | |
149 if (!p1.isNamed || p1.simpleName != p2.simpleName) return false; | |
150 } | |
151 } else { | |
152 // If the base param is required, the sub may be optional, but not named. | |
153 if (p1.isNamed) return false; | |
154 } | |
155 } | |
156 | |
157 for (int i = params2.length; i < params1.length; ++i) { | |
158 ParameterMirror p1 = params1[i]; | |
159 // Any additional sub params must be optional. | |
160 if (!p1.isOptional) return false; | |
161 } | |
162 | |
163 return true; | |
164 } | |
165 | |
166 bool _isClassSubType(ClassMirror m1, ClassMirror m2) { | |
167 // TODO(vsm): Consider some caching for efficiency here. | |
168 | |
169 // We support Dart's covariant generics with the caveat that we do not | |
170 // substitute bottom for dynamic in subtyping rules. | |
171 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: | |
172 // - S !<: S<T1, ..., Tn> | |
173 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> | |
174 if (m1 == m2) return true; | |
175 | |
176 if (_isTop(m1)) return false; | |
177 | |
178 // Check if m1 and m2 have the same raw type. If so, check covariance on | |
179 // type parameters. | |
180 if (m1.originalDeclaration == m2.originalDeclaration) { | |
181 if (_isRawClass(m2)) return true; | |
182 if (_isRawClass(m1)) return false; | |
183 | |
184 final typeArguments1 = m1.typeArguments; | |
185 final typeArguments2 = m2.typeArguments; | |
186 final length = typeArguments1.length; | |
187 assert(typeArguments1.isNotEmpty && typeArguments2.isNotEmpty); | |
188 assert(typeArguments2.length == length); | |
189 for (var i = 0; i < length; ++i) { | |
190 var typeArgument1 = typeArguments1[i]; | |
191 var typeArgument2 = typeArguments2[i]; | |
192 if (!_isSubType(typeArgument1, typeArgument2)) { | |
193 return false; | |
194 } | |
195 } | |
196 return true; | |
197 } | |
198 | |
199 // Check superclass. | |
200 if (_isClassSubType(m1.superclass, m2)) return true; | |
201 | |
202 // Check for mixins. The mixin getter returns the original class if there is | |
203 // no mixin. | |
204 if (m1 != m1.mixin && _isClassSubType(m1.mixin, m2)) return true; | |
205 | |
206 // Check interfaces. | |
207 for (final parent in m1.superinterfaces) { | |
208 if (_isClassSubType(parent, m2)) return true; | |
209 } | |
210 | |
211 return false; | |
212 } | |
213 | |
214 final _dynamicMirror = reflectType(dynamic); | |
215 final _objectMirror = reflectType(Object); | |
216 | |
217 bool _isBottom(TypeMirror t, {bool dynamicIsBottom: false}) { | |
218 if (t == _dynamicMirror && dynamicIsBottom) return true; | |
219 // TODO(vsm): Do we need an explicit representation of Bottom? | |
220 return false; | |
221 } | |
222 | |
223 bool _isTop(TypeMirror t, {bool dynamicIsBottom: false}) { | |
224 if (t == _dynamicMirror && !dynamicIsBottom) return true; | |
225 if (t == _objectMirror) return true; | |
226 return false; | |
227 } | |
228 | |
229 bool _isGroundTypeMirror(TypeMirror mirror) { | |
230 // This is a runtime type - we should not see type parameters here. | |
231 assert(mirror is! TypeVariableMirror); | |
232 | |
233 // Allow only 'raw' functions. | |
234 if (mirror is TypedefMirror) { | |
235 return _isRawFunction(mirror.referent); | |
236 } | |
237 if (mirror is FunctionTypeMirror) { | |
238 return _isRawFunction(mirror); | |
239 } | |
240 | |
241 // Allow only 'raw' classes. | |
242 if (mirror is ClassMirror) { | |
243 return _isRawClass(mirror); | |
244 } | |
245 | |
246 // Only dynamic should be left. Should this be allowed? | |
247 // It's not particularly useful. | |
248 assert(mirror.reflectedType == dynamic); | |
249 return true; | |
250 } | |
251 | |
252 bool _isRawFunction(FunctionTypeMirror mirror) { | |
253 var returnType = mirror.returnType; | |
254 if (!_isTop(returnType)) return false; | |
255 for (var parameter in mirror.parameters) { | |
256 var paramType = parameter.type; | |
257 if (!_isBottom(paramType, dynamicIsBottom: true)) return false; | |
258 } | |
259 return true; | |
260 } | |
261 | |
262 bool _isRawClass(ClassMirror mirror) { | |
263 // Allow only raw types. | |
264 if (mirror == mirror.originalDeclaration) return true; | |
265 for (var typeArgument in mirror.typeArguments) { | |
266 if (!_isTop(typeArgument)) return false; | |
267 } | |
268 return true; | |
269 } | |
270 | |
271 TypeMirror _canonicalizeTypeMirror(TypeMirror t) { | |
272 if (t is TypedefMirror) { | |
273 // We canonicalize Typedefs to their underlying function types. | |
274 t = (t as TypedefMirror).referent; | |
275 } | |
276 if (t is ClassMirror && _isRawClass(t)) { | |
277 // We canonicalize T<dynamic> to T. | |
278 t = t.originalDeclaration; | |
279 } | |
280 return t; | |
281 } | |
282 | |
283 bool _reflects(TypeMirror mirror, Type t) { | |
284 return mirror.hasReflectedType && mirror.reflectedType == t; | |
285 } | |
286 | |
287 bool _isSubType(TypeMirror t1, TypeMirror t2, {bool dynamicIsBottom: false}) { | |
288 t1 = _canonicalizeTypeMirror(t1); | |
289 t2 = _canonicalizeTypeMirror(t2); | |
290 | |
291 if (t1 is TypeVariableMirror) { | |
292 t1 = t1.upperBound; | |
293 } | |
294 | |
295 if (t1 == t2) return true; | |
296 | |
297 // Trivially true. | |
298 if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) || | |
299 _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) { | |
300 return true; | |
301 } | |
302 | |
303 // Trivially false. | |
304 if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) || | |
305 _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) { | |
306 return false; | |
307 } | |
308 | |
309 // "Traditional" name-based subtype check. | |
310 final c1 = t1 as ClassMirror; | |
311 final c2 = t2 as ClassMirror; | |
312 if (_isClassSubType(c1, c2)) { | |
313 return true; | |
314 } | |
315 | |
316 // Function subtyping. | |
317 // Note: it appears under the hood all Dart functions map to a class / hidden
type | |
318 // that: | |
319 // (a) subtypes Object (an internal _FunctionImpl in the VM) | |
320 // (b) implements Function | |
321 // (c) provides standard Object members (hashCode, toString) | |
322 // (d) contains private members (corresponding to _FunctionImpl?) | |
323 // (e) provides a call method to handle the actual function invocation | |
324 // | |
325 // The standard Dart subtyping rules are structural in nature. I.e., | |
326 // bivariant on arguments and return type. | |
327 // | |
328 // The below tries for a more traditional subtyping rule: | |
329 // - covariant on return type | |
330 // - contravariant on parameters | |
331 // - 'sensible' (?) rules on optional and/or named params | |
332 // but doesn't properly mix with class subtyping yet. | |
333 // | |
334 // Note, a class type that implements a call method implicitly subtypes | |
335 // the function type of the call method. However, the converse is not true: | |
336 // a function type does not subtype a class type with a call method. | |
337 | |
338 // If c1 is not a proper function or a class type with call method, | |
339 // return false. | |
340 TypeMirror ret1; | |
341 List<ParameterMirror> params1; | |
342 // Note, a proper function has a call method, but it's not a regular method, | |
343 // so we break out the two cases. | |
344 if (c1 is FunctionTypeMirror) { | |
345 // Regular function | |
346 ret1 = c1.returnType; | |
347 params1 = c1.parameters; | |
348 } else { | |
349 var call1 = c1.instanceMembers[#call]; | |
350 if (call1 == null || !call1.isRegularMethod) return false; | |
351 // Class that emulate a function | |
352 ret1 = call1.returnType; | |
353 params1 = call1.parameters; | |
354 } | |
355 | |
356 // Any type that implements a call method implicitly subtypes Function. | |
357 if (_reflects(c2, Function)) return true; | |
358 | |
359 // Check structural function subtyping | |
360 return _isFunctionSubType(ret1, params1, c2.returnType, c2.parameters); | |
361 } | |
OLD | NEW |