From a6a2152482465a620597ed5dfc6b3ec24f0d4541 Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Fri, 30 Mar 2018 11:05:43 +0000 Subject: [PATCH] Add runtime type representation for void Change-Id: Id4eff63656fc5eacfa7aada6120d85acec735cad Reviewed-on: https://dart-review.googlesource.com/48426 Reviewed-by: Stephen Adams Reviewed-by: Sigmund Cherem --- .../lib/src/js_backend/runtime_types.dart | 43 ++++++++- .../src/js_emitter/full_emitter/emitter.dart | 6 ++ .../js_emitter/startup_emitter/emitter.dart | 6 ++ .../_internal/js_runtime/lib/js_helper.dart | 20 ++++ sdk/lib/_internal/js_runtime/lib/js_rti.dart | 96 ++++++++++++++----- .../js_runtime/lib/shared/embedded_names.dart | 10 ++ 6 files changed, 152 insertions(+), 29 deletions(-) diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart index ff8e492dfd8..505d53c28fe 100644 --- a/pkg/compiler/lib/src/js_backend/runtime_types.dart +++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart @@ -620,6 +620,14 @@ abstract class RuntimeTypesEncoder { /// is a FutureOr type. jsAst.Template get templateForIsFutureOrType; + /// Returns the JavaScript template to determine at runtime if a type object + /// is the void type. + jsAst.Template get templateForIsVoidType; + + /// Returns the JavaScript template to determine at runtime if a type object + /// is the dynamic type. + jsAst.Template get templateForIsDynamicType; + jsAst.Name get getFunctionThatReturnsNullName; /// Returns a [jsAst.Expression] representing the given [type]. Type variables @@ -1822,6 +1830,20 @@ class RuntimeTypesEncoderImpl implements RuntimeTypesEncoder { return _representationGenerator.templateForIsFutureOrType; } + /// Returns the JavaScript template to determine at runtime if a type object + /// is the void type. + @override + jsAst.Template get templateForIsVoidType { + return _representationGenerator.templateForIsVoidType; + } + + /// Returns the JavaScript template to determine at runtime if a type object + /// is the dynamic type. + @override + jsAst.Template get templateForIsDynamicType { + return _representationGenerator.templateForIsDynamicType; + } + @override jsAst.Expression getTypeRepresentation( Emitter emitter, DartType type, OnVariableCallback onVariable, @@ -2015,6 +2037,8 @@ class TypeRepresentationGenerator jsAst.Expression getDynamicValue() => js('null'); + jsAst.Expression getVoidValue() => js('-1'); + @override jsAst.Expression visit(DartType type, Emitter emitter) => type.accept(this, emitter); @@ -2078,6 +2102,18 @@ class TypeRepresentationGenerator return jsAst.js.expressionTemplateFor("'${namer.futureOrTag}' in #"); } + /// Returns the JavaScript template to determine at runtime if a type object + /// is the void type. + jsAst.Template get templateForIsVoidType { + return jsAst.js.expressionTemplateFor("# === -1"); + } + + /// Returns the JavaScript template to determine at runtime if a type object + /// is the dynamic type. + jsAst.Template get templateForIsDynamicType { + return jsAst.js.expressionTemplateFor("# == null"); + } + jsAst.Expression visitFunctionType(FunctionType type, Emitter emitter) { List properties = []; @@ -2102,7 +2138,7 @@ class TypeRepresentationGenerator visitList(type.typeVariables.map((v) => v.bound).toList(), emitter)); } - if (type.returnType.isVoid) { + if (!_strongMode && type.returnType.isVoid) { addProperty(namer.functionTypeVoidReturnTag, js('true')); } else if (!type.returnType.treatAsDynamic) { addProperty( @@ -2140,12 +2176,11 @@ class TypeRepresentationGenerator jsAst.Expression visitMalformedType(MalformedType type, Emitter emitter) { // Treat malformed types as dynamic at runtime. - return js('null'); + return getDynamicValue(); } jsAst.Expression visitVoidType(VoidType type, Emitter emitter) { - // TODO(ahe): Reify void type ("null" means "dynamic"). - return js('null'); + return _strongMode ? getVoidValue() : getDynamicValue(); } jsAst.Expression visitTypedefType( diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart index 66aa0247138..79398048a84 100644 --- a/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart @@ -339,6 +339,12 @@ class Emitter extends js_emitter.EmitterBase { case JsBuiltin.isFutureOrType: return backend.rtiEncoder.templateForIsFutureOrType; + case JsBuiltin.isVoidType: + return backend.rtiEncoder.templateForIsVoidType; + + case JsBuiltin.isDynamicType: + return backend.rtiEncoder.templateForIsDynamicType; + case JsBuiltin.rawRtiToJsConstructorName: return jsAst.js.expressionTemplateFor("#.$typeNameProperty"); diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart index 19b685bed45..18541ceadae 100644 --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart @@ -129,6 +129,12 @@ class Emitter extends emitterTask.EmitterBase { case JsBuiltin.isFutureOrType: return _backend.rtiEncoder.templateForIsFutureOrType; + case JsBuiltin.isVoidType: + return _backend.rtiEncoder.templateForIsVoidType; + + case JsBuiltin.isDynamicType: + return _backend.rtiEncoder.templateForIsDynamicType; + case JsBuiltin.rawRtiToJsConstructorName: return js.js.expressionTemplateFor("#.name"); diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart index a57a9d9477b..1f9f8a9a573 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -92,6 +92,9 @@ String isCheckPropertyToJsConstructorName(String isCheckProperty) { // TODO(floitsch): move this to foreign_helper.dart or similar. @ForceInline() bool isDartFunctionType(Object type) { + // Function type test is using the `in` operator which doesn't work on + // primitive types. + assert(!(type == null || type is num || type is String)); return JS_BUILTIN( 'returns:bool;effects:none;depends:none', JsBuiltin.isFunctionType, type); } @@ -100,10 +103,19 @@ bool isDartFunctionType(Object type) { // TODO(floitsch): move this to foreign_helper.dart or similar. @ForceInline() bool isDartFutureOrType(Object type) { + // FutureOr test is using the `in` operator which doesn't work on primitive + // types. + assert(!(type == null || type is num || type is String)); return JS_BUILTIN( 'returns:bool;effects:none;depends:none', JsBuiltin.isFutureOrType, type); } +@ForceInline() +bool isDartVoidTypeRti(Object type) { + return JS_BUILTIN( + 'returns:bool;effects:none;depends:none', JsBuiltin.isVoidType, type); +} + /// Retrieves the class name from type information stored on the constructor of /// [type]. // TODO(floitsch): move this to foreign_helper.dart or similar. @@ -162,6 +174,14 @@ bool isNullType(Object type) { JS_GET_NAME(JsGetName.NULL_CLASS_TYPE_NAME)); } +/// Returns whether the given type is the dynamic type. +// TODO(floitsch): move this to foreign_helper.dart or similar. +@ForceInline() +bool isDartDynamicTypeRti(type) { + return JS_BUILTIN( + 'returns:bool;effects:none;depends:none', JsBuiltin.isDynamicType, type); +} + /// Returns whether the given type is _the_ Dart Object type. // TODO(floitsch): move this to foreign_helper.dart or similar. @ForceInline() diff --git a/sdk/lib/_internal/js_runtime/lib/js_rti.dart b/sdk/lib/_internal/js_runtime/lib/js_rti.dart index b3343365ab0..ae3fac400c7 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_rti.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_rti.dart @@ -215,9 +215,12 @@ String runtimeTypeToStringV1(var rti, {String onTypeVariable(int i)}) { } String runtimeTypeToStringV2(var rti, List genericContext) { - if (rti == null) { + if (isDartDynamicTypeRti(rti)) { return 'dynamic'; } + if (isDartVoidTypeRti(rti)) { + return 'void'; + } if (isJsArray(rti)) { // A list representing a type with arguments. return _getRuntimeTypeAsStringV2(rti, genericContext); @@ -706,13 +709,47 @@ computeSignature(var signature, var context, var contextName) { return invokeOn(signature, context, typeArguments); } +/// Returns `true` if the runtime type representation [type] is a top type. +/// +/// For Dart 1 this is either `dynamic` or `Object`. For Dart 2 this is either +/// `dynamic`, `void` or `Object`. +@ForceInline() +bool isTopType(var type) { + return JS_GET_FLAG('STRONG_MODE') ? isTopTypeV2(type) : isTopTypeV1(type); +} + +/// Returns `true` if the runtime type representation [type] is a top type for +/// Dart 1. That is, either `dynamic` or `Object`. +@ForceInline() +bool isTopTypeV1(var type) { + return isDartDynamicTypeRti(type) || isDartObjectTypeRti(type); +} + +/// Returns `true` if the runtime type representation [type] is a top type for +/// Dart 2. That is, either `dynamic`, `void` or `Object`. +@ForceInline() +bool isTopTypeV2(var type) { + return isDartDynamicTypeRti(type) || + isDartVoidTypeRti(type) || + isDartObjectTypeRti(type); +} + /// Returns `true` if the runtime type representation [type] is a supertype of /// [Null]. @ForceInline() bool isSupertypeOfNull(var type) { return JS_GET_FLAG('STRONG_MODE') - ? isSupertypeOfNullBase(type) || isSupertypeOfNullRecursive(type) - : isSupertypeOfNullBase(type); + ? isSupertypeOfNullBaseV2(type) || isSupertypeOfNullRecursive(type) + : isSupertypeOfNullBaseV1(type); +} + +/// Returns `true` if the runtime type representation [type] is a simple +/// supertype of [Null]. +@ForceInline() +bool isSupertypeOfNullBaseV1(var type) { + return isDartDynamicTypeRti(type) || + isDartObjectTypeRti(type) || + isNullTypeRti(type); } /// Returns `true` if the runtime type representation [type] is a simple @@ -721,9 +758,11 @@ bool isSupertypeOfNull(var type) { /// This method doesn't handle `FutureOr`. This is handle by /// [isSupertypeOfNullRecursive] because it requires a recursive check. @ForceInline() -bool isSupertypeOfNullBase(var type) { - // `null` means `dynamic`. - return type == null || isDartObjectTypeRti(type) || isNullTypeRti(type); +bool isSupertypeOfNullBaseV2(var type) { + return isDartDynamicTypeRti(type) || + isDartObjectTypeRti(type) || + isNullTypeRti(type) || + isDartVoidTypeRti(type); } /// Returns `true` if the runtime type representation [type] is a `FutureOr` @@ -732,9 +771,14 @@ bool isSupertypeOfNullBase(var type) { /// This method is recursive to be able to handle both `FutureOr` and /// `FutureOr>` etc. bool isSupertypeOfNullRecursive(var type) { + if (isGenericFunctionTypeParameter(type)) { + // We need to check for function type variables because `isDartFutureOrType` + // doesn't work on numbers. + return false; + } if (isDartFutureOrType(type)) { var typeArgument = getFutureOrArgument(type); - return isSupertypeOfNullBase(type) || + return isSupertypeOfNullBaseV2(type) || isSupertypeOfNullRecursive(typeArgument); } return false; @@ -762,7 +806,7 @@ Object getFutureOrArgument(var type) { */ bool checkSubtypeOfRuntimeType(o, t) { if (o == null) return isSupertypeOfNull(t); - if (t == null) return true; + if (isTopType(t)) return true; // Get the runtime type information from the object here, because we may // overwrite o with the interceptor below. var rti = getRuntimeTypeInfo(o); @@ -832,7 +876,7 @@ bool isSubtypeV1(var s, var t) { // Subtyping is reflexive. if (isIdentical(s, t)) return true; // If either type is dynamic, [s] is a subtype of [t]. - if (s == null || t == null) return true; + if (isDartDynamicTypeRti(s) || isDartDynamicTypeRti(t)) return true; // Generic function type parameters must match exactly, which would have // exited earlier. The de Bruijn indexing ensures the representation as a @@ -886,12 +930,15 @@ bool isSubtypeV2(var s, var sEnv, var t, var tEnv) { if (isIdentical(s, t)) return true; // [t] is a top type? - if (t == null) return true; - if (isDartObjectTypeRti(t)) return true; - // TODO(sra): void is a top type. + if (isTopTypeV2(t)) return true; // [s] is a top type? - if (s == null) { + if (isTopTypeV2(s)) { + if (isGenericFunctionTypeParameter(t)) { + // We need to check for function type variables because + // `isDartFutureOrType` doesn't work on numbers. + return false; + } if (isDartFutureOrType(t)) { // [t] is FutureOr. Check [s] <: T. var tTypeArgument = getFutureOrArgument(t); @@ -1158,12 +1205,8 @@ bool isFunctionSubtypeV2(var s, var sEnv, var t, var tEnv) { return false; } - // 'void' is a top type, so use `null` (dynamic) in its place. - // TODO(sra): Create a void type that can be used in all positions. - var sReturnType = - hasField(s, voidReturnTag) ? null : getField(s, returnTypeTag); - var tReturnType = - hasField(t, voidReturnTag) ? null : getField(t, returnTypeTag); + var sReturnType = getField(s, returnTypeTag); + var tReturnType = getField(t, returnTypeTag); if (!isSubtypeV2(sReturnType, sEnv, tReturnType, tEnv)) return false; var requiredParametersTag = @@ -1251,12 +1294,14 @@ bool namedParametersSubtypeCheckV2(var s, var sEnv, var t, var tEnv) { return true; } -/** - * Returns whether [type] is the representation of a generic function type - * parameter. Generic function type parameters are represented de Bruijn - * indexes. - */ +/// Returns whether [type] is the representation of a generic function type +/// parameter. Generic function type parameters are represented de Bruijn +/// indexes. +/// +/// This test is only valid if [type] is known _not_ to be the void rti, whose +/// runtime representation is -1. bool isGenericFunctionTypeParameter(var type) { + assert(!isDartVoidTypeRti(type)); return type is num; // Actually int, but 'is num' is faster. } @@ -1362,7 +1407,8 @@ finishBindInstantiatedFunctionType(rti, result, parameters, int depth) { /// scope. This is subtracted off the de Bruijn index for the type parameter to /// arrive at an potential index into [parameters]. bindInstantiatedType(rti, parameters, int depth) { - if (rti == null) return rti; // dynamic. + if (isDartDynamicTypeRti(rti)) return rti; // dynamic. + if (isDartVoidTypeRti(rti)) return rti; // void. // Functions are constructors denoting the class of the constructor. if (isJsFunction(rti)) return rti; diff --git a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart index 3e07289ecf1..fdcc1837589 100644 --- a/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart +++ b/sdk/lib/_internal/js_runtime/lib/shared/embedded_names.dart @@ -418,6 +418,16 @@ enum JsBuiltin { /// JS_BUILTIN('bool', JsBuiltin.isFutureOrType, o) isFutureOrType, + /// Returns true if the given type is the `void` type. + /// + /// JS_BUILTIN('bool', JsBuiltin.isVoidType, o) + isVoidType, + + /// Returns true if the given type is the `dynamic` type. + /// + /// JS_BUILTIN('bool', JsBuiltin.isDynamicType, o) + isDynamicType, + /// Returns the JavaScript-constructor name given an rti encoding. /// /// JS_BUILTIN('String', JsBuiltin.rawRtiToJsConstructorName, rti)