| 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 /// This library defines runtime operations on objects used by the code | 5 /// This library defines runtime operations on objects used by the code |
| 6 /// generator. | 6 /// generator. |
| 7 part of dart._runtime; | 7 part of dart._runtime; |
| 8 | 8 |
| 9 class InvocationImpl extends Invocation { | 9 class InvocationImpl extends Invocation { |
| 10 final Symbol memberName; | 10 final Symbol memberName; |
| 11 final List positionalArguments; | 11 final List positionalArguments; |
| 12 final Map<Symbol, dynamic> namedArguments; | 12 final Map<Symbol, dynamic> namedArguments; |
| 13 final List<Type> typeArguments; |
| 13 final bool isMethod; | 14 final bool isMethod; |
| 14 final bool isGetter; | 15 final bool isGetter; |
| 15 final bool isSetter; | 16 final bool isSetter; |
| 16 | 17 |
| 17 InvocationImpl(memberName, this.positionalArguments, | 18 InvocationImpl(memberName, this.positionalArguments, |
| 18 {namedArguments, | 19 {namedArguments, |
| 20 List typeArguments, |
| 19 this.isMethod: false, | 21 this.isMethod: false, |
| 20 this.isGetter: false, | 22 this.isGetter: false, |
| 21 this.isSetter: false}) | 23 this.isSetter: false}) |
| 22 : memberName = _dartSymbol(memberName), | 24 : memberName = |
| 23 namedArguments = _namedArgsToSymbols(namedArguments); | 25 isSetter ? _setterSymbol(memberName) : _dartSymbol(memberName), |
| 26 namedArguments = _namedArgsToSymbols(namedArguments), |
| 27 typeArguments = typeArguments == null |
| 28 ? const [] |
| 29 : typeArguments.map(wrapType).toList(); |
| 24 | 30 |
| 25 static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { | 31 static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { |
| 26 if (namedArgs == null) return {}; | 32 if (namedArgs == null) return {}; |
| 27 return new Map.fromIterable(getOwnPropertyNames(namedArgs), | 33 return new Map.fromIterable(getOwnPropertyNames(namedArgs), |
| 28 key: _dartSymbol, value: (k) => JS('', '#[#]', namedArgs, k)); | 34 key: _dartSymbol, value: (k) => JS('', '#[#]', namedArgs, k)); |
| 29 } | 35 } |
| 30 } | 36 } |
| 31 | 37 |
| 32 /// Given an object and a method name, tear off the method. | 38 /// Given an object and a method name, tear off the method. |
| 33 /// Sets the runtime type of the torn off method appropriately, | 39 /// Sets the runtime type of the torn off method appropriately, |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 if (isJsInterop(obj)) { | 173 if (isJsInterop(obj)) { |
| 168 return JS('', '#[#] = #', obj, f, value); | 174 return JS('', '#[#] = #', obj, f, value); |
| 169 } | 175 } |
| 170 } | 176 } |
| 171 return noSuchMethod( | 177 return noSuchMethod( |
| 172 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); | 178 obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); |
| 173 } | 179 } |
| 174 | 180 |
| 175 /// Check that a function of a given type can be applied to | 181 /// Check that a function of a given type can be applied to |
| 176 /// actuals. | 182 /// actuals. |
| 177 _checkApply(type, actuals) => JS( | 183 _checkApply(type, actuals) => JS('', '''(() => { |
| 178 '', | |
| 179 '''(() => { | |
| 180 // TODO(vsm): Remove when we no longer need mirrors metadata. | 184 // TODO(vsm): Remove when we no longer need mirrors metadata. |
| 181 // An array is used to encode annotations attached to the type. | 185 // An array is used to encode annotations attached to the type. |
| 182 if ($type instanceof Array) { | 186 if ($type instanceof Array) { |
| 183 $type = type[0]; | 187 $type = type[0]; |
| 184 } | 188 } |
| 185 if ($actuals.length < $type.args.length) return false; | 189 if ($actuals.length < $type.args.length) return false; |
| 186 let index = 0; | 190 let index = 0; |
| 187 for(let i = 0; i < $type.args.length; ++i) { | 191 for(let i = 0; i < $type.args.length; ++i) { |
| 188 $check($actuals[i], $type.args[i]); | 192 $check($actuals[i], $type.args[i]); |
| 189 ++index; | 193 ++index; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 209 if (names.length == 0) return false; | 213 if (names.length == 0) return false; |
| 210 for (var name of names) { | 214 for (var name of names) { |
| 211 if (!($hasOwnProperty.call($type.named, name))) { | 215 if (!($hasOwnProperty.call($type.named, name))) { |
| 212 return false; | 216 return false; |
| 213 } | 217 } |
| 214 $check(opts[name], $type.named[name]); | 218 $check(opts[name], $type.named[name]); |
| 215 } | 219 } |
| 216 return true; | 220 return true; |
| 217 })()'''); | 221 })()'''); |
| 218 | 222 |
| 219 _toSymbolName(symbol) => JS( | 223 _toSymbolName(symbol) => JS('', '''(() => { |
| 220 '', | |
| 221 '''(() => { | |
| 222 let str = $symbol.toString(); | 224 let str = $symbol.toString(); |
| 223 // Strip leading 'Symbol(' and trailing ')' | 225 // Strip leading 'Symbol(' and trailing ')' |
| 224 return str.substring(7, str.length-1); | 226 return str.substring(7, str.length-1); |
| 225 })()'''); | 227 })()'''); |
| 226 | 228 |
| 227 _toDisplayName(name) => JS( | 229 _toDisplayName(name) => JS('', '''(() => { |
| 228 '', | |
| 229 '''(() => { | |
| 230 // Names starting with _ are escaped names used to disambiguate Dart and | 230 // Names starting with _ are escaped names used to disambiguate Dart and |
| 231 // JS names. | 231 // JS names. |
| 232 if ($name[0] === '_') { | 232 if ($name[0] === '_') { |
| 233 // Inverse of | 233 // Inverse of |
| 234 switch($name) { | 234 switch($name) { |
| 235 case '_get': | 235 case '_get': |
| 236 return '[]'; | 236 return '[]'; |
| 237 case '_set': | 237 case '_set': |
| 238 return '[]='; | 238 return '[]='; |
| 239 case '_negate': | 239 case '_negate': |
| 240 return 'unary-'; | 240 return 'unary-'; |
| 241 case '_constructor': | 241 case '_constructor': |
| 242 case '_prototype': | 242 case '_prototype': |
| 243 return $name.substring(1); | 243 return $name.substring(1); |
| 244 } | 244 } |
| 245 } | 245 } |
| 246 return $name; | 246 return $name; |
| 247 })()'''); | 247 })()'''); |
| 248 | 248 |
| 249 Symbol _dartSymbol(name) { | 249 Symbol _dartSymbol(name) { |
| 250 return (JS('bool', 'typeof # === "symbol"', name)) | 250 return (JS('bool', 'typeof # === "symbol"', name)) |
| 251 ? JS('Symbol', '#(new #.new(#, #))', const_, _internal.PrivateSymbol, | 251 ? JS('Symbol', '#(new #.new(#, #))', const_, _internal.PrivateSymbol, |
| 252 _toSymbolName(name), name) | 252 _toSymbolName(name), name) |
| 253 : JS('Symbol', '#(#.new(#))', const_, Symbol, _toDisplayName(name)); | 253 : JS('Symbol', '#(#.new(#))', const_, Symbol, _toDisplayName(name)); |
| 254 } | 254 } |
| 255 | 255 |
| 256 Symbol _setterSymbol(name) { |
| 257 return (JS('bool', 'typeof # === "symbol"', name)) |
| 258 ? JS('Symbol', '#(new #.new(# + "=", #))', const_, |
| 259 _internal.PrivateSymbol, _toSymbolName(name), name) |
| 260 : JS('Symbol', '#(#.new(# + "="))', const_, Symbol, _toDisplayName(name)); |
| 261 } |
| 262 |
| 256 /// Extracts the named argument array from a list of arguments, and returns it. | 263 /// Extracts the named argument array from a list of arguments, and returns it. |
| 257 // TODO(jmesserly): we need to handle named arguments better. | 264 // TODO(jmesserly): we need to handle named arguments better. |
| 258 extractNamedArgs(args) { | 265 extractNamedArgs(args) { |
| 259 if (JS('bool', '#.length > 0', args)) { | 266 if (JS('bool', '#.length > 0', args)) { |
| 260 var last = JS('', '#[#.length - 1]', args, args); | 267 var last = JS('', '#[#.length - 1]', args, args); |
| 261 if (JS( | 268 if (JS( |
| 262 'bool', '# != null && #.__proto__ === Object.prototype', last, last)) { | 269 'bool', '# != null && #.__proto__ === Object.prototype', last, last)) { |
| 263 return JS('', '#.pop()', args); | 270 return JS('', '#.pop()', args); |
| 264 } | 271 } |
| 265 } | 272 } |
| 266 return null; | 273 return null; |
| 267 } | 274 } |
| 268 | 275 |
| 269 _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS( | 276 _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => { |
| 270 '', | |
| 271 '''(() => { | |
| 272 $_trackCall($obj); | 277 $_trackCall($obj); |
| 273 | 278 |
| 274 let originalTarget = obj === void 0 ? f : obj; | 279 let originalTarget = obj === void 0 ? f : obj; |
| 275 | 280 |
| 276 function callNSM() { | 281 function callNSM() { |
| 277 return $noSuchMethod(originalTarget, new $InvocationImpl.new( | 282 return $noSuchMethod(originalTarget, new $InvocationImpl.new( |
| 278 $name, $args, | 283 $name, $args, { |
| 279 {namedArguments: $extractNamedArgs($args), isMethod: true})); | 284 namedArguments: $extractNamedArgs($args), |
| 285 typeArguments: $typeArgs, |
| 286 isMethod: true |
| 287 })); |
| 280 } | 288 } |
| 281 if (!($f instanceof Function)) { | 289 if (!($f instanceof Function)) { |
| 282 // We're not a function (and hence not a method either) | 290 // We're not a function (and hence not a method either) |
| 283 // Grab the `call` method if it's not a function. | 291 // Grab the `call` method if it's not a function. |
| 284 if ($f != null) { | 292 if ($f != null) { |
| 285 $ftype = $getMethodType($getType($f), 'call'); | 293 $ftype = $getMethodType($getType($f), 'call'); |
| 286 $f = f.call ? $bind($f, 'call') : void 0; | 294 $f = f.call ? $bind($f, 'call') : void 0; |
| 287 } | 295 } |
| 288 if (!($f instanceof Function)) { | 296 if (!($f instanceof Function)) { |
| 289 return callNSM(); | 297 return callNSM(); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 })()'''); | 350 })()'''); |
| 343 | 351 |
| 344 dcall(f, @rest args) => | 352 dcall(f, @rest args) => |
| 345 _checkAndCall(f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); | 353 _checkAndCall(f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); |
| 346 | 354 |
| 347 dgcall(f, typeArgs, @rest args) => _checkAndCall( | 355 dgcall(f, typeArgs, @rest args) => _checkAndCall( |
| 348 f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); | 356 f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); |
| 349 | 357 |
| 350 /// Helper for REPL dynamic invocation variants that make a best effort to | 358 /// Helper for REPL dynamic invocation variants that make a best effort to |
| 351 /// enable accessing private members across library boundaries. | 359 /// enable accessing private members across library boundaries. |
| 352 _dhelperRepl(object, field, callback) => JS( | 360 _dhelperRepl(object, field, callback) => JS('', '''(() => { |
| 353 '', | |
| 354 '''(() => { | |
| 355 let rawField = $field; | 361 let rawField = $field; |
| 356 if (typeof(field) == 'symbol') { | 362 if (typeof(field) == 'symbol') { |
| 357 // test if the specified field exists in which case it is safe to use it. | 363 // test if the specified field exists in which case it is safe to use it. |
| 358 if ($field in $object) return $callback($field); | 364 if ($field in $object) return $callback($field); |
| 359 | 365 |
| 360 // Symbol is from a different library. Make a best effort to | 366 // Symbol is from a different library. Make a best effort to |
| 361 $field = $field.toString(); | 367 $field = $field.toString(); |
| 362 $field = $field.substring('Symbol('.length, field.length - 1); | 368 $field = $field.substring('Symbol('.length, field.length - 1); |
| 363 | 369 |
| 364 } else if ($field.charAt(0) != '_') { | 370 } else if ($field.charAt(0) != '_') { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 424 | 430 |
| 425 dindex(obj, index) => | 431 dindex(obj, index) => |
| 426 _callMethod(obj, '_get', null, JS('', '[#]', index), '[]'); | 432 _callMethod(obj, '_get', null, JS('', '[#]', index), '[]'); |
| 427 | 433 |
| 428 dsetindex(obj, index, value) => | 434 dsetindex(obj, index, value) => |
| 429 _callMethod(obj, '_set', null, JS('', '[#, #]', index, value), '[]='); | 435 _callMethod(obj, '_set', null, JS('', '[#, #]', index, value), '[]='); |
| 430 | 436 |
| 431 /// TODO(leafp): This duplicates code in types.dart. | 437 /// TODO(leafp): This duplicates code in types.dart. |
| 432 /// I haven't found a way to factor it out that makes the | 438 /// I haven't found a way to factor it out that makes the |
| 433 /// code generator happy though. | 439 /// code generator happy though. |
| 434 _ignoreMemo(f) => JS( | 440 _ignoreMemo(f) => JS('', '''(() => { |
| 435 '', | |
| 436 '''(() => { | |
| 437 let memo = new Map(); | 441 let memo = new Map(); |
| 438 return (t1, t2) => { | 442 return (t1, t2) => { |
| 439 let map = memo.get(t1); | 443 let map = memo.get(t1); |
| 440 let result; | 444 let result; |
| 441 if (map) { | 445 if (map) { |
| 442 result = map.get(t2); | 446 result = map.get(t2); |
| 443 if (result !== void 0) return result; | 447 if (result !== void 0) return result; |
| 444 } else { | 448 } else { |
| 445 memo.set(t1, map = new Map()); | 449 memo.set(t1, map = new Map()); |
| 446 } | 450 } |
| 447 result = $f(t1, t2); | 451 result = $f(t1, t2); |
| 448 map.set(t2, result); | 452 map.set(t2, result); |
| 449 return result; | 453 return result; |
| 450 }; | 454 }; |
| 451 })()'''); | 455 })()'''); |
| 452 | 456 |
| 453 final _ignoreTypeFailure = JS( | 457 final _ignoreTypeFailure = JS('', '''(() => { |
| 454 '', | |
| 455 '''(() => { | |
| 456 return $_ignoreMemo((actual, type) => { | 458 return $_ignoreMemo((actual, type) => { |
| 457 // TODO(vsm): Remove this hack ... | 459 // TODO(vsm): Remove this hack ... |
| 458 // This is primarily due to the lack of generic methods, | 460 // This is primarily due to the lack of generic methods, |
| 459 // but we need to triage all the types. | 461 // but we need to triage all the types. |
| 460 if ($_isFutureOr(type)) { | 462 if ($_isFutureOr(type)) { |
| 461 // Ignore if we would ignore either side of union. | 463 // Ignore if we would ignore either side of union. |
| 462 let typeArg = $getGenericArgs(type)[0]; | 464 let typeArg = $getGenericArgs(type)[0]; |
| 463 let typeFuture = ${getGenericClass(Future)}(typeArg); | 465 let typeFuture = ${getGenericClass(Future)}(typeArg); |
| 464 return $_ignoreTypeFailure(actual, typeFuture) || | 466 return $_ignoreTypeFailure(actual, typeFuture) || |
| 465 $_ignoreTypeFailure(actual, typeArg); | 467 $_ignoreTypeFailure(actual, typeArg); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 479 return false; | 481 return false; |
| 480 }); | 482 }); |
| 481 })()'''); | 483 })()'''); |
| 482 | 484 |
| 483 /// Returns true if [obj] is an instance of [type] | 485 /// Returns true if [obj] is an instance of [type] |
| 484 /// Returns true if [obj] is a JS function and [type] is a function type | 486 /// Returns true if [obj] is a JS function and [type] is a function type |
| 485 /// Returns false if [obj] is not an instance of [type] in both spec | 487 /// Returns false if [obj] is not an instance of [type] in both spec |
| 486 /// and strong mode | 488 /// and strong mode |
| 487 /// Returns null if [obj] is not an instance of [type] in strong mode | 489 /// Returns null if [obj] is not an instance of [type] in strong mode |
| 488 /// but might be in spec mode | 490 /// but might be in spec mode |
| 489 bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS( | 491 bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS('', '''(() => { |
| 490 '', | |
| 491 '''(() => { | |
| 492 let actual = $getReifiedType($obj); | 492 let actual = $getReifiedType($obj); |
| 493 let result = $isSubtype(actual, $type); | 493 let result = $isSubtype(actual, $type); |
| 494 if (result || (actual == $int && $isSubtype($double, $type))) return true; | 494 if (result || (actual == $int && $isSubtype($double, $type))) return true; |
| 495 if (actual == $jsobject && $_isFunctionType(type) && | 495 if (actual == $jsobject && $_isFunctionType(type) && |
| 496 typeof(obj) === 'function') { | 496 typeof(obj) === 'function') { |
| 497 return true; | 497 return true; |
| 498 } | 498 } |
| 499 if (result === false) return false; | 499 if (result === false) return false; |
| 500 if (!dart.__ignoreWhitelistedErrors || | 500 if (!dart.__ignoreWhitelistedErrors || |
| 501 ($ignoreFromWhiteList == void 0)) { | 501 ($ignoreFromWhiteList == void 0)) { |
| 502 return result; | 502 return result; |
| 503 } | 503 } |
| 504 if ($_ignoreTypeFailure(actual, $type)) return true; | 504 if ($_ignoreTypeFailure(actual, $type)) return true; |
| 505 return result; | 505 return result; |
| 506 })()'''); | 506 })()'''); |
| 507 | 507 |
| 508 /// Returns true if [obj] is null or an instance of [type] | 508 /// Returns true if [obj] is null or an instance of [type] |
| 509 /// Returns false if [obj] is non-null and not an instance of [type] | 509 /// Returns false if [obj] is non-null and not an instance of [type] |
| 510 /// in strong mode | 510 /// in strong mode |
| 511 instanceOfOrNull(obj, type) => JS( | 511 instanceOfOrNull(obj, type) => JS('', '''(() => { |
| 512 '', | |
| 513 '''(() => { | |
| 514 // If strongInstanceOf returns null, convert to false here. | 512 // If strongInstanceOf returns null, convert to false here. |
| 515 if (($obj == null) || $strongInstanceOf($obj, $type, true)) return true; | 513 if (($obj == null) || $strongInstanceOf($obj, $type, true)) return true; |
| 516 return false; | 514 return false; |
| 517 })()'''); | 515 })()'''); |
| 518 | 516 |
| 519 @JSExportName('is') | 517 @JSExportName('is') |
| 520 bool instanceOf(obj, type) => JS( | 518 bool instanceOf(obj, type) => JS('', '''(() => { |
| 521 '', | |
| 522 '''(() => { | |
| 523 if ($obj == null) { | 519 if ($obj == null) { |
| 524 return $type == $Null || $_isTop($type); | 520 return $type == $Null || $_isTop($type); |
| 525 } | 521 } |
| 526 let result = $strongInstanceOf($obj, $type); | 522 let result = $strongInstanceOf($obj, $type); |
| 527 if (result !== null) return result; | 523 if (result !== null) return result; |
| 528 if (!dart.__failForWeakModeIsChecks) return false; | 524 if (!dart.__failForWeakModeIsChecks) return false; |
| 529 let actual = $getReifiedType($obj); | 525 let actual = $getReifiedType($obj); |
| 530 let message = 'Strong mode is-check failure: ' + | 526 let message = 'Strong mode is-check failure: ' + |
| 531 $typeName(actual) + ' does not soundly subtype ' + | 527 $typeName(actual) + ' does not soundly subtype ' + |
| 532 $typeName($type); | 528 $typeName($type); |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 605 | 601 |
| 606 if (JS('bool', 'Math.floor(#) != #', obj, obj)) { | 602 if (JS('bool', 'Math.floor(#) != #', obj, obj)) { |
| 607 throwCastError(obj, getReifiedType(obj), JS('', '#', int)); | 603 throwCastError(obj, getReifiedType(obj), JS('', '#', int)); |
| 608 } | 604 } |
| 609 return obj; | 605 return obj; |
| 610 } | 606 } |
| 611 | 607 |
| 612 /// Adds type type test predicates to a constructor for a non-parameterized | 608 /// Adds type type test predicates to a constructor for a non-parameterized |
| 613 /// type. Non-parameterized types can use `instanceof` for subclass checks and | 609 /// type. Non-parameterized types can use `instanceof` for subclass checks and |
| 614 /// fall through to a helper for subtype tests. | 610 /// fall through to a helper for subtype tests. |
| 615 addSimpleTypeTests(ctor) => JS( | 611 addSimpleTypeTests(ctor) => JS('', '''(() => { |
| 616 '', | |
| 617 '''(() => { | |
| 618 $ctor.is = function is_C(object) { | 612 $ctor.is = function is_C(object) { |
| 619 // This is incorrect for classes [Null] and [Object], so we do not use | 613 // This is incorrect for classes [Null] and [Object], so we do not use |
| 620 // [addSimpleTypeTests] for these classes. | 614 // [addSimpleTypeTests] for these classes. |
| 621 if (object instanceof this) return true; | 615 if (object instanceof this) return true; |
| 622 return dart.is(object, this); | 616 return dart.is(object, this); |
| 623 }; | 617 }; |
| 624 $ctor.as = function as_C(object) { | 618 $ctor.as = function as_C(object) { |
| 625 if (object instanceof this) return object; | 619 if (object instanceof this) return object; |
| 626 return dart.as(object, this); | 620 return dart.as(object, this); |
| 627 }; | 621 }; |
| 628 $ctor._check = function check_C(object) { | 622 $ctor._check = function check_C(object) { |
| 629 if (object instanceof this) return object; | 623 if (object instanceof this) return object; |
| 630 return dart.check(object, this); | 624 return dart.check(object, this); |
| 631 }; | 625 }; |
| 632 })()'''); | 626 })()'''); |
| 633 | 627 |
| 634 /// Adds type type test predicates to a constructor. Used for parmeterized | 628 /// Adds type type test predicates to a constructor. Used for parmeterized |
| 635 /// types. We avoid `instanceof` for, e.g. `x is ListQueue` since there is | 629 /// types. We avoid `instanceof` for, e.g. `x is ListQueue` since there is |
| 636 /// no common class for `ListQueue<int>` and `ListQueue<String>`. | 630 /// no common class for `ListQueue<int>` and `ListQueue<String>`. |
| 637 addTypeTests(ctor) => JS( | 631 addTypeTests(ctor) => JS('', '''(() => { |
| 638 '', | |
| 639 '''(() => { | |
| 640 $ctor.as = function as_G(object) { | 632 $ctor.as = function as_G(object) { |
| 641 return dart.as(object, this); | 633 return dart.as(object, this); |
| 642 }; | 634 }; |
| 643 $ctor.is = function is_G(object) { | 635 $ctor.is = function is_G(object) { |
| 644 return dart.is(object, this); | 636 return dart.is(object, this); |
| 645 }; | 637 }; |
| 646 $ctor._check = function check_G(object) { | 638 $ctor._check = function check_G(object) { |
| 647 return dart.check(object, this); | 639 return dart.check(object, this); |
| 648 }; | 640 }; |
| 649 })()'''); | 641 })()'''); |
| 650 | 642 |
| 651 // TODO(vsm): Consider optimizing this. We may be able to statically | 643 // TODO(vsm): Consider optimizing this. We may be able to statically |
| 652 // determine which == operation to invoke given the static types. | 644 // determine which == operation to invoke given the static types. |
| 653 equals(x, y) => JS( | 645 equals(x, y) => JS('', '''(() => { |
| 654 '', | |
| 655 '''(() => { | |
| 656 if ($x == null || $y == null) return $x == $y; | 646 if ($x == null || $y == null) return $x == $y; |
| 657 let eq = $x[dartx['==']] || $x['==']; | 647 let eq = $x[dartx['==']] || $x['==']; |
| 658 return eq ? eq.call($x, $y) : $x === $y; | 648 return eq ? eq.call($x, $y) : $x === $y; |
| 659 })()'''); | 649 })()'''); |
| 660 | 650 |
| 661 /// Checks that `x` is not null or undefined. */ | 651 /// Checks that `x` is not null or undefined. */ |
| 662 notNull(x) { | 652 notNull(x) { |
| 663 if (x == null) throwNullValueError(); | 653 if (x == null) throwNullValueError(); |
| 664 return x; | 654 return x; |
| 665 } | 655 } |
| 666 | 656 |
| 667 /// | 657 /// |
| 668 /// Creates a dart:collection LinkedHashMap. | 658 /// Creates a dart:collection LinkedHashMap. |
| 669 /// | 659 /// |
| 670 /// For a map with string keys an object literal can be used, for example | 660 /// For a map with string keys an object literal can be used, for example |
| 671 /// `map({'hi': 1, 'there': 2})`. | 661 /// `map({'hi': 1, 'there': 2})`. |
| 672 /// | 662 /// |
| 673 /// Otherwise an array should be used, for example `map([1, 2, 3, 4])` will | 663 /// Otherwise an array should be used, for example `map([1, 2, 3, 4])` will |
| 674 /// create a map with keys [1, 3] and values [2, 4]. Each key-value pair | 664 /// create a map with keys [1, 3] and values [2, 4]. Each key-value pair |
| 675 /// should be adjacent entries in the array. | 665 /// should be adjacent entries in the array. |
| 676 /// | 666 /// |
| 677 /// For a map with no keys the function can be called with no arguments, for | 667 /// For a map with no keys the function can be called with no arguments, for |
| 678 /// example `map()`. | 668 /// example `map()`. |
| 679 /// | 669 /// |
| 680 // TODO(jmesserly): this could be faster | 670 // TODO(jmesserly): this could be faster |
| 681 // TODO(jmesserly): we can use default values `= dynamic` once #417 is fixed. | 671 // TODO(jmesserly): we can use default values `= dynamic` once #417 is fixed. |
| 682 // TODO(jmesserly): move this to classes for consistency with list literals? | 672 // TODO(jmesserly): move this to classes for consistency with list literals? |
| 683 map(values, [K, V]) => JS( | 673 map(values, [K, V]) => JS('', '''(() => { |
| 684 '', | |
| 685 '''(() => { | |
| 686 if ($K == null) $K = $dynamic; | 674 if ($K == null) $K = $dynamic; |
| 687 if ($V == null) $V = $dynamic; | 675 if ($V == null) $V = $dynamic; |
| 688 let map = ${getGenericClass(LinkedHashMap)}($K, $V).new(); | 676 let map = ${getGenericClass(LinkedHashMap)}($K, $V).new(); |
| 689 if (Array.isArray($values)) { | 677 if (Array.isArray($values)) { |
| 690 for (let i = 0, end = $values.length - 1; i < end; i += 2) { | 678 for (let i = 0, end = $values.length - 1; i < end; i += 2) { |
| 691 let key = $values[i]; | 679 let key = $values[i]; |
| 692 let value = $values[i + 1]; | 680 let value = $values[i + 1]; |
| 693 map._set(key, value); | 681 map._set(key, value); |
| 694 } | 682 } |
| 695 } else if (typeof $values === 'object') { | 683 } else if (typeof $values === 'object') { |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 788 /// returns the result. If the value is not found, [valueFn] will be called to | 776 /// returns the result. If the value is not found, [valueFn] will be called to |
| 789 /// add it. For example: | 777 /// add it. For example: |
| 790 /// | 778 /// |
| 791 /// let map = new Map(); | 779 /// let map = new Map(); |
| 792 /// putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world'); | 780 /// putIfAbsent(map, [1, 2, 'hi ', 'there '], () => 'world'); |
| 793 /// | 781 /// |
| 794 /// ... will create a Map with a structure like: | 782 /// ... will create a Map with a structure like: |
| 795 /// | 783 /// |
| 796 /// { 1: { 2: { 'hi ': { 'there ': 'world' } } } } | 784 /// { 1: { 2: { 'hi ': { 'there ': 'world' } } } } |
| 797 /// | 785 /// |
| 798 multiKeyPutIfAbsent(map, keys, valueFn) => JS( | 786 multiKeyPutIfAbsent(map, keys, valueFn) => JS('', '''(() => { |
| 799 '', | |
| 800 '''(() => { | |
| 801 for (let k of $keys) { | 787 for (let k of $keys) { |
| 802 let value = $map.get(k); | 788 let value = $map.get(k); |
| 803 if (!value) { | 789 if (!value) { |
| 804 // TODO(jmesserly): most of these maps are very small (e.g. 1 item), | 790 // TODO(jmesserly): most of these maps are very small (e.g. 1 item), |
| 805 // so it may be worth optimizing for that. | 791 // so it may be worth optimizing for that. |
| 806 $map.set(k, value = new Map()); | 792 $map.set(k, value = new Map()); |
| 807 } | 793 } |
| 808 $map = value; | 794 $map = value; |
| 809 } | 795 } |
| 810 if ($map.has($_value)) return $map.get($_value); | 796 if ($map.has($_value)) return $map.get($_value); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 822 final constants = JS('', 'new Map()'); | 808 final constants = JS('', 'new Map()'); |
| 823 | 809 |
| 824 /// | 810 /// |
| 825 /// Canonicalize a constant object. | 811 /// Canonicalize a constant object. |
| 826 /// | 812 /// |
| 827 /// Preconditions: | 813 /// Preconditions: |
| 828 /// - `obj` is an objects or array, not a primitive. | 814 /// - `obj` is an objects or array, not a primitive. |
| 829 /// - nested values of the object are themselves already canonicalized. | 815 /// - nested values of the object are themselves already canonicalized. |
| 830 /// | 816 /// |
| 831 @JSExportName('const') | 817 @JSExportName('const') |
| 832 const_(obj) => JS( | 818 const_(obj) => JS('', '''(() => { |
| 833 '', | |
| 834 '''(() => { | |
| 835 // TODO(leafp): This table gets quite large in apps. | 819 // TODO(leafp): This table gets quite large in apps. |
| 836 // Keeping the paths is probably expensive. It would probably | 820 // Keeping the paths is probably expensive. It would probably |
| 837 // be more space efficient to just use a direct hash table with | 821 // be more space efficient to just use a direct hash table with |
| 838 // an appropriately defined structural equality function. | 822 // an appropriately defined structural equality function. |
| 839 function lookupNonTerminal(map, key) { | 823 function lookupNonTerminal(map, key) { |
| 840 let result = map.get(key); | 824 let result = map.get(key); |
| 841 if (result !== void 0) return result; | 825 if (result !== void 0) return result; |
| 842 map.set(key, result = new Map()); | 826 map.set(key, result = new Map()); |
| 843 return result; | 827 return result; |
| 844 }; | 828 }; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 871 return $obj; | 855 return $obj; |
| 872 })()'''); | 856 })()'''); |
| 873 | 857 |
| 874 /// The global constant list table. | 858 /// The global constant list table. |
| 875 /// This maps the number of elements in the list (n) | 859 /// This maps the number of elements in the list (n) |
| 876 /// to a path of length n of maps indexed by the value | 860 /// to a path of length n of maps indexed by the value |
| 877 /// of the field. The final map is indexed by the element | 861 /// of the field. The final map is indexed by the element |
| 878 /// type and contains the canonical version of the list. | 862 /// type and contains the canonical version of the list. |
| 879 final constantLists = JS('', 'new Map()'); | 863 final constantLists = JS('', 'new Map()'); |
| 880 | 864 |
| 881 /// | |
| 882 /// Canonicalize a constant list | 865 /// Canonicalize a constant list |
| 883 /// | 866 constList(elements, elementType) => JS('', '''(() => { |
| 884 constList(elements, elementType) => JS( | |
| 885 '', | |
| 886 '''(() => { | |
| 887 function lookupNonTerminal(map, key) { | 867 function lookupNonTerminal(map, key) { |
| 888 let result = map.get(key); | 868 let result = map.get(key); |
| 889 if (result !== void 0) return result; | 869 if (result !== void 0) return result; |
| 890 map.set(key, result = new Map()); | 870 map.set(key, result = new Map()); |
| 891 return result; | 871 return result; |
| 892 }; | 872 }; |
| 893 let count = $elements.length; | 873 let count = $elements.length; |
| 894 let map = lookupNonTerminal($constantLists, count); | 874 let map = lookupNonTerminal($constantLists, count); |
| 895 for (let i = 0; i < count; i++) { | 875 for (let i = 0; i < count; i++) { |
| 896 map = lookupNonTerminal(map, elements[i]); | 876 map = lookupNonTerminal(map, elements[i]); |
| 897 } | 877 } |
| 898 let value = map.get($elementType); | 878 let value = map.get($elementType); |
| 899 if (value) return value; | 879 if (value) return value; |
| 900 value = $list($elements, $elementType); | 880 |
| 881 value = $setType($elements, ${getGenericClass(JSArray)}($elementType)); |
| 901 map.set($elementType, value); | 882 map.set($elementType, value); |
| 902 return value; | 883 return value; |
| 903 })()'''); | 884 })()'''); |
| 904 | 885 |
| 905 // The following are helpers for Object methods when the receiver | 886 // The following are helpers for Object methods when the receiver |
| 906 // may be null or primitive. These should only be generated by | 887 // may be null or primitive. These should only be generated by |
| 907 // the compiler. | 888 // the compiler. |
| 908 hashCode(obj) { | 889 hashCode(obj) { |
| 909 if (obj == null) return 0; | 890 if (obj == null) return 0; |
| 910 | 891 |
| 911 switch (JS('String', 'typeof #', obj)) { | 892 switch (JS('String', 'typeof #', obj)) { |
| 912 case "number": | 893 case "number": |
| 913 return JS('', '# & 0x1FFFFFFF', obj); | 894 return JS('', '# & 0x1FFFFFFF', obj); |
| 914 case "boolean": | 895 case "boolean": |
| 915 // From JSBool.hashCode, see comment there. | 896 // From JSBool.hashCode, see comment there. |
| 916 return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); | 897 return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); |
| 917 case "function": | 898 case "function": |
| 918 var hashFn = JS('', '#[#]', obj, _tearoffHashcode); | 899 if (JS('bool', '# instanceof Function', obj)) { |
| 919 if (hashFn != null) return JS('int', '#()', hashFn); | 900 var hashFn = JS('', '#[#]', obj, _tearoffHashcode); |
| 920 return Primitives.objectHashCode(obj); | 901 if (hashFn != null) return JS('int', '#()', hashFn); |
| 902 return Primitives.objectHashCode(obj); |
| 903 } |
| 921 } | 904 } |
| 922 | 905 |
| 923 var extension = getExtensionType(obj); | 906 var extension = getExtensionType(obj); |
| 924 if (extension != null) { | 907 if (extension != null) { |
| 925 return JS('', '#[dartx.hashCode]', obj); | 908 return JS('', '#[dartx.hashCode]', obj); |
| 926 } | 909 } |
| 927 return JS('', '#.hashCode', obj); | 910 return JS('', '#.hashCode', obj); |
| 928 } | 911 } |
| 929 | 912 |
| 930 @JSExportName('toString') | 913 @JSExportName('toString') |
| 931 String _toString(obj) { | 914 String _toString(obj) { |
| 932 if (obj == null) return "null"; | 915 if (obj == null) return "null"; |
| 933 | 916 |
| 934 var extension = getExtensionType(obj); | 917 var extension = getExtensionType(obj); |
| 935 if (extension != null) { | 918 if (extension != null) { |
| 936 return JS('String', '#[dartx.toString]()', obj); | 919 return JS('String', '#[dartx.toString]()', obj); |
| 937 } | 920 } |
| 938 if (JS('bool', 'typeof # == "function"', obj)) { | 921 if (JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
| 939 // If the function is a Type object, we should just display the type name. | 922 // If the function is a Type object, we should just display the type name. |
| 940 // Regular Dart code should typically get wrapped type objects instead of | 923 // Regular Dart code should typically get wrapped type objects instead of |
| 941 // raw type (aka JS constructor) objects however raw type objects can be | 924 // raw type (aka JS constructor) objects however raw type objects can be |
| 942 // exposed to Dart code via JS interop or debugging tools. | 925 // exposed to Dart code via JS interop or debugging tools. |
| 943 if (isType(obj)) return typeName(obj); | 926 if (isType(obj)) return typeName(obj); |
| 944 | 927 |
| 945 return JS( | 928 return JS( |
| 946 'String', r'"Closure: " + # + " from: " + #', getReifiedType(obj), obj); | 929 'String', r'"Closure: " + # + " from: " + #', getReifiedType(obj), obj); |
| 947 } | 930 } |
| 948 // TODO(jmesserly): restore this faster path once ES Symbol is treated as | 931 // TODO(jmesserly): restore this faster path once ES Symbol is treated as |
| 949 // an extension type (and thus hits the above code path). | 932 // an extension type (and thus hits the above code path). |
| 950 // See https://github.com/dart-lang/sdk/issues/28323 | 933 // See https://github.com/dart-lang/sdk/issues/28323 |
| 951 // return JS('', '"" + #', obj); | 934 // return JS('', '"" + #', obj); |
| 952 return JS('String', '#.toString()', obj); | 935 return JS('String', '#.toString()', obj); |
| 953 } | 936 } |
| 954 | 937 |
| 955 // TODO(jmesserly): is the argument type verified statically? | 938 // TODO(jmesserly): is the argument type verified statically? |
| 956 noSuchMethod(obj, Invocation invocation) { | 939 noSuchMethod(obj, Invocation invocation) { |
| 957 if (obj == null || JS('bool', 'typeof # == "function"', obj)) { | 940 if (obj == null || |
| 941 JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
| 958 throwNoSuchMethodError(obj, invocation.memberName, | 942 throwNoSuchMethodError(obj, invocation.memberName, |
| 959 invocation.positionalArguments, invocation.namedArguments); | 943 invocation.positionalArguments, invocation.namedArguments); |
| 960 } | 944 } |
| 961 // Delegate to the (possibly user-defined) method on the object. | 945 // Delegate to the (possibly user-defined) method on the object. |
| 962 var extension = getExtensionType(obj); | 946 var extension = getExtensionType(obj); |
| 963 if (extension != null) { | 947 if (extension != null) { |
| 964 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); | 948 return JS('', '#[dartx.noSuchMethod](#)', obj, invocation); |
| 965 } | 949 } |
| 966 return JS('', '#.noSuchMethod(#)', obj, invocation); | 950 return JS('', '#.noSuchMethod(#)', obj, invocation); |
| 967 } | 951 } |
| 968 | 952 |
| 969 constFn(x) => JS('', '() => x'); | 953 constFn(x) => JS('', '() => x'); |
| 970 | 954 |
| 971 runtimeType(obj) { | 955 runtimeType(obj) { |
| 972 // Handle primitives where the method isn't on the object. | 956 // Handle primitives where the method isn't on the object. |
| 973 var result = _checkPrimitiveType(obj); | 957 var result = _checkPrimitiveType(obj); |
| 974 if (result != null) return wrapType(result); | 958 if (result != null) return wrapType(result); |
| 975 | 959 |
| 976 // Delegate to the (possibly user-defined) method on the object. | 960 // Delegate to the (possibly user-defined) method on the object. |
| 977 var extension = getExtensionType(obj); | 961 var extension = getExtensionType(obj); |
| 978 if (extension != null) { | 962 if (extension != null) { |
| 979 result = JS('', '#[dartx.runtimeType]', obj); | 963 result = JS('', '#[dartx.runtimeType]', obj); |
| 980 // If extension doesn't override runtimeType, return the extension type. | 964 // If extension doesn't override runtimeType, return the extension type. |
| 981 return result ?? wrapType(extension); | 965 return result ?? wrapType(extension); |
| 982 } | 966 } |
| 983 if (JS('bool', 'typeof # == "function"', obj)) { | 967 if (JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
| 984 return wrapType(getReifiedType(obj)); | 968 return wrapType(getReifiedType(obj)); |
| 985 } | 969 } |
| 986 return JS('', '#.runtimeType', obj); | 970 return JS('', '#.runtimeType', obj); |
| 987 } | 971 } |
| 988 | 972 |
| 989 /// Implements Dart's interpolated strings as ES2015 tagged template literals. | 973 /// Implements Dart's interpolated strings as ES2015 tagged template literals. |
| 990 /// | 974 /// |
| 991 /// For example: dart.str`hello ${name}` | 975 /// For example: dart.str`hello ${name}` |
| 992 String str(strings, @rest values) => JS( | 976 String str(strings, @rest values) => JS('', '''(() => { |
| 993 '', | |
| 994 '''(() => { | |
| 995 let s = $strings[0]; | 977 let s = $strings[0]; |
| 996 for (let i = 0, len = $values.length; i < len; ) { | 978 for (let i = 0, len = $values.length; i < len; ) { |
| 997 s += $notNull($_toString($values[i])) + $strings[++i]; | 979 s += $notNull($_toString($values[i])) + $strings[++i]; |
| 998 } | 980 } |
| 999 return s; | 981 return s; |
| 1000 })()'''); | 982 })()'''); |
| 1001 | 983 |
| 1002 final JsIterator = JS( | 984 final JsIterator = JS('', ''' |
| 1003 '', | |
| 1004 ''' | |
| 1005 class JsIterator { | 985 class JsIterator { |
| 1006 constructor(dartIterator) { | 986 constructor(dartIterator) { |
| 1007 this.dartIterator = dartIterator; | 987 this.dartIterator = dartIterator; |
| 1008 } | 988 } |
| 1009 next() { | 989 next() { |
| 1010 let i = this.dartIterator; | 990 let i = this.dartIterator; |
| 1011 let done = !i.moveNext(); | 991 let done = !i.moveNext(); |
| 1012 return { done: done, value: done ? void 0 : i.current }; | 992 return { done: done, value: done ? void 0 : i.current }; |
| 1013 } | 993 } |
| 1014 } | 994 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1034 /// Libraries are not actually deferred in DDC, so this just returns a future | 1014 /// Libraries are not actually deferred in DDC, so this just returns a future |
| 1035 /// that completes immediately. | 1015 /// that completes immediately. |
| 1036 Future loadLibrary() => new Future.value(); | 1016 Future loadLibrary() => new Future.value(); |
| 1037 | 1017 |
| 1038 /// Defines lazy statics. | 1018 /// Defines lazy statics. |
| 1039 void defineLazy(to, from) { | 1019 void defineLazy(to, from) { |
| 1040 for (var name in getOwnNamesAndSymbols(from)) { | 1020 for (var name in getOwnNamesAndSymbols(from)) { |
| 1041 defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); | 1021 defineLazyProperty(to, name, getOwnPropertyDescriptor(from, name)); |
| 1042 } | 1022 } |
| 1043 } | 1023 } |
| OLD | NEW |