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

Side by Side Diff: tool/input_sdk/private/operations.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 runtime operations on objects used by the code
6 /// generator.
7
8 library dart._operations;
9 import 'dart:_foreign_helper' show JS, JsName, rest;
10 import 'dart:_utils' as utils;
11 import 'dart:_js_helper' show getTraceFromException;
12 import 'dart:_classes' show canonicalMember, hasMethod, bind, getMethodType;
13 import 'dart:_errors';
14 import 'dart:_errors' as errors;
15 import 'dart:_rtti' show read, realRuntimeType;
16 import 'dart:_types' show isGroundType, isSubtype, jsobject, typeName;
17
18 import 'dart:async';
19 import 'dart:collection';
20
21 final getOwnNamesAndSymbols = JS('', '${utils.getOwnNamesAndSymbols}');
22
23 final getOwnPropertyNames = JS('', 'Object.getOwnPropertyNames');
24 final hasOwnProperty = JS('', 'Object.prototype.hasOwnProperty');
25
26 _canonicalFieldName(obj, name, args, displayName) => JS('', '''(() => {
27 $name = $canonicalMember($obj, $name);
28 if ($name) return $name;
29 // TODO(jmesserly): in the future we might have types that "overlay" Dart
30 // methods while also exposing the full native API, e.g. dart:html vs
31 // dart:dom. To support that we'd need to fall back to the normal name
32 // if an extension method wasn't found.
33 $throwNoSuchMethod($obj, $displayName, $args);
34 })()''');
35
36 dload(obj, field) => JS('', '''(() => {
37 $field = $_canonicalFieldName($obj, $field, [], $field);
38 if ($hasMethod($obj, $field)) {
39 return $bind($obj, $field);
40 }
41 // TODO(vsm): Implement NSM robustly. An 'in' check breaks on certain
42 // types. hasOwnProperty doesn't chase the proto chain.
43 // Also, do we want an NSM on regular JS objects?
44 // See: https://github.com/dart-lang/dev_compiler/issues/169
45 let result = $obj[$field];
46 return result;
47 })()''');
48
49 dput(obj, field, value) => JS('', '''(() => {
50 $field = $_canonicalFieldName($obj, $field, [$value], $field);
51 // TODO(vsm): Implement NSM and type checks.
52 // See: https://github.com/dart-lang/dev_compiler/issues/170
53 $obj[$field] = $value;
54 return $value;
55 })()''');
56
57 /// Check that a function of a given type can be applied to
58 /// actuals.
59 checkApply(type, actuals) => JS('', '''(() => {
60 if ($actuals.length < $type.args.length) return false;
61 let index = 0;
62 for(let i = 0; i < $type.args.length; ++i) {
63 if (!$instanceOfOrNull($actuals[i], $type.args[i])) return false;
64 ++index;
65 }
66 if ($actuals.length == $type.args.length) return true;
67 let extras = $actuals.length - $type.args.length;
68 if ($type.optionals.length > 0) {
69 if (extras > $type.optionals.length) return false;
70 for(let i = 0, j=index; i < extras; ++i, ++j) {
71 if (!$instanceOfOrNull($actuals[j], $type.optionals[i])) return false;
72 }
73 return true;
74 }
75 // TODO(leafp): We can't tell when someone might be calling
76 // something expecting an optional argument with named arguments
77
78 if (extras != 1) return false;
79 // An empty named list means no named arguments
80 if ($getOwnPropertyNames($type.named).length == 0) return false;
81 let opts = $actuals[index];
82 let names = $getOwnPropertyNames(opts);
83 // Type is something other than a map
84 if (names.length == 0) return false;
85 for (var name of names) {
86 if (!($hasOwnProperty.call($type.named, name))) {
87 return false;
88 }
89 if (!$instanceOfOrNull(opts[name], $type.named[name])) return false;
90 }
91 return true;
92 })()''');
93
94 throwNoSuchMethod(obj, name, args, opt_func) => JS('', '''(() => {
95 if ($obj === void 0) $obj = $opt_func;
96 ${errors.throwNoSuchMethod}($obj, $name, $args);
97 })()''');
98
99 checkAndCall(f, ftype, obj, args, name) => JS('', '''(() => {
100 if (!($f instanceof Function)) {
101 // We're not a function (and hence not a method either)
102 // Grab the `call` method if it's not a function.
103 if ($f != null) {
104 $ftype = $getMethodType($f, 'call');
105 $f = $f.call;
106 }
107 if (!($f instanceof Function)) {
108 $throwNoSuchMethod($obj, $name, $args);
109 }
110 }
111 // If f is a function, but not a method (no method type)
112 // then it should have been a function valued field, so
113 // get the type from the function.
114 if ($ftype === void 0) {
115 $ftype = $read($f);
116 }
117
118 if (!$ftype) {
119 // TODO(leafp): Allow JS objects to go through?
120 // This includes the DOM.
121 return $f.apply($obj, $args);
122 }
123
124 if ($checkApply($ftype, $args)) {
125 return $f.apply($obj, $args);
126 }
127
128 // TODO(leafp): throw a type error (rather than NSM)
129 // if the arity matches but the types are wrong.
130 $throwNoSuchMethod($obj, $name, $args, $f);
131 })()''');
132
133 dcall(f, @rest args) => JS('', '''(() => {
134 let ftype = $read($f);
135 return $checkAndCall($f, ftype, void 0, $args, 'call');
136 })()''');
137
138 /// Shared code for dsend, dindex, and dsetindex. */
139 callMethod(obj, name, args, displayName) => JS('', '''(() => {
140 let symbol = $_canonicalFieldName($obj, $name, $args, $displayName);
141 let f = $obj != null ? $obj[symbol] : null;
142 let ftype = $getMethodType($obj, $name);
143 return $checkAndCall(f, ftype, $obj, $args, $displayName);
144 })()''');
145
146 dsend(obj, method, @rest args) => JS('', '''(() => {
147 return $callMethod($obj, $method, $args, $method);
148 })()''');
149
150 dindex(obj, index) => JS('', '''(() => {
151 return $callMethod($obj, 'get', [$index], '[]');
152 })()''');
153
154 dsetindex(obj, index, value) => JS('', '''(() => {
155 $callMethod($obj, 'set', [$index, $value], '[]=');
156 return $value;
157 })()''');
158
159 _ignoreTypeFailure(actual, type) => JS('', '''(() => {
160 // TODO(vsm): Remove this hack ...
161 // This is primarily due to the lack of generic methods,
162 // but we need to triage all the types.
163 let isSubtype = $isSubtype;
164 if (isSubtype($type, $Iterable) && isSubtype($actual, $Iterable) ||
165 isSubtype($type, $Future) && isSubtype($actual, $Future) ||
166 isSubtype($type, $Map) && isSubtype($actual, $Map) ||
167 isSubtype($type, $Function) && isSubtype($actual, $Function) ||
168 isSubtype($type, $Stream) && isSubtype($actual, $Stream) ||
169 isSubtype($type, $StreamSubscription) &&
170 isSubtype($actual, $StreamSubscription)) {
171 console.warn('Ignoring cast fail from ' + $typeName($actual) +
172 ' to ' + $typeName($type));
173 return true;
174 }
175 return false;
176 })()''');
177
178 strongInstanceOf(obj, type, ignoreFromWhiteList) => JS('', '''(() => {
179 let actual = $realRuntimeType($obj);
180 if ($isSubtype(actual, $type) || actual == $jsobject) return true;
181 if ($ignoreFromWhiteList == void 0) return false;
182 if ($isGroundType($type)) return false;
183 if ($_ignoreTypeFailure(actual, $type)) return true;
184 return false;
185 })()''');
186
187 instanceOfOrNull(obj, type) => JS('', '''(() => {
188 if (($obj == null) || $strongInstanceOf($obj, $type, true)) return true;
189 return false;
190 })()''');
191
192 instanceOf(obj, type) => JS('', '''(() => {
193 if ($strongInstanceOf($obj, $type)) return true;
194 // TODO(#296): This is perhaps too eager to throw a StrongModeError?
195 // It will throw on <int>[] is List<String>.
196 // TODO(vsm): We can statically detect many cases where this
197 // check is unnecessary.
198 if ($isGroundType($type)) return false;
199 let actual = $realRuntimeType($obj);
200 ${utils.throwStrongModeError}('Strong mode is check failure: ' +
201 $typeName(actual) + ' does not soundly subtype ' +
202 $typeName($type));
203 })()''');
204
205 cast(obj, type) => JS('', '''(() => {
206 // TODO(#296): This is perhaps too eager to throw a StrongModeError?
207 // TODO(vsm): handle non-nullable types
208 if ($instanceOfOrNull($obj, $type)) return $obj;
209 let actual = $realRuntimeType($obj);
210 if ($isGroundType($type)) $throwCastError(actual, $type);
211
212 if ($_ignoreTypeFailure(actual, $type)) return $obj;
213
214 ${utils.throwStrongModeError}('Strong mode cast failure from ' +
215 $typeName(actual) + ' to ' + $typeName($type));
216 })()''');
217
218 asInt(obj) => JS('', '''(() => {
219 if ($obj == null) {
220 return null;
221 }
222 if (Math.floor($obj) != $obj) {
223 // Note: null will also be caught by this check
224 $throwCastError($realRuntimeType($obj), $int);
225 }
226 return $obj;
227 })()''');
228
229 arity(f) => JS('', '''(() => {
230 // TODO(jmesserly): need to parse optional params.
231 // In ES6, length is the number of required arguments.
232 return { min: $f.length, max: $f.length };
233 })()''');
234
235 equals(x, y) => JS('', '''(() => {
236 if ($x == null || $y == null) return $x == $y;
237 let eq = $x['=='];
238 return eq ? eq.call($x, $y) : $x === $y;
239 })()''');
240
241 /// Checks that `x` is not null or undefined. */
242 notNull(x) => JS('', '''(() => {
243 if ($x == null) $throwNullValueError();
244 return $x;
245 })()''');
246
247 ///
248 /// Creates a dart:collection LinkedHashMap.
249 ///
250 /// For a map with string keys an object literal can be used, for example
251 /// `map({'hi': 1, 'there': 2})`.
252 ///
253 /// Otherwise an array should be used, for example `map([1, 2, 3, 4])` will
254 /// create a map with keys [1, 3] and values [2, 4]. Each key-value pair
255 /// should be adjacent entries in the array.
256 ///
257 /// For a map with no keys the function can be called with no arguments, for
258 /// example `map()`.
259 ///
260 // TODO(jmesserly): this could be faster
261 map(values) => JS('', '''(() => {
262 let map = $LinkedHashMap.new();
263 if (Array.isArray($values)) {
264 for (let i = 0, end = $values.length - 1; i < end; i += 2) {
265 let key = $values[i];
266 let value = $values[i + 1];
267 map.set(key, value);
268 }
269 } else if (typeof $values === 'object') {
270 for (let key of $getOwnPropertyNames($values)) {
271 map.set(key, $values[key]);
272 }
273 }
274 return map;
275 })()''');
276
277 @JsName('assert')
278 assert_(condition) => JS('', '''(() => {
279 if (!$condition) $throwAssertionError();
280 })()''');
281
282 final _stack = JS('', 'new WeakMap()');
283 @JsName('throw')
284 throw_(obj) => JS('', '''(() => {
285 if ($obj != null && (typeof $obj == 'object' || typeof $obj == 'function')) {
286 // TODO(jmesserly): couldn't we store the most recent stack in a single
287 // variable? There should only be one active stack trace. That would
288 // allow it to work for things like strings and numbers.
289 _stack.set($obj, new Error());
290 }
291 throw $obj;
292 })()''');
293
294 getError(exception) => JS('', '''(() => {
295 var stack = _stack.get($exception);
296 return stack !== void 0 ? stack : $exception;
297 })()''');
298
299 // This is a utility function: it is only intended to be called from dev
300 // tools.
301 stackPrint(exception) => JS('', '''(() => {
302 var error = $getError($exception);
303 console.log(error.stack ? error.stack : 'No stack trace for: ' + error);
304 })()''');
305
306 stackTrace(exception) => JS('', '''(() => {
307 var error = $getError($exception);
308 return $getTraceFromException(error);
309 })()''');
310
311 ///
312 /// Implements a sequence of .? operations.
313 ///
314 /// Will call each successive callback, unless one returns null, which stops
315 /// the sequence.
316 ///
317 nullSafe(obj, @rest callbacks) => JS('', '''(() => {
318 if ($obj == null) return $obj;
319 for (const callback of $callbacks) {
320 $obj = callback($obj);
321 if ($obj == null) break;
322 }
323 return $obj;
324 })()''');
325
326 final _value = JS('', 'Symbol("_value")');
327 ///
328 /// Looks up a sequence of [keys] in [map], recursively, and
329 /// returns the result. If the value is not found, [valueFn] will be called to
330 /// add it. For example:
331 ///
332 /// let map = new Map();
333 /// putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world');
334 ///
335 /// ... will create a Map with a structure like:
336 ///
337 /// { 1: { 2: { 'hi ': { 'there ': 'world' } } } }
338 ///
339 multiKeyPutIfAbsent(map, keys, valueFn) => JS('', '''(() => {
340 for (let k of $keys) {
341 let value = $map.get(k);
342 if (!value) {
343 // TODO(jmesserly): most of these maps are very small (e.g. 1 item),
344 // so it may be worth optimizing for that.
345 $map.set(k, value = new Map());
346 }
347 $map = value;
348 }
349 if ($map.has(_value)) return $map.get(_value);
350 let value = $valueFn();
351 $map.set(_value, value);
352 return value;
353 })()''');
354
355 /// The global constant table. */
356 final constants = JS('', 'new Map()');
357
358 ///
359 /// Canonicalize a constant object.
360 ///
361 /// Preconditions:
362 /// - `obj` is an objects or array, not a primitive.
363 /// - nested values of the object are themselves already canonicalized.
364 ///
365 @JsName('const')
366 const_(obj) => JS('', '''(() => {
367 let objectKey = [$realRuntimeType($obj)];
368 // TODO(jmesserly): there's no guarantee in JS that names/symbols are
369 // returned in the same order.
370 //
371 // We could probably get the same order if we're judicious about
372 // initializing fields in a consistent order across all const constructors.
373 // Alternatively we need a way to sort them to make consistent.
374 //
375 // Right now we use the (name,value) pairs in sequence, which prevents
376 // an object with incorrect field values being returned, but won't
377 // canonicalize correctly if key order is different.
378 for (let name of $getOwnNamesAndSymbols($obj)) {
379 objectKey.push(name);
380 objectKey.push($obj[name]);
381 }
382 return $multiKeyPutIfAbsent($constants, objectKey, () => $obj);
383 })()''');
384
385
386 // The following are helpers for Object methods when the receiver
387 // may be null or primitive. These should only be generated by
388 // the compiler.
389 hashCode(obj) => JS('', '''(() => {
390 if ($obj == null) {
391 return 0;
392 }
393 // TODO(vsm): What should we do for primitives and non-Dart objects?
394 switch (typeof $obj) {
395 case "number":
396 case "boolean":
397 return $obj & 0x1FFFFFFF;
398 case "string":
399 // TODO(vsm): Call the JSString hashCode?
400 return $obj.length;
401 }
402 return $obj.hashCode;
403 })()''');
404
405 toString(obj) => JS('', '''(() => {
406 if ($obj == null) {
407 return "null";
408 }
409 return $obj.toString();
410 })()''');
411
412 noSuchMethod(obj, invocation) => JS('', '''(() => {
413 if ($obj == null) {
414 $throwNoSuchMethod($obj, $invocation.memberName,
415 $invocation.positionalArguments, $invocation.namedArguments);
416 }
417 switch (typeof $obj) {
418 case "number":
419 case "boolean":
420 case "string":
421 $throwNoSuchMethod($obj, $invocation.memberName,
422 $invocation.positionalArguments, $invocation.namedArguments);
423 }
424 return $obj.noSuchMethod($invocation);
425 })()''');
426
427 final JsIterator = JS('', '''
428 class JsIterator {
429 constructor(dartIterator) {
430 this.dartIterator = dartIterator;
431 }
432 next() {
433 let i = this.dartIterator;
434 let done = !i.moveNext();
435 return { done: done, value: done ? void 0 : i.current };
436 }
437 }
438 ''');
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698