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

Side by Side Diff: tool/input_sdk/private/classes.dart

Issue 1530563003: Generate all runtime files from dart. (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Reverted to new .dart files in input_sdk: please compare them against previous patchset Created 5 years 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) 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 operations that define and manipulate Dart
6 /// classes. Included in this are:
7 /// - Generics
8 /// - Class metadata
9 /// - Extension methods
10 ///
11
12 // TODO(leafp): Consider splitting some of this out.
13 library dart._classes;
14
15 import 'dart:_foreign_helper' show JS, JsName, rest, genericTypeConstructor;
16
17 import 'dart:_interceptors' show JSArray;
18 import 'dart:_types' show definiteFunctionType, dynamicR;
19 import 'dart:_rtti' show tag, tagMemoized;
20 import 'dart:_utils' as utils;
21
22 @JsName('assert')
23 final assert_ = JS('', '${utils.assert_}');
24 final copyProperties = JS('', '${utils.copyProperties}');
25 final copyTheseProperties = JS('', '${utils.copyTheseProperties}');
26 final defineMemoizedGetter = JS('', '${utils.defineMemoizedGetter}');
27 final safeGetOwnProperty = JS('', '${utils.safeGetOwnProperty}');
28 final throwInternalError = JS('', '${utils.throwInternalError}');
29
30 final defineProperty = JS('', 'Object.defineProperty');
31 final getOwnPropertyDescriptor = JS('', 'Object.getOwnPropertyDescriptor');
32 final getOwnPropertySymbols = JS('', 'Object.getOwnPropertySymbols');
33
34 /// The Symbol for storing type arguments on a specialized generic type.
35 final _mixins = JS('', 'Symbol("mixins")');
36 @JsName('implements')
37 final implements_ = JS('', 'Symbol("implements")');
38 final metadata = JS('', 'Symbol("metadata")');
39
40 ///
41 /// Returns a new type that mixes members from base and all mixins.
42 ///
43 /// Each mixin applies in sequence, with further to the right ones overriding
44 /// previous entries.
45 ///
46 /// For each mixin, we only take its own properties, not anything from its
47 /// superclass (prototype).
48 ///
49 mixin(base, @rest mixins) => JS('', '''(() => {
50 // Create an initializer for the mixin, so when derived constructor calls
51 // super, we can correctly initialize base and mixins.
52
53 // Create a class that will hold all of the mixin methods.
54 class Mixin extends $base {
55 // Initializer method: run mixin initializers, then the base.
56 [$base.name](...args) {
57 // Run mixin initializers. They cannot have arguments.
58 // Run them backwards so most-derived mixin is initialized first.
59 for (let i = $mixins.length - 1; i >= 0; i--) {
60 let mixin = $mixins[i];
61 let init = mixin.prototype[mixin.name];
62 if (init) init.call(this);
63 }
64 // Run base initializer.
65 let init = $base.prototype[base.name];
66 if (init) init.apply(this, args);
67 }
68 }
69 // Copy each mixin's methods, with later ones overwriting earlier entries.
70 for (let m of $mixins) {
71 $copyProperties(Mixin.prototype, m.prototype);
72 }
73
74 // Set the signature of the Mixin class to be the composition
75 // of the signatures of the mixins.
76 $setSignature(Mixin, {
77 methods: () => {
78 let s = {};
79 for (let m of $mixins) {
80 $copyProperties(s, m[$_methodSig]);
81 }
82 return s;
83 }
84 });
85
86 // Save mixins for reflection
87 Mixin[$_mixins] = $mixins;
88 return Mixin;
89 })()''');
90
91 getMixins(clazz) => JS('', '''(() => {
92 return $clazz[$_mixins];
93 })()''');
94
95 getImplements(clazz) => JS('', '''(() => {
96 return $clazz[$implements_];
97 })()''');
98
99 /// The Symbol for storing type arguments on a specialized generic type.
100 final _typeArguments = JS('', 'Symbol("typeArguments")');
101 final _originalDeclaration = JS('', 'Symbol("originalDeclaration")');
102
103 /// Memoize a generic type constructor function.
104 generic(typeConstructor) => JS('', '''(() => {
105 let length = $typeConstructor.length;
106 if (length < 1) {
107 $throwInternalError('must have at least one generic type argument');
108 }
109 let resultMap = new Map();
110 function makeGenericType(...args) {
111 if (args.length != length && args.length != 0) {
112 $throwInternalError('requires ' + length + ' or 0 type arguments');
113 }
114 while (args.length < length) args.push($dynamicR);
115
116 let value = resultMap;
117 for (let i = 0; i < length; i++) {
118 let arg = args[i];
119 if (arg == null) {
120 $throwInternalError('type arguments should not be null: '
121 + $typeConstructor);
122 }
123 let map = value;
124 value = map.get(arg);
125 if (value === void 0) {
126 if (i + 1 == length) {
127 value = $typeConstructor.apply(null, args);
128 // Save the type constructor and arguments for reflection.
129 if (value) {
130 value[$_typeArguments] = args;
131 value[$_originalDeclaration] = makeGenericType;
132 }
133 } else {
134 value = new Map();
135 }
136 map.set(arg, value);
137 }
138 }
139 return value;
140 }
141 return makeGenericType;
142 })()''');
143
144 getGenericClass(type) => JS('', '''(() => {
145 return $safeGetOwnProperty($type, $_originalDeclaration);
146 })()''');
147
148 getGenericArgs(type) => JS('', '''(() => {
149 return $safeGetOwnProperty($type, $_typeArguments);
150 })()''');
151
152 final _constructorSig = JS('', 'Symbol("sigCtor")');
153 final _methodSig = JS('', 'Symbol("sig")');
154 final _staticSig = JS('', 'Symbol("sigStatic")');
155
156 /// Get the type of a method using the stored signature
157 getMethodType(obj, name) => JS('', '''(() => {
158 if ($obj === void 0) return void 0;
159 if ($obj == null) return void 0;
160 let sigObj = $obj.__proto__.constructor[$_methodSig];
161 if (sigObj === void 0) return void 0;
162 let parts = sigObj[$name];
163 if (parts === void 0) return void 0;
164 return $definiteFunctionType.apply(null, parts);
165 })()''');
166
167 /// Get the type of a constructor from a class using the stored signature
168 /// If name is undefined, returns the type of the default constructor
169 /// Returns undefined if the constructor is not found.
170 classGetConstructorType(cls, name) => JS('', '''(() => {
171 if(!$name) $name = $cls.name;
172 if ($cls === void 0) return void 0;
173 if ($cls == null) return void 0;
174 let sigCtor = $cls[$_constructorSig];
175 if (sigCtor === void 0) return void 0;
176 let parts = sigCtor[$name];
177 if (parts === void 0) return void 0;
178 return $definiteFunctionType.apply(null, parts);
179 })()''');
180
181 /// Given an object and a method name, tear off the method.
182 /// Sets the runtime type of the torn off method appropriately,
183 /// and also binds the object.
184 ///
185 /// If the optional `f` argument is passed in, it will be used as the method.
186 /// This supports cases like `super.foo` where we need to tear off the method
187 /// from the superclass, not from the `obj` directly.
188 /// TODO(leafp): Consider caching the tearoff on the object?
189 bind(obj, name, f) => JS('', '''(() => {
190 if ($f === void 0) $f = $obj[$name];
191 $f = $f.bind($obj);
192 // TODO(jmesserly): track the function's signature on the function, instead
193 // of having to go back to the class?
194 let sig = $getMethodType($obj, $name);
195 $assert_(sig);
196 $tag($f, sig);
197 return $f;
198 })()''');
199
200 // Set up the method signature field on the constructor
201 _setMethodSignature(f, sigF) => JS('', '''(() => {
202 $defineMemoizedGetter($f, $_methodSig, () => {
203 let sigObj = $sigF();
204 sigObj.__proto__ = $f.__proto__[$_methodSig];
205 return sigObj;
206 });
207 })()''');
208
209 // Set up the constructor signature field on the constructor
210 _setConstructorSignature(f, sigF) => JS('', '''(() => {
211 $defineMemoizedGetter($f, $_constructorSig, $sigF);
212 })()''');
213
214 // Set up the static signature field on the constructor
215 _setStaticSignature(f, sigF) => JS('', '''(() => {
216 $defineMemoizedGetter($f, $_staticSig, $sigF);
217 })()''');
218
219 // Set the lazily computed runtime type field on static methods
220 _setStaticTypes(f, names) => JS('', '''(() => {
221 for (let name of $names) {
222 $tagMemoized($f[name], function() {
223 let parts = $f[$_staticSig][name];
224 return $definiteFunctionType.apply(null, parts);
225 })
226 }
227 })()''');
228
229 /// Set up the type signature of a class (constructor object)
230 /// f is a constructor object
231 /// signature is an object containing optional properties as follows:
232 /// methods: A function returning an object mapping method names
233 /// to method types. The function is evaluated lazily and cached.
234 /// statics: A function returning an object mapping static method
235 /// names to types. The function is evalutated lazily and cached.
236 /// names: An array of the names of the static methods. Used to
237 /// permit eagerly setting the runtimeType field on the methods
238 /// while still lazily computing the type descriptor object.
239 setSignature(f, signature) => JS('', '''(() => {
240 // TODO(ochafik): Deconstruct these when supported by Chrome.
241 let constructors =
242 ('constructors' in signature) ? signature.constructors : () => ({});
243 let methods =
244 ('methods' in signature) ? signature.methods : () => ({});
245 let statics =
246 ('statics' in signature) ? signature.statics : () => ({});
247 let names =
248 ('names' in signature) ? signature.names : [];
249 $_setConstructorSignature($f, constructors);
250 $_setMethodSignature($f, methods);
251 $_setStaticSignature($f, statics);
252 $_setStaticTypes($f, names);
253 $tagMemoized($f, () => $Type);
254 })()''');
255
256 hasMethod(obj, name) => JS('', '''(() => {
257 return $getMethodType($obj, $name) !== void 0;
258 })()''');
259
260 ///
261 /// This is called whenever a derived class needs to introduce a new field,
262 /// shadowing a field or getter/setter pair on its parent.
263 ///
264 /// This is important because otherwise, trying to read or write the field
265 /// would end up calling the getter or setter, and one of those might not even
266 /// exist, resulting in a runtime error. Even if they did exist, that's the
267 /// wrong behavior if a new field was declared.
268 ///
269 virtualField(subclass, fieldName) => JS('', '''(() => {
270 // If the field is already overridden, do nothing.
271 let prop = $getOwnPropertyDescriptor($subclass.prototype, $fieldName);
272 if (prop) return;
273
274 let symbol = Symbol($subclass.name + '.' + $fieldName);
275 $defineProperty($subclass.prototype, $fieldName, {
276 get: function() { return this[symbol]; },
277 set: function(x) { this[symbol] = x; }
278 });
279 })()''');
280
281 ///
282 /// Given a class and an initializer method name, creates a constructor
283 /// function with the same name. For example `new SomeClass.name(args)`.
284 ///
285 defineNamedConstructor(clazz, name) => JS('', '''(() => {
286 let proto = $clazz.prototype;
287 let initMethod = proto[$name];
288 let ctor = function() { return initMethod.apply(this, arguments); };
289 ctor.prototype = proto;
290 // Use defineProperty so we don't hit a property defined on Function,
291 // like `caller` and `arguments`.
292 $defineProperty($clazz, $name, { value: ctor, configurable: true });
293 })()''');
294
295 final _extensionType = JS('', 'Symbol("extensionType")');
296
297 final dartx = JS('', '{}');
298
299 getExtensionSymbol(name) => JS('', '''(() => {
300 let sym = $dartx[$name];
301 if (!sym) $dartx[$name] = sym = Symbol('dartx.' + $name);
302 return sym;
303 })()''');
304
305 defineExtensionNames(names) => JS('', '''(() => {
306 $names.forEach($getExtensionSymbol);
307 })()''');
308
309 ///
310 /// Copy symbols from the prototype of the source to destination.
311 /// These are the only properties safe to copy onto an existing public
312 /// JavaScript class.
313 ///
314 registerExtension(jsType, dartExtType) => JS('', '''(() => {
315 let extProto = $dartExtType.prototype;
316 let jsProto = $jsType.prototype;
317
318 // Mark the JS type's instances so we can easily check for extensions.
319 $assert_(jsProto[$_extensionType] === void 0);
320 jsProto[$_extensionType] = extProto;
321
322 let dartObjProto = $Object.prototype;
323 while (extProto !== dartObjProto && extProto !== jsProto) {
324 $copyTheseProperties(jsProto, extProto, $getOwnPropertySymbols(extProto));
325 extProto = extProto.__proto__;
326 }
327 let originalSigFn = $getOwnPropertyDescriptor($dartExtType, $_methodSig).get;
328 $assert_(originalSigFn);
329 $defineMemoizedGetter($jsType, $_methodSig, originalSigFn);
330 })()''');
331
332 ///
333 /// Mark a concrete type as implementing extension methods.
334 /// For example: `class MyIter implements Iterable`.
335 ///
336 /// This takes a list of names, which are the extension methods implemented.
337 /// It will add a forwarder, so the extension method name redirects to the
338 /// normal Dart method name. For example:
339 ///
340 /// defineExtensionMembers(MyType, ['add', 'remove']);
341 ///
342 /// Results in:
343 ///
344 /// MyType.prototype[dartx.add] = MyType.prototype.add;
345 /// MyType.prototype[dartx.remove] = MyType.prototype.remove;
346 ///
347 // TODO(jmesserly): essentially this gives two names to the same method.
348 // This benefit is roughly equivalent call performance either way, but the
349 // cost is we need to call defineExtensionMembers any time a subclass
350 // overrides one of these methods.
351 defineExtensionMembers(type, methodNames) => JS('', '''(() => {
352 let proto = $type.prototype;
353 for (let name of $methodNames) {
354 let method = $getOwnPropertyDescriptor(proto, name);
355 $defineProperty(proto, $getExtensionSymbol(name), method);
356 }
357 // Ensure the signature is available too.
358 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially
359 // we need to copy the signature (and in the future, other data like
360 // annotations) any time we copy a method as part of our metaprogramming.
361 // It might be more friendly to JS metaprogramming if we include this info
362 // on the function.
363 let originalSigFn = $getOwnPropertyDescriptor($type, $_methodSig).get;
364 $defineMemoizedGetter(type, $_methodSig, function() {
365 let sig = originalSigFn();
366 for (let name of $methodNames) {
367 sig[$getExtensionSymbol(name)] = sig[name];
368 }
369 return sig;
370 });
371 })()''');
372
373 canonicalMember(obj, name) => JS('', '''(() => {
374 if ($obj != null && $obj[$_extensionType]) return $dartx[$name];
375 // Check for certain names that we can't use in JS
376 if ($name == 'constructor' || $name == 'prototype') {
377 $name = '+' + $name;
378 }
379 return $name;
380 })()''');
381
382 /// Sets the type of `obj` to be `type`
383 setType(obj, type) => JS('', '''(() => {
384 $obj.__proto__ = $type.prototype;
385 return $obj;
386 })()''');
387
388 /// Sets the element type of a list literal.
389 list(obj, elementType) => JS('', '''(() => {
390 // TODO(ochafik): Expose a way to build imported generics.
391 return $setType($obj, ${genericTypeConstructor(JSArray)}($elementType));
392 })()''');
393
394 setBaseClass(derived, base) => JS('', '''(() => {
395 // Link the extension to the type it's extending as a base class.
396 $derived.prototype.__proto__ = $base.prototype;
397 })()''');
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698