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

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

Powered by Google App Engine
This is Rietveld 408576698