| // 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 "bin/builtin.h" |
| #include "include/dart_api.h" |
| #include "include/dart_mirrors_api.h" |
| #include "include/dart_native_api.h" |
| #include "include/dart_tools_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/lockers.h" |
| #include "vm/unit_test.h" |
| #include "vm/verifier.h" |
| |
| namespace dart { |
| |
| DECLARE_FLAG(int, optimization_counter_threshold); |
| DECLARE_FLAG(bool, verify_acquired_data); |
| DECLARE_FLAG(bool, ignore_patch_signature_mismatch); |
| |
| 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 (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(StacktraceInfo) { |
| const char* kScriptChars = |
| "bar() => throw new Error();\n" |
| "foo() => bar();\n" |
| "testMain() => foo();\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); |
| Dart_Handle error = Dart_Invoke(lib, NewString("testMain"), 0, NULL); |
| |
| EXPECT(Dart_IsError(error)); |
| |
| Dart_StackTrace stacktrace; |
| Dart_Handle result = Dart_GetStackTraceFromError(error, &stacktrace); |
| EXPECT_VALID(result); |
| |
| intptr_t frame_count = 0; |
| result = Dart_StackTraceLength(stacktrace, &frame_count); |
| EXPECT_VALID(result); |
| EXPECT_EQ(3, frame_count); |
| |
| Dart_Handle function_name; |
| Dart_Handle script_url; |
| intptr_t line_number = 0; |
| intptr_t column_number = 0; |
| const char* cstr = ""; |
| |
| Dart_ActivationFrame frame; |
| result = Dart_GetActivationFrame(stacktrace, 0, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("bar", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(1, line_number); |
| EXPECT_EQ(10, column_number); |
| |
| result = Dart_GetActivationFrame(stacktrace, 1, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("foo", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(2, line_number); |
| EXPECT_EQ(10, column_number); |
| |
| result = Dart_GetActivationFrame(stacktrace, 2, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("testMain", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(3, line_number); |
| EXPECT_EQ(15, column_number); |
| |
| // Out-of-bounds frames. |
| result = Dart_GetActivationFrame(stacktrace, frame_count, &frame); |
| EXPECT(Dart_IsError(result)); |
| result = Dart_GetActivationFrame(stacktrace, -1, &frame); |
| EXPECT(Dart_IsError(result)); |
| } |
| |
| |
| TEST_CASE(DeepStacktraceInfo) { |
| const char* kScriptChars = |
| "foo(n) => n == 1 ? throw new Error() : foo(n-1);\n" |
| "testMain() => foo(50);\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); |
| Dart_Handle error = Dart_Invoke(lib, NewString("testMain"), 0, NULL); |
| |
| EXPECT(Dart_IsError(error)); |
| |
| Dart_StackTrace stacktrace; |
| Dart_Handle result = Dart_GetStackTraceFromError(error, &stacktrace); |
| EXPECT_VALID(result); |
| |
| intptr_t frame_count = 0; |
| result = Dart_StackTraceLength(stacktrace, &frame_count); |
| EXPECT_VALID(result); |
| EXPECT_EQ(51, frame_count); |
| // Test something bigger than the preallocated size to verify nothing was |
| // truncated. |
| EXPECT(51 > Stacktrace::kPreallocatedStackdepth); |
| |
| Dart_Handle function_name; |
| Dart_Handle script_url; |
| intptr_t line_number = 0; |
| intptr_t column_number = 0; |
| const char* cstr = ""; |
| |
| // Top frame at positioned at throw. |
| Dart_ActivationFrame frame; |
| result = Dart_GetActivationFrame(stacktrace, 0, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("foo", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(1, line_number); |
| EXPECT_EQ(20, column_number); |
| |
| // Middle frames positioned at the recursive call. |
| for (intptr_t frame_index = 1; |
| frame_index < (frame_count - 1); |
| frame_index++) { |
| result = Dart_GetActivationFrame(stacktrace, frame_index, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("foo", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(1, line_number); |
| EXPECT_EQ(40, column_number); |
| } |
| |
| // Bottom frame positioned at testMain(). |
| result = Dart_GetActivationFrame(stacktrace, frame_count - 1, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("testMain", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(2, line_number); |
| EXPECT_EQ(15, column_number); |
| |
| // Out-of-bounds frames. |
| result = Dart_GetActivationFrame(stacktrace, frame_count, &frame); |
| EXPECT(Dart_IsError(result)); |
| result = Dart_GetActivationFrame(stacktrace, -1, &frame); |
| EXPECT(Dart_IsError(result)); |
| } |
| |
| |
| TEST_CASE(StackOverflowStacktraceInfo) { |
| const char* kScriptChars = |
| "class C {\n" |
| " static foo() => foo();\n" |
| "}\n" |
| "testMain() => C.foo();\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); |
| Dart_Handle error = Dart_Invoke(lib, NewString("testMain"), 0, NULL); |
| |
| EXPECT(Dart_IsError(error)); |
| |
| Dart_StackTrace stacktrace; |
| Dart_Handle result = Dart_GetStackTraceFromError(error, &stacktrace); |
| EXPECT_VALID(result); |
| |
| intptr_t frame_count = 0; |
| result = Dart_StackTraceLength(stacktrace, &frame_count); |
| EXPECT_VALID(result); |
| EXPECT_EQ(Stacktrace::kPreallocatedStackdepth - 1, frame_count); |
| |
| Dart_Handle function_name; |
| Dart_Handle script_url; |
| intptr_t line_number = 0; |
| intptr_t column_number = 0; |
| const char* cstr = ""; |
| |
| // Top frame at recursive call. |
| Dart_ActivationFrame frame; |
| result = Dart_GetActivationFrame(stacktrace, 0, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("C.foo", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(2, line_number); |
| EXPECT_EQ(3, column_number); |
| |
| // Out-of-bounds frames. |
| result = Dart_GetActivationFrame(stacktrace, frame_count, &frame); |
| EXPECT(Dart_IsError(result)); |
| result = Dart_GetActivationFrame(stacktrace, -1, &frame); |
| EXPECT(Dart_IsError(result)); |
| } |
| |
| |
| TEST_CASE(OutOfMemoryStacktraceInfo) { |
| const char* kScriptChars = |
| "var number_of_ints = 134000000;\n" |
| "testMain() {\n" |
| " new List<int>(number_of_ints)\n" |
| "}\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); |
| Dart_Handle error = Dart_Invoke(lib, NewString("testMain"), 0, NULL); |
| |
| EXPECT(Dart_IsError(error)); |
| |
| Dart_StackTrace stacktrace; |
| Dart_Handle result = Dart_GetStackTraceFromError(error, &stacktrace); |
| EXPECT(Dart_IsError(result)); // No Stacktrace for OutOfMemory. |
| } |
| |
| |
| void CurrentStackTraceNative(Dart_NativeArguments args) { |
| Dart_EnterScope(); |
| |
| Dart_StackTrace stacktrace; |
| Dart_Handle result = Dart_GetStackTrace(&stacktrace); |
| EXPECT_VALID(result); |
| |
| intptr_t frame_count = 0; |
| result = Dart_StackTraceLength(stacktrace, &frame_count); |
| EXPECT_VALID(result); |
| EXPECT_EQ(52, frame_count); |
| // Test something bigger than the preallocated size to verify nothing was |
| // truncated. |
| EXPECT(52 > Stacktrace::kPreallocatedStackdepth); |
| |
| Dart_Handle function_name; |
| Dart_Handle script_url; |
| intptr_t line_number = 0; |
| intptr_t column_number = 0; |
| const char* cstr = ""; |
| |
| // Top frame is inspectStack(). |
| Dart_ActivationFrame frame; |
| result = Dart_GetActivationFrame(stacktrace, 0, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("inspectStack", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(1, line_number); |
| EXPECT_EQ(47, column_number); |
| |
| // Second frame is foo() positioned at call to inspectStack(). |
| result = Dart_GetActivationFrame(stacktrace, 1, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("foo", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(2, line_number); |
| EXPECT_EQ(20, column_number); |
| |
| // Middle frames positioned at the recursive call. |
| for (intptr_t frame_index = 2; |
| frame_index < (frame_count - 1); |
| frame_index++) { |
| result = Dart_GetActivationFrame(stacktrace, frame_index, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("foo", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(2, line_number); |
| EXPECT_EQ(37, column_number); |
| } |
| |
| // Bottom frame positioned at testMain(). |
| result = Dart_GetActivationFrame(stacktrace, frame_count - 1, &frame); |
| EXPECT_VALID(result); |
| result = Dart_ActivationFrameInfo( |
| frame, &function_name, &script_url, &line_number, &column_number); |
| EXPECT_VALID(result); |
| Dart_StringToCString(function_name, &cstr); |
| EXPECT_STREQ("testMain", cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ("test-lib", cstr); |
| EXPECT_EQ(3, line_number); |
| EXPECT_EQ(15, column_number); |
| |
| // Out-of-bounds frames. |
| result = Dart_GetActivationFrame(stacktrace, frame_count, &frame); |
| EXPECT(Dart_IsError(result)); |
| result = Dart_GetActivationFrame(stacktrace, -1, &frame); |
| EXPECT(Dart_IsError(result)); |
| |
| Dart_SetReturnValue(args, Dart_NewInteger(42)); |
| Dart_ExitScope(); |
| } |
| |
| |
| static Dart_NativeFunction CurrentStackTraceNativeLookup( |
| Dart_Handle name, int argument_count, bool* auto_setup_scope) { |
| ASSERT(auto_setup_scope != NULL); |
| *auto_setup_scope = false; |
| return reinterpret_cast<Dart_NativeFunction>(&CurrentStackTraceNative); |
| } |
| |
| |
| TEST_CASE(CurrentStacktraceInfo) { |
| const char* kScriptChars = |
| "inspectStack() native 'CurrentStackTraceNatve';\n" |
| "foo(n) => n == 1 ? inspectStack() : foo(n-1);\n" |
| "testMain() => foo(50);\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, |
| &CurrentStackTraceNativeLookup); |
| Dart_Handle result = Dart_Invoke(lib, NewString("testMain"), 0, NULL); |
| EXPECT_VALID(result); |
| EXPECT(Dart_IsInteger(result)); |
| int64_t value = 0; |
| EXPECT_VALID(Dart_IntegerToInt64(result, &value)); |
| EXPECT_EQ(42, value); |
| } |
| |
| |
| 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 = Api::NewError("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)); |
| } |
| |
| |
| TEST_CASE(UnhandleExceptionError) { |
| Isolate* isolate = Isolate::Current(); |
| const char* exception_cstr = ""; |
| |
| // Test with an API Error. |
| const char* kApiError = "Api Error Exception Test."; |
| Dart_Handle api_error = Api::NewHandle( |
| isolate, |
| ApiError::New(String::Handle(String::New(kApiError)))); |
| Dart_Handle exception_error = Dart_NewUnhandledExceptionError(api_error); |
| EXPECT(!Dart_IsApiError(exception_error)); |
| EXPECT(Dart_IsUnhandledExceptionError(exception_error)); |
| EXPECT(Dart_IsString(Dart_ErrorGetException(exception_error))); |
| EXPECT_VALID(Dart_StringToCString(Dart_ErrorGetException(exception_error), |
| &exception_cstr)); |
| EXPECT_STREQ(kApiError, exception_cstr); |
| |
| // Test with a Compilation Error. |
| const char* kCompileError = "CompileError Exception Test."; |
| const String& compile_message = |
| String::Handle(String::New(kCompileError)); |
| Dart_Handle compile_error = |
| Api::NewHandle(isolate, LanguageError::New(compile_message)); |
| exception_error = Dart_NewUnhandledExceptionError(compile_error); |
| EXPECT(!Dart_IsApiError(exception_error)); |
| EXPECT(Dart_IsUnhandledExceptionError(exception_error)); |
| EXPECT(Dart_IsString(Dart_ErrorGetException(exception_error))); |
| EXPECT_VALID(Dart_StringToCString(Dart_ErrorGetException(exception_error), |
| &exception_cstr)); |
| EXPECT_STREQ(kCompileError, exception_cstr); |
| |
| // Test with a Fatal Error. |
| const String& fatal_message = |
| String::Handle(String::New("FatalError Exception Test.")); |
| Dart_Handle fatal_error = |
| Api::NewHandle(isolate, UnwindError::New(fatal_message)); |
| exception_error = Dart_NewUnhandledExceptionError(fatal_error); |
| EXPECT(Dart_IsError(exception_error)); |
| EXPECT(!Dart_IsUnhandledExceptionError(exception_error)); |
| |
| // Test with a Regular object. |
| const char* kRegularString = "Regular String Exception Test."; |
| Dart_Handle obj = Api::NewHandle(isolate, String::New(kRegularString)); |
| exception_error = Dart_NewUnhandledExceptionError(obj); |
| EXPECT(!Dart_IsApiError(exception_error)); |
| EXPECT(Dart_IsUnhandledExceptionError(exception_error)); |
| EXPECT(Dart_IsString(Dart_ErrorGetException(exception_error))); |
| EXPECT_VALID(Dart_StringToCString(Dart_ErrorGetException(exception_error), |
| &exception_cstr)); |
| EXPECT_STREQ(kRegularString, exception_cstr); |
| } |
| |
| |
| 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, bool* auto_setup_scope) { |
| ASSERT(auto_setup_scope != NULL); |
| *auto_setup_scope = false; |
| 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)); |
| } |
| |
| |
| TEST_CASE(Dart_Error) { |
| Dart_Handle error = Api::NewError("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(EmptyString) { |
| Dart_Handle empty = Dart_EmptyString(); |
| EXPECT_VALID(empty); |
| EXPECT(!Dart_IsNull(empty)); |
| } |
| |
| |
| TEST_CASE(IdentityEquals) { |
| Dart_Handle five = Dart_NewInteger(5); |
| Dart_Handle five_again = Dart_NewInteger(5); |
| Dart_Handle mint = Dart_NewInteger(0xFFFFFFFF); |
| Dart_Handle mint_again = Dart_NewInteger(0xFFFFFFFF); |
| Dart_Handle abc = NewString("abc"); |
| Dart_Handle abc_again = NewString("abc"); |
| Dart_Handle xyz = NewString("xyz"); |
| Dart_Handle dart_core = NewString("dart:core"); |
| Dart_Handle dart_mirrors = NewString("dart:mirrors"); |
| |
| // Same objects. |
| EXPECT(Dart_IdentityEquals(five, five)); |
| EXPECT(Dart_IdentityEquals(mint, mint)); |
| EXPECT(Dart_IdentityEquals(abc, abc)); |
| EXPECT(Dart_IdentityEquals(xyz, xyz)); |
| |
| // Equal objects with special spec rules. |
| EXPECT(Dart_IdentityEquals(five, five_again)); |
| EXPECT(Dart_IdentityEquals(mint, mint_again)); |
| |
| // Equal objects without special spec rules. |
| EXPECT(!Dart_IdentityEquals(abc, abc_again)); |
| |
| // Different objects. |
| EXPECT(!Dart_IdentityEquals(five, mint)); |
| EXPECT(!Dart_IdentityEquals(abc, xyz)); |
| |
| // Case where identical() is not the same as pointer equality. |
| Dart_Handle nan1 = Dart_NewDouble(NAN); |
| Dart_Handle nan2 = Dart_NewDouble(NAN); |
| EXPECT(Dart_IdentityEquals(nan1, nan2)); |
| |
| // Non-instance objects. |
| { |
| DARTSCOPE(Thread::Current()); |
| Dart_Handle lib1 = Dart_LookupLibrary(dart_core); |
| Dart_Handle lib2 = Dart_LookupLibrary(dart_mirrors); |
| |
| EXPECT(Dart_IdentityEquals(lib1, lib1)); |
| EXPECT(Dart_IdentityEquals(lib2, lib2)); |
| EXPECT(!Dart_IdentityEquals(lib1, lib2)); |
| |
| // Mix instance and non-instance. |
| EXPECT(!Dart_IdentityEquals(lib1, nan1)); |
| EXPECT(!Dart_IdentityEquals(nan1, lib1)); |
| } |
| } |
| |
| |
| TEST_CASE(IdentityHash) { |
| Dart_Handle five = Dart_NewInteger(5); |
| Dart_Handle five_again = Dart_NewInteger(5); |
| Dart_Handle mint = Dart_NewInteger(0xFFFFFFFF); |
| Dart_Handle mint_again = Dart_NewInteger(0xFFFFFFFF); |
| Dart_Handle abc = NewString("abc"); |
| // Dart_Handle abc_again = NewString("abc"); |
| Dart_Handle xyz = NewString("xyz"); |
| Dart_Handle dart_core = NewString("dart:core"); |
| Dart_Handle dart_mirrors = NewString("dart:mirrors"); |
| |
| // Same objects. |
| EXPECT_EQ(Dart_IdentityHash(five), Dart_IdentityHash(five)); |
| EXPECT_EQ(Dart_IdentityHash(mint), Dart_IdentityHash(mint)); |
| EXPECT_EQ(Dart_IdentityHash(abc), Dart_IdentityHash(abc)); |
| EXPECT_EQ(Dart_IdentityHash(xyz), Dart_IdentityHash(xyz)); |
| |
| // Equal objects with special spec rules. |
| EXPECT_EQ(Dart_IdentityHash(five), Dart_IdentityHash(five_again)); |
| EXPECT_EQ(Dart_IdentityHash(mint), Dart_IdentityHash(mint_again)); |
| |
| // Note abc and abc_again are not required to have equal identity hashes. |
| |
| // Case where identical() is not the same as pointer equality. |
| Dart_Handle nan1 = Dart_NewDouble(NAN); |
| Dart_Handle nan2 = Dart_NewDouble(NAN); |
| EXPECT_EQ(Dart_IdentityHash(nan1), Dart_IdentityHash(nan2)); |
| |
| // Non-instance objects. |
| { |
| DARTSCOPE(Thread::Current()); |
| Dart_Handle lib1 = Dart_LookupLibrary(dart_core); |
| Dart_Handle lib2 = Dart_LookupLibrary(dart_mirrors); |
| |
| EXPECT_EQ(Dart_IdentityHash(lib1), Dart_IdentityHash(lib1)); |
| EXPECT_EQ(Dart_IdentityHash(lib2), Dart_IdentityHash(lib2)); |
| } |
| } |
| |
| |
| 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); |
| |
| // Case where identity is not equality. |
| Dart_Handle nan = Dart_NewDouble(NAN); |
| EXPECT_VALID(Dart_ObjectEquals(nan, nan, &equal)); |
| EXPECT(!equal); |
| } |
| |
| |
| 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(InstanceGetType) { |
| Zone* zone = thread->zone(); |
| // Get the handle from a valid instance handle. |
| Dart_Handle type = Dart_InstanceGetType(Dart_Null()); |
| EXPECT_VALID(type); |
| EXPECT(Dart_IsType(type)); |
| const Type& null_type_obj = Api::UnwrapTypeHandle(zone, type); |
| EXPECT(null_type_obj.raw() == Type::NullType()); |
| |
| Dart_Handle instance = Dart_True(); |
| type = Dart_InstanceGetType(instance); |
| EXPECT_VALID(type); |
| EXPECT(Dart_IsType(type)); |
| const Type& bool_type_obj = Api::UnwrapTypeHandle(zone, type); |
| EXPECT(bool_type_obj.raw() == Type::BoolType()); |
| |
| Dart_Handle cls_name = Dart_TypeName(type); |
| EXPECT_VALID(cls_name); |
| const char* cls_name_cstr = ""; |
| EXPECT_VALID(Dart_StringToCString(cls_name, &cls_name_cstr)); |
| EXPECT_STREQ("bool", cls_name_cstr); |
| |
| Dart_Handle qual_cls_name = Dart_QualifiedTypeName(type); |
| EXPECT_VALID(qual_cls_name); |
| const char* qual_cls_name_cstr = ""; |
| EXPECT_VALID(Dart_StringToCString(qual_cls_name, &qual_cls_name_cstr)); |
| EXPECT_STREQ("Library:'dart:core' Class: bool", qual_cls_name_cstr); |
| |
| // Errors propagate. |
| Dart_Handle error = Dart_NewApiError("MyError"); |
| Dart_Handle error_type = Dart_InstanceGetType(error); |
| EXPECT_ERROR(error_type, "MyError"); |
| |
| // Get the handle from a non-instance handle. |
| Dart_Handle dart_core = NewString("dart:core"); |
| Dart_Handle obj = Dart_LookupLibrary(dart_core); |
| Dart_Handle type_type = Dart_InstanceGetType(obj); |
| EXPECT_ERROR(type_type, |
| "Dart_InstanceGetType 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); |
| } |
| |
| |
| 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)); |
| } |
| |
| |
| TEST_CASE(IntegerValues) { |
| const int64_t kIntegerVal1 = 100; |
| const int64_t kIntegerVal2 = 0xffffffff; |
| const char* kIntegerVal3 = "0x123456789123456789123456789"; |
| const uint64_t kIntegerVal4 = 0xffffffffffffffff; |
| |
| 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)); |
| |
| Dart_Handle val4 = Dart_NewIntegerFromUint64(kIntegerVal4); |
| EXPECT_VALID(val4); |
| uint64_t out4 = 0; |
| result = Dart_IntegerToUint64(val4, &out4); |
| EXPECT_VALID(result); |
| EXPECT_EQ(kIntegerVal4, out4); |
| |
| Dart_Handle val5 = Dart_NewInteger(-1); |
| EXPECT_VALID(val5); |
| uint64_t out5 = 0; |
| result = Dart_IntegerToUint64(val5, &out5); |
| EXPECT(Dart_IsError(result)); |
| } |
| |
| |
| 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(kMinInt64); |
| 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_NewIntegerFromUint64(kMaxUint64); |
| 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); |
| intptr_t char_size; |
| intptr_t str_len; |
| void* peer; |
| EXPECT_VALID(Dart_StringGetProperties(latin1str, |
| &char_size, |
| &str_len, |
| &peer)); |
| EXPECT_EQ(1, char_size); |
| EXPECT_EQ(4, str_len); |
| EXPECT(!peer); |
| |
| 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), |
| data8, NULL); |
| EXPECT_VALID(ext8); |
| EXPECT(Dart_IsString(ext8)); |
| EXPECT(Dart_IsExternalString(ext8)); |
| EXPECT_VALID(Dart_StringGetProperties(ext8, |
| &char_size, |
| &str_len, |
| &peer)); |
| EXPECT_EQ(1, char_size); |
| EXPECT_EQ(4, str_len); |
| EXPECT_EQ(data8, peer); |
| |
| 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)); |
| EXPECT_VALID(Dart_StringGetProperties(str16, |
| &char_size, |
| &str_len, |
| &peer)); |
| EXPECT_EQ(2, char_size); |
| EXPECT_EQ(4, str_len); |
| EXPECT(!peer); |
| |
| Dart_Handle ext16 = Dart_NewExternalUTF16String(data16, ARRAY_SIZE(data16), |
| data16, NULL); |
| EXPECT_VALID(ext16); |
| EXPECT(Dart_IsString(ext16)); |
| EXPECT(Dart_IsExternalString(ext16)); |
| EXPECT_VALID(Dart_StringGetProperties(ext16, |
| &char_size, |
| &str_len, |
| &peer)); |
| EXPECT_EQ(2, char_size); |
| EXPECT_EQ(4, str_len); |
| EXPECT_EQ(data16, peer); |
| |
| 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(MalformedStringToUTF8) { |
| // 1D11E = treble clef |
| // [0] should be high surrogate D834 |
| // [1] should be low surrogate DD1E |
| // Strings are allowed to have individual or out of order surrogates, even |
| // if that doesn't make sense as renderable characters. |
| const char* kScriptChars = |
| "String lowSurrogate() {" |
| " return '\\u{1D11E}'[1];" |
| "}" |
| "String highSurrogate() {" |
| " return '\\u{1D11E}'[0];" |
| "}" |
| "String reversed() => lowSurrogate() + highSurrogate();"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); |
| Dart_Handle str1 = Dart_Invoke(lib, NewString("lowSurrogate"), 0, NULL); |
| EXPECT_VALID(str1); |
| |
| uint8_t* utf8_encoded = NULL; |
| intptr_t utf8_length = 0; |
| Dart_Handle result = Dart_StringToUTF8(str1, &utf8_encoded, &utf8_length); |
| EXPECT_VALID(result); |
| EXPECT_EQ(3, utf8_length); |
| EXPECT_EQ(237, static_cast<intptr_t>(utf8_encoded[0])); |
| EXPECT_EQ(180, static_cast<intptr_t>(utf8_encoded[1])); |
| EXPECT_EQ(158, static_cast<intptr_t>(utf8_encoded[2])); |
| |
| Dart_Handle str2 = Dart_NewStringFromUTF8(utf8_encoded, utf8_length); |
| EXPECT_VALID(str2); // Standalone low surrogate, but still valid |
| |
| Dart_Handle reversed = Dart_Invoke(lib, NewString("reversed"), 0, NULL); |
| EXPECT_VALID(reversed); // This is also allowed. |
| uint8_t* utf8_encoded_reversed = NULL; |
| intptr_t utf8_length_reversed = 0; |
| result = Dart_StringToUTF8(reversed, |
| &utf8_encoded_reversed, &utf8_length_reversed); |
| EXPECT_VALID(result); |
| EXPECT_EQ(6, utf8_length_reversed); |
| uint8_t expected[6] = {237, 180, 158, 237, 160, 180}; |
| for (int i = 0; i < 6; i++) { |
| EXPECT_EQ(expected[i], utf8_encoded_reversed[i]); |
| } |
| } |
| |
| |
| 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); |
| |
| uint16_t data16[] = { 'h', 'e', 'l', 'l', 'o' }; |
| Dart_Handle obj16 = Dart_NewExternalUTF16String( |
| data16, |
| ARRAY_SIZE(data16), |
| &peer16, |
| ExternalStringCallbackFinalizer); |
| EXPECT_VALID(obj16); |
| |
| 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(ExternalStringPretenure) { |
| { |
| Dart_EnterScope(); |
| static const uint8_t big_data8[16*MB] = {0, }; |
| Dart_Handle big8 = Dart_NewExternalLatin1String( |
| big_data8, |
| ARRAY_SIZE(big_data8), |
| NULL, |
| NULL); |
| EXPECT_VALID(big8); |
| static const uint16_t big_data16[16*MB/2] = {0, }; |
| Dart_Handle big16 = Dart_NewExternalUTF16String( |
| big_data16, |
| ARRAY_SIZE(big_data16), |
| NULL, |
| NULL); |
| static const uint8_t small_data8[] = {'f', 'o', 'o'}; |
| Dart_Handle small8 = Dart_NewExternalLatin1String( |
| small_data8, |
| ARRAY_SIZE(small_data8), |
| NULL, |
| NULL); |
| EXPECT_VALID(small8); |
| static const uint16_t small_data16[] = {'b', 'a', 'r'}; |
| Dart_Handle small16 = Dart_NewExternalUTF16String( |
| small_data16, |
| ARRAY_SIZE(small_data16), |
| NULL, |
| NULL); |
| EXPECT_VALID(small16); |
| { |
| DARTSCOPE(Thread::Current()); |
| String& handle = String::Handle(); |
| handle ^= Api::UnwrapHandle(big8); |
| EXPECT(handle.IsOld()); |
| handle ^= Api::UnwrapHandle(big16); |
| EXPECT(handle.IsOld()); |
| handle ^= Api::UnwrapHandle(small8); |
| EXPECT(handle.IsNew()); |
| handle ^= Api::UnwrapHandle(small16); |
| EXPECT(handle.IsNew()); |
| } |
| Dart_ExitScope(); |
| } |
| } |
| |
| |
| TEST_CASE(ExternalTypedDataPretenure) { |
| { |
| Dart_EnterScope(); |
| static const int kBigLength = 16*MB/8; |
| int64_t* big_data = new int64_t[kBigLength](); |
| Dart_Handle big = Dart_NewExternalTypedData( |
| Dart_TypedData_kInt64, |
| big_data, |
| kBigLength); |
| EXPECT_VALID(big); |
| static const int kSmallLength = 16*KB/8; |
| int64_t* small_data = new int64_t[kSmallLength](); |
| Dart_Handle small = Dart_NewExternalTypedData( |
| Dart_TypedData_kInt64, |
| small_data, |
| kSmallLength); |
| EXPECT_VALID(small); |
| { |
| DARTSCOPE(Thread::Current()); |
| ExternalTypedData& handle = ExternalTypedData::Handle(); |
| handle ^= Api::UnwrapHandle(big); |
| EXPECT(handle.IsOld()); |
| handle ^= Api::UnwrapHandle(small); |
| EXPECT(handle.IsNew()); |
| } |
| Dart_ExitScope(); |
| delete[] big_data; |
| delete[] small_data; |
| } |
| } |
| |
| |
| 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 if we can get a range of values. |
| result = Dart_ListGetRange(list_access_test_obj, 8, 4, NULL); |
| EXPECT(Dart_IsError(result)); |
| const int kRangeOffset = 1; |
| const int kRangeLength = 2; |
| Dart_Handle values[kRangeLength]; |
| |
| result = Dart_ListGetRange(list_access_test_obj, 8, 4, values); |
| EXPECT(Dart_IsError(result)); |
| |
| result = Dart_ListGetRange( |
| list_access_test_obj, kRangeOffset, kRangeLength, values); |
| EXPECT_VALID(result); |
| |
| result = Dart_IntegerToInt64(values[0], &value); |
| EXPECT_VALID(result); |
| EXPECT_EQ(20, value); |
| |
| result = Dart_IntegerToInt64(values[1], &value); |
| EXPECT_VALID(result); |
| EXPECT_EQ(30, value); |
| |
| // 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(MapAccess) { |
| const char* kScriptChars = |
| "Map testMain() {" |
| " return {" |
| " 'a' : 1," |
| " 'b' : null," |
| " };" |
| "}"; |
| 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 Map. |
| result = Dart_Invoke(lib, NewString("testMain"), 0, NULL); |
| EXPECT_VALID(result); |
| |
| // First ensure that the returned object is a map. |
| Dart_Handle map = result; |
| Dart_Handle a = NewString("a"); |
| Dart_Handle b = NewString("b"); |
| Dart_Handle c = NewString("c"); |
| |
| EXPECT(Dart_IsMap(map)); |
| EXPECT(!Dart_IsMap(a)); |
| |
| // Access values in the map. |
| int64_t value; |
| result = Dart_MapGetAt(map, a); |
| EXPECT_VALID(result); |
| result = Dart_IntegerToInt64(result, &value); |
| EXPECT_VALID(result); |
| EXPECT_EQ(value, 1); |
| |
| result = Dart_MapGetAt(map, b); |
| EXPECT(Dart_IsNull(result)); |
| |
| result = Dart_MapGetAt(map, c); |
| EXPECT(Dart_IsNull(result)); |
| |
| EXPECT(Dart_IsError(Dart_MapGetAt(a, a))); |
| |
| // Test for presence of keys. |
| bool contains = false; |
| result = Dart_MapContainsKey(map, a); |
| EXPECT_VALID(result); |
| result = Dart_BooleanValue(result, &contains); |
| EXPECT_VALID(result); |
| EXPECT(contains); |
| |
| contains = false; |
| result = Dart_MapContainsKey(map, NewString("b")); |
| EXPECT_VALID(result); |
| result = Dart_BooleanValue(result, &contains); |
| EXPECT_VALID(result); |
| EXPECT(contains); |
| |
| contains = true; |
| result = Dart_MapContainsKey(map, NewString("c")); |
| EXPECT_VALID(result); |
| result = Dart_BooleanValue(result, &contains); |
| EXPECT_VALID(result); |
| EXPECT(!contains); |
| |
| EXPECT(Dart_IsError(Dart_MapContainsKey(a, a))); |
| |
| // Enumerate keys. (Note literal maps guarantee key order.) |
| Dart_Handle keys = Dart_MapKeys(map); |
| EXPECT_VALID(keys); |
| |
| intptr_t len = 0; |
| bool equals; |
| result = Dart_ListLength(keys, &len); |
| EXPECT_VALID(result); |
| EXPECT_EQ(2, len); |
| |
| result = Dart_ListGetAt(keys, 0); |
| EXPECT(Dart_IsString(result)); |
| equals = false; |
| EXPECT_VALID(Dart_ObjectEquals(result, a, &equals)); |
| EXPECT(equals); |
| |
| result = Dart_ListGetAt(keys, 1); |
| EXPECT(Dart_IsString(result)); |
| equals = false; |
| EXPECT_VALID(Dart_ObjectEquals(result, b, &equals)); |
| EXPECT(equals); |
| |
| EXPECT(Dart_IsError(Dart_MapKeys(a))); |
| } |
| |
| |
| TEST_CASE(IsFuture) { |
| const char* kScriptChars = |
| "import 'dart:async';" |
| "Future testMain() {" |
| " return new Completer().future;" |
| "}"; |
| 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 Future. |
| result = Dart_Invoke(lib, NewString("testMain"), 0, NULL); |
| EXPECT_VALID(result); |
| EXPECT(Dart_IsFuture(result)); |
| |
| EXPECT(!Dart_IsFuture(lib)); // Non-instance. |
| Dart_Handle anInteger = Dart_NewInteger(0); |
| EXPECT(!Dart_IsFuture(anInteger)); |
| Dart_Handle aString = NewString("I am not a Future"); |
| EXPECT(!Dart_IsFuture(aString)); |
| Dart_Handle null = Dart_Null(); |
| EXPECT(!Dart_IsFuture(null)); |
| } |
| |
| |
| TEST_CASE(TypedDataViewListGetAsBytes) { |
| const int kSize = 1000; |
| |
| const char* kScriptChars = |
| "import 'dart:typed_data';\n" |
| "List main(int size) {\n" |
| " var a = new Int8List(size);\n" |
| " var view = new Int8List.view(a.buffer, 0, size);\n" |
| " return view;\n" |
| "}\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 dart_args[1]; |
| dart_args[0] = Dart_NewInteger(kSize); |
| Dart_Handle view_obj = Dart_Invoke(lib, NewString("main"), 1, dart_args); |
| EXPECT_VALID(view_obj); |
| for (intptr_t i = 0; i < kSize; ++i) { |
| EXPECT_VALID(Dart_ListSetAt(view_obj, i, Dart_NewInteger(i & 0xff))); |
| } |
| uint8_t* data = new uint8_t[kSize]; |
| EXPECT_VALID(Dart_ListGetAsBytes(view_obj, 0, data, kSize)); |
| for (intptr_t i = 0; i < kSize; ++i) { |
| EXPECT_EQ(i & 0xff, data[i]); |
| } |
| |
| Dart_Handle result = Dart_ListGetAsBytes(view_obj, 0, data, kSize + 1); |
| EXPECT(Dart_IsError(result)); |
| delete[] data; |
| } |
| |
| |
| TEST_CASE(TypedDataViewListIsTypedData) { |
| const int kSize = 1000; |
| |
| const char* kScriptChars = |
| "import 'dart:typed_data';\n" |
| "List main(int size) {\n" |
| " var a = new Int8List(size);\n" |
| " var view = new Int8List.view(a.buffer, 0, size);\n" |
| " return view;\n" |
| "}\n"; |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL); |
| |
| // Create a typed data view object. |
| Dart_Handle dart_args[1]; |
| dart_args[0] = Dart_NewInteger(kSize); |
| Dart_Handle view_obj = Dart_Invoke(lib, NewString("main"), 1, dart_args); |
| EXPECT_VALID(view_obj); |
| // Test that the API considers it a TypedData object. |
| EXPECT(Dart_IsTypedData(view_obj)); |
| } |
| |
| |
| TEST_CASE(TypedDataAccess) { |
| EXPECT_EQ(Dart_TypedData_kInvalid, |
| Dart_GetTypeOfTypedData(Dart_True())); |
| EXPECT_EQ(Dart_TypedData_kInvalid, |
| Dart_GetTypeOfExternalTypedData(Dart_False())); |
| Dart_Handle byte_array1 = Dart_NewTypedData(Dart_TypedData_kUint8, 10); |
| EXPECT_VALID(byte_array1); |
| EXPECT_EQ(Dart_TypedData_kUint8, |
| Dart_GetTypeOfTypedData(byte_array1)); |
| EXPECT_EQ(Dart_TypedData_kInvalid, |
| Dart_GetTypeOfExternalTypedData(byte_array1)); |
| EXPECT(Dart_IsList(byte_array1)); |
| EXPECT(!Dart_IsTypedData(Dart_True())); |
| EXPECT(Dart_IsTypedData(byte_array1)); |
| EXPECT(!Dart_IsByteBuffer(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(Dart_TypedData_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); |
| } |
| } |
| |
| |
| TEST_CASE(ByteBufferAccess) { |
| EXPECT(!Dart_IsByteBuffer(Dart_True())); |
| Dart_Handle byte_array = Dart_NewTypedData(Dart_TypedData_kUint8, 10); |
| EXPECT_VALID(byte_array); |
| // Set through the List API. |
| for (intptr_t i = 0; i < 10; ++i) { |
| EXPECT_VALID(Dart_ListSetAt(byte_array, i, Dart_NewInteger(i + 1))); |
| } |
| Dart_Handle byte_buffer = Dart_NewByteBuffer(byte_array); |
| EXPECT_VALID(byte_buffer); |
| EXPECT(Dart_IsByteBuffer(byte_buffer)); |
| EXPECT(!Dart_IsTypedData(byte_buffer)); |
| |
| Dart_Handle byte_buffer_data = Dart_GetDataFromByteBuffer(byte_buffer); |
| EXPECT_VALID(byte_buffer_data); |
| EXPECT(!Dart_IsByteBuffer(byte_buffer_data)); |
| EXPECT(Dart_IsTypedData(byte_buffer_data)); |
| |
| intptr_t length = 0; |
| Dart_Handle result = Dart_ListLength(byte_buffer_data, &length); |
| EXPECT_VALID(result); |
| EXPECT_EQ(10, length); |
| |
| for (intptr_t i = 0; i < 10; ++i) { |
| // Get through the List API. |
| Dart_Handle integer_obj = Dart_ListGetAt(byte_buffer_data, 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); |
| } |
| |
| // Some negative tests. |
| result = Dart_NewByteBuffer(Dart_True()); |
| EXPECT(Dart_IsError(result)); |
| result = Dart_NewByteBuffer(byte_buffer); |
| EXPECT(Dart_IsError(result)); |
| result = Dart_GetDataFromByteBuffer(Dart_False()); |
| EXPECT(Dart_IsError(result)); |
| result = Dart_GetDataFromByteBuffer(byte_array); |
| EXPECT(Dart_IsError(result)); |
| } |
| |
| |
| static int kLength = 16; |
| |
| static void ByteDataNativeFunction(Dart_NativeArguments args) { |
| Dart_EnterScope(); |
| Dart_Handle byte_data = Dart_NewTypedData(Dart_TypedData_kByteData, kLength); |
| EXPECT_VALID(byte_data); |
| EXPECT_EQ(Dart_TypedData_kByteData, Dart_GetTypeOfTypedData(byte_data)); |
| Dart_SetReturnValue(args, byte_data); |
| Dart_ExitScope(); |
| } |
| |
| |
| static Dart_NativeFunction ByteDataNativeResolver(Dart_Handle name, |
| int arg_count, |
| bool* auto_setup_scope) { |
| ASSERT(auto_setup_scope != NULL); |
| *auto_setup_scope = false; |
| 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, NULL); |
| 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( |
| Dart_TypedData_kByteData, data, 16); |
| EXPECT_VALID(external_byte_data); |
| EXPECT_EQ(Dart_TypedData_kByteData, |
| Dart_GetTypeOfTypedData(external_byte_data)); |
| Dart_SetReturnValue(args, external_byte_data); |
| Dart_ExitScope(); |
| } |
| |
| |
| static Dart_NativeFunction ExternalByteDataNativeResolver( |
| Dart_Handle name, int arg_count, bool* auto_setup_scope) { |
| ASSERT(auto_setup_scope != NULL); |
| *auto_setup_scope = false; |
| 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, |
| NULL); |
| 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]); |
| } |
| } |
| |
| |
| static const intptr_t kOptExtLength = 16; |
| static int8_t opt_data[kOptExtLength] = { 0x01, 0x02, 0x03, 0x04, |
| 0x05, 0x06, 0x07, 0x08, |
| 0x09, 0x0a, 0x0b, 0x0c, |
| 0x0d, 0x0e, 0x0f, 0x10, }; |
| |
| static void OptExternalByteDataNativeFunction(Dart_NativeArguments args) { |
| Dart_EnterScope(); |
| Dart_Handle external_byte_data = Dart_NewExternalTypedData( |
| Dart_TypedData_kByteData, opt_data, 16); |
| EXPECT_VALID(external_byte_data); |
| EXPECT_EQ(Dart_TypedData_kByteData, |
| Dart_GetTypeOfTypedData(external_byte_data)); |
| Dart_SetReturnValue(args, external_byte_data); |
| Dart_ExitScope(); |
| } |
| |
| |
| static Dart_NativeFunction OptExternalByteDataNativeResolver( |
| Dart_Handle name, int arg_count, bool* auto_setup_scope) { |
| ASSERT(auto_setup_scope != NULL); |
| *auto_setup_scope = false; |
| return &OptExternalByteDataNativeFunction; |
| } |
| |
| |
| TEST_CASE(OptimizedExternalByteDataAccess) { |
| 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';" |
| "access(ByteData a) {" |
| " Expect.equals(0x04030201, a.getUint32(0, Endianness.LITTLE_ENDIAN));" |
| " Expect.equals(0x08070605, a.getUint32(4, Endianness.LITTLE_ENDIAN));" |
| " Expect.equals(0x0c0b0a09, a.getUint32(8, Endianness.LITTLE_ENDIAN));" |
| " Expect.equals(0x100f0e0d, a.getUint32(12, Endianness.LITTLE_ENDIAN));" |
| "}" |
| "ByteData main() {" |
| " var length = 16;" |
| " var a = createExternalByteData();" |
| " Expect.equals(length, a.lengthInBytes);" |
| " for (int i = 0; i < 20; i++) {" |
| " access(a);" |
| " }" |
| " 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, &OptExternalByteDataNativeResolver, NULL); |
| EXPECT_VALID(result); |
| |
| // Invoke 'main' function. |
| int old_oct = FLAG_optimization_counter_threshold; |
| FLAG_optimization_counter_threshold = 5; |
| result = Dart_Invoke(lib, NewString("main"), 0, NULL); |
| EXPECT_VALID(result); |
| FLAG_optimization_counter_threshold = old_oct; |
| } |
| |
| |
| static void TestTypedDataDirectAccess() { |
| Dart_Handle str = Dart_NewStringFromCString("junk"); |
| Dart_Handle byte_array = Dart_NewTypedData(Dart_TypedData_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'."); |
| } |
| |
| |
| TEST_CASE(TypedDataDirectAccessUnverified) { |
| FLAG_verify_acquired_data = false; |
| TestTypedDataDirectAccess(); |
| } |
| |
| |
| TEST_CASE(TypedDataDirectAccessVerified) { |
| FLAG_verify_acquired_data = true; |
| TestTypedDataDirectAccess(); |
| } |
| |
| |
| static void TestDirectAccess(Dart_Handle lib, |
| Dart_Handle array, |
| Dart_TypedData_Type expected_type, |
| bool is_external) { |
| 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]); |
| } |
| |
| if (!is_external) { |
| // Now try allocating a string with outstanding Acquires and it should |
| // return an error. |
| result = NewString("We expect an error here"); |
| EXPECT_ERROR(result, |
| "Internal Dart data pointers have been acquired, " |
| "please release them using Dart_TypedDataReleaseData."); |
| } |
| |
| // 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); |
| } |
| |
| |
| static void TestTypedDataDirectAccess1() { |
| 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, Dart_TypedData_kInt8, false); |
| |
| // 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(Dart_TypedData_kUint8, |
| data, data_length); |
| EXPECT_VALID(ext_list_access_test_obj); |
| TestDirectAccess(lib, ext_list_access_test_obj, Dart_TypedData_kUint8, true); |
| } |
| |
| |
| TEST_CASE(TypedDataDirectAccess1Unverified) { |
| FLAG_verify_acquired_data = false; |
| TestTypedDataDirectAccess1(); |
| } |
| |
| |
| TEST_CASE(TypedDataDirectAccess1Verified) { |
| FLAG_verify_acquired_data = true; |
| TestTypedDataDirectAccess1(); |
| } |
| |
| |
| static void TestTypedDataViewDirectAccess() { |
| 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, Dart_TypedData_kInt8, false); |
| } |
| |
| |
| TEST_CASE(TypedDataViewDirectAccessUnverified) { |
| FLAG_verify_acquired_data = false; |
| TestTypedDataViewDirectAccess(); |
| } |
| |
| |
| TEST_CASE(TypedDataViewDirectAccessVerified) { |
| FLAG_verify_acquired_data = true; |
| TestTypedDataViewDirectAccess(); |
| } |
| |
| |
| static void TestByteDataDirectAccess() { |
| 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, Dart_TypedData_kByteData, false); |
| } |
| |
| |
| TEST_CASE(ByteDataDirectAccessUnverified) { |
| FLAG_verify_acquired_data = false; |
| TestByteDataDirectAccess(); |
| } |
| |
| |
| TEST_CASE(ByteDataDirectAccessVerified) { |
| FLAG_verify_acquired_data = true; |
| TestByteDataDirectAccess(); |
| } |
| |
| |
| 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)); |
| |
| 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( |
| Dart_TypedData_kUint8, data, data_length); |
| ExternalTypedDataAccessTests(obj, Dart_TypedData_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( |
| Dart_TypedData_kUint8Clamped, data, data_length); |
| ExternalTypedDataAccessTests(obj, Dart_TypedData_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(Dart_TypedData_kUint8Clamped, |
| data, data_length); |
| 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 NopCallback(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* peer) { |
| } |
| |
| |
| static void ExternalTypedDataFinalizer(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* peer) { |
| *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( |
| Dart_TypedData_kUint8, |
| data, |
| ARRAY_SIZE(data)); |
| Dart_NewWeakPersistentHandle( |
| obj, &peer, sizeof(data), ExternalTypedDataFinalizer); |
| EXPECT_VALID(obj); |
| 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(Dart_TypedData_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(Dart_TypedData_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 }; |
| // Push a scope so that we can collect the local handle created as part of |
| // Dart_NewExternalTypedData. |
| Dart_EnterScope(); |
| { |
| Dart_Handle lcl = Dart_NewExternalTypedData( |
| Dart_TypedData_kFloat32x4, data, 10); |
| Dart_NewWeakPersistentHandle( |
| lcl, &peer, sizeof(data), ExternalTypedDataFinalizer); |
| CheckFloat32x4Data(lcl); |
| } |
| Dart_ExitScope(); |
| Isolate::Current()->heap()->CollectGarbage(Heap::kNew); |
| EXPECT(peer == 42); |
| } |
| |
| |
| // 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(Thread::Current()); |
| 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(); |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| EXPECT(isolate != NULL); |
| ApiState* state = isolate->api_state(); |
| EXPECT(state != NULL); |
| ApiLocalScope* scope = state->top_scope(); |
| Dart_PersistentHandle handles[2000]; |
| Dart_EnterScope(); |
| { |
| DARTSCOPE(Thread::Current()); |
| 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(thread); |
| HANDLESCOPE(thread); |
| for (int i = 0; i < 500; i++) { |
| String& str = String::Handle(); |
| str ^= PersistentHandle::Cast(handles[i])->raw(); |
| EXPECT(str.Equals(kTestString1)); |
| } |
| for (int i = 500; i < 1000; i++) { |
| String& str = String::Handle(); |
| str ^= PersistentHandle::Cast(handles[i])->raw(); |
| EXPECT(str.Equals(kTestString2)); |
| } |
| for (int i = 1000; i < 1500; i++) { |
| String& str = String::Handle(); |
| str ^= PersistentHandle::Cast(handles[i])->raw(); |
| EXPECT(str.Equals(kTestString1)); |
| } |
| for (int i = 1500; i < 2000; i++) { |
| String& str = String::Handle(); |
| str ^= PersistentHandle::Cast(handles[i])->raw(); |
| 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(Thread::Current()); |
| |
| // Start with a known persistent handle. |
| Dart_PersistentHandle obj1 = Dart_NewPersistentHandle(Dart_True()); |
| EXPECT(state->IsValidPersistentHandle(obj1)); |
| |
| // And use it to allocate a second persistent handle. |
| Dart_Handle obj2 = Dart_HandleFromPersistent(obj1); |
| Dart_PersistentHandle obj3 = Dart_NewPersistentHandle(obj2); |
| EXPECT(state->IsValidPersistentHandle(obj3)); |
| |
| // Make sure that the value transferred. |
| Dart_Handle obj4 = Dart_HandleFromPersistent(obj3); |
| EXPECT(Dart_IsBoolean(obj4)); |
| bool value = false; |
| Dart_Handle result = Dart_BooleanValue(obj4, &value); |
| EXPECT_VALID(result); |
| EXPECT(value); |
| } |
| |
| |
| // Test that we can assign to a persistent handle. |
| UNIT_TEST_CASE(AssignToPersistentHandle) { |
| const char* kTestString1 = "Test String1"; |
| const char* kTestString2 = "Test String2"; |
| TestIsolateScope __test_isolate__; |
| |
| Isolate* isolate = Isolate::Current(); |
| EXPECT(isolate != NULL); |
| ApiState* state = isolate->api_state(); |
| EXPECT(state != NULL); |
| DARTSCOPE(Thread::Current()); |
| String& str = String::Handle(); |
| |
| // Start with a known persistent handle. |
| Dart_Handle ref1 = Api::NewHandle(isolate, String::New(kTestString1)); |
| Dart_PersistentHandle obj = Dart_NewPersistentHandle(ref1); |
| EXPECT(state->IsValidPersistentHandle(obj)); |
| str ^= PersistentHandle::Cast(obj)->raw(); |
| EXPECT(str.Equals(kTestString1)); |
| |
| // Now create another local handle and assign it to the persistent handle. |
| Dart_Handle ref2 = Api::NewHandle(isolate, String::New(kTestString2)); |
| Dart_SetPersistentHandle(obj, ref2); |
| str ^= PersistentHandle::Cast(obj)->raw(); |
| EXPECT(str.Equals(kTestString2)); |
| |
| // Now assign Null to the persistent handle and check. |
| Dart_SetPersistentHandle(obj, Dart_Null()); |
| EXPECT(Dart_IsNull(obj)); |
| } |
| |
| |
| // 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); |
| } |
| }; |
| |
| |
| static Dart_Handle AsHandle(Dart_PersistentHandle weak) { |
| return Dart_HandleFromPersistent(weak); |
| } |
| |
| |
| static Dart_Handle AsHandle(Dart_WeakPersistentHandle weak) { |
| return Dart_HandleFromWeakPersistent(weak); |
| } |
| |
| |
| static Dart_WeakPersistentHandle weak_new_ref = NULL; |
| static Dart_WeakPersistentHandle weak_old_ref = NULL; |
| |
| |
| static void WeakPersistentHandleCallback(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* peer) { |
| if (handle == weak_new_ref) { |
| weak_new_ref = NULL; |
| } else if (handle == weak_old_ref) { |
| weak_old_ref = NULL; |
| } |
| } |
| |
| |
| TEST_CASE(WeakPersistentHandle) { |
| Dart_Handle local_new_ref = Dart_Null(); |
| weak_new_ref = Dart_NewWeakPersistentHandle( |
| local_new_ref, NULL, 0, WeakPersistentHandleCallback); |
| |
| Dart_Handle local_old_ref = Dart_Null(); |
| weak_old_ref = Dart_NewWeakPersistentHandle( |
| local_old_ref, NULL, 0, WeakPersistentHandleCallback); |
| |
| { |
| 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(Thread::Current()); |
| 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, |
| 0, |
| WeakPersistentHandleCallback); |
| EXPECT_VALID(AsHandle(weak_new_ref)); |
| EXPECT(!Dart_IsNull(AsHandle(weak_new_ref))); |
| |
| // Create a weak ref to the old space object. |
| weak_old_ref = Dart_NewWeakPersistentHandle(old_ref, |
| NULL, |
| 0, |
| WeakPersistentHandleCallback); |
| EXPECT_VALID(AsHandle(weak_old_ref)); |
| EXPECT(!Dart_IsNull(AsHandle(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(AsHandle(weak_new_ref)); |
| EXPECT(!Dart_IsNull(AsHandle(weak_new_ref))); |
| EXPECT(Dart_IdentityEquals(new_ref, AsHandle(weak_new_ref))); |
| |
| EXPECT_VALID(AsHandle(weak_old_ref)); |
| EXPECT(!Dart_IsNull(AsHandle(weak_old_ref))); |
| EXPECT(Dart_IdentityEquals(old_ref, AsHandle(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(AsHandle(weak_new_ref)); |
| EXPECT(!Dart_IsNull(AsHandle(weak_new_ref))); |
| EXPECT(Dart_IdentityEquals(new_ref, AsHandle(weak_new_ref))); |
| |
| EXPECT_VALID(AsHandle(weak_old_ref)); |
| EXPECT(!Dart_IsNull(AsHandle(weak_old_ref))); |
| EXPECT(Dart_IdentityEquals(old_ref, AsHandle(weak_old_ref))); |
| |
| // Delete local (strong) references. |
| Dart_ExitScope(); |
| } |
| |
| // Garbage collect new space again. |
| GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks); |
| |
| { |
| Dart_EnterScope(); |
| // Weak ref to new space object should now be cleared. |
| EXPECT(weak_new_ref == NULL); |
| EXPECT_VALID(AsHandle(weak_old_ref)); |
| EXPECT(!Dart_IsNull(AsHandle(weak_old_ref))); |
| Dart_ExitScope(); |
| } |
| |
| // Garbage collect old space again. |
| Isolate::Current()->heap()->CollectGarbage(Heap::kOld); |
| |
| { |
| Dart_EnterScope(); |
| // Weak ref to old space object should now be cleared. |
| EXPECT(weak_new_ref == NULL); |
| EXPECT(weak_old_ref == NULL); |
| Dart_ExitScope(); |
| } |
| |
| // Garbage collect one last time to revisit deleted handles. |
| Isolate::Current()->heap()->CollectGarbage(Heap::kNew); |
| Isolate::Current()->heap()->CollectGarbage(Heap::kOld); |
| } |
| |
| |
| static void WeakPersistentHandlePeerFinalizer(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* peer) { |
| *static_cast<int*>(peer) = 42; |
| } |
| |
| |
| TEST_CASE(WeakPersistentHandleCallback) { |
| Dart_WeakPersistentHandle weak_ref = NULL; |
| int peer = 0; |
| { |
| Dart_EnterScope(); |
| Dart_Handle obj = NewString("new string"); |
| EXPECT_VALID(obj); |
| weak_ref = Dart_NewWeakPersistentHandle(obj, &peer, 0, |
| WeakPersistentHandlePeerFinalizer); |
| EXPECT_VALID(AsHandle(weak_ref)); |
| EXPECT(peer == 0); |
| Dart_ExitScope(); |
| } |
| Isolate::Current()->heap()->CollectGarbage(Heap::kOld); |
| EXPECT(peer == 0); |
| GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks); |
| EXPECT(peer == 42); |
| } |
| |
| |
| TEST_CASE(WeakPersistentHandleNoCallback) { |
| Dart_WeakPersistentHandle weak_ref = NULL; |
| int peer = 0; |
| { |
| Dart_EnterScope(); |
| Dart_Handle obj = NewString("new string"); |
| EXPECT_VALID(obj); |
| weak_ref = Dart_NewWeakPersistentHandle(obj, &peer, 0, |
| 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_Isolate isolate = reinterpret_cast<Dart_Isolate>(Isolate::Current()); |
| Dart_DeleteWeakPersistentHandle(isolate, 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, |
| 0, |
| WeakPersistentHandlePeerFinalizer); |
| Dart_ShutdownIsolate(); |
| EXPECT(peer == 42); |
| } |
| |
| |
| TEST_CASE(WeakPersistentHandleExternalAllocationSize) { |
| Heap* heap = Isolate::Current()->heap(); |
| EXPECT(heap->ExternalInWords(Heap::kNew) == 0); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == 0); |
| Dart_WeakPersistentHandle weak1 = NULL; |
| static const intptr_t kWeak1ExternalSize = 1 * KB; |
| { |
| Dart_EnterScope(); |
| Dart_Handle obj = NewString("weakly referenced string"); |
| EXPECT_VALID(obj); |
| weak1 = Dart_NewWeakPersistentHandle(obj, |
| NULL, |
| kWeak1ExternalSize, |
| NopCallback); |
| EXPECT_VALID(AsHandle(weak1)); |
| EXPECT(!Dart_IsPrologueWeakPersistentHandle(weak1)); |
| Dart_ExitScope(); |
| } |
| Dart_PersistentHandle strong_ref = NULL; |
| Dart_WeakPersistentHandle weak2 = NULL; |
| static const intptr_t kWeak2ExternalSize = 2 * KB; |
| { |
| Dart_EnterScope(); |
| Dart_Handle obj = NewString("strongly referenced string"); |
| EXPECT_VALID(obj); |
| strong_ref = Dart_NewPersistentHandle(obj); |
| weak2 = Dart_NewWeakPersistentHandle(obj, |
| NULL, |
| kWeak2ExternalSize, |
| NopCallback); |
| EXPECT_VALID(AsHandle(strong_ref)); |
| Dart_ExitScope(); |
| } |
| Isolate::Current()->heap()->CollectGarbage(Heap::kOld); |
| EXPECT(heap->ExternalInWords(Heap::kNew) == |
| (kWeak1ExternalSize + kWeak2ExternalSize) / kWordSize); |
| // Collect weakly referenced string, and promote strongly referenced string. |
| GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks); |
| GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks); |
| EXPECT(heap->ExternalInWords(Heap::kNew) == 0); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == kWeak2ExternalSize / kWordSize); |
| Dart_Isolate isolate = reinterpret_cast<Dart_Isolate>(Isolate::Current()); |
| Dart_DeleteWeakPersistentHandle(isolate, weak1); |
| Dart_DeleteWeakPersistentHandle(isolate, weak2); |
| Dart_DeletePersistentHandle(strong_ref); |
| Isolate::Current()->heap()->CollectGarbage(Heap::kOld); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == 0); |
| } |
| |
| |
| TEST_CASE(PrologueWeakPersistentHandleExternalAllocationSize) { |
| Heap* heap = Isolate::Current()->heap(); |
| EXPECT(heap->ExternalInWords(Heap::kNew) == 0); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == 0); |
| Dart_WeakPersistentHandle pwph = NULL; |
| static const intptr_t kWeakExternalSize = 1 * KB; |
| { |
| Dart_EnterScope(); |
| Dart_Handle obj = NewString("a string"); |
| EXPECT_VALID(obj); |
| pwph = Dart_NewPrologueWeakPersistentHandle( |
| obj, NULL, kWeakExternalSize, NopCallback); |
| EXPECT_VALID(AsHandle(pwph)); |
| Dart_ExitScope(); |
| } |
| EXPECT(heap->ExternalInWords(Heap::kNew) == kWeakExternalSize / kWordSize); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == 0); |
| // Promoting the string should transfer the external size to old. |
| GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks); |
| GCTestHelper::CollectNewSpace(Heap::kIgnoreApiCallbacks); |
| EXPECT(heap->ExternalInWords(Heap::kNew) == 0); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == kWeakExternalSize / kWordSize); |
| Isolate::Current()->heap()->CollectGarbage(Heap::kOld); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == 0); |
| } |
| |
| |
| TEST_CASE(WeakPersistentHandleExternalAllocationSizeNewspaceGC) { |
| Dart_Isolate isolate = reinterpret_cast<Dart_Isolate>(Isolate::Current()); |
| Heap* heap = Isolate::Current()->heap(); |
| Dart_WeakPersistentHandle weak1 = NULL; |
| // Large enough to exceed any new space limit. Not actually allocated. |
| const intptr_t kWeak1ExternalSize = 500 * MB; |
| { |
| Dart_EnterScope(); |
| Dart_Handle obj = NewString("weakly referenced string"); |
| EXPECT_VALID(obj); |
| // Triggers a scavenge immediately, since kWeak1ExternalSize is above limit. |
| weak1 = Dart_NewWeakPersistentHandle(obj, |
| NULL, |
| kWeak1ExternalSize, |
| NopCallback); |
| EXPECT_VALID(AsHandle(weak1)); |
| // ... but the object is still alive and not yet promoted, so external size |
| // in new space is still above the limit. Thus, even the following tiny |
| // external allocation will trigger another scavenge. |
| Dart_WeakPersistentHandle trigger = |
| Dart_NewWeakPersistentHandle(obj, NULL, 1, NopCallback); |
| EXPECT_VALID(AsHandle(trigger)); |
| Dart_DeleteWeakPersistentHandle(isolate, trigger); |
| // After the two scavenges above, 'obj' should now be promoted, hence its |
| // external size charged to old space. |
| { |
| DARTSCOPE(thread); |
| String& handle = String::Handle(thread->zone()); |
| handle ^= Api::UnwrapHandle(obj); |
| EXPECT(handle.IsOld()); |
| } |
| EXPECT(heap->ExternalInWords(Heap::kNew) == 0); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == kWeak1ExternalSize / kWordSize); |
| Dart_ExitScope(); |
| } |
| Dart_DeleteWeakPersistentHandle(isolate, weak1); |
| Isolate::Current()->heap()->CollectGarbage(Heap::kOld); |
| EXPECT(heap->ExternalInWords(Heap::kOld) == 0); |
| } |
| |
| |
| TEST_CASE(WeakPersistentHandleExternalAllocationSizeOldspaceGC) { |
| // Check that external allocation in old space can trigger GC. |
| Isolate* isolate = Isolate::Current(); |
| Dart_EnterScope(); |
| Dart_Handle live = Api::NewHandle(isolate, String::New("live", Heap::kOld)); |
| EXPECT_VALID(live); |
| Dart_WeakPersistentHandle weak = NULL; |
| EXPECT_EQ(0, isolate->heap()->ExternalInWords(Heap::kOld)); |
| const intptr_t kSmallExternalSize = 1 * KB; |
| { |
| Dart_EnterScope(); |
| Dart_Handle dead = Api::NewHandle(isolate, String::New("dead", Heap::kOld)); |
| EXPECT_VALID(dead); |
| weak = Dart_NewWeakPersistentHandle(dead, |
| NULL, |
| kSmallExternalSize, |
| NopCallback); |
| EXPECT_VALID(AsHandle(weak)); |
| Dart_ExitScope(); |
| } |
| EXPECT_EQ(kSmallExternalSize, |
| isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize); |
| // Large enough to trigger GC in old space. Not actually allocated. |
| const intptr_t kHugeExternalSize = (kWordSize == 4) ? 513 * MB : 1025 * MB; |
| Dart_NewWeakPersistentHandle(live, |
| NULL, |
| kHugeExternalSize, |
| NopCallback); |
| // Expect small garbage to be collected. |
| EXPECT_EQ(kHugeExternalSize, |
| isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize); |
| Dart_DeleteWeakPersistentHandle(reinterpret_cast<Dart_Isolate>(isolate), |
| weak); |
| Dart_ExitScope(); |
| } |
| |
| |
| TEST_CASE(WeakPersistentHandleExternalAllocationSizeOddReferents) { |
| Heap* heap = Isolate::Current()->heap(); |
| Dart_WeakPersistentHandle weak1 = NULL; |
| static const intptr_t kWeak1ExternalSize = 1 * KB; |
| Dart_WeakPersistentHandle weak2 = NULL; |
| static const intptr_t kWeak2ExternalSize = 2 * KB; |
| { |
| Dart_EnterScope(); |
| Dart_Handle dart_true = Dart_True(); // VM heap object. |
| EXPECT_VALID(dart_true); |
| weak1 = Dart_NewWeakPersistentHandle( |
| dart_true, NULL, kWeak1ExternalSize, NopCallback); |
| EXPECT_VALID |