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

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

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

Powered by Google App Engine
This is Rietveld 408576698