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

Side by Side Diff: lib/runtime/_operations.js

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

Powered by Google App Engine
This is Rietveld 408576698