| // 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 "vm/dart_api_impl.h" |
| #include "bin/builtin.h" |
| #include "bin/dartutils.h" |
| #include "include/dart_api.h" |
| #include "include/dart_native_api.h" |
| #include "include/dart_tools_api.h" |
| #include "platform/assert.h" |
| #include "platform/text_buffer.h" |
| #include "platform/utils.h" |
| #include "vm/class_finalizer.h" |
| #include "vm/compiler/jit/compiler.h" |
| #include "vm/dart.h" |
| #include "vm/dart_api_state.h" |
| #include "vm/debugger_api_impl_test.h" |
| #include "vm/flags.h" |
| #include "vm/heap/verifier.h" |
| #include "vm/lockers.h" |
| #include "vm/timeline.h" |
| #include "vm/unit_test.h" |
| |
| namespace dart { |
| |
| DECLARE_FLAG(bool, verify_acquired_data); |
| DECLARE_FLAG(bool, complete_timeline); |
| |
| #ifndef PRODUCT |
| |
| UNIT_TEST_CASE(DartAPI_DartInitializeAfterCleanup) { |
| EXPECT(Dart_SetVMFlags(TesterState::argc, TesterState::argv) == nullptr); |
| Dart_InitializeParams params; |
| memset(¶ms, 0, sizeof(Dart_InitializeParams)); |
| params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; |
| params.vm_snapshot_data = TesterState::vm_snapshot_data; |
| params.create_group = TesterState::create_callback; |
| params.shutdown_isolate = TesterState::shutdown_callback; |
| params.cleanup_group = TesterState::group_cleanup_callback; |
| params.start_kernel_isolate = true; |
| |
| // Reinitialize and ensure we can execute Dart code. |
| EXPECT(Dart_Initialize(¶ms) == nullptr); |
| { |
| TestIsolateScope scope; |
| const char* kScriptChars = |
| "@pragma('vm:entry-point', 'call')\n" |
| "int testMain() {\n" |
| " return 42;\n" |
| "}\n"; |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| Dart_Handle result = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| EXPECT_VALID(result); |
| int64_t value = 0; |
| EXPECT_VALID(Dart_IntegerToInt64(result, &value)); |
| EXPECT_EQ(42, value); |
| } |
| EXPECT(Dart_Cleanup() == nullptr); |
| } |
| |
| UNIT_TEST_CASE(DartAPI_DartInitializeCallsCodeObserver) { |
| EXPECT(Dart_SetVMFlags(TesterState::argc, TesterState::argv) == nullptr); |
| Dart_InitializeParams params; |
| memset(¶ms, 0, sizeof(Dart_InitializeParams)); |
| params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; |
| params.vm_snapshot_data = TesterState::vm_snapshot_data; |
| params.create_group = TesterState::create_callback; |
| params.shutdown_isolate = TesterState::shutdown_callback; |
| params.cleanup_group = TesterState::group_cleanup_callback; |
| params.start_kernel_isolate = true; |
| |
| bool was_called = false; |
| Dart_CodeObserver code_observer; |
| code_observer.data = &was_called; |
| code_observer.on_new_code = [](Dart_CodeObserver* observer, const char* name, |
| uintptr_t base, uintptr_t size) { |
| *static_cast<bool*>(observer->data) = true; |
| }; |
| params.code_observer = &code_observer; |
| |
| // Reinitialize and ensure we can execute Dart code. |
| EXPECT(Dart_Initialize(¶ms) == nullptr); |
| |
| // Wait for 5 seconds to let the kernel service load the snapshot, |
| // which should trigger calls to the code observer. |
| OS::Sleep(5); |
| |
| EXPECT(was_called); |
| EXPECT(Dart_Cleanup() == nullptr); |
| } |
| |
| UNIT_TEST_CASE(DartAPI_DartInitializeHeapSizes) { |
| Dart_InitializeParams params; |
| memset(¶ms, 0, sizeof(Dart_InitializeParams)); |
| params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; |
| params.vm_snapshot_data = TesterState::vm_snapshot_data; |
| params.create_group = TesterState::create_callback; |
| params.shutdown_isolate = TesterState::shutdown_callback; |
| params.cleanup_group = TesterState::group_cleanup_callback; |
| params.start_kernel_isolate = true; |
| |
| // Initialize with a normal heap size specification. |
| const char* options_1[] = {"--old-gen-heap-size=3192", |
| "--new-gen-semi-max-size=32"}; |
| EXPECT(Dart_SetVMFlags(2, options_1) == nullptr); |
| EXPECT(Dart_Initialize(¶ms) == nullptr); |
| EXPECT(FLAG_old_gen_heap_size == 3192); |
| EXPECT(FLAG_new_gen_semi_max_size == 32); |
| EXPECT(Dart_Cleanup() == nullptr); |
| |
| const char* options_2[] = {"--old-gen-heap-size=16384", |
| "--new-gen-semi-max-size=16384"}; |
| EXPECT(Dart_SetVMFlags(2, options_2) == nullptr); |
| EXPECT(Dart_Initialize(¶ms) == nullptr); |
| if (kMaxAddrSpaceMB == 4096) { |
| EXPECT(FLAG_old_gen_heap_size == 0); |
| EXPECT(FLAG_new_gen_semi_max_size == kDefaultNewGenSemiMaxSize); |
| } else { |
| EXPECT(FLAG_old_gen_heap_size == 16384); |
| EXPECT(FLAG_new_gen_semi_max_size == 16384); |
| } |
| EXPECT(Dart_Cleanup() == nullptr); |
| |
| const char* options_3[] = {"--old-gen-heap-size=30720", |
| "--new-gen-semi-max-size=30720"}; |
| EXPECT(Dart_SetVMFlags(2, options_3) == nullptr); |
| EXPECT(Dart_Initialize(¶ms) == nullptr); |
| if (kMaxAddrSpaceMB == 4096) { |
| EXPECT(FLAG_old_gen_heap_size == 0); |
| EXPECT(FLAG_new_gen_semi_max_size == kDefaultNewGenSemiMaxSize); |
| } else { |
| EXPECT(FLAG_old_gen_heap_size == 30720); |
| EXPECT(FLAG_new_gen_semi_max_size == 30720); |
| } |
| EXPECT(Dart_Cleanup() == nullptr); |
| } |
| |
| TEST_CASE(Dart_KillIsolate) { |
| const char* kScriptChars = |
| "@pragma('vm:entry-point', 'call')\n" |
| "int testMain() {\n" |
| " return 42;\n" |
| "}\n"; |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| Dart_Handle result = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| EXPECT_VALID(result); |
| int64_t value = 0; |
| EXPECT_VALID(Dart_IntegerToInt64(result, &value)); |
| EXPECT_EQ(42, value); |
| Dart_Isolate isolate = reinterpret_cast<Dart_Isolate>(Isolate::Current()); |
| Dart_KillIsolate(isolate); |
| result = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT_STREQ("isolate terminated by Isolate.kill", Dart_GetError(result)); |
| } |
| |
| class InfiniteLoopTask : public ThreadPool::Task { |
| public: |
| InfiniteLoopTask(Dart_Isolate* isolate, Monitor* monitor, bool* interrupted) |
| : isolate_(isolate), monitor_(monitor), interrupted_(interrupted) {} |
| virtual void Run() { |
| TestIsolateScope scope; |
| const char* kScriptChars = |
| "@pragma('vm:entry-point', 'call')\n" |
| "testMain() {\n" |
| " while(true) {};" |
| "}\n"; |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| *isolate_ = reinterpret_cast<Dart_Isolate>(Isolate::Current()); |
| { |
| MonitorLocker ml(monitor_); |
| ml.Notify(); |
| } |
| Dart_Handle result = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| // Test should run an infinite loop and expect that to be killed. |
| EXPECT(Dart_IsError(result)); |
| EXPECT_STREQ("isolate terminated by Isolate.kill", Dart_GetError(result)); |
| { |
| MonitorLocker ml(monitor_); |
| *interrupted_ = true; |
| ml.Notify(); |
| } |
| } |
| |
| private: |
| Dart_Isolate* isolate_; |
| Monitor* monitor_; |
| bool* interrupted_; |
| }; |
| |
| TEST_CASE(Dart_KillIsolatePriority) { |
| Monitor monitor; |
| bool interrupted = false; |
| Dart_Isolate isolate; |
| Dart::thread_pool()->Run<InfiniteLoopTask>(&isolate, &monitor, &interrupted); |
| { |
| MonitorLocker ml(&monitor); |
| ml.Wait(); |
| } |
| |
| Dart_KillIsolate(isolate); |
| |
| { |
| MonitorLocker ml(&monitor); |
| while (!interrupted) { |
| ml.Wait(); |
| } |
| } |
| EXPECT(interrupted); |
| } |
| |
| TEST_CASE(DartAPI_ErrorHandleBasics) { |
| const char* kScriptChars = |
| "@pragma('vm:entry-point', 'call')\n" |
| "void testMain() {\n" |
| " throw new Exception(\"bad news\");\n" |
| "}\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| Dart_Handle instance = Dart_True(); |
| Dart_Handle error = Api::NewError("myerror"); |
| Dart_Handle exception = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| |
| 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(ZONE_STR("Unhandled exception:\n" |
| "Exception: bad news\n" |
| "#0 testMain (%s:3:3)", |
| TestCase::url()), |
| 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(DartAPI_StackTraceInfo) { |
| const char* kScriptChars = |
| "bar() => throw new Error();\n" |
| "foo() => bar();\n" |
| "@pragma('vm:entry-point', 'call')\n" |
| "testMain() => foo();\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| Dart_Handle error = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| |
| 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_SUBSTRING("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_SUBSTRING("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_SUBSTRING("test-lib", cstr); |
| EXPECT_EQ(4, 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(DartAPI_DeepStackTraceInfo) { |
| const char* kScriptChars = |
| "foo(n) => n == 1 ? throw new Error() : foo(n-1);\n" |
| "@pragma('vm:entry-point', 'call')\n" |
| "testMain() => foo(100);\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| Dart_Handle error = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| |
| 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(101, frame_count); |
| // Test something bigger than the preallocated size to verify nothing was |
| // truncated. |
| EXPECT(101 > 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_SUBSTRING("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_SUBSTRING("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_SUBSTRING("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)); |
| } |
| |
| void VerifyStackOverflowStackTraceInfo(const char* script, |
| const char* top_frame_func_name, |
| const char* entry_func_name, |
| int expected_line_number, |
| int expected_column_number) { |
| Dart_Handle lib = TestCase::LoadTestScript(script, nullptr); |
| Dart_Handle error; |
| { |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| error = Dart_Invoke(lib, NewString(entry_func_name), 0, nullptr); |
| } |
| 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(top_frame_func_name, cstr); |
| Dart_StringToCString(script_url, &cstr); |
| EXPECT_STREQ(TestCase::url(), cstr); |
| EXPECT_EQ(expected_line_number, line_number); |
| EXPECT_EQ(expected_column_number, 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(DartAPI_StackOverflowStackTraceInfoBraceFunction1) { |
| int line = 2; |
| int col = 3; |
| VerifyStackOverflowStackTraceInfo( |
| "class C {\n" |
| " static foo(int i) { foo(i); }\n" |
| "}\n" |
| "testMain() => C.foo(10);\n", |
| "C.foo", "testMain", line, col); |
| } |
| |
| TEST_CASE(DartAPI_StackOverflowStackTraceInfoBraceFunction2) { |
| int line = 2; |
| int col = 3; |
| VerifyStackOverflowStackTraceInfo( |
| "class C {\n" |
| " static foo(int i, int j) {\n" |
| " foo(i, j);\n" |
| " }\n" |
| "}\n" |
| "testMain() => C.foo(10, 11);\n", |
| "C.foo", "testMain", line, col); |
| } |
| |
| TEST_CASE(DartAPI_StackOverflowStackTraceInfoArrowFunction) { |
| int line = 2; |
| int col = 3; |
| VerifyStackOverflowStackTraceInfo( |
| "class C {\n" |
| " static foo(int i) => foo(i);\n" |
| "}\n" |
| "testMain() => C.foo(10);\n", |
| "C.foo", "testMain", line, col); |
| } |
| |
| TEST_CASE(DartAPI_OutOfMemoryStackTraceInfo) { |
| const char* kScriptChars = |
| "var number_of_ints = 134000000;\n" |
| "@pragma('vm:entry-point', 'call')" |
| "testMain() {\n" |
| " new List<int>(number_of_ints)\n" |
| "}\n"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| Dart_Handle error = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| |
| 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(102, frame_count); |
| // Test something bigger than the preallocated size to verify nothing was |
| // truncated. |
| EXPECT(102 > StackTrace::kPreallocatedStackdepth); |
| |
| Dart_Handle function_name; |
| Dart_Handle script_url; |
| intptr_t line_number = 0; |
| intptr_t column_number = 0; |
| const char* cstr = ""; |
| const char* test_lib = "file:///test-lib"; |
| |
| // 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(3, line_number); |
| EXPECT_EQ(24, 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(4, 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(4, 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(6, 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 != nullptr); |
| *auto_setup_scope = true; |
| return CurrentStackTraceNative; |
| } |
| |
| TEST_CASE(DartAPI_CurrentStackTraceInfo) { |
| const char* kScriptChars = R"( |
| @pragma("vm:external-name", "CurrentStackTraceNative") |
| external inspectStack(); |
| foo(n) => n == 1 ? inspectStack() : foo(n-1); |
| @pragma("vm:entry-point", "call") |
| testMain() => foo(100); |
| )"; |
| |
| Dart_Handle lib = |
| TestCase::LoadTestScript(kScriptChars, &CurrentStackTraceNativeLookup); |
| Dart_Handle result = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| EXPECT_VALID(result); |
| EXPECT(Dart_IsInteger(result)); |
| int64_t value = 0; |
| EXPECT_VALID(Dart_IntegerToInt64(result, &value)); |
| EXPECT_EQ(42, value); |
| } |
| |
| #endif // !PRODUCT |
| |
| TEST_CASE(DartAPI_ErrorHandleTypes) { |
| Dart_Handle not_error = NewString("NotError"); |
| Dart_Handle api_error = Dart_NewApiError("ApiError"); |
| Dart_Handle exception_error = |
| Dart_NewUnhandledExceptionError(NewString("ExceptionError")); |
| Dart_Handle compile_error = Dart_NewCompilationError("CompileError"); |
| Dart_Handle fatal_error; |
| { |
| TransitionNativeToVM transition(thread); |
| const String& fatal_message = String::Handle(String::New("FatalError")); |
| fatal_error = Api::NewHandle(thread, 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(DartAPI_UnhandleExceptionError) { |
| const char* exception_cstr = ""; |
| |
| // Test with an API Error. |
| const char* kApiError = "Api Error Exception Test."; |
| Dart_Handle api_error = Dart_NewApiError(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."; |
| Dart_Handle compile_error = Dart_NewCompilationError(kCompileError); |
| 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. |
| Dart_Handle fatal_error; |
| { |
| TransitionNativeToVM transition(thread); |
| const String& fatal_message = |
| String::Handle(String::New("FatalError Exception Test.")); |
| fatal_error = Api::NewHandle(thread, 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."; |
| exception_error = Dart_NewUnhandledExceptionError(NewString(kRegularString)); |
| 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 JustPropagateErrorNative(Dart_NativeArguments args) { |
| Dart_Handle closure = Dart_GetNativeArgument(args, 0); |
| EXPECT(Dart_IsClosure(closure)); |
| Dart_Handle result = Dart_InvokeClosure(closure, 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| Dart_PropagateError(result); |
| UNREACHABLE(); |
| } |
| |
| static Dart_NativeFunction JustPropagateError_lookup(Dart_Handle name, |
| int argument_count, |
| bool* auto_setup_scope) { |
| ASSERT(auto_setup_scope != nullptr); |
| *auto_setup_scope = true; |
| return JustPropagateErrorNative; |
| } |
| |
| TEST_CASE(DartAPI_EnsureUnwindErrorHandled_WhenKilled) { |
| const char* kScriptChars = R"( |
| import 'dart:isolate'; |
| |
| exitRightNow() { |
| Isolate.current.kill(priority: Isolate.immediate); |
| } |
| |
| @pragma("vm:external-name", "Test_nativeFunc") |
| external void nativeFunc(closure); |
| |
| @pragma("vm:entry-point", "call") |
| void Func1() { |
| nativeFunc(() => exitRightNow()); |
| } |
| )"; |
| Dart_Handle lib = |
| TestCase::LoadTestScript(kScriptChars, &JustPropagateError_lookup); |
| Dart_Handle result; |
| |
| result = Dart_Invoke(lib, NewString("Func1"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT_SUBSTRING("isolate terminated by Isolate.kill", Dart_GetError(result)); |
| |
| result = Dart_Invoke(lib, NewString("Func1"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT_SUBSTRING("No api calls are allowed while unwind is in progress", |
| Dart_GetError(result)); |
| } |
| |
| TEST_CASE(DartAPI_EnsureUnwindErrorHandled_WhenSendAndExit) { |
| const char* kScriptChars = R"( |
| import 'dart:isolate'; |
| |
| sendAndExitNow() { |
| final receivePort = ReceivePort(); |
| Isolate.exit(receivePort.sendPort, true); |
| } |
| |
| @pragma("vm:external-name", "Test_nativeFunc") |
| external void nativeFunc(closure); |
| |
| @pragma("vm:entry-point", "call") |
| void Func1() { |
| nativeFunc(() => sendAndExitNow()); |
| } |
| )"; |
| Dart_Handle lib = |
| TestCase::LoadTestScript(kScriptChars, &JustPropagateError_lookup); |
| Dart_Handle result; |
| |
| result = Dart_Invoke(lib, NewString("Func1"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT_SUBSTRING("isolate terminated by Isolate.exit", Dart_GetError(result)); |
| |
| result = Dart_Invoke(lib, NewString("Func1"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT_SUBSTRING("No api calls are allowed while unwind is in progress", |
| Dart_GetError(result)); |
| } |
| |
| // Should we propagate the error via Dart_SetReturnValue? |
| static bool use_set_return = false; |
| |
| // Should we propagate the error via Dart_ThrowException? |
| static bool use_throw_exception = false; |
| |
| void PropagateErrorNative(Dart_NativeArguments args) { |
| Dart_Handle closure = Dart_GetNativeArgument(args, 0); |
| EXPECT(Dart_IsClosure(closure)); |
| Dart_Handle result = Dart_InvokeClosure(closure, 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| if (use_set_return) { |
| Dart_SetReturnValue(args, result); |
| } else if (use_throw_exception) { |
| result = Dart_ThrowException(result); |
| EXPECT_VALID(result); // We do not expect to reach here. |
| UNREACHABLE(); |
| } else { |
| Dart_PropagateError(result); |
| UNREACHABLE(); |
| } |
| } |
| |
| static Dart_NativeFunction PropagateError_native_lookup( |
| Dart_Handle name, |
| int argument_count, |
| bool* auto_setup_scope) { |
| ASSERT(auto_setup_scope != nullptr); |
| *auto_setup_scope = true; |
| return PropagateErrorNative; |
| } |
| |
| TEST_CASE(DartAPI_PropagateCompileTimeError) { |
| const char* kScriptChars = R"( |
| raiseCompileError() { |
| return missing_semicolon |
| } |
| |
| @pragma("vm:external-name", "Test_nativeFunc") |
| external void nativeFunc(closure); |
| |
| @pragma("vm:entry-point", "call") |
| void Func1() { |
| nativeFunc(() => raiseCompileError()); |
| } |
| )"; |
| Dart_Handle lib = |
| TestCase::LoadTestScript(kScriptChars, &PropagateError_native_lookup); |
| Dart_Handle result; |
| |
| // Use Dart_PropagateError to propagate the error. |
| use_throw_exception = false; |
| use_set_return = false; |
| |
| result = Dart_Invoke(lib, NewString("Func1"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| |
| EXPECT_SUBSTRING("Expected ';' after this.", Dart_GetError(result)); |
| |
| // Use Dart_SetReturnValue to propagate the error. |
| use_throw_exception = false; |
| use_set_return = true; |
| |
| result = Dart_Invoke(lib, NewString("Func1"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT_SUBSTRING("Expected ';' after this.", Dart_GetError(result)); |
| |
| // Use Dart_ThrowException to propagate the error. |
| use_throw_exception = true; |
| use_set_return = false; |
| |
| result = Dart_Invoke(lib, NewString("Func1"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT_SUBSTRING("Expected ';' after this.", Dart_GetError(result)); |
| } |
| |
| TEST_CASE(DartAPI_PropagateError) { |
| const char* kScriptChars = R"( |
| void throwException() { |
| throw new Exception('myException'); |
| } |
| |
| @pragma("vm:external-name", "Test_nativeFunc") |
| external void nativeFunc(closure); |
| |
| @pragma("vm:entry-point", "call") |
| void Func2() { |
| nativeFunc(() => throwException()); |
| } |
| )"; |
| Dart_Handle lib = |
| TestCase::LoadTestScript(kScriptChars, &PropagateError_native_lookup); |
| Dart_Handle result; |
| |
| // Use Dart_PropagateError to propagate the error. |
| use_throw_exception = false; |
| use_set_return = false; |
| |
| result = Dart_Invoke(lib, NewString("Func2"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT(Dart_ErrorHasException(result)); |
| EXPECT_SUBSTRING("myException", Dart_GetError(result)); |
| |
| // Use Dart_SetReturnValue to propagate the error. |
| use_throw_exception = false; |
| use_set_return = true; |
| |
| result = Dart_Invoke(lib, NewString("Func2"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT(Dart_ErrorHasException(result)); |
| EXPECT_SUBSTRING("myException", Dart_GetError(result)); |
| |
| // Use Dart_ThrowException to propagate the error. |
| use_throw_exception = true; |
| use_set_return = false; |
| |
| result = Dart_Invoke(lib, NewString("Func2"), 0, nullptr); |
| EXPECT(Dart_IsError(result)); |
| EXPECT(Dart_ErrorHasException(result)); |
| EXPECT_SUBSTRING("myException", Dart_GetError(result)); |
| } |
| |
| TEST_CASE(DartAPI_Error) { |
| Dart_Handle error; |
| { |
| TransitionNativeToVM transition(thread); |
| error = Api::NewError("An %s", "error"); |
| } |
| EXPECT(Dart_IsError(error)); |
| EXPECT_STREQ("An error", Dart_GetError(error)); |
| } |
| |
| TEST_CASE(DartAPI_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(DartAPI_EmptyString) { |
| Dart_Handle empty = Dart_EmptyString(); |
| EXPECT_VALID(empty); |
| EXPECT(!Dart_IsNull(empty)); |
| EXPECT(Dart_IsString(empty)); |
| intptr_t length = -1; |
| EXPECT_VALID(Dart_StringLength(empty, &length)); |
| EXPECT_EQ(0, length); |
| } |
| |
| TEST_CASE(DartAPI_TypeDynamic) { |
| Dart_Handle type = Dart_TypeDynamic(); |
| EXPECT_VALID(type); |
| EXPECT(Dart_IsType(type)); |
| |
| Dart_Handle str = Dart_ToString(type); |
| EXPECT_VALID(str); |
| const char* cstr = nullptr; |
| EXPECT_VALID(Dart_StringToCString(str, &cstr)); |
| EXPECT_STREQ("dynamic", cstr); |
| } |
| |
| TEST_CASE(DartAPI_TypeVoid) { |
| Dart_Handle type = Dart_TypeVoid(); |
| EXPECT_VALID(type); |
| EXPECT(Dart_IsType(type)); |
| |
| Dart_Handle str = Dart_ToString(type); |
| EXPECT_VALID(str); |
| const char* cstr = nullptr; |
| EXPECT_VALID(Dart_StringToCString(str, &cstr)); |
| EXPECT_STREQ("void", cstr); |
| } |
| |
| TEST_CASE(DartAPI_TypeNever) { |
| Dart_Handle type = Dart_TypeNever(); |
| EXPECT_VALID(type); |
| EXPECT(Dart_IsType(type)); |
| |
| Dart_Handle str = Dart_ToString(type); |
| EXPECT_VALID(str); |
| const char* cstr = nullptr; |
| EXPECT_VALID(Dart_StringToCString(str, &cstr)); |
| EXPECT_STREQ("Never", cstr); |
| } |
| |
| TEST_CASE(DartAPI_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. |
| { |
| CHECK_API_SCOPE(thread); |
| 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(DartAPI_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(DartAPI_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(DartAPI_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)); |
| { |
| TransitionNativeToVM transition(thread); |
| const Type& null_type_obj = Api::UnwrapTypeHandle(zone, type); |
| EXPECT(null_type_obj.ptr() == Type::NullType()); |
| } |
| |
| Dart_Handle instance = Dart_True(); |
| type = Dart_InstanceGetType(instance); |
| EXPECT_VALID(type); |
| EXPECT(Dart_IsType(type)); |
| { |
| TransitionNativeToVM transition(thread); |
| const Type& bool_type_obj = Api::UnwrapTypeHandle(zone, type); |
| EXPECT(bool_type_obj.ptr() == Type::BoolType()); |
| } |
| |
| // 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(DartAPI_FunctionName) { |
| const char* kScriptChars = "int getInt() { return 1; }\n"; |
| // Create a test library and Load up a test script in it. |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| |
| Dart_Handle closure = Dart_GetField(lib, NewString("getInt")); |
| EXPECT_VALID(closure); |
| if (Dart_IsClosure(closure)) { |
| closure = Dart_ClosureFunction(closure); |
| EXPECT_VALID(closure); |
| } |
| |
| Dart_Handle name = Dart_FunctionName(closure); |
| EXPECT_VALID(name); |
| const char* result_str = ""; |
| Dart_StringToCString(name, &result_str); |
| EXPECT_STREQ(result_str, "getInt"); |
| } |
| |
| TEST_CASE(DartAPI_FunctionOwner) { |
| const char* kScriptChars = "int getInt() { return 1; }\n"; |
| // Create a test library and Load up a test script in it. |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| |
| Dart_Handle closure = Dart_GetField(lib, NewString("getInt")); |
| EXPECT_VALID(closure); |
| if (Dart_IsClosure(closure)) { |
| closure = Dart_ClosureFunction(closure); |
| EXPECT_VALID(closure); |
| } |
| |
| const char* url = ""; |
| Dart_Handle owner = Dart_FunctionOwner(closure); |
| EXPECT_VALID(owner); |
| Dart_Handle owner_url = Dart_LibraryUrl(owner); |
| EXPECT_VALID(owner_url); |
| Dart_StringToCString(owner_url, &url); |
| |
| const char* lib_url = ""; |
| Dart_Handle library_url = Dart_LibraryUrl(lib); |
| EXPECT_VALID(library_url); |
| Dart_StringToCString(library_url, &lib_url); |
| |
| EXPECT_STREQ(url, lib_url); |
| } |
| |
| TEST_CASE(DartAPI_IsTearOff) { |
| const char* kScriptChars = |
| "int getInt() { return 1; }\n" |
| "getTearOff() => getInt;\n" |
| "Function foo = () { print('baz'); };\n" |
| "class Baz {\n" |
| " static int foo() => 42;\n" |
| " getTearOff() => bar;\n" |
| " int bar() => 24;\n" |
| "}\n" |
| "Baz getBaz() => Baz();\n"; |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| |
| // Check tear-off of top-level static method. |
| Dart_Handle get_tear_off = Dart_GetField(lib, NewString("getTearOff")); |
| EXPECT_VALID(get_tear_off); |
| EXPECT(Dart_IsTearOff(get_tear_off)); |
| Dart_Handle tear_off = Dart_InvokeClosure(get_tear_off, 0, nullptr); |
| EXPECT_VALID(tear_off); |
| EXPECT(Dart_IsTearOff(tear_off)); |
| |
| // Check anonymous closures are not considered tear-offs. |
| Dart_Handle anonymous_closure = Dart_GetField(lib, NewString("foo")); |
| EXPECT_VALID(anonymous_closure); |
| EXPECT(!Dart_IsTearOff(anonymous_closure)); |
| |
| Dart_Handle baz_cls = Dart_GetClass(lib, NewString("Baz")); |
| EXPECT_VALID(baz_cls); |
| |
| // Check tear-off for a static method in a class. |
| Dart_Handle closure = |
| Dart_GetStaticMethodClosure(lib, baz_cls, NewString("foo")); |
| EXPECT_VALID(closure); |
| EXPECT(Dart_IsTearOff(closure)); |
| |
| // Flutter will use Dart_IsTearOff in conjunction with Dart_ClosureFunction |
| // and Dart_FunctionIsStatic to prevent anonymous closures from being used to |
| // generate callback handles. We'll test that case here, just to be sure. |
| Dart_Handle function = Dart_ClosureFunction(closure); |
| EXPECT_VALID(function); |
| bool is_static = false; |
| Dart_Handle result = Dart_FunctionIsStatic(function, &is_static); |
| EXPECT_VALID(result); |
| EXPECT(is_static); |
| |
| // Check tear-off for an instance method in a class. |
| Dart_Handle instance = Dart_Invoke(lib, NewString("getBaz"), 0, nullptr); |
| EXPECT_VALID(instance); |
| closure = Dart_Invoke(instance, NewString("getTearOff"), 0, nullptr); |
| EXPECT_VALID(closure); |
| EXPECT(Dart_IsTearOff(closure)); |
| } |
| |
| TEST_CASE(DartAPI_FunctionIsStatic) { |
| const char* kScriptChars = |
| "int getInt() { return 1; }\n" |
| "class Foo { String getString() => 'foobar'; }\n"; |
| // Create a test library and Load up a test script in it. |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| |
| Dart_Handle closure = Dart_GetField(lib, NewString("getInt")); |
| EXPECT_VALID(closure); |
| if (Dart_IsClosure(closure)) { |
| closure = Dart_ClosureFunction(closure); |
| EXPECT_VALID(closure); |
| } |
| |
| bool is_static = false; |
| Dart_Handle result = Dart_FunctionIsStatic(closure, &is_static); |
| EXPECT_VALID(result); |
| EXPECT(is_static); |
| |
| Dart_Handle klass = |
| Dart_GetNonNullableType(lib, NewString("Foo"), 0, nullptr); |
| EXPECT_VALID(klass); |
| |
| Dart_Handle instance = Dart_Allocate(klass); |
| |
| closure = Dart_GetField(instance, NewString("getString")); |
| EXPECT_VALID(closure); |
| if (Dart_IsClosure(closure)) { |
| closure = Dart_ClosureFunction(closure); |
| EXPECT_VALID(closure); |
| } |
| |
| result = Dart_FunctionIsStatic(closure, &is_static); |
| EXPECT_VALID(result); |
| EXPECT(!is_static); |
| } |
| |
| TEST_CASE(DartAPI_ClosureFunction) { |
| const char* kScriptChars = "int getInt() { return 1; }\n"; |
| // Create a test library and Load up a test script in it. |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| |
| Dart_Handle closure = Dart_GetField(lib, NewString("getInt")); |
| EXPECT_VALID(closure); |
| EXPECT(Dart_IsClosure(closure)); |
| Dart_Handle closure_str = Dart_ToString(closure); |
| const char* result = ""; |
| Dart_StringToCString(closure_str, &result); |
| EXPECT(strstr(result, "getInt") != nullptr); |
| |
| Dart_Handle function = Dart_ClosureFunction(closure); |
| EXPECT_VALID(function); |
| EXPECT(Dart_IsFunction(function)); |
| Dart_Handle func_str = Dart_ToString(function); |
| Dart_StringToCString(func_str, &result); |
| EXPECT(strstr(result, "getInt")); |
| } |
| |
| TEST_CASE(DartAPI_GetStaticMethodClosure) { |
| const char* kScriptChars = |
| "class Foo {\n" |
| " static int getInt() {\n" |
| " return 1;\n" |
| " }\n" |
| " double getDouble() {\n" |
| " return 1.0;\n" |
| " }\n" |
| "}\n"; |
| // Create a test library and Load up a test script in it. |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| EXPECT_VALID(lib); |
| Dart_Handle foo_cls = Dart_GetClass(lib, NewString("Foo")); |
| EXPECT_VALID(foo_cls); |
| |
| Dart_Handle closure = |
| Dart_GetStaticMethodClosure(lib, foo_cls, NewString("getInt")); |
| EXPECT_VALID(closure); |
| EXPECT(Dart_IsClosure(closure)); |
| Dart_Handle closure_str = Dart_ToString(closure); |
| const char* result = ""; |
| Dart_StringToCString(closure_str, &result); |
| EXPECT_SUBSTRING("getInt", result); |
| |
| Dart_Handle function = Dart_ClosureFunction(closure); |
| EXPECT_VALID(function); |
| EXPECT(Dart_IsFunction(function)); |
| Dart_Handle func_str = Dart_ToString(function); |
| Dart_StringToCString(func_str, &result); |
| EXPECT_SUBSTRING("getInt", result); |
| |
| Dart_Handle cls = Dart_FunctionOwner(function); |
| EXPECT_VALID(cls); |
| EXPECT(Dart_IsInstance(cls)); |
| Dart_Handle cls_str = Dart_ClassName(cls); |
| Dart_StringToCString(cls_str, &result); |
| EXPECT_SUBSTRING("Foo", result); |
| |
| EXPECT_ERROR(Dart_ClassName(Dart_Null()), |
| "Dart_ClassName expects argument 'cls_type' to be non-null."); |
| EXPECT_ERROR( |
| Dart_GetStaticMethodClosure(Dart_Null(), foo_cls, NewString("getInt")), |
| "Dart_GetStaticMethodClosure expects argument 'library' to be non-null."); |
| EXPECT_ERROR( |
| Dart_GetStaticMethodClosure(lib, Dart_Null(), NewString("getInt")), |
| "Dart_GetStaticMethodClosure expects argument 'cls_type' to be " |
| "non-null."); |
| EXPECT_ERROR(Dart_GetStaticMethodClosure(lib, foo_cls, Dart_Null()), |
| "Dart_GetStaticMethodClosure expects argument 'function_name' " |
| "to be non-null."); |
| } |
| |
| TEST_CASE(DartAPI_ClassLibrary) { |
| Dart_Handle lib = Dart_LookupLibrary(NewString("dart:core")); |
| EXPECT_VALID(lib); |
| Dart_Handle type = Dart_GetNonNullableType(lib, NewString("int"), 0, nullptr); |
| EXPECT_VALID(type); |
| Dart_Handle result = Dart_ClassLibrary(type); |
| EXPECT_VALID(result); |
| Dart_Handle lib_url = Dart_LibraryUrl(result); |
| const char* str = nullptr; |
| Dart_StringToCString(lib_url, &str); |
| EXPECT_STREQ("dart:core", str); |
| } |
| |
| TEST_CASE(DartAPI_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(DartAPI_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(DartAPI_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(DartAPI_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"; |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| Dart_Handle result; |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| // Check int case. |
| result = Dart_Invoke(lib, NewString("getInt"), 0, nullptr); |
| EXPECT_VALID(result); |
| EXPECT(Dart_IsNumber(result)); |
| |
| // Check double case. |
| result = Dart_Invoke(lib, NewString("getDouble"), 0, nullptr); |
| EXPECT_VALID(result); |
| EXPECT(Dart_IsNumber(result)); |
| |
| // Check bool case. |
| result = Dart_Invoke(lib, NewString("getBool"), 0, nullptr); |
| EXPECT_VALID(result); |
| EXPECT(!Dart_IsNumber(result)); |
| |
| // Check null case. |
| result = Dart_Invoke(lib, NewString("getNull"), 0, nullptr); |
| EXPECT_VALID(result); |
| EXPECT(!Dart_IsNumber(result)); |
| } |
| |
| TEST_CASE(DartAPI_IntegerValues) { |
| const int64_t kIntegerVal1 = 100; |
| const int64_t kIntegerVal2 = 0xffffffff; |
| const char* kIntegerVal3 = "0x123456789123456789123456789"; |
| const uint64_t kIntegerVal4 = 0xffffffffffffffff; |
| const int64_t kIntegerVal5 = -0x7fffffffffffffff; |
| |
| 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); |
| |
| int64_t out = 0; |
| result = Dart_IntegerToInt64(val1, &out); |
| EXPECT_VALID(result); |
| EXPECT_EQ(kIntegerVal1, out); |
| |
| Dart_Handle val2 = Dart_NewInteger(kIntegerVal2); |
| EXPECT(Dart_IsInteger(val2)); |
| result = Dart_IntegerFitsIntoInt64(val2, &fits); |
| EXPECT_VALID(result); |
| EXPECT(fits); |
| |
| result = Dart_IntegerToInt64(val2, &out); |
| EXPECT_VALID(result); |
| EXPECT_EQ(kIntegerVal2, out); |
| |
| Dart_Handle val3 = Dart_NewIntegerFromHexCString(kIntegerVal3); |
| EXPECT(Dart_IsApiError(val3)); |
| |
| Dart_Handle val4 = Dart_NewIntegerFromUint64(kIntegerVal4); |
| EXPECT(Dart_IsApiError(val4)); |
| |
| Dart_Handle val5 = Dart_NewInteger(-1); |
| EXPECT_VALID(val5); |
| uint64_t out5 = 0; |
| result = Dart_IntegerToUint64(val5, &out5); |
| EXPECT(Dart_IsError(result)); |
| |
| Dart_Handle val6 = Dart_NewInteger(kIntegerVal5); |
| EXPECT_VALID(val6); |
| uint64_t out6 = 0; |
| result = Dart_IntegerToUint64(val6, &out6); |
| EXPECT(Dart_IsError(result)); |
| } |
| |
| TEST_CASE(DartAPI_IntegerToHexCString) { |
| const struct { |
| int64_t i; |
| const char* s; |
| } kIntTestCases[] = { |
| {0, "0x0"}, |
| {1, "0x1"}, |
| {-1, "-0x1"}, |
| {0x123, "0x123"}, |
| {-0xABCDEF, "-0xABCDEF"}, |
| {DART_INT64_C(-0x7FFFFFFFFFFFFFFF), "-0x7FFFFFFFFFFFFFFF"}, |
| {kMaxInt64, "0x7FFFFFFFFFFFFFFF"}, |
| {kMinInt64, "-0x8000000000000000"}, |
| }; |
| |
| const size_t kNumberOfIntTestCases = |
| sizeof(kIntTestCases) / sizeof(kIntTestCases[0]); |
| |
| for (size_t i = 0; i < kNumberOfIntTestCases; ++i) { |
| Dart_Handle val = Dart_NewInteger(kIntTestCases[i].i); |
| EXPECT_VALID(val); |
| const char* chars = nullptr; |
| Dart_Handle result = Dart_IntegerToHexCString(val, &chars); |
| EXPECT_VALID(result); |
| EXPECT_STREQ(kIntTestCases[i].s, chars); |
| } |
| } |
| |
| TEST_CASE(DartAPI_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("0x10000000000000000"); |
| EXPECT(Dart_IsApiError(above_max)); |
| |
| 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("-0x10000000000000001"); |
| EXPECT(Dart_IsApiError(below_min)); |
| } |
| |
| TEST_CASE(DartAPI_IntegerFitsIntoUint64) { |
| Dart_Handle max = Dart_NewIntegerFromUint64(kMaxUint64); |
| EXPECT(Dart_IsApiError(max)); |
| |
| Dart_Handle above_max = Dart_NewIntegerFromHexCString("0x10000000000000000"); |
| EXPECT(Dart_IsApiError(above_max)); |
| |
| Dart_Handle min = Dart_NewInteger(0); |
| EXPECT(Dart_IsInteger(min)); |
| bool fits = false; |
| Dart_Handle 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(DartAPI_ArrayValues) { |
| EXPECT(!Dart_IsList(Dart_Null())); |
| 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(DartAPI_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)); |
| 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)); |
| |
| 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); |
| for (intptr_t i = 0; i < len; i++) { |
| EXPECT_EQ(data8[i], latin1_array[i]); |
| } |
| |
| 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_VALID(Dart_StringGetProperties(str16, &char_size, &str_len, &peer)); |
| EXPECT_EQ(2, char_size); |
| EXPECT_EQ(4, str_len); |
| EXPECT(!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)); |
| } |
| |
| TEST_CASE(DartAPI_NewString) { |
| const char* ascii = "string"; |
| Dart_Handle ascii_str = NewString(ascii); |
| EXPECT_VALID(ascii_str); |
| EXPECT(Dart_IsString(ascii_str)); |
| |
| const char* null = nullptr; |
| 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(DartAPI_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 = |
| "@pragma('vm:entry-point', 'call')" |
| "String lowSurrogate() {" |
| " return '\\u{1D11E}'[1];" |
| "}" |
| "String highSurrogate() {" |
| " return '\\u{1D11E}'[0];" |
| "}" |
| "@pragma('vm:entry-point', 'call')" |
| "String reversed() => lowSurrogate() + highSurrogate();"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| Dart_Handle str1 = Dart_Invoke(lib, NewString("lowSurrogate"), 0, nullptr); |
| EXPECT_VALID(str1); |
| |
| uint8_t* utf8_encoded = nullptr; |
| intptr_t utf8_length = 0; |
| Dart_Handle result = Dart_StringToUTF8(str1, &utf8_encoded, &utf8_length); |
| EXPECT_VALID(result); |
| EXPECT_EQ(3, utf8_length); |
| // Unpaired surrogate is encoded as replacement character. |
| EXPECT_EQ(239, static_cast<intptr_t>(utf8_encoded[0])); |
| EXPECT_EQ(191, static_cast<intptr_t>(utf8_encoded[1])); |
| EXPECT_EQ(189, static_cast<intptr_t>(utf8_encoded[2])); |
| |
| Dart_Handle str2 = Dart_NewStringFromUTF8(utf8_encoded, utf8_length); |
| EXPECT_VALID(str2); // Replacement character, but still valid |
| |
| Dart_Handle reversed = Dart_Invoke(lib, NewString("reversed"), 0, nullptr); |
| EXPECT_VALID(reversed); // This is also allowed. |
| uint8_t* utf8_encoded_reversed = nullptr; |
| 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); |
| // Two unpaired surrogates are encoded as two replacement characters. |
| uint8_t expected[6] = {239, 191, 189, 239, 191, 189}; |
| for (int i = 0; i < 6; i++) { |
| EXPECT_EQ(expected[i], utf8_encoded_reversed[i]); |
| } |
| } |
| |
| TEST_CASE(DartAPI_CopyUTF8EncodingOfString) { |
| const char* kScriptChars = |
| "@pragma('vm:entry-point', 'call')" |
| "String lowSurrogate() {" |
| " return '\\u{1D11E}'[1];" |
| "}"; |
| |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| Dart_Handle str1 = Dart_Invoke(lib, NewString("lowSurrogate"), 0, nullptr); |
| EXPECT_VALID(str1); |
| |
| uint8_t* utf8_encoded = nullptr; |
| intptr_t utf8_length = 0; |
| Dart_Handle result = Dart_StringToUTF8(str1, &utf8_encoded, &utf8_length); |
| EXPECT_VALID(result); |
| EXPECT_EQ(3, utf8_length); |
| |
| intptr_t utf8_copy_length = 0; |
| result = Dart_StringUTF8Length(str1, &utf8_copy_length); |
| uint8_t* utf8_encoded_copy = Dart_ScopeAllocate(utf8_copy_length); |
| result = |
| Dart_CopyUTF8EncodingOfString(str1, utf8_encoded_copy, utf8_copy_length); |
| EXPECT_VALID(result); |
| EXPECT_EQ(0, memcmp(utf8_encoded, utf8_encoded_copy, utf8_length)); |
| } |
| |
| TEST_CASE(DartAPI_ExternalTypedDataPretenure) { |
| { |
| Dart_EnterScope(); |
| 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); |
| 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); |
| { |
| CHECK_API_SCOPE(thread); |
| TransitionNativeToVM transition(thread); |
| HANDLESCOPE(thread); |
| 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(DartAPI_ListAccess) { |
| const char* kScriptChars = |
| "List testMain() {" |
| " List a = List.empty(growable: true);" |
| " a.add(10);" |
| " a.add(20);" |
| " a.add(30);" |
| " return a;" |
| "}" |
| "" |
| "List immutable() {" |
| " return const [0, 1, 2];" |
| "}"; |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| Dart_Handle result; |
| |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| // Invoke a function which returns an object of type List. |
| result = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| 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, nullptr); |
| 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, nullptr); |
| 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(DartAPI_MapAccess) { |
| EXPECT(!Dart_IsMap(Dart_Null())); |
| const char* kScriptChars = |
| "@pragma('vm:entry-point', 'call')" |
| "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, nullptr); |
| |
| // Invoke a function which returns an object of type Map. |
| result = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| 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(DartAPI_IsFuture) { |
| const char* kScriptChars = |
| "import 'dart:async';" |
| "@pragma('vm:entry-point', 'call')" |
| "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, nullptr); |
| |
| // Invoke a function which returns an object of type Future. |
| result = Dart_Invoke(lib, NewString("testMain"), 0, nullptr); |
| 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(DartAPI_TypedDataViewListGetAsBytes) { |
| const int kSize = 1000; |
| |
| const char* kScriptChars = |
| "import 'dart:typed_data';\n" |
| "@pragma('vm:entry-point', 'call')\n" |
| "List testMain(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, nullptr); |
| |
| // 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("testMain"), 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(DartAPI_TypedDataViewListIsTypedData) { |
| const int kSize = 1000; |
| |
| const char* kScriptChars = |
| "import 'dart:typed_data';\n" |
| "@pragma('vm:entry-point', 'call')\n" |
| "List testMain(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, nullptr); |
| |
| // 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("testMain"), 1, dart_args); |
| EXPECT_VALID(view_obj); |
| // Test that the API considers it a TypedData object. |
| EXPECT(Dart_IsTypedData(view_obj)); |
| EXPECT_EQ(Dart_TypedData_kInt8, Dart_GetTypeOfTypedData(view_obj)); |
| } |
| |
| TEST_CASE(DartAPI_UnmodifiableTypedDataViewListIsTypedData) { |
| const int kSize = 1000; |
| |
| const char* kScriptChars = |
| "import 'dart:typed_data';\n" |
| "@pragma('vm:entry-point', 'call')\n" |
| "List testMain(int size) {\n" |
| " var a = new Int8List(size);\n" |
| " var view = a.asUnmodifiableView();\n" |
| " return view;\n" |
| "}\n"; |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| // 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("testMain"), 1, dart_args); |
| EXPECT_VALID(view_obj); |
| // Test that the API considers it a TypedData object. |
| EXPECT(Dart_IsTypedData(view_obj)); |
| EXPECT_EQ(Dart_TypedData_kInt8, Dart_GetTypeOfTypedData(view_obj)); |
| } |
| |
| TEST_CASE(DartAPI_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(DartAPI_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 != nullptr); |
| *auto_setup_scope = true; |
| return &ByteDataNativeFunction; |
| } |
| |
| TEST_CASE(DartAPI_ByteDataAccess) { |
| const char* kScriptChars = R"( |
| import 'dart:typed_data'; |
| class Expect { |
| static equals(a, b) { |
| if (a != b) { |
| throw 'not equal. expected: $a, got: $b'; |
| } |
| } |
| } |
| @pragma("vm:external-name", "CreateByteData") |
| external ByteData 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; |
| } |
| )"; |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| Dart_Handle result = |
| Dart_SetNativeResolver(lib, &ByteDataNativeResolver, nullptr); |
| EXPECT_VALID(result); |
| |
| // Invoke 'main' function. |
| result = Dart_Invoke(lib, NewString("main"), 0, nullptr); |
| EXPECT_VALID(result); |
| } |
| |
| static constexpr 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 != nullptr); |
| *auto_setup_scope = true; |
| return &ExternalByteDataNativeFunction; |
| } |
| |
| TEST_CASE(DartAPI_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 = R"( |
| import 'dart:typed_data'; |
| class Expect { |
| static equals(a, b) { |
| if (a != b) { |
| throw 'not equal. expected: $a, got: $b'; |
| } |
| } |
| } |
| @pragma("vm:external-name", "CreateExternalByteData") |
| external ByteData 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, Endian.little)); |
| } |
| 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, Endian.little)); |
| } |
| return a; |
| } |
| )"; |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| Dart_Handle result = |
| Dart_SetNativeResolver(lib, &ExternalByteDataNativeResolver, nullptr); |
| EXPECT_VALID(result); |
| |
| // Invoke 'main' function. |
| result = Dart_Invoke(lib, NewString("main"), 0, nullptr); |
| EXPECT_VALID(result); |
| |
| for (intptr_t i = 0; i < kExtLength; i += 2) { |
| EXPECT_EQ(0x24, data[i]); |
| EXPECT_EQ(0x28, data[i + 1]); |
| } |
| } |
| |
| static bool byte_data_finalizer_run = false; |
| void ByteDataFinalizer(void* isolate_data, void* peer) { |
| ASSERT(!byte_data_finalizer_run); |
| free(peer); |
| byte_data_finalizer_run = true; |
| } |
| |
| TEST_CASE(DartAPI_ExternalByteDataFinalizer) { |
| // Check finalizer associated with the underlying array instead of the |
| // wrapper. |
| const char* kScriptChars = |
| "var array;\n" |
| "@pragma('vm:entry-point', 'call')\n" |
| "extractAndSaveArray(byteData) {\n" |
| " array = byteData.buffer.asUint8List();\n" |
| "}\n" |
| "@pragma('vm:entry-point', 'call')\n" |
| "releaseArray() {\n" |
| " array = null;\n" |
| "}\n"; |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| { |
| Dart_EnterScope(); |
| |
| const intptr_t kBufferSize = 100; |
| void* buffer = malloc(kBufferSize); |
| // The buffer becomes readable by Dart, so ensure it is initialized to |
| // satisfy our eager MSAN check. |
| memset(buffer, 0, kBufferSize); |
| Dart_Handle byte_data = Dart_NewExternalTypedDataWithFinalizer( |
| Dart_TypedData_kByteData, buffer, kBufferSize, buffer, kBufferSize, |
| ByteDataFinalizer); |
| |
| Dart_Handle result = |
| Dart_Invoke(lib, NewString("extractAndSaveArray"), 1, &byte_data); |
| EXPECT_VALID(result); |
| |
| // ByteData wrapper is still reachable from the scoped handle. |
| EXPECT(!byte_data_finalizer_run); |
| |
| // The ByteData wrapper is now unreachable, but the underlying |
| // ExternalUint8List is still alive. |
| Dart_ExitScope(); |
| } |
| |
| { |
| TransitionNativeToVM transition(Thread::Current()); |
| GCTestHelper::CollectAllGarbage(); |
| } |
| |
| EXPECT(!byte_data_finalizer_run); |
| |
| Dart_Handle result = Dart_Invoke(lib, NewString("releaseArray"), 0, nullptr); |
| EXPECT_VALID(result); |
| |
| { |
| TransitionNativeToVM transition(Thread::Current()); |
| GCTestHelper::CollectAllGarbage(); |
| } |
| |
| EXPECT(byte_data_finalizer_run); |
| } |
| |
| #ifndef PRODUCT |
| |
| static constexpr 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 != nullptr); |
| *auto_setup_scope = true; |
| return &OptExternalByteDataNativeFunction; |
| } |
| |
| TEST_CASE(DartAPI_OptimizedExternalByteDataAccess) { |
| const char* kScriptChars = R"( |
| import 'dart:typed_data'; |
| class Expect { |
| static equals(a, b) { |
| if (a != b) { |
| throw 'not equal. expected: $a, got: $b'; |
| } |
| } |
| } |
| @pragma("vm:external-name", "CreateExternalByteData") |
| external ByteData createExternalByteData(); |
| access(ByteData a) { |
| Expect.equals(0x04030201, a.getUint32(0, Endian.little)); |
| Expect.equals(0x08070605, a.getUint32(4, Endian.little)); |
| Expect.equals(0x0c0b0a09, a.getUint32(8, Endian.little)); |
| Expect.equals(0x100f0e0d, a.getUint32(12, Endian.little)); |
| } |
| ByteData main() { |
| var length = 16; |
| var a = createExternalByteData(); |
| Expect.equals(length, a.lengthInBytes); |
| for (int i = 0; i < 20; i++) { |
| access(a); |
| } |
| return a; |
| } |
| )"; |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| Dart_Handle result = |
| Dart_SetNativeResolver(lib, &OptExternalByteDataNativeResolver, nullptr); |
| 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, nullptr); |
| EXPECT_VALID(result); |
| FLAG_optimization_counter_threshold = old_oct; |
| } |
| |
| #endif // !PRODUCT |
| |
| 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, nullptr, nullptr, nullptr); |
| EXPECT_ERROR(result, |
| "Dart_TypedDataAcquireData expects argument 'type'" |
| " to be non-null."); |
| Dart_TypedData_Type type; |
| result = Dart_TypedDataAcquireData(byte_array, &type, nullptr, nullptr); |
| EXPECT_ERROR(result, |
| "Dart_TypedDataAcquireData expects argument 'data'" |
| " to be non-null."); |
| void* data; |
| result = Dart_TypedDataAcquireData(byte_array, &type, &data, nullptr); |
| 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(DartAPI_TypedDataDirectAccessUnverified) { |
| FLAG_verify_acquired_data = false; |
| TestTypedDataDirectAccess(); |
| } |
| |
| TEST_CASE(DartAPI_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; |
| { |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| 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(!Thread::Current()->IsAtSafepoint()); |
| EXPECT_VALID(result); |
| EXPECT_EQ(expected_type, type); |
| EXPECT_EQ(kLength, len); |
| int8_t* dataP = reinterpret_cast<int8_t*>(data); |
| for (int i = 0; i < kLength; i++) { |
| EXPECT_EQ(i, dataP[i]); |
| } |
| |
| // Now try allocating a string with outstanding Acquires and it should |
| // return an error. |
| result = NewString("We expect an error here"); |
| EXPECT_ERROR(result, "Callbacks into the Dart VM are currently prohibited"); |
| |
| // 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 access to the typed data object. |
| EXPECT(!Thread::Current()->IsAtSafepoint()); |
| result = Dart_TypedDataReleaseData(array); |
| EXPECT_VALID(result); |
| |
| // Invoke the dart function in order to check the modified values. |
| { |
| SetFlagScope<bool> sfs(&FLAG_verify_entry_points, false); |
| result = Dart_Invoke(lib, NewString("testMain"), 1, dart_args); |
| } |
| EXPECT_VALID(result); |
| } |
| |
| class BackgroundGCTask : public ThreadPool::Task { |
| public: |
| BackgroundGCTask(IsolateGroup* isolate_group, Monitor* monitor, bool* done) |
| : isolate_group_(isolate_group), monitor_(monitor), done_(done) {} |
| virtual void Run() { |
| const bool kBypassSafepoint = false; |
| Thread::EnterIsolateGroupAsHelper(isolate_group_, Thread::kUnknownTask, |
| kBypassSafepoint); |
| for (intptr_t i = 0; i < 10; i++) { |
| GCTestHelper::CollectAllGarbage(); |
| } |
| Thread::ExitIsolateGroupAsHelper(kBypassSafepoint); |
| { |
| MonitorLocker ml(monitor_); |
| *done_ = true; |
| ml.Notify(); |
| } |
| } |
| |
| private: |
| IsolateGroup* isolate_group_; |
| Monitor* monitor_; |
| bool* done_; |
| }; |
| |
| 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, nullptr); |
| |
| Monitor monitor; |
| bool done = false; |
| Dart::thread_pool()->Run<BackgroundGCTask>(IsolateGroup::Current(), &monitor, |
| &done); |
| |
| for (intptr_t i = 0; i < 10; i++) { |
| // Test with an regular typed data object. |
| Dart_Handle list_access_test_obj; |
| list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, nullptr); |
| 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); |
| } |
| |
| { |
| MonitorLocker ml(&monitor); |
| while (!done) { |
| ml.Wait(); |
| } |
| } |
| } |
| |
| TEST_CASE(DartAPI_TypedDataDirectAccess1Unverified) { |
| FLAG_verify_acquired_data = false; |
| TestTypedDataDirectAccess1(); |
| } |
| |
| TEST_CASE(DartAPI_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, nullptr); |
| |
| // Test with a typed data view object. |
| Dart_Handle list_access_test_obj; |
| list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, nullptr); |
| EXPECT_VALID(list_access_test_obj); |
| TestDirectAccess(lib, list_access_test_obj, Dart_TypedData_kInt8, false); |
| } |
| |
| TEST_CASE(DartAPI_TypedDataViewDirectAccessUnverified) { |
| FLAG_verify_acquired_data = false; |
| TestTypedDataViewDirectAccess(); |
| } |
| |
| TEST_CASE(DartAPI_TypedDataViewDirectAccessVerified) { |
| FLAG_verify_acquired_data = true; |
| TestTypedDataViewDirectAccess(); |
| } |
| |
| static void TestUnmodifiableTypedDataViewDirectAccess() { |
| const char* kScriptChars = |
| "import 'dart:typed_data';\n" |
| "List main() {" |
| " var list = new Int8List(100);" |
| " for (var i = 0; i < 100; i++) {" |
| " list[i] = i;" |
| " }" |
| " return list.asUnmodifiableView();" |
| "}\n"; |
| // Create a test library and Load up a test script in it. |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| // Test with a typed data view object. |
| Dart_Handle view_obj; |
| view_obj = Dart_Invoke(lib, NewString("main"), 0, nullptr); |
| EXPECT_VALID(view_obj); |
| |
| const int kLength = 100; |
| Dart_TypedData_Type type; |
| void* data; |
| intptr_t len; |
| Dart_Handle result = Dart_TypedDataAcquireData(view_obj, &type, &data, &len); |
| EXPECT(!Thread::Current()->IsAtSafepoint()); |
| EXPECT_VALID(result); |
| EXPECT_EQ(Dart_TypedData_kInt8, type); |
| EXPECT_EQ(kLength, len); |
| int8_t* dataP = reinterpret_cast<int8_t*>(data); |
| for (int i = 0; i < kLength; i++) { |
| EXPECT_EQ(i, dataP[i]); |
| } |
| |
| // Now try allocating a string with outstanding Acquires and it should |
| // return an error. |
| result = NewString("We expect an error here"); |
| EXPECT_ERROR(result, "Callbacks into the Dart VM are currently prohibited"); |
| |
| // Release direct access to the typed data object. |
| EXPECT(!Thread::Current()->IsAtSafepoint()); |
| result = Dart_TypedDataReleaseData(view_obj); |
| EXPECT_VALID(result); |
| } |
| |
| TEST_CASE(DartAPI_UnmodifiableTypedDataViewDirectAccessUnverified) { |
| FLAG_verify_acquired_data = false; |
| TestUnmodifiableTypedDataViewDirectAccess(); |
| } |
| |
| TEST_CASE(DartAPI_UnmodifiableTypedDataViewDirectAccessVerified) { |
| FLAG_verify_acquired_data = true; |
| TestUnmodifiableTypedDataViewDirectAccess(); |
| } |
| |
| 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, nullptr); |
| |
| // Test with a typed data view object. |
| Dart_Handle list_access_test_obj; |
| list_access_test_obj = Dart_Invoke(lib, NewString("main"), 0, nullptr); |
| EXPECT_VALID(list_access_test_obj); |
| TestDirectAccess(lib, list_access_test_obj, Dart_TypedData_kByteData, false); |
| } |
| |
| TEST_CASE(DartAPI_ByteDataDirectAccessUnverified) { |
| FLAG_verify_acquired_data = false; |
| TestByteDataDirectAccess(); |
| } |
| |
| TEST_CASE(DartAPI_ByteDataDirectAccessVerified) { |
| FLAG_verify_acquired_data = true; |
| TestByteDataDirectAccess(); |
| } |
| |
| TEST_CASE(DartAPI_NewUnmodifiableExternalTypedDataWithFinalizer) { |
| const char* kScriptChars = R"( |
| class Expect { |
| static equals(a, b) { |
| if (a != b) { |
| throw 'not equal. expected: $a, got: $b'; |
| } |
| } |
| static throws(block) { |
| bool threw = false; |
| try { block(); } catch (e) { threw = true; } |
| if (!threw) throw 'did not throw'; |
| } |
| } |
| @pragma('vm:entry-point', 'call') |
| testList(data) { |
| for (var i = 0; i < data.length; i++) { |
| Expect.equals(i, data[i]); |
| Expect.throws(() => data[i] = 0); |
| } |
| } |
| @pragma('vm:entry-point', 'call') |
| testBytes(data) { |
| for (var i = 0; i < data.length; i++) { |
| Expect.equals(i, data.getUint8(i)); |
| Expect.throws(() => data.setUint8(i, 0)); |
| } |
| })"; |
| Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); |
| |
| { |
| uint8_t data[] = {0, 1, 2, 3}; |
| Dart_Handle typed_data = Dart_NewUnmodifiableExternalTypedDataWithFinalizer( |
| Dart_TypedData_kUint8, &data[0], ARRAY_SIZE(data), nullptr, 0, nullptr); |
| EXPECT_VALID(typed_data); |
| EXPECT_EQ(Dart_TypedData_kUint8, Dart_GetTypeOfTypedData(typed_data)); |
|