Chromium Code Reviews| 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..10171cc8986363d4ccacc8f483efc0937e04f9ce 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; |
| @@ -2006,3 +2007,318 @@ class RuntimeError extends Error { |
| RuntimeError(this.message); |
| String toString() => "RuntimeError: $message"; |
| } |
| + |
| +abstract class RuntimeType { |
| + const RuntimeType(); |
| + |
| + bool isTest(expression); |
|
Johnni Winther
2013/10/30 12:02:19
Several subtypes have no implementation for this.
ahe
2013/11/29 13:10:17
Done.
|
| + |
| + 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; |
| + |
| + 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(); |
| + print("<<$pretty is $this (${self}) [$result]>>"); |
| + |
| + return result; |
| + } |
| + |
| + void _asCheck(expression) { |
| + // Type inferrer don't think this is called with dynamic arguments. |
| + _check(JS('', '#', expression), true); |
| + } |
| + |
| + void _assertCheck(expression) { |
| + // Type inferrer don't think this is called with dynamic arguments. |
| + _check(JS('', '#', expression), false); |
| + } |
| + |
| + void _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(), |
| + new List.from(parameterTypes.map((p) => p.toRti()))); |
| + } |
| + |
| + if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) { |
| + JS('', '#[#] = #', result, JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG(), |
| + new List.from(optionalParameterTypes.map((p) => p.toRti()))); |
| + } |
| + |
| + 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; |
| + } |
| + |
| + 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 += ']'; |
| + } |
|
Johnni Winther
2013/10/30 12:02:19
Named parameters not handled.
ahe
2013/11/29 13:10:17
Done.
|
| + 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"; |
| + } |
| +} |