| // 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"); |
| <
|