Index: pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart |
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart |
index d8014d99ed6feb7339dd88c153a6a3d89553b003..880d0db1adcd1316285ab24d7a8407404a9c9d44 100644 |
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart |
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart |
@@ -10,17 +10,23 @@ class InvocationImpl extends Invocation { |
final Symbol memberName; |
final List positionalArguments; |
final Map<Symbol, dynamic> namedArguments; |
+ final List<Type> typeArguments; |
final bool isMethod; |
final bool isGetter; |
final bool isSetter; |
InvocationImpl(memberName, this.positionalArguments, |
{namedArguments, |
+ List typeArguments, |
this.isMethod: false, |
this.isGetter: false, |
this.isSetter: false}) |
- : memberName = _dartSymbol(memberName), |
- namedArguments = _namedArgsToSymbols(namedArguments); |
+ : memberName = |
+ isSetter ? _setterSymbol(memberName) : _dartSymbol(memberName), |
+ namedArguments = _namedArgsToSymbols(namedArguments), |
+ typeArguments = typeArguments == null |
+ ? const [] |
+ : typeArguments.map(wrapType).toList(); |
static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { |
if (namedArgs == null) return {}; |
@@ -174,9 +180,7 @@ dput(obj, field, value) { |
/// Check that a function of a given type can be applied to |
/// actuals. |
-_checkApply(type, actuals) => JS( |
- '', |
- '''(() => { |
+_checkApply(type, actuals) => JS('', '''(() => { |
// TODO(vsm): Remove when we no longer need mirrors metadata. |
// An array is used to encode annotations attached to the type. |
if ($type instanceof Array) { |
@@ -216,17 +220,13 @@ _checkApply(type, actuals) => JS( |
return true; |
})()'''); |
-_toSymbolName(symbol) => JS( |
- '', |
- '''(() => { |
+_toSymbolName(symbol) => JS('', '''(() => { |
let str = $symbol.toString(); |
// Strip leading 'Symbol(' and trailing ')' |
return str.substring(7, str.length-1); |
})()'''); |
-_toDisplayName(name) => JS( |
- '', |
- '''(() => { |
+_toDisplayName(name) => JS('', '''(() => { |
// Names starting with _ are escaped names used to disambiguate Dart and |
// JS names. |
if ($name[0] === '_') { |
@@ -253,6 +253,13 @@ Symbol _dartSymbol(name) { |
: JS('Symbol', '#(#.new(#))', const_, Symbol, _toDisplayName(name)); |
} |
+Symbol _setterSymbol(name) { |
+ return (JS('bool', 'typeof # === "symbol"', name)) |
+ ? JS('Symbol', '#(new #.new(# + "=", #))', const_, |
+ _internal.PrivateSymbol, _toSymbolName(name), name) |
+ : JS('Symbol', '#(#.new(# + "="))', const_, Symbol, _toDisplayName(name)); |
+} |
+ |
/// Extracts the named argument array from a list of arguments, and returns it. |
// TODO(jmesserly): we need to handle named arguments better. |
extractNamedArgs(args) { |
@@ -266,17 +273,18 @@ extractNamedArgs(args) { |
return null; |
} |
-_checkAndCall(f, ftype, obj, typeArgs, args, name) => JS( |
- '', |
- '''(() => { |
+_checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => { |
$_trackCall($obj); |
let originalTarget = obj === void 0 ? f : obj; |
function callNSM() { |
return $noSuchMethod(originalTarget, new $InvocationImpl.new( |
- $name, $args, |
- {namedArguments: $extractNamedArgs($args), isMethod: true})); |
+ $name, $args, { |
+ namedArguments: $extractNamedArgs($args), |
+ typeArguments: $typeArgs, |
+ isMethod: true |
+ })); |
} |
if (!($f instanceof Function)) { |
// We're not a function (and hence not a method either) |
@@ -349,9 +357,7 @@ dgcall(f, typeArgs, @rest args) => _checkAndCall( |
/// Helper for REPL dynamic invocation variants that make a best effort to |
/// enable accessing private members across library boundaries. |
-_dhelperRepl(object, field, callback) => JS( |
- '', |
- '''(() => { |
+_dhelperRepl(object, field, callback) => JS('', '''(() => { |
let rawField = $field; |
if (typeof(field) == 'symbol') { |
// test if the specified field exists in which case it is safe to use it. |
@@ -431,9 +437,7 @@ dsetindex(obj, index, value) => |
/// TODO(leafp): This duplicates code in types.dart. |
/// I haven't found a way to factor it out that makes the |
/// code generator happy though. |
-_ignoreMemo(f) => JS( |
- '', |
- '''(() => { |
+_ignoreMemo(f) => JS('', '''(() => { |
let memo = new Map(); |
return (t1, t2) => { |
let map = memo.get(t1); |
@@ -450,9 +454,7 @@ _ignoreMemo(f) => JS( |
}; |
})()'''); |
-final _ignoreTypeFailure = JS( |
- '', |
- '''(() => { |
+final _ignoreTypeFailure = JS('', '''(() => { |
return $_ignoreMemo((actual, type) => { |
// TODO(vsm): Remove this hack ... |
// This is primarily due to the lack of generic methods, |
@@ -486,9 +488,7 @@ final _ignoreTypeFailure = JS( |
/// and strong mode |
/// Returns null if [obj] is not an instance of [type] in strong mode |
/// but might be in spec mode |
-bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS( |
- '', |
- '''(() => { |
+bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS('', '''(() => { |
let actual = $getReifiedType($obj); |
let result = $isSubtype(actual, $type); |
if (result || (actual == $int && $isSubtype($double, $type))) return true; |
@@ -508,18 +508,14 @@ bool strongInstanceOf(obj, type, ignoreFromWhiteList) => JS( |
/// Returns true if [obj] is null or an instance of [type] |
/// Returns false if [obj] is non-null and not an instance of [type] |
/// in strong mode |
-instanceOfOrNull(obj, type) => JS( |
- '', |
- '''(() => { |
+instanceOfOrNull(obj, type) => JS('', '''(() => { |
// If strongInstanceOf returns null, convert to false here. |
if (($obj == null) || $strongInstanceOf($obj, $type, true)) return true; |
return false; |
})()'''); |
@JSExportName('is') |
-bool instanceOf(obj, type) => JS( |
- '', |
- '''(() => { |
+bool instanceOf(obj, type) => JS('', '''(() => { |
if ($obj == null) { |
return $type == $Null || $_isTop($type); |
} |
@@ -612,9 +608,7 @@ asInt(obj) { |
/// Adds type type test predicates to a constructor for a non-parameterized |
/// type. Non-parameterized types can use `instanceof` for subclass checks and |
/// fall through to a helper for subtype tests. |
-addSimpleTypeTests(ctor) => JS( |
- '', |
- '''(() => { |
+addSimpleTypeTests(ctor) => JS('', '''(() => { |
$ctor.is = function is_C(object) { |
// This is incorrect for classes [Null] and [Object], so we do not use |
// [addSimpleTypeTests] for these classes. |
@@ -634,9 +628,7 @@ addSimpleTypeTests(ctor) => JS( |
/// Adds type type test predicates to a constructor. Used for parmeterized |
/// types. We avoid `instanceof` for, e.g. `x is ListQueue` since there is |
/// no common class for `ListQueue<int>` and `ListQueue<String>`. |
-addTypeTests(ctor) => JS( |
- '', |
- '''(() => { |
+addTypeTests(ctor) => JS('', '''(() => { |
$ctor.as = function as_G(object) { |
return dart.as(object, this); |
}; |
@@ -650,9 +642,7 @@ addTypeTests(ctor) => JS( |
// TODO(vsm): Consider optimizing this. We may be able to statically |
// determine which == operation to invoke given the static types. |
-equals(x, y) => JS( |
- '', |
- '''(() => { |
+equals(x, y) => JS('', '''(() => { |
if ($x == null || $y == null) return $x == $y; |
let eq = $x[dartx['==']] || $x['==']; |
return eq ? eq.call($x, $y) : $x === $y; |
@@ -680,9 +670,7 @@ notNull(x) { |
// TODO(jmesserly): this could be faster |
// TODO(jmesserly): we can use default values `= dynamic` once #417 is fixed. |
// TODO(jmesserly): move this to classes for consistency with list literals? |
-map(values, [K, V]) => JS( |
- '', |
- '''(() => { |
+map(values, [K, V]) => JS('', '''(() => { |
if ($K == null) $K = $dynamic; |
if ($V == null) $V = $dynamic; |
let map = ${getGenericClass(LinkedHashMap)}($K, $V).new(); |
@@ -795,9 +783,7 @@ final _value = JS('', 'Symbol("_value")'); |
/// |
/// { 1: { 2: { 'hi ': { 'there ': 'world' } } } } |
/// |
-multiKeyPutIfAbsent(map, keys, valueFn) => JS( |
- '', |
- '''(() => { |
+multiKeyPutIfAbsent(map, keys, valueFn) => JS('', '''(() => { |
for (let k of $keys) { |
let value = $map.get(k); |
if (!value) { |
@@ -829,9 +815,7 @@ final constants = JS('', 'new Map()'); |
/// - nested values of the object are themselves already canonicalized. |
/// |
@JSExportName('const') |
-const_(obj) => JS( |
- '', |
- '''(() => { |
+const_(obj) => JS('', '''(() => { |
// TODO(leafp): This table gets quite large in apps. |
// Keeping the paths is probably expensive. It would probably |
// be more space efficient to just use a direct hash table with |
@@ -878,12 +862,8 @@ const_(obj) => JS( |
/// type and contains the canonical version of the list. |
final constantLists = JS('', 'new Map()'); |
-/// |
/// Canonicalize a constant list |
-/// |
-constList(elements, elementType) => JS( |
- '', |
- '''(() => { |
+constList(elements, elementType) => JS('', '''(() => { |
function lookupNonTerminal(map, key) { |
let result = map.get(key); |
if (result !== void 0) return result; |
@@ -897,7 +877,8 @@ constList(elements, elementType) => JS( |
} |
let value = map.get($elementType); |
if (value) return value; |
- value = $list($elements, $elementType); |
+ |
+ value = $setType($elements, ${getGenericClass(JSArray)}($elementType)); |
map.set($elementType, value); |
return value; |
})()'''); |
@@ -915,9 +896,11 @@ hashCode(obj) { |
// From JSBool.hashCode, see comment there. |
return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); |
case "function": |
- var hashFn = JS('', '#[#]', obj, _tearoffHashcode); |
- if (hashFn != null) return JS('int', '#()', hashFn); |
- return Primitives.objectHashCode(obj); |
+ if (JS('bool', '# instanceof Function', obj)) { |
+ var hashFn = JS('', '#[#]', obj, _tearoffHashcode); |
+ if (hashFn != null) return JS('int', '#()', hashFn); |
+ return Primitives.objectHashCode(obj); |
+ } |
} |
var extension = getExtensionType(obj); |
@@ -935,7 +918,7 @@ String _toString(obj) { |
if (extension != null) { |
return JS('String', '#[dartx.toString]()', obj); |
} |
- if (JS('bool', 'typeof # == "function"', obj)) { |
+ if (JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
// If the function is a Type object, we should just display the type name. |
// Regular Dart code should typically get wrapped type objects instead of |
// raw type (aka JS constructor) objects however raw type objects can be |
@@ -954,7 +937,8 @@ String _toString(obj) { |
// TODO(jmesserly): is the argument type verified statically? |
noSuchMethod(obj, Invocation invocation) { |
- if (obj == null || JS('bool', 'typeof # == "function"', obj)) { |
+ if (obj == null || |
+ JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
throwNoSuchMethodError(obj, invocation.memberName, |
invocation.positionalArguments, invocation.namedArguments); |
} |
@@ -980,7 +964,7 @@ runtimeType(obj) { |
// If extension doesn't override runtimeType, return the extension type. |
return result ?? wrapType(extension); |
} |
- if (JS('bool', 'typeof # == "function"', obj)) { |
+ if (JS('bool', 'typeof # == "function" && # instanceof Function', obj, obj)) { |
return wrapType(getReifiedType(obj)); |
} |
return JS('', '#.runtimeType', obj); |
@@ -989,9 +973,7 @@ runtimeType(obj) { |
/// Implements Dart's interpolated strings as ES2015 tagged template literals. |
/// |
/// For example: dart.str`hello ${name}` |
-String str(strings, @rest values) => JS( |
- '', |
- '''(() => { |
+String str(strings, @rest values) => JS('', '''(() => { |
let s = $strings[0]; |
for (let i = 0, len = $values.length; i < len; ) { |
s += $notNull($_toString($values[i])) + $strings[++i]; |
@@ -999,9 +981,7 @@ String str(strings, @rest values) => JS( |
return s; |
})()'''); |
-final JsIterator = JS( |
- '', |
- ''' |
+final JsIterator = JS('', ''' |
class JsIterator { |
constructor(dartIterator) { |
this.dartIterator = dartIterator; |