| Index: dart/sdk/lib/_internal/lib/js_helper.dart
|
| diff --git a/dart/sdk/lib/_internal/lib/js_helper.dart b/dart/sdk/lib/_internal/lib/js_helper.dart
|
| index ff0cf493b32e7ca4331741af2107beaf45320721..a28ab4fe0365b865f919bf0a0589eb4b34789cc5 100644
|
| --- a/dart/sdk/lib/_internal/lib/js_helper.dart
|
| +++ b/dart/sdk/lib/_internal/lib/js_helper.dart
|
| @@ -31,6 +31,7 @@ import 'dart:_interceptors';
|
| import 'dart:_collection-dev' as _symbol_dev;
|
|
|
| import 'dart:_js_names' show
|
| + extractKeys,
|
| mangledNames,
|
| unmangleGlobalNameIfPreservedAnyways;
|
|
|
| @@ -281,6 +282,42 @@ class Primitives {
|
|
|
| static computeGlobalThis() => JS('', 'function() { return this; }()');
|
|
|
| + /**
|
| + * This is the low-level method that is used to implement
|
| + * [print]. It is possible to override this function from JavaScript
|
| + * by defining a function in JavaScript called "dartPrint".
|
| + */
|
| + static void printString(String string) {
|
| + if (JS('bool', r'typeof dartPrint == "function"')) {
|
| + // Support overriding print from JavaScript.
|
| + JS('void', r'dartPrint(#)', string);
|
| + return;
|
| + }
|
| +
|
| + // Inside browser or nodejs.
|
| + if (JS('bool', r'typeof console == "object"') &&
|
| + JS('bool', r'typeof console.log == "function"')) {
|
| + JS('void', r'console.log(#)', string);
|
| + return;
|
| + }
|
| +
|
| + // Don't throw inside IE, the console is only defined if dev tools is open.
|
| + if (JS('bool', r'typeof window == "object"')) {
|
| + return;
|
| + }
|
| +
|
| + // Running in d8, the V8 developer shell, or in Firefox' js-shell.
|
| + if (JS('bool', r'typeof print == "function"')) {
|
| + JS('void', r'print(#)', string);
|
| + return;
|
| + }
|
| +
|
| + // This is somewhat nasty, but we don't want to drag in a bunch of
|
| + // dependencies to handle a situation that cannot happen. So we
|
| + // avoid using Dart [:throw:] and Dart [toString].
|
| + JS('void', 'throw "Unable to print message: " + String(#)', string);
|
| + }
|
| +
|
| static _throwFormatException(String string) {
|
| throw new FormatException(string);
|
| }
|
| @@ -2006,3 +2043,335 @@ class RuntimeError extends Error {
|
| RuntimeError(this.message);
|
| String toString() => "RuntimeError: $message";
|
| }
|
| +
|
| +abstract class RuntimeType {
|
| + const RuntimeType();
|
| +
|
| + bool isTest(expression);
|
| +
|
| + toRti();
|
| +}
|
| +
|
| +// TODO(ahe): Remove this class.
|
| +class DummyRuntimeType extends RuntimeType {
|
| + final String impl;
|
| +
|
| + DummyRuntimeType(this.impl);
|
| +
|
| + bool isTest(expression) {
|
| + throw "<<is-test with '$impl' not implemented>>";
|
| + }
|
| +
|
| + String toString() => 'DummyRuntimeType($impl)';
|
| +}
|
| +
|
| +// TODO(ahe): Delete this method.
|
| +DummyRuntimeType buildDummyType(String impl) => new DummyRuntimeType(impl);
|
| +
|
| +class RuntimeFunctionType extends RuntimeType {
|
| + final RuntimeType returnType;
|
| + final List<RuntimeType> parameterTypes;
|
| + final List<RuntimeType> optionalParameterTypes;
|
| + final namedParameters;
|
| +
|
| + static var /* bool */ inAssert = false;
|
| +
|
| + RuntimeFunctionType(this.returnType,
|
| + this.parameterTypes,
|
| + this.optionalParameterTypes,
|
| + this.namedParameters);
|
| +
|
| + // TODO(ahe): Implement this.
|
| + bool get isVoid => returnType is VoidRuntimeType;
|
| +
|
| + /// Called from generated code. [expression] is a Dart object and this method
|
| + /// returns true if [this] is a supertype of [expression].
|
| + bool _isTest(expression) {
|
| + // Type inferrer don't think this is called with dynamic arguments.
|
| + expression = JS('', '#', expression);
|
| +
|
| + var interceptor = getInterceptor(expression);
|
| + if (!JS('bool', '# in #', JS_SIGNATURE_NAME(), interceptor)) return false;
|
| + var functionTypeObject = JS('', '#[#]()', interceptor, JS_SIGNATURE_NAME());
|
| + bool result = isFunctionSubtype(functionTypeObject, toRti());
|
| +
|
| + // DEBUG REMOVE
|
| + var pretty = new FunctionTypeInfoDecoderRing(functionTypeObject).toString();
|
| + var self = new FunctionTypeInfoDecoderRing(toRti()).toString();
|
| + Primitives.printString("<<$pretty is $this (${self}) [$result]>>");
|
| +
|
| + return result;
|
| + }
|
| +
|
| + _asCheck(expression) {
|
| + // Type inferrer don't think this is called with dynamic arguments.
|
| + return _check(JS('', '#', expression), true);
|
| + }
|
| +
|
| + _assertCheck(expression) {
|
| + if (inAssert) return;
|
| + inAssert = true; // Don't try to check this library itself.
|
| + try {
|
| + // Type inferrer don't think this is called with dynamic arguments.
|
| + return _check(JS('', '#', expression), false);
|
| + } finally {
|
| + inAssert = false;
|
| + }
|
| + }
|
| +
|
| + _check(expression, bool isCast) {
|
| + if (expression == null) return null;
|
| + if (_isTest(expression)) return expression;
|
| +
|
| + var self = new FunctionTypeInfoDecoderRing(toRti()).toString();
|
| + if (isCast) {
|
| + var interceptor = getInterceptor(expression);
|
| + var pretty;
|
| + if (JS('bool', '# in #', JS_SIGNATURE_NAME(), interceptor)) {
|
| + var functionTypeObject =
|
| + JS('', '#[#]()', interceptor, JS_SIGNATURE_NAME());
|
| + pretty = new FunctionTypeInfoDecoderRing(functionTypeObject).toString();
|
| + } else {
|
| + pretty = Primitives.objectTypeName(expression);
|
| + }
|
| + throw new CastErrorImplementation(pretty, self);
|
| + } else {
|
| + // TODO(ahe): Pass "pretty" function-type to TypeErrorImplementation?
|
| + throw new TypeErrorImplementation(expression, self);
|
| + }
|
| + }
|
| +
|
| + toRti() {
|
| + var result = JS('=Object', '{ #: "dynafunc" }', JS_FUNCTION_TYPE_TAG());
|
| + if (isVoid) {
|
| + JS('', '#[#] = true', result, JS_FUNCTION_TYPE_VOID_RETURN_TAG());
|
| + } else {
|
| + if (returnType is! DynamicRuntimeType) {
|
| + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_RETURN_TYPE_TAG(),
|
| + returnType.toRti());
|
| + }
|
| + }
|
| + if (parameterTypes.length > 0) {
|
| + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG(),
|
| + listToRti(parameterTypes));
|
| + }
|
| +
|
| + if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) {
|
| + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG(),
|
| + listToRti(optionalParameterTypes));
|
| + }
|
| +
|
| + if (namedParameters != null) {
|
| + var namedRti = JS('=Object', '{}');
|
| + for (var name in extractKeys(namedParameters)) {
|
| + var rti = JS('', '#[#]', namedParameters, name).toRti();
|
| + JS('', '#[#] = #', namedRti, name, rti);
|
| + }
|
| + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG(),
|
| + namedRti);
|
| + }
|
| +
|
| + return result;
|
| + }
|
| +
|
| + static listToRti(list) {
|
| + list = JS('JSFixedArray', '#', list);
|
| + var result = JS('JSExtendableArray', '[]');
|
| + for (var i = 0; i < list.length; i++) {
|
| + JS('', '#.push(#)', result, list[i].toRti());
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + String toString() {
|
| + String result = '(';
|
| + bool needsComma = false;
|
| + for (RuntimeType type in parameterTypes) {
|
| + if (needsComma) result += ', ';
|
| + result += '$type';
|
| + needsComma = true;
|
| + }
|
| + if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) {
|
| + if (needsComma) result += ', ';
|
| + needsComma = false;
|
| + result += '[';
|
| + for (RuntimeType type in optionalParameterTypes) {
|
| + if (needsComma) result += ', ';
|
| + result += '$type';
|
| + needsComma = true;
|
| + }
|
| + result += ']';
|
| + }
|
| + result += ') -> $returnType';
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +RuntimeFunctionType buildFunctionType(RuntimeType returnType,
|
| + List parameterTypes,
|
| + List optionalParameterTypes) {
|
| + return new RuntimeFunctionType(
|
| + returnType, parameterTypes, optionalParameterTypes, null);
|
| +}
|
| +
|
| +RuntimeFunctionType buildNamedFunctionType(RuntimeType returnType,
|
| + List parameterTypes,
|
| + namedParameters) {
|
| + return new RuntimeFunctionType(
|
| + returnType, parameterTypes, null, namedParameters);
|
| +}
|
| +
|
| +RuntimeType buildInterfaceType(rti, List typeArguments) {
|
| + String name = JS('String|Null', r'#.name', rti);
|
| + typeArguments = JS('JSFixedArray', '#', typeArguments);
|
| + if (typeArguments.isEmpty) return new RuntimeTypePlain(name);
|
| + return new RuntimeTypeGeneric(name, typeArguments, null);
|
| +}
|
| +
|
| +class DynamicRuntimeType extends RuntimeType {
|
| + const DynamicRuntimeType();
|
| +
|
| + String toString() => 'dynamic';
|
| +
|
| + toRti() => null;
|
| +}
|
| +
|
| +RuntimeType getDynamicRuntimeType() => const DynamicRuntimeType();
|
| +
|
| +class VoidRuntimeType extends RuntimeType {
|
| + const VoidRuntimeType();
|
| +
|
| + String toString() => 'void';
|
| +
|
| + toRti() => throw 'internal error';
|
| +}
|
| +
|
| +RuntimeType getVoidRuntimeType() => const VoidRuntimeType();
|
| +
|
| +RuntimeType convertRtiToRuntimeType(rti) {
|
| + if (rti == null) {
|
| + return getDynamicRuntimeType();
|
| + } else if (JS('bool', 'typeof # == "function"', rti)) {
|
| + return new RuntimeTypePlain(JS('String', r'rti.name'));
|
| + } else if (JS('bool', '#.constructor == Array', rti)) {
|
| + List list = JS('JSFixedArray', '#', rti);
|
| + String name = JS('String', r'#.name', list[0]);
|
| + List arguments = [];
|
| + for (int i = 1; i < list.length; i++) {
|
| + arguments.add(convertRtiToRuntimeType(list[i]));
|
| + }
|
| + return new RuntimeTypeGeneric(name, arguments, rti);
|
| + } else {
|
| + return new DummyRuntimeType("${JS('String', 'JSON.stringify(#)', rti)}");
|
| + }
|
| +}
|
| +
|
| +class RuntimeTypePlain extends RuntimeType {
|
| + final String name;
|
| +
|
| + RuntimeTypePlain(this.name);
|
| +
|
| + toRti() {
|
| + var rti = JS('', 'init.allClasses[#]', name);
|
| + if (rti == null) throw "no type for '$name'";
|
| + return rti;
|
| + }
|
| +
|
| + String toString() => name;
|
| +}
|
| +
|
| +class RuntimeTypeGeneric extends RuntimeType {
|
| + final String name;
|
| + final List<RuntimeType> arguments;
|
| + var rti;
|
| +
|
| + RuntimeTypeGeneric(this.name, this.arguments, this.rti);
|
| +
|
| + toRti() {
|
| + if (rti != null) return rti;
|
| + var result = JS('JSExtendableArray', '[init.allClasses[#]]', name);
|
| + if (result[0] == null) {
|
| + throw "no type for '$name<...>'";
|
| + }
|
| + for (RuntimeType argument in arguments) {
|
| + JS('', '#.push(#)', result, argument.toRti());
|
| + }
|
| + return rti = result;
|
| + }
|
| +
|
| + String toString() => '$name<${arguments.join(", ")}>';
|
| +}
|
| +
|
| +class FunctionTypeInfoDecoderRing {
|
| + final _typeData;
|
| + String _cachedToString;
|
| +
|
| + FunctionTypeInfoDecoderRing(this._typeData);
|
| +
|
| + bool get _hasReturnType => JS('bool', '"ret" in #', _typeData);
|
| + get _returnType => JS('', '#.ret', _typeData);
|
| +
|
| + bool get _isVoid => JS('bool', '!!#.void', _typeData);
|
| +
|
| + bool get _hasArguments => JS('bool', '"args" in #', _typeData);
|
| + List get _arguments => JS('JSExtendableArray', '#.args', _typeData);
|
| +
|
| + bool get _hasOptionalArguments => JS('bool', '"opt" in #', _typeData);
|
| + List get _optionalArguments => JS('JSExtendableArray', '#.opt', _typeData);
|
| +
|
| + bool get _hasNamedArguments => JS('bool', '"named" in #', _typeData);
|
| + get _namedArguments => JS('=Object', '#.named', _typeData);
|
| +
|
| + String _convert(type) {
|
| + String result = runtimeTypeToString(type);
|
| + if (result != null) return result;
|
| + if (JS('bool', '"func" in #', type)) {
|
| + return new FunctionTypeInfoDecoderRing(type).toString();
|
| + } else {
|
| + throw 'bad type';
|
| + }
|
| + }
|
| +
|
| + String toString() {
|
| + if (_cachedToString != null) return _cachedToString;
|
| + var s = "(";
|
| + var sep = '';
|
| + if (_hasArguments) {
|
| + for (var argument in _arguments) {
|
| + s += sep;
|
| + s += _convert(argument);
|
| + sep = ', ';
|
| + }
|
| + }
|
| + if (_hasOptionalArguments) {
|
| + s += '$sep[';
|
| + sep = '';
|
| + for (var argument in _optionalArguments) {
|
| + s += sep;
|
| + s += _convert(argument);
|
| + sep = ', ';
|
| + }
|
| + s += ']';
|
| + }
|
| + if (_hasNamedArguments) {
|
| + s += '$sep{';
|
| + sep = '';
|
| + for (var name in extractKeys(_namedArguments)) {
|
| + s += sep;
|
| + s += '$name: ';
|
| + s += _convert(JS('', '#[#]', _namedArguments, name));
|
| + sep = ', ';
|
| + }
|
| + s += '}';
|
| + }
|
| + s += ') -> ';
|
| + if (_isVoid) {
|
| + s += 'void';
|
| + } else if (_hasReturnType) {
|
| + s += _convert(_returnType);
|
| + } else {
|
| + s += 'dynamic';
|
| + }
|
| + return _cachedToString = "$s";
|
| + }
|
| +}
|
|
|