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