| Index: tool/input_sdk/private/ddc_runtime/operations.dart
|
| diff --git a/tool/input_sdk/private/ddc_runtime/operations.dart b/tool/input_sdk/private/ddc_runtime/operations.dart
|
| index c64546271822394781d492987138c68c1d4f87af..2b5460524c7ced557f41af9e610b2a7e10588c32 100644
|
| --- a/tool/input_sdk/private/ddc_runtime/operations.dart
|
| +++ b/tool/input_sdk/private/ddc_runtime/operations.dart
|
| @@ -6,39 +6,51 @@
|
| /// generator.
|
| part of dart._runtime;
|
|
|
| -_canonicalFieldName(obj, name, args, displayName) => JS('', '''(() => {
|
| - $name = $canonicalMember($obj, $name);
|
| - if ($name) return $name;
|
| - // TODO(jmesserly): in the future we might have types that "overlay" Dart
|
| - // methods while also exposing the full native API, e.g. dart:html vs
|
| - // dart:dom. To support that we'd need to fall back to the normal name
|
| - // if an extension method wasn't found.
|
| - $throwNoSuchMethodFunc($obj, $displayName, $args);
|
| -})()''');
|
| -
|
| -dload(obj, field) => JS('', '''(() => {
|
| - $field = $_canonicalFieldName($obj, $field, [], $field);
|
| - $_trackCall($obj, $field);
|
| - if ($hasMethod($obj, $field)) {
|
| - return $bind($obj, $field);
|
| - }
|
| - // TODO(vsm): Implement NSM robustly. An 'in' check breaks on certain
|
| - // types. hasOwnProperty doesn't chase the proto chain.
|
| - // Also, do we want an NSM on regular JS objects?
|
| - // See: https://github.com/dart-lang/dev_compiler/issues/169
|
| - let result = $obj[$field];
|
| - return result;
|
| -})()''');
|
| +class _Invocation extends Invocation {
|
| + final Symbol memberName;
|
| + final List positionalArguments;
|
| + final Map<Symbol, dynamic> namedArguments;
|
| + final bool isMethod;
|
| + final bool isGetter;
|
| + final bool isSetter;
|
| +
|
| + _Invocation(String memberName, this.positionalArguments,
|
| + {namedArguments,
|
| + this.isMethod: false,
|
| + this.isGetter: false,
|
| + this.isSetter: false})
|
| + : 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));
|
| + }
|
| +}
|
|
|
| -dput(obj, field, value) => JS('', '''(() => {
|
| - $field = $_canonicalFieldName($obj, $field, [$value], $field);
|
| - $_trackCall($obj, $field);
|
| +dload(obj, field) {
|
| + var f = _canonicalMember(obj, field);
|
| + _trackCall(obj, f);
|
| + if (f != null) {
|
| + if (hasMethod(obj, f)) return bind(obj, f, JS('', 'void 0'));
|
| + return JS('', '#[#]', obj, f);
|
| + }
|
| + return noSuchMethod(obj,
|
| + new _Invocation(field, JS('', '[]'), isGetter: true));
|
| +}
|
|
|
| - // TODO(vsm): Implement NSM and type checks.
|
| - // See: https://github.com/dart-lang/dev_compiler/issues/170
|
| - $obj[$field] = $value;
|
| - return $value;
|
| -})()''');
|
| +dput(obj, field, value) {
|
| + var f = _canonicalMember(obj, field);
|
| + _trackCall(obj, f);
|
| + if (f != null) {
|
| + return JS('', '#[#] = #', obj, f, value);
|
| + }
|
| + return noSuchMethod(obj,
|
| + new _Invocation(field, JS('', '[#]', value), isSetter: true));
|
| +}
|
|
|
| /// Check that a function of a given type can be applied to
|
| /// actuals.
|
| @@ -77,23 +89,24 @@ _checkApply(type, actuals) => JS('', '''(() => {
|
| return true;
|
| })()''');
|
|
|
| -_dartSymbol(name) => JS('', '''
|
| - $const_($Symbol.new($name.toString()))
|
| -''');
|
| -
|
| -throwNoSuchMethod(obj, name, pArgs, nArgs, extras) => JS('', '''(() => {
|
| - $throw_(new $NoSuchMethodError($obj, $_dartSymbol($name), $pArgs, $nArgs, $extras));
|
| -})()''');
|
| -
|
| -throwNoSuchMethodFunc(obj, name, pArgs, opt_func) => JS('', '''(() => {
|
| - if ($obj === void 0) $obj = $opt_func;
|
| - $throwNoSuchMethod($obj, $name, $pArgs);
|
| -})()''');
|
| +Symbol _dartSymbol(name) =>
|
| + JS('', '#(#.new(#.toString()))', const_, Symbol, name);
|
|
|
| +// TODO(jmesserly): we need to handle named arguments better.
|
| _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => {
|
| $_trackCall($obj, $name);
|
|
|
| - let originalFunction = $f;
|
| + let originalTarget = obj === void 0 ? f : obj;
|
| +
|
| + function callNSM() {
|
| + let namedArgs = null;
|
| + if (args.length > 1 &&
|
| + args[args.length - 1].__proto__ == Object.prototype) {
|
| + namedArgs = args.pop();
|
| + }
|
| + return $noSuchMethod(originalTarget, new $_Invocation(
|
| + $name, $args, {namedArguments: namedArgs, isMethod: true}));
|
| + }
|
| if (!($f instanceof Function)) {
|
| // We're not a function (and hence not a method either)
|
| // Grab the `call` method if it's not a function.
|
| @@ -102,7 +115,7 @@ _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => {
|
| $f = $f.call;
|
| }
|
| if (!($f instanceof Function)) {
|
| - $throwNoSuchMethodFunc($obj, $name, $args, originalFunction);
|
| + return callNSM();
|
| }
|
| }
|
| // If f is a function, but not a method (no method type)
|
| @@ -152,13 +165,12 @@ _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => {
|
| // TODO(leafp): throw a type error (rather than NSM)
|
| // if the arity matches but the types are wrong.
|
| // TODO(jmesserly): nSM should include type args?
|
| - $throwNoSuchMethodFunc($obj, $name, $args, originalFunction);
|
| + return callNSM();
|
| })()''');
|
|
|
| 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');
|
|
|
| @@ -209,7 +221,11 @@ _trackCall(obj, name) {
|
|
|
| /// Shared code for dsend, dindex, and dsetindex.
|
| _callMethod(obj, name, typeArgs, args, displayName) {
|
| - var symbol = _canonicalFieldName(obj, name, args, displayName);
|
| + var symbol = _canonicalMember(obj, name);
|
| + if (symbol == null) {
|
| + return noSuchMethod(obj,
|
| + new _Invocation(displayName, args, isMethod: true));
|
| + }
|
| var f = obj != null ? JS('', '#[#]', obj, symbol) : null;
|
| var ftype = getMethodType(obj, symbol);
|
| return _checkAndCall(f, ftype, obj, typeArgs, args, displayName);
|
| @@ -611,6 +627,9 @@ hashCode(obj) {
|
| case "boolean":
|
| // From JSBool.hashCode, see comment there.
|
| return JS('', '# ? (2 * 3 * 23 * 3761) : (269 * 811)', obj);
|
| + case "function":
|
| + // TODO(jmesserly): this doesn't work for method tear-offs.
|
| + return Primitives.objectHashCode(obj);
|
| }
|
|
|
| var extension = getExtensionType(obj);
|
| @@ -628,6 +647,10 @@ String _toString(obj) {
|
| if (extension != null) {
|
| return JS('String', '#[dartx.toString]()', obj);
|
| }
|
| + if (JS('bool', 'typeof # == "function"', 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).
|
| // See https://github.com/dart-lang/dev_compiler/issues/578.
|
| @@ -637,9 +660,9 @@ String _toString(obj) {
|
|
|
| // TODO(jmesserly): is the argument type verified statically?
|
| noSuchMethod(obj, Invocation invocation) {
|
| - if (obj == null) {
|
| + if (obj == null || JS('bool', 'typeof # == "function"', obj)) {
|
| throw new NoSuchMethodError(
|
| - null,
|
| + obj,
|
| invocation.memberName,
|
| invocation.positionalArguments,
|
| invocation.namedArguments);
|
| @@ -664,6 +687,9 @@ runtimeType(obj) {
|
| if (extension != null) {
|
| return JS('', '#[dartx.runtimeType]', obj);
|
| }
|
| + if (JS('bool', 'typeof # == "function"', obj)) {
|
| + return wrapType(getReifiedType(obj));
|
| + }
|
| return JS('', '#.runtimeType', obj);
|
| }
|
|
|
| @@ -691,3 +717,18 @@ final JsIterator = JS('', '''
|
| }
|
| }
|
| ''');
|
| +
|
| +_canonicalMember(obj, name) {
|
| + // Private names are symbols and are already canonical.
|
| + if (JS('bool', 'typeof # === "symbol"', name)) return name;
|
| +
|
| + if (obj != null && getExtensionType(obj) != null) {
|
| + return JS('', 'dartx.#', name);
|
| + }
|
| +
|
| + // Check for certain names that we can't use in JS
|
| + if (name == 'constructor' || name == 'prototype') {
|
| + name = '+' + name;
|
| + }
|
| + return name;
|
| +}
|
|
|