blob: 1f939b593a8cdbea69df3887fd45bc65fcf0754e [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "include/dart_api.h"
#include "platform/assert.h"
#include "platform/json.h"
#include "platform/utils.h"
#include "vm/class_finalizer.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_api_state.h"
#include "vm/thread.h"
#include "vm/unit_test.h"
#include "vm/verifier.h"
namespace dart {
DECLARE_FLAG(bool, enable_type_checks);
#if defined(TARGET_ARCH_IA32) || \
defined(TARGET_ARCH_X64) || \
defined(TARGET_ARCH_ARM)
TEST_CASE(ErrorHandleBasics) {
const char* kScriptChars =
"void testMain() {\n"
" throw new Exception(\"bad news\");\n"
"}\n";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
Dart_Handle instance = Dart_True();
Dart_Handle error = Api::NewError("myerror");
Dart_Handle exception = Dart_Invoke(lib,
NewString("testMain"),
0,
NULL);
EXPECT_VALID(instance);
EXPECT(Dart_IsError(error));
EXPECT(Dart_IsError(exception));
EXPECT(!Dart_ErrorHasException(instance));
EXPECT(!Dart_ErrorHasException(error));
EXPECT(Dart_ErrorHasException(exception));
EXPECT_STREQ("", Dart_GetError(instance));
EXPECT_STREQ("myerror", Dart_GetError(error));
EXPECT_STREQ(
"Unhandled exception:\n"
"Exception: bad news\n"
"#0 testMain (dart:test-lib:2:3)",
Dart_GetError(exception));
EXPECT(Dart_IsError(Dart_ErrorGetException(instance)));
EXPECT(Dart_IsError(Dart_ErrorGetException(error)));
EXPECT_VALID(Dart_ErrorGetException(exception));
EXPECT(Dart_IsError(Dart_ErrorGetStacktrace(instance)));
EXPECT(Dart_IsError(Dart_ErrorGetStacktrace(error)));
EXPECT_VALID(Dart_ErrorGetStacktrace(exception));
}
TEST_CASE(ErrorHandleTypes) {
Isolate* isolate = Isolate::Current();
const String& compile_message = String::Handle(String::New("CompileError"));
const String& fatal_message = String::Handle(String::New("FatalError"));
Dart_Handle not_error = NewString("NotError");
Dart_Handle api_error = Dart_NewApiError("Api%s", "Error");
Dart_Handle exception_error =
Dart_NewUnhandledExceptionError(NewString("ExceptionError"));
Dart_Handle compile_error =
Api::NewHandle(isolate, LanguageError::New(compile_message));
Dart_Handle fatal_error =
Api::NewHandle(isolate, UnwindError::New(fatal_message));
EXPECT_VALID(not_error);
EXPECT(Dart_IsError(api_error));
EXPECT(Dart_IsError(exception_error));
EXPECT(Dart_IsError(compile_error));
EXPECT(Dart_IsError(fatal_error));
EXPECT(!Dart_IsApiError(not_error));
EXPECT(Dart_IsApiError(api_error));
EXPECT(!Dart_IsApiError(exception_error));
EXPECT(!Dart_IsApiError(compile_error));
EXPECT(!Dart_IsApiError(fatal_error));
EXPECT(!Dart_IsUnhandledExceptionError(not_error));
EXPECT(!Dart_IsUnhandledExceptionError(api_error));
EXPECT(Dart_IsUnhandledExceptionError(exception_error));
EXPECT(!Dart_IsUnhandledExceptionError(compile_error));
EXPECT(!Dart_IsUnhandledExceptionError(fatal_error));
EXPECT(!Dart_IsCompilationError(not_error));
EXPECT(!Dart_IsCompilationError(api_error));
EXPECT(!Dart_IsCompilationError(exception_error));
EXPECT(Dart_IsCompilationError(compile_error));
EXPECT(!Dart_IsCompilationError(fatal_error));
EXPECT(!Dart_IsFatalError(not_error));
EXPECT(!Dart_IsFatalError(api_error));
EXPECT(!Dart_IsFatalError(exception_error));
EXPECT(!Dart_IsFatalError(compile_error));
EXPECT(Dart_IsFatalError(fatal_error));
EXPECT_STREQ("", Dart_GetError(not_error));
EXPECT_STREQ("ApiError", Dart_GetError(api_error));
EXPECT_SUBSTRING("Unhandled exception:\nExceptionError",
Dart_GetError(exception_error));
EXPECT_STREQ("CompileError", Dart_GetError(compile_error));
EXPECT_STREQ("FatalError", Dart_GetError(fatal_error));
}
void PropagateErrorNative(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle closure = Dart_GetNativeArgument(args, 0);
EXPECT(Dart_IsClosure(closure));
Dart_Handle result = Dart_InvokeClosure(closure, 0, NULL);
EXPECT(Dart_IsError(result));
result = Dart_PropagateError(result);
EXPECT_VALID(result); // We do not expect to reach here.
UNREACHABLE();
}
static Dart_NativeFunction PropagateError_native_lookup(
Dart_Handle name, int argument_count) {
return reinterpret_cast<Dart_NativeFunction>(&PropagateErrorNative);
}
TEST_CASE(Dart_PropagateError) {
const char* kScriptChars =
"raiseCompileError() {\n"
" return missing_semicolon\n"
"}\n"
"\n"
"void throwException() {\n"
" throw new Exception('myException');\n"
"}\n"
"\n"
"void nativeFunc(closure) native 'Test_nativeFunc';\n"
"\n"
"void Func1() {\n"
" nativeFunc(() => raiseCompileError());\n"
"}\n"
"\n"
"void Func2() {\n"
" nativeFunc(() => throwException());\n"
"}\n";
Dart_Handle lib = TestCase::LoadTestScript(
kScriptChars, &PropagateError_native_lookup);
Dart_Handle result;
result = Dart_Invoke(lib, NewString("Func1"), 0, NULL);
EXPECT(Dart_IsError(result));
EXPECT(!Dart_ErrorHasException(result));
EXPECT_SUBSTRING("semicolon expected", Dart_GetError(result));
result = Dart_Invoke(lib, NewString("Func2"), 0, NULL);
EXPECT(Dart_IsError(result));
EXPECT(Dart_ErrorHasException(result));
EXPECT_SUBSTRING("myException", Dart_GetError(result));
}
#endif // TARGET_ARCH_IA32 || TARGET_ARCH_X64 || TARGET_ARCH_ARM
TEST_CASE(Dart_Error) {
Dart_Handle error = Dart_Error("An %s", "error");
EXPECT(Dart_IsError(error));
EXPECT_STREQ("An error", Dart_GetError(error));
}
TEST_CASE(Null) {
Dart_Handle null = Dart_Null();
EXPECT_VALID(null);
EXPECT(Dart_IsNull(null));
Dart_Handle str = NewString("test");
EXPECT_VALID(str);
EXPECT(!Dart_IsNull(str));
}
TEST_CASE(IdentityEquals) {
Dart_Handle five = NewString("5");
Dart_Handle five_again = NewString("5");
Dart_Handle seven = NewString("7");
// Same objects.
EXPECT(Dart_IdentityEquals(five, five));
// Equal objects.
EXPECT(!Dart_IdentityEquals(five, five_again));
// Different objects.
EXPECT(!Dart_IdentityEquals(five, seven));
// Non-instance objects.
{
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
Dart_Handle class1 = Api::NewHandle(isolate, Object::null_class());
Dart_Handle class2 = Api::NewHandle(isolate, Object::class_class());
EXPECT(Dart_IdentityEquals(class1, class1));
EXPECT(!Dart_IdentityEquals(class1, class2));
}
}
#if defined(TARGET_ARCH_IA32) || \
defined(TARGET_ARCH_X64) || \
defined(TARGET_ARCH_ARM)
TEST_CASE(ObjectEquals) {
bool equal = false;
Dart_Handle five = NewString("5");
Dart_Handle five_again = NewString("5");
Dart_Handle seven = NewString("7");
// Same objects.
EXPECT_VALID(Dart_ObjectEquals(five, five, &equal));
EXPECT(equal);
// Equal objects.
EXPECT_VALID(Dart_ObjectEquals(five, five_again, &equal));
EXPECT(equal);
// Different objects.
EXPECT_VALID(Dart_ObjectEquals(five, seven, &equal));
EXPECT(!equal);
}
#endif // TARGET_ARCH_IA32 || TARGET_ARCH_X64 || TARGET_ARCH_ARM
TEST_CASE(InstanceValues) {
EXPECT(Dart_IsInstance(NewString("test")));
EXPECT(Dart_IsInstance(Dart_True()));
// By convention, our Is*() functions exclude null.
EXPECT(!Dart_IsInstance(Dart_Null()));
}
TEST_CASE(InstanceGetClass) {
// Get the handle from a valid instance handle.
Dart_Handle instance = Dart_True();
Dart_Handle cls = Dart_InstanceGetClass(instance);
EXPECT_VALID(cls);
EXPECT(Dart_IsClass(cls));
Dart_Handle cls_name = Dart_ClassName(cls);
EXPECT_VALID(cls_name);
const char* cls_name_cstr = "";
EXPECT_VALID(Dart_StringToCString(cls_name, &cls_name_cstr));
EXPECT_STREQ("bool", cls_name_cstr);
// Errors propagate.
Dart_Handle error = Dart_NewApiError("MyError");
Dart_Handle error_cls = Dart_InstanceGetClass(error);
EXPECT_ERROR(error_cls, "MyError");
// Get the handle from a non-instance handle
ASSERT(Dart_IsClass(cls));
Dart_Handle cls_cls = Dart_InstanceGetClass(cls);
EXPECT_ERROR(cls_cls,
"Dart_InstanceGetClass expects argument 'instance' to be of "
"type Instance.");
}
TEST_CASE(BooleanValues) {
Dart_Handle str = NewString("test");
EXPECT(!Dart_IsBoolean(str));
bool value = false;
Dart_Handle result = Dart_BooleanValue(str, &value);
EXPECT(Dart_IsError(result));
Dart_Handle val1 = Dart_NewBoolean(true);
EXPECT(Dart_IsBoolean(val1));
result = Dart_BooleanValue(val1, &value);
EXPECT_VALID(result);
EXPECT(value);
Dart_Handle val2 = Dart_NewBoolean(false);
EXPECT(Dart_IsBoolean(val2));
result = Dart_BooleanValue(val2, &value);
EXPECT_VALID(result);
EXPECT(!value);
}
TEST_CASE(BooleanConstants) {
Dart_Handle true_handle = Dart_True();
EXPECT_VALID(true_handle);
EXPECT(Dart_IsBoolean(true_handle));
bool value = false;
Dart_Handle result = Dart_BooleanValue(true_handle, &value);
EXPECT_VALID(result);
EXPECT(value);
Dart_Handle false_handle = Dart_False();
EXPECT_VALID(false_handle);
EXPECT(Dart_IsBoolean(false_handle));
result = Dart_BooleanValue(false_handle, &value);
EXPECT_VALID(result);
EXPECT(!value);
}
TEST_CASE(DoubleValues) {
const double kDoubleVal1 = 201.29;
const double kDoubleVal2 = 101.19;
Dart_Handle val1 = Dart_NewDouble(kDoubleVal1);
EXPECT(Dart_IsDouble(val1));
Dart_Handle val2 = Dart_NewDouble(kDoubleVal2);
EXPECT(Dart_IsDouble(val2));
double out1, out2;
Dart_Handle result = Dart_DoubleValue(val1, &out1);
EXPECT_VALID(result);
EXPECT_EQ(kDoubleVal1, out1);
result = Dart_DoubleValue(val2, &out2);
EXPECT_VALID(result);
EXPECT_EQ(kDoubleVal2, out2);
}
#if defined(TARGET_ARCH_IA32) || \
defined(TARGET_ARCH_X64) || \
defined(TARGET_ARCH_ARM)
TEST_CASE(NumberValues) {
// TODO(antonm): add various kinds of ints (smi, mint, bigint).
const char* kScriptChars =
"int getInt() { return 1; }\n"
"double getDouble() { return 1.0; }\n"
"bool getBool() { return false; }\n"
"getNull() { return null; }\n";
Dart_Handle result;
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
// Check int case.
result = Dart_Invoke(lib, NewString("getInt"), 0, NULL);
EXPECT_VALID(result);
EXPECT(Dart_IsNumber(result));
// Check double case.
result = Dart_Invoke(lib, NewString("getDouble"), 0, NULL);
EXPECT_VALID(result);
EXPECT(Dart_IsNumber(result));
// Check bool case.
result = Dart_Invoke(lib, NewString("getBool"), 0, NULL);
EXPECT_VALID(result);
EXPECT(!Dart_IsNumber(result));
// Check null case.
result = Dart_Invoke(lib, NewString("getNull"), 0, NULL);
EXPECT_VALID(result);
EXPECT(!Dart_IsNumber(result));
}
#endif // TARGET_ARCH_IA32 || TARGET_ARCH_X64 || TARGET_ARCH_ARM
TEST_CASE(IntegerValues) {
const int64_t kIntegerVal1 = 100;
const int64_t kIntegerVal2 = 0xffffffff;
const char* kIntegerVal3 = "0x123456789123456789123456789";
Dart_Handle val1 = Dart_NewInteger(kIntegerVal1);
EXPECT(Dart_IsInteger(val1));
bool fits = false;
Dart_Handle result = Dart_IntegerFitsIntoInt64(val1, &fits);
EXPECT_VALID(result);
EXPECT(fits);
Dart_Handle val2 = Dart_NewInteger(kIntegerVal2);
EXPECT(Dart_IsInteger(val2));
result = Dart_IntegerFitsIntoInt64(val2, &fits);
EXPECT_VALID(result);
EXPECT(fits);
Dart_Handle val3 = Dart_NewIntegerFromHexCString(kIntegerVal3);
EXPECT(Dart_IsInteger(val3));
result = Dart_IntegerFitsIntoInt64(val3, &fits);
EXPECT_VALID(result);
EXPECT(!fits);
int64_t out = 0;
result = Dart_IntegerToInt64(val1, &out);
EXPECT_VALID(result);
EXPECT_EQ(kIntegerVal1, out);
result = Dart_IntegerToInt64(val2, &out);
EXPECT_VALID(result);
EXPECT_EQ(kIntegerVal2, out);
const char* chars = NULL;
result = Dart_IntegerToHexCString(val3, &chars);
EXPECT_VALID(result);
EXPECT(!strcmp(kIntegerVal3, chars));
}
TEST_CASE(IntegerFitsIntoInt64) {
Dart_Handle max = Dart_NewInteger(kMaxInt64);
EXPECT(Dart_IsInteger(max));
bool fits = false;
Dart_Handle result = Dart_IntegerFitsIntoInt64(max, &fits);
EXPECT_VALID(result);
EXPECT(fits);
Dart_Handle above_max = Dart_NewIntegerFromHexCString("0x8000000000000000");
EXPECT(Dart_IsInteger(above_max));
fits = true;
result = Dart_IntegerFitsIntoInt64(above_max, &fits);
EXPECT_VALID(result);
EXPECT(!fits);
Dart_Handle min = Dart_NewInteger(kMaxInt64);
EXPECT(Dart_IsInteger(min));
fits = false;
result = Dart_IntegerFitsIntoInt64(min, &fits);
EXPECT_VALID(result);
EXPECT(fits);
Dart_Handle below_min = Dart_NewIntegerFromHexCString("-0x8000000000000001");
EXPECT(Dart_IsInteger(below_min));
fits = true;
result = Dart_IntegerFitsIntoInt64(below_min, &fits);
EXPECT_VALID(result);
EXPECT(!fits);
}
TEST_CASE(IntegerFitsIntoUint64) {
Dart_Handle max = Dart_NewIntegerFromHexCString("0xFFFFFFFFFFFFFFFF");
EXPECT(Dart_IsInteger(max));
bool fits = false;
Dart_Handle result = Dart_IntegerFitsIntoUint64(max, &fits);
EXPECT_VALID(result);
EXPECT(fits);
Dart_Handle above_max = Dart_NewIntegerFromHexCString("0x10000000000000000");
EXPECT(Dart_IsInteger(above_max));
fits = true;
result = Dart_IntegerFitsIntoUint64(above_max, &fits);
EXPECT_VALID(result);
EXPECT(!fits);
Dart_Handle min = Dart_NewInteger(0);
EXPECT(Dart_IsInteger(min));
fits = false;
result = Dart_IntegerFitsIntoUint64(min, &fits);
EXPECT_VALID(result);
EXPECT(fits);
Dart_Handle below_min = Dart_NewIntegerFromHexCString("-1");
EXPECT(Dart_IsInteger(below_min));
fits = true;
result = Dart_IntegerFitsIntoUint64(below_min, &fits);
EXPECT_VALID(result);
EXPECT(!fits);
}
TEST_CASE(ArrayValues) {
const int kArrayLength = 10;
Dart_Handle str = NewString("test");
EXPECT(!Dart_IsList(str));
Dart_Handle val = Dart_NewList(kArrayLength);
EXPECT(Dart_IsList(val));
intptr_t len = 0;
Dart_Handle result = Dart_ListLength(val, &len);
EXPECT_VALID(result);
EXPECT_EQ(kArrayLength, len);
// Check invalid array access.
result = Dart_ListSetAt(val, (kArrayLength + 10), Dart_NewInteger(10));
EXPECT(Dart_IsError(result));
result = Dart_ListSetAt(val, -10, Dart_NewInteger(10));
EXPECT(Dart_IsError(result));
result = Dart_ListGetAt(val, (kArrayLength + 10));
EXPECT(Dart_IsError(result));
result = Dart_ListGetAt(val, -10);
EXPECT(Dart_IsError(result));
for (int i = 0; i < kArrayLength; i++) {
result = Dart_ListSetAt(val, i, Dart_NewInteger(i));
EXPECT_VALID(result);
}
for (int i = 0; i < kArrayLength; i++) {
result = Dart_ListGetAt(val, i);
EXPECT_VALID(result);
int64_t value;
result = Dart_IntegerToInt64(result, &value);
EXPECT_VALID(result);
EXPECT_EQ(i, value);
}
}
TEST_CASE(IsString) {
uint8_t latin1[] = { 'o', 'n', 'e', 0xC2, 0xA2 };
Dart_Handle latin1str = Dart_NewStringFromUTF8(latin1, ARRAY_SIZE(latin1));
EXPECT_VALID(latin1str);
EXPECT(Dart_IsString(latin1str));
EXPECT(Dart_IsStringLatin1(latin1str));
EXPECT(!Dart_IsExternalString(latin1str));
intptr_t len = -1;
EXPECT_VALID(Dart_StringLength(latin1str, &len));
EXPECT_EQ(4, len);
uint8_t data8[] = { 'o', 'n', 'e', 0x7F };
Dart_Handle str8 = Dart_NewStringFromUTF8(data8, ARRAY_SIZE(data8));
EXPECT_VALID(str8);
EXPECT(Dart_IsString(str8));
EXPECT(Dart_IsStringLatin1(str8));
EXPECT(!Dart_IsExternalString(str8));
uint8_t latin1_array[] = {0, 0, 0, 0, 0};
len = 5;
Dart_Handle result = Dart_StringToLatin1(str8, latin1_array, &len);
EXPECT_VALID(result);
EXPECT_EQ(4, len);
EXPECT(latin1_array != NULL);
for (intptr_t i = 0; i < len; i++) {
EXPECT_EQ(data8[i], latin1_array[i]);
}
Dart_Handle ext8 = Dart_NewExternalLatin1String(data8, ARRAY_SIZE(data8),
NULL, NULL);
EXPECT_VALID(ext8);
EXPECT(Dart_IsString(ext8));
EXPECT(Dart_IsExternalString(ext8));
uint16_t data16[] = { 't', 'w', 'o', 0xFFFF };
Dart_Handle str16 = Dart_NewStringFromUTF16(data16, ARRAY_SIZE(data16));
EXPECT_VALID(str16);
EXPECT(Dart_IsString(str16));
EXPECT(!Dart_IsStringLatin1(str16));
EXPECT(!Dart_IsExternalString(str16));
Dart_Handle ext16 = Dart_NewExternalUTF16String(data16, ARRAY_SIZE(data16),
NULL, NULL);
EXPECT_VALID(ext16);
EXPECT(Dart_IsString(ext16));
EXPECT(Dart_IsExternalString(ext16));
int32_t data32[] = { 'f', 'o', 'u', 'r', 0x10FFFF };
Dart_Handle str32 = Dart_NewStringFromUTF32(data32, ARRAY_SIZE(data32));
EXPECT_VALID(str32);
EXPECT(Dart_IsString(str32));
EXPECT(!Dart_IsExternalString(str32));
}
TEST_CASE(NewString) {
const char* ascii = "string";
Dart_Handle ascii_str = NewString(ascii);
EXPECT_VALID(ascii_str);
EXPECT(Dart_IsString(ascii_str));
const char* null = NULL;
Dart_Handle null_str = NewString(null);
EXPECT(Dart_IsError(null_str));
uint8_t data[] = { 0xE4, 0xBA, 0x8c }; // U+4E8C.
Dart_Handle utf8_str = Dart_NewStringFromUTF8(data, ARRAY_SIZE(data));
EXPECT_VALID(utf8_str);
EXPECT(Dart_IsString(utf8_str));
uint8_t invalid[] = { 0xE4, 0xBA }; // underflow.
Dart_Handle invalid_str = Dart_NewStringFromUTF8(invalid,
ARRAY_SIZE(invalid));
EXPECT(Dart_IsError(invalid_str));
}
TEST_CASE(ExternalStringGetPeer) {
Dart_Handle result;
uint8_t data8[] = { 'o', 'n', 'e', 0xFF };
int peer_data = 123;
void* peer = NULL;
// Success.
Dart_Handle ext8 = Dart_NewExternalLatin1String(data8, ARRAY_SIZE(data8),
&peer_data, NULL);
EXPECT_VALID(ext8);
result = Dart_ExternalStringGetPeer(ext8, &peer);
EXPECT_VALID(result);
EXPECT_EQ(&peer_data, peer);
// NULL peer.
result = Dart_ExternalStringGetPeer(ext8, NULL);
EXPECT(Dart_IsError(result));
EXPECT_STREQ("Dart_ExternalStringGetPeer expects argument 'peer' to be "
"non-null.", Dart_GetError(result));
// String is not external.
peer = NULL;
uint8_t utf8_data8[] = { 'o', 'n', 'e', 0x7F };
Dart_Handle str8 = Dart_NewStringFromUTF8(utf8_data8, ARRAY_SIZE(data8));
EXPECT_VALID(str8);
result = Dart_ExternalStringGetPeer(str8, &peer);
EXPECT(Dart_IsError(result));
EXPECT_STREQ("Dart_ExternalStringGetPeer expects argument 'object' to be "
"an external String.", Dart_GetError(result));
EXPECT(peer == NULL);
// Not a String.
peer = NULL;
result = Dart_ExternalStringGetPeer(Dart_True(), &peer);
EXPECT(Dart_IsError(result));
EXPECT_STREQ("Dart_ExternalStringGetPeer expects argument 'object' to be "
"of type String.", Dart_GetError(result));
EXPECT(peer == NULL);
}
#if defined(TARGET_ARCH_IA32) || \
defined(TARGET_ARCH_X64) || \
defined(TARGET_ARCH_ARM)
static void ExternalStringCallbackFinalizer(void* peer) {
*static_cast<int*>(peer) *= 2;
}
TEST_CASE(ExternalStringCallback) {
int peer8 = 40;
int peer16 = 41;
{
Dart_EnterScope();
uint8_t data8[] = { 'h', 'e', 'l', 'l', 'o' };
Dart_Handle obj8 = Dart_NewExternalLatin1String(
data8,
ARRAY_SIZE(data8),
&peer8,
ExternalStringCallbackFinalizer);
EXPECT_VALID(obj8);
void* api_peer8 = NULL;
EXPECT_VALID(Dart_ExternalStringGetPeer(obj8, &api_peer8));
EXPECT_EQ(api_peer8, &peer8);
uint16_t data16[] = { 'h', 'e', 'l', 'l', 'o' };
Dart_Handle obj16 = Dart_NewExternalUTF16String(
data16,
ARRAY_SIZE(data16),
&peer16,
ExternalStringCallbackFinalizer);
EXPECT_VALID(obj16);
void* api_peer16 = NULL;
EXPECT_VALID(Dart_ExternalStringGetPeer(obj16, &api_peer16));
EXPECT_EQ(api_peer16, &peer16);
Dart_ExitScope();
}
EXPECT_EQ(40, peer8);
EXPECT_EQ(41, peer16);
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(40, peer8);
EXPECT_EQ(41, peer16);
Isolate::Current()->heap()->CollectGarbage(Heap::kNew);
EXPECT_EQ(80, peer8);
EXPECT_EQ(82, peer16);
}
TEST_CASE(ListAccess) {
const char* kScriptChars =
"List testMain() {"
" List a = new List();"
" a.add(10);"
" a.add(20);"
" a.add(30);"
" return a;"
"}"
""
"List immutable() {"
" return const [0, 1, 2];"
"}";
Dart_Handle result;
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
// Invoke a function which returns an object of type List.
result = Dart_Invoke(lib, NewString("testMain"), 0, NULL);
EXPECT_VALID(result);
// First ensure that the returned object is an array.
Dart_Handle list_access_test_obj = result;
EXPECT(Dart_IsList(list_access_test_obj));
// Get length of array object.
intptr_t len = 0;
result = Dart_ListLength(list_access_test_obj, &len);
EXPECT_VALID(result);
EXPECT_EQ(3, len);
// Access elements in the array.
int64_t value;
result = Dart_ListGetAt(list_access_test_obj, 0);
EXPECT_VALID(result);
result = Dart_IntegerToInt64(result, &value);
EXPECT_VALID(result);
EXPECT_EQ(10, value);
result = Dart_ListGetAt(list_access_test_obj, 1);
EXPECT_VALID(result);
result = Dart_IntegerToInt64(result, &value);
EXPECT_VALID(result);
EXPECT_EQ(20, value);
result = Dart_ListGetAt(list_access_test_obj, 2);
EXPECT_VALID(result);
result = Dart_IntegerToInt64(result, &value);
EXPECT_VALID(result);
EXPECT_EQ(30, value);
// Set some elements in the array.
result = Dart_ListSetAt(list_access_test_obj, 0, Dart_NewInteger(0));
EXPECT_VALID(result);
result = Dart_ListSetAt(list_access_test_obj, 1, Dart_NewInteger(1));
EXPECT_VALID(result);
result = Dart_ListSetAt(list_access_test_obj, 2, Dart_NewInteger(2));
EXPECT_VALID(result);
// Get length of array object.
result = Dart_ListLength(list_access_test_obj, &len);
EXPECT_VALID(result);
EXPECT_EQ(3, len);
// Now try and access these elements in the array.
result = Dart_ListGetAt(list_access_test_obj, 0);
EXPECT_VALID(result);
result = Dart_IntegerToInt64(result, &value);
EXPECT_VALID(result);
EXPECT_EQ(0, value);
result = Dart_ListGetAt(list_access_test_obj, 1);
EXPECT_VALID(result);
result = Dart_IntegerToInt64(result, &value);
EXPECT_VALID(result);
EXPECT_EQ(1, value);
result = Dart_ListGetAt(list_access_test_obj, 2);
EXPECT_VALID(result);
result = Dart_IntegerToInt64(result, &value);
EXPECT_VALID(result);
EXPECT_EQ(2, value);
uint8_t native_array[3];
result = Dart_ListGetAsBytes(list_access_test_obj, 0, native_array, 3);
EXPECT_VALID(result);
EXPECT_EQ(0, native_array[0]);
EXPECT_EQ(1, native_array[1]);
EXPECT_EQ(2, native_array[2]);
native_array[0] = 10;
native_array[1] = 20;
native_array[2] = 30;
result = Dart_ListSetAsBytes(list_access_test_obj, 0, native_array, 3);
EXPECT_VALID(result);
result = Dart_ListGetAsBytes(list_access_test_obj, 0, native_array, 3);
EXPECT_VALID(result);
EXPECT_EQ(10, native_array[0]);
EXPECT_EQ(20, native_array[1]);
EXPECT_EQ(30, native_array[2]);
result = Dart_ListGetAt(list_access_test_obj, 2);
EXPECT_VALID(result);
result = Dart_IntegerToInt64(result, &value);
EXPECT_VALID(result);
EXPECT_EQ(30, value);
// Check if we get an exception when accessing beyond limit.
result = Dart_ListGetAt(list_access_test_obj, 4);
EXPECT(Dart_IsError(result));
// Check that we get an exception (and not a fatal error) when
// calling ListSetAt and ListSetAsBytes with an immutable list.
list_access_test_obj = Dart_Invoke(lib, NewString("immutable"), 0, NULL);
EXPECT_VALID(list_access_test_obj);
EXPECT(Dart_IsList(list_access_test_obj));
result = Dart_ListSetAsBytes(list_access_test_obj, 0, native_array, 3);
EXPECT(Dart_IsError(result));
EXPECT(Dart_IsUnhandledExceptionError(result));
result = Dart_ListSetAt(list_access_test_obj, 0, Dart_NewInteger(42));
EXPECT(Dart_IsError(result));
EXPECT(Dart_IsUnhandledExceptionError(result));
}
TEST_CASE(TypedDataAccess) {
EXPECT_EQ(kInvalid, Dart_GetTypeOfTypedData(Dart_True()));
EXPECT_EQ(kInvalid, Dart_GetTypeOfExternalTypedData(Dart_False()));
Dart_Handle byte_array1 = Dart_NewTypedData(kUint8, 10);
EXPECT_VALID(byte_array1);
EXPECT_EQ(kUint8, Dart_GetTypeOfTypedData(byte_array1));
EXPECT_EQ(kInvalid, Dart_GetTypeOfExternalTypedData(byte_array1));
EXPECT(Dart_IsList(byte_array1));
intptr_t length = 0;
Dart_Handle result = Dart_ListLength(byte_array1, &length);
EXPECT_VALID(result);
EXPECT_EQ(10, length);
result = Dart_ListSetAt(byte_array1, -1, Dart_NewInteger(1));
EXPECT(Dart_IsError(result));
result = Dart_ListSetAt(byte_array1, 10, Dart_NewInteger(1));
EXPECT(Dart_IsError(result));
// Set through the List API.
for (intptr_t i = 0; i < 10; ++i) {
EXPECT_VALID(Dart_ListSetAt(byte_array1, i, Dart_NewInteger(i + 1)));
}
for (intptr_t i = 0; i < 10; ++i) {
// Get through the List API.
Dart_Handle integer_obj = Dart_ListGetAt(byte_array1, i);
EXPECT_VALID(integer_obj);
int64_t int64_t_value = -1;
EXPECT_VALID(Dart_IntegerToInt64(integer_obj, &int64_t_value));
EXPECT_EQ(i + 1, int64_t_value);
}
Dart_Handle byte_array2 = Dart_NewTypedData(kUint8, 10);
bool is_equal = false;
Dart_ObjectEquals(byte_array1, byte_array2, &is_equal);
EXPECT(!is_equal);
// Set through the List API.
for (intptr_t i = 0; i < 10; ++i) {
result = Dart_ListSetAt(byte_array1, i, Dart_NewInteger(i + 2));
EXPECT_VALID(result);
result = Dart_ListSetAt(byte_array2, i, Dart_NewInteger(i + 2));
EXPECT_VALID(result);
}
for (intptr_t i = 0; i < 10; ++i) {
// Get through the List API.
Dart_Handle e1 = Dart_ListGetAt(byte_array1, i);
Dart_Handle e2 = Dart_ListGetAt(byte_array2, i);
is_equal = false;
Dart_ObjectEquals(e1, e2, &is_equal);
EXPECT(is_equal);
}
uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (intptr_t i = 0; i < 10; ++i) {
EXPECT_VALID(Dart_ListSetAt(byte_array1, i, Dart_NewInteger(10 - i)));
}
Dart_ListGetAsBytes(byte_array1, 0, data, 10);
for (intptr_t i = 0; i < 10; ++i) {
Dart_Handle integer_obj = Dart_ListGetAt(byte_array1, i);
EXPECT_VALID(integer_obj);
int64_t int64_t_value = -1;
EXPECT_VALID(Dart_IntegerToInt64(integer_obj, &int64_t_value));
EXPECT_EQ(10 - i, int64_t_value);
}
}
static int kLength = 16;
static void ByteDataNativeFunction(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle byte_data = Dart_NewTypedData(kByteData, kLength);
EXPECT_VALID(byte_data);
EXPECT_EQ(kByteData, Dart_GetTypeOfTypedData(byte_data));
Dart_SetReturnValue(args, byte_data);
Dart_ExitScope();
}
static Dart_NativeFunction ByteDataNativeResolver(Dart_Handle name,
int arg_count) {
return &ByteDataNativeFunction;
}
TEST_CASE(ByteDataAccess) {
const char* kScriptChars =
"import 'dart:typed_data';\n"
"class Expect {\n"
" static equals(a, b) {\n"
" if (a != b) {\n"
" throw 'not equal. expected: $a, got: $b';\n"
" }\n"
" }\n"
"}\n"
"ByteData createByteData() native 'CreateByteData';"
"ByteData main() {"
" var length = 16;"
" var a = createByteData();"
" Expect.equals(length, a.lengthInBytes);"
" for (int i = 0; i < length; i+=1) {"
" a.setInt8(i, 0x42);"
" }"
" for (int i = 0; i < length; i+=2) {"
" Expect.equals(0x4242, a.getInt16(i));"
" }"
" return a;"
"}\n";
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
Dart_Handle result = Dart_SetNativeResolver(lib, &ByteDataNativeResolver);
EXPECT_VALID(result);
// Invoke 'main' function.
result = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(result);
}
static const intptr_t kExtLength = 16;
static int8_t data[kExtLength] = { 0x41, 0x42, 0x41, 0x42,
0x41, 0x42, 0x41, 0x42,
0x41, 0x42, 0x41, 0x42,
0x41, 0x42, 0x41, 0x42, };
static void ExternalByteDataNativeFunction(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle external_byte_data = Dart_NewExternalTypedData(kByteData,
data,
16,
NULL, NULL);
EXPECT_VALID(external_byte_data);
EXPECT_EQ(kByteData, Dart_GetTypeOfTypedData(external_byte_data));
Dart_SetReturnValue(args, external_byte_data);
Dart_ExitScope();
}
static Dart_NativeFunction ExternalByteDataNativeResolver(Dart_Handle name,
int arg_count) {
return &ExternalByteDataNativeFunction;
}
TEST_CASE(ExternalByteDataAccess) {
// TODO(asiva): Once we have getInt16LE and getInt16BE support use the
// appropriate getter instead of the host endian format used now.
const char* kScriptChars =
"import 'dart:typed_data';\n"
"class Expect {\n"
" static equals(a, b) {\n"
" if (a != b) {\n"
" throw 'not equal. expected: $a, got: $b';\n"
" }\n"
" }\n"
"}\n"
"ByteData createExternalByteData() native 'CreateExternalByteData';"
"ByteData main() {"
" var length = 16;"
" var a = createExternalByteData();"
" Expect.equals(length, a.lengthInBytes);"
" for (int i = 0; i < length; i+=2) {"
" Expect.equals(0x4241, a.getInt16(i, Endianness.LITTLE_ENDIAN));"
" }"
" for (int i = 0; i < length; i+=2) {"
" a.setInt8(i, 0x24);"
" a.setInt8(i + 1, 0x28);"
" }"
" for (int i = 0; i < length; i+=2) {"
" Expect.equals(0x2824, a.getInt16(i, Endianness.LITTLE_ENDIAN));"
" }"
" return a;"
"}\n";
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
Dart_Handle result = Dart_SetNativeResolver(lib,
&ExternalByteDataNativeResolver);
EXPECT_VALID(result);
// Invoke 'main' function.
result = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(result);
for (intptr_t i = 0; i < kExtLength; i+=2) {
EXPECT_EQ(0x24, data[i]);
EXPECT_EQ(0x28, data[i+1]);
}
}
TEST_CASE(TypedDataDirectAccess) {
Dart_Handle str = Dart_NewStringFromCString("junk");
Dart_Handle byte_array = Dart_NewTypedData(kUint8, 10);
EXPECT_VALID(byte_array);
Dart_Handle result;
result = Dart_TypedDataAcquireData(byte_array, NULL, NULL, NULL);
EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'type'"
" to be non-null.");
Dart_TypedData_Type type;
result = Dart_TypedDataAcquireData(byte_array, &type, NULL, NULL);
EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'data'"
" to be non-null.");
void* data;
result = Dart_TypedDataAcquireData(byte_array, &type, &data, NULL);
EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'len'"
" to be non-null.");
intptr_t len;
result = Dart_TypedDataAcquireData(Dart_Null(), &type, &data, &len);
EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'object'"
" to be non-null.");
result = Dart_TypedDataAcquireData(str, &type, &data, &len);
EXPECT_ERROR(result, "Dart_TypedDataAcquireData expects argument 'object'"
" to be of type 'TypedData'.");
result = Dart_TypedDataReleaseData(Dart_Null());
EXPECT_ERROR(result, "Dart_TypedDataReleaseData expects argument 'object'"
" to be non-null.");
result = Dart_TypedDataReleaseData(str);
EXPECT_ERROR(result, "Dart_TypedDataReleaseData expects argument 'object'"
" to be of type 'TypedData'.");
}
static void TestDirectAccess(Dart_Handle lib,
Dart_Handle array,
Dart_TypedData_Type expected_type) {
Dart_Handle result;
// Invoke the dart function that sets initial values.
Dart_Handle dart_args[1];
dart_args[0] = array;
result = Dart_Invoke(lib, NewString("setMain"), 1, dart_args);
EXPECT_VALID(result);
// Now Get a direct access to this typed data object and check it's contents.
const int kLength = 10;
Dart_TypedData_Type type;
void* data;
intptr_t len;
result = Dart_TypedDataAcquireData(array, &type, &data, &len);
EXPECT_VALID(result);
EXPECT_EQ(expected_type, type);
EXPECT_EQ(kLength, len);
int8_t* dataP = reinterpret_cast<int8_t*>(data);
for (int i = 0; i < kLength; i++) {
EXPECT_EQ(i, dataP[i]);
}
// Now modify the values in the directly accessible array and then check
// it we see the changes back in dart.
for (int i = 0; i < kLength; i++) {
dataP[i] += 10;
}
// Release direct accesss to the typed data object.
result = Dart_TypedDataReleaseData(array);
EXPECT_VALID(result);
// Invoke the dart function in order to check the modified values.
result = Dart_Invoke(lib, NewString("testMain"), 1, dart_args);
EXPECT_VALID(result);
}
TEST_CASE(TypedDataDirectAccess1) {
const char* kScriptChars =
"import 'dart:typed_data';\n"
"class Expect {\n"
" static equals(a, b) {\n"
" if (a != b) {\n"
" throw new Exception('not equal. expected: $a, got: $b');\n"
" }\n"
" }\n"
"}\n"
"void setMain(var a) {"
" for (var i = 0; i < 10; i++) {"
" a[i] = i;"
" }"
"}\n"
"bool testMain(var list) {"
" for (var i = 0; i < 10; i++) {"
" Expect.equals((10 + i), list[i]);"
" }\n"
" return true;"
"}\n"
"List main() {"
" var a = new Int8List(10);"
" return a;"
"}\n";
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
// Test with an regular typed data object.
Dart_Handle list_access_test_obj;
list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(list_access_test_obj);
TestDirectAccess(lib, list_access_test_obj, kInt8);
// Test with an external typed data object.
uint8_t data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
intptr_t data_length = ARRAY_SIZE(data);
Dart_Handle ext_list_access_test_obj;
ext_list_access_test_obj = Dart_NewExternalTypedData(kUint8,
data,
data_length,
NULL, NULL);
EXPECT_VALID(ext_list_access_test_obj);
TestDirectAccess(lib, ext_list_access_test_obj, kUint8);
}
TEST_CASE(TypedDataViewDirectAccess) {
const char* kScriptChars =
"import 'dart:typed_data';\n"
"class Expect {\n"
" static equals(a, b) {\n"
" if (a != b) {\n"
" throw 'not equal. expected: $a, got: $b';\n"
" }\n"
" }\n"
"}\n"
"void setMain(var list) {"
" Expect.equals(10, list.length);"
" for (var i = 0; i < 10; i++) {"
" list[i] = i;"
" }"
"}\n"
"bool testMain(var list) {"
" Expect.equals(10, list.length);"
" for (var i = 0; i < 10; i++) {"
" Expect.equals((10 + i), list[i]);"
" }"
" return true;"
"}\n"
"List main() {"
" var a = new Int8List(100);"
" var view = new Int8List.view(a.buffer, 50, 10);"
" return view;"
"}\n";
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
// Test with a typed data view object.
Dart_Handle list_access_test_obj;
list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(list_access_test_obj);
TestDirectAccess(lib, list_access_test_obj, kInt8);
}
TEST_CASE(ByteDataDirectAccess) {
const char* kScriptChars =
"import 'dart:typed_data';\n"
"class Expect {\n"
" static equals(a, b) {\n"
" if (a != b) {\n"
" throw 'not equal. expected: $a, got: $b';\n"
" }\n"
" }\n"
"}\n"
"void setMain(var list) {"
" Expect.equals(10, list.length);"
" for (var i = 0; i < 10; i++) {"
" list.setInt8(i, i);"
" }"
"}\n"
"bool testMain(var list) {"
" Expect.equals(10, list.length);"
" for (var i = 0; i < 10; i++) {"
" Expect.equals((10 + i), list.getInt8(i));"
" }"
" return true;"
"}\n"
"ByteData main() {"
" var a = new Int8List(100);"
" var view = new ByteData.view(a.buffer, 50, 10);"
" return view;"
"}\n";
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
// Test with a typed data view object.
Dart_Handle list_access_test_obj;
list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, NULL);
EXPECT_VALID(list_access_test_obj);
TestDirectAccess(lib, list_access_test_obj, kByteData);
}
static void ExternalTypedDataAccessTests(Dart_Handle obj,
Dart_TypedData_Type expected_type,
uint8_t data[],
intptr_t data_length) {
EXPECT_VALID(obj);
EXPECT_EQ(expected_type, Dart_GetTypeOfExternalTypedData(obj));
EXPECT(Dart_IsList(obj));
void* raw_data = NULL;
intptr_t len;
Dart_TypedData_Type type;
EXPECT_VALID(Dart_TypedDataAcquireData(obj, &type, &raw_data, &len));
EXPECT(raw_data == data);
EXPECT_EQ(data_length, len);
EXPECT_EQ(expected_type, type);
EXPECT_VALID(Dart_TypedDataReleaseData(obj));
void* peer = &data; // just a non-NULL value
EXPECT_VALID(Dart_ExternalTypedDataGetPeer(obj, &peer));
EXPECT(peer == NULL);
intptr_t list_length = 0;
EXPECT_VALID(Dart_ListLength(obj, &list_length));
EXPECT_EQ(data_length, list_length);
// Load and check values from underlying array and API.
for (intptr_t i = 0; i < list_length; ++i) {
EXPECT_EQ(11 * i, data[i]);
Dart_Handle elt = Dart_ListGetAt(obj, i);
EXPECT_VALID(elt);
int64_t value = 0;
EXPECT_VALID(Dart_IntegerToInt64(elt, &value));
EXPECT_EQ(data[i], value);
}
// Write values through the underlying array.
for (intptr_t i = 0; i < data_length; ++i) {
data[i] *= 2;
}
// Read them back through the API.
for (intptr_t i = 0; i < list_length; ++i) {
Dart_Handle elt = Dart_ListGetAt(obj, i);
EXPECT_VALID(elt);
int64_t value = 0;
EXPECT_VALID(Dart_IntegerToInt64(elt, &value));
EXPECT_EQ(22 * i, value);
}
// Write values through the API.
for (intptr_t i = 0; i < list_length; ++i) {
Dart_Handle value = Dart_NewInteger(33 * i);
EXPECT_VALID(value);
EXPECT_VALID(Dart_ListSetAt(obj, i, value));
}
// Read them back through the underlying array.
for (intptr_t i = 0; i < data_length; ++i) {
EXPECT_EQ(33 * i, data[i]);
}
}
TEST_CASE(ExternalTypedDataAccess) {
uint8_t data[] = { 0, 11, 22, 33, 44, 55, 66, 77 };
intptr_t data_length = ARRAY_SIZE(data);
Dart_Handle obj = Dart_NewExternalTypedData(kUint8,
data, data_length,
NULL, NULL);
ExternalTypedDataAccessTests(obj, kUint8, data, data_length);
}
TEST_CASE(ExternalClampedTypedDataAccess) {
uint8_t data[] = { 0, 11, 22, 33, 44, 55, 66, 77 };
intptr_t data_length = ARRAY_SIZE(data);
Dart_Handle obj = Dart_NewExternalTypedData(kUint8Clamped,
data, data_length,
NULL, NULL);
ExternalTypedDataAccessTests(obj, kUint8Clamped, data, data_length);
}
TEST_CASE(ExternalUint8ClampedArrayAccess) {
const char* kScriptChars =
"testClamped(List a) {\n"
" if (a[1] != 11) return false;\n"
" a[1] = 3;\n"
" if (a[1] != 3) return false;\n"
" a[1] = -12;\n"
" if (a[1] != 0) return false;\n"
" a[1] = 1200;\n"
" if (a[1] != 255) return false;\n"
" return true;\n"
"}\n";
uint8_t data[] = { 0, 11, 22, 33, 44, 55, 66, 77 };
intptr_t data_length = ARRAY_SIZE(data);
Dart_Handle obj = Dart_NewExternalTypedData(kUint8Clamped,
data, data_length,
NULL, NULL);
EXPECT_VALID(obj);
Dart_Handle result;
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
Dart_Handle args[1];
args[0] = obj;
result = Dart_Invoke(lib, NewString("testClamped"), 1, args);
// Check that result is true.
EXPECT_VALID(result);
EXPECT(Dart_IsBoolean(result));
bool value = false;
result = Dart_BooleanValue(result, &value);
EXPECT_VALID(result);
EXPECT(value);
}
static void ExternalTypedDataCallbackFinalizer(Dart_Handle handle,
void* peer) {
Dart_DeletePersistentHandle(handle);
*static_cast<int*>(peer) = 42;
}
TEST_CASE(ExternalTypedDataCallback) {
int peer = 0;
{
Dart_EnterScope();
uint8_t data[] = { 1, 2, 3, 4 };
Dart_Handle obj = Dart_NewExternalTypedData(
kUint8,
data,
ARRAY_SIZE(data),
&peer,
ExternalTypedDataCallbackFinalizer);
EXPECT_VALID(obj);
void* api_peer = NULL;
EXPECT_VALID(Dart_ExternalTypedDataGetPeer(obj, &api_peer));
EXPECT_EQ(api_peer, &peer);
Dart_ExitScope();
}
EXPECT(peer == 0);
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT(peer == 0);
Isolate::Current()->heap()->CollectGarbage(Heap::kNew);
EXPECT(peer == 42);
}
static void CheckFloat32x4Data(Dart_Handle obj) {
void* raw_data = NULL;
intptr_t len;
Dart_TypedData_Type type;
EXPECT_VALID(Dart_TypedDataAcquireData(obj, &type, &raw_data, &len));
EXPECT_EQ(kFloat32x4, type);
EXPECT_EQ(len, 10);
float* float_data = reinterpret_cast<float*>(raw_data);
for (int i = 0; i < len * 4; i++) {
EXPECT_EQ(0.0, float_data[i]);
}
EXPECT_VALID(Dart_TypedDataReleaseData(obj));
}
TEST_CASE(Float32x4List) {
const char* kScriptChars =
"import 'dart:typed_data';\n"
"Float32x4List float32x4() {\n"
" return new Float32x4List(10);\n"
"}\n";
// Create a test library and Load up a test script in it.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
Dart_Handle obj = Dart_Invoke(lib, NewString("float32x4"), 0, NULL);
EXPECT_VALID(obj);
CheckFloat32x4Data(obj);
obj = Dart_NewTypedData(kFloat32x4, 10);
EXPECT_VALID(obj);
CheckFloat32x4Data(obj);
int peer = 0;
float data[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
obj = Dart_NewExternalTypedData(kFloat32x4,
data,
10,
&peer,
ExternalTypedDataCallbackFinalizer);
CheckFloat32x4Data(obj);
Isolate::Current()->heap()->CollectGarbage(Heap::kNew);
EXPECT(peer == 42);
}
#endif // TARGET_ARCH_IA32 || TARGET_ARCH_X64 || TARGET_ARCH_ARM
// Unit test for entering a scope, creating a local handle and exiting
// the scope.
UNIT_TEST_CASE(EnterExitScope) {
TestIsolateScope __test_isolate__;
Isolate* isolate = Isolate::Current();
EXPECT(isolate != NULL);
ApiState* state = isolate->api_state();
EXPECT(state != NULL);
ApiLocalScope* scope = state->top_scope();
Dart_EnterScope();
{
EXPECT(state->top_scope() != NULL);
DARTSCOPE(isolate);
const String& str1 = String::Handle(String::New("Test String"));
Dart_Handle ref = Api::NewHandle(isolate, str1.raw());
String& str2 = String::Handle();
str2 ^= Api::UnwrapHandle(ref);
EXPECT(str1.Equals(str2));
}
Dart_ExitScope();
EXPECT(scope == state->top_scope());
}
// Unit test for creating and deleting persistent handles.
UNIT_TEST_CASE(PersistentHandles) {
const char* kTestString1 = "Test String1";
const char* kTestString2 = "Test String2";
TestCase::CreateTestIsolate();
Isolate* isolate = Isolate::Current();
EXPECT(isolate != NULL);
ApiState* state = isolate->api_state();
EXPECT(state != NULL);
ApiLocalScope* scope = state->top_scope();
Dart_Handle handles[2000];
Dart_EnterScope();
{
DARTSCOPE(isolate);
Dart_Handle ref1 = Api::NewHandle(isolate, String::New(kTestString1));
for (int i = 0; i < 1000; i++) {
handles[i] = Dart_NewPersistentHandle(ref1);
}
Dart_EnterScope();
Dart_Handle ref2 = Api::NewHandle(isolate, String::New(kTestString2));
for (int i = 1000; i < 2000; i++) {
handles[i] = Dart_NewPersistentHandle(ref2);
}
for (int i = 500; i < 1500; i++) {
Dart_DeletePersistentHandle(handles[i]);
}
for (int i = 500; i < 1000; i++) {
handles[i] = Dart_NewPersistentHandle(ref2);
}
for (int i = 1000; i < 1500; i++) {
handles[i] = Dart_NewPersistentHandle(ref1);
}
VERIFY_ON_TRANSITION;
Dart_ExitScope();
}
Dart_ExitScope();
{
StackZone zone(isolate);
HANDLESCOPE(isolate);
for (int i = 0; i < 500; i++) {
String& str = String::Handle();
str ^= Api::UnwrapHandle(handles[i]);
EXPECT(str.Equals(kTestString1));
}
for (int i = 500; i < 1000; i++) {
String& str = String::Handle();
str ^= Api::UnwrapHandle(handles[i]);
EXPECT(str.Equals(kTestString2));
}
for (int i = 1000; i < 1500; i++) {
String& str = String::Handle();
str ^= Api::UnwrapHandle(handles[i]);
EXPECT(str.Equals(kTestString1));
}
for (int i = 1500; i < 2000; i++) {
String& str = String::Handle();
str ^= Api::UnwrapHandle(handles[i]);
EXPECT(str.Equals(kTestString2));
}
}
EXPECT(scope == state->top_scope());
EXPECT_EQ(2001, state->CountPersistentHandles());
Dart_ShutdownIsolate();
}
// Test that we are able to create a persistent handle from a
// persistent handle.
UNIT_TEST_CASE(NewPersistentHandle_FromPersistentHandle) {
TestIsolateScope __test_isolate__;
Isolate* isolate = Isolate::Current();
EXPECT(isolate != NULL);
ApiState* state = isolate->api_state();
EXPECT(state != NULL);
DARTSCOPE(isolate);
// Start with a known persistent handle.
Dart_Handle obj1 = Dart_True();
EXPECT(state->IsValidPersistentHandle(obj1));
// And use it to allocate a second persistent handle.
Dart_Handle obj2 = Dart_NewPersistentHandle(obj1);
EXPECT(state->IsValidPersistentHandle(obj2));
// Make sure that the value transferred.
EXPECT(Dart_IsBoolean(obj2));
bool value = false;
Dart_Handle result = Dart_BooleanValue(obj2, &value);
EXPECT_VALID(result);
EXPECT(value);
}
// Helper class to ensure new gen GC is triggered without any side effects.
// The normal call to CollectGarbage(Heap::kNew) could potentially trigger
// an old gen collection if there is a promotion failure and this could
// perturb the test.
class GCTestHelper : public AllStatic {
public:
static void CollectNewSpace(Heap::ApiCallbacks api_callbacks) {
bool invoke_api_callbacks = (api_callbacks == Heap::kInvokeApiCallbacks);
Isolate::Current()->heap()->new_space_->Scavenge(invoke_api_callbacks);
}
};
#if defined(TARGET_ARCH_IA32) || \
defined(TARGET_ARCH_X64) || \
defined(TARGET_ARCH_ARM)
TEST_CASE(WeakPersistentHandle) {
Dart_Handle weak_new_ref = Dart_Null();
EXPECT(Dart_IsNull(weak_new_ref));
Dart_Handle weak_old_ref = Dart_Null();
EXPECT(Dart_IsNull(weak_old_ref));
{
Dart_EnterScope();
// Create an object in new space.
Dart_Handle new_ref = NewString("new string");
EXPECT_VALID(new_ref);
// Create an object in old space.
Dart_Handle old_ref;
{
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
old_ref = Api::NewHandle(isolate, String::New("old string", Heap::kOld));
EXPECT_VALID(old_ref);
}
// Create a weak ref to the new space object.
weak_new_ref = Dart_NewWeakPersistentHandle(new_ref, NULL, NULL);
EXPECT_VALID(weak_new_ref);
EXPECT(!Dart_IsNull(weak_new_ref));
// Create a weak ref to the old space object.
weak_old_ref = Dart_NewWeakPersistentHandle(old_ref, NULL, NULL);
EXPECT_VALID(weak_old_ref);
EXPECT(!Dart_IsNull(weak_old_ref));
// Garbage collect new space.
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
// Nothing should be invalidated or cleared.
EXPECT_VALID(new_ref);
EXPECT(!Dart_IsNull(new_ref));
EXPECT_VALID(old_ref);
EXPECT(!Dart_IsNull(old_ref));
EXPECT_VALID(weak_new_ref);
EXPECT(!Dart_IsNull(weak_new_ref));
EXPECT(Dart_IdentityEquals(new_ref, weak_new_ref));
EXPECT_VALID(weak_old_ref);
EXPECT(!Dart_IsNull(weak_old_ref));
EXPECT(Dart_IdentityEquals(old_ref, weak_old_ref));
// Garbage collect old space.
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
// Nothing should be invalidated or cleared.
EXPECT_VALID(new_ref);
EXPECT(!Dart_IsNull(new_ref));
EXPECT_VALID(old_ref);
EXPECT(!Dart_IsNull(old_ref));
EXPECT_VALID(weak_new_ref);
EXPECT(!Dart_IsNull(weak_new_ref));
EXPECT(Dart_IdentityEquals(new_ref, weak_new_ref));
EXPECT_VALID(weak_old_ref);
EXPECT(!Dart_IsNull(weak_old_ref));
EXPECT(Dart_IdentityEquals(old_ref, weak_old_ref));
// Delete local (strong) references.
Dart_ExitScope();
}
// Garbage collect new space again.
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
// Weak ref to new space object should now be cleared.
EXPECT_VALID(weak_new_ref);
EXPECT(Dart_IsNull(weak_new_ref));
EXPECT_VALID(weak_old_ref);
EXPECT(!Dart_IsNull(weak_old_ref));
// Garbage collect old space again.
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
// Weak ref to old space object should now be cleared.
EXPECT_VALID(weak_new_ref);
EXPECT(Dart_IsNull(weak_new_ref));
EXPECT_VALID(weak_old_ref);
EXPECT(Dart_IsNull(weak_old_ref));
Dart_DeletePersistentHandle(weak_new_ref);
Dart_DeletePersistentHandle(weak_old_ref);
// Garbage collect one last time to revisit deleted handles.
Isolate::Current()->heap()->CollectGarbage(Heap::kNew);
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
static void WeakPersistentHandlePeerFinalizer(Dart_Handle handle, void* peer) {
*static_cast<int*>(peer) = 42;
}
TEST_CASE(WeakPersistentHandleCallback) {
Dart_Handle weak_ref = Dart_Null();
EXPECT(Dart_IsNull(weak_ref));
int peer = 0;
{
Dart_EnterScope();
Dart_Handle obj = NewString("new string");
EXPECT_VALID(obj);
weak_ref = Dart_NewWeakPersistentHandle(obj, &peer,
WeakPersistentHandlePeerFinalizer);
Dart_ExitScope();
}
EXPECT_VALID(weak_ref);
EXPECT(peer == 0);
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT(peer == 0);
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
EXPECT(peer == 42);
Dart_DeletePersistentHandle(weak_ref);
}
TEST_CASE(WeakPersistentHandleNoCallback) {
Dart_Handle weak_ref = Dart_Null();
EXPECT(Dart_IsNull(weak_ref));
int peer = 0;
{
Dart_EnterScope();
Dart_Handle obj = NewString("new string");
EXPECT_VALID(obj);
weak_ref = Dart_NewWeakPersistentHandle(obj, &peer,
WeakPersistentHandlePeerFinalizer);
Dart_ExitScope();
}
// A finalizer is not invoked on a deleted handle. Therefore, the
// peer value should not change after the referent is collected.
Dart_DeletePersistentHandle(weak_ref);
EXPECT_VALID(weak_ref);
EXPECT(peer == 0);
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT(peer == 0);
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
EXPECT(peer == 0);
}
UNIT_TEST_CASE(WeakPersistentHandlesCallbackShutdown) {
TestCase::CreateTestIsolate();
Dart_EnterScope();
Dart_Handle ref = Dart_True();
int peer = 1234;
Dart_NewWeakPersistentHandle(ref,
&peer,
WeakPersistentHandlePeerFinalizer);
Dart_ShutdownIsolate();
EXPECT(peer == 42);
}
TEST_CASE(ObjectGroups) {
Dart_Handle strong = Dart_Null();
EXPECT(Dart_IsNull(strong));
Dart_Handle weak1 = Dart_Null();
EXPECT(Dart_IsNull(weak1));
Dart_Handle weak2 = Dart_Null();
EXPECT(Dart_IsNull(weak2));
Dart_Handle weak3 = Dart_Null();
EXPECT(Dart_IsNull(weak3));
Dart_Handle weak4 = Dart_Null();
EXPECT(Dart_IsNull(weak4));
Dart_EnterScope();
{
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
strong = Dart_NewPersistentHandle(
Api::NewHandle(isolate, String::New("strongly reachable", Heap::kOld)));
EXPECT_VALID(strong);
EXPECT(!Dart_IsNull(strong));
weak1 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 1", Heap::kOld)),
NULL, NULL);
EXPECT_VALID(weak1);
EXPECT(!Dart_IsNull(weak1));
weak2 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 2", Heap::kOld)),
NULL, NULL);
EXPECT_VALID(weak2);
EXPECT(!Dart_IsNull(weak2));
weak3 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 3", Heap::kOld)),
NULL, NULL);
EXPECT_VALID(weak3);
EXPECT(!Dart_IsNull(weak3));
weak4 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 4", Heap::kOld)),
NULL, NULL);
EXPECT_VALID(weak4);
EXPECT(!Dart_IsNull(weak4));
}
Dart_ExitScope();
EXPECT_VALID(strong);
EXPECT_VALID(weak1);
EXPECT_VALID(weak2);
EXPECT_VALID(weak3);
EXPECT_VALID(weak4);
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
// New space collection should not affect old space objects
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(!Dart_IsNull(weak3));
EXPECT(!Dart_IsNull(weak4));
{
Dart_Handle array1[] = { weak1, strong };
EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
array1, ARRAY_SIZE(array1)));
Dart_Handle array2[] = { weak2, weak1 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
array2, ARRAY_SIZE(array2)));
Dart_Handle array3[] = { weak3, weak2 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
array3, ARRAY_SIZE(array3)));
Dart_Handle array4[] = { weak4, weak3 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
array4, ARRAY_SIZE(array4)));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
// All weak references should be preserved.
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(!Dart_IsNull(weak3));
EXPECT(!Dart_IsNull(weak4));
{
Dart_Handle array1[] = { weak1, strong };
EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
array1, ARRAY_SIZE(array1)));
Dart_Handle array2[] = { weak2, weak1 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
array2, ARRAY_SIZE(array2)));
Dart_Handle array3[] = { weak2 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
array3, ARRAY_SIZE(array3)));
// Strong reference to weak3 to retain weak3 and weak4.
Dart_Handle weak3_strong_ref = Dart_NewPersistentHandle(weak3);
EXPECT_VALID(weak3_strong_ref);
Dart_Handle array4[] = { weak4, weak3 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
array4, ARRAY_SIZE(array4)));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
// Delete strong reference to weak3.
Dart_DeletePersistentHandle(weak3_strong_ref);
}
// All weak references should be preserved.
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(!Dart_IsNull(weak3));
EXPECT(!Dart_IsNull(weak4));
{
Dart_Handle array1[] = { weak1, strong };
EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
array1, ARRAY_SIZE(array1)));
Dart_Handle array2[] = { weak2, weak1 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
array2, ARRAY_SIZE(array2)));
Dart_Handle array3[] = { weak2 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
array3, ARRAY_SIZE(array3)));
Dart_Handle array4[] = { weak4, weak3 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
array4, ARRAY_SIZE(array4)));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
// Only weak1 and weak2 should be preserved.
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(Dart_IsNull(weak3));
EXPECT(Dart_IsNull(weak4));
{
Dart_Handle array1[] = { weak1, strong };
EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
array1, ARRAY_SIZE(array1)));
// weak3 is cleared so weak2 is unreferenced and should be cleared
Dart_Handle array2[] = { weak2, weak3 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
array2, ARRAY_SIZE(array2)));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
// Only weak1 should be preserved, weak3 should not preserve weak2.
EXPECT(!Dart_IsNull(weak1));
EXPECT(Dart_IsNull(weak2));
EXPECT(Dart_IsNull(weak3)); // was cleared, should remain cleared
EXPECT(Dart_IsNull(weak4)); // was cleared, should remain cleared
{
// weak{2,3,4} are cleared and should have no effect on weak1
Dart_Handle array1[] = { strong, weak2, weak3, weak4 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
array1, ARRAY_SIZE(array1)));
// weak1 is weakly reachable and should be cleared
Dart_Handle array2[] = { weak1 };
EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
array2, ARRAY_SIZE(array2)));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
// All weak references should now be cleared.
EXPECT(Dart_IsNull(weak1));
EXPECT(Dart_IsNull(weak2));
EXPECT(Dart_IsNull(weak3));
EXPECT(Dart_IsNull(weak4));
}
TEST_CASE(PrologueWeakPersistentHandles) {
Dart_Handle old_pwph = Dart_Null();
EXPECT(Dart_IsNull(old_pwph));
Dart_Handle new_pwph = Dart_Null();
EXPECT(Dart_IsNull(new_pwph));
Dart_EnterScope();
{
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
new_pwph = Dart_NewPrologueWeakPersistentHandle(
Api::NewHandle(isolate,
String::New("new space prologue weak", Heap::kNew)),
NULL, NULL);
EXPECT_VALID(new_pwph);
EXPECT(!Dart_IsNull(new_pwph));
old_pwph = Dart_NewPrologueWeakPersistentHandle(
Api::NewHandle(isolate,
String::New("old space prologue weak", Heap::kOld)),
NULL, NULL);
EXPECT_VALID(old_pwph);
EXPECT(!Dart_IsNull(old_pwph));
}
Dart_ExitScope();
EXPECT_VALID(new_pwph);
EXPECT(!Dart_IsNull(new_pwph));
EXPECT(Dart_IsPrologueWeakPersistentHandle(new_pwph));
EXPECT_VALID(old_pwph);
EXPECT(!Dart_IsNull(old_pwph));
EXPECT(Dart_IsPrologueWeakPersistentHandle(old_pwph));
// Garbage collect new space without invoking API callbacks.
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
// Both prologue weak handles should be preserved.
EXPECT(!Dart_IsNull(new_pwph));
EXPECT(!Dart_IsNull(old_pwph));
// Garbage collect old space without invoking API callbacks.
Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
Heap::kIgnoreApiCallbacks);
// Both prologue weak handles should be preserved.
EXPECT(!Dart_IsNull(new_pwph));
EXPECT(!Dart_IsNull(old_pwph));
// Garbage collect new space invoking API callbacks.
GCTestHelper::CollectNewSpace(Heap::kInvokeApiCallbacks);
// The prologue weak handle with a new space referent should now be
// cleared. The old space referent should be preserved.
EXPECT(Dart_IsNull(new_pwph));
EXPECT(!Dart_IsNull(old_pwph));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
Heap::kInvokeApiCallbacks);
// The prologue weak handle with an old space referent should now be
// cleared. The new space referent should remain cleared.
EXPECT(Dart_IsNull(new_pwph));
EXPECT(Dart_IsNull(old_pwph));
}
TEST_CASE(ImplicitReferencesOldSpace) {
Dart_Handle strong = Dart_Null();
EXPECT(Dart_IsNull(strong));
Dart_Handle weak1 = Dart_Null();
EXPECT(Dart_IsNull(weak1));
Dart_Handle weak2 = Dart_Null();
EXPECT(Dart_IsNull(weak2));
Dart_Handle weak3 = Dart_Null();
EXPECT(Dart_IsNull(weak3));
Dart_EnterScope();
{
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
strong = Dart_NewPersistentHandle(
Api::NewHandle(isolate, String::New("strongly reachable", Heap::kOld)));
EXPECT(!Dart_IsNull(strong));
EXPECT_VALID(strong);
weak1 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 1", Heap::kOld)),
NULL, NULL);
EXPECT(!Dart_IsNull(weak1));
EXPECT_VALID(weak1);
weak2 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 2", Heap::kOld)),
NULL, NULL);
EXPECT(!Dart_IsNull(weak2));
EXPECT_VALID(weak2);
weak3 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 3", Heap::kOld)),
NULL, NULL);
EXPECT(!Dart_IsNull(weak3));
EXPECT_VALID(weak3);
}
Dart_ExitScope();
EXPECT_VALID(strong);
EXPECT_VALID(weak1);
EXPECT_VALID(weak2);
EXPECT_VALID(weak3);
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
// New space collection should not affect old space objects
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(!Dart_IsNull(weak3));
// A strongly referenced key should preserve all the values.
{
Dart_Handle keys[] = { strong };
Dart_Handle values[] = { weak1, weak2, weak3 };
EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
values, ARRAY_SIZE(values)));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
// All weak references should be preserved.
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(!Dart_IsNull(weak3));
// Key membership does not imply a strong reference.
{
Dart_Handle keys[] = { strong, weak3 };
Dart_Handle values[] = { weak1, weak2 };
EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
values, ARRAY_SIZE(values)));
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
}
// All weak references except weak3 should be preserved.
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(Dart_IsNull(weak3));
}
TEST_CASE(ImplicitReferencesNewSpace) {
Dart_Handle strong = Dart_Null();
EXPECT(Dart_IsNull(strong));
Dart_Handle weak1 = Dart_Null();
EXPECT(Dart_IsNull(weak1));
Dart_Handle weak2 = Dart_Null();
EXPECT(Dart_IsNull(weak2));
Dart_Handle weak3 = Dart_Null();
EXPECT(Dart_IsNull(weak3));
Dart_EnterScope();
{
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
strong = Dart_NewPersistentHandle(
Api::NewHandle(isolate, String::New("strongly reachable", Heap::kNew)));
EXPECT(!Dart_IsNull(strong));
EXPECT_VALID(strong);
weak1 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 1", Heap::kNew)),
NULL, NULL);
EXPECT(!Dart_IsNull(weak1));
EXPECT_VALID(weak1);
weak2 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 2", Heap::kNew)),
NULL, NULL);
EXPECT(!Dart_IsNull(weak2));
EXPECT_VALID(weak2);
weak3 = Dart_NewWeakPersistentHandle(
Api::NewHandle(isolate, String::New("weakly reachable 3", Heap::kNew)),
NULL, NULL);
EXPECT(!Dart_IsNull(weak3));
EXPECT_VALID(weak3);
}
Dart_ExitScope();
EXPECT_VALID(strong);
EXPECT_VALID(weak1);
EXPECT_VALID(weak2);
EXPECT_VALID(weak3);
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
// Old space collection should not affect old space objects.
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(!Dart_IsNull(weak3));
// A strongly referenced key should preserve all the values.
{
Dart_Handle keys[] = { strong };
Dart_Handle values[] = { weak1, weak2, weak3 };
EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
values, ARRAY_SIZE(values)));
GCTestHelper::CollectNewSpace(Heap::kInvokeApiCallbacks);
}
// All weak references should be preserved.
EXPECT(!Dart_IsNull(weak1));
EXPECT(!Dart_IsNull(weak2));
EXPECT(!Dart_IsNull(weak3));
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
// No weak references should be preserved.
EXPECT(Dart_IsNull(weak1));
EXPECT(Dart_IsNull(weak2));
EXPECT(Dart_IsNull(weak3));
}
static int global_prologue_callback_status;
static void PrologueCallbackTimes2() {
global_prologue_callback_status *= 2;
}
static void PrologueCallbackTimes3() {
global_prologue_callback_status *= 3;
}
static int global_epilogue_callback_status;
static void EpilogueCallbackTimes4() {
global_epilogue_callback_status *= 4;
}
static void EpilogueCallbackTimes5() {
global_epilogue_callback_status *= 5;
}
TEST_CASE(AddGarbageCollectionCallbacks) {
// Add a prologue callback.
EXPECT_VALID(Dart_AddGcPrologueCallback(&PrologueCallbackTimes2));
// Add the same prologue callback again. This is an error.
EXPECT(Dart_IsError(Dart_AddGcPrologueCallback(&PrologueCallbackTimes2)));
// Add another prologue callback.
EXPECT_VALID(Dart_AddGcPrologueCallback(&PrologueCallbackTimes3));
// Add the same prologue callback again. This is an error.
EXPECT(Dart_IsError(Dart_AddGcPrologueCallback(&PrologueCallbackTimes3)));
// Add an epilogue callback.
EXPECT_VALID(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes4));
// Add the same epilogue callback again. This is an error.
EXPECT(Dart_IsError(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes4)));
// Add annother epilogue callback.
EXPECT_VALID(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes5));
// Add the same epilogue callback again. This is an error.
EXPECT(Dart_IsError(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes5)));
}
TEST_CASE(RemoveGarbageCollectionCallbacks) {
// Remove a prologue callback that has not been added. This is an error.
EXPECT(Dart_IsError(Dart_RemoveGcPrologueCallback(&PrologueCallbackTimes2)));
// Add a prologue callback.
EXPECT_VALID(Dart_AddGcPrologueCallback(&PrologueCallbackTimes2));
// Remove a prologue callback.
EXPECT_VALID(Dart_RemoveGcPrologueCallback(&PrologueCallbackTimes2));
// Remove a prologue callback again. This is an error.
EXPECT(Dart_IsError(Dart_RemoveGcPrologueCallback(&PrologueCallbackTimes2)));
// Add two prologue callbacks.
EXPECT_VALID(Dart_AddGcPrologueCallback(&PrologueCallbackTimes2));
EXPECT_VALID(Dart_AddGcPrologueCallback(&PrologueCallbackTimes3));
// Remove two prologue callbacks.
EXPECT_VALID(Dart_RemoveGcPrologueCallback(&PrologueCallbackTimes3));
EXPECT_VALID(Dart_RemoveGcPrologueCallback(&PrologueCallbackTimes2));
// Remove epilogue callbacks again. This is an error.
EXPECT(Dart_IsError(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes4)));
EXPECT(Dart_IsError(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes5)));
// Remove a epilogue callback that has not been added. This is an error.
EXPECT(Dart_IsError(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes5)));
// Add a epilogue callback.
EXPECT_VALID(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes4));
// Remove a epilogue callback.
EXPECT_VALID(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes4));
// Remove a epilogue callback again. This is an error.
EXPECT(Dart_IsError(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes4)));
// Add two epilogue callbacks.
EXPECT_VALID(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes4));
EXPECT_VALID(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes5));
// Remove two epilogue callbacks.
EXPECT_VALID(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes5));
EXPECT_VALID(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes4));
// Remove epilogue callbacks again. This is an error.
EXPECT(Dart_IsError(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes4)));
EXPECT(Dart_IsError(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes5)));
}
TEST_CASE(SingleGarbageCollectionCallback) {
// Add a prologue callback.
EXPECT_VALID(Dart_AddGcPrologueCallback(&PrologueCallbackTimes2));
// Garbage collect new space ignoring callbacks. This should not
// invoke the prologue callback. No status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
EXPECT_EQ(3, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Garbage collect new space invoking callbacks. This should
// invoke the prologue callback. No status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
GCTestHelper::CollectNewSpace(Heap::kInvokeApiCallbacks);
EXPECT_EQ(6, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Garbage collect old space ignoring callbacks. This should invoke
// the prologue callback. The prologue status value should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
Heap::kIgnoreApiCallbacks);
EXPECT_EQ(3, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Garbage collect old space. This should invoke the prologue
// callback. The prologue status value should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(6, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Garbage collect old space again. Callbacks are persistent so the
// prologue status value should change again.
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(12, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Add an epilogue callback.
EXPECT_VALID(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes4));
// Garbage collect new space. This should not invoke the prologue
// or the epilogue callback. No status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
EXPECT_EQ(3, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Garbage collect new space. This should invoke the prologue and
// the epilogue callback. The prologue and epilogue status values
// should change.
GCTestHelper::CollectNewSpace(Heap::kInvokeApiCallbacks);
EXPECT_EQ(6, global_prologue_callback_status);
EXPECT_EQ(28, global_epilogue_callback_status);
// Garbage collect old space. This should invoke the prologue and
// the epilogue callbacks. The prologue and epilogue status values
// should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(6, global_prologue_callback_status);
EXPECT_EQ(28, global_epilogue_callback_status);
// Garbage collect old space again without invoking callbacks.
// Nothing should change.
Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
Heap::kIgnoreApiCallbacks);
EXPECT_EQ(6, global_prologue_callback_status);
EXPECT_EQ(28, global_epilogue_callback_status);
// Garbage collect old space again. Callbacks are persistent so the
// prologue and epilogue status values should change again.
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(12, global_prologue_callback_status);
EXPECT_EQ(112, global_epilogue_callback_status);
// Remove the prologue and epilogue callbacks
EXPECT_VALID(Dart_RemoveGcPrologueCallback(&PrologueCallbackTimes2));
EXPECT_VALID(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes4));
// Garbage collect old space. No callbacks should be invoked. No
// status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(3, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
}
TEST_CASE(MultipleGarbageCollectionCallbacks) {
// Add prologue callbacks.
EXPECT_VALID(Dart_AddGcPrologueCallback(&PrologueCallbackTimes2));
EXPECT_VALID(Dart_AddGcPrologueCallback(&PrologueCallbackTimes3));
// Add an epilogue callback.
EXPECT_VALID(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes4));
// Garbage collect new space. This should not invoke the prologue
// or epilogue callbacks. No status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks);
EXPECT_EQ(3, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Garbage collect old space. This should invoke both prologue
// callbacks and the epilogue callback. The prologue and epilogue
// status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(18, global_prologue_callback_status);
EXPECT_EQ(28, global_epilogue_callback_status);
// Add another GC epilogue callback.
EXPECT_VALID(Dart_AddGcEpilogueCallback(&EpilogueCallbackTimes5));
// Garbage collect old space. This should invoke both prologue
// callbacks and both epilogue callbacks. The prologue and epilogue
// status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(18, global_prologue_callback_status);
EXPECT_EQ(140, global_epilogue_callback_status);
// Remove an epilogue callback.
EXPECT_VALID(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes4));
// Garbage collect old space. This should invoke both prologue
// callbacks and the remaining epilogue callback. The prologue and
// epilogue status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(18, global_prologue_callback_status);
EXPECT_EQ(35, global_epilogue_callback_status);
// Remove the remaining epilogue callback.
EXPECT_VALID(Dart_RemoveGcEpilogueCallback(&EpilogueCallbackTimes5));
// Garbage collect old space. This should invoke both prologue
// callbacks. The prologue status value should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(18, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Remove a prologue callback.
EXPECT_VALID(Dart_RemoveGcPrologueCallback(&PrologueCallbackTimes3));
// Garbage collect old space. This should invoke the remaining
// prologue callback. The prologue status value should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(6, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
// Remove the remaining prologue callback.
EXPECT_VALID(Dart_RemoveGcPrologueCallback(&PrologueCallbackTimes2));
// Garbage collect old space. No callbacks should be invoked. No
// status values should change.
global_prologue_callback_status = 3;
global_epilogue_callback_status = 7;
Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
EXPECT_EQ(3, global_prologue_callback_status);
EXPECT_EQ(7, global_epilogue_callback_status);
}
#endif // TARGET_ARCH_IA32 || TARGET_ARCH_X64 || TARGET_ARCH_ARM
// Unit test for creating multiple scopes and local handles within them.
// Ensure that the local handles get all cleaned out when exiting the
// scope.
UNIT_TEST_CASE(LocalHandles) {
TestCase::CreateTestIsolate();
Isolate* isolate = Isolate::Current();
EXPECT(isolate != NULL);
ApiState* state = isolate->api_state();
EXPECT(state != NULL);
ApiLocalScope* scope = state->top_scope();
Dart_Handle handles[300];
{
StackZone zone(isolate);
HANDLESCOPE(isolate);
Smi& val = Smi::Handle();
// Start a new scope and allocate some local handles.
Dart_EnterScope();
for (int i = 0; i < 100; i++) {
handles[i] = Api::NewHandle(isolate, Smi::New(i));
}
EXPECT_EQ(100, state->CountLocalHandles());
for (int i = 0; i < 100; i++) {
val ^= Api::UnwrapHandle(handles[i]);
EXPECT_EQ(i, val.Value());
}
// Start another scope and allocate some more local handles.
{
Dart_EnterScope();
for (int i = 100; i < 200; i++) {
handles[i] = Api::NewHandle(isolate, Smi::New(i));
}
EXPECT_EQ(200, state->CountLocalHandles());
for (int i = 100; i < 200; i++) {
val ^= Api::UnwrapHandle(handles[i]);
EXPECT_EQ(i, val.Value());
}
// Start another scope and allocate some more local handles.
{
Dart_EnterScope();
for (int i = 200; i < 300; i++) {
handles[i] = Api::NewHandle(isolate, Smi::New(i));
}
EXPECT_EQ(300, state->CountLocalHandles());
for (int i = 200; i < 300; i++) {
val ^= Api::UnwrapHandle(handles[i]);
EXPECT_EQ(i, val.Value());
}
EXPECT_EQ(300, state->CountLocalHandles());
VERIFY_ON_TRANSITION;
Dart_ExitScope();
}
EXPECT_EQ(200, state->CountLocalHandles());
Dart_ExitScope();
}
EXPECT_EQ(100, state->CountLocalHandles());
Dart_ExitScope();
}
EXPECT_EQ(0, state->CountLocalHandles());
EXPECT(scope == state->top_scope());
Dart_ShutdownIsolate();
}
// Unit test for creating multiple scopes and allocating objects in the
// zone for the scope. Ensure that the memory is freed when the scope
// exits.
UNIT_TEST_CASE(LocalZoneMemory) {
TestCase::CreateTestIsolate();
Isolate* isolate = Isolate::Current();
EXPECT(isolate != NULL);
ApiState* state = isolate->api_state();
EXPECT(state != NULL);
ApiLocalScope* scope = state->top_scope();
{
// Start a new scope and allocate some memory.
Dart_EnterScope();
for (int i = 0; i < 100; i++) {
Dart_ScopeAllocate(16);
}
EXPECT_EQ(1600, state->ZoneSizeInBytes());
// Start another scope and allocate some more memory.
{
Dart_EnterScope();
for (int i = 0; i < 100; i++) {
Dart_ScopeAllocate(16);
}
EXPECT_EQ(3200, state->ZoneSizeInBytes());
{
// Start another scope and allocate some more memory.
{
Dart_EnterScope();
for (int i = 0; i < 200; i++) {
Dart_ScopeAllocate(16);
}
EXPECT_EQ(6400, state->ZoneSizeInBytes());
Dart_ExitScope();
}
}
EXPECT_EQ(3200, state->ZoneSizeInBytes());
Dart_ExitScope();
}
EXPECT_EQ(1600, state->ZoneSizeInBytes());
Dart_ExitScope();
}
EXPECT_EQ(0, state->ZoneSizeInBytes());
EXPECT(scope == state->top_scope());
Dart_ShutdownIsolate();
}
UNIT_TEST_CASE(Isolates) {
// This test currently assumes that the Dart_Isolate type is an opaque
// representation of Isolate*.
Dart_Isolate iso_1 = TestCase::CreateTestIsolate();
EXPECT_EQ(iso_1, Api::CastIsolate(Isolate::Current()));
Dart_Isolate isolate = Dart_CurrentIsolate();
EXPECT_EQ(iso_1, isolate);
Dart_ExitIsolate();
EXPECT(NULL == Dart_CurrentIsolate());
Dart_Isolate iso_2 = TestCase::CreateTestIsolate();
EXPECT_EQ(iso_2, Dart_CurrentIsolate());
Dart_ExitIsolate();
EXPECT(NULL == Dart_CurrentIsolate());
Dart_EnterIsolate(iso_2);
EXPECT_EQ(iso_2, Dart_CurrentIsolate());
Dart_ShutdownIsolate();
EXPECT(NULL == Dart_CurrentIsolate());
Dart_EnterIsolate(iso_1);
EXPECT_EQ(iso_1, Dart_CurrentIsolate());
Dart_ShutdownIsolate();
EXPECT(NULL == Dart_CurrentIsolate());
}
UNIT_TEST_CASE(CurrentIsolateData) {
intptr_t mydata = 12345;
char* err;
Dart_Isolate isolate =
Dart_CreateIsolate(NULL, NULL, NULL,
reinterpret_cast<void*>(mydata),
&err);
EXPECT(isolate != NULL);
EXPECT_EQ(mydata, reinterpret_cast<intptr_t>(Dart_CurrentIsolateData()));
Dart_ShutdownIsolate();
}
TEST_CASE(DebugName) {
Dart_Handle debug_name = Dart_DebugName();
EXPECT_VALID(debug_name);
EXPECT(Dart_IsString(debug_name));
}
static void MyMessageNotifyCallback(Dart_Isolate dest_isolate) {
}
UNIT_TEST_CASE(SetMessageCallbacks) {
Dart_Isolate dart_isolate = TestCase::CreateTestIsolate();
Dart_SetMessageNotifyCallback(&MyMessageNotifyCallback);
Isolate* isolate = reinterpret_cast<Isolate*>(dart_isolate);
EXPECT_EQ(&MyMessageNotifyCallback, isolate->message_notify_callback());
Dart_ShutdownIsolate();
}
#if defined(TARGET_ARCH_IA32) || \
defined(TARGET_ARCH_X64) || \
defined(TARGET_ARCH_ARM)
TEST_CASE(ClassTypedefsEtc) {
const char* kScriptChars =
"class SomeClass {\n"
"}\n"
"typedef void SomeHandler(String a);\n";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(lib);
Dart_Handle normal_cls = Dart_GetClass(lib, NewString("SomeClass"));
EXPECT_VALID(normal_cls);
Dart_Handle typedef_cls = Dart_GetClass(lib, NewString("SomeHandler"));
EXPECT_VALID(typedef_cls);
EXPECT(Dart_IsClass(normal_cls));
EXPECT(!Dart_ClassIsTypedef(normal_cls));
EXPECT(!Dart_ClassIsFunctionType(normal_cls));
EXPECT(Dart_IsClass(typedef_cls));
EXPECT(Dart_ClassIsTypedef(typedef_cls));
EXPECT(!Dart_ClassIsFunctionType(typedef_cls));
// Exercise the typedef class api.
Dart_Handle functype_cls = Dart_ClassGetTypedefReferent(typedef_cls);
EXPECT_VALID(functype_cls);
// Pass the wrong class kind to Dart_ClassGetTypedefReferent
EXPECT_ERROR(Dart_ClassGetTypedefReferent(normal_cls),
"class 'SomeClass' is not a typedef class.");
EXPECT(Dart_IsClass(functype_cls));
EXPECT(!Dart_ClassIsTypedef(functype_cls));
EXPECT(Dart_ClassIsFunctionType(functype_cls));
// Exercise the function type class.
Dart_Handle sig_func = Dart_ClassGetFunctionTypeSignature(functype_cls);
EXPECT_VALID(sig_func);
EXPECT(Dart_IsFunction(sig_func));
int64_t fixed_params = -1;
int64_t opt_params = -1;
EXPECT_VALID(
Dart_FunctionParameterCounts(sig_func, &fixed_params, &opt_params));
EXPECT_EQ(1, fixed_params);
EXPECT_EQ(0, opt_params);
// Pass the wrong class kind to Dart_ClassGetFunctionTypeSignature
EXPECT_ERROR(Dart_ClassGetFunctionTypeSignature(normal_cls),
"class 'SomeClass' is not a function-type class.");
}
#define CHECK_ABSTRACT_CLASS(handle, name) \
{ \
Dart_Handle tmp = (handle); \
EXPECT_VALID(tmp); \
EXPECT(Dart_IsAbstractClass(tmp)); \
Dart_Handle intf_name = Dart_ClassName(tmp); \
EXPECT_VALID(intf_name); \
const char* intf_name_cstr = ""; \
EXPECT_VALID(Dart_StringToCString(intf_name, &intf_name_cstr)); \
EXPECT_STREQ((name), intf_name_cstr); \
}
TEST_CASE(ClassGetInterfaces) {
const char* kScriptChars =
"class MyClass0 {\n"
"}\n"
"\n"
"class MyClass1 implements MyInterface1 {\n"
"}\n"
"\n"
"class MyClass2 implements MyInterface0, MyInterface1 {\n"
"}\n"
"\n"
"abstract class MyInterface0 {\n"
"}\n"
"\n"
"abstract class MyInterface1 implements MyInterface0 {\n"
"}\n";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
Dart_Handle cls0 = Dart_GetClass(lib, NewString("MyClass0"));
Dart_Handle cls1 = Dart_GetClass(lib, NewString("MyClass1"));
Dart_Handle cls2 = Dart_GetClass(lib, NewString("MyClass2"));
Dart_Handle intf0 = Dart_GetClass(lib, NewString("MyInterface0"));
Dart_Handle intf1 = Dart_GetClass(lib, NewString("MyInterface1"));
intptr_t len = -1;
EXPECT_VALID(Dart_ClassGetInterfaceCount(cls0, &len));
EXPECT_EQ(0, len);
EXPECT_ERROR(Dart_ClassGetInterfaceAt(cls0, 0),
"Dart_ClassGetInterfaceAt: argument 'index' out of bounds");
len = -1;
EXPECT_VALID(Dart_ClassGetInterfaceCount(cls1, &len));
EXPECT_EQ(1, len);
CHECK_ABSTRACT_CLASS(Dart_ClassGetInterfaceAt(cls1, 0), "MyInterface1");
EXPECT_ERROR(Dart_ClassGetInterfaceAt(cls1, -1),
"Dart_ClassGetInterfaceAt: argument 'index' out of bounds");
EXPECT_ERROR(Dart_ClassGetInterfaceAt(cls1, 1),
"Dart_ClassGetInterfaceAt: argument 'index' out of bounds");
len = -1;
EXPECT_VALID(Dart_ClassGetInterfaceCount(cls2, &len));
EXPECT_EQ(2, len);
// TODO(turnidge): The test relies on the ordering here. Sort this.
CHECK_ABSTRACT_CLASS(Dart_ClassGetInterfaceAt(cls2, 0), "MyInterface0");
CHECK_ABSTRACT_CLASS(Dart_ClassGetInterfaceAt(cls2, 1), "MyInterface1");
len = -1;
EXPECT_VALID(Dart_ClassGetInterfaceCount(intf0, &len));
EXPECT_EQ(0, len);
len = -1;
EXPECT_VALID(Dart_ClassGetInterfaceCount(intf1, &len));
EXPECT_EQ(1, len);
CHECK_ABSTRACT_CLASS(Dart_ClassGetInterfaceAt(intf1, 0), "MyInterface0");
// Error cases.
EXPECT_ERROR(Dart_ClassGetInterfaceCount(Dart_True(), &len),
"Dart_ClassGetInterfaceCount expects argument 'clazz' to be of "
"type Class.");
EXPECT_ERROR(Dart_ClassGetInterfaceCount(Dart_NewApiError("MyError"), &len),
"MyError");
}
static void TestFieldOk(Dart_Handle container,
Dart_Handle name,
bool final,
const char* initial_value) {
Dart_Handle result;
// Make sure we have the right initial value.
result = Dart_GetField(container, name);
EXPECT_VALID(result);
const char* value = "";
EXPECT_VALID(Dart_StringToCString(result, &value));
EXPECT_STREQ(initial_value, value);
// Use a unique expected value.
static int counter = 0;
char buffer[256];
OS::SNPrint(buffer, 256, "Expected%d", ++counter);
// Try to change the field value.
result = Dart_SetField(container, name, NewString(buffer));
if (final) {
EXPECT(Dart_IsError(result));
} else {
EXPECT_VALID(result);
}
// Make sure we have the right final value.
result = Dart_GetField(container, name);
EXPECT_VALID(result);
EXPECT_VALID(Dart_StringToCString(result, &value));
if (final) {
EXPECT_STREQ(initial_value, value);
} else {
EXPECT_STREQ(buffer, value);
}
}
static void TestFieldNotFound(Dart_Handle container,
Dart_Handle name) {
EXPECT(Dart_IsError(Dart_GetField(container, name)));
EXPECT(Dart_IsError(Dart_SetField(container, name, Dart_Null())));
}
TEST_CASE(FieldAccess) {
const char* kScriptChars =
"class BaseFields {\n"
" BaseFields()\n"
" : this.inherited_fld = 'inherited' {\n"
" }\n"
" var inherited_fld;\n"
" static var non_inherited_fld;\n"
"}\n"
"\n"
"class Fields extends BaseFields {\n"
" Fields()\n"
" : this.instance_fld = 'instance',\n"
" this._instance_fld = 'hidden instance',\n"
" this.final_instance_fld = 'final instance',\n"
" this._final_instance_fld = 'hidden final instance' {\n"
" instance_getset_fld = 'instance getset';\n"
" _instance_getset_fld = 'hidden instance getset';\n"
" }\n"
"\n"
" static Init() {\n"
" static_fld = 'static';\n"
" _static_fld = 'hidden static';\n"
" static_getset_fld = 'static getset';\n"
" _static_getset_fld = 'hidden static getset';\n"
" }\n"
"\n"
" var instance_fld;\n"
" var _instance_fld;\n"
" final final_instance_fld;\n"
" final _final_instance_fld;\n"
" static var static_fld;\n"
" static var _static_fld;\n"
" static const const_static_fld = 'const static';\n"
" static const _const_static_fld = 'hidden const static';\n"
"\n"
" get instance_getset_fld { return _gs_fld1; }\n"
" void set instance_getset_fld(var value) { _gs_fld1 = value; }\n"
" get _instance_getset_fld { return _gs_fld2; }\n"
" void set _instance_getset_fld(var value) { _gs_fld2 = value; }\n"
" var _gs_fld1;\n"
" var _gs_fld2;\n"
"\n"
" static get static_getset_fld { return _gs_fld3; }\n"
" static void set static_getset_fld(var value) { _gs_fld3 = value; }\n"
" static get _static_getset_fld { return _gs_fld4; }\n"
" static void set _static_getset_fld(var value) { _gs_fld4 = value; }\n"
" static var _gs_fld3;\n"
" static var _gs_fld4;\n"
"}\n"
"var top_fld;\n"
"var _top_fld;\n"
"const const_top_fld = 'const top';\n"
"const _const_top_fld = 'hidden const top';\n"
"\n"
"get top_getset_fld { return _gs_fld5; }\n"
"void set top_getset_fld(var value) { _gs_fld5 = value; }\n"
"get _top_getset_fld { return _gs_fld6; }\n"
"void set _top_getset_fld(var value) { _gs_fld6 = value; }\n"
"var _gs_fld5;\n"
"var _gs_fld6;\n"
"\n"
"Fields test() {\n"
" Fields.Init();\n"
" top_fld = 'top';\n"
" _top_fld = 'hidden top';\n"
" top_getset_fld = 'top getset';\n"
" _top_getset_fld = 'hidden top getset';\n"
" return new Fields();\n"
"}\n";
const char* kImportedScriptChars =
"library library_name;\n"
"var imported_fld = 'imported';\n"
"var _imported_fld = 'hidden imported';\n"
"get imported_getset_fld { return _gs_fld1; }\n"
"void set imported_getset_fld(var value) { _gs_fld1 = value; }\n"
"get _imported_getset_fld { return _gs_fld2; }\n"
"void set _imported_getset_fld(var value) { _gs_fld2 = value; }\n"
"var _gs_fld1;\n"
"var _gs_fld2;\n"
"void test2() {\n"
" imported_getset_fld = 'imported getset';\n"
" _imported_getset_fld = 'hidden imported getset';\n"
"}\n";
// Shared setup.
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
Dart_Handle cls = Dart_GetClass(lib, NewString("Fields"));
EXPECT_VALID(cls);
Dart_Handle instance = Dart_Invoke(lib, NewString("test"), 0, NULL);
EXPECT_VALID(instance);
Dart_Handle name;
// Load imported lib.
Dart_Handle url = NewString("library_url");
Dart_Handle source = NewString(kImportedScriptChars);
Dart_Handle imported_lib = Dart_LoadLibrary(url, source);
Dart_Handle prefix = NewString("");
EXPECT_VALID(imported_lib);
Dart_Handle result = Dart_LibraryImportLibrary(lib, imported_lib, prefix);
EXPECT_VALID(result);
result = Dart_Invoke(imported_lib, NewString("test2"), 0, NULL);
EXPECT_VALID(result);
// Instance field.
name = NewString("instance_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(cls, name);
TestFieldOk(instance, name, false, "instance");
// Hidden instance field.
name = NewString("_instance_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(cls, name);
TestFieldOk(instance, name, false, "hidden instance");
// Final instance field.
name = NewString("final_instance_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(cls, name);
TestFieldOk(instance, name, true, "final instance");
// Hidden final instance field.
name = NewString("_final_instance_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(cls, name);
TestFieldOk(instance, name, true, "hidden final instance");
// Inherited field.
name = NewString("inherited_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(cls, name);
TestFieldOk(instance, name, false, "inherited");
// Instance get/set field.
name = NewString("instance_getset_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(cls, name);
TestFieldOk(instance, name, false, "instance getset");
// Hidden instance get/set field.
name = NewString("_instance_getset_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(cls, name);
TestFieldOk(instance, name, false, "hidden instance getset");
// Static field.
name = NewString("static_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(instance, name);
TestFieldOk(cls, name, false, "static");
// Hidden static field.
name = NewString("_static_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(instance, name);
TestFieldOk(cls, name, false, "hidden static");
// Static final field.
name = NewString("const_static_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(instance, name);
TestFieldOk(cls, name, true, "const static");
// Hidden static const field.
name = NewString("_const_static_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(instance, name);
TestFieldOk(cls, name, true, "hidden const static");
// Static non-inherited field. Not found at any level.
name = NewString("non_inherited_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(instance, name);
TestFieldNotFound(cls, name);
// Static get/set field.
name = NewString("static_getset_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(instance, name);
TestFieldOk(cls, name, false, "static getset");
// Hidden static get/set field.
name = NewString("_static_getset_fld");
TestFieldNotFound(lib, name);
TestFieldNotFound(instance, name);
TestFieldOk(cls, name, false, "hidden static getset");
// Top-Level field.
name = NewString("top_fld");
TestFieldNotFound(cls, name);
TestFieldNotFound(instance, name);
TestFieldOk(lib, name, false, "top");
// Hidden top-level field.
name = NewString("_top_fld");
TestFieldNotFound(cls, name);
TestFieldNotFound(instance, name);
TestFieldOk(lib, name, false, "hidden top");
// Top-Level final field.
name = NewString("const_top_fld");
TestFieldNotFound(cls, name);
TestFieldNotFound(instance, name);
TestFieldOk(lib, name, true, "const top");
// Hidden top-level final field.
name = NewString("_const_top_fld");
TestFieldNotFound(cls, name);
TestFieldNotFound(instance, name);
TestFieldOk(lib, name, true, "hidden const top");
// Top-Level get/set field.
name = NewString("top_getset_fld");
TestFieldNotFound(cls, name);
TestFieldNotFound(instance, name);
TestFieldOk(lib, name, false, "top getset");
// Hidden top-level get/set field.
name = NewString("_top_getset_fld");
TestFieldNotFound(cls, name);
TestFieldNotFound(instance, name);
TestFieldOk(lib, name, false, "hidden top getset");
// Imported top-Level field.
name = NewString("imported_fld");
<