Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 var dart, dartx; | 5 loader.library('dart/dart_runtime', null, /* Imports */[ |
| 6 (function (dart) { | 6 'dart/classes', |
| 7 'dart/errors', | |
| 8 'dart/operations', | |
| 9 'dart/rtti', | |
| 10 'dart/types', | |
| 11 ], /* Lazy Imports */[ | |
| 12 'dart/_js_helper' | |
| 13 ], function(exports, classes, errors, operations, rtti, types, _js_helper) { | |
| 7 'use strict'; | 14 'use strict'; |
| 8 | 15 |
| 9 const defineProperty = Object.defineProperty; | 16 function _export(value) { |
| 10 const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; | 17 if (value) return value; |
| 11 const getOwnPropertyNames = Object.getOwnPropertyNames; | 18 console.log("Re-exporting null field: " + name); |
| 12 const getOwnPropertySymbols = Object.getOwnPropertySymbols; | 19 throw "Bad export"; |
| 13 const hasOwnProperty = Object.prototype.hasOwnProperty; | |
| 14 const slice = [].slice; | |
| 15 | |
| 16 let _constructorSig = Symbol('sigCtor'); | |
| 17 let _methodSig = Symbol("sig"); | |
| 18 let _staticSig = Symbol("sigStatic"); | |
| 19 | |
| 20 function getOwnNamesAndSymbols(obj) { | |
| 21 return getOwnPropertyNames(obj).concat(getOwnPropertySymbols(obj)); | |
| 22 } | 20 } |
| 23 | 21 |
| 24 function dload(obj, field) { | 22 function exportFrom(value, names) { |
| 25 field = _canonicalFieldName(obj, field, [], field); | 23 for (let name of names) { |
| 26 if (_getMethodType(obj, field) !== void 0) { | 24 exports[name] = _export(value[name]); |
| 27 return dart.bind(obj, field); | |
| 28 } | |
| 29 // TODO(vsm): Implement NSM robustly. An 'in' check breaks on certain | |
| 30 // types. hasOwnProperty doesn't chase the proto chain. | |
| 31 // Also, do we want an NSM on regular JS objects? | |
| 32 // See: https://github.com/dart-lang/dev_compiler/issues/169 | |
| 33 let result = obj[field]; | |
| 34 | |
| 35 // TODO(vsm): Check this more robustly. | |
| 36 if (typeof result == "function" && !hasOwnProperty.call(obj, field)) { | |
| 37 // This appears to be a method tearoff. Bind this. | |
| 38 return result.bind(obj); | |
| 39 } | |
| 40 return result; | |
| 41 } | |
| 42 dart.dload = dload; | |
| 43 | |
| 44 function dput(obj, field, value) { | |
| 45 field = _canonicalFieldName(obj, field, [value], field); | |
| 46 // TODO(vsm): Implement NSM and type checks. | |
| 47 // See: https://github.com/dart-lang/dev_compiler/issues/170 | |
| 48 obj[field] = value; | |
| 49 } | |
| 50 dart.dput = dput; | |
| 51 | |
| 52 function throwRuntimeError(message) { | |
| 53 throw Error(message); | |
| 54 } | |
| 55 | |
| 56 // TODO(jmesserly): this should call noSuchMethod, not throw. | |
| 57 function throwNoSuchMethod(obj, name, args, opt_func) { | |
| 58 if (obj === void 0) obj = opt_func; | |
| 59 throw new core.NoSuchMethodError(obj, name, args); | |
| 60 } | |
| 61 | |
| 62 function checkAndCall(f, ftype, obj, args, name) { | |
| 63 if (!(f instanceof Function)) { | |
| 64 // We're not a function (and hence not a method either) | |
| 65 // Grab the `call` method if it's not a function. | |
| 66 if (f !== null) { | |
| 67 ftype = _getMethodType(f, 'call'); | |
| 68 f = f.call; | |
| 69 } | |
| 70 if (!(f instanceof Function)) { | |
| 71 throwNoSuchMethod(obj, name, args); | |
| 72 } | |
| 73 } | |
| 74 // If f is a function, but not a method (no method type) | |
| 75 // then it should have been a function valued field, so | |
| 76 // get the type from the function. | |
| 77 if (ftype === void 0) { | |
| 78 ftype = _getFunctionType(f); | |
| 79 } | |
| 80 | |
| 81 if (!ftype) { | |
| 82 // TODO(leafp): Allow JS objects to go through? | |
| 83 // This includes the DOM. | |
| 84 return f.apply(obj, args); | |
| 85 } | |
| 86 | |
| 87 if (ftype.checkApply(args)) { | |
| 88 return f.apply(obj, args); | |
| 89 } | |
| 90 | |
| 91 // TODO(leafp): throw a type error (rather than NSM) | |
| 92 // if the arity matches but the types are wrong. | |
| 93 throwNoSuchMethod(obj, name, args, f); | |
| 94 } | |
| 95 | |
| 96 function dcall(f/*, ...args*/) { | |
| 97 let args = slice.call(arguments, 1); | |
| 98 let ftype = _getFunctionType(f); | |
| 99 return checkAndCall(f, ftype, void 0, args, 'call'); | |
| 100 } | |
| 101 dart.dcall = dcall; | |
| 102 | |
| 103 let _extensionType = Symbol('extensionType'); | |
| 104 function _canonicalFieldName(obj, name, args, displayName) { | |
| 105 if (obj[_extensionType]) { | |
| 106 let extension = dartx[name]; | |
| 107 if (extension) return extension; | |
| 108 // TODO(jmesserly): in the future we might have types that "overlay" Dart | |
| 109 // methods while also exposing the full native API, e.g. dart:html vs | |
| 110 // dart:dom. To support that we'd need to fall back to the normal name | |
| 111 // if an extension method wasn't found. | |
| 112 throwNoSuchMethod(obj, displayName, args); | |
| 113 } | |
| 114 return name; | |
| 115 } | |
| 116 | |
| 117 /** Shared code for dsend, dindex, and dsetindex. */ | |
| 118 function callMethod(obj, name, args, displayName) { | |
| 119 let symbol = _canonicalFieldName(obj, name, args, displayName); | |
| 120 let f = obj[symbol]; | |
| 121 let ftype = _getMethodType(obj, name); | |
| 122 return checkAndCall(f, ftype, obj, args, displayName); | |
| 123 } | |
| 124 | |
| 125 function dsend(obj, method/*, ...args*/) { | |
| 126 return callMethod(obj, method, slice.call(arguments, 2)); | |
| 127 } | |
| 128 dart.dsend = dsend; | |
| 129 | |
| 130 function dindex(obj, index) { | |
| 131 return callMethod(obj, 'get', [index], '[]'); | |
| 132 } | |
| 133 dart.dindex = dindex; | |
| 134 | |
| 135 function dsetindex(obj, index, value) { | |
| 136 return callMethod(obj, 'set', [index, value], '[]='); | |
| 137 } | |
| 138 dart.dsetindex = dsetindex; | |
| 139 | |
| 140 function _typeName(type) { | |
| 141 if (typeof(type) == "function") { | |
| 142 let name = type.name; | |
| 143 let args = type[dart.typeArguments]; | |
| 144 if (args) { | |
| 145 name += '<'; | |
| 146 for (let i = 0; i < args.length; ++i) { | |
| 147 if (i > 0) name += ', '; | |
| 148 name += _typeName(args[i]); | |
| 149 } | |
| 150 name += '>'; | |
| 151 } | |
| 152 return name; | |
| 153 } else { | |
| 154 return type.toString(); | |
| 155 } | |
| 156 } | |
| 157 dart.typeName = _typeName; | |
| 158 | |
| 159 function _ignoreTypeFailure(actual, type) { | |
| 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 errors. | |
| 163 if (isSubtype(type, core.Iterable) && isSubtype(actual, core.Iterable) || | |
| 164 isSubtype(type, async.Future) && isSubtype(actual, async.Future) || | |
| 165 isSubtype(type, core.Map) && isSubtype(actual, core.Map) || | |
| 166 isSubtype(type, core.Function) && isSubtype(actual, core.Function)) { | |
| 167 console.error('Ignoring cast fail from ' + _typeName(actual) + | |
| 168 ' to ' + _typeName(type)); | |
| 169 return true; | |
| 170 } | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 function cast(obj, type) { | |
| 175 // TODO(vsm): handle non-nullable types | |
| 176 if (obj == null) return obj; | |
| 177 let actual = realRuntimeType(obj); | |
| 178 if (isSubtype(actual, type)) return obj; | |
| 179 if (_ignoreTypeFailure(actual, type)) return obj; | |
| 180 throw new _js_helper.CastErrorImplementation(actual, type); | |
| 181 } | |
| 182 dart.as = cast; | |
| 183 | |
| 184 | |
| 185 // TODO(vsm): How should we encode the runtime type? | |
| 186 const _runtimeType = Symbol('_runtimeType'); | |
| 187 | |
| 188 function checkPrimitiveType(obj) { | |
| 189 switch (typeof obj) { | |
| 190 case "undefined": | |
| 191 return core.Null; | |
| 192 case "number": | |
| 193 return Math.floor(obj) == obj ? core.int : core.double; | |
| 194 case "boolean": | |
| 195 return core.bool; | |
| 196 case "string": | |
| 197 return core.String; | |
| 198 case "symbol": | |
| 199 return Symbol; | |
| 200 } | |
| 201 // Undefined is handled above. For historical reasons, | |
| 202 // typeof null == "object" in JS. | |
| 203 if (obj === null) return core.Null; | |
| 204 return null; | |
| 205 } | |
| 206 | |
| 207 /** | |
| 208 * Returns the runtime type of obj. This is the same as `obj.realRuntimeType` | |
| 209 * but will not call an overridden getter. | |
| 210 * | |
| 211 * Currently this will return null for non-Dart objects. | |
| 212 */ | |
| 213 function realRuntimeType(obj) { | |
| 214 let result = checkPrimitiveType(obj); | |
| 215 if (result !== null) return result; | |
| 216 // TODO(vsm): Should we treat Dart and JS objects differently here? | |
| 217 // E.g., we can check if obj instanceof core.Object to differentiate. | |
| 218 result = obj[_runtimeType]; | |
| 219 if (result) return result; | |
| 220 result = obj.constructor; | |
| 221 if (result == Function) { | |
| 222 return getFunctionType(obj); | |
| 223 } | |
| 224 return result; | |
| 225 } | |
| 226 dart.realRuntimeType = realRuntimeType; | |
| 227 | |
| 228 function instanceOf(obj, type) { | |
| 229 return isSubtype(realRuntimeType(obj), type); | |
| 230 } | |
| 231 dart.is = instanceOf; | |
| 232 | |
| 233 function instanceOfOrNull(obj, type) { | |
| 234 // FIXME(vsm): This is used only in checkApply. | |
| 235 // Just log failures due to generics for now. | |
| 236 if ((obj == null) || instanceOf(obj, type)) return true; | |
| 237 let actual = realRuntimeType(obj); | |
| 238 if (_ignoreTypeFailure(actual, type)) return true; | |
| 239 return false; | |
| 240 } | |
| 241 | |
| 242 /** | |
| 243 * Computes the canonical type. | |
| 244 * This maps JS types onto their corresponding Dart Type. | |
| 245 */ | |
| 246 // TODO(jmesserly): lots more needs to be done here. | |
| 247 function canonicalType(t) { | |
| 248 if (t === Object) return core.Object; | |
| 249 if (t === Function) return core.Function; | |
| 250 if (t === Array) return core.List; | |
| 251 | |
| 252 // We shouldn't normally get here with these types, unless something strange | |
| 253 // happens like subclassing Number in JS and passing it to Dart. | |
| 254 if (t === String) return core.String; | |
| 255 if (t === Number) return core.double; | |
| 256 if (t === Boolean) return core.bool; | |
| 257 return t; | |
| 258 } | |
| 259 | |
| 260 const subtypeMap = new Map(); | |
| 261 function isSubtype(t1, t2) { | |
| 262 // See if we already know the answer | |
| 263 // TODO(jmesserly): general purpose memoize function? | |
| 264 let map = subtypeMap.get(t1); | |
| 265 let result; | |
| 266 if (map) { | |
| 267 result = map.get(t2); | |
| 268 if (result !== void 0) return result; | |
| 269 } else { | |
| 270 subtypeMap.set(t1, map = new Map()); | |
| 271 } | |
| 272 if (t2 == core.Type) { | |
| 273 // Special case Types. | |
| 274 result = t1.prototype instanceof core.Type || | |
| 275 t1 instanceof AbstractFunctionType || | |
| 276 isSubtype_(t1, t2); | |
| 277 } else { | |
| 278 result = isSubtype_(t1, t2) | |
| 279 } | |
| 280 map.set(t2, result); | |
| 281 return result; | |
| 282 } | |
| 283 dart.isSubtype = isSubtype; | |
| 284 | |
| 285 function _isBottom(type, dynamicIsBottom) { | |
| 286 return (type == dart.dynamic && dynamicIsBottom) || type == dart.bottom; | |
| 287 } | |
| 288 | |
| 289 function _isTop(type, dynamicIsBottom) { | |
| 290 return type == core.Object || (type == dart.dynamic && !dynamicIsBottom); | |
| 291 } | |
| 292 | |
| 293 function isSubtype_(t1, t2, opt_dynamicIsBottom) { | |
| 294 let dynamicIsBottom = | |
| 295 opt_dynamicIsBottom === void 0 ? false : opt_dynamicIsBottom; | |
| 296 | |
| 297 t1 = canonicalType(t1); | |
| 298 t2 = canonicalType(t2); | |
| 299 if (t1 == t2) return true; | |
| 300 | |
| 301 // In Dart, dynamic is effectively both top and bottom. | |
| 302 // Here, we treat dynamic as one or the other depending on context, | |
| 303 // but not both. | |
| 304 | |
| 305 // Trivially true. | |
| 306 if (_isTop(t2, dynamicIsBottom) || _isBottom(t1, dynamicIsBottom)) { | |
| 307 return true; | |
| 308 } | |
| 309 | |
| 310 // Trivially false. | |
| 311 if (_isTop(t1, dynamicIsBottom) || _isBottom(t2, dynamicIsBottom)) { | |
| 312 return false; | |
| 313 } | |
| 314 | |
| 315 // "Traditional" name-based subtype check. | |
| 316 if (isClassSubType(t1, t2)) { | |
| 317 return true; | |
| 318 } | |
| 319 | |
| 320 // Function subtyping. | |
| 321 // TODO(vsm): Handle Objects with call methods. Those are functions | |
| 322 // even if they do not *nominally* subtype core.Function. | |
| 323 if (isFunctionType(t1) && | |
| 324 isFunctionType(t2)) { | |
| 325 return isFunctionSubType(t1, t2); | |
| 326 } | |
| 327 return false; | |
| 328 } | |
| 329 | |
| 330 function safeGetOwnProperty(obj, name) { | |
| 331 let desc = getOwnPropertyDescriptor(obj, name); | |
| 332 if (desc) return desc.value; | |
| 333 } | |
| 334 | |
| 335 function isClassSubType(t1, t2) { | |
| 336 // We support Dart's covariant generics with the caveat that we do not | |
| 337 // substitute bottom for dynamic in subtyping rules. | |
| 338 // I.e., given T1, ..., Tn where at least one Ti != dynamic we disallow: | |
| 339 // - S !<: S<T1, ..., Tn> | |
| 340 // - S<dynamic, ..., dynamic> !<: S<T1, ..., Tn> | |
| 341 t1 = canonicalType(t1); | |
| 342 assert(t2 == canonicalType(t2)); | |
| 343 if (t1 == t2) return true; | |
| 344 | |
| 345 if (t1 == core.Object) return false; | |
| 346 | |
| 347 // If t1 is a JS Object, we may not hit core.Object. | |
| 348 if (t1 == null) return t2 == core.Object || t2 == dart.dynamic; | |
| 349 | |
| 350 // Check if t1 and t2 have the same raw type. If so, check covariance on | |
| 351 // type parameters. | |
| 352 let raw1 = safeGetOwnProperty(t1, dart.originalDeclaration); | |
| 353 let raw2 = safeGetOwnProperty(t2, dart.originalDeclaration); | |
| 354 if (raw1 != null && raw1 == raw2) { | |
| 355 let typeArguments1 = safeGetOwnProperty(t1, dart.typeArguments); | |
| 356 let typeArguments2 = safeGetOwnProperty(t2, dart.typeArguments); | |
| 357 let length = typeArguments1.length; | |
| 358 if (typeArguments2.length == 0) { | |
| 359 // t2 is the raw form of t1 | |
| 360 return true; | |
| 361 } else if (length == 0) { | |
| 362 // t1 is raw, but t2 is not | |
| 363 return false; | |
| 364 } | |
| 365 assert(length == typeArguments2.length); | |
| 366 for (let i = 0; i < length; ++i) { | |
| 367 if (!isSubtype(typeArguments1[i], typeArguments2[i])) { | |
| 368 return false; | |
| 369 } | |
| 370 } | |
| 371 return true; | |
| 372 } | |
| 373 | |
| 374 // Check superclass. | |
| 375 if (isClassSubType(t1.__proto__, t2)) return true; | |
| 376 | |
| 377 // Check mixins. | |
| 378 let mixins = safeGetOwnProperty(t1, dart.mixins); | |
| 379 if (mixins) { | |
| 380 for (let m1 of mixins) { | |
| 381 // TODO(jmesserly): remove the != null check once we can load core libs. | |
| 382 if (m1 != null && isClassSubType(m1, t2)) return true; | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 // Check interfaces. | |
| 387 let getInterfaces = safeGetOwnProperty(t1, dart.implements); | |
| 388 if (getInterfaces) { | |
| 389 for (let i1 of getInterfaces()) { | |
| 390 // TODO(jmesserly): remove the != null check once we can load core libs. | |
| 391 if (i1 != null && isClassSubType(i1, t2)) return true; | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 return false; | |
| 396 } | |
| 397 | |
| 398 // TODO(jmesserly): this isn't currently used, but it could be if we want | |
| 399 // `obj is NonGroundType<T,S>` to be rejected at runtime instead of compile | |
| 400 // time. | |
| 401 function isGroundType(type) { | |
| 402 // TODO(vsm): Cache this if we start using it at runtime. | |
| 403 | |
| 404 if (type instanceof AbstractFunctionType) { | |
| 405 if (!_isTop(type.returnType, false)) return false; | |
| 406 for (let i = 0; i < type.args.length; ++i) { | |
| 407 if (!_isBottom(type.args[i], true)) return false; | |
| 408 } | |
| 409 for (let i = 0; i < type.optionals.length; ++i) { | |
| 410 if (!_isBottom(type.optionals[i], true)) return false; | |
| 411 } | |
| 412 let names = getOwnPropertyNames(type.named); | |
| 413 for (let i = 0; i < names.length; ++i) { | |
| 414 if (!_isBottom(type.named[names[i]], true)) return false; | |
| 415 } | |
| 416 return true; | |
| 417 } | |
| 418 | |
| 419 let typeArgs = safeGetOwnProperty(type, dart.typeArguments); | |
| 420 if (!typeArgs) return true; | |
| 421 for (let t of typeArgs) { | |
| 422 if (t != core.Object && t != dart.dynamic) return false; | |
| 423 } | |
| 424 return true; | |
| 425 } | |
| 426 dart.isGroundType = isGroundType; | |
| 427 | |
| 428 function arity(f) { | |
| 429 // TODO(jmesserly): need to parse optional params. | |
| 430 // In ES6, length is the number of required arguments. | |
| 431 return { min: f.length, max: f.length }; | |
| 432 } | |
| 433 dart.arity = arity; | |
| 434 | |
| 435 function equals(x, y) { | |
| 436 if (x == null || y == null) return x == y; | |
| 437 let eq = x['==']; | |
| 438 return eq ? eq.call(x, y) : x === y; | |
| 439 } | |
| 440 dart.equals = equals; | |
| 441 | |
| 442 /** Checks that `x` is not null or undefined. */ | |
| 443 function notNull(x) { | |
| 444 if (x == null) throwRuntimeError('expected not-null value'); | |
| 445 return x; | |
| 446 } | |
| 447 dart.notNull = notNull; | |
| 448 | |
| 449 class AbstractFunctionType { | |
| 450 constructor() { | |
| 451 this._stringValue = null; | |
| 452 } | |
| 453 | |
| 454 /// Check that a function of this type can be applied to | |
| 455 /// actuals. | |
| 456 checkApply(actuals) { | |
| 457 if (actuals.length < this.args.length) return false; | |
| 458 let index = 0; | |
| 459 for(let i = 0; i < this.args.length; ++i) { | |
| 460 if (!instanceOfOrNull(actuals[i], this.args[i])) return false; | |
| 461 ++index; | |
| 462 } | |
| 463 if (actuals.length == this.args.length) return true; | |
| 464 let extras = actuals.length - this.args.length; | |
| 465 if (this.optionals.length > 0) { | |
| 466 if (extras > this.optionals.length) return false; | |
| 467 for(let i = 0, j=index; i < extras; ++i, ++j) { | |
| 468 if (!instanceOfOrNull(actuals[j], this.optionals[i])) return false; | |
| 469 } | |
| 470 return true; | |
| 471 } | |
| 472 // TODO(leafp): We can't tell when someone might be calling | |
| 473 // something expecting an optional argument with named arguments | |
| 474 | |
| 475 if (extras != 1) return false; | |
| 476 // An empty named list means no named arguments | |
| 477 if (getOwnPropertyNames(this.named).length == 0) return false; | |
| 478 let opts = actuals[index]; | |
| 479 let names = getOwnPropertyNames(opts); | |
| 480 // This is something other than a map | |
| 481 if (names.length == 0) return false; | |
| 482 for (name of names) { | |
| 483 if (!(Object.prototype.hasOwnProperty.call(this.named, name))) { | |
| 484 return false; | |
| 485 } | |
| 486 if (!instanceOfOrNull(opts[name], this.named[name])) return false; | |
| 487 } | |
| 488 return true; | |
| 489 } | |
| 490 | |
| 491 toString() { return this.name; } | |
| 492 | |
| 493 get name() { | |
| 494 if (this._stringValue) return this._stringValue; | |
| 495 | |
| 496 let buffer = '('; | |
| 497 for (let i = 0; i < this.args.length; ++i) { | |
| 498 if (i > 0) { | |
| 499 buffer += ', '; | |
| 500 } | |
| 501 buffer += _typeName(this.args[i]); | |
| 502 } | |
| 503 if (this.optionals.length > 0) { | |
| 504 if (this.args.length > 0) buffer += ', '; | |
| 505 buffer += '['; | |
| 506 for (let i = 0; i < this.optionals.length; ++i) { | |
| 507 if (i > 0) { | |
| 508 buffer += ', '; | |
| 509 } | |
| 510 buffer += _typeName(this.optionals[i]); | |
| 511 } | |
| 512 buffer += ']'; | |
| 513 } else if (Object.keys(this.named).length > 0) { | |
| 514 if (this.args.length > 0) buffer += ', '; | |
| 515 buffer += '{'; | |
| 516 let names = getOwnPropertyNames(this.named).sort(); | |
| 517 for (let i = 0; i < names.length; ++i) { | |
| 518 if (i > 0) { | |
| 519 buffer += ', '; | |
| 520 } | |
| 521 buffer += names[i] + ': ' + _typeName(this.named[names[i]]); | |
| 522 } | |
| 523 buffer += '}'; | |
| 524 } | |
| 525 | |
| 526 buffer += ') -> ' + _typeName(this.returnType); | |
| 527 this._stringValue = buffer; | |
| 528 return buffer; | |
| 529 } | 25 } |
| 530 } | 26 } |
| 531 | 27 |
| 532 class FunctionType extends AbstractFunctionType { | 28 exports.global = window || global; |
| 533 constructor(returnType, args, optionals, named) { | 29 exports.JsSymbol = _export(Symbol); |
| 534 super(); | |
| 535 this.returnType = returnType; | |
| 536 this.args = args; | |
| 537 this.optionals = optionals; | |
| 538 this.named = named; | |
| 539 } | |
| 540 } | |
| 541 | 30 |
| 542 /// Tag a closure with a type, using one of three forms: | 31 // TODO(vsm): This is referenced (as init.globalState) from |
| 543 /// dart.fn(cls) marks cls has having no optional or named | 32 // isolate_helper.dart. Where should it go? |
| 544 /// parameters, with all argument and return types as dynamic | 33 // See: https://github.com/dart-lang/dev_compiler/issues/164 |
| 545 /// dart.fn(cls, func) marks cls with the lazily computed | 34 exports.globalState = null; |
| 546 /// runtime type as computed by func() | 35 _js_helper.checkNum = operations.notNull; |
| 547 /// dart.fn(cls, rType, argsT, extras) marks cls as having the | |
| 548 /// runtime type dart.functionType(rType, argsT, extras) | |
| 549 function fn(closure/* ...args*/) { | |
| 550 // Closure and a lazy type constructor | |
| 551 if (arguments.length == 2) { | |
| 552 defineLazyProperty(closure, _runtimeType, {get : arguments[1]}); | |
| 553 return closure; | |
| 554 } | |
| 555 let t; | |
| 556 if (arguments.length == 1) { | |
| 557 // No type arguments, it's all dynamic | |
| 558 let len = closure.length; | |
| 559 let build = () => { | |
| 560 let args = Array.apply(null, new Array(len)).map(() => core.Object); | |
| 561 return functionType(core.Object, args); | |
| 562 }; | |
| 563 // We could be called before Object is defined. | |
| 564 if (core.Object === void 0) return fn(closure, build); | |
| 565 t = build(); | |
| 566 } else { | |
| 567 // We're passed the piecewise components of the function type, | |
| 568 // construct it. | |
| 569 let args = slice.call(arguments, 1); | |
| 570 t = functionType.apply(null, args); | |
| 571 } | |
| 572 setRuntimeType(closure, t); | |
| 573 return closure; | |
| 574 } | |
| 575 dart.fn = fn; | |
| 576 | 36 |
| 577 function functionType(returnType, args, extra) { | 37 // Re-exports |
| 578 // TODO(vsm): Cache / memomize? | |
| 579 let optionals; | |
| 580 let named; | |
| 581 if (extra === void 0) { | |
| 582 optionals = []; | |
| 583 named = {}; | |
| 584 } else if (extra instanceof Array) { | |
| 585 optionals = extra; | |
| 586 named = {}; | |
| 587 } else { | |
| 588 optionals = []; | |
| 589 named = extra; | |
| 590 } | |
| 591 return new FunctionType(returnType, args, optionals, named); | |
| 592 } | |
| 593 dart.functionType = functionType; | |
| 594 | 38 |
| 595 class Typedef extends AbstractFunctionType { | 39 // From classes |
| 596 constructor(name, closure) { | 40 exportFrom(classes, [ |
| 597 super(); | 41 'bind', |
| 598 this._name = name; | 42 'classGetConstructorType', |
| 599 this._closure = closure; | 43 'defineNamedConstructor', |
| 600 this._functionType = null; | 44 'defineExtensionNames', |
| 601 } | 45 'defineExtensionMembers', |
| 46 'generic', | |
| 47 'implements', | |
| 48 'list', | |
| 49 'metadata', | |
| 50 'mixin', | |
| 51 'registerExtension', | |
| 52 'setBaseClass', | |
| 53 'setSignature', | |
| 54 'setType', | |
| 55 'virtualField', | |
| 56 ]) | |
| 602 | 57 |
| 603 get name() { | 58 // From js_utils |
| 604 return this._name; | 59 exportFrom(js_utils, ['copyProperties']); |
| 605 } | 60 // Renames |
| 61 exports.defineLazyClass = _export(js_utils.defineLazy); | |
| 62 exports.defineLazyProperties = _export(js_utils.defineLazy); | |
| 63 exports.defineLazyClassGeneric = _export(js_utils.defineLazyProperty); | |
| 606 | 64 |
| 607 get functionType() { | 65 // From operations |
| 608 if (!this._functionType) { | 66 exportFrom(operations, [ |
| 609 this._functionType = this._closure(); | 67 'JsIterator', |
| 610 } | 68 'arity', |
| 611 return this._functionType; | 69 'assert', |
| 612 } | 70 'const', |
| 71 'dcall', | |
| 72 'dindex', | |
| 73 'dload', | |
| 74 'dput', | |
| 75 'dsend', | |
| 76 'dsetindex', | |
| 77 'equals', | |
| 78 'hashCode', | |
| 79 'map', | |
| 80 'noSuchMethod', | |
| 81 'notNull', | |
| 82 'stackTrace', | |
| 83 'throw_', | |
| 84 'toString', | |
| 85 ]) | |
| 86 // Renames | |
| 87 exports.as = _export(operations.cast); | |
| 88 exports.is = _export(operations.instanceOf); | |
| 613 | 89 |
| 614 get returnType() { | 90 // From types |
| 615 return this.functionType.returnType; | 91 exportFrom(types, [ |
| 616 } | 92 'bottom', |
| 93 'dynamic', | |
| 94 'functionType', | |
| 95 'typedef', | |
| 96 'void', | |
| 97 ]); | |
| 98 // Renames | |
| 99 exports.typeName = _export(types.typeToString); | |
|
vsm
2015/06/12 15:50:32
I think I renamed typeToString to _typeName. Cou
Leaf
2015/06/12 19:52:13
I think I re-renamed it as part of a merge. I'm h
| |
| 617 | 100 |
| 618 get args() { | 101 // From rtti |
| 619 return this.functionType.args; | 102 exportFrom(rtti, [ |
| 620 } | 103 'fn', |
| 104 'realRuntimeType', | |
| 105 'runtimeType', | |
| 106 ]); | |
| 621 | 107 |
| 622 get optionals() { | 108 }); |
| 623 return this.functionType.optionals; | |
| 624 } | |
| 625 | |
| 626 get named() { | |
| 627 return this.functionType.named; | |
| 628 } | |
| 629 } | |
| 630 | |
| 631 function typedef(name, closure) { | |
| 632 return new Typedef(name, closure); | |
| 633 } | |
| 634 dart.typedef = typedef; | |
| 635 | |
| 636 function isFunctionType(type) { | |
| 637 return isClassSubType(type, core.Function) || | |
| 638 type instanceof AbstractFunctionType; | |
| 639 } | |
| 640 | |
| 641 function getFunctionType(obj) { | |
| 642 // TODO(vsm): Encode this properly on the function for Dart-generated code. | |
| 643 let args = Array.apply(null, new Array(obj.length)).map(() => core.Object); | |
| 644 return functionType(dart.bottom, args); | |
| 645 } | |
| 646 | |
| 647 function isFunctionSubType(ft1, ft2) { | |
| 648 if (ft2 == core.Function) { | |
| 649 return true; | |
| 650 } | |
| 651 | |
| 652 let ret1 = ft1.returnType; | |
| 653 let ret2 = ft2.returnType; | |
| 654 | |
| 655 if (!isSubtype_(ret1, ret2)) { | |
| 656 // Covariant return types | |
| 657 // Note, void (which can only appear as a return type) is effectively | |
| 658 // treated as dynamic. If the base return type is void, we allow any | |
| 659 // subtype return type. | |
| 660 // E.g., we allow: | |
| 661 // () -> int <: () -> void | |
| 662 if (ret2 != dart.void) { | |
| 663 return false; | |
| 664 } | |
| 665 } | |
| 666 | |
| 667 let args1 = ft1.args; | |
| 668 let args2 = ft2.args; | |
| 669 | |
| 670 if (args1.length > args2.length) { | |
| 671 return false; | |
| 672 } | |
| 673 | |
| 674 for (let i = 0; i < args1.length; ++i) { | |
| 675 if (!isSubtype_(args2[i], args1[i], true)) { | |
| 676 return false; | |
| 677 } | |
| 678 } | |
| 679 | |
| 680 let optionals1 = ft1.optionals; | |
| 681 let optionals2 = ft2.optionals; | |
| 682 | |
| 683 if (args1.length + optionals1.length < args2.length + optionals2.length) { | |
| 684 return false; | |
| 685 } | |
| 686 | |
| 687 let j = 0; | |
| 688 for (let i = args1.length; i < args2.length; ++i, ++j) { | |
| 689 if (!isSubtype_(args2[i], optionals1[j], true)) { | |
| 690 return false; | |
| 691 } | |
| 692 } | |
| 693 | |
| 694 for (let i = 0; i < optionals2.length; ++i, ++j) { | |
| 695 if (!isSubtype_(optionals2[i], optionals1[j], true)) { | |
| 696 return false; | |
| 697 } | |
| 698 } | |
| 699 | |
| 700 let named1 = ft1.named; | |
| 701 let named2 = ft2.named; | |
| 702 | |
| 703 let names = getOwnPropertyNames(named2); | |
| 704 for (let i = 0; i < names.length; ++i) { | |
| 705 let name = names[i]; | |
| 706 let n1 = named1[name]; | |
| 707 let n2 = named2[name]; | |
| 708 if (n1 === void 0) { | |
| 709 return false; | |
| 710 } | |
| 711 if (!isSubtype_(n2, n1, true)) { | |
| 712 return false; | |
| 713 } | |
| 714 } | |
| 715 | |
| 716 return true; | |
| 717 } | |
| 718 | |
| 719 /** | |
| 720 * Defines a lazy property. | |
| 721 * After initial get or set, it will replace itself with a value property. | |
| 722 */ | |
| 723 // TODO(jmesserly): is this the best implementation for JS engines? | |
| 724 // TODO(jmesserly): reusing descriptor objects has been shown to improve | |
| 725 // performance in other projects (e.g. webcomponents.js ShadowDOM polyfill). | |
| 726 function defineLazyProperty(to, name, desc) { | |
| 727 let init = desc.get; | |
| 728 let writable = !!desc.set; | |
| 729 function lazySetter(value) { | |
| 730 defineProperty(to, name, { value: value, writable: writable }); | |
| 731 } | |
| 732 function lazyGetter() { | |
| 733 // Clear the init function to detect circular initialization. | |
| 734 let f = init; | |
| 735 if (f === null) throwRuntimeError('circular initialization for field ' + n ame); | |
| 736 init = null; | |
| 737 | |
| 738 // Compute and store the value. | |
| 739 let value = f(); | |
| 740 lazySetter(value); | |
| 741 return value; | |
| 742 } | |
| 743 desc.get = lazyGetter; | |
| 744 desc.configurable = true; | |
| 745 if (writable) desc.set = lazySetter; | |
| 746 defineProperty(to, name, desc); | |
| 747 } | |
| 748 | |
| 749 function defineLazy(to, from) { | |
| 750 for (let name of getOwnNamesAndSymbols(from)) { | |
| 751 defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); | |
| 752 } | |
| 753 } | |
| 754 // TODO(jmesserly): these are identical, but this makes it easier to grep for. | |
| 755 dart.defineLazyClass = defineLazy; | |
| 756 dart.defineLazyProperties = defineLazy; | |
| 757 dart.defineLazyClassGeneric = defineLazyProperty; | |
| 758 | |
| 759 function defineMemoizedGetter(obj, name, get) { | |
| 760 let cache = null; | |
| 761 function getter() { | |
| 762 if (cache != null) return cache; | |
| 763 cache = get(); | |
| 764 get = null; | |
| 765 return cache; | |
| 766 } | |
| 767 defineProperty(obj, name, {get: getter, configurable: true}); | |
| 768 } | |
| 769 | |
| 770 function copyPropertiesHelper(to, from, names) { | |
| 771 for (let name of names) { | |
| 772 defineProperty(to, name, getOwnPropertyDescriptor(from, name)); | |
| 773 } | |
| 774 return to; | |
| 775 } | |
| 776 | |
| 777 /** | |
| 778 * Copy properties from source to destination object. | |
| 779 * This operation is commonly called `mixin` in JS. | |
| 780 */ | |
| 781 function copyProperties(to, from) { | |
| 782 return copyPropertiesHelper(to, from, getOwnNamesAndSymbols(from)); | |
| 783 } | |
| 784 dart.copyProperties = copyProperties; | |
| 785 | |
| 786 function getExtensionSymbol(name) { | |
| 787 let sym = dartx[name]; | |
| 788 if (!sym) dartx[name] = sym = Symbol('dartx.' + name); | |
| 789 return sym; | |
| 790 } | |
| 791 | |
| 792 function defineExtensionNames(names) { | |
| 793 names.forEach(getExtensionSymbol); | |
| 794 } | |
| 795 dart.defineExtensionNames = defineExtensionNames; | |
| 796 | |
| 797 /** | |
| 798 * Copy symbols from the prototype of the source to destination. | |
| 799 * These are the only properties safe to copy onto an existing public | |
| 800 * JavaScript class. | |
| 801 */ | |
| 802 function registerExtension(jsType, dartExtType) { | |
| 803 let extProto = dartExtType.prototype; | |
| 804 let jsProto = jsType.prototype; | |
| 805 | |
| 806 // Mark the JS type's instances so we can easily check for extensions. | |
| 807 assert(jsProto[_extensionType] === void 0); | |
| 808 jsProto[_extensionType] = extProto; | |
| 809 | |
| 810 let dartObjProto = core.Object.prototype; | |
| 811 while (extProto !== dartObjProto && extProto !== jsProto) { | |
| 812 copyPropertiesHelper(jsProto, extProto, getOwnPropertySymbols(extProto)); | |
| 813 extProto = extProto.__proto__; | |
| 814 } | |
| 815 } | |
| 816 dart.registerExtension = registerExtension; | |
| 817 | |
| 818 /** | |
| 819 * Mark a concrete type as implementing extension methods. | |
| 820 * For example: `class MyIter implements Iterable`. | |
| 821 * | |
| 822 * This takes a list of names, which are the extension methods implemented. | |
| 823 * It will add a forwarder, so the extension method name redirects to the | |
| 824 * normal Dart method name. For example: | |
| 825 * | |
| 826 * defineExtensionMembers(MyType, ['add', 'remove']); | |
| 827 * | |
| 828 * Results in: | |
| 829 * | |
| 830 * MyType.prototype[dartx.add] = MyType.prototype.add; | |
| 831 * MyType.prototype[dartx.remove] = MyType.prototype.remove; | |
| 832 */ | |
| 833 // TODO(jmesserly): essentially this gives two names to the same method. | |
| 834 // This benefit is roughly equivalent call performance either way, but the | |
| 835 // cost is we need to call defineExtensionMEmbers any time a subclass override s | |
| 836 // one of these methods. | |
| 837 function defineExtensionMembers(type, methodNames) { | |
| 838 let proto = type.prototype; | |
| 839 for (let name of methodNames) { | |
| 840 let method = getOwnPropertyDescriptor(proto, name); | |
| 841 defineProperty(proto, getExtensionSymbol(name), method); | |
| 842 } | |
| 843 // Ensure the signature is available too. | |
| 844 // TODO(jmesserly): not sure if we can do this in a cleaner way. Essentially | |
| 845 // we need to copy the signature (and in the future, other data like | |
| 846 // annotations) any time we copy a method as part of our metaprogramming. | |
| 847 // It might be more friendly to JS metaprogramming if we include this info | |
| 848 // on the function. | |
| 849 let originalSigFn = getOwnPropertyDescriptor(type, _methodSig).get; | |
| 850 defineMemoizedGetter(type, _methodSig, function() { | |
| 851 let sig = originalSigFn(); | |
| 852 for (let name of methodNames) { | |
| 853 sig[getExtensionSymbol(name)] = sig[name]; | |
| 854 } | |
| 855 return sig; | |
| 856 }); | |
| 857 } | |
| 858 dart.defineExtensionMembers = defineExtensionMembers; | |
| 859 | |
| 860 function setBaseClass(derived, base) { | |
| 861 // Link the extension to the type it's extending as a base class. | |
| 862 derived.prototype.__proto__ = base.prototype; | |
| 863 } | |
| 864 dart.setBaseClass = setBaseClass; | |
| 865 | |
| 866 /** | |
| 867 * This is called whenever a derived class needs to introduce a new field, | |
| 868 * shadowing a field or getter/setter pair on its parent. | |
| 869 * | |
| 870 * This is important because otherwise, trying to read or write the field | |
| 871 * would end up calling the getter or setter, and one of those might not even | |
| 872 * exist, resulting in a runtime error. Even if they did exist, that's the | |
| 873 * wrong behavior if a new field was declared. | |
| 874 */ | |
| 875 function virtualField(subclass, fieldName) { | |
| 876 // If the field is already overridden, do nothing. | |
| 877 let prop = getOwnPropertyDescriptor(subclass.prototype, fieldName); | |
| 878 if (prop) return; | |
| 879 | |
| 880 let symbol = Symbol(subclass.name + '.' + fieldName); | |
| 881 defineProperty(subclass.prototype, fieldName, { | |
| 882 get: function() { return this[symbol]; }, | |
| 883 set: function(x) { this[symbol] = x; } | |
| 884 }); | |
| 885 } | |
| 886 dart.virtualField = virtualField; | |
| 887 | |
| 888 /** The Symbol for storing type arguments on a specialized generic type. */ | |
| 889 dart.mixins = Symbol('mixins'); | |
| 890 dart.implements = Symbol('implements'); | |
| 891 dart.metadata = Symbol('metadata'); | |
| 892 | |
| 893 /** | |
| 894 * Returns a new type that mixes members from base and all mixins. | |
| 895 * | |
| 896 * Each mixin applies in sequence, with further to the right ones overriding | |
| 897 * previous entries. | |
| 898 * | |
| 899 * For each mixin, we only take its own properties, not anything from its | |
| 900 * superclass (prototype). | |
| 901 */ | |
| 902 function mixin(base/*, ...mixins*/) { | |
| 903 // Create an initializer for the mixin, so when derived constructor calls | |
| 904 // super, we can correctly initialize base and mixins. | |
| 905 let mixins = slice.call(arguments, 1); | |
| 906 | |
| 907 // Create a class that will hold all of the mixin methods. | |
| 908 class Mixin extends base { | |
| 909 // Initializer method: run mixin initializers, then the base. | |
| 910 [base.name](/*...args*/) { | |
| 911 // Run mixin initializers. They cannot have arguments. | |
| 912 // Run them backwards so most-derived mixin is initialized first. | |
| 913 for (let i = mixins.length - 1; i >= 0; i--) { | |
| 914 let mixin = mixins[i]; | |
| 915 let init = mixin.prototype[mixin.name]; | |
| 916 if (init) init.call(this); | |
| 917 } | |
| 918 // Run base initializer. | |
| 919 let init = base.prototype[base.name]; | |
| 920 if (init) init.apply(this, arguments); | |
| 921 } | |
| 922 } | |
| 923 // Copy each mixin's methods, with later ones overwriting earlier entries. | |
| 924 for (let m of mixins) { | |
| 925 copyProperties(Mixin.prototype, m.prototype); | |
| 926 } | |
| 927 | |
| 928 // Set the signature of the Mixin class to be the composition | |
| 929 // of the signatures of the mixins. | |
| 930 dart.setSignature(Mixin, { | |
| 931 methods: () => { | |
| 932 let s = {}; | |
| 933 for (let m of mixins) { | |
| 934 copyProperties(s, m[_methodSig]); | |
| 935 } | |
| 936 return s; | |
| 937 } | |
| 938 }); | |
| 939 | |
| 940 // Save mixins for reflection | |
| 941 Mixin[dart.mixins] = mixins; | |
| 942 return Mixin; | |
| 943 } | |
| 944 dart.mixin = mixin; | |
| 945 | |
| 946 /** | |
| 947 * Creates a dart:collection LinkedHashMap. | |
| 948 * | |
| 949 * For a map with string keys an object literal can be used, for example | |
| 950 * `map({'hi': 1, 'there': 2})`. | |
| 951 * | |
| 952 * Otherwise an array should be used, for example `map([1, 2, 3, 4])` will | |
| 953 * create a map with keys [1, 3] and values [2, 4]. Each key-value pair | |
| 954 * should be adjacent entries in the array. | |
| 955 * | |
| 956 * For a map with no keys the function can be called with no arguments, for | |
| 957 * example `map()`. | |
| 958 */ | |
| 959 // TODO(jmesserly): this could be faster | |
| 960 function map(values) { | |
| 961 let map = collection.LinkedHashMap.new(); | |
| 962 if (Array.isArray(values)) { | |
| 963 for (let i = 0, end = values.length - 1; i < end; i += 2) { | |
| 964 let key = values[i]; | |
| 965 let value = values[i + 1]; | |
| 966 map.set(key, value); | |
| 967 } | |
| 968 } else if (typeof values === 'object') { | |
| 969 for (let key of getOwnPropertyNames(values)) { | |
| 970 map.set(key, values[key]); | |
| 971 } | |
| 972 } | |
| 973 return map; | |
| 974 } | |
| 975 dart.map = map; | |
| 976 | |
| 977 function assert(condition) { | |
| 978 if (!condition) throw new core.AssertionError(); | |
| 979 } | |
| 980 dart.assert = assert; | |
| 981 | |
| 982 function throw_(obj) { throw obj; } | |
| 983 dart.throw_ = throw_; | |
| 984 | |
| 985 /** | |
| 986 * Given a class and an initializer method name, creates a constructor | |
| 987 * function with the same name. For example `new SomeClass.name(args)`. | |
| 988 */ | |
| 989 function defineNamedConstructor(clazz, name) { | |
| 990 let proto = clazz.prototype; | |
| 991 let initMethod = proto[name]; | |
| 992 let ctor = function() { return initMethod.apply(this, arguments); }; | |
| 993 ctor.prototype = proto; | |
| 994 // Use defineProperty so we don't hit a property defined on Function, | |
| 995 // like `caller` and `arguments`. | |
| 996 defineProperty(clazz, name, { value: ctor, configurable: true }); | |
| 997 } | |
| 998 dart.defineNamedConstructor = defineNamedConstructor; | |
| 999 | |
| 1000 function stackTrace(exception) { | |
| 1001 return _js_helper.getTraceFromException(exception); | |
| 1002 } | |
| 1003 dart.stackTrace = stackTrace; | |
| 1004 | |
| 1005 /** The Symbol for storing type arguments on a specialized generic type. */ | |
| 1006 dart.typeArguments = Symbol('typeArguments'); | |
| 1007 dart.originalDeclaration = Symbol('originalDeclaration'); | |
| 1008 | |
| 1009 /** Memoize a generic type constructor function. */ | |
| 1010 function generic(typeConstructor) { | |
| 1011 let length = typeConstructor.length; | |
| 1012 if (length < 1) throwRuntimeError('must have at least one generic type argum ent'); | |
| 1013 | |
| 1014 let resultMap = new Map(); | |
| 1015 function makeGenericType(/*...arguments*/) { | |
| 1016 if (arguments.length != length && arguments.length != 0) { | |
| 1017 throwRuntimeError('requires ' + length + ' or 0 type arguments'); | |
| 1018 } | |
| 1019 let args = slice.call(arguments); | |
| 1020 // TODO(leafp): This should really be core.Object for | |
| 1021 // consistency, but Object is not attached to core | |
| 1022 // until the entire core library has been processed, | |
| 1023 // which is too late. | |
| 1024 while (args.length < length) args.push(dart.dynamic); | |
| 1025 | |
| 1026 let value = resultMap; | |
| 1027 for (let i = 0; i < length; i++) { | |
| 1028 let arg = args[i]; | |
| 1029 if (arg == null) { | |
| 1030 throwRuntimeError('type arguments should not be null: ' + typeConstruc tor); | |
| 1031 } | |
| 1032 let map = value; | |
| 1033 value = map.get(arg); | |
| 1034 if (value === void 0) { | |
| 1035 if (i + 1 == length) { | |
| 1036 value = typeConstructor.apply(null, args); | |
| 1037 // Save the type constructor and arguments for reflection. | |
| 1038 if (value) { | |
| 1039 value[dart.typeArguments] = args; | |
| 1040 value[dart.originalDeclaration] = makeGenericType; | |
| 1041 } | |
| 1042 } else { | |
| 1043 value = new Map(); | |
| 1044 } | |
| 1045 map.set(arg, value); | |
| 1046 } | |
| 1047 } | |
| 1048 return value; | |
| 1049 } | |
| 1050 return makeGenericType; | |
| 1051 } | |
| 1052 dart.generic = generic; | |
| 1053 | |
| 1054 /// Get the type of a function using the store runtime type | |
| 1055 function _getFunctionType(f) { | |
| 1056 return f[_runtimeType]; | |
| 1057 } | |
| 1058 | |
| 1059 /// Get the type of a method using the stored signature | |
| 1060 function _getMethodType(obj, name) { | |
| 1061 if (obj === void 0) return void 0; | |
| 1062 if (obj == null) return void 0; | |
| 1063 let sigObj = obj.__proto__.constructor[_methodSig]; | |
| 1064 if (sigObj === void 0) return void 0; | |
| 1065 let parts = sigObj[name]; | |
| 1066 if (parts === void 0) return void 0; | |
| 1067 return functionType.apply(null, parts); | |
| 1068 } | |
| 1069 | |
| 1070 /// Get the type of a constructor from a class using the stored signature | |
| 1071 /// If name is undefined, returns the type of the default constructor | |
| 1072 /// Returns undefined if the constructor is not found. | |
| 1073 function _getConstructorType(cls, name) { | |
| 1074 if(!name) name = cls.name; | |
| 1075 if (cls === void 0) return void 0; | |
| 1076 if (cls == null) return void 0; | |
| 1077 let sigCtor = cls[_constructorSig]; | |
| 1078 if (sigCtor === void 0) return void 0; | |
| 1079 let parts = sigCtor[name]; | |
| 1080 if (parts === void 0) return void 0; | |
| 1081 return functionType.apply(null, parts); | |
| 1082 } | |
| 1083 dart.classGetConstructorType = _getConstructorType; | |
| 1084 | |
| 1085 /// Given an object and a method name, tear off the method. | |
| 1086 /// Sets the runtime type of the torn off method appropriately, | |
| 1087 /// and also binds the object. | |
| 1088 /// TODO(leafp): Consider caching the tearoff on the object? | |
| 1089 function bind(obj, name) { | |
| 1090 let f = obj[name].bind(obj); | |
| 1091 let sig = _getMethodType(obj, name); | |
| 1092 assert(sig); | |
| 1093 setRuntimeType(f, sig); | |
| 1094 return f; | |
| 1095 } | |
| 1096 dart.bind = bind; | |
| 1097 | |
| 1098 // Set up the method signature field on the constructor | |
| 1099 function _setMethodSignature(f, sigF) { | |
| 1100 defineMemoizedGetter(f, _methodSig, () => { | |
| 1101 let sigObj = sigF(); | |
| 1102 sigObj.__proto__ = f.__proto__[_methodSig]; | |
| 1103 return sigObj; | |
| 1104 }); | |
| 1105 } | |
| 1106 | |
| 1107 // Set up the constructor signature field on the constructor | |
| 1108 function _setConstructorSignature(f, sigF) { | |
| 1109 defineMemoizedGetter(f, _constructorSig, sigF); | |
| 1110 } | |
| 1111 | |
| 1112 // Set up the static signature field on the constructor | |
| 1113 function _setStaticSignature(f, sigF) { | |
| 1114 defineMemoizedGetter(f, _staticSig, sigF); | |
| 1115 } | |
| 1116 | |
| 1117 // Set the lazily computed runtime type field on static methods | |
| 1118 function _setStaticTypes(f, names) { | |
| 1119 for (let name of names) { | |
| 1120 defineProperty(f[name], _runtimeType, { get: function() { | |
| 1121 let parts = f[_staticSig][name]; | |
| 1122 return functionType.apply(null, parts); | |
| 1123 }}); | |
| 1124 } | |
| 1125 } | |
| 1126 | |
| 1127 /// Set up the type signature of a class (constructor object) | |
| 1128 /// f is a constructor object | |
| 1129 /// signature is an object containing optional properties as follows: | |
| 1130 /// methods: A function returning an object mapping method names | |
| 1131 /// to method types. The function is evaluated lazily and cached. | |
| 1132 /// statics: A function returning an object mapping static method | |
| 1133 /// names to types. The function is evalutated lazily and cached. | |
| 1134 /// names: An array of the names of the static methods. Used to | |
| 1135 /// permit eagerly setting the runtimeType field on the methods | |
| 1136 /// while still lazily computing the type descriptor object. | |
| 1137 function setSignature(f, signature) { | |
| 1138 let constructors = | |
| 1139 ('constructors' in signature) ? signature.constructors : () => ({}); | |
| 1140 let methods = | |
| 1141 ('methods' in signature) ? signature.methods : () => ({}); | |
| 1142 let statics = | |
| 1143 ('statics' in signature) ? signature.statics : () => ({}); | |
| 1144 let names = | |
| 1145 ('names' in signature) ? signature.names : []; | |
| 1146 _setConstructorSignature(f, constructors); | |
| 1147 _setMethodSignature(f, methods); | |
| 1148 _setStaticSignature(f, statics); | |
| 1149 _setStaticTypes(f, names); | |
| 1150 } | |
| 1151 dart.setSignature = setSignature; | |
| 1152 | |
| 1153 let _value = Symbol('_value'); | |
| 1154 /** | |
| 1155 * Looks up a sequence of [keys] in [map], recursively, and | |
| 1156 * returns the result. If the value is not found, [valueFn] will be called to | |
| 1157 * add it. For example: | |
| 1158 * | |
| 1159 * let map = new Map(); | |
| 1160 * putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world'); | |
| 1161 * | |
| 1162 * ... will create a Map with a structure like: | |
| 1163 * | |
| 1164 * { 1: { 2: { 'hi ': { 'there ': 'world' } } } } | |
| 1165 */ | |
| 1166 function multiKeyPutIfAbsent(map, keys, valueFn) { | |
| 1167 for (let k of keys) { | |
| 1168 let value = map.get(k); | |
| 1169 if (!value) { | |
| 1170 // TODO(jmesserly): most of these maps are very small (e.g. 1 item), | |
| 1171 // so it may be worth optimizing for that. | |
| 1172 map.set(k, value = new Map()); | |
| 1173 } | |
| 1174 map = value; | |
| 1175 } | |
| 1176 if (map.has(_value)) return map.get(_value); | |
| 1177 let value = valueFn(); | |
| 1178 map.set(_value, value); | |
| 1179 return value; | |
| 1180 } | |
| 1181 | |
| 1182 /** The global constant table. */ | |
| 1183 const constants = new Map(); | |
| 1184 | |
| 1185 /** | |
| 1186 * Canonicalize a constant object. | |
| 1187 * | |
| 1188 * Preconditions: | |
| 1189 * - `obj` is an objects or array, not a primitive. | |
| 1190 * - nested values of the object are themselves already canonicalized. | |
| 1191 */ | |
| 1192 function constant(obj) { | |
| 1193 let objectKey = [realRuntimeType(obj)]; | |
| 1194 // TODO(jmesserly): there's no guarantee in JS that names/symbols are | |
| 1195 // returned in the same order. | |
| 1196 // | |
| 1197 // We could probably get the same order if we're judicious about | |
| 1198 // initializing fields in a consistent order across all const constructors. | |
| 1199 // Alternatively we need a way to sort them to make consistent. | |
| 1200 // | |
| 1201 // Right now we use the (name,value) pairs in sequence, which prevents | |
| 1202 // an object with incorrect field values being returned, but won't | |
| 1203 // canonicalize correctly if key order is different. | |
| 1204 for (let name of getOwnNamesAndSymbols(obj)) { | |
| 1205 objectKey.push(name); | |
| 1206 objectKey.push(obj[name]); | |
| 1207 } | |
| 1208 return multiKeyPutIfAbsent(constants, objectKey, () => obj); | |
| 1209 } | |
| 1210 dart.const = constant; | |
| 1211 | |
| 1212 // TODO(vsm): Rationalize these type methods. We're currently using the | |
| 1213 // setType / proto scheme for nominal types (e.g., classes) and the | |
| 1214 // setRuntimeType / field scheme for structural types (e.g., functions | |
| 1215 // - and only in tests for now). | |
| 1216 // See: https://github.com/dart-lang/dev_compiler/issues/172 | |
| 1217 | |
| 1218 /** Sets the type of `obj` to be `type` */ | |
| 1219 function setType(obj, type) { | |
| 1220 obj.__proto__ = type.prototype; | |
| 1221 return obj; | |
| 1222 } | |
| 1223 dart.setType = setType; | |
| 1224 | |
| 1225 /** Sets the element type of a list literal. */ | |
| 1226 function list(obj, elementType) { | |
| 1227 return setType(obj, _interceptors.JSArray$(elementType)); | |
| 1228 } | |
| 1229 dart.list = list; | |
| 1230 | |
| 1231 /** Sets the internal runtime type of `obj` to be `type` */ | |
| 1232 function setRuntimeType(obj, type) { | |
| 1233 obj[_runtimeType] = type; | |
| 1234 } | |
| 1235 dart.setRuntimeType = setRuntimeType; | |
| 1236 | |
| 1237 // The following are helpers for Object methods when the receiver | |
| 1238 // may be null or primitive. These should only be generated by | |
| 1239 // the compiler. | |
| 1240 function hashCode(obj) { | |
| 1241 if (obj == null) { | |
| 1242 return 0; | |
| 1243 } | |
| 1244 // TODO(vsm): What should we do for primitives and non-Dart objects? | |
| 1245 switch (typeof obj) { | |
| 1246 case "number": | |
| 1247 case "boolean": | |
| 1248 return obj & 0x1FFFFFFF; | |
| 1249 case "string": | |
| 1250 // TODO(vsm): Call the JSString hashCode? | |
| 1251 return obj.length; | |
| 1252 } | |
| 1253 return obj.hashCode; | |
| 1254 } | |
| 1255 dart.hashCode = hashCode; | |
| 1256 | |
| 1257 function runtimeType(obj) { | |
| 1258 let result = checkPrimitiveType(obj); | |
| 1259 if (result !== null) return result; | |
| 1260 return obj.runtimeType; | |
| 1261 } | |
| 1262 dart.runtimeType = runtimeType; | |
| 1263 | |
| 1264 function toString(obj) { | |
| 1265 if (obj == null) { | |
| 1266 return "null"; | |
| 1267 } | |
| 1268 return obj.toString(); | |
| 1269 } | |
| 1270 dart.toString = toString; | |
| 1271 | |
| 1272 function noSuchMethod(obj, invocation) { | |
| 1273 if (obj == null) { | |
| 1274 throw new core.NoSuchMethodError(obj, invocation.memberName, | |
| 1275 invocation.positionalArguments, invocation.namedArguments); | |
| 1276 } | |
| 1277 switch (typeof obj) { | |
| 1278 case "number": | |
| 1279 case "boolean": | |
| 1280 case "string": | |
| 1281 throw new core.NoSuchMethodError(obj, invocation.memberName, | |
| 1282 invocation.positionalArguments, invocation.namedArguments); | |
| 1283 } | |
| 1284 return obj.noSuchMethod(invocation); | |
| 1285 } | |
| 1286 dart.noSuchMethod = noSuchMethod; | |
| 1287 | |
| 1288 class JsIterator { | |
| 1289 constructor(dartIterator) { | |
| 1290 this.dartIterator = dartIterator; | |
| 1291 } | |
| 1292 next() { | |
| 1293 let i = this.dartIterator; | |
| 1294 let done = !i.moveNext(); | |
| 1295 return { done: done, value: done ? void 0 : i.current }; | |
| 1296 } | |
| 1297 } | |
| 1298 dart.JsIterator = JsIterator; | |
| 1299 | |
| 1300 // TODO(jmesserly/vsm): Right now these are sentinels. They should be type | |
| 1301 // objects of some sort, assuming we keep them at runtime. | |
| 1302 function _sentinel(_name) { | |
| 1303 return { | |
| 1304 toString() { return _name; }, | |
| 1305 get name() { return this.toString(); } | |
| 1306 }; | |
| 1307 } | |
| 1308 dart.dynamic = _sentinel('dynamic'); | |
| 1309 dart.void = _sentinel('void'); | |
| 1310 dart.bottom = _sentinel('bottom'); | |
| 1311 | |
| 1312 dart.global = window || global; | |
| 1313 dart.JsSymbol = Symbol; | |
| 1314 | |
| 1315 // Module support. This is a simplified module system for Dart. | |
| 1316 // Longer term, we can easily migrate to an existing JS module system: | |
| 1317 // ES6, AMD, RequireJS, .... | |
| 1318 | |
| 1319 class LibraryLoader { | |
| 1320 constructor(name, defaultValue, imports, lazyImports, loader) { | |
| 1321 this._name = name; | |
| 1322 this._library = defaultValue ? defaultValue : {}; | |
| 1323 this._imports = imports; | |
| 1324 this._lazyImports = lazyImports; | |
| 1325 this._loader = loader; | |
| 1326 | |
| 1327 // Cyclic import detection | |
| 1328 this._state = LibraryLoader.NOT_LOADED; | |
| 1329 } | |
| 1330 | |
| 1331 loadImports(pendingSet) { | |
| 1332 return this.handleImports(this._imports, (lib) => lib.load(pendingSet)); | |
| 1333 } | |
| 1334 | |
| 1335 deferLazyImports(pendingSet) { | |
| 1336 return this.handleImports(this._lazyImports, | |
| 1337 (lib) => { | |
| 1338 pendingSet.add(lib._name); | |
| 1339 return lib.stub(); | |
| 1340 }); | |
| 1341 } | |
| 1342 | |
| 1343 loadLazyImports(pendingSet) { | |
| 1344 return this.handleImports(pendingSet, (lib) => lib.load()); | |
| 1345 } | |
| 1346 | |
| 1347 handleImports(list, handler) { | |
| 1348 let results = []; | |
| 1349 for (let name of list) { | |
| 1350 let lib = libraries[name]; | |
| 1351 if (!lib) { | |
| 1352 throwRuntimeError('Library not available: ' + name); | |
| 1353 } | |
| 1354 results.push(handler(lib)); | |
| 1355 } | |
| 1356 return results; | |
| 1357 } | |
| 1358 | |
| 1359 load(inheritedPendingSet) { | |
| 1360 // Check for cycles | |
| 1361 if (this._state == LibraryLoader.LOADING) { | |
| 1362 throwRuntimeError('Circular dependence on library: ' + this._name); | |
| 1363 } else if (this._state >= LibraryLoader.LOADED) { | |
| 1364 return this._library; | |
| 1365 } | |
| 1366 this._state = LibraryLoader.LOADING; | |
| 1367 | |
| 1368 // Handle imports and record lazy imports | |
| 1369 let pendingSet = inheritedPendingSet ? inheritedPendingSet : new Set(); | |
| 1370 let args = this.loadImports(pendingSet); | |
| 1371 args = args.concat(this.deferLazyImports(pendingSet)); | |
| 1372 | |
| 1373 // Load the library | |
| 1374 args.unshift(this._library); | |
| 1375 this._loader.apply(null, args); | |
| 1376 this._state = LibraryLoader.LOADED; | |
| 1377 | |
| 1378 // Handle lazy imports | |
| 1379 if (inheritedPendingSet === void 0) { | |
| 1380 // Drain the queue | |
| 1381 this.loadLazyImports(pendingSet); | |
| 1382 } | |
| 1383 this._state = LibraryLoader.READY; | |
| 1384 return this._library; | |
| 1385 } | |
| 1386 | |
| 1387 stub() { | |
| 1388 return this._library; | |
| 1389 } | |
| 1390 } | |
| 1391 LibraryLoader.NOT_LOADED = 0; | |
| 1392 LibraryLoader.LOADING = 1; | |
| 1393 LibraryLoader.LOADED = 2; | |
| 1394 LibraryLoader.READY = 3; | |
| 1395 | |
| 1396 // Map from name to LibraryLoader | |
| 1397 let libraries = new Map(); | |
| 1398 | |
| 1399 function library(name, defaultValue, imports, lazyImports, loader) { | |
| 1400 return libraries[name] = | |
| 1401 new LibraryLoader(name, defaultValue, imports, lazyImports, loader); | |
| 1402 } | |
| 1403 dart.library = library; | |
| 1404 | |
| 1405 function import_(libraryName) { | |
| 1406 bootstrap(); | |
| 1407 let loader = libraries[libraryName]; | |
| 1408 return loader.load(); | |
| 1409 } | |
| 1410 dart.import = import_; | |
| 1411 | |
| 1412 function start(libraryName) { | |
| 1413 let library = import_(libraryName); | |
| 1414 _isolate_helper.startRootIsolate(library.main, []); | |
| 1415 } | |
| 1416 dart.start = start; | |
| 1417 | |
| 1418 // Libraries used in this file. | |
| 1419 let core; | |
| 1420 let collection; | |
| 1421 let async; | |
| 1422 let _interceptors; | |
| 1423 let _isolate_helper; | |
| 1424 let _js_helper; | |
| 1425 let _js_primitives; | |
| 1426 | |
| 1427 let _bootstrapped = false; | |
| 1428 function bootstrap() { | |
| 1429 if (_bootstrapped) return; | |
| 1430 _bootstrapped = true; | |
| 1431 | |
| 1432 // Setup stubs for top-level symbols. | |
| 1433 let lazyImport = (name) => libraries[name].stub(); | |
| 1434 core = lazyImport('dart/core'); | |
| 1435 collection = lazyImport('dart/collection'); | |
| 1436 async = lazyImport('dart/async'); | |
| 1437 _interceptors = lazyImport('dart/_interceptors'); | |
| 1438 _isolate_helper = lazyImport('dart/_isolate_helper'); | |
| 1439 _js_helper = lazyImport('dart/_js_helper'); | |
| 1440 _js_helper.checkNum = notNull; | |
| 1441 _js_primitives = lazyImport('dart/_js_primitives'); | |
| 1442 _js_primitives.printString = (s) => console.log(s); | |
| 1443 | |
| 1444 // Create namespace for dart extension members. | |
| 1445 dartx = dartx || {}; | |
| 1446 | |
| 1447 // Force import of core. | |
| 1448 import_('dart/core'); | |
| 1449 | |
| 1450 // TODO(vsm): DOM facades? | |
| 1451 // See: https://github.com/dart-lang/dev_compiler/issues/173 | |
| 1452 NodeList.prototype.get = function(i) { return this[i]; }; | |
| 1453 NamedNodeMap.prototype.get = function(i) { return this[i]; }; | |
| 1454 DOMTokenList.prototype.get = function(i) { return this[i]; }; | |
| 1455 HTMLCollection.prototype.get = function(i) { return this[i]; }; | |
| 1456 | |
| 1457 // TODO(vsm): This is referenced (as init.globalState) from | |
| 1458 // isolate_helper.dart. Where should it go? | |
| 1459 // See: https://github.com/dart-lang/dev_compiler/issues/164 | |
| 1460 dart.globalState = null; | |
| 1461 } | |
| 1462 })(dart || (dart = {})); | |
| OLD | NEW |