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