| // Copyright (c) 2019, 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. | |
| // This file contains test functions for the dart:ffi test cases. | |
| // This file is not allowed to depend on any symbols from the embedder and is | |
| // therefore not allowed to use `dart_api.h`. (The flutter/flutter integration | |
| // tests will run dart tests using this library only.) | |
| #include <stdarg.h> | |
| #include <stddef.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <sys/types.h> | |
| #include <cinttypes> | |
| #include <cmath> | |
| #include <iostream> | |
| #include <limits> | |
| #include <thread> // NOLINT | |
| #if defined(_WIN32) | |
| #include <windows.h> | |
| #else | |
| #include <sys/mman.h> | |
| #endif | |
| #include "bin/ffi_test/ffi_test_fields.h" | |
| #include "include/dart_api.h" | |
| namespace dart { | |
| #define CHECK(X) \ | |
| if (!(X)) { \ | |
| fprintf(stderr, "%s\n", "Check failed: " #X); \ | |
| return 1; \ | |
| } | |
| #define CHECK_EQ(X, Y) CHECK((X) == (Y)) | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Tests for Dart -> native calls. | |
| // | |
| // Note: If this interface is changed please also update | |
| // sdk/runtime/tools/dartfuzz/dartfuzz_ffi_api.dart | |
| DART_EXPORT void InduceACrash() { | |
| *reinterpret_cast<int*>(InduceACrash) = 123; | |
| } | |
| DART_EXPORT void SetGlobalVar(int32_t v) { | |
| globalInt = v; | |
| } | |
| DART_EXPORT int32_t GetGlobalVar() { | |
| return globalInt; | |
| } | |
| DART_EXPORT Coord GetGlobalStruct() { | |
| return globalStruct; | |
| } | |
| DART_EXPORT void SleepFor(int32_t ms) { | |
| std::cout << "Sleeping for " << ms << " milliseconds...\n"; | |
| std::this_thread::sleep_for(std::chrono::milliseconds(ms)); | |
| std::cout << "done\n"; | |
| } | |
| // Sums two ints and adds 42. | |
| // Simple function to test trampolines. | |
| // Also used for testing argument exception on passing null instead of a Dart | |
| // int. | |
| DART_EXPORT int32_t SumPlus42(int32_t a, int32_t b) { | |
| std::cout << "SumPlus42(" << a << ", " << b << ")\n"; | |
| const int32_t retval = 42 + a + b; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Sums the first two elements from the arr, | |
| // where arr must contains at least 2 elements. | |
| // It's been written to test `.address.cast()` expressions, | |
| // in the dart side and signature for the below function is | |
| // intentionally, `int sumFirstTwoElements(Pointer<Void> arr)` to test the | |
| // TypedData.address.cast() would not affect the address in anyways as it | |
| // stays the same as if it sent without the cast, as Pointer<Int32>. | |
| // Hence the `arr` is sent as Pointer<Void>. | |
| DART_EXPORT int32_t SumFirstTwoElements(int32_t* arr) { | |
| std::cout << "SumFirstTwoElements(" << *arr << ", " << *(arr + 1) << ")\n"; | |
| const int32_t retval = *(arr) + *(arr + 1); | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| DART_EXPORT int32_t SumTwoPointers(int32_t* a, int32_t* b) { | |
| std::cout << "SumTwoPointers(" << *a << ", " << *b << ")\n"; | |
| const int32_t retval = *a + *b; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Tests for sign and zero extension return values when passed to Dart. | |
| DART_EXPORT uint8_t ReturnMaxUint8() { | |
| return 0xff; | |
| } | |
| DART_EXPORT uint16_t ReturnMaxUint16() { | |
| return 0xffff; | |
| } | |
| DART_EXPORT uint32_t ReturnMaxUint32() { | |
| return 0xffffffff; | |
| } | |
| DART_EXPORT int8_t ReturnMinInt8() { | |
| return 0x80; | |
| } | |
| DART_EXPORT int16_t ReturnMinInt16() { | |
| return 0x8000; | |
| } | |
| DART_EXPORT int32_t ReturnMinInt32() { | |
| return 0x80000000; | |
| } | |
| // Test that return values are truncated by callee before passed to Dart. | |
| DART_EXPORT uint8_t ReturnMaxUint8v2() { | |
| uint64_t v = 0xabcff; | |
| // Truncated to 8 bits and zero extended, or truncated to 32 bits, depending | |
| // on ABI. | |
| return v; | |
| } | |
| DART_EXPORT uint16_t ReturnMaxUint16v2() { | |
| uint64_t v = 0xabcffff; | |
| return v; | |
| } | |
| DART_EXPORT uint32_t ReturnMaxUint32v2() { | |
| uint64_t v = 0xabcffffffff; | |
| return v; | |
| } | |
| DART_EXPORT int8_t ReturnMinInt8v2() { | |
| int64_t v = 0x8abc80; | |
| return v; | |
| } | |
| DART_EXPORT int16_t ReturnMinInt16v2() { | |
| int64_t v = 0x8abc8000; | |
| return v; | |
| } | |
| DART_EXPORT int32_t ReturnMinInt32v2() { | |
| int64_t v = 0x8abc80000000; | |
| return v; | |
| } | |
| // Test that arguments are truncated correctly. | |
| DART_EXPORT intptr_t TakeMaxUint8(uint8_t x) { | |
| std::cout << "TakeMaxUint8(" << static_cast<int>(x) << ")\n"; | |
| return x == 0xff ? 1 : 0; | |
| } | |
| DART_EXPORT intptr_t TakeMaxUint16(uint16_t x) { | |
| std::cout << "TakeMaxUint16(" << x << ")\n"; | |
| return x == 0xffff ? 1 : 0; | |
| } | |
| DART_EXPORT intptr_t TakeMaxUint32(uint32_t x) { | |
| std::cout << "TakeMaxUint32(" << x << ")\n"; | |
| return x == 0xffffffff ? 1 : 0; | |
| } | |
| DART_EXPORT intptr_t TakeMinInt8(int8_t x) { | |
| std::cout << "TakeMinInt8(" << static_cast<int>(x) << ")\n"; | |
| const int64_t expected = -0x80; | |
| const int64_t received = x; | |
| return expected == received ? 1 : 0; | |
| } | |
| DART_EXPORT intptr_t TakeMinInt16(int16_t x) { | |
| std::cout << "TakeMinInt16(" << x << ")\n"; | |
| const int64_t expected = -0x8000; | |
| const int64_t received = x; | |
| return expected == received ? 1 : 0; | |
| } | |
| DART_EXPORT intptr_t TakeMinInt32(int32_t x) { | |
| std::cout << "TakeMinInt32(" << x << ")\n"; | |
| const int64_t expected = INT32_MIN; | |
| const int64_t received = x; | |
| return expected == received ? 1 : 0; | |
| } | |
| // Test that arguments are truncated correctly, including stack arguments | |
| DART_EXPORT intptr_t TakeMaxUint8x10(uint8_t a, | |
| uint8_t b, | |
| uint8_t c, | |
| uint8_t d, | |
| uint8_t e, | |
| uint8_t f, | |
| uint8_t g, | |
| uint8_t h, | |
| uint8_t i, | |
| uint8_t j) { | |
| std::cout << "TakeMaxUint8x10(" << static_cast<int>(a) << ", " | |
| << static_cast<int>(b) << ", " << static_cast<int>(c) << ", " | |
| << static_cast<int>(d) << ", " << static_cast<int>(e) << ", " | |
| << static_cast<int>(f) << ", " << static_cast<int>(g) << ", " | |
| << static_cast<int>(h) << ", " << static_cast<int>(i) << ", " | |
| << static_cast<int>(j) << ")\n"; | |
| return (a == 0xff && b == 0xff && c == 0xff && d == 0xff && e == 0xff && | |
| f == 0xff && g == 0xff && h == 0xff && i == 0xff && j == 0xff) | |
| ? 1 | |
| : 0; | |
| } | |
| // Performs some computation on various sized signed ints. | |
| // Used for testing value ranges for signed ints. | |
| DART_EXPORT int64_t IntComputation(int8_t a, int16_t b, int32_t c, int64_t d) { | |
| std::cout << "IntComputation(" << static_cast<int>(a) << ", " << b << ", " | |
| << c << ", " << d << ")\n"; | |
| const int64_t retval = d - c + b - a; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Used in regress_39044_test.dart. | |
| DART_EXPORT int64_t Regress39044(int64_t a, int8_t b) { | |
| std::cout << "Regress39044(" << a << ", " << static_cast<int>(b) << ")\n"; | |
| const int64_t retval = a - b; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| DART_EXPORT intptr_t Regress40537(uint8_t x) { | |
| std::cout << "Regress40537(" << static_cast<int>(x) << ")\n"; | |
| return x == 249 ? 1 : 0; | |
| } | |
| DART_EXPORT intptr_t Regress40537Variant2(uint8_t x) { | |
| std::cout << "Regress40537Variant2(" << static_cast<int>(x) << ")\n"; | |
| return x; | |
| } | |
| DART_EXPORT uint8_t Regress40537Variant3(intptr_t x) { | |
| std::cout << "Regress40537Variant3(" << static_cast<int>(x) << ")\n"; | |
| return x; | |
| } | |
| // Performs some computation on various sized unsigned ints. | |
| // Used for testing value ranges for unsigned ints. | |
| DART_EXPORT int64_t UintComputation(uint8_t a, | |
| uint16_t b, | |
| uint32_t c, | |
| uint64_t d) { | |
| std::cout << "UintComputation(" << static_cast<int>(a) << ", " << b << ", " | |
| << c << ", " << d << ")\n"; | |
| const uint64_t retval = d - c + b - a; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Multiplies pointer sized intptr_t by three. | |
| // Used for testing pointer sized parameter and return value. | |
| DART_EXPORT intptr_t Times3(intptr_t a) { | |
| std::cout << "Times3(" << a << ")\n"; | |
| const intptr_t retval = a * 3; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Multiples a double by 1.337. | |
| // Used for testing double parameter and return value. | |
| // Also used for testing argument exception on passing null instead of a Dart | |
| // double. | |
| DART_EXPORT double Times1_337Double(double a) { | |
| std::cout << "Times1_337Double(" << a << ")\n"; | |
| const double retval = a * 1.337; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Multiples a float by 1.337. | |
| // Used for testing float parameter and return value. | |
| DART_EXPORT float Times1_337Float(float a) { | |
| std::cout << "Times1_337Float(" << a << ")\n"; | |
| const float retval = a * 1.337f; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Sums many ints. | |
| // Used for testing calling conventions. With so many integers we are using all | |
| // normal parameter registers and some stack slots. | |
| DART_EXPORT intptr_t SumManyInts(intptr_t a, | |
| intptr_t b, | |
| intptr_t c, | |
| intptr_t d, | |
| intptr_t e, | |
| intptr_t f, | |
| intptr_t g, | |
| intptr_t h, | |
| intptr_t i, | |
| intptr_t j) { | |
| std::cout << "SumManyInts(" << a << ", " << b << ", " << c << ", " << d | |
| << ", " << e << ", " << f << ", " << g << ", " << h << ", " << i | |
| << ", " << j << ")\n"; | |
| const intptr_t retval = a + b + c + d + e + f + g + h + i + j; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Sums many ints. | |
| // Used for testing calling conventions. With small integers on stack slots we | |
| // test stack alignment. | |
| DART_EXPORT int16_t SumManySmallInts(int8_t a, | |
| int16_t b, | |
| int8_t c, | |
| int16_t d, | |
| int8_t e, | |
| int16_t f, | |
| int8_t g, | |
| int16_t h, | |
| int8_t i, | |
| int16_t j) { | |
| std::cout << "SumManySmallInts(" << static_cast<int>(a) << ", " << b << ", " | |
| << static_cast<int>(c) << ", " << d << ", " << static_cast<int>(e) | |
| << ", " << f << ", " << static_cast<int>(g) << ", " << h << ", " | |
| << static_cast<int>(i) << ", " << j << ")\n"; | |
| const int16_t retval = a + b + c + d + e + f + g + h + i + j; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Used for testing floating point argument backfilling on Arm32 in hardfp. | |
| DART_EXPORT double SumFloatsAndDoubles(float a, double b, float c) { | |
| std::cout << "SumFloatsAndDoubles(" << a << ", " << b << ", " << c << ")\n"; | |
| const double retval = a + b + c; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Very many small integers, tests alignment on stack. | |
| DART_EXPORT int16_t SumVeryManySmallInts(int8_t a01, | |
| int16_t a02, | |
| int8_t a03, | |
| int16_t a04, | |
| int8_t a05, | |
| int16_t a06, | |
| int8_t a07, | |
| int16_t a08, | |
| int8_t a09, | |
| int16_t a10, | |
| int8_t a11, | |
| int16_t a12, | |
| int8_t a13, | |
| int16_t a14, | |
| int8_t a15, | |
| int16_t a16, | |
| int8_t a17, | |
| int16_t a18, | |
| int8_t a19, | |
| int16_t a20, | |
| int8_t a21, | |
| int16_t a22, | |
| int8_t a23, | |
| int16_t a24, | |
| int8_t a25, | |
| int16_t a26, | |
| int8_t a27, | |
| int16_t a28, | |
| int8_t a29, | |
| int16_t a30, | |
| int8_t a31, | |
| int16_t a32, | |
| int8_t a33, | |
| int16_t a34, | |
| int8_t a35, | |
| int16_t a36, | |
| int8_t a37, | |
| int16_t a38, | |
| int8_t a39, | |
| int16_t a40) { | |
| std::cout << "SumVeryManySmallInts(" << static_cast<int>(a01) << ", " << a02 | |
| << ", " << static_cast<int>(a03) << ", " << a04 << ", " | |
| << static_cast<int>(a05) << ", " << a06 << ", " | |
| << static_cast<int>(a07) << ", " << a08 << ", " | |
| << static_cast<int>(a09) << ", " << a10 << ", " | |
| << static_cast<int>(a11) << ", " << a12 << ", " | |
| << static_cast<int>(a13) << ", " << a14 << ", " | |
| << static_cast<int>(a15) << ", " << a16 << ", " | |
| << static_cast<int>(a17) << ", " << a18 << ", " | |
| << static_cast<int>(a19) << ", " << a20 << ", " | |
| << static_cast<int>(a21) << ", " << a22 << ", " | |
| << static_cast<int>(a23) << ", " << a24 << ", " | |
| << static_cast<int>(a25) << ", " << a26 << ", " | |
| << static_cast<int>(a27) << ", " << a28 << ", " | |
| << static_cast<int>(a29) << ", " << a30 << ", " | |
| << static_cast<int>(a31) << ", " << a32 << ", " | |
| << static_cast<int>(a33) << ", " << a34 << ", " | |
| << static_cast<int>(a35) << ", " << a36 << ", " | |
| << static_cast<int>(a37) << ", " << a38 << ", " | |
| << static_cast<int>(a39) << ", " << a40 << ")\n"; | |
| const int16_t retval = a01 + a02 + a03 + a04 + a05 + a06 + a07 + a08 + a09 + | |
| a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + | |
| a19 + a20 + a21 + a22 + a23 + a24 + a25 + a26 + a27 + | |
| a28 + a29 + a30 + a31 + a32 + a33 + a34 + a35 + a36 + | |
| a37 + a38 + a39 + a40; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Very many floating points, tests alignment on stack, and packing in | |
| // floating point registers in hardfp. | |
| DART_EXPORT double SumVeryManyFloatsDoubles(float a01, | |
| double a02, | |
| float a03, | |
| double a04, | |
| float a05, | |
| double a06, | |
| float a07, | |
| double a08, | |
| float a09, | |
| double a10, | |
| float a11, | |
| double a12, | |
| float a13, | |
| double a14, | |
| float a15, | |
| double a16, | |
| float a17, | |
| double a18, | |
| float a19, | |
| double a20, | |
| float a21, | |
| double a22, | |
| float a23, | |
| double a24, | |
| float a25, | |
| double a26, | |
| float a27, | |
| double a28, | |
| float a29, | |
| double a30, | |
| float a31, | |
| double a32, | |
| float a33, | |
| double a34, | |
| float a35, | |
| double a36, | |
| float a37, | |
| double a38, | |
| float a39, | |
| double a40) { | |
| std::cout << "SumVeryManyFloatsDoubles(" << a01 << ", " << a02 << ", " << a03 | |
| << ", " << a04 << ", " << a05 << ", " << a06 << ", " << a07 << ", " | |
| << a08 << ", " << a09 << ", " << a10 << ", " << a11 << ", " << a12 | |
| << ", " << a13 << ", " << a14 << ", " << a15 << ", " << a16 << ", " | |
| << a17 << ", " << a18 << ", " << a19 << ", " << a20 << ", " << a21 | |
| << ", " << a22 << ", " << a23 << ", " << a24 << ", " << a25 << ", " | |
| << a26 << ", " << a27 << ", " << a28 << ", " << a29 << ", " << a30 | |
| << ", " << a31 << ", " << a32 << ", " << a33 << ", " << a34 << ", " | |
| << a35 << ", " << a36 << ", " << a37 << ", " << a38 << ", " << a39 | |
| << ", " << a40 << ")\n"; | |
| const double retval = a01 + a02 + a03 + a04 + a05 + a06 + a07 + a08 + a09 + | |
| a10 + a11 + a12 + a13 + a14 + a15 + a16 + a17 + a18 + | |
| a19 + a20 + a21 + a22 + a23 + a24 + a25 + a26 + a27 + | |
| a28 + a29 + a30 + a31 + a32 + a33 + a34 + a35 + a36 + | |
| a37 + a38 + a39 + a40; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Sums an odd number of ints. | |
| // Used for testing calling conventions. With so many arguments, and an odd | |
| // number of arguments, we are testing stack alignment on various architectures. | |
| DART_EXPORT intptr_t SumManyIntsOdd(intptr_t a, | |
| intptr_t b, | |
| intptr_t c, | |
| intptr_t d, | |
| intptr_t e, | |
| intptr_t f, | |
| intptr_t g, | |
| intptr_t h, | |
| intptr_t i, | |
| intptr_t j, | |
| intptr_t k) { | |
| std::cout << "SumManyInts(" << a << ", " << b << ", " << c << ", " << d | |
| << ", " << e << ", " << f << ", " << g << ", " << h << ", " << i | |
| << ", " << j << ", " << k << ")\n"; | |
| const intptr_t retval = | |
| static_cast<uintptr_t>(a) + static_cast<uintptr_t>(b) + | |
| static_cast<uintptr_t>(c) + static_cast<uintptr_t>(d) + | |
| static_cast<uintptr_t>(e) + static_cast<uintptr_t>(f) + | |
| static_cast<uintptr_t>(g) + static_cast<uintptr_t>(h) + | |
| static_cast<uintptr_t>(i) + static_cast<uintptr_t>(j) + | |
| static_cast<uintptr_t>(k); | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Sums many doubles. | |
| // Used for testing calling conventions. With so many doubles we are using all | |
| // xmm parameter registers and some stack slots. | |
| DART_EXPORT double SumManyDoubles(double a, | |
| double b, | |
| double c, | |
| double d, | |
| double e, | |
| double f, | |
| double g, | |
| double h, | |
| double i, | |
| double j) { | |
| std::cout << "SumManyDoubles(" << a << ", " << b << ", " << c << ", " << d | |
| << ", " << e << ", " << f << ", " << g << ", " << h << ", " << i | |
| << ", " << j << ")\n"; | |
| const double retval = a + b + c + d + e + f + g + h + i + j; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Sums many numbers. | |
| // Used for testing calling conventions. With so many parameters we are using | |
| // both registers and stack slots. | |
| DART_EXPORT double SumManyNumbers(intptr_t a, | |
| float b, | |
| intptr_t c, | |
| double d, | |
| intptr_t e, | |
| float f, | |
| intptr_t g, | |
| double h, | |
| intptr_t i, | |
| float j, | |
| intptr_t k, | |
| double l, | |
| intptr_t m, | |
| float n, | |
| intptr_t o, | |
| double p, | |
| intptr_t q, | |
| float r, | |
| intptr_t s, | |
| double t) { | |
| std::cout << "SumManyNumbers(" << a << ", " << b << ", " << c << ", " << d | |
| << ", " << e << ", " << f << ", " << g << ", " << h << ", " << i | |
| << ", " << j << ", " << k << ", " << l << ", " << m << ", " << n | |
| << ", " << o << ", " << p << ", " << q << ", " << r << ", " << s | |
| << ", " << t << ")\n"; | |
| const double retval = a + b + c + d + e + f + g + h + i + j + k + l + m + n + | |
| o + p + q + r + s + t; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Assigns 1337 to the second element and returns the address of that element. | |
| // Used for testing Pointer parameters and return values. | |
| DART_EXPORT int64_t* Assign1337Index1(int64_t* a) { | |
| std::cout << "Assign1337Index1(" << a << ")\n"; | |
| std::cout << "val[0] = " << a[0] << "\n"; | |
| std::cout << "val[1] = " << a[1] << "\n"; | |
| a[1] = 1337; | |
| std::cout << "val[1] = " << a[1] << "\n"; | |
| int64_t* retval = a + 1; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Transposes Coordinate by (10, 10) and returns next Coordinate. | |
| // Used for testing struct pointer parameter, struct pointer return value, | |
| // struct field access, and struct pointer field dereference. | |
| DART_EXPORT Coord* TransposeCoordinate(Coord* coord) { | |
| std::cout << "TransposeCoordinate(" << coord << " {" << coord->x << ", " | |
| << coord->y << ", " << coord->next << "})\n"; | |
| coord->x = coord->x + 10.0; | |
| coord->y = coord->y + 10.0; | |
| std::cout << "returning " << coord->next << "\n"; | |
| return coord->next; | |
| } | |
| // Takes a Coordinate array and returns a Coordinate pointer to the next | |
| // element. | |
| // Used for testing struct arrays. | |
| DART_EXPORT Coord* CoordinateElemAt1(Coord* coord) { | |
| std::cout << "CoordinateElemAt1(" << coord << ")\n"; | |
| std::cout << "sizeof(Coord): " << sizeof(Coord) << "\n"; | |
| std::cout << "coord[0] = {" << coord[0].x << ", " << coord[0].y << ", " | |
| << coord[0].next << "}\n"; | |
| std::cout << "coord[1] = {" << coord[1].x << ", " << coord[1].y << ", " | |
| << coord[1].next << "}\n"; | |
| Coord* retval = coord + 1; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| typedef Coord* (*CoordUnOp)(Coord* coord); | |
| // Takes a Coordinate Function(Coordinate) and applies it three times to a | |
| // Coordinate. | |
| // Used for testing function pointers with structs. | |
| DART_EXPORT Coord* CoordinateUnOpTrice(CoordUnOp unop, Coord* coord) { | |
| std::cout << "CoordinateUnOpTrice(" << &unop << ", " << coord << ")\n"; | |
| Coord* retval = unop(unop(unop(coord))); | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| typedef intptr_t (*IntptrBinOp)(intptr_t a, intptr_t b); | |
| // Returns a closure. | |
| // Note this closure is not properly marked as DART_EXPORT or extern "C". | |
| // Used for testing passing a pointer to a closure to Dart. | |
| DART_EXPORT IntptrBinOp IntptrAdditionClosure() { | |
| std::cout << "IntptrAdditionClosure()\n"; | |
| IntptrBinOp retval = [](intptr_t a, intptr_t b) { return a + b; }; | |
| std::cout << "returning " << &retval << "\n"; | |
| return retval; | |
| } | |
| // Applies an intptr binop function to 42 and 74. | |
| // Used for testing passing a function pointer to C. | |
| DART_EXPORT intptr_t ApplyTo42And74(IntptrBinOp binop) { | |
| std::cout << "ApplyTo42And74()\n"; | |
| intptr_t retval = binop(42, 74); | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Returns next element in the array, unless a null pointer is passed. | |
| // When a null pointer is passed, a null pointer is returned. | |
| // Used for testing null pointers. | |
| DART_EXPORT int64_t* NullableInt64ElemAt1(int64_t* a) { | |
| std::cout << "NullableInt64ElemAt1(" << a << ")\n"; | |
| int64_t* retval; | |
| if (a != nullptr) { | |
| std::cout << "not null pointer, address: " << a << "\n"; | |
| retval = a + 1; | |
| } else { | |
| std::cout << "null pointer, address: " << a << "\n"; | |
| retval = nullptr; | |
| } | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // A struct designed to exercise all kinds of alignment rules. | |
| // Note that offset32A (System V ia32, iOS arm) aligns doubles on 4 bytes while | |
| // offset32B (Arm 32 bit and MSVC ia32) aligns on 8 bytes. | |
| // TODO(37470): Add uncommon primitive data types when we want to support them. | |
| struct VeryLargeStruct { | |
| // size32 size64 offset32A offset32B offset64 | |
| int8_t a; // 1 0 0 0 | |
| int16_t b; // 2 2 2 2 | |
| int32_t c; // 4 4 4 4 | |
| int64_t d; // 8 8 8 8 | |
| uint8_t e; // 1 16 16 16 | |
| uint16_t f; // 2 18 18 18 | |
| uint32_t g; // 4 20 20 20 | |
| uint64_t h; // 8 24 24 24 | |
| intptr_t i; // 4 8 32 32 32 | |
| double j; // 8 36 40 40 | |
| float k; // 4 44 48 48 | |
| VeryLargeStruct* parent; // 4 8 48 52 56 | |
| intptr_t numChildren; // 4 8 52 56 64 | |
| VeryLargeStruct* children; // 4 8 56 60 72 | |
| int8_t smallLastField; // 1 60 64 80 | |
| // sizeof 64 72 88 | |
| }; | |
| // Sums the fields of a very large struct, including the first field (a) from | |
| // the parent and children. | |
| // Used for testing alignment and padding in structs. | |
| DART_EXPORT int64_t SumVeryLargeStruct(VeryLargeStruct* vls) { | |
| std::cout << "SumVeryLargeStruct(" << vls << ")\n"; | |
| std::cout << "offsetof(a): " << offsetof(VeryLargeStruct, a) << "\n"; | |
| std::cout << "offsetof(b): " << offsetof(VeryLargeStruct, b) << "\n"; | |
| std::cout << "offsetof(c): " << offsetof(VeryLargeStruct, c) << "\n"; | |
| std::cout << "offsetof(d): " << offsetof(VeryLargeStruct, d) << "\n"; | |
| std::cout << "offsetof(e): " << offsetof(VeryLargeStruct, e) << "\n"; | |
| std::cout << "offsetof(f): " << offsetof(VeryLargeStruct, f) << "\n"; | |
| std::cout << "offsetof(g): " << offsetof(VeryLargeStruct, g) << "\n"; | |
| std::cout << "offsetof(h): " << offsetof(VeryLargeStruct, h) << "\n"; | |
| std::cout << "offsetof(i): " << offsetof(VeryLargeStruct, i) << "\n"; | |
| std::cout << "offsetof(j): " << offsetof(VeryLargeStruct, j) << "\n"; | |
| std::cout << "offsetof(k): " << offsetof(VeryLargeStruct, k) << "\n"; | |
| std::cout << "offsetof(parent): " << offsetof(VeryLargeStruct, parent) | |
| << "\n"; | |
| std::cout << "offsetof(numChildren): " | |
| << offsetof(VeryLargeStruct, numChildren) << "\n"; | |
| std::cout << "offsetof(children): " << offsetof(VeryLargeStruct, children) | |
| << "\n"; | |
| std::cout << "offsetof(smallLastField): " | |
| << offsetof(VeryLargeStruct, smallLastField) << "\n"; | |
| std::cout << "sizeof(VeryLargeStruct): " << sizeof(VeryLargeStruct) << "\n"; | |
| std::cout << "vls->a: " << static_cast<int>(vls->a) << "\n"; | |
| std::cout << "vls->b: " << vls->b << "\n"; | |
| std::cout << "vls->c: " << vls->c << "\n"; | |
| std::cout << "vls->d: " << vls->d << "\n"; | |
| std::cout << "vls->e: " << static_cast<int>(vls->e) << "\n"; | |
| std::cout << "vls->f: " << vls->f << "\n"; | |
| std::cout << "vls->g: " << vls->g << "\n"; | |
| std::cout << "vls->h: " << vls->h << "\n"; | |
| std::cout << "vls->i: " << vls->i << "\n"; | |
| std::cout << "vls->j: " << vls->j << "\n"; | |
| std::cout << "vls->k: " << vls->k << "\n"; | |
| std::cout << "vls->parent: " << vls->parent << "\n"; | |
| std::cout << "vls->numChildren: " << vls->numChildren << "\n"; | |
| std::cout << "vls->children: " << vls->children << "\n"; | |
| std::cout << "vls->smallLastField: " << static_cast<int>(vls->smallLastField) | |
| << "\n"; | |
| int64_t retval = 0; | |
| retval += 0x0L + vls->a; | |
| retval += vls->b; | |
| retval += vls->c; | |
| retval += vls->d; | |
| retval += vls->e; | |
| retval += vls->f; | |
| retval += vls->g; | |
| retval += vls->h; | |
| retval += vls->i; | |
| retval += vls->j; | |
| retval += vls->k; | |
| retval += vls->smallLastField; | |
| std::cout << retval << "\n"; | |
| if (vls->parent != nullptr) { | |
| std::cout << "has parent\n"; | |
| retval += vls->parent->a; | |
| } | |
| std::cout << "has " << vls->numChildren << " children\n"; | |
| for (intptr_t i = 0; i < vls->numChildren; i++) { | |
| retval += vls->children[i].a; | |
| } | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| struct Struct9Uint8 { | |
| uint8_t a0; | |
| uint8_t a1; | |
| uint8_t a2; | |
| uint8_t a3; | |
| uint8_t a4; | |
| uint8_t a5; | |
| uint8_t a6; | |
| uint8_t a7; | |
| uint8_t a8; | |
| }; | |
| DART_EXPORT int64_t SumStruct9Uint8(Struct9Uint8 s9) { | |
| return s9.a0 + s9.a1 + s9.a2 + s9.a3 + s9.a4 + s9.a5 + s9.a6 + s9.a7 + s9.a8; | |
| } | |
| DART_EXPORT int64_t | |
| SumReturnStruct9Uint8(Struct9Uint8 (*callback)(Struct9Uint8*), | |
| Struct9Uint8* in) { | |
| std::cout << "SumReturnStruct9Uint8 in (" << in->a0 << ", " << in->a1 << ", " | |
| << in->a2 << ", " << in->a3 << ", " << in->a4 << ", " << in->a5 | |
| << ", " << in->a6 << ", " << in->a7 << ", " << in->a8 << ")\n"; | |
| Struct9Uint8 out = callback(in); | |
| std::cout << "SumReturnStruct9Uint8 out (" << out.a0 << ", " << out.a1 << ", " | |
| << out.a2 << ", " << out.a3 << ", " << out.a4 << ", " << out.a5 | |
| << ", " << out.a6 << ", " << out.a7 << ", " << out.a8 << ")\n"; | |
| return out.a0 + out.a1 + out.a2 + out.a3 + out.a4 + out.a5 + out.a6 + out.a7 + | |
| out.a8; | |
| } | |
| // Allocates a multiple of the larest page size, so the last element of the | |
| // array is right at a page boundary. Explicitly allocate and make inaccessible | |
| // the next page to avoid flaky false-successes if the next page happens to be | |
| // allocated. | |
| DART_EXPORT Struct9Uint8* AllocStruct9Uint8() { | |
| size_t size = sizeof(Struct9Uint8) * 64 * 1024; | |
| #if defined(_WIN32) | |
| void* result = | |
| VirtualAlloc(nullptr, size * 2, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); | |
| void* guard_page = | |
| reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(result) + size); | |
| DWORD old_prot; | |
| if (VirtualProtect(guard_page, size, PAGE_NOACCESS, &old_prot) == 0) { | |
| fprintf(stderr, "VirtualProtect failed\n"); | |
| abort(); | |
| } | |
| #else | |
| void* result = mmap(nullptr, size * 2, PROT_READ | PROT_WRITE, | |
| MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
| void* guard_page = | |
| reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(result) + size); | |
| if (mprotect(guard_page, size, PROT_NONE) != 0) { | |
| fprintf(stderr, "mprotect failed\n"); | |
| abort(); | |
| } | |
| #endif | |
| return reinterpret_cast<Struct9Uint8*>(result); | |
| } | |
| DART_EXPORT void FreeStruct9Uint8(Struct9Uint8* address) { | |
| #if defined(_WIN32) | |
| VirtualFree(address, 0, MEM_RELEASE); | |
| #else | |
| size_t size = sizeof(Struct9Uint8) * 64 * 1024; | |
| munmap(address, size * 2); | |
| #endif | |
| } | |
| // Sums numbers of various sizes. | |
| // Used for testing truncation and sign extension of non 64 bit parameters. | |
| DART_EXPORT int64_t SumSmallNumbers(int8_t a, | |
| int16_t b, | |
| int32_t c, | |
| uint8_t d, | |
| uint16_t e, | |
| uint32_t f) { | |
| std::cout << "SumSmallNumbers(" << static_cast<int>(a) << ", " << b << ", " | |
| << c << ", " << static_cast<int>(d) << ", " << e << ", " << f | |
| << ")\n"; | |
| int64_t retval = 0; | |
| retval += a; | |
| retval += b; | |
| retval += c; | |
| retval += d; | |
| retval += e; | |
| retval += f; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Checks whether the float is between 1336.0f and 1338.0f. | |
| // Used for testing rounding of Dart Doubles to floats in Pointer.store(). | |
| DART_EXPORT uint8_t IsRoughly1337(float* a) { | |
| std::cout << "IsRoughly1337(" << a[0] << ")\n"; | |
| uint8_t retval = (1336.0f < a[0] && a[0] < 1338.0f) ? 1 : 0; | |
| std::cout << "returning " << static_cast<int>(retval) << "\n"; | |
| return retval; | |
| } | |
| // Does nothing with input. | |
| // Used for testing functions that return void | |
| DART_EXPORT void DevNullFloat(float a) { | |
| std::cout << "DevNullFloat(" << a << ")\n"; | |
| std::cout << "returning nothing\n"; | |
| } | |
| // Invents an elite floating pointptr_t number. | |
| // Used for testing functions that do not take any arguments. | |
| DART_EXPORT float InventFloatValue() { | |
| std::cout << "InventFloatValue()\n"; | |
| const float retval = 1337.0f; | |
| std::cout << "returning " << retval << "\n"; | |
| return retval; | |
| } | |
| // Can't easily share this with the generated file. | |
| struct Struct20BytesHomogeneousInt32Copy { | |
| int32_t a0; | |
| int32_t a1; | |
| int32_t a2; | |
| int32_t a3; | |
| int32_t a4; | |
| }; | |
| DART_EXPORT Struct20BytesHomogeneousInt32Copy PassStructRecursive( | |
| int64_t recursionCounter, | |
| Struct20BytesHomogeneousInt32Copy a0, | |
| Struct20BytesHomogeneousInt32Copy (*f)(int64_t, | |
| Struct20BytesHomogeneousInt32Copy)) { | |
| std::cout << "PassStruct20BytesHomogeneousInt32x10" | |
| << "(" << recursionCounter << ", (" << a0.a0 << ", " << a0.a1 | |
| << ", " << a0.a2 << ", " << a0.a3 << ", " << a0.a4 << "), " | |
| << reinterpret_cast<void*>(f) << ")\n"; | |
| a0.a0++; | |
| const int32_t a0_a0_saved = a0.a0; | |
| if (recursionCounter <= 0) { | |
| return a0; | |
| } | |
| Struct20BytesHomogeneousInt32Copy result = f(recursionCounter - 1, a0); | |
| result.a0++; | |
| if (a0_a0_saved != a0.a0) { | |
| result.a4 = 0; | |
| } | |
| return result; | |
| } | |
| // Can't easily share this with the generated file. | |
| struct Struct4BytesHomogeneousInt16Copy { | |
| int16_t a0; | |
| int16_t a1; | |
| }; | |
| // Can't easily share this with the generated file. | |
| struct Struct8BytesNestedIntCopy { | |
| Struct4BytesHomogeneousInt16Copy a0; | |
| Struct4BytesHomogeneousInt16Copy a1; | |
| }; | |
| DART_EXPORT void CallbackWithStruct(void (*f)(Struct8BytesNestedIntCopy)) { | |
| std::cout << "CallbackWithStruct" | |
| << "(" << reinterpret_cast<void*>(f) << ")\n"; | |
| Struct8BytesNestedIntCopy arg; | |
| arg.a0.a0 = 10; | |
| arg.a0.a1 = 11; | |
| arg.a1.a0 = 12; | |
| arg.a1.a1 = 13; | |
| f(arg); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Tests for callbacks. | |
| // Sanity test. | |
| DART_EXPORT intptr_t TestSimpleAddition(intptr_t (*add)(int, int)) { | |
| const intptr_t result = add(10, 20); | |
| printf("result %" PRIdPTR "\n", result); | |
| CHECK_EQ(result, 30); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestUnwindError(intptr_t (*add)(int, int)) { | |
| const intptr_t result = add(10, 20); | |
| printf("result %" PRIdPTR "\n", result); | |
| CHECK_EQ(result, 42); | |
| return 0; | |
| } | |
| //// Following tests are copied from above, with the role of Dart and C++ code | |
| //// reversed. | |
| DART_EXPORT intptr_t | |
| TestIntComputation(int64_t (*fn)(int8_t, int16_t, int32_t, int64_t)) { | |
| const int64_t result = fn(125, 250, 500, 1000); | |
| std::cout << "result " << result << "\n"; | |
| CHECK_EQ(result, 625); | |
| CHECK_EQ(0x7FFFFFFFFFFFFFFFLL, fn(0, 0, 0, 0x7FFFFFFFFFFFFFFFLL)); | |
| CHECK_EQ(((int64_t)0x8000000000000000LL), fn(0, 0, 0, 0x8000000000000000LL)); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t | |
| TestUintComputation(uint64_t (*fn)(uint8_t, uint16_t, uint32_t, uint64_t)) { | |
| CHECK_EQ(0x7FFFFFFFFFFFFFFFLL, fn(0, 0, 0, 0x7FFFFFFFFFFFFFFFLL)); | |
| CHECK_EQ(0x8000000000000000LL, fn(0, 0, 0, 0x8000000000000000LL)); | |
| CHECK_EQ(-1, (int64_t)fn(0, 0, 0, -1)); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestSimpleMultiply(double (*fn)(double)) { | |
| CHECK_EQ(fn(2.0), 2.0 * 1.337); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestSimpleMultiplyFloat(float (*fn)(float)) { | |
| CHECK(::std::abs(fn(2.0) - 2.0 * 1.337) < 0.001); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestManyInts(intptr_t (*fn)(intptr_t, | |
| intptr_t, | |
| intptr_t, | |
| intptr_t, | |
| intptr_t, | |
| intptr_t, | |
| intptr_t, | |
| intptr_t, | |
| intptr_t, | |
| intptr_t)) { | |
| CHECK_EQ(55, fn(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestManyDoubles(double (*fn)(double, | |
| double, | |
| double, | |
| double, | |
| double, | |
| double, | |
| double, | |
| double, | |
| double, | |
| double)) { | |
| CHECK_EQ(55, fn(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestManyArgs(double (*fn)(intptr_t a, | |
| float b, | |
| intptr_t c, | |
| double d, | |
| intptr_t e, | |
| float f, | |
| intptr_t g, | |
| double h, | |
| intptr_t i, | |
| float j, | |
| intptr_t k, | |
| double l, | |
| intptr_t m, | |
| float n, | |
| intptr_t o, | |
| double p, | |
| intptr_t q, | |
| float r, | |
| intptr_t s, | |
| double t)) { | |
| CHECK(210.0 == fn(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0, | |
| 15, 16.0, 17, 18.0, 19, 20.0)); | |
| return 0; | |
| } | |
| // Used for testing floating point argument backfilling on Arm32 in hardfp. | |
| DART_EXPORT intptr_t TestSumFloatsAndDoubles(double (*fn)(float, | |
| double, | |
| float)) { | |
| CHECK_EQ(6.0, fn(1.0, 2.0, 3.0)); | |
| return 0; | |
| } | |
| // Very many small integers, tests alignment on stack. | |
| DART_EXPORT intptr_t TestSumVeryManySmallInts(int16_t (*fn)(int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t, | |
| int8_t, | |
| int16_t)) { | |
| CHECK_EQ(40 * 41 / 2, fn(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, | |
| 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, | |
| 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40)); | |
| return 0; | |
| } | |
| // Very many floating points, tests alignment on stack, and packing in | |
| // floating point registers in hardfp. | |
| DART_EXPORT intptr_t TestSumVeryManyFloatsDoubles(double (*fn)(float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double, | |
| float, | |
| double)) { | |
| CHECK_EQ(40 * 41 / 2, | |
| fn(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, | |
| 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, | |
| 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, | |
| 35.0, 36.0, 37.0, 38.0, 39.0, 40.0)); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestStore(int64_t* (*fn)(int64_t* a)) { | |
| int64_t p[2] = {42, 1000}; | |
| int64_t* result = fn(p); | |
| CHECK_EQ(*result, 1337); | |
| CHECK_EQ(p[1], 1337); | |
| CHECK_EQ(result, p + 1); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestReturnNull(int32_t (*fn)()) { | |
| CHECK_EQ(fn(), 42); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestNullPointers(int64_t* (*fn)(int64_t* ptr)) { | |
| CHECK_EQ(fn(nullptr), reinterpret_cast<void*>(sizeof(int64_t))); | |
| int64_t p[2] = {0}; | |
| CHECK_EQ(fn(p), p + 1); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestReturnVoid(intptr_t (*return_void)()) { | |
| CHECK_EQ(return_void(), 0); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestThrowExceptionDouble(double (*fn)()) { | |
| CHECK_EQ(fn(), 42.0); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestThrowExceptionPointer(void* (*fn)()) { | |
| CHECK_EQ(fn(), nullptr); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestThrowException(intptr_t (*fn)()) { | |
| CHECK_EQ(fn(), 42); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestTakeMaxUint8x10(intptr_t (*fn)(uint8_t, | |
| uint8_t, | |
| uint8_t, | |
| uint8_t, | |
| uint8_t, | |
| uint8_t, | |
| uint8_t, | |
| uint8_t, | |
| uint8_t, | |
| uint8_t)) { | |
| CHECK_EQ(1, fn(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)); | |
| // Check the argument values are properly truncated. | |
| uint64_t v = 0xabcFF; | |
| CHECK_EQ(1, fn(v, v, v, v, v, v, v, v, v, v)); | |
| return 0; | |
| } | |
| DART_EXPORT intptr_t TestReturnMaxUint8(uint8_t (*fn)()) { | |
| std::cout << "TestReturnMaxUint8(fn): " << static_cast<int>(fn()) << "\n"; | |
| CHECK_EQ(0xFF, fn()); | |
| return 0; | |
| } | |
| // Receives some pointer (Pointer<NativeType> in Dart) and writes some bits. | |
| DART_EXPORT void NativeTypePointerParam(void* p) { | |
| uint8_t* p2 = reinterpret_cast<uint8_t*>(p); | |
| p2[0] = 42; | |
| } | |
| // Manufactures some pointer (Pointer<NativeType> in Dart) with a bogus address. | |
| DART_EXPORT void* NativeTypePointerReturn() { | |
| uint64_t bogus_address = 0x13370000; | |
| return reinterpret_cast<void*>(bogus_address); | |
| } | |
| // Passes some pointer (Pointer<NativeType> in Dart) to Dart as argument. | |
| DART_EXPORT void CallbackNativeTypePointerParam(void (*f)(void*)) { | |
| void* pointer = malloc(sizeof(int64_t)); | |
| f(pointer); | |
| free(pointer); | |
| } | |
| // Receives some pointer (Pointer<NativeType> in Dart) from Dart as return | |
| // value. | |
| DART_EXPORT void CallbackNativeTypePointerReturn(void* (*f)()) { | |
| void* p = f(); | |
| uint8_t* p2 = reinterpret_cast<uint8_t*>(p); | |
| p2[0] = 42; | |
| } | |
| DART_EXPORT int32_t PassStruct(void*) { | |
| return 42; | |
| } | |
| struct Struct43693 { | |
| void* pSomePtr; | |
| uint64_t someValue; | |
| }; | |
| DART_EXPORT uint64_t Regress43693(Struct43693* my_struct) { | |
| return my_struct->someValue; | |
| } | |
| struct Struct46127 { | |
| uint64_t val; | |
| }; | |
| DART_EXPORT Struct46127 Regress46127() { | |
| struct Struct46127 myStruct; | |
| myStruct.val = 123; | |
| return myStruct; | |
| } | |
| #pragma pack(push, 1) | |
| struct Struct3BytesPackedIntCopy { | |
| int8_t a0; | |
| int16_t a1; | |
| }; | |
| #pragma pack(pop) | |
| // Define ssize_t for Windows as intptr_t. | |
| #if defined(_WIN32) | |
| typedef intptr_t ssize_t; | |
| #endif | |
| #define DEFINE_SIZE_OF_AND_SIGN_OF(type_modifier, type, type2) \ | |
| DART_EXPORT uint64_t FfiSizeOf_##type_modifier##_##type##_##type2() { \ | |
| return sizeof(type_modifier type type2); \ | |
| } \ | |
| \ | |
| DART_EXPORT uint64_t FfiSignOf_##type_modifier##_##type##_##type2() { \ | |
| return std::numeric_limits<type_modifier type type2>::is_signed; \ | |
| } | |
| #define TYPES(F) \ | |
| F(, char, ) /* NOLINT */ \ | |
| F(signed, char, ) /* NOLINT */ \ | |
| F(unsigned, char, ) /* NOLINT */ \ | |
| F(, short, ) /* NOLINT */ \ | |
| F(unsigned, short, ) /* NOLINT */ \ | |
| F(, int, ) /* NOLINT */ \ | |
| F(unsigned, int, ) /* NOLINT */ \ | |
| F(, long, ) /* NOLINT */ \ | |
| F(unsigned, long, ) /* NOLINT */ \ | |
| F(, long, long) /* NOLINT */ \ | |
| F(unsigned, long, long) /* NOLINT */ \ | |
| F(, intptr_t, ) /* NOLINT */ \ | |
| F(, uintptr_t, ) /* NOLINT */ \ | |
| F(, size_t, ) /* NOLINT */ \ | |
| F(, wchar_t, ) /* NOLINT */ | |
| TYPES(DEFINE_SIZE_OF_AND_SIGN_OF) | |
| #undef DEFINE_SIZE_OF_AND_SIGN_OF | |
| #undef TYPES | |
| DART_EXPORT int64_t WCharMinValue() { | |
| return WCHAR_MIN; | |
| } | |
| DART_EXPORT int64_t WCharMaxValue() { | |
| return WCHAR_MAX; | |
| } | |
| struct VarArgs { | |
| int32_t a; | |
| }; | |
| DART_EXPORT int64_t VariadicStructVarArgs(VarArgs a0, ...) { | |
| va_list var_args; | |
| va_start(var_args, a0); | |
| VarArgs a1 = va_arg(var_args, VarArgs); | |
| va_end(var_args); | |
| std::cout << "VariadicStructVarArgs" | |
| << "(" << a0.a << ", " << a1.a << ")" | |
| << "\n"; | |
| int64_t result = 0; | |
| result += a0.a; | |
| result += a1.a; | |
| std::cout << "result = " << result << "\n"; | |
| return result; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Tests for async callbacks. | |
| DART_EXPORT void CallFunctionOnSameThread(int64_t response_id, | |
| void (*fn)(int64_t, int32_t)) { | |
| fn(response_id, 123); | |
| } | |
| DART_EXPORT void CallFunctionOnNewThreadBlocking(int64_t response_id, | |
| void (*fn)(int64_t, int32_t)) { | |
| std::thread thread(fn, response_id, 123); | |
| thread.join(); | |
| } | |
| struct RepeatingThread { | |
| std::thread thread; | |
| std::atomic<bool> run; | |
| }; | |
| DART_EXPORT void* CallFunctionOnNewThreadRepeatedly(void (*fn)(int64_t)) { | |
| RepeatingThread* repeater = new RepeatingThread(); | |
| repeater->run.store(true); | |
| repeater->thread = std::thread([fn, repeater]() { | |
| for (int64_t i = 0; repeater->run.load(); ++i) { | |
| fn(i); | |
| } | |
| }); | |
| return repeater; | |
| } | |
| DART_EXPORT void CallFunctionOnNewThreadStop(void* ptr) { | |
| RepeatingThread* repeater = reinterpret_cast<RepeatingThread*>(ptr); | |
| repeater->run.store(false); | |
| repeater->thread.join(); | |
| delete repeater; | |
| } | |
| #if defined(__linux__) | |
| struct Data { | |
| void (*fn)(int64_t, int32_t); | |
| int64_t a; | |
| int32_t b; | |
| }; | |
| static void* Start(void* data_in) { | |
| Data* data = reinterpret_cast<Data*>(data_in); | |
| void (*fn)(int64_t, int32_t) = data->fn; | |
| int64_t a = data->a; | |
| int32_t b = data->b; | |
| delete data; | |
| fn(a, b); | |
| return nullptr; | |
| } | |
| #endif | |
| DART_EXPORT void CallFunctionOnNewThreadNonBlocking(int64_t response_id, | |
| void (*fn)(int64_t, | |
| int32_t)) { | |
| #if defined(__linux__) | |
| // std::thread::detach is implemented via pthread_detach, which contains a | |
| // race. https://sourceware.org/bugzilla/show_bug.cgi?id=19951 | |
| Data* data = new Data; | |
| data->fn = fn; | |
| data->a = response_id; | |
| data->b = 123; | |
| pthread_attr_t attr; | |
| int result = pthread_attr_init(&attr); | |
| if (result != 0) perror("pthread_attr_init"); | |
| result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
| if (result != 0) perror("pthread_attr_setdetachstate"); | |
| pthread_t tid; | |
| result = pthread_create(&tid, &attr, Start, data); | |
| if (result != 0) perror("pthread_create"); | |
| result = pthread_attr_destroy(&attr); | |
| if (result != 0) perror("pthread_attr_destroy"); | |
| #else | |
| std::thread thread(fn, response_id, 123); | |
| thread.detach(); // NOLINT(not glibc) | |
| #endif | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Tests for isolate local callbacks. | |
| DART_EXPORT int32_t CallTwoIntFunction(int32_t (*fn)(int32_t, int32_t), | |
| int32_t a, | |
| int32_t b) { | |
| return fn(a, b); | |
| } | |
| DART_EXPORT void CallTwoIntVoidFunction(void (*fn)(int32_t, int32_t), | |
| int32_t a, | |
| int32_t b) { | |
| fn(a, b); | |
| } | |
| DART_EXPORT void* CallTwoIntPointerFunction(void* (*fn)(int32_t, int32_t), | |
| int32_t a, | |
| int32_t b) { | |
| return fn(a, b); | |
| } | |
| DART_EXPORT int32_t CallTwoPointerIntFunction(int32_t (*fn)(void*, void*), | |
| void* a, | |
| void* b) { | |
| return fn(a, b); | |
| } | |
| DART_EXPORT char TakeString(char* my_string) { | |
| std::cout << "TakeString(" << my_string << ")\n"; | |
| return my_string[4]; | |
| } | |
| struct Vec4 { | |
| double x; | |
| double y; | |
| double z; | |
| double w; | |
| }; | |
| // Rearranges the components of the input Vec4 and stores them in the result | |
| // Vec4. | |
| // [x, y, z, w] -> [y, z, w, x] | |
| DART_EXPORT void TwiddleVec4Components(Vec4 input, Vec4* result) { | |
| result->x = input.y; | |
| result->y = input.z; | |
| result->z = input.w; | |
| result->w = input.x; | |
| } | |
| DART_EXPORT int32_t CallTwoIntFunctionIsolateOwnership( | |
| void (*Dart_ClearCurrentThreadOwnsIsolate_ForTesting)(), | |
| int32_t (*fn)(int32_t, int32_t), | |
| int32_t a, | |
| int32_t b) { | |
| Dart_Isolate isolate = Dart_CurrentIsolate(); | |
| Dart_SetCurrentThreadOwnsIsolate(); | |
| Dart_ExitIsolate(); | |
| int result = fn(a, b); | |
| Dart_EnterIsolate(isolate); | |
| Dart_ClearCurrentThreadOwnsIsolate_ForTesting(); | |
| return result; | |
| } | |
| } // namespace dart |