[ VM ] Added nullability aware Type methods in embedding API

Added the following methods to the VM embedding API:
  * Dart_GetNonNullableType
  * Dart_GetNullableType
  * Dart_TypeToNonNullable
  * Dart_TypeToNullable
  * Dart_IsLegacyType
  * Dart_IsNonNullableType
  * Dart_IsNullableType

Change-Id: I7de1a99179c4d16a0e6a040bb209de18db379436
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135484
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Ben Konyi 2020-02-14 18:29:17 +00:00 committed by commit-bot@chromium.org
parent 0c5b98bb15
commit 06f86bf58a
4 changed files with 380 additions and 47 deletions

View file

@ -33,6 +33,14 @@ used (see Issue [39627][]).
* Added `Dart_TypeDynamic`, `Dart_TypeVoid` and `Dart_TypeNever`. Type dynamic
can no longer by reached by `Dart_GetType(dart:core, dynamic)`.
* Added the following methods to the VM embedding API:
* `Dart_GetNonNullableType`
* `Dart_GetNullableType`
* `Dart_TypeToNonNullable`
* `Dart_TypeToNullable`
* `Dart_IsLegacyType`
* `Dart_IsNonNullableType`
* `Dart_IsNullableType`
### Tools

View file

@ -3046,7 +3046,8 @@ DART_EXPORT Dart_Handle Dart_RootLibrary();
DART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library);
/**
* Lookup or instantiate a type by name and type arguments from a Library.
* Lookup or instantiate a legacy type by name and type arguments from a
* Library.
*
* \param library The library containing the class or interface.
* \param class_name The class name for the type.
@ -3063,6 +3064,78 @@ DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library,
intptr_t number_of_type_arguments,
Dart_Handle* type_arguments);
/**
* Lookup or instantiate a nullable type by name and type arguments from
* Library.
*
* \param library The library containing the class or interface.
* \param class_name The class name for the type.
* \param number_of_type_arguments Number of type arguments.
* For non parametric types the number of type arguments would be 0.
* \param type_arguments Pointer to an array of type arguments.
* For non parameteric types a NULL would be passed in for this argument.
*
* \return If no error occurs, the type is returned.
* Otherwise an error handle is returned.
*/
DART_EXPORT Dart_Handle Dart_GetNullableType(Dart_Handle library,
Dart_Handle class_name,
intptr_t number_of_type_arguments,
Dart_Handle* type_arguments);
/**
* Lookup or instantiate a non-nullable type by name and type arguments from
* Library.
*
* \param library The library containing the class or interface.
* \param class_name The class name for the type.
* \param number_of_type_arguments Number of type arguments.
* For non parametric types the number of type arguments would be 0.
* \param type_arguments Pointer to an array of type arguments.
* For non parameteric types a NULL would be passed in for this argument.
*
* \return If no error occurs, the type is returned.
* Otherwise an error handle is returned.
*/
DART_EXPORT Dart_Handle
Dart_GetNonNullableType(Dart_Handle library,
Dart_Handle class_name,
intptr_t number_of_type_arguments,
Dart_Handle* type_arguments);
/**
* Creates a nullable version of the provided type.
*
* \param type The type to be converted to a nullable type.
*
* \return If no error occurs, a nullable type is returned.
* Otherwise an error handle is returned.
*/
DART_EXPORT Dart_Handle Dart_TypeToNullableType(Dart_Handle type);
/**
* Creates a non-nullable version of the provided type.
*
* \param type The type to be converted to a non-nullable type.
*
* \return If no error occurs, a non-nullable type is returned.
* Otherwise an error handle is returned.
*/
DART_EXPORT Dart_Handle Dart_TypeToNonNullableType(Dart_Handle type);
/**
* A type's nullability.
*
* \param type A Dart type.
* \param result An out parameter containing the result of the check. True if
* the type is of the specified nullability, false otherwise.
*
* \return Returns an error handle if type is not of type Type.
*/
DART_EXPORT Dart_Handle Dart_IsNullableType(Dart_Handle type, bool* result);
DART_EXPORT Dart_Handle Dart_IsNonNullableType(Dart_Handle type, bool* result);
DART_EXPORT Dart_Handle Dart_IsLegacyType(Dart_Handle type, bool* result);
/**
* Lookup a class or interface by name from a Library.
*

View file

@ -5261,12 +5261,12 @@ DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library,
return Api::NewHandle(T, cls.RareType());
}
DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library,
Dart_Handle class_name,
intptr_t number_of_type_arguments,
Dart_Handle* type_arguments) {
static Dart_Handle GetTypeCommon(Dart_Handle library,
Dart_Handle class_name,
intptr_t number_of_type_arguments,
Dart_Handle* type_arguments,
Nullability nullability) {
DARTSCOPE(Thread::Current());
// Validate the input arguments.
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
if (lib.IsNull()) {
@ -5288,6 +5288,8 @@ DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library,
}
cls.EnsureDeclarationLoaded();
CHECK_ERROR_HANDLE(cls.VerifyEntryPoint());
Type& type = Type::Handle();
if (cls.NumTypeArguments() == 0) {
if (number_of_type_arguments != 0) {
return Api::NewError(
@ -5295,44 +5297,116 @@ DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library,
"got %" Pd " expected 0",
number_of_type_arguments);
}
return Api::NewHandle(T, Type::NewNonParameterizedType(cls));
}
intptr_t num_expected_type_arguments = cls.NumTypeParameters();
TypeArguments& type_args_obj = TypeArguments::Handle();
if (number_of_type_arguments > 0) {
if (type_arguments == NULL) {
RETURN_NULL_ERROR(type_arguments);
type ^= Type::NewNonParameterizedType(cls);
type ^= type.ToNullability(nullability, Heap::kOld);
} else {
intptr_t num_expected_type_arguments = cls.NumTypeParameters();
TypeArguments& type_args_obj = TypeArguments::Handle();
if (number_of_type_arguments > 0) {
if (type_arguments == NULL) {
RETURN_NULL_ERROR(type_arguments);
}
if (num_expected_type_arguments != number_of_type_arguments) {
return Api::NewError(
"Invalid number of type arguments specified, "
"got %" Pd " expected %" Pd,
number_of_type_arguments, num_expected_type_arguments);
}
const Array& array = Api::UnwrapArrayHandle(Z, *type_arguments);
if (array.IsNull()) {
RETURN_TYPE_ERROR(Z, *type_arguments, Array);
}
if (array.Length() != num_expected_type_arguments) {
return Api::NewError(
"Invalid type arguments specified, expected an "
"array of len %" Pd " but got an array of len %" Pd,
number_of_type_arguments, array.Length());
}
// Set up the type arguments array.
type_args_obj = TypeArguments::New(num_expected_type_arguments);
AbstractType& type_arg = AbstractType::Handle();
for (intptr_t i = 0; i < number_of_type_arguments; i++) {
type_arg ^= array.At(i);
type_args_obj.SetTypeAt(i, type_arg);
}
}
if (num_expected_type_arguments != number_of_type_arguments) {
return Api::NewError(
"Invalid number of type arguments specified, "
"got %" Pd " expected %" Pd,
number_of_type_arguments, num_expected_type_arguments);
}
const Array& array = Api::UnwrapArrayHandle(Z, *type_arguments);
if (array.IsNull()) {
RETURN_TYPE_ERROR(Z, *type_arguments, Array);
}
if (array.Length() != num_expected_type_arguments) {
return Api::NewError(
"Invalid type arguments specified, expected an "
"array of len %" Pd " but got an array of len %" Pd,
number_of_type_arguments, array.Length());
}
// Set up the type arguments array.
type_args_obj = TypeArguments::New(num_expected_type_arguments);
AbstractType& type_arg = AbstractType::Handle();
for (intptr_t i = 0; i < number_of_type_arguments; i++) {
type_arg ^= array.At(i);
type_args_obj.SetTypeAt(i, type_arg);
}
}
// Construct the type object, canonicalize it and return.
Type& instantiated_type =
Type::Handle(Type::New(cls, type_args_obj, TokenPosition::kNoSource));
instantiated_type ^= ClassFinalizer::FinalizeType(cls, instantiated_type);
return Api::NewHandle(T, instantiated_type.raw());
// Construct the type object, canonicalize it and return.
type ^=
Type::New(cls, type_args_obj, TokenPosition::kNoSource, nullability);
}
type ^= ClassFinalizer::FinalizeType(cls, type);
return Api::NewHandle(T, type.raw());
}
DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library,
Dart_Handle class_name,
intptr_t number_of_type_arguments,
Dart_Handle* type_arguments) {
return GetTypeCommon(library, class_name, number_of_type_arguments,
type_arguments, Nullability::kLegacy);
}
DART_EXPORT Dart_Handle Dart_GetNullableType(Dart_Handle library,
Dart_Handle class_name,
intptr_t number_of_type_arguments,
Dart_Handle* type_arguments) {
return GetTypeCommon(library, class_name, number_of_type_arguments,
type_arguments, Nullability::kNullable);
}
DART_EXPORT Dart_Handle
Dart_GetNonNullableType(Dart_Handle library,
Dart_Handle class_name,
intptr_t number_of_type_arguments,
Dart_Handle* type_arguments) {
return GetTypeCommon(library, class_name, number_of_type_arguments,
type_arguments, Nullability::kNonNullable);
}
static Dart_Handle TypeToHelper(Dart_Handle type, Nullability nullability) {
DARTSCOPE(Thread::Current());
const Type& ty = Api::UnwrapTypeHandle(Z, type);
if (ty.IsNull()) {
RETURN_TYPE_ERROR(Z, type, Type);
}
if (ty.nullability() == nullability) {
return type;
}
return Api::NewHandle(T, ty.ToNullability(nullability, Heap::kOld));
}
DART_EXPORT Dart_Handle Dart_TypeToNullableType(Dart_Handle type) {
return TypeToHelper(type, Nullability::kNullable);
}
DART_EXPORT Dart_Handle Dart_TypeToNonNullableType(Dart_Handle type) {
return TypeToHelper(type, Nullability::kNonNullable);
}
static Dart_Handle IsOfTypeNullabilityHelper(Dart_Handle type,
Nullability nullability,
bool* result) {
DARTSCOPE(Thread::Current());
const Type& ty = Api::UnwrapTypeHandle(Z, type);
if (ty.IsNull()) {
*result = false;
RETURN_TYPE_ERROR(Z, type, Type);
}
*result = (ty.nullability() == nullability);
return Api::Success();
}
DART_EXPORT Dart_Handle Dart_IsNullableType(Dart_Handle type, bool* result) {
return IsOfTypeNullabilityHelper(type, Nullability::kNullable, result);
}
DART_EXPORT Dart_Handle Dart_IsNonNullableType(Dart_Handle type, bool* result) {
return IsOfTypeNullabilityHelper(type, Nullability::kNonNullable, result);
}
DART_EXPORT Dart_Handle Dart_IsLegacyType(Dart_Handle type, bool* result) {
return IsOfTypeNullabilityHelper(type, Nullability::kLegacy, result);
}
DART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library) {

View file

@ -3847,15 +3847,20 @@ TEST_CASE(DartAPI_TypeGetNonParamtericTypes) {
bool instanceOf = false;
// First get the type objects of these non parameterized types.
Dart_Handle type0 = Dart_GetType(lib, NewString("MyClass0"), 0, NULL);
Dart_Handle type0 =
Dart_GetNonNullableType(lib, NewString("MyClass0"), 0, NULL);
EXPECT_VALID(type0);
Dart_Handle type1 = Dart_GetType(lib, NewString("MyClass1"), 0, NULL);
Dart_Handle type1 =
Dart_GetNonNullableType(lib, NewString("MyClass1"), 0, NULL);
EXPECT_VALID(type1);
Dart_Handle type2 = Dart_GetType(lib, NewString("MyClass2"), 0, NULL);
Dart_Handle type2 =
Dart_GetNonNullableType(lib, NewString("MyClass2"), 0, NULL);
EXPECT_VALID(type2);
Dart_Handle type3 = Dart_GetType(lib, NewString("MyInterface0"), 0, NULL);
Dart_Handle type3 =
Dart_GetNonNullableType(lib, NewString("MyInterface0"), 0, NULL);
EXPECT_VALID(type3);
Dart_Handle type4 = Dart_GetType(lib, NewString("MyInterface1"), 0, NULL);
Dart_Handle type4 =
Dart_GetNonNullableType(lib, NewString("MyInterface1"), 0, NULL);
EXPECT_VALID(type4);
// Now create objects of these non parameterized types and check
@ -5857,6 +5862,52 @@ TEST_CASE(DartAPI_GetNativeArgumentCount) {
EXPECT_EQ(3, value);
}
TEST_CASE(DartAPI_TypeToNullability) {
const char* kScriptChars =
"library testlib;\n"
"class Class {\n"
" static var name = 'Class';\n"
"}\n";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
const Dart_Handle name = NewString("Class");
// Lookup the legacy type for Class.
Dart_Handle type = Dart_GetType(lib, name, 0, NULL);
EXPECT_VALID(type);
bool result = false;
EXPECT_VALID(Dart_IsLegacyType(type, &result));
EXPECT(result);
// Legacy -> Nullable
Dart_Handle nullableType = Dart_TypeToNullableType(type);
EXPECT_VALID(nullableType);
result = false;
EXPECT_VALID(Dart_IsNullableType(nullableType, &result));
EXPECT(result);
EXPECT(Dart_IdentityEquals(nullableType,
Dart_GetNullableType(lib, name, 0, nullptr)));
// Legacy -> Non-Nullable
Dart_Handle nonNullableType = Dart_TypeToNonNullableType(type);
EXPECT_VALID(nonNullableType);
result = false;
EXPECT_VALID(Dart_IsNonNullableType(nonNullableType, &result));
EXPECT(result);
EXPECT(Dart_IdentityEquals(nonNullableType,
Dart_GetNonNullableType(lib, name, 0, nullptr)));
// Nullable -> Non-Nullable
EXPECT(Dart_IdentityEquals(
nonNullableType,
Dart_TypeToNonNullableType(Dart_GetNullableType(lib, name, 0, nullptr))));
// Nullable -> Non-Nullable
EXPECT(Dart_IdentityEquals(
nullableType,
Dart_TypeToNullableType(Dart_GetNonNullableType(lib, name, 0, nullptr))));
}
TEST_CASE(DartAPI_GetType) {
const char* kScriptChars =
"library testlib;\n"
@ -5873,6 +5924,9 @@ TEST_CASE(DartAPI_GetType) {
// Lookup a class.
Dart_Handle type = Dart_GetType(lib, NewString("Class"), 0, NULL);
EXPECT_VALID(type);
bool result = false;
EXPECT_VALID(Dart_IsLegacyType(type, &result));
EXPECT(result);
Dart_Handle name = Dart_GetField(type, NewString("name"));
EXPECT_VALID(name);
const char* name_cstr = "";
@ -5882,6 +5936,9 @@ TEST_CASE(DartAPI_GetType) {
// Lookup a private class.
type = Dart_GetType(lib, NewString("_Class"), 0, NULL);
EXPECT_VALID(type);
result = false;
EXPECT_VALID(Dart_IsLegacyType(type, &result));
EXPECT(result);
name = Dart_GetField(type, NewString("name"));
EXPECT_VALID(name);
name_cstr = "";
@ -5905,6 +5962,127 @@ TEST_CASE(DartAPI_GetType) {
EXPECT_STREQ("myerror", Dart_GetError(type));
}
TEST_CASE(DartAPI_GetNullableType) {
const char* kScriptChars =
"library testlib;\n"
"class Class {\n"
" static var name = 'Class';\n"
"}\n"
"\n"
"class _Class {\n"
" static var name = '_Class';\n"
"}\n";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
// Lookup a class.
Dart_Handle type = Dart_GetNullableType(lib, NewString("Class"), 0, NULL);
EXPECT_VALID(type);
bool result = false;
EXPECT_VALID(Dart_IsNullableType(type, &result));
EXPECT(result);
Dart_Handle name = Dart_ToString(type);
EXPECT_VALID(name);
const char* name_cstr = "";
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
EXPECT_STREQ("Class", name_cstr);
name = Dart_GetField(type, NewString("name"));
EXPECT_VALID(name);
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
EXPECT_STREQ("Class", name_cstr);
// Lookup a private class.
type = Dart_GetNullableType(lib, NewString("_Class"), 0, NULL);
EXPECT_VALID(type);
result = false;
EXPECT_VALID(Dart_IsNullableType(type, &result));
name = Dart_GetField(type, NewString("name"));
EXPECT_VALID(name);
name_cstr = "";
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
EXPECT_STREQ("_Class", name_cstr);
// Lookup a class that does not exist.
type = Dart_GetNullableType(lib, NewString("DoesNotExist"), 0, NULL);
EXPECT(Dart_IsError(type));
EXPECT_STREQ("Type 'DoesNotExist' not found in library 'testlib'.",
Dart_GetError(type));
// Lookup a class from an error library. The error propagates.
type = Dart_GetNullableType(Api::NewError("myerror"), NewString("Class"), 0,
NULL);
EXPECT(Dart_IsError(type));
EXPECT_STREQ("myerror", Dart_GetError(type));
// Lookup a type using an error class name. The error propagates.
type = Dart_GetNullableType(lib, Api::NewError("myerror"), 0, NULL);
EXPECT(Dart_IsError(type));
EXPECT_STREQ("myerror", Dart_GetError(type));
}
TEST_CASE(DartAPI_GetNonNullableType) {
const char* kScriptChars =
"library testlib;\n"
"class Class {\n"
" static var name = 'Class';\n"
"}\n"
"\n"
"class _Class {\n"
" static var name = '_Class';\n"
"}\n";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
// Lookup a class.
Dart_Handle type = Dart_GetNonNullableType(lib, NewString("Class"), 0, NULL);
EXPECT_VALID(type);
bool result = false;
EXPECT_VALID(Dart_IsNonNullableType(type, &result));
EXPECT(result);
Dart_Handle name = Dart_ToString(type);
EXPECT_VALID(name);
const char* name_cstr = "";
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
EXPECT_STREQ("Class", name_cstr);
name = Dart_GetField(type, NewString("name"));
EXPECT_VALID(name);
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
EXPECT_STREQ("Class", name_cstr);
// Lookup a private class.
type = Dart_GetNonNullableType(lib, NewString("_Class"), 0, NULL);
EXPECT_VALID(type);
result = false;
EXPECT_VALID(Dart_IsNonNullableType(type, &result));
EXPECT(result);
name = Dart_GetField(type, NewString("name"));
EXPECT_VALID(name);
name_cstr = "";
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
EXPECT_STREQ("_Class", name_cstr);
// Lookup a class that does not exist.
type = Dart_GetNonNullableType(lib, NewString("DoesNotExist"), 0, NULL);
EXPECT(Dart_IsError(type));
EXPECT_STREQ("Type 'DoesNotExist' not found in library 'testlib'.",
Dart_GetError(type));
// Lookup a class from an error library. The error propagates.
type = Dart_GetNonNullableType(Api::NewError("myerror"), NewString("Class"),
0, NULL);
EXPECT(Dart_IsError(type));
EXPECT_STREQ("myerror", Dart_GetError(type));
// Lookup a type using an error class name. The error propagates.
type = Dart_GetNonNullableType(lib, Api::NewError("myerror"), 0, NULL);
EXPECT(Dart_IsError(type));
EXPECT_STREQ("myerror", Dart_GetError(type));
}
TEST_CASE(DartAPI_InstanceOf) {
const char* kScriptChars =
"class OtherClass {\n"