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 913860a3b4aa8b5bf870d628fc1a5a298cbdd95d..ba0dbc662b8cca7f558a38849e0b79aa130d8eeb 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 |
@@ -19,15 +19,13 @@ class InvocationImpl extends Invocation { |
this.isMethod: false, |
this.isGetter: false, |
this.isSetter: false}) |
- : memberName = _dartSymbol(memberName), |
- namedArguments = _namedArgsToSymbols(namedArguments); |
+ : memberName = _dartSymbol(memberName), |
+ namedArguments = _namedArgsToSymbols(namedArguments); |
static Map<Symbol, dynamic> _namedArgsToSymbols(namedArgs) { |
if (namedArgs == null) return {}; |
- return new Map.fromIterable( |
- getOwnPropertyNames(namedArgs), |
- key: _dartSymbol, |
- value: (k) => JS('', '#[#]', namedArgs, k)); |
+ return new Map.fromIterable(getOwnPropertyNames(namedArgs), |
+ key: _dartSymbol, value: (k) => JS('', '#[#]', namedArgs, k)); |
} |
} |
@@ -38,8 +36,8 @@ dload(obj, field) { |
if (hasMethod(obj, f)) return bind(obj, f, JS('', 'void 0')); |
return JS('', '#[#]', obj, f); |
} |
- return noSuchMethod(obj, |
- new InvocationImpl(field, JS('', '[]'), isGetter: true)); |
+ return noSuchMethod( |
+ obj, new InvocationImpl(field, JS('', '[]'), isGetter: true)); |
} |
dput(obj, field, value) { |
@@ -48,13 +46,15 @@ dput(obj, field, value) { |
if (f != null) { |
return JS('', '#[#] = #', obj, f, value); |
} |
- return noSuchMethod(obj, |
- new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); |
+ return noSuchMethod( |
+ obj, new InvocationImpl(field, JS('', '[#]', value), isSetter: true)); |
} |
/// Check that a function of a given type can be applied to |
/// actuals. |
-_checkApply(type, actuals) => JS('', '''(() => { |
+_checkApply(type, actuals) => JS( |
+ '', |
+ '''(() => { |
if ($actuals.length < $type.args.length) return false; |
let index = 0; |
for(let i = 0; i < $type.args.length; ++i) { |
@@ -97,15 +97,17 @@ Symbol _dartSymbol(name) => |
extractNamedArgs(args) { |
if (JS('bool', '#.length > 0', args)) { |
var last = JS('', '#[#.length - 1]', args, args); |
- if (JS('bool', '# != null && #.__proto__ === Object.prototype', |
- last, last)) { |
+ if (JS( |
+ 'bool', '# != null && #.__proto__ === Object.prototype', last, last)) { |
return JS('', '#.pop()', 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; |
@@ -176,12 +178,68 @@ _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => { |
return callNSM(); |
})()'''); |
-dcall(f, @rest args) => _checkAndCall( |
- f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); |
+dcall(f, @rest args) => |
+ _checkAndCall(f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call'); |
dgcall(f, typeArgs, @rest args) => _checkAndCall( |
f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call'); |
+/// Helper for REPL dynamic invocation variants that make a best effort to |
+/// enable accessing private members across library boundaries. |
+_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. |
+ if ($field in $object) return $callback($field); |
+ |
+ // Symbol is from a different library. Make a best effort to |
+ $field = $field.toString(); |
+ $field = $field.substring('Symbol('.length, field.length - 1); |
+ |
+ } else if ($field.charAt(0) != '_') { |
+ // Not a private member so default call path is safe. |
+ return $callback($field); |
+ } |
+ |
+ // If the exact field name is present, invoke callback with it. |
+ if ($field in $object) return $callback($field); |
+ |
+ // TODO(jacobr): warn if there are multiple private members with the same |
+ // name which could happen if super classes in different libraries have |
+ // the same private member name. |
+ let proto = $object; |
+ while (proto !== null) { |
+ // Private field (indicated with "_"). |
+ let symbols = Object.getOwnPropertySymbols(proto); |
+ let target = 'Symbol(' + $field + ')'; |
+ |
+ for (let s = 0; s < symbols.length; s++) { |
+ let sym = symbols[s]; |
+ if (target == sym.toString()) return $callback(sym); |
+ } |
+ proto = proto.__proto__; |
+ } |
+ // We didn't find a plausible alternate private symbol so just fall back |
+ // to the regular field. |
+ return $callback(rawField); |
+})()'''); |
+ |
+dloadRepl(obj, field) => |
+ _dhelperRepl(obj, field, (resolvedField) => dload(obj, resolvedField)); |
+ |
+dputRepl(obj, field, value) => _dhelperRepl( |
+ obj, field, (resolvedField) => dput(obj, resolvedField, value)); |
+ |
+_callMethodRepl(obj, method, typeArgs, args) => _dhelperRepl(obj, method, |
+ (resolvedField) => _callMethod(obj, resolvedField, typeArgs, args, method)); |
+ |
+dsendRepl(obj, method, @rest args) => _callMethodRepl(obj, method, null, args); |
+ |
+dgsendRepl(obj, typeArgs, method, @rest args) => |
+ _callMethodRepl(obj, method, typeArgs, args); |
+ |
class _MethodStats { |
final String typeName; |
final String frame; |
@@ -199,8 +257,8 @@ List<List<Object>> getDynamicStats() { |
var keys = _callMethodStats.keys.toList(); |
- keys.sort((a, b) => _callMethodStats[b].count.compareTo( |
- _callMethodStats[a].count)); |
+ keys.sort( |
+ (a, b) => _callMethodStats[b].count.compareTo(_callMethodStats[a].count)); |
for (var key in keys) { |
var stats = _callMethodStats[key]; |
ret.add([stats.typeName, stats.frame, stats.count]); |
@@ -231,16 +289,18 @@ _trackCall(obj) { |
} |
var actualTypeName = typeName(actual); |
- _callMethodStats.putIfAbsent("$actualTypeName <$src>", |
- () => new _MethodStats(actualTypeName, src)).count++; |
+ _callMethodStats |
+ .putIfAbsent( |
+ "$actualTypeName <$src>", () => new _MethodStats(actualTypeName, src)) |
+ .count++; |
} |
/// Shared code for dsend, dindex, and dsetindex. |
_callMethod(obj, name, typeArgs, args, displayName) { |
var symbol = _canonicalMember(obj, name); |
if (symbol == null) { |
- return noSuchMethod(obj, |
- new InvocationImpl(displayName, args, isMethod: true)); |
+ return noSuchMethod( |
+ obj, new InvocationImpl(displayName, args, isMethod: true)); |
} |
var f = obj != null ? JS('', '#[#]', obj, symbol) : null; |
var ftype = getMethodType(obj, symbol); |
@@ -260,7 +320,9 @@ 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); |
@@ -277,7 +339,9 @@ _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, |
@@ -302,7 +366,9 @@ 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 == $jsobject || |
@@ -316,14 +382,18 @@ 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') |
-instanceOf(obj, type) => JS('', '''(() => { |
+instanceOf(obj, type) => JS( |
+ '', |
+ '''(() => { |
let result = $strongInstanceOf($obj, $type); |
if (result !== null) return result; |
let actual = $getReifiedType($obj); |
@@ -389,7 +459,9 @@ 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. |
@@ -409,7 +481,9 @@ 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); |
}; |
@@ -421,7 +495,9 @@ addTypeTests(ctor) => JS('', '''(() => { |
}; |
})()'''); |
-equals(x, y) => JS('', '''(() => { |
+equals(x, y) => JS( |
+ '', |
+ '''(() => { |
if ($x == null || $y == null) return $x == $y; |
let eq = $x['==']; |
return eq ? eq.call($x, $y) : $x === $y; |
@@ -449,7 +525,9 @@ 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 consistentcy 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(); |
@@ -468,13 +546,17 @@ map(values, [K, V]) => JS('', '''(() => { |
})()'''); |
@JSExportName('assert') |
-assert_(condition) => JS('', '''(() => { |
+assert_(condition) => JS( |
+ '', |
+ '''(() => { |
if (!$condition) $throwAssertionError(); |
})()'''); |
final _stack = JS('', 'new WeakMap()'); |
@JSExportName('throw') |
-throw_(obj) => JS('', '''(() => { |
+throw_(obj) => JS( |
+ '', |
+ '''(() => { |
if ($obj != null && (typeof $obj == 'object' || typeof $obj == 'function')) { |
// TODO(jmesserly): couldn't we store the most recent stack in a single |
// variable? There should only be one active stack trace. That would |
@@ -484,19 +566,25 @@ throw_(obj) => JS('', '''(() => { |
throw $obj; |
})()'''); |
-getError(exception) => JS('', '''(() => { |
+getError(exception) => JS( |
+ '', |
+ '''(() => { |
var stack = $_stack.get($exception); |
return stack !== void 0 ? stack : $exception; |
})()'''); |
// This is a utility function: it is only intended to be called from dev |
// tools. |
-stackPrint(exception) => JS('', '''(() => { |
+stackPrint(exception) => JS( |
+ '', |
+ '''(() => { |
var error = $getError($exception); |
console.log(error.stack ? error.stack : 'No stack trace for: ' + error); |
})()'''); |
-stackTrace(exception) => JS('', '''(() => { |
+stackTrace(exception) => JS( |
+ '', |
+ '''(() => { |
var error = $getError($exception); |
return $getTraceFromException(error); |
})()'''); |
@@ -507,7 +595,9 @@ stackTrace(exception) => JS('', '''(() => { |
/// Will call each successive callback, unless one returns null, which stops |
/// the sequence. |
/// |
-nullSafe(obj, @rest callbacks) => JS('', '''(() => { |
+nullSafe(obj, @rest callbacks) => JS( |
+ '', |
+ '''(() => { |
if ($obj == null) return $obj; |
for (let callback of $callbacks) { |
$obj = callback($obj); |
@@ -517,6 +607,7 @@ nullSafe(obj, @rest callbacks) => JS('', '''(() => { |
})()'''); |
final _value = JS('', 'Symbol("_value")'); |
+ |
/// |
/// Looks up a sequence of [keys] in [map], recursively, and |
/// returns the result. If the value is not found, [valueFn] will be called to |
@@ -529,7 +620,9 @@ 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) { |
@@ -561,7 +654,9 @@ 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 |
@@ -612,7 +707,9 @@ final constantLists = JS('', 'new Map()'); |
/// Canonicalize a constant list |
/// |
@JSExportName('constList') |
-constList_(elements, elementType) => JS('', '''(() => { |
+constList_(elements, elementType) => JS( |
+ '', |
+ '''(() => { |
function lookupNonTerminal(map, key) { |
let result = map.get(key); |
if (result !== void 0) return result; |
@@ -639,7 +736,7 @@ hashCode(obj) { |
switch (JS('String', 'typeof #', obj)) { |
case "number": |
- return JS('','# & 0x1FFFFFFF', obj); |
+ return JS('', '# & 0x1FFFFFFF', obj); |
case "boolean": |
// From JSBool.hashCode, see comment there. |
return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj); |
@@ -664,8 +761,8 @@ String _toString(obj) { |
return JS('String', '#[dartx.toString]()', obj); |
} |
if (JS('bool', 'typeof # == "function"', obj)) { |
- return JS('String', r'"Closure: " + # + " from: " + #', |
- getReifiedType(obj), obj); |
+ return JS( |
+ 'String', r'"Closure: " + # + " from: " + #', getReifiedType(obj), obj); |
} |
// TODO(jmesserly): restore this faster path once ES Symbol is treated as |
// an extension type (and thus hits the above code path). |
@@ -677,11 +774,8 @@ String _toString(obj) { |
// TODO(jmesserly): is the argument type verified statically? |
noSuchMethod(obj, Invocation invocation) { |
if (obj == null || JS('bool', 'typeof # == "function"', obj)) { |
- throw new NoSuchMethodError( |
- obj, |
- invocation.memberName, |
- invocation.positionalArguments, |
- invocation.namedArguments); |
+ throw new NoSuchMethodError(obj, invocation.memberName, |
+ invocation.positionalArguments, invocation.namedArguments); |
} |
// Delegate to the (possibly user-defined) method on the object. |
var extension = getExtensionType(obj); |
@@ -712,7 +806,9 @@ 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]; |
@@ -720,8 +816,9 @@ String str(strings, @rest values) => JS('', '''(() => { |
return s; |
})()'''); |
- |
-final JsIterator = JS('', ''' |
+final JsIterator = JS( |
+ '', |
+ ''' |
class JsIterator { |
constructor(dartIterator) { |
this.dartIterator = dartIterator; |