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"; |
+ } |
+} |