Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(312)

Unified Diff: dart/sdk/lib/_internal/lib/js_helper.dart

Issue 50313007: Implement dynamic function checks. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Merged with r30897. Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 7e753ef160c00c8277862e632ea5cf588e36f39b..412572139b373c0a5bfd6d9906f704f6be8891b7 100644
--- a/dart/sdk/lib/_internal/lib/js_helper.dart
+++ b/dart/sdk/lib/_internal/lib/js_helper.dart
@@ -33,6 +33,7 @@ import 'dart:_collection-dev' as _symbol_dev;
import 'dart:_collection-dev' show MappedIterable;
import 'dart:_js_names' show
+ extractKeys,
mangledNames,
unmangleGlobalNameIfPreservedAnyways;
@@ -2027,3 +2028,376 @@ class RuntimeError extends Error {
RuntimeError(this.message);
String toString() => "RuntimeError: $message";
}
+
+abstract class RuntimeType {
+ const RuntimeType();
+
+ toRti();
+}
+
+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);
+
+ 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].
+ @NoInline() @NoSideEffects()
+ bool _isTest(expression) {
+ var functionTypeObject = _extractFunctionTypeObjectFrom(expression);
+ return functionTypeObject == null
+ ? false
+ : isFunctionSubtype(functionTypeObject, toRti());
+ }
+
+ @NoInline() @NoSideEffects()
+ _asCheck(expression) {
+ // Type inferrer doesn't think this is called with dynamic arguments.
ngeoffray 2013/12/09 09:48:52 Maybe add an 'obfuscate' method that will return t
ahe 2013/12/09 15:30:06 Done.
+ return _check(JS('', '#', expression), true);
+ }
+
+ @NoInline() @NoSideEffects()
+ _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 functionTypeObject = _extractFunctionTypeObjectFrom(expression);
+ var pretty;
+ if (functionTypeObject != null) {
+ 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);
+ }
+ }
+
+ _extractFunctionTypeObjectFrom(o) {
+ var interceptor = getInterceptor(o);
+ return JS('bool', '# in #', JS_SIGNATURE_NAME(), interceptor)
+ ? JS('', '#[#]()', interceptor, JS_SIGNATURE_NAME())
+ : null;
+ }
+
+ 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 != null && !parameterTypes.isEmpty) {
+ 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', '{}');
+ var keys = extractKeys(namedParameters);
+ for (var i = 0; i < keys.length; i++) {
+ var name = keys[i];
+ 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', '[]');
ngeoffray 2013/12/09 09:48:52 Why the JS here?
ahe 2013/12/09 15:30:06 Avoids adding type parameters, for one thing. The
ngeoffray 2013/12/09 15:32:49 This is surprising, we've made sure in the backend
+ for (var i = 0; i < list.length; i++) {
+ JS('', '#.push(#)', result, list[i].toRti());
ngeoffray 2013/12/09 09:48:52 Why the JS here?
ahe 2013/12/09 15:30:06 The generated code improved.
ngeoffray 2013/12/09 15:32:49 Ditto.
+ }
+ return result;
+ }
+
+ String toString() {
+ String result = '(';
+ bool needsComma = false;
+ if (parameterTypes != null) {
+ for (var i = 0; i < parameterTypes.length; i++) {
+ RuntimeType type = parameterTypes[i];
+ if (needsComma) result += ', ';
+ result += '$type';
+ needsComma = true;
+ }
+ }
+ if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) {
+ if (needsComma) result += ', ';
+ needsComma = false;
+ result += '[';
+ for (var i = 0; i < optionalParameterTypes.length; i++) {
+ RuntimeType type = optionalParameterTypes[i];
+ if (needsComma) result += ', ';
+ result += '$type';
+ needsComma = true;
+ }
+ result += ']';
+ } else if (namedParameters != null) {
+ if (needsComma) result += ', ';
+ needsComma = false;
+ result += '{';
+ var keys = extractKeys(namedParameters);
+ for (var i = 0; i < keys.length; i++) {
+ var name = keys[i];
+ if (needsComma) result += ', ';
+ var rti = JS('', '#[#]', namedParameters, name).toRti();
+ result += '$rti ${JS("String", "#", name)}';
+ needsComma = true;
+ }
+ result += '}';
+ }
+
+ result += ') -> $returnType';
+ return result;
+ }
+}
+
+RuntimeFunctionType buildFunctionType(returnType,
+ parameterTypes,
+ optionalParameterTypes) {
+ return new RuntimeFunctionType(
+ returnType,
+ parameterTypes,
+ optionalParameterTypes,
+ null);
+}
+
+RuntimeFunctionType buildNamedFunctionType(returnType,
+ parameterTypes,
+ namedParameters) {
+ return new RuntimeFunctionType(
+ returnType,
+ parameterTypes,
+ null,
+ namedParameters);
+}
+
+RuntimeType buildInterfaceType(rti, typeArguments) {
+ String name = JS('String|Null', r'#.name', rti);
+ if (typeArguments == null || 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();
+
+/**
+ * Meta helper for function type tests.
+ *
+ * A "meta helper" is a helper function that is never called but simulates how
+ * generated code behaves as far as resolution and type inference is concerned.
+ */
+functionTypeTestMetaHelper() {
+ var dyn = JS('', 'x');
+ var dyn2 = JS('', 'x');
+ List fixedListOrNull = JS('JSFixedArray|Null', 'x');
+ List fixedListOrNull2 = JS('JSFixedArray|Null', 'x');
+ List fixedList = JS('JSFixedArray', 'x');
+ // TODO(ahe): Can we use [UnknownJavaScriptObject] below?
+ var /* UnknownJavaScriptObject */ jsObject = JS('=Object', 'x');
+
+ buildFunctionType(dyn, fixedListOrNull, fixedListOrNull2);
+ buildNamedFunctionType(dyn, fixedList, jsObject);
+ buildInterfaceType(dyn, fixedListOrNull);
+ getDynamicRuntimeType();
+ getVoidRuntimeType();
+ convertRtiToRuntimeType(dyn);
+ dyn._isTest(dyn2);
+ dyn._asCheck(dyn2);
+ dyn._assertCheck(dyn2);
+}
+
+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 if (JS('bool', '"func" in #', rti)) {
+ return new FunctionTypeInfoDecoderRing(rti).toRuntimeType();
+ } else {
+ throw new RuntimeError(
+ "Cannot convert "
+ "'${JS('String', 'JSON.stringify(#)', rti)}' to RuntimeType.");
+ }
+}
+
+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);
+
+ RuntimeType toRuntimeType() {
+ // TODO(ahe): Implement this (and update return type).
+ return const DynamicRuntimeType();
+ }
+
+ 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";
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698