diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8523bc9..1dd4bec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ on: jobs: test: - name: Kotlin/Native (${{ matrix.os }}) + name: Tests (${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false diff --git a/libs.versions.toml b/libs.versions.toml index 8a36a5c..57a97eb 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -1,14 +1,13 @@ [versions] -kotlin = "2.3.21" +kotlin = "2.4.0" kotlin_serialization = "1.11.0" coroutines = "1.11.0" benchmark = "0.4.17" android_plugin = "9.2.0" -# old versions to allow testing on SDK < 21 -android_test_runner = "1.2.0" -android_test_juint = "1.1.5" +android_test_runner = "1.2.0" #| old versions to allow testing on SDK < 21 +android_test_juint = "1.1.5" #| webidl = "1.0.3" diff --git a/modules/benchmarks/build.gradle.kts b/modules/benchmarks/build.gradle.kts index a37cc3e..f5f3d42 100644 --- a/modules/benchmarks/build.gradle.kts +++ b/modules/benchmarks/build.gradle.kts @@ -9,6 +9,10 @@ group = "com.huskerdev" version = projectDir.parentFile.parentFile.resolve("VERSION").readText() kotlin { + jvmToolchain { + vendor = JvmVendorSpec.GRAAL_VM + languageVersion = JavaLanguageVersion.of(23) + } jvm() sourceSets.commonMain.dependencies { diff --git a/modules/benchmarks/natives/foreignBindings/include/api.h b/modules/benchmarks/natives/foreignBindings/include/api.h index 28afab9..1848d0c 100644 --- a/modules/benchmarks/natives/foreignBindings/include/api.h +++ b/modules/benchmarks/natives/foreignBindings/include/api.h @@ -10,8 +10,6 @@ #define KOTLIN_NATIVE_FOREIGNBINDINGS_H #include -#include - #include #include @@ -19,6 +17,18 @@ extern "C" { #endif +#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) +#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ + 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ + 5, 4, 3, 2, 1, 0) +#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ + _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ + _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ + _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ + _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + // ╔════════════════╗ // ║ stdlib ║ // ╚════════════════╝ @@ -34,72 +44,35 @@ typedef uint16_t KChar; typedef struct KString { const char* data; + size_t size; KInt length; - KBoolean releasable; - KBoolean released; + char __flags; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; -} - -static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } -} - -#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) -#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) -#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count - -#define KArrayDef(Name, Type, VarargType) \ -typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ -} Name; \ - \ -static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ -} \ - \ -static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ -} \ - \ -static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ -} - -KArrayDef(KCharArray, KChar, int32_t) -KArrayDef(KBooleanArray, KBoolean, int32_t) -KArrayDef(KByteArray, KByte, int32_t) -KArrayDef(KShortArray, KShort, int32_t) -KArrayDef(KIntArray, KInt, int32_t) -KArrayDef(KLongArray, KLong, int64_t) -KArrayDef(KFloatArray, KFloat, double) -KArrayDef(KDoubleArray, KDouble, double) -KArrayDef(KArray, void*, void*) +KString* KString_new(const char* data, KInt length, KInt size); +KString* KString_clone(const KString* self); +void KString_free(KString* self); + +#define KArrayDef(Name, Type) \ +typedef struct Name { \ + const Type* elements; \ + size_t size; \ + KInt length; \ + char __flags; \ +} Name; \ + \ +Name* Name##_new(const Type* elements, const KInt length); \ +Name* _##Name##_of(const int n, ...); + +KArrayDef(KCharArray, KChar ) +KArrayDef(KBooleanArray, KBoolean) +KArrayDef(KByteArray, KByte ) +KArrayDef(KShortArray, KShort ) +KArrayDef(KIntArray, KInt ) +KArrayDef(KLongArray, KLong ) +KArrayDef(KFloatArray, KFloat ) +KArrayDef(KDoubleArray, KDouble ) +KArrayDef(KArray, void* ) #undef KArrayDef #define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) @@ -111,11 +84,31 @@ KArrayDef(KArray, void*, void*) #define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ +#define KArrayCloneFreeDef(Name, Type) \ +Name* Name##_clone(const Name* self); \ +void Name##_free(Name* self); + +KArrayCloneFreeDef(KCharArray, KChar) +KArrayCloneFreeDef(KBooleanArray, KBoolean) +KArrayCloneFreeDef(KByteArray, KByte) +KArrayCloneFreeDef(KShortArray, KShort) +KArrayCloneFreeDef(KIntArray, KInt) +KArrayCloneFreeDef(KLongArray, KLong) +KArrayCloneFreeDef(KFloatArray, KFloat) +KArrayCloneFreeDef(KDoubleArray, KDouble) +#undef KArrayCloneFreeDef + +KArray* KArray_clone(const KArray* self, void* (*cloneOp)(void*)); +void KArray_free(const KArray* self, void (*freeOp)(void*)); + +#define KCallbackDef(Name, Type, ...) \ +struct Name { \ + char __flags; \ + Type (*invoke)(Name* self, ##__VA_ARGS__); \ + Name* (*clone)(Name* self); \ + KBoolean (*equals)(Name* self, Name* obj); \ + KInt (*hashCode)(Name* self); \ + void (*free)(Name* self); \ }; // ╔═══════════════════╗ @@ -124,10 +117,10 @@ struct Name { \ KInt callForeign(); KInt callForeignAdd(KInt a, KInt b); -KInt callForeignString(KString arg); +KInt callForeignString(KString* arg); KInt callCriticalForeign(); KInt callCriticalForeignAdd(KInt a, KInt b); -KInt callCriticalForeignString(KString arg); +KInt callCriticalForeignString(KString* arg); #ifdef __cplusplus } diff --git a/modules/benchmarks/natives/foreignBindings/src/bindings.c b/modules/benchmarks/natives/foreignBindings/src/bindings.c index 0fc10ae..c8d254d 100644 --- a/modules/benchmarks/natives/foreignBindings/src/bindings.c +++ b/modules/benchmarks/natives/foreignBindings/src/bindings.c @@ -8,7 +8,7 @@ KInt callForeignAdd(KInt a, KInt b) { return a + b; } -KInt callForeignString(KString arg) { +KInt callForeignString(KString* arg) { return 10; } @@ -21,6 +21,6 @@ KInt callCriticalForeignAdd(KInt a, KInt b) { return a + b; } -KInt callCriticalForeignString(KString arg) { +KInt callCriticalForeignString(KString* arg) { return 10; } \ No newline at end of file diff --git a/modules/benchmarks/natives/jniBindings/include/api.h b/modules/benchmarks/natives/jniBindings/include/api.h index 66605ae..14c9b66 100644 --- a/modules/benchmarks/natives/jniBindings/include/api.h +++ b/modules/benchmarks/natives/jniBindings/include/api.h @@ -10,8 +10,6 @@ #define KOTLIN_NATIVE_JNIBINDINGS_H #include -#include - #include #include @@ -19,6 +17,18 @@ extern "C" { #endif +#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) +#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ + 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ + 5, 4, 3, 2, 1, 0) +#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ + _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ + _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ + _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ + _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + // ╔════════════════╗ // ║ stdlib ║ // ╚════════════════╝ @@ -34,72 +44,35 @@ typedef uint16_t KChar; typedef struct KString { const char* data; + size_t size; KInt length; - KBoolean releasable; - KBoolean released; + char __flags; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; -} - -static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } -} - -#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) -#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) -#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count - -#define KArrayDef(Name, Type, VarargType) \ -typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ -} Name; \ - \ -static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ -} \ - \ -static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ -} \ - \ -static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ -} - -KArrayDef(KCharArray, KChar, int32_t) -KArrayDef(KBooleanArray, KBoolean, int32_t) -KArrayDef(KByteArray, KByte, int32_t) -KArrayDef(KShortArray, KShort, int32_t) -KArrayDef(KIntArray, KInt, int32_t) -KArrayDef(KLongArray, KLong, int64_t) -KArrayDef(KFloatArray, KFloat, double) -KArrayDef(KDoubleArray, KDouble, double) -KArrayDef(KArray, void*, void*) +KString* KString_new(const char* data, KInt length, KInt size); +KString* KString_clone(const KString* self); +void KString_free(KString* self); + +#define KArrayDef(Name, Type) \ +typedef struct Name { \ + const Type* elements; \ + size_t size; \ + KInt length; \ + char __flags; \ +} Name; \ + \ +Name* Name##_new(const Type* elements, const KInt length); \ +Name* _##Name##_of(const int n, ...); + +KArrayDef(KCharArray, KChar ) +KArrayDef(KBooleanArray, KBoolean) +KArrayDef(KByteArray, KByte ) +KArrayDef(KShortArray, KShort ) +KArrayDef(KIntArray, KInt ) +KArrayDef(KLongArray, KLong ) +KArrayDef(KFloatArray, KFloat ) +KArrayDef(KDoubleArray, KDouble ) +KArrayDef(KArray, void* ) #undef KArrayDef #define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) @@ -111,11 +84,31 @@ KArrayDef(KArray, void*, void*) #define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ +#define KArrayCloneFreeDef(Name, Type) \ +Name* Name##_clone(const Name* self); \ +void Name##_free(Name* self); + +KArrayCloneFreeDef(KCharArray, KChar) +KArrayCloneFreeDef(KBooleanArray, KBoolean) +KArrayCloneFreeDef(KByteArray, KByte) +KArrayCloneFreeDef(KShortArray, KShort) +KArrayCloneFreeDef(KIntArray, KInt) +KArrayCloneFreeDef(KLongArray, KLong) +KArrayCloneFreeDef(KFloatArray, KFloat) +KArrayCloneFreeDef(KDoubleArray, KDouble) +#undef KArrayCloneFreeDef + +KArray* KArray_clone(const KArray* self, void* (*cloneOp)(void*)); +void KArray_free(const KArray* self, void (*freeOp)(void*)); + +#define KCallbackDef(Name, Type, ...) \ +struct Name { \ + char __flags; \ + Type (*invoke)(Name* self, ##__VA_ARGS__); \ + Name* (*clone)(Name* self); \ + KBoolean (*equals)(Name* self, Name* obj); \ + KInt (*hashCode)(Name* self); \ + void (*free)(Name* self); \ }; // ╔═══════════════════╗ @@ -124,9 +117,9 @@ struct Name { \ KInt callJni(); KInt callJniAdd(KInt a, KInt b); -KInt callJniString(KString arg); +KInt callJniString(KString* arg); KInt callCriticalJni(); -KInt callCriticalJniString(KString arg); +KInt callCriticalJniString(KString* arg); #ifdef __cplusplus } diff --git a/modules/benchmarks/natives/jniBindings/src/bindings.c b/modules/benchmarks/natives/jniBindings/src/bindings.c index 3bf4ca9..7af0bc8 100644 --- a/modules/benchmarks/natives/jniBindings/src/bindings.c +++ b/modules/benchmarks/natives/jniBindings/src/bindings.c @@ -8,7 +8,7 @@ KInt callJniAdd(KInt a, KInt b) { return a + b; } -KInt callJniString(KString arg) { +KInt callJniString(KString* arg) { return 10; } @@ -16,6 +16,6 @@ KInt callCriticalJni() { return 10; } -KInt callCriticalJniString(KString arg) { +KInt callCriticalJniString(KString* arg) { return 10; } \ No newline at end of file diff --git a/modules/benchmarks/natives/jvmciBindings/include/api.h b/modules/benchmarks/natives/jvmciBindings/include/api.h index ec4540a..28611aa 100644 --- a/modules/benchmarks/natives/jvmciBindings/include/api.h +++ b/modules/benchmarks/natives/jvmciBindings/include/api.h @@ -10,8 +10,6 @@ #define KOTLIN_NATIVE_JVMCIBINDINGS_H #include -#include - #include #include @@ -19,6 +17,18 @@ extern "C" { #endif +#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) +#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ + 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ + 5, 4, 3, 2, 1, 0) +#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ + _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ + _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ + _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ + _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + // ╔════════════════╗ // ║ stdlib ║ // ╚════════════════╝ @@ -34,72 +44,35 @@ typedef uint16_t KChar; typedef struct KString { const char* data; + size_t size; KInt length; - KBoolean releasable; - KBoolean released; + char __flags; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; -} - -static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } -} - -#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) -#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) -#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count - -#define KArrayDef(Name, Type, VarargType) \ -typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ -} Name; \ - \ -static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ -} \ - \ -static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ -} \ - \ -static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ -} - -KArrayDef(KCharArray, KChar, int32_t) -KArrayDef(KBooleanArray, KBoolean, int32_t) -KArrayDef(KByteArray, KByte, int32_t) -KArrayDef(KShortArray, KShort, int32_t) -KArrayDef(KIntArray, KInt, int32_t) -KArrayDef(KLongArray, KLong, int64_t) -KArrayDef(KFloatArray, KFloat, double) -KArrayDef(KDoubleArray, KDouble, double) -KArrayDef(KArray, void*, void*) +KString* KString_new(const char* data, KInt length, KInt size); +KString* KString_clone(const KString* self); +void KString_free(KString* self); + +#define KArrayDef(Name, Type) \ +typedef struct Name { \ + const Type* elements; \ + size_t size; \ + KInt length; \ + char __flags; \ +} Name; \ + \ +Name* Name##_new(const Type* elements, const KInt length); \ +Name* _##Name##_of(const int n, ...); + +KArrayDef(KCharArray, KChar ) +KArrayDef(KBooleanArray, KBoolean) +KArrayDef(KByteArray, KByte ) +KArrayDef(KShortArray, KShort ) +KArrayDef(KIntArray, KInt ) +KArrayDef(KLongArray, KLong ) +KArrayDef(KFloatArray, KFloat ) +KArrayDef(KDoubleArray, KDouble ) +KArrayDef(KArray, void* ) #undef KArrayDef #define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) @@ -111,11 +84,31 @@ KArrayDef(KArray, void*, void*) #define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ +#define KArrayCloneFreeDef(Name, Type) \ +Name* Name##_clone(const Name* self); \ +void Name##_free(Name* self); + +KArrayCloneFreeDef(KCharArray, KChar) +KArrayCloneFreeDef(KBooleanArray, KBoolean) +KArrayCloneFreeDef(KByteArray, KByte) +KArrayCloneFreeDef(KShortArray, KShort) +KArrayCloneFreeDef(KIntArray, KInt) +KArrayCloneFreeDef(KLongArray, KLong) +KArrayCloneFreeDef(KFloatArray, KFloat) +KArrayCloneFreeDef(KDoubleArray, KDouble) +#undef KArrayCloneFreeDef + +KArray* KArray_clone(const KArray* self, void* (*cloneOp)(void*)); +void KArray_free(const KArray* self, void (*freeOp)(void*)); + +#define KCallbackDef(Name, Type, ...) \ +struct Name { \ + char __flags; \ + Type (*invoke)(Name* self, ##__VA_ARGS__); \ + Name* (*clone)(Name* self); \ + KBoolean (*equals)(Name* self, Name* obj); \ + KInt (*hashCode)(Name* self); \ + void (*free)(Name* self); \ }; // ╔═══════════════════╗ @@ -124,7 +117,7 @@ struct Name { \ KInt callCriticalJVMCI(); KInt callCriticalJVMCIAdd(KInt a, KInt b); -KInt callCriticalJVMCIString(KString arg); +KInt callCriticalJVMCIString(KString* arg); #ifdef __cplusplus } diff --git a/modules/benchmarks/natives/jvmciBindings/src/bindings.c b/modules/benchmarks/natives/jvmciBindings/src/bindings.c index ac60f4c..50d476b 100644 --- a/modules/benchmarks/natives/jvmciBindings/src/bindings.c +++ b/modules/benchmarks/natives/jvmciBindings/src/bindings.c @@ -9,6 +9,6 @@ KInt callCriticalJVMCIAdd(KInt a, KInt b) { return a + b; } -KInt callCriticalJVMCIString(KString arg) { +KInt callCriticalJVMCIString(KString* arg) { return 10; } \ No newline at end of file diff --git a/modules/benchmarks/src/jvmMain/kotlin/benchmark/Benchmark.kt b/modules/benchmarks/src/jvmMain/kotlin/benchmark/Benchmark.kt index c45805d..cf09408 100644 --- a/modules/benchmarks/src/jvmMain/kotlin/benchmark/Benchmark.kt +++ b/modules/benchmarks/src/jvmMain/kotlin/benchmark/Benchmark.kt @@ -34,6 +34,7 @@ open class NativeKtBenchmark { @Setup open fun prepare() { + println("JVM: ${System.getProperty("java.vendor.version")}") a = ThreadLocalRandom.current().nextInt() b = ThreadLocalRandom.current().nextInt() diff --git a/modules/examples/freetype/natives/freetypeBindings/include/api.h b/modules/examples/freetype/natives/freetypeBindings/include/api.h index 70082b2..c33fe2d 100644 --- a/modules/examples/freetype/natives/freetypeBindings/include/api.h +++ b/modules/examples/freetype/natives/freetypeBindings/include/api.h @@ -10,8 +10,6 @@ #define KOTLIN_NATIVE_FREETYPEBINDINGS_H #include -#include - #include #include @@ -19,6 +17,18 @@ extern "C" { #endif +#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) +#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ + 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ + 5, 4, 3, 2, 1, 0) +#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ + _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ + _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ + _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ + _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + // ╔════════════════╗ // ║ stdlib ║ // ╚════════════════╝ @@ -34,72 +44,35 @@ typedef uint16_t KChar; typedef struct KString { const char* data; + size_t size; KInt length; - KBoolean releasable; - KBoolean released; + char __flags; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; -} - -static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } -} - -#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) -#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) -#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count - -#define KArrayDef(Name, Type, VarargType) \ -typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ -} Name; \ - \ -static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ -} \ - \ -static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ -} \ - \ -static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ -} - -KArrayDef(KCharArray, KChar, int32_t) -KArrayDef(KBooleanArray, KBoolean, int32_t) -KArrayDef(KByteArray, KByte, int32_t) -KArrayDef(KShortArray, KShort, int32_t) -KArrayDef(KIntArray, KInt, int32_t) -KArrayDef(KLongArray, KLong, int64_t) -KArrayDef(KFloatArray, KFloat, double) -KArrayDef(KDoubleArray, KDouble, double) -KArrayDef(KArray, void*, void*) +KString* KString_new(const char* data, KInt length, KInt size); +KString* KString_clone(const KString* self); +void KString_free(KString* self); + +#define KArrayDef(Name, Type) \ +typedef struct Name { \ + const Type* elements; \ + size_t size; \ + KInt length; \ + char __flags; \ +} Name; \ + \ +Name* Name##_new(const Type* elements, const KInt length); \ +Name* _##Name##_of(const int n, ...); + +KArrayDef(KCharArray, KChar ) +KArrayDef(KBooleanArray, KBoolean) +KArrayDef(KByteArray, KByte ) +KArrayDef(KShortArray, KShort ) +KArrayDef(KIntArray, KInt ) +KArrayDef(KLongArray, KLong ) +KArrayDef(KFloatArray, KFloat ) +KArrayDef(KDoubleArray, KDouble ) +KArrayDef(KArray, void* ) #undef KArrayDef #define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) @@ -111,11 +84,31 @@ KArrayDef(KArray, void*, void*) #define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ +#define KArrayCloneFreeDef(Name, Type) \ +Name* Name##_clone(const Name* self); \ +void Name##_free(Name* self); + +KArrayCloneFreeDef(KCharArray, KChar) +KArrayCloneFreeDef(KBooleanArray, KBoolean) +KArrayCloneFreeDef(KByteArray, KByte) +KArrayCloneFreeDef(KShortArray, KShort) +KArrayCloneFreeDef(KIntArray, KInt) +KArrayCloneFreeDef(KLongArray, KLong) +KArrayCloneFreeDef(KFloatArray, KFloat) +KArrayCloneFreeDef(KDoubleArray, KDouble) +#undef KArrayCloneFreeDef + +KArray* KArray_clone(const KArray* self, void* (*cloneOp)(void*)); +void KArray_free(const KArray* self, void (*freeOp)(void*)); + +#define KCallbackDef(Name, Type, ...) \ +struct Name { \ + char __flags; \ + Type (*invoke)(Name* self, ##__VA_ARGS__); \ + Name* (*clone)(Name* self); \ + KBoolean (*equals)(Name* self, Name* obj); \ + KInt (*hashCode)(Name* self); \ + void (*free)(Name* self); \ }; // ╔═══════════════════╗ diff --git a/modules/examples/glfw/natives/glfwBindings/include/api.h b/modules/examples/glfw/natives/glfwBindings/include/api.h index 51d1dcb..18ff713 100644 --- a/modules/examples/glfw/natives/glfwBindings/include/api.h +++ b/modules/examples/glfw/natives/glfwBindings/include/api.h @@ -10,8 +10,6 @@ #define KOTLIN_NATIVE_GLFWBINDINGS_H #include -#include - #include #include @@ -19,6 +17,18 @@ extern "C" { #endif +#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) +#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ + 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ + 5, 4, 3, 2, 1, 0) +#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ + _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ + _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ + _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ + _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + // ╔════════════════╗ // ║ stdlib ║ // ╚════════════════╝ @@ -34,72 +44,35 @@ typedef uint16_t KChar; typedef struct KString { const char* data; + size_t size; KInt length; - KBoolean releasable; - KBoolean released; + char __flags; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; -} - -static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } -} - -#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) -#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) -#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count - -#define KArrayDef(Name, Type, VarargType) \ -typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ -} Name; \ - \ -static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ -} \ - \ -static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ -} \ - \ -static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ -} - -KArrayDef(KCharArray, KChar, int32_t) -KArrayDef(KBooleanArray, KBoolean, int32_t) -KArrayDef(KByteArray, KByte, int32_t) -KArrayDef(KShortArray, KShort, int32_t) -KArrayDef(KIntArray, KInt, int32_t) -KArrayDef(KLongArray, KLong, int64_t) -KArrayDef(KFloatArray, KFloat, double) -KArrayDef(KDoubleArray, KDouble, double) -KArrayDef(KArray, void*, void*) +KString* KString_new(const char* data, KInt length, KInt size); +KString* KString_clone(const KString* self); +void KString_free(KString* self); + +#define KArrayDef(Name, Type) \ +typedef struct Name { \ + const Type* elements; \ + size_t size; \ + KInt length; \ + char __flags; \ +} Name; \ + \ +Name* Name##_new(const Type* elements, const KInt length); \ +Name* _##Name##_of(const int n, ...); + +KArrayDef(KCharArray, KChar ) +KArrayDef(KBooleanArray, KBoolean) +KArrayDef(KByteArray, KByte ) +KArrayDef(KShortArray, KShort ) +KArrayDef(KIntArray, KInt ) +KArrayDef(KLongArray, KLong ) +KArrayDef(KFloatArray, KFloat ) +KArrayDef(KDoubleArray, KDouble ) +KArrayDef(KArray, void* ) #undef KArrayDef #define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) @@ -111,11 +84,31 @@ KArrayDef(KArray, void*, void*) #define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ +#define KArrayCloneFreeDef(Name, Type) \ +Name* Name##_clone(const Name* self); \ +void Name##_free(Name* self); + +KArrayCloneFreeDef(KCharArray, KChar) +KArrayCloneFreeDef(KBooleanArray, KBoolean) +KArrayCloneFreeDef(KByteArray, KByte) +KArrayCloneFreeDef(KShortArray, KShort) +KArrayCloneFreeDef(KIntArray, KInt) +KArrayCloneFreeDef(KLongArray, KLong) +KArrayCloneFreeDef(KFloatArray, KFloat) +KArrayCloneFreeDef(KDoubleArray, KDouble) +#undef KArrayCloneFreeDef + +KArray* KArray_clone(const KArray* self, void* (*cloneOp)(void*)); +void KArray_free(const KArray* self, void (*freeOp)(void*)); + +#define KCallbackDef(Name, Type, ...) \ +struct Name { \ + char __flags; \ + Type (*invoke)(Name* self, ##__VA_ARGS__); \ + Name* (*clone)(Name* self); \ + KBoolean (*equals)(Name* self, Name* obj); \ + KInt (*hashCode)(Name* self); \ + void (*free)(Name* self); \ }; // ╔═══════════════════╗ diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/AndroidConfigurator.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/AndroidConfigurator.kt index 300d76f..2c1c32e 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/AndroidConfigurator.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/AndroidConfigurator.kt @@ -4,9 +4,9 @@ import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryExtension import com.android.build.api.variant.KotlinMultiplatformAndroidComponentsExtension import com.android.build.gradle.internal.tasks.factory.dependsOn import com.huskerdev.nativekt.plugin.* -import com.huskerdev.nativekt.printers.c.CHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiImplPrinter import com.huskerdev.nativekt.printers.kotlin.KotlinAndroidPrinter -import com.huskerdev.nativekt.printers.c.CJniArenaPrinter import com.huskerdev.nativekt.printers.c.CJniPrinter import com.huskerdev.nativekt.printers.c.CJniUtilsPrinter import com.huskerdev.nativekt.utils.* @@ -164,7 +164,7 @@ private abstract class PrepareNativesAndroid: DefaultTask() { doLast { val idl = Json.decodeFromString(idl) - val srcList = arrayListOf("jni_bindings.c") + val srcList = arrayListOf("api.c", "jni_bindings.c") // Create Kotlin/Android bindings KotlinAndroidPrinter( @@ -194,14 +194,16 @@ private abstract class PrepareNativesAndroid: DefaultTask() { isAndroidCriticalEnabled = useAndroidCriticalNative ) - CJniArenaPrinter( - target = File(nativesBuildDir, "jni_arena.h"), - callbacks = idl.callbacks.isNotEmpty() + CApiHeaderPrinter( + idl = idl, + target = File(nativesBuildDir, "api.h"), + isInternal = true ) - CHeaderPrinter( + CApiImplPrinter( idl = idl, - target = File(nativesBuildDir, "api.h") + target = File(nativesBuildDir, "api.c"), + classPath = moduleClasspath ) when(buildSystem) { diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/JsConfigurator.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/JsConfigurator.kt index c86cba0..9e4faf8 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/JsConfigurator.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/JsConfigurator.kt @@ -5,7 +5,8 @@ import com.huskerdev.nativekt.plugin.BuildSystem import com.huskerdev.nativekt.plugin.NATIVE_TASK_GROUP import com.huskerdev.nativekt.plugin.NativeKtJsInterface import com.huskerdev.nativekt.plugin.NativeProject -import com.huskerdev.nativekt.printers.c.CHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiImplPrinter import com.huskerdev.nativekt.printers.c.CEmscriptenPrinter import com.huskerdev.nativekt.printers.kotlin.KotlinJsPrinter import com.huskerdev.nativekt.utils.* @@ -150,19 +151,66 @@ private abstract class PrepareNativesJs: DefaultTask() { expectActual = expectActual ) - CEmscriptenPrinter( - idl = idl, - target = File(nativesBuildDir, "emscripten_bindings.cpp") - ) - - CHeaderPrinter( - idl = idl, - target = File(nativesBuildDir, "api.h") - ) - when(buildSystem) { is BuildSystem.CMake -> { - // Create CMakeLists.txt with emscripten linker flags + CApiHeaderPrinter( + idl = idl, + target = File(nativesBuildDir, "api.h"), + isInternal = true + ) + CApiImplPrinter( + idl = idl, + target = File(nativesBuildDir, "api.c"), + classPath = moduleClasspath + ) + CEmscriptenPrinter( + idl = idl, + target = File(nativesBuildDir, "emscripten_bindings.c") + ) + + val exportedFunctions = buildList { + addAll(listOf( + "free", + "malloc", + "KString_free", + "KCharArray_free", + "KBooleanArray_free", + "KByteArray_free", + "KShortArray_free", + "KIntArray_free", + "KLongArray_free", + "KFloatArray_free", + "KDoubleArray_free", + "KArray_free" + )) + idl.dictionaries.values.mapTo(this) { "${it.name}_free" } + idl.globalOperators().mapTo(this) { it.name } + }.joinToString(separator = ",") { "_$it" } + + val runtimeFunctions = listOf( + "UTF8ToString", "stringToUTF8", "lengthBytesUTF8", + "HEAP8", "HEAP16", "HEAP32", "HEAPF32", "HEAPF64", + "addFunction", "wasmTable" + ).joinToString(separator = ",") + + // ASSERTIONS=2 -s SAFE_HEAP=1 -s STACK_OVERFLOW_CHECK=1 + val args = listOf( + "--no-entry", + + "SAFE_HEAP=1", + "ASSERTIONS=2", + "STACK_OVERFLOW_CHECK=1", + + "ALLOW_MEMORY_GROWTH=1", + "ALLOW_TABLE_GROWTH=1", + "MODULARIZE=1", + "EXPORT_ES6=1", + "WASM_BIGINT=${if (useJsBigInt) "1" else "0"}", + "EXPORTED_RUNTIME_METHODS=$runtimeFunctions", + "EXPORTED_FUNCTIONS=$exportedFunctions", + ).joinToString(separator = " ") { "-s $it" } + + // Create CMakeLists.txt with Emscripten linker flags File(nativesBuildDir, "CMakeLists.txt").writeText($$""" cmake_minimum_required(VERSION 3.15) @@ -176,10 +224,9 @@ private abstract class PrepareNativesJs: DefaultTask() { File(nativesBuildDir, "sub").absolutePath.replace("\\", "/") }") - add_executable(lib$$moduleName $ emscripten_bindings.cpp) - set_target_properties(lib$$moduleName PROPERTIES CXX_STANDARD 17) + add_executable(lib$$moduleName $ emscripten_bindings.c api.c) - set_target_properties(lib$$moduleName PROPERTIES LINK_FLAGS "${EXTRA_LINK_FLAGS} -s -lembind -s --no-entry -s ALLOW_MEMORY_GROWTH=1 -s ALLOW_TABLE_GROWTH=1 -s MODULARIZE=1 -s EXPORT_ES6=1 -s WASM_BIGINT=$${if(useJsBigInt) "1" else "0"} -s EXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8,lengthBytesUTF8,HEAP8,HEAP16,HEAP32,HEAPF32,HEAPF64,addFunction -s EXPORTED_FUNCTIONS=_free,_malloc") + set_target_properties(lib$$moduleName PROPERTIES LINK_FLAGS "${EXTRA_LINK_FLAGS} $$args") """.trimIndent()) } is BuildSystem.Cargo -> { diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/JvmConfigurator.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/JvmConfigurator.kt index df32b3f..2992842 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/JvmConfigurator.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/JvmConfigurator.kt @@ -4,9 +4,9 @@ import com.huskerdev.nativekt.plugin.BuildSystem import com.huskerdev.nativekt.plugin.NATIVE_TASK_GROUP import com.huskerdev.nativekt.plugin.NativeKtJvmInterface import com.huskerdev.nativekt.plugin.NativeProject -import com.huskerdev.nativekt.printers.c.CHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiImplPrinter import com.huskerdev.nativekt.printers.c.CExportedPrinter -import com.huskerdev.nativekt.printers.c.CJniArenaPrinter import com.huskerdev.nativekt.printers.c.CJniPrinter import com.huskerdev.nativekt.printers.c.CJniUtilsPrinter import com.huskerdev.nativekt.printers.kotlin.KotlinJvmPrinter @@ -199,7 +199,7 @@ private abstract class PrepareNativesJvm: DefaultTask() { val nativesBuildDir = File(nativesBuildDir) nativesBuildDir.fresh() - val srcList = arrayListOf() + val srcList = arrayListOf("api.c") val includeList = arrayListOf() // Generate all files @@ -238,11 +238,6 @@ private abstract class PrepareNativesJvm: DefaultTask() { isAndroidCriticalEnabled = false ) - CJniArenaPrinter( - target = File(nativesBuildDir, "jni_arena.h"), - callbacks = idl.callbacks.isNotEmpty() - ) - // unpack jni headers val includeDir = File(nativesBuildDir, "include") if(!includeDir.exists()) { @@ -284,9 +279,16 @@ private abstract class PrepareNativesJvm: DefaultTask() { ) } - CHeaderPrinter( + CApiHeaderPrinter( + idl = idl, + target = File(nativesBuildDir, "api.h"), + isInternal = true + ) + + CApiImplPrinter( idl = idl, - target = File(nativesBuildDir, "api.h") + target = File(nativesBuildDir, "api.c"), + classPath = moduleClasspath ) when(buildSystem) { diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/NativeConfigurator.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/NativeConfigurator.kt index 5255e7b..5b61080 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/NativeConfigurator.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/configurators/NativeConfigurator.kt @@ -7,8 +7,9 @@ import com.huskerdev.nativekt.plugin.CMakeBuildType import com.huskerdev.nativekt.plugin.NATIVE_TASK_GROUP import com.huskerdev.nativekt.plugin.NativeKtNativeInterface import com.huskerdev.nativekt.plugin.NativeProject -import com.huskerdev.nativekt.printers.c.CHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiHeaderPrinter import com.huskerdev.nativekt.printers.DefPrinter +import com.huskerdev.nativekt.printers.c.CApiImplPrinter import com.huskerdev.nativekt.printers.kotlin.KotlinNativePrinter import com.huskerdev.nativekt.utils.* import com.huskerdev.webidl.resolver.IdlResolver @@ -357,7 +358,7 @@ private abstract class PrepareNativesKn @Inject constructor( val linkerOpts = arrayListOf() // Generate header - CHeaderPrinter( + CApiHeaderPrinter( idl = idl, target = headerFile, guardName = moduleName.uppercase(), @@ -367,6 +368,18 @@ private abstract class PrepareNativesKn @Inject constructor( is BuildSystem.CMake -> { val buildDir = File(nativesBuildDir, "build") + CApiHeaderPrinter( + idl = idl, + target = File(buildDir, "api.h"), + isInternal = true + ) + + CApiImplPrinter( + idl = idl, + target = File(buildDir, "api.c"), + classPath = moduleClasspath + ) + // Create CMake file File(nativesBuildDir, "CMakeLists.txt").writeText($$""" cmake_minimum_required(VERSION 3.15) @@ -377,14 +390,14 @@ private abstract class PrepareNativesKn @Inject constructor( File(buildDir, "common").absolutePath.replace("\\", "/") }") - add_library(lib_$$moduleName SHARED stub.c) + add_library(lib_$$moduleName SHARED api.c) target_link_libraries(lib_$$moduleName PUBLIC $$moduleName) - add_library(libstatic_$$moduleName STATIC stub.c) + add_library(libstatic_$$moduleName STATIC api.c) target_link_libraries(libstatic_$$moduleName PUBLIC $$moduleName) """.trimIndent()) - File(nativesBuildDir, "stub.c").writeText("") + //File(nativesBuildDir, "stub.c").writeText("") // Configure CMake (if needed) if(shouldInit) { @@ -421,7 +434,6 @@ private abstract class PrepareNativesKn @Inject constructor( target = File(kotlinFile), classPath = moduleClasspath, moduleName = moduleName, - is32Bit = targetType in setOf(TargetType.WATCHOS_ARM32, TargetType.WATCHOS_ARM64), useCoroutines = useCoroutines, expectActual = expectActual ) diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/plugin/Configurator.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/plugin/Configurator.kt index d6a9da2..b9420ab 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/plugin/Configurator.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/plugin/Configurator.kt @@ -3,7 +3,7 @@ package com.huskerdev.nativekt.plugin import com.android.build.api.variant.KotlinMultiplatformAndroidComponentsExtension import com.huskerdev.nativekt.TargetType import com.huskerdev.nativekt.configurators.* -import com.huskerdev.nativekt.printers.c.CHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiHeaderPrinter import com.huskerdev.nativekt.printers.rust.RustPrinter import com.huskerdev.nativekt.utils.validateIDL import com.huskerdev.nativekt.utils.dir @@ -15,7 +15,6 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.internal.extensions.stdlib.capitalized import org.gradle.kotlin.dsl.the import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension @@ -78,7 +77,7 @@ fun NativeKtPlugin.configureKotlin( when(module.buildSystem) { is BuildSystem.CMake -> { - CHeaderPrinter( + CApiHeaderPrinter( idl = idl, target = module.getHeaderFile(project), guardName = module.name.uppercase() @@ -227,7 +226,6 @@ private fun getTargetType( ): TargetType { when (kotlin) { is KotlinSingleJavaTargetExtension -> return TargetType.JVM - is KotlinJsProjectExtension -> return TargetType.JS is KotlinAndroidProjectExtension -> return TargetType.ANDROID } kotlin as KotlinTargetsContainer diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/plugin/InitTask.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/plugin/InitTask.kt index 35a12e5..bc0111c 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/plugin/InitTask.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/plugin/InitTask.kt @@ -1,7 +1,7 @@ package com.huskerdev.nativekt.plugin import com.huskerdev.nativekt.NDLEnv -import com.huskerdev.nativekt.printers.c.CHeaderPrinter +import com.huskerdev.nativekt.printers.c.CApiHeaderPrinter import com.huskerdev.webidl.WebIDL import com.huskerdev.webidl.jvm.iterator import org.gradle.api.DefaultTask @@ -56,7 +56,7 @@ abstract class InitTask: DefaultTask() { env = NDLEnv() ) - CHeaderPrinter( + CApiHeaderPrinter( idl = idl, target = File(dir, "include/api.h"), guardName = moduleName.uppercase() diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CHeaderPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt similarity index 53% rename from modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CHeaderPrinter.kt rename to modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt index d1710bf..79cb52d 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CHeaderPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt @@ -1,23 +1,15 @@ package com.huskerdev.nativekt.printers.c -import com.huskerdev.nativekt.utils.allFields -import com.huskerdev.nativekt.utils.globalOperators -import com.huskerdev.nativekt.utils.isCallback -import com.huskerdev.nativekt.utils.isDictionary -import com.huskerdev.nativekt.utils.printLabel -import com.huskerdev.nativekt.utils.toCDefType -import com.huskerdev.webidl.resolver.IdlResolver -import com.huskerdev.webidl.resolver.ResolvedIdlCallbackFunction -import com.huskerdev.webidl.resolver.ResolvedIdlDictionary -import com.huskerdev.webidl.resolver.ResolvedIdlEnum -import com.huskerdev.webidl.resolver.ResolvedIdlOperation +import com.huskerdev.nativekt.utils.* +import com.huskerdev.webidl.resolver.* import java.io.File import kotlin.math.max -class CHeaderPrinter( +class CApiHeaderPrinter( idl: IdlResolver, target: File, - val guardName: String? = null + val guardName: String? = null, + val isInternal: Boolean = false ) { private val defName = "KOTLIN_NATIVE_${guardName}_H" @@ -32,8 +24,14 @@ class CHeaderPrinter( if(idl.callbacks.isNotEmpty()) { printLabel(builder, "Type defs") - idl.dictionaries.values.forEach { printStructTypedef(builder, it) } - idl.callbacks.values.forEach { printCallbackTypedef(builder, it) } + idl.dictionaries.values.forEach { + builder.append("\ntypedef struct ${it.name} ${it.name};") + } + idl.callbacks.values.forEach { + builder.append("\ntypedef struct ${it.name} ${it.name};") + } + if(isInternal) + builder.append("\ntypedef struct _AbstractCallback _AbstractCallback;") builder.append("\n") } @@ -46,8 +44,10 @@ class CHeaderPrinter( if(idl.dictionaries.isNotEmpty()) { builder.append("\n") printLabel(builder, "Structs") - idl.dictionaries.values.forEach { printStruct(builder, it) } - idl.dictionaries.values.forEach { printStructNew(builder, it) } + idl.dictionaries.values.forEach { + printStruct(builder, it) + printStructFunctions(builder, it) + } } printLabel(builder, "Functions") @@ -96,59 +96,32 @@ class CHeaderPrinter( append(" // : ").append(dictionary.implements!!.name) append("\n\t") - dictionary.allFields().joinTo(builder, separator = "\n\t") { field -> - "${field.type.toCDefType()} ${field.name};" - } + buildList { + dictionary.allFields().mapTo(this) { field -> + "${field.type.toCDefType()} ${field.name};" + } + add("char __flags;") + }.joinTo(builder, separator = "\n\t") + append("\n};\n") } - private fun printStructNew(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - append("\nstatic ") - append(dictionary.name) - append("* ") - append(dictionary.name) - append("_new(") - dictionary.allFields().joinTo(builder) { field -> - val const = if( - !field.type.isDictionary() && - !field.type.isCallback() - ) "const " else "" - "$const${field.type.toCDefType()} ${field.name}" - } - append(") {\n\t") - // malloc - append(dictionary.name) - append("* result = (") - append(dictionary.name) - append("*)malloc(sizeof(") - append(dictionary.name) - append("));\n\t") - // set - append("*result = (") - append(dictionary.name) - append("){ ") + private fun printStructFunctions(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + val name = dictionary.name + + append("\n$name* ${name}_new(") + dictionary.allFields().joinTo(builder) { field -> - field.name + "${field.type.toCDefType()} ${field.name}" } - append(" };\n\t") - // return - append("return result;\n}\n") - } - - private fun printStructTypedef(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - append("\ntypedef struct ") - append(dictionary.name) - append(" ") - append(dictionary.name) - append(";") - } + append(");") - private fun printCallbackTypedef(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - append("\ntypedef struct ") - append(callback.name) - append(" ") - append(callback.name) - append(";") + append("\n$name* ${name}_clone(const $name* self);") + append("\nvoid ${name}_free($name* self);") + if(isInternal) { + append("\nvoid _${name}_forceFree($name* self);") + } + append("\n") } private fun printCallbacks(builder: StringBuilder, callbacks: Collection) = builder.apply { @@ -207,7 +180,14 @@ class CHeaderPrinter( } append(")\n") } + if(isInternal) + append("KCallbackDef(_AbstractCallback, void)\n") append("#undef KCallbackDef\n") + + if(isInternal) { + append("\nvoid _AbstractCallback_free(_AbstractCallback* self);") + append("\nvoid __AbstractCallback_forceFree(_AbstractCallback* self);\n") + } } private fun printHeader(builder: StringBuilder){ @@ -231,8 +211,6 @@ class CHeaderPrinter( builder.append(""" #include - #include - #include #include @@ -240,7 +218,31 @@ class CHeaderPrinter( extern "C" { #endif + #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) + #define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ + 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ + 5, 4, 3, 2, 1, 0) + #define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ + _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ + _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ + _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ + _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + """.trimIndent()) + + if(isInternal) { + builder.append(""" + + #define K_FLAG_RELEASABLE 1 + #define K_FLAG_ON_STACK 2 + + #define K_OBJECT_IS_RELEASABLE(flags) ((flags) & K_FLAG_RELEASABLE) + #define K_OBJECT_IS_ON_STACK(flags) ((flags) & K_FLAG_ON_STACK) + + """.trimIndent()) + } } private fun printFooter(builder: StringBuilder){ @@ -256,9 +258,9 @@ class CHeaderPrinter( builder.append("\n\n#endif // $defName") } - private fun printStdLib(builder: StringBuilder){ - builder.append(""" - + private fun printStdLib(builder: StringBuilder) = builder.apply { + append(""" + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -270,72 +272,35 @@ class CHeaderPrinter( typedef struct KString { const char* data; + size_t size; KInt length; - KBoolean releasable; - KBoolean released; + char __flags; } KString; - static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; - } + KString* KString_new(const char* data, KInt length, KInt size); + KString* KString_clone(const KString* self); + void KString_free(KString* self); - static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } - } - - #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) - #define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) - #define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + #define KArrayDef(Name, Type) \ + typedef struct Name { \ + const Type* elements; \ + size_t size; \ + KInt length; \ + char __flags; \ + } Name; \ + \ + Name* Name##_new(const Type* elements, const KInt length); \ + Name* _##Name##_of(const int n, ...); - #define KArrayDef(Name, Type, VarargType) \ - typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ - } Name; \ - \ - static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ - } \ - \ - static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ - } \ - \ - static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ - } - - KArrayDef(KCharArray, KChar, int32_t) - KArrayDef(KBooleanArray, KBoolean, int32_t) - KArrayDef(KByteArray, KByte, int32_t) - KArrayDef(KShortArray, KShort, int32_t) - KArrayDef(KIntArray, KInt, int32_t) - KArrayDef(KLongArray, KLong, int64_t) - KArrayDef(KFloatArray, KFloat, double) - KArrayDef(KDoubleArray, KDouble, double) - KArrayDef(KArray, void*, void*) + KArrayDef(KCharArray, KChar ) + KArrayDef(KBooleanArray, KBoolean) + KArrayDef(KByteArray, KByte ) + KArrayDef(KShortArray, KShort ) + KArrayDef(KIntArray, KInt ) + KArrayDef(KLongArray, KLong ) + KArrayDef(KFloatArray, KFloat ) + KArrayDef(KDoubleArray, KDouble ) + KArrayDef(KArray, void* ) #undef KArrayDef #define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) @@ -346,14 +311,111 @@ class CHeaderPrinter( #define KFloatArray_of(...) _KFloatArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) - - #define KCallbackDef(Name, Type, ...) \ - struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ + + #define KArrayCloneFreeDef(Name, Type) \ + Name* Name##_clone(const Name* self); \ + void Name##_free(Name* self); + + KArrayCloneFreeDef(KCharArray, KChar) + KArrayCloneFreeDef(KBooleanArray, KBoolean) + KArrayCloneFreeDef(KByteArray, KByte) + KArrayCloneFreeDef(KShortArray, KShort) + KArrayCloneFreeDef(KIntArray, KInt) + KArrayCloneFreeDef(KLongArray, KLong) + KArrayCloneFreeDef(KFloatArray, KFloat) + KArrayCloneFreeDef(KDoubleArray, KDouble) + #undef KArrayCloneFreeDef + + KArray* KArray_clone(const KArray* self, void* (*cloneOp)(void*)); + void KArray_free(const KArray* self, void (*freeOp)(void*)); + + #define KCallbackDef(Name, Type, ...) \ + struct Name { \ + char __flags; \ + Type (*invoke)(Name* self, ##__VA_ARGS__); \ + Name* (*clone)(Name* self); \ + KBoolean (*equals)(Name* self, Name* obj); \ + KInt (*hashCode)(Name* self); \ + void (*free)(Name* self); \ }; """.trimIndent()) + + if(isInternal) { + arrayOf( + "KString", + "KCharArray", + "KBooleanArray", + "KByteArray", + "KShortArray", + "KIntArray", + "KLongArray", + "KFloatArray", + "KDoubleArray" + ).joinTo(builder, separator = "") { + "\nvoid _${it}_forceFree($it* self);" + } + append("\nvoid _KArray_forceFree(KArray* self, void (*freeOp)(void*));") + append("\n") + } + } +} + +internal fun cloneFuncFor( + type: ResolvedIdlType, + content: String +) = when { + type.isString() -> "KString_clone($content)" + type.isArray() -> { + (type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> { + val name = declaration.kind.simpleName() + "K${name}Array_clone($content)" + } + is ResolvedIdlEnum -> "KIntArray_clone($content)" + is ResolvedIdlDictionary -> "KArray_clone($content, (void*) ${declaration.name}_clone)" + else -> throw UnsupportedOperationException(type.toString()) + } + } + } + type.isCallback() -> "$content->clone($content)" + type.isDictionary() -> "${(type as ResolvedIdlType.Default).declaration.name}_clone($content)" + else -> content +} + +internal fun freeFuncFor( + type: ResolvedIdlType, + content: String +) = when { + type.isString() -> "KString_free($content)" + type.isArray() -> (type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> "K${declaration.kind.simpleName()}Array_free($content)" + is ResolvedIdlEnum -> "KIntArray_free($content)" + is ResolvedIdlDictionary -> "KArray_free($content, (void*) ${declaration.name}_free)" + else -> throw UnsupportedOperationException(type.toString()) + } + } + type.isCallback() -> "_AbstractCallback_free((_AbstractCallback*) $content)" + type.isDictionary() -> "${(type as ResolvedIdlType.Default).declaration.name}_free(${content})" + else -> null +} + +internal fun forceFreeFuncFor( + type: ResolvedIdlType, + content: String +) = when { + type.isString() -> "_KString_forceFree($content)" + type.isArray() -> (type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> "_K${declaration.kind.simpleName()}Array_forceFree($content)" + is ResolvedIdlEnum -> "_KIntArray_forceFree($content)" + is ResolvedIdlDictionary -> "_KArray_forceFree($content, (void*) _${declaration.name}_forceFree)" + else -> throw UnsupportedOperationException(type.toString()) + } } + type.isCallback() -> "__AbstractCallback_forceFree((_AbstractCallback*) $content)" + type.isDictionary() -> "_${(type as ResolvedIdlType.Default).declaration.name}_forceFree($content)" + else -> null } \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiImplPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiImplPrinter.kt new file mode 100644 index 0000000..392d964 --- /dev/null +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiImplPrinter.kt @@ -0,0 +1,293 @@ +package com.huskerdev.nativekt.printers.c + +import com.huskerdev.nativekt.utils.allFields +import com.huskerdev.nativekt.utils.isPrimitive +import com.huskerdev.nativekt.utils.printLabel +import com.huskerdev.nativekt.utils.toCDefType +import com.huskerdev.webidl.resolver.IdlResolver +import com.huskerdev.webidl.resolver.ResolvedIdlDictionary +import java.io.File + +class CApiImplPrinter( + idl: IdlResolver, + target: File, + val classPath: String +) { + init { + target.parentFile.mkdirs() + + val builder = StringBuilder() + builder.append(""" + #include "api.h" + + #include + #include + + + #ifdef __cplusplus + extern "C" { + #endif + + """.trimIndent()) + printStdLib(builder) + + if(idl.dictionaries.isNotEmpty()) { + printLabel(builder, "Struct functions") + idl.dictionaries.values.forEach { + printStructNew(builder, it) + printStructClone(builder, it) + printStructFree(builder, it) + } + } + + if(idl.callbacks.isNotEmpty()) { + printLabel(builder, "Callback free") + builder.append(""" + + void _AbstractCallback_free(_AbstractCallback* self) { + self->free(self); + } + + void __AbstractCallback_forceFree(_AbstractCallback* self) { + self->__flags |= K_FLAG_RELEASABLE; + self->free(self); + } + + """.trimIndent()) + } + + target.writeText(builder.toString().replace("\n", System.lineSeparator())) + } + + private fun printStructNew(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + append("\n${dictionary.name}* ${dictionary.name}_new(") + + dictionary.allFields().joinTo(builder) { field -> + val const = if(field.type.isPrimitive()) + "const " else "" + "$const${field.type.toCDefType()} ${field.name}" + } + append(") {\n\t") + + // malloc + append("${dictionary.name}* result = (${dictionary.name}*) malloc(sizeof(${dictionary.name}));\n\t") + + // fill + append("*result = (${dictionary.name}) { ") + buildList { + dictionary.allFields().mapTo(this) { it.name } + add("K_FLAG_RELEASABLE") + }.joinTo(builder) + append(" };\n\t") + + // return + append("return result;\n}\n") + } + + private fun printStructClone(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + append("\n${dictionary.name}* ${dictionary.name}_clone(const ${dictionary.name}* self) {\n\t") + + // malloc + append("${dictionary.name}* result = (${dictionary.name}*) malloc(sizeof(${dictionary.name}));\n\t") + + // fill + append("*result = (${dictionary.name}) {\n\t\t") + buildList { + dictionary.allFields().mapTo(this) { field -> + cloneFuncFor(field.type, "self->${field.name}") + } + add("K_FLAG_RELEASABLE") + }.joinTo(builder, separator = ",\n\t\t") + append("\n\t};\n\t") + + // return + append("return result;\n}\n") + } + + private fun printStructFree(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + // free + append(""" + + void ${dictionary.name}_free(${dictionary.name}* self) { + if(!K_OBJECT_IS_RELEASABLE(self->__flags)) + return; + + """.trimIndent()) + dictionary.allFields().forEach { field -> + freeFuncFor( + field.type, + "self->${field.name}" + )?.apply { append("\n\t$this;") } + } + append(""" + + if(!K_OBJECT_IS_ON_STACK(self->__flags)) + free((void*) self); + """.replaceIndent("\t")) + append("\n}\n") + + // forceFree + append("\nvoid _${dictionary.name}_forceFree(${dictionary.name}* self) {") + dictionary.allFields().forEach { field -> + forceFreeFuncFor( + field.type, + "self->${field.name}" + )?.apply { append("\n\t$this;") } + } + append(""" + + if(!K_OBJECT_IS_ON_STACK(self->__flags)) + free((void*) self); + """.replaceIndent("\t")) + append("\n}\n") + } + + private fun printStdLib(builder: StringBuilder) = builder.apply { + printLabel(builder, "stdlib") + + builder.append(""" + + // String + + KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { data, size, length, K_FLAG_RELEASABLE }; + return result; + } + + KString* KString_clone(const KString* of) { + const KInt size = of->size; + void* data = malloc(size); + memcpy(data, of->data, size); + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { (const char*) data, size,of->length, K_FLAG_RELEASABLE }; + return result; + } + + void KString_free(KString* self) { + if(!K_OBJECT_IS_RELEASABLE(self->__flags)) + return; + free((void*) self->data); + if(!K_OBJECT_IS_ON_STACK(self->__flags)) + free((void*) self); + } + + void _KString_forceFree(KString* self) { + self->__flags |= K_FLAG_RELEASABLE; + KString_free(self); + } + + // Arrays + + #define KArrayDef(Name, Type, VarargType) \ + Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + elements, \ + length * sizeof(Name), \ + length, \ + K_FLAG_RELEASABLE \ + }; \ + return result; \ + } \ + \ + Name* _##Name##_of(const int n, ...) { \ + va_list args; \ + va_start(args, n); \ + Type* elements = (Type*)malloc(n * sizeof(Type)); \ + for (int i = 0; i < n; i++) \ + elements[i] = (Type)va_arg(args, VarargType); \ + va_end(args); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + (const Type*) elements, \ + n * sizeof(Name), \ + n, \ + K_FLAG_RELEASABLE \ + }; \ + return result; \ + } + + #define KArrayCloneDef(Name, Type) \ + Name* Name##_clone(const Name* of) { \ + const KInt size = of->size; \ + void** elements = malloc(size); \ + memcpy(elements, (void*) of->elements, size); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name) { \ + (Type*) elements, \ + of->size, \ + of->length, \ + K_FLAG_RELEASABLE \ + }; \ + return result; \ + } \ + \ + void Name##_free(Name* arr) { \ + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) \ + return; \ + free((void*) arr->elements); \ + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) \ + free((void*) arr); \ + } \ + \ + void _##Name##_forceFree(Name* self) { \ + self->__flags |= K_FLAG_RELEASABLE; \ + Name##_free(self); \ + } + + KArray* KArray_clone(const KArray* of, void* (*cloneOp)(void*)) { + const KInt size = of->size; + void** elements = malloc(size); + for (int i = 0; i < of->length; i++) + elements[i] = cloneOp((void*)of->elements[i]); + KArray* result = (KArray*) malloc(sizeof(KArray)); + *result = (KArray) { + (const void**) elements, + of->size, + of->length, + K_FLAG_RELEASABLE + }; + return result; + } + + void KArray_free(const KArray* self, void (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(self->__flags)) + return; + const void** elements = self->elements; + for (int i = 0; i < self->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(self->__flags)) + free((void*) self); + } + + void _KArray_forceFree(KArray* self, void (*freeOp)(void*)) { + self->__flags |= K_FLAG_RELEASABLE; + KArray_free(self, freeOp); + } + + KArrayDef(KCharArray, KChar, int32_t) + KArrayDef(KBooleanArray, KBoolean, int32_t) + KArrayDef(KByteArray, KByte, int32_t) + KArrayDef(KShortArray, KShort, int32_t) + KArrayDef(KIntArray, KInt, int32_t) + KArrayDef(KLongArray, KLong, int64_t) + KArrayDef(KFloatArray, KFloat, double) + KArrayDef(KDoubleArray, KDouble, double) + KArrayDef(KArray, void*, void*) + #undef KArrayDef + + KArrayCloneDef(KCharArray, KChar) + KArrayCloneDef(KBooleanArray, KBoolean) + KArrayCloneDef(KByteArray, KByte) + KArrayCloneDef(KShortArray, KShort) + KArrayCloneDef(KIntArray, KInt) + KArrayCloneDef(KLongArray, KLong) + KArrayCloneDef(KFloatArray, KFloat) + KArrayCloneDef(KDoubleArray, KDouble) + #undef KArrayCloneDef + + """.trimIndent()) + } +} \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CEmscriptenPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CEmscriptenPrinter.kt index 5585484..1c2abda 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CEmscriptenPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CEmscriptenPrinter.kt @@ -1,18 +1,6 @@ package com.huskerdev.nativekt.printers.c -import com.huskerdev.nativekt.utils.allFields -import com.huskerdev.nativekt.utils.globalOperators -import com.huskerdev.nativekt.utils.isCallback -import com.huskerdev.nativekt.utils.isDictionary -import com.huskerdev.nativekt.utils.printLabel -import com.huskerdev.nativekt.utils.toCDefType -import com.huskerdev.nativekt.utils.toCType import com.huskerdev.webidl.resolver.IdlResolver -import com.huskerdev.webidl.resolver.ResolvedIdlCallbackFunction -import com.huskerdev.webidl.resolver.ResolvedIdlDictionary -import com.huskerdev.webidl.resolver.ResolvedIdlOperation -import com.huskerdev.webidl.resolver.ResolvedIdlType -import org.gradle.internal.extensions.stdlib.capitalized import java.io.File class CEmscriptenPrinter( @@ -21,200 +9,32 @@ class CEmscriptenPrinter( ) { init { val builder = StringBuilder() - builder.append(""" - #include "api.h" - #include - #include - #include - - using namespace emscripten; - - #define POINTER_FIELD(Name, SelfType, PointerType) \ - optional_override([](const SelfType& s) -> int { \ - return (int)(intptr_t)s.Name; \ - }), \ - optional_override([](SelfType& s, int v) { \ - s.Name = (PointerType)(intptr_t)v; \ - }) - - #define K_ARRAY_DECL(Name, Type) \ - value_object(#Name) \ - .field("elements", POINTER_FIELD(elements, Name, Type)) \ - .field("size", &Name::size) \ - .field("releasable", &Name::releasable) \ - .field("released", &Name::released); \ - - """.trimIndent()) - - if(idl.callbacks.isNotEmpty()) { - printLabel(builder, "Callbacks") - printCallbacks(builder, idl.callbacks.values) - } - - printLabel(builder, "Functions") - - builder.append(""" - - EMSCRIPTEN_BINDINGS(my_module) { - - value_object("KString") - .field("data", POINTER_FIELD(data, KString, const char*)) - .field("length", &KString::length) - .field("releasable", &KString::releasable) - .field("released", &KString::released); - - K_ARRAY_DECL(KCharArray, KChar*) - K_ARRAY_DECL(KBooleanArray, KBoolean*) - K_ARRAY_DECL(KByteArray, KByte*) - K_ARRAY_DECL(KShortArray, KShort*) - K_ARRAY_DECL(KIntArray, KInt*) - K_ARRAY_DECL(KLongArray, KLong*) - K_ARRAY_DECL(KFloatArray, KFloat*) - K_ARRAY_DECL(KDoubleArray, KDouble*) - K_ARRAY_DECL(KArray, const void**) - - """.trimIndent()) - - idl.dictionaries.values.forEach { printDictionary(builder, it) } - - if(idl.enums.isNotEmpty()) { - idl.enums.values.forEach { enum -> - builder.append("\n\tenum_<${enum.name}>(\"${enum.name}\", enum_value_type::number)\n\t\t") - enum.elements.joinTo(builder, separator = "\n\t\t") { - ".value(\"$it\", ${enum.name.capitalized()}_$it)" + builder.append("#include \"api.h\"\n") + builder.append("#include \n") + + buildList { + addAll(listOf( + "KString", + "KCharArray", + "KBooleanArray", + "KByteArray", + "KShortArray", + "KIntArray", + "KLongArray", + "KFloatArray", + "KDoubleArray" + )) + idl.dictionaries.values.mapTo(this) { it.name } + }.joinTo(builder, separator = "") { + """ + + EMSCRIPTEN_KEEPALIVE void* ${it}_freeAddr() { + return (void*) &${it}_free; } - builder.append(";\n") - } - builder.append("\n") - } - - if(idl.callbacks.isNotEmpty()) { - builder.append("\tfunction(\"_setCallback\", &_setCallback);\n\n") - } - - idl.globalOperators().forEach { - if(it.hasPointers()) - printFunctionOverride(builder, it) - else - printFunctionSimple(builder, it) + + """.trimIndent() } - - builder.append("\n}") target.writeText(builder.toString()) } - private fun printDictionary(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - append("\n\tvalue_object<") - append(dictionary.name) - append(">(\"") - append(dictionary.name) - append("\")\n\t\t") - - dictionary.allFields().joinTo(builder, separator = "\n\t\t") { - val link = if(it.type.isDictionary() || it.type.isCallback()) - "POINTER_FIELD(${it.name}, ${dictionary.name}, ${it.type.toCType()})" - else "&${dictionary.name}::${it.name}" - ".field(\"${it.name}\", $link)" - } - append(";\n") - } - - private fun printCallbacks(builder: StringBuilder, callbacks: Collection) = builder.apply { - val valNames = callbacks.associateWith { "_callback${it.name}" } - val invokeNames = callbacks.associateWith { "_invoke${it.name}" } - - // Fields - append("\n") - callbacks.forEach { - append("val ${valNames[it]} = val::undefined();\n") - } - - // invoke functions - callbacks.forEach { callback -> - append("\nstatic ") - append(callback.type.toCDefType()) - append(" ") - append(invokeNames[callback]) - append("(") - - ( - listOf("${callback.name}* _c") + - callback.args.map { "${it.type.toCDefType()} ${it.name}" } - ).joinTo(builder) - - append(") {\n\t") - - if(callback.type !is ResolvedIdlType.Void) - append("return ") - if(callback.type.isPointer()) - append("(${callback.type.toCDefType()})") - - val args = listOf("(intptr_t)_c") + callback.args.map { - if(it.type.isPointer()) - "(intptr_t)${it.name}" - else it.name - } - - append("${valNames[callback]}(${args.joinToString()})") - - if(callback.type.isPointer()) - append(".as()") - else - append(".as<${callback.type.toCDefType()}>()") - - append(";\n}\n") - } - - // setter - append("\nstatic intptr_t _setCallback(int index, val value) {\n") - append("\tswitch (index) {\n") - callbacks.forEachIndexed { index, callback -> - append("\t\tcase $index:\n") - append("\t\t\t${valNames[callback]} = value;\n") - append("\t\t\treturn (intptr_t)${invokeNames[callback]};\n") - } - append("\t}\n") - append("\treturn 0;\n}\n") - } - - private fun printFunctionSimple(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { - append("\tfunction(\"${function.name}\", &${function.name});\n") - } - - private fun printFunctionOverride(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { - append("\tfunction(\"") - append(function.name) - append("\", optional_override([](") - - function.args.joinTo(builder) { - "${it.type.toCType(callbackAsPtr = true, dictionaryAsPtr = true)} ${it.name}" - } - append(")") - - if(function.type !is ResolvedIdlType.Void) { - append(" -> ") - append(function.type.toCType(callbackAsPtr = true, dictionaryAsPtr = true)) - } - append(" {\n\t\t") - - if(function.type !is ResolvedIdlType.Void) - append("return ") - if(function.type.isPointer()) - append("(intptr_t)") - - val args = function.args.joinToString { - if(it.type.isPointer()) - "(${(it.type as ResolvedIdlType.Default).declaration.name}*)${it.name}" - else it.name - } - - append("${function.name}($args);\n") - append("\t}));\n") - } - - private fun ResolvedIdlType.isPointer() = - isCallback() || isDictionary() - - private fun ResolvedIdlOperation.hasPointers() = - type.isPointer() || args.any { it.type.isPointer() } } \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CExportedPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CExportedPrinter.kt index 2979a09..1b6d493 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CExportedPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CExportedPrinter.kt @@ -14,14 +14,49 @@ class CExportedPrinter( builder.append(""" #include "api.h" - #if defined(_WIN32) || defined(__CYGWIN__) - #define NATIVEKT_EXPORT __declspec(dllexport) - #else - #define NATIVEKT_EXPORT __attribute__((visibility("default"))) + #ifndef NATIVEKT_EXPORT + #if defined(_WIN32) || defined(__CYGWIN__) + #define NATIVEKT_EXPORT __declspec(dllexport) + #else + #define NATIVEKT_EXPORT __attribute__((visibility("default"))) + #endif #endif + + NATIVEKT_EXPORT void ${exportedName("KString_free")}(KString* self) { + KString_free(self); + } + + NATIVEKT_EXPORT void ${exportedName("KArray_free")}(KArray* self, void (*freeOp)(void*)) { + KArray_free(self, freeOp); + } + + #define KArrayFreeDef(Name) \ + NATIVEKT_EXPORT void ${exportedName("##Name##_free")}(Name* self) { \ + Name##_free(self); \ + } + KArrayFreeDef(KCharArray) + KArrayFreeDef(KBooleanArray) + KArrayFreeDef(KByteArray) + KArrayFreeDef(KShortArray) + KArrayFreeDef(KIntArray) + KArrayFreeDef(KLongArray) + KArrayFreeDef(KFloatArray) + KArrayFreeDef(KDoubleArray) + #undef KArrayFreeDef + """.trimIndent()) + idl.dictionaries.values.forEach { + builder.append("\n") + builder.append(""" + NATIVEKT_EXPORT void ${exportedName("${it.name}_free")}(${it.name}* self) { + ${it.name}_free(self); + } + """.trimIndent()) + builder.append("\n") + } + idl.globalOperators().forEach { printFunction(builder, it) if(it.isCriticalCapable() && (it.hasString() || it.hasArray())) @@ -32,70 +67,82 @@ class CExportedPrinter( } private fun printFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { - append("\nNATIVEKT_EXPORT ") - append(function.type.toCDefType()) - append(" EXPORTED_") - append(classPath.replace(".", "_")) - append("_") - append(function.name) - append("(") - function.args.joinTo(this) { - "${it.type.toCDefType()} __arg_${it.name}" + // == Type and name == + append("\nNATIVEKT_EXPORT ${function.type.toCDefType()} ${function.exportedName()}") + + // == Function args == + function.args.joinTo(this, prefix = "(", postfix = ") {\n") { + "${it.type.toCDefType()} _arg_${it.name}" } - append(") {\n") // == Function call == append("\t") if(function.type !is ResolvedIdlType.Void) append("return ") - val args = function.args.joinToString { "__arg_${it.name}" } + val args = function.args.joinToString { "_arg_${it.name}" } append("${function.name}($args);\n}\n") } - private fun printFunctionCritical(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { append("\nNATIVEKT_EXPORT ") - append(function.type.toCDefType(enumAsInt = true)) - append(" EXPORTED_") - append(classPath.replace(".", "_")) - append("_") - append(function.name) - append("_(") - function.args.flatMap { - if(it.type.isString()) - listOf("const char* __arg_${it.name}", "int32_t __length_${it.name}") - else if(it.type.isArray()) { - val type = (it.type as ResolvedIdlType.Default).firstParam { type, _ -> type.toCDefType(enumAsInt = true) } - listOf("$type* __arg_${it.name}", "int32_t __length_${it.name}") - } else - listOf("${it.type.toCDefType(enumAsInt = true)} __arg_${it.name}") - }.joinTo(this) - append(") {\n") + printCriticalNativeFunctionContent( + builder, + name = "${function.exportedName()}_", + function + ) + } - // == Function call == - append("\t") - if(function.type !is ResolvedIdlType.Void) - append("return ") + private fun ResolvedIdlOperation.exportedName() = + exportedName(name) - val args = function.args.joinToString { castToKTypeFromCritical(it.type, it.name) } - val call = "${function.name}($args)" - append(call) - append(";\n}\n") - } + private fun exportedName(name: String) = + "EXPORTED_${classPath.replace(".", "_")}_${name}" } -internal fun castToKTypeFromCritical(type: ResolvedIdlType, name: String): String { - return when(type) { - is ResolvedIdlType.Void -> name - is ResolvedIdlType.Default -> when(val decl = type.declaration) { - is BuiltinIdlDeclaration -> when(decl.kind) { - WebIDLBuiltinKind.LIST -> "(${type.toCDefType()}){__arg_${name}, __length_${name}, false, false}" - WebIDLBuiltinKind.STRING -> "(KString){__arg_${name}, __length_${name}, false, false}" - else -> "__arg_$name" +internal fun printCriticalNativeFunctionContent(builder: StringBuilder, name: String, function: ResolvedIdlOperation) = builder.apply { + // == Type and name == + append(function.type.toCDefType(enumAsInt = true)) + append(" ") + append(name) + + // == Function args == + function.args.flatMap { + when { + it.type.isString() -> listOf("const char* _arr_${it.name}", "int32_t _length_${it.name}, size_t _size_${it.name}") + it.type.isArray() -> { + val type = (it.type as ResolvedIdlType.Default).firstParam { type, _ -> type.toCDefType(enumAsInt = true) } + listOf("$type* _arr_${it.name}", "int32_t _length_${it.name}") + } + else -> listOf("${it.type.toCDefType(enumAsInt = true)} _arg_${it.name}") + } + }.joinTo(this, prefix = "(", postfix = ") {") + + // == Casts == + function.args.forEach { + val name = it.name + when { + it.type.isString() -> append("\n\tKString _arg_$name = (KString) { _arr_$name, _size_$name,_length_$name, K_FLAG_ON_STACK };") + it.type.isArray() -> { + val type = it.type.toCDefType(enumAsInt = true, ptr = false) + append("\n\t$type _arg_$name = ($type) { _arr_$name, sizeof(_arr_$name[0]) * _length_$name, _length_$name, K_FLAG_ON_STACK };") } - else -> "__arg_$name" } - else -> throw UnsupportedOperationException(type.toString()) } + + // == Call args == + val args = function.args.joinToString { + if(it.type.isString() || it.type.isArray()) + "&_arg_${it.name}" + else "_arg_${it.name}" + } + + // == Function call == + append("\n\t") + if(function.type !is ResolvedIdlType.Void) + append("return ") + + val call = "${function.name}($args)" + append(call) + append(";\n}\n") } \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniArenaPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniArenaPrinter.kt deleted file mode 100644 index 737d09d..0000000 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniArenaPrinter.kt +++ /dev/null @@ -1,160 +0,0 @@ -package com.huskerdev.nativekt.printers.c - -import com.huskerdev.nativekt.utils.printLabel -import java.io.File - -class CJniArenaPrinter( - target: File, - callbacks: Boolean -) { - init { - val builder = StringBuilder() - builder.append(""" - #include "jni_utils.h" - - typedef struct Arena Arena; - typedef struct ArenaNode ArenaNode; - - struct ArenaNode { - jobject obj; - void* ptr; - - void (*free)(Arena*, ArenaNode*); - }; - - struct Arena { - JNIEnv* env; - ArenaNode nodes[12]; - uint32_t count; - }; - - - void* Arena__push( - Arena* arena, - jobject obj, - void* ptr, - void (*free)(Arena*, ArenaNode*) - ) { - ArenaNode* node = &arena->nodes[arena->count++]; - node->obj = obj; - node->ptr = ptr; - node->free = free; - return ptr; - } - - bool Arena__contains(Arena* arena, void* ptr) { - for(int i = 0; i < arena->count; i++) { - ArenaNode node = arena->nodes[i]; - if(node.ptr == ptr) - return true; - } - return false; - } - - """.trimIndent()) - - printLabel(builder, "Primitive arrays") - builder.append(""" - - #define KArrayCast(Name, JType) \ - static void ArenaNode__free##Name##Array(Arena* arena, ArenaNode* node){ \ - JNIEnv *env = arena->env; \ - (*env)->Release##Name##ArrayElements(env, node->obj, (JType*)node->ptr, 0); \ - } \ - \ - K##Name##Array Arena__toNative##Name##Array(Arena* arena, JType##Array arr) { \ - JNIEnv *env = arena->env; \ - jsize size = (*env)->GetArrayLength(env, arr); \ - \ - K##Name* elements = (K##Name*) Arena__push(arena, \ - arr, \ - (void*)(*env)->Get##Name##ArrayElements(env, arr, NULL), \ - ArenaNode__free##Name##Array \ - ); \ - return (K##Name##Array) { elements, size, false, false }; \ - } \ - \ - JType##Array Arena__toKotlin##Name##Array(Arena* arena, K##Name##Array arr, bool dealloc) {\ - return JNI_toKotlin##Name##Array( \ - arena->env, arr, \ - dealloc && arr.releasable \ - ); \ - } - - KArrayCast(Char, jchar) - KArrayCast(Boolean, jboolean) - KArrayCast(Byte, jbyte) - KArrayCast(Short, jshort) - KArrayCast(Int, jint) - KArrayCast(Long, jlong) - KArrayCast(Float, jfloat) - KArrayCast(Double, jdouble) - - #undef KArrayCast - - """.trimIndent()) - - printLabel(builder, "String") - builder.append(""" - - void ArenaNode__freeString(Arena* arena, ArenaNode* node){ - JNIEnv *env = arena->env; - (*env)->ReleaseStringUTFChars(env, node->obj, (const char*)node->ptr); - } - - KString Arena__toNativeString(Arena* arena, jstring str) { - JNIEnv *env = arena->env; - jsize length = (*env)->GetStringLength(env, str); - const char* data = (const char*) Arena__push(arena, - str, - (void*)(*env)->GetStringUTFChars(env, str, NULL), - ArenaNode__freeString - ); - return (KString) { data, length, false, false }; - } - - jstring Arena__toKotlinString(Arena* arena, KString str, bool dealloc) { - return JNI_toKotlinString(arena->env, str, dealloc && str.releasable); - } - - """.trimIndent()) - - printLabel(builder, "new/free") - builder.append(""" - - void Arena__free(Arena* arena) { - for(int i = 0; i < arena->count; i++) { - ArenaNode node = arena->nodes[i]; - node.free(arena, &node); - } - } - - void Arena__init(Arena* arena, JNIEnv *env) { - arena->env = env; - arena->count = 0; - } - """.trimIndent()) - - if(callbacks) { - printLabel(builder, "Callback") - builder.append(""" - - void ArenaNode__freeCallback(Arena* arena, ArenaNode* node){ - JNI_freeCallback((JNI_Callback*)node->ptr); - } - - JNI_Callback* Arena__callback(Arena* arena, JNI_Callback* callback) { - Arena__push(arena, - NULL, - (void*)callback, - ArenaNode__freeCallback - ); - return callback; - } - - """.trimIndent()) - } - - target.writeText(builder.toString()) - } -} \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniPrinter.kt index d4f7c88..0c046ab 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniPrinter.kt @@ -16,7 +16,7 @@ class CJniPrinter( init { val builder = StringBuilder() builder.append(""" - #include "jni_arena.h" + #include "jni_utils.h" """.trimIndent()) @@ -84,139 +84,172 @@ class CJniPrinter( buildList { add("JNIEnv *env") - add("jclass __cls") + add("jclass cls") addAll(function.args.map { - "${it.type.toJNIType()} __arg_${it.name}" + "${it.type.toJNIType()} __jvm_${it.name}" }) - }.joinTo(builder, prefix = "(", postfix = ")") - - append(" {\n") - - val useArena = function.args.any { it.type.isString() || it.type.isArray() || it.isDealloc() } - - if(useArena) { - append("\tArena arena;\n") - append("\tArena__init(&arena, env);\n") + }.joinTo(builder, prefix = "(", postfix = ") {") + + val returns = function.type !is ResolvedIdlType.Void + val needReleases = function.args.any { !it.type.isPrimitive() } || function.isDealloc() + + // =========== + // Cast + // =========== + + val argsToCast = function.args.filter { !it.type.isPrimitive() } + val typeMaxLength = argsToCast.maxOfOrNull { it.type.toCDefType().length } ?: 0 + + // write args + argsToCast.joinTo(this, separator = "") { + "\n\t${it.type.toCDefType().padEnd(typeMaxLength)} __native_${it.name} = ${castJavaToJNI( + it.type, + "__jvm_${it.name}", + onStack = true, + flags = "0" + )};" } - append("\t") - if(function.type !is ResolvedIdlType.Void) { - if(useArena) { - append(function.type.toJNIType()) - append(" __result = ") - } else append("return ") - } + // =========== + // Call + // =========== - // == Function call == val args = function.args.joinToString { - castJavaToJNI(it.type, "__arg_${it.name}", it.isDealloc(), useArena, releasable = "false") + if(it.type.isPrimitive()) + "__jvm_${it.name}" + else "__native_${it.name}" } val call = "${function.name}($args)" - append(castJniToJava(function.type, call, function.isDealloc(), function.isDeallocContent(), useArena)) - append(";\n") - if(useArena) { - append("\tArena__free(&arena);\n") - if(function.type !is ResolvedIdlType.Void) - append("\treturn __result;\n") + if(returns) { + if(needReleases) { + if(!function.type.isPrimitive()) { + append("\n\t${function.type.toCDefType()} result_native = $call;") + append("\n\t${function.type.toJNIType()} result_jvm = ${castJniToJava(function.type, "result_native")};") + } else + append("\n\t${function.type.toJNIType()} result_jvm = ${castJniToJava(function.type, call)};") + } else + append("\n\treturn ${castJniToJava(function.type, call)};") + } else append("\n\t$call;") + + // ================== + // Dealloc result + // ================== + + if(function.isDealloc()) { + freeFuncFor( + function.type, + "result_native" + )?.apply { append("\n\t$this;") } } - append("}\n") - } - - private fun printCriticalFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { - append("\nstatic ") - append(function.type.toJNIType(isCritical = true)) - append(" ") - append(function.jniName()) - append("_") - - function.args.flatMap { - val arg = "${it.type.toJNIType(isCritical = true)} __arg_${it.name}" + // ================== + // Release args + // ================== - if(it.type.isArray() || it.type.isString()) - listOf(arg, "jint __length_${it.name}") - else listOf(arg) - - }.joinTo(builder, prefix = "(", postfix = ")") + function.args.forEach { arg -> + when { + arg.type.isString() -> "JNI_releaseStringOnStack(env, __native_${arg.name})" + arg.type.isArray() -> (arg.type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> "JNI_release${declaration.kind.simpleName()}ArrayOnStack(env, __native_${arg.name})" + is ResolvedIdlEnum -> "free((void*)__native_${arg.name}->elements)" + is ResolvedIdlDictionary -> forceFreeFuncFor(arg.type, "__native_${arg.name}") + else -> throw UnsupportedOperationException(arg.type.toString()) + } + } + arg.type.isDictionary() -> forceFreeFuncFor(arg.type, "__native_${arg.name}") + else -> return@forEach + }.apply { append("\n\t$this;") } + } - append(" {\n\t") + // ================== + // Return + // ================== - if(function.type !is ResolvedIdlType.Void) - append("return ") + if(returns && needReleases) + append("\n\treturn result_jvm;") + append("\n}\n") + } - // == Function call == - val args = function.args.joinToString { - castToKTypeFromCritical(it.type, it.name) - } - append("${function.name}($args);\n}\n") + private fun printCriticalFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { + append("\nstatic ") + printCriticalNativeFunctionContent( + builder, + name = "${function.jniName()}_", + function + ) } private fun ResolvedIdlOperation.jniName() = "Java_${classPath.replace(".", "_")}_${this@CJniPrinter.name}_$name" } -internal fun castJniToJava(type: ResolvedIdlType, content: String, dealloc: Boolean, deallocContent: Boolean, useArena: Boolean): String { +internal fun castJniToJava(type: ResolvedIdlType, content: String): String { return when(type) { is ResolvedIdlType.Void -> content is ResolvedIdlType.Default -> when(val decl = type.declaration) { is BuiltinIdlDeclaration -> when(decl.kind) { WebIDLBuiltinKind.STRING -> - if(useArena) "Arena__toKotlinString(&arena, $content, $dealloc)" - else "JNI_toKotlinString(env, $content, $dealloc)" + "JNI_toKotlinString(env, $content)" WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - if (useArena) "Arena__toKotlin${name}Array(&arena, $content, $dealloc)" - else "JNI_toKotlin${name}Array(env, $content, $dealloc)" - } - is ResolvedIdlEnum -> "JNI_toKotlinEnumArray(env, $content, enum${declaration.name}Class, enum${declaration.name}Values, $dealloc)" - is ResolvedIdlDictionary -> "JNI_toKotlinArray(env, $content, (jobject(*)(JNIEnv*, void*, bool))JNI_toKotlinDictionary${declaration.name}, struct${declaration.name}Class, $dealloc, $deallocContent)" + is BuiltinIdlDeclaration -> + "JNI_toKotlin${declaration.kind.simpleName()}Array(env, $content)" + is ResolvedIdlEnum -> "JNI_toKotlinEnumArray(env, $content, enum${declaration.name}Class, enum${declaration.name}Values)" + is ResolvedIdlDictionary -> "JNI_toKotlinArray(env, $content, (jobject(*)(JNIEnv*, void*)) JNI_toKotlinDictionary${declaration.name}, struct${declaration.name}Class)" else -> throw UnsupportedOperationException(type.toString()) } } else -> content } - is ResolvedIdlCallbackFunction -> "JNI_toKotlinCallback(env, (JNI_Callback*)$content, $dealloc)" + is ResolvedIdlCallbackFunction -> "JNI_toKotlinCallback(env, (_AbstractCallback*)$content)" is ResolvedIdlEnum -> "JNI_toKotlinEnum(env, $content, enum${decl.name}Class, enum${decl.name}Values)" - is ResolvedIdlDictionary -> "JNI_toKotlinDictionary${decl.name}(env, $content, $dealloc)" + is ResolvedIdlDictionary -> "JNI_toKotlinDictionary${decl.name}(env, $content)" else -> throw UnsupportedOperationException(type.toString()) } else -> throw UnsupportedOperationException(type.toString()) } } -internal fun castJavaToJNI(type: ResolvedIdlType, content: String, dealloc: Boolean, useArena: Boolean, releasable: String): String { - return when(type) { - is ResolvedIdlType.Default -> when(val decl = type.declaration) { - is BuiltinIdlDeclaration -> when(decl.kind) { - WebIDLBuiltinKind.STRING -> - if(useArena) "Arena__toNativeString(&arena, $content)" - else "JNI_toNativeString(env, $content, $releasable)" - WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> - when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - if (useArena) "Arena__toNative${name}Array(&arena, $content)" - else "JNI_toNative${name}Array(env, $content, $releasable)" - } - is ResolvedIdlEnum -> "JNI_toNativeEnumArray(env, $content, $releasable)" - is ResolvedIdlDictionary -> "JNI_toNativeArray(env, $content, (void*(*)(JNIEnv*, jobject, bool))JNI_toNativeDictionary${declaration.name}, $releasable)" - else -> throw UnsupportedOperationException(type.toString()) +internal fun castJavaToJNI( + type: ResolvedIdlType, + content: String, + onStack: Boolean, + flags: String +) = when(type) { + is ResolvedIdlType.Default -> when(val decl = type.declaration) { + is BuiltinIdlDeclaration -> when(decl.kind) { + WebIDLBuiltinKind.STRING -> + if(onStack) "JNI_toNativeStringOnStack(env, $content, alloca(JNI_StringStackSize))" + else "JNI_toNativeString(env, $content, /* flags */ $flags)" + WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> { + val name = declaration.kind.simpleName() + if(onStack) "JNI_toNative${name}ArrayOnStack(env, $content, alloca(JNI_ArrayStackSize))" + else "JNI_toNative${name}Array(env, $content, /* flags */ $flags)" + } + is ResolvedIdlEnum -> { + if(onStack) "JNI_toNativeEnumArrayOnStack(env, $content, alloca(JNI_ArrayStackSize))" + else "JNI_toNativeEnumArray(env, $content, /* flags */ $flags)" + } + is ResolvedIdlDictionary -> { + if(onStack) "JNI_toNativeArrayOnStack(env, $content, (void*(*)(JNIEnv*, jobject, char)) JNI_toNativeDictionary${declaration.name}, alloca(JNI_ArrayStackSize))" + else "JNI_toNativeArray(env, $content, (void*(*)(JNIEnv*, jobject, char)) JNI_toNativeDictionary${declaration.name}, /* flags */ $flags)" } + else -> throw UnsupportedOperationException(type.toString()) } - else -> content - } - is ResolvedIdlCallbackFunction -> { - val call = "JNI_toNativeCallback(env, $content, (void(*)())JNI_CALLBACK_INVOKE_${decl.name})" - if (dealloc) "(${decl.name}*)Arena__callback(&arena, $call)" - else "(${decl.name}*)$call" } - is ResolvedIdlEnum -> "JNI_toNativeEnum(env, $content)" - is ResolvedIdlDictionary -> "JNI_toNativeDictionary${decl.name}(env, $content, $releasable)" - else -> throw UnsupportedOperationException(type.toString()) + else -> content + } + is ResolvedIdlCallbackFunction -> { + if (onStack) "(${decl.name}*) JNI_toNativeCallbackOnStack(env, $content, (void(*)())JNI_CALLBACK_INVOKE_${decl.name}, alloca(_AbstractCallbackSize))" + else "(${decl.name}*) JNI_toNativeCallback(env, $content, (void(*)())JNI_CALLBACK_INVOKE_${decl.name}, /* flags */ $flags)" } + is ResolvedIdlEnum -> "JNI_toNativeEnum(env, $content)" + is ResolvedIdlDictionary -> "JNI_toNativeDictionary${decl.name}(env, $content, /* flags */ $flags)" else -> throw UnsupportedOperationException(type.toString()) } + else -> throw UnsupportedOperationException(type.toString()) } \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniUtilsPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniUtilsPrinter.kt index 2242891..e945199 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniUtilsPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniUtilsPrinter.kt @@ -1,8 +1,6 @@ package com.huskerdev.nativekt.printers.c import com.huskerdev.nativekt.utils.allFields -import com.huskerdev.nativekt.utils.isDealloc -import com.huskerdev.nativekt.utils.isDeallocContent import com.huskerdev.nativekt.utils.printLabel import com.huskerdev.nativekt.utils.toCDefType import com.huskerdev.nativekt.utils.toJavaDesc @@ -31,34 +29,73 @@ class CJniUtilsPrinter( JavaVM *jvm; jclass jniClass; + jmethodID objectEquals, objectHashCode; + """.trimIndent()) printLabel(builder, "String") builder.append(""" jclass stringClass; - jmethodID stringConstructor; + jmethodID stringConstructor, stringGetBytes; + jstring stringUTF8Const; typedef struct KString KString; - jstring JNI_toKotlinString(JNIEnv *env, KString str, bool dealloc) { - int32_t length = str.length; - - jbyteArray bytes = (*env)->NewByteArray(env, length); - (*env)->SetByteArrayRegion(env, bytes, 0, length, (jbyte*)str.data); + const size_t JNI_StringStackSize = sizeof(KString) + sizeof(intptr_t) * 2; + + jstring JNI_toKotlinString(JNIEnv *env, KString* str) { + if(K_OBJECT_IS_ON_STACK(str->__flags)) + return (jstring)((intptr_t*)((char*)str + sizeof(KString)))[0]; + + int32_t size = str->size; + + jbyteArray bytes = (*env)->NewByteArray(env, size); + (*env)->SetByteArrayRegion(env, bytes, 0, size, (jbyte*)str->data); - jstring result = (jstring)(*env)->NewObject(env, stringClass, stringConstructor, bytes); + jstring result = (jstring)(*env)->NewObject(env, stringClass, stringConstructor, bytes, stringUTF8Const); (*env)->DeleteLocalRef(env, bytes); - - if(dealloc) free((void*)str.data); return result; } - KString JNI_toNativeString(JNIEnv *env, jstring obj, bool releasable) { + KString* JNI_toNativeStringOnStack(JNIEnv *env, jstring obj, void* mem) { + jbyteArray bytes = (jbyteArray) (*env)->CallObjectMethod(env, obj, stringGetBytes, stringUTF8Const); + ((size_t*)((char*)mem + sizeof(KString)))[0] = (size_t) obj; + ((size_t*)((char*)mem + sizeof(KString)))[1] = (size_t) bytes; + KString* result = (KString*)mem; + *result = (KString) { + (const char*)(*env)->GetByteArrayElements(env, bytes, JNI_FALSE), + (*env)->GetArrayLength(env, bytes), + (*env)->GetStringLength(env, obj), + K_FLAG_ON_STACK + }; + return result; + } + + void JNI_releaseStringOnStack(JNIEnv *env, KString* str) { + jbyteArray bytes = (jbyteArray)((size_t*)((char*)str + sizeof(KString)))[1]; + (*env)->ReleaseByteArrayElements(env, bytes, (jbyte*) str->data, JNI_ABORT); + } + + KString* JNI_toNativeString(JNIEnv *env, jstring obj, char flags) { + jbyteArray bytes = (jbyteArray) (*env)->CallObjectMethod(env, obj, stringGetBytes, stringUTF8Const); jsize length = (*env)->GetStringLength(env, obj); - const char* temp = (*env)->GetStringUTFChars(env, obj, NULL); - const char* copy = strdup(temp); - (*env)->ReleaseStringUTFChars(env, obj, temp); - return (KString) { copy, length, releasable, false }; + jsize size = (*env)->GetArrayLength(env, bytes); + + jbyte* str = (*env)->GetByteArrayElements(env, bytes, JNI_FALSE); + void* strCopy = malloc(size); + memcpy(strCopy, (void*)str, size); + + (*env)->ReleaseByteArrayElements(env, bytes, str, JNI_ABORT); + (*env)->DeleteLocalRef(env, bytes); + + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { + (const char*) strCopy, + size, + length, + flags + }; + return result; } """.trimIndent()) @@ -66,21 +103,52 @@ class CJniUtilsPrinter( printLabel(builder, "Primitive Arrays") builder.append(""" - #define KArrayCast(Name, JType) \ - K##Name##Array JNI_toNative##Name##Array(JNIEnv *env, JType##Array arr, bool releasable) {\ - jsize size = (*env)->GetArrayLength(env, arr); \ - JType* tmp = (*env)->Get##Name##ArrayElements(env, arr, NULL); \ - const JType* copy = (JType*)malloc(size * sizeof(JType)); \ - memcpy((void*)copy, (void*)tmp, size * sizeof(JType)); \ - (*env)->Release##Name##ArrayElements(env, arr, tmp, JNI_ABORT); \ - return (K##Name##Array) { (K##Name*)copy, size, releasable, false }; \ - } \ - \ - JType##Array JNI_toKotlin##Name##Array(JNIEnv *env, K##Name##Array arr, bool dealloc) { \ - JType##Array result = (*env)->New##Name##Array(env, arr.size); \ - (*env)->Set##Name##ArrayRegion(env, result, 0, arr.size, (JType*)arr.elements); \ - if(dealloc) free((void*)arr.elements); \ - return result; \ + const size_t JNI_ArrayStackSize = sizeof(KArray) + sizeof(size_t); + + #define KArrayCast(Name, JType) \ + K##Name##Array* JNI_toNative##Name##Array(JNIEnv *env, JType##Array arr, char flags) { \ + KInt length = (*env)->GetArrayLength(env, arr); \ + size_t size = length * sizeof(JType); \ + \ + JType* elements = (*env)->Get##Name##ArrayElements(env, arr, JNI_FALSE); \ + void* elementsCopy = malloc(size); \ + memcpy(elementsCopy, (void*)elements, size); \ + (*env)->Release##Name##ArrayElements(env, arr, elements, JNI_ABORT); \ + \ + K##Name##Array* result = (K##Name##Array*) malloc(sizeof(K##Name##Array)); \ + *result = (K##Name##Array) { \ + (K##Name*)elementsCopy, \ + size, \ + length, \ + flags \ + }; \ + return result; \ + } \ + \ + K##Name##Array* JNI_toNative##Name##ArrayOnStack(JNIEnv *env, JType##Array arr, void* mem) { \ + ((size_t*)((char*)mem + sizeof(K##Name##Array)))[0] = (size_t) arr; \ + KInt length = (*env)->GetArrayLength(env, arr); \ + K##Name##Array* result = (K##Name##Array*)mem; \ + *result = (K##Name##Array) { \ + (K##Name*)(*env)->Get##Name##ArrayElements(env, arr, JNI_FALSE), \ + length * sizeof(JType), \ + length, \ + K_FLAG_ON_STACK \ + }; \ + return result; \ + } \ + \ + void JNI_release##Name##ArrayOnStack(JNIEnv *env, K##Name##Array* arr) { \ + JType##Array elements = (JType##Array)*((size_t*)((char*)arr + sizeof(K##Name##Array))); \ + (*env)->Release##Name##ArrayElements(env, elements, (JType*)arr->elements, JNI_ABORT); \ + } \ + \ + JType##Array JNI_toKotlin##Name##Array(JNIEnv *env, K##Name##Array* arr) { \ + if(K_OBJECT_IS_ON_STACK(arr->__flags)) \ + return (JType##Array)*((size_t*)((char*)arr + sizeof(K##Name##Array))); \ + JType##Array result = (*env)->New##Name##Array(env, arr->length); \ + (*env)->Set##Name##ArrayRegion(env, result, 0, arr->length, (JType*)arr->elements); \ + return result; \ } KArrayCast(Char, jchar) @@ -99,33 +167,70 @@ class CJniUtilsPrinter( printLabel(builder, "Object array") builder.append(""" - jobjectArray JNI_toKotlinArray( + KArray* JNI_toNativeArray( JNIEnv *env, - KArray src, - jobject (*converter)(JNIEnv*, void*, bool), - jclass clazz, - bool dealloc, - bool deallocContent + jobjectArray src, + void* (*converter)(JNIEnv*, jobject, char), + char flags ) { - jobjectArray result = (*env)->NewObjectArray(env, src.size, clazz, NULL); - void** elements = (void**)src.elements; - for(int i = 0; i < src.size; i++) - (*env)->SetObjectArrayElement(env, result, i, converter(env, elements[i], deallocContent)); - if(dealloc) free((void*) src.elements); + int length = (*env)->GetArrayLength(env, src); + void** elements = malloc(length * sizeof(void*)); + for(int i = 0; i < length; i++) { + jobject obj = (*env)->GetObjectArrayElement(env, src, i); + elements[i] = converter(env, obj, flags); + (*env)->DeleteLocalRef(env, obj); + } + + KArray* result = (KArray*) malloc(sizeof(KArray)); + *result = (KArray) { + (const void**) elements, + length * sizeof(void*), + length, + flags, + }; return result; } - - KArray JNI_toNativeArray( - JNIEnv *env, + + KArray* JNI_toNativeArrayOnStack( + JNIEnv *env, jobjectArray src, - void* (*converter)(JNIEnv*, jobject, bool), - bool releasable + void* (*converter)(JNIEnv*, jobject, char), + void* mem ) { - int length = (*env)->GetArrayLength(env, src); + ((size_t*)((char*)mem + sizeof(KArray)))[0] = (size_t) src; + KInt length = (*env)->GetArrayLength(env, src); + void** elements = malloc(length * sizeof(void*)); - for(int i = 0; i < length; i++) - elements[i] = converter(env, (*env)->GetObjectArrayElement(env, src, i), releasable); - return (KArray){ (const void**) elements, length, releasable, false }; + for(int i = 0; i < length; i++) { + jobject obj = (*env)->GetObjectArrayElement(env, src, i); + elements[i] = converter(env, obj, 0); + (*env)->DeleteLocalRef(env, obj); + } + + KArray* result = (KArray*)mem; + *result = (KArray) { + (const void**) elements, + length * sizeof(void*), + length, + K_FLAG_ON_STACK + }; + return result; + } + + jobjectArray JNI_toKotlinArray( + JNIEnv *env, + KArray* src, + jobject (*converter)(JNIEnv*, void*), + jclass clazz + ) { + if(K_OBJECT_IS_ON_STACK(src->__flags)) + return (jobjectArray)*((size_t*)((char*)src + sizeof(KArray))); + + jobjectArray result = (*env)->NewObjectArray(env, src->length, clazz, NULL); + void** elements = (void**)src->elements; + for(int i = 0; i < src->length; i++) + (*env)->SetObjectArrayElement(env, result, i, converter(env, elements[i])); + return result; } """.trimIndent()) @@ -142,42 +247,84 @@ class CJniUtilsPrinter( } builder.append(""" - + KInt JNI_toNativeEnum(JNIEnv* env, jobject of) { - return (*env)->CallIntMethod(env, of, enumOrdinal); + return (*env)->CallIntMethod(env, of, enumOrdinal); } - jobject JNI_toKotlinEnum(JNIEnv* env, KInt of, jclass clazz, jmethodID valuesMethod) { - jobjectArray values = (jobjectArray) (*env)->CallStaticObjectMethod(env, clazz, valuesMethod); - jobject result = (*env)->GetObjectArrayElement(env, values, of); - (*env)->DeleteLocalRef(env, values); - return result; + jobject JNI_toKotlinEnum( + JNIEnv* env, + KInt of, + jclass clazz, + jmethodID valuesMethod + ) { + jobjectArray values = (jobjectArray) (*env)->CallStaticObjectMethod(env, clazz, valuesMethod); + jobject result = (*env)->GetObjectArrayElement(env, values, of); + (*env)->DeleteLocalRef(env, values); + return result; } - - KIntArray JNI_toNativeEnumArray( - JNIEnv *env, - jobjectArray src, - bool releasable + + KIntArray* JNI_toNativeEnumArray( + JNIEnv *env, + jobjectArray src, + char flags + ) { + int length = (*env)->GetArrayLength(env, src); + KInt* elements = malloc(length * sizeof(KInt)); + for(int i = 0; i < length; i++) { + jobject obj = (*env)->GetObjectArrayElement(env, src, i); + elements[i] = JNI_toNativeEnum(env, (*env)->GetObjectArrayElement(env, src, i)); + (*env)->DeleteLocalRef(env, obj); + } + + KIntArray* result = (KIntArray*) malloc(sizeof(KIntArray)); + *result = (KIntArray) { + (const KInt*) elements, + length * sizeof(KInt), + length, + flags + }; + return result; + } + + KIntArray* JNI_toNativeEnumArrayOnStack( + JNIEnv *env, + jobjectArray src, + void* mem ) { - int length = (*env)->GetArrayLength(env, src); - KInt* elements = malloc(length * sizeof(KInt)); - for(int i = 0; i < length; i++) - elements[i] = JNI_toNativeEnum(env, (*env)->GetObjectArrayElement(env, src, i)); - return (KIntArray){ elements, length, releasable, false }; + ((size_t*)((char*)mem + sizeof(KArray)))[0] = (size_t) src; + + int length = (*env)->GetArrayLength(env, src); + KInt* elements = malloc(length * sizeof(KInt)); + for(int i = 0; i < length; i++) { + jobject obj = (*env)->GetObjectArrayElement(env, src, i); + elements[i] = JNI_toNativeEnum(env, (*env)->GetObjectArrayElement(env, src, i)); + (*env)->DeleteLocalRef(env, obj); + } + + KIntArray* result = (KIntArray*)mem; + *result = (KIntArray) { + (const KInt*) elements, + length * sizeof(KInt), + length, + K_FLAG_ON_STACK + }; + return result; } jobjectArray JNI_toKotlinEnumArray( JNIEnv *env, - KIntArray src, + KIntArray* src, jclass clazz, - jmethodID valuesMethod, - bool dealloc + jmethodID valuesMethod ) { - jobjectArray result = (*env)->NewObjectArray(env, src.size, clazz, NULL); - const KInt* elements = src.elements; - for(int i = 0; i < src.size; i++) + if(K_OBJECT_IS_ON_STACK(src->__flags)) + return (jobjectArray)*((size_t*)((char*)src + sizeof(KArray))); + + jobjectArray result = (*env)->NewObjectArray(env, src->length, clazz, NULL); + const KInt* elements = src->elements; + for(int i = 0; i < src->length; i++) (*env)->SetObjectArrayElement(env, result, i, JNI_toKotlinEnum(env, elements[i], clazz, valuesMethod)); - if(dealloc) free((void*) src.elements); return result; } @@ -188,51 +335,111 @@ class CJniUtilsPrinter( printLabel(builder, "Callback casts") builder.append(""" - typedef struct JNI_Callback { - void *m; - void (*invoke)(); - void (*free)(struct JNI_Callback*); - } JNI_Callback; + const size_t _AbstractCallbackSize = sizeof(_AbstractCallback) + sizeof(size_t)*2; static jint JVM_attach(JNIEnv **env) { jint status = (*jvm)->GetEnv(jvm, (void**)env, JNI_VERSION_1_6); if (status == JNI_EDETACHED) - (*jvm)->AttachCurrentThread(jvm, (${if(isAndroid) "JNIEnv**" else "void**"})env, NULL); + (*jvm)->AttachCurrentThread(jvm, ${if(!isAndroid) "(void**) " else "" }env, NULL); return status; } - + static inline void JVM_detach(jint status) { if (status == JNI_EDETACHED) (*jvm)->DetachCurrentThread(jvm); } - void JNI_freeCallback(JNI_Callback* callback) { + static inline KBoolean JNI_CALLBACK_equals(_AbstractCallback* ref, _AbstractCallback* with) { + jobject obj = (jobject)((size_t*)((char*)ref + sizeof(_AbstractCallback)))[0]; + jobject obj2 = (jobject)((size_t*)((char*)with + sizeof(_AbstractCallback)))[0]; + JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(_AbstractCallback)))[1]; + + jint status = env == NULL ? JVM_attach(&env) : JNI_OK; + KBoolean result = (*env)->CallBooleanMethod(env, obj, objectEquals, obj2); + if(status == JNI_EDETACHED) JVM_detach(status); + return result; + } + + static inline KInt JNI_CALLBACK_hashCode(_AbstractCallback* ref) { + jobject obj = (jobject)((size_t*)((char*)ref + sizeof(_AbstractCallback)))[0]; + JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(_AbstractCallback)))[1]; + + jint status = env == NULL ? JVM_attach(&env) : JNI_OK; + KInt result = (*env)->CallIntMethod(env, obj, objectHashCode); + if(status == JNI_EDETACHED) JVM_detach(status); + return result; + } + + static void JNI_CALLBACK_free(_AbstractCallback* callback) { + if (!K_OBJECT_IS_RELEASABLE(callback->__flags) || K_OBJECT_IS_ON_STACK(callback->__flags)) + return; + JNIEnv *env; jint status = JVM_attach(&env); - - (*env)->DeleteGlobalRef(env, (jobject)callback->m); + + jobject obj = (jobject)((size_t*)((char*)callback + sizeof(_AbstractCallback)))[0]; + (*env)->DeleteGlobalRef(env, obj); free((void*)callback); - + JVM_detach(status); } - jobject JNI_toKotlinCallback(JNIEnv *env, JNI_Callback* callback, bool dealloc) { - jobject result = (jobject)callback->m; - if(dealloc) JNI_freeCallback(callback); - return result; + static _AbstractCallback* JNI_CALLBACK_clone(_AbstractCallback* ref) { + jobject obj = (jobject)((size_t*)((char*)ref + sizeof(_AbstractCallback)))[0]; + JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(_AbstractCallback)))[1]; + jint status = env == NULL ? JVM_attach(&env) : JNI_OK; + + _AbstractCallback* callback = (_AbstractCallback*) malloc(_AbstractCallbackSize); + memcpy(callback, ref, sizeof(_AbstractCallback)); + callback->__flags = K_FLAG_RELEASABLE; + + ((size_t*)((char*)callback + sizeof(_AbstractCallback)))[0] = (size_t)(*env)->NewGlobalRef(env, obj); + ((size_t*)((char*)callback + sizeof(_AbstractCallback)))[1] = (size_t) NULL; + + if(status == JNI_EDETACHED) JVM_detach(status); + return callback; + } + + jobject JNI_toKotlinCallback(JNIEnv *env, _AbstractCallback* callback) { + return (jobject)((size_t*)((char*)callback + sizeof(_AbstractCallback)))[0]; } - JNI_Callback* JNI_toNativeCallback(JNIEnv *env, jobject obj, void (*invoke)()) { - JNI_Callback* callback = (JNI_Callback*)malloc(sizeof(JNI_Callback)); - callback->invoke = invoke; - callback->free = JNI_freeCallback; - callback->m = (void*)(*env)->NewGlobalRef(env, obj); - return callback; + _AbstractCallback* JNI_toNativeCallbackOnStack(JNIEnv *env, jobject obj, void (*invoke)(), void* mem) { + ((size_t*)((char*)mem + sizeof(_AbstractCallback)))[0] = (size_t) obj; + ((size_t*)((char*)mem + sizeof(_AbstractCallback)))[1] = (size_t) env; + _AbstractCallback* callback = (_AbstractCallback*) mem; + *callback = (_AbstractCallback) { + K_FLAG_ON_STACK, + invoke, + JNI_CALLBACK_clone, + JNI_CALLBACK_equals, + JNI_CALLBACK_hashCode, + JNI_CALLBACK_free + }; + return callback; + } + + _AbstractCallback* JNI_toNativeCallback(JNIEnv *env, jobject obj, void (*invoke)(), char flags) { + void* mem = malloc(_AbstractCallbackSize); + ((size_t*)((char*)mem + sizeof(_AbstractCallback)))[0] = (size_t) (*env)->NewGlobalRef(env, obj); + ((size_t*)((char*)mem + sizeof(_AbstractCallback)))[1] = (size_t) NULL; + _AbstractCallback* callback = (_AbstractCallback*) mem; + *callback = (_AbstractCallback) { + flags, + invoke, + JNI_CALLBACK_clone, + JNI_CALLBACK_equals, + JNI_CALLBACK_hashCode, + JNI_CALLBACK_free + }; + return callback; } """.trimIndent()) - idl.callbacks.values.forEach { printCallbackInvokeDef(builder, it) } + idl.callbacks.values.forEach { + printCallbackInvokeDef(builder, it) + } } if(idl.dictionaries.isNotEmpty()) { @@ -290,40 +497,27 @@ class CJniUtilsPrinter( append("\n// ${struct.name}\n") // to JVM - append("\n") - append("jobject JNI_toKotlinDictionary") - append(struct.name) - append("(JNIEnv *env, ") - append(struct.name) - append("* src, bool dealloc) {\n\t") - append("jobject result = (*env)->CallObjectMethod(env, ") - append("struct") - append(struct.name) - append("Companion, struct") - append(struct.name) - append("Constructor, \n\t\t") + append("\njobject JNI_toKotlinDictionary${struct.name}(JNIEnv *env, ${struct.name}* src) {\n\t") + append("return (*env)->CallObjectMethod(env, struct${struct.name}Companion, struct${struct.name}Constructor, \n\t\t") + struct.allFields().joinTo(builder, separator = ",\n\t\t") { - castJniToJava(it.type, "src->${it.name}", dealloc = false, deallocContent = false, useArena = false) + castJniToJava(it.type, "src->${it.name}") } - append("\n\t);\n\t") - append("if (dealloc) free((void*) src);\n\t") - append("return result;\n") - append("}\n") + append("\n\t);\n}\n") // to Native - append("\n") - append(struct.name) - append("* JNI_toNativeDictionary") - append(struct.name) - append("(JNIEnv *env, jobject src, bool releasable) {\n\t") - append(struct.name) - append("* result = malloc(sizeof(").append(struct.name).append("));\n\t") - append("*result = (").append(struct.name).append(") {\n\t\t") - struct.allFields().joinTo(builder, separator = ",\n\t\t") { field -> - val fieldVariable = "struct${struct.name}Field${field.name.capitalized()}" - val getter = field.type.toMethodCall() - castJavaToJNI(field.type, "(*env)->$getter(env, src, $fieldVariable)", dealloc = false, useArena = false, releasable = "releasable") - } + append("\n${struct.name}* JNI_toNativeDictionary${struct.name}(JNIEnv *env, jobject src, char flags) {\n\t") + append("${struct.name}* result = malloc(sizeof(${struct.name}));\n\t") + append("*result = (${struct.name}) {\n\t\t") + + buildList { + struct.allFields().mapTo(this) { field -> + val fieldVariable = "struct${struct.name}Field${field.name.capitalized()}" + val getter = field.type.toMethodCall() + castJavaToJNI(field.type, "(*env)->$getter(env, src, $fieldVariable)", onStack = false, flags = "flags") + } + add("flags") + }.joinTo(builder, separator = ",\n\t\t") append("\n\t};") append("\n\treturn result;\n}\n") } @@ -337,39 +531,42 @@ class CJniUtilsPrinter( } private fun printCallbackInvoke(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - val args = listOf("${callback.name}* _callback") + - callback.args.map { "${it.type.toCDefType()} ${it.name}" } + val args = buildList { + add("${callback.name}* callback") + callback.args.mapTo(this) { + "${it.type.toCDefType()} __${it.name}" + } + }.joinToString() - var jvmArgs = callback.args.joinToString { - castJniToJava(it.type, it.name, it.isDealloc(), it.isDeallocContent(), false) - } - if(jvmArgs.isNotEmpty()) - jvmArgs = ", $jvmArgs" + val jvmArgs = buildList { + add("callback${callback.name}Invoke") + callback.args.mapTo(this) { + castJniToJava(it.type, "__${it.name}") + } + }.joinToString() append(""" - ${callback.type.toCDefType()} JNI_CALLBACK_INVOKE_${callback.name}(${args.joinToString()}) { - JNIEnv *env; - jint __status = JVM_attach(&env); + ${callback.type.toCDefType()} JNI_CALLBACK_INVOKE_${callback.name}(${args}) { + jobject obj = (jobject)((size_t*)((char*)callback + sizeof(_AbstractCallback)))[0]; + JNIEnv *env = (JNIEnv*)((size_t*)((char*)callback + sizeof(_AbstractCallback)))[1]; + jint status = env == NULL ? JVM_attach(&env) : JNI_OK; """.trimIndent()) - val funcName = callback.type.toMethodCall() - val call = "(*env)->$funcName(env, (jobject)_callback->m, callback${callback.name}Invoke$jvmArgs)" + val call = "(*env)->${callback.type.toMethodCall()}(env, obj, $jvmArgs)" if(callback.type !is ResolvedIdlType.Void) { append(callback.type.toCDefType()) - append(" __result = ") - append(castJavaToJNI(callback.type, call, dealloc = false, useArena = false, releasable = "true")) + append(" result = ") + append(castJavaToJNI(callback.type, call, onStack = false, flags = "K_FLAG_RELEASABLE")) } else append(call) - append(";\n") - - append("\tJVM_detach(__status);\n") + append(";\n\tif(status == JNI_EDETACHED) JVM_detach(status);\n") if(callback.type !is ResolvedIdlType.Void) - append("\treturn __result;\n") + append("\treturn result;\n") append("}\n") } @@ -383,9 +580,16 @@ class CJniUtilsPrinter( jniClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "${(classPath.split(".") + name).joinToString(separator = "/")}")); (*env)->RegisterNatives(env, jniClass, methods, count); + // Object + jclass objectClass = (*env)->FindClass(env, "java/lang/Object"); + objectEquals = (*env)->GetMethodID(env, objectClass, "equals", "(Ljava/lang/Object;)Z"); + objectHashCode = (*env)->GetMethodID(env, objectClass, "hashCode", "()I"); + // String stringClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/lang/String")); - stringConstructor = (*env)->GetMethodID(env, stringClass, "", "([B)V"); + stringConstructor = (*env)->GetMethodID(env, stringClass, "", "([BLjava/lang/String;)V"); + stringGetBytes = (*env)->GetMethodID(env, stringClass, "getBytes", "(Ljava/lang/String;)[B"); + stringUTF8Const = (*env)->NewGlobalRef(env, (*env)->NewStringUTF(env, "UTF-8")); """.replaceIndent()) diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinAndroidPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinAndroidPrinter.kt index 8a70242..0c60080 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinAndroidPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinAndroidPrinter.kt @@ -55,6 +55,7 @@ class KotlinAndroidPrinter( _isLib${moduleName.capitalized()}Loaded = true System.loadLibrary("$moduleName") + TestJNI.JNILoad(supportsCritical) } ${actual}fun ${asyncFunctionName(moduleName)}(onReady: () -> Unit) { diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinCommonPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinCommonPrinter.kt index 263fce2..9a2c064 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinCommonPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinCommonPrinter.kt @@ -158,7 +158,7 @@ class KotlinCommonPrinter( append(dictionary.name) if(dictionary.allFields().any { it.type.isArray() }) { - append("{\n") + append(" {\n") // equals append(""" override fun equals(other: Any?): Boolean { diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinJsPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinJsPrinter.kt index 16453e5..03c865d 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinJsPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinJsPrinter.kt @@ -47,7 +47,7 @@ class KotlinJsPrinter( """.trimIndent()) val initCallbacks = if(idl.callbacks.isNotEmpty()) - "initCallbacks()" else "" + "_initCallbacks()" else "" builder.append(""" @@ -93,39 +93,17 @@ class KotlinJsPrinter( // Callbacks loading if(idl.callbacks.isNotEmpty()) { - builder.append("\nprivate var _freeCallback: Int = 0\n") - idl.callbacks.values.joinTo(builder, separator = "\n") { - "private var _invoke${it.name} = 0" - } + printLabel(builder, "Callbacks") + printCallbacks(builder) + } + if(idl.dictionaries.isNotEmpty()) { + printLabel(builder, "Dictionaries") + idl.dictionaries.values.forEach { printDictionaryLayout(builder, it) } builder.append("\n") - idl.callbacks.values.forEachIndexed { index, callback -> - builder.append("\nprivate fun _callback") - builder.append(index) - builder.append("Js(block: (") - (arrayListOf("Int") + callback.args.map { toSimpleJsType(it.type) }) - .joinTo(builder) - builder.append(") -> ") - builder.append(toSimpleJsType(callback.type)) - builder.append("): JsAny = js(\"block\")") - } - - builder.append(""" - - - private fun initCallbacks() { - _freeCallback = createCallbackFreeFunction(_module) - - """.trimIndent()) - idl.callbacks.values.forEachIndexed { index, callback -> - printCallbackInvoke(builder, callback, index) - } - builder.append("}\n") - - // wrap - idl.callbacks.values.forEach { callback -> printCallbackCast(builder, callback) } + idl.dictionaries.values.forEach { printDictionaryCasts(builder, it) } } - idl.dictionaries.values.forEach { printDictionaryCasts(builder, it) } + idl.globalOperators().forEach { printFunction(builder, it) } printTypes(builder) @@ -134,154 +112,241 @@ class KotlinJsPrinter( target.writeText(builder.toString()) } - private fun printDictionaryCasts(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - val structLayout = CStructLayout(dictionary, true) - - val heaps = StringBuilder() - if(dictionary.allFields().any { it.type.isLong() }) - heaps.append("val HEAP64 = BigInt64Array(_module.HEAP8.buffer, 0, _module.HEAP8.buffer.byteLength / 8)\n\t") - if(dictionary.allFields().any { it.type.isDouble() }) - heaps.append("val HEAPF64 = _module.HEAPF64\n\t") - if(dictionary.allFields().any { it.type.isInt() || it.type.isDictionary() || it.type.isEnum() }) - heaps.append("val HEAP32 = _module.HEAP32\n\t") - if(dictionary.allFields().any { it.type.isFloat() }) - heaps.append("val HEAPF32 = _module.HEAPF32\n\t") - if(dictionary.allFields().any { it.type.getAlignment() == 2 }) - heaps.append("val HEAP16 = _module.HEAP16\n\t") - if(dictionary.allFields().any { it.type.getAlignment() == 1 }) - heaps.append("val HEAP8 = _module.HEAP8\n\t") + private fun printCallbacks(builder: StringBuilder) = builder.apply { + // Variables + append("\nprivate var _callbackClone: Int = 0") + append("\nprivate var _callbackEquals: Int = 0") + append("\nprivate var _callbackHashCode: Int = 0") + append("\nprivate var _callbackFree: Int = 0\n") - // to native - append("\nfun toNativeDictionary") - append(dictionary.name) - append("(of: ") - append(dictionary.name) - append(", releasable: Boolean) = _module._malloc(") - append(structLayout.size) - append(").apply {\n\t") - append(heaps) - - dictionary.allFields().forEachIndexed { i, field -> - append("\n\t") - val fieldRef = castToNative(field.type, "of.${field.name}", dealloc = false, useArena = false, releasable = "releasable") - val address = "this + ${structLayout.addressOf(i)}" - - append(when(val declaration = (field.type as ResolvedIdlType.Default).declaration) { - is BuiltinIdlDeclaration -> when(declaration.kind) { - WebIDLBuiltinKind.BYTE, - WebIDLBuiltinKind.BOOLEAN -> "HEAP8[$address] = $fieldRef" - WebIDLBuiltinKind.SHORT, - WebIDLBuiltinKind.CHAR -> "HEAP16[($address) shr 1] = $fieldRef" - WebIDLBuiltinKind.INT -> "HEAP32[($address) shr 2] = $fieldRef" - WebIDLBuiltinKind.LONG -> "HEAP64[($address) shr 3] = $fieldRef" - WebIDLBuiltinKind.FLOAT -> "HEAPF32[($address) shr 2] = $fieldRef" - WebIDLBuiltinKind.DOUBLE -> "HEAPF64[($address) shr 3] = $fieldRef" - WebIDLBuiltinKind.STRING -> "fillEmString(_module, $address, $fieldRef)" - WebIDLBuiltinKind.LIST -> "fillEmArray(_module, $address, $fieldRef)" - else -> throw UnsupportedOperationException(field.type.toString()) - } - is ResolvedIdlEnum -> "HEAP32[($address) shr 2] = $fieldRef" - is ResolvedIdlCallbackFunction, - is ResolvedIdlDictionary -> "HEAP32[($address) shr 2] = $fieldRef" - else -> throw UnsupportedOperationException(field.type.toString()) - }) - } - append("\n\t// padding: ${structLayout.postPadding}") - append("\n}\n") + // toNative + append(""" + + fun Arena.toNativeCallbackOnArena(callback: Any, invoke: Int) = + toNativeCallbackOnArena(callback, invoke, _callbackClone, _callbackEquals, _callbackHashCode, _callbackFree) + + fun toNativeCallback(callback: Any, invoke: Int) = + toNativeCallback(_module, callback, invoke, _callbackClone, _callbackEquals, _callbackHashCode, _callbackFree) + + """.trimIndent()) - // to kotlin - append("\nfun toKotlinDictionary") - append(dictionary.name) - append("(of: Int, dealloc: Boolean): ") - append(dictionary.name) - append(" {\n\t") - append(heaps) - append("return ") - append(dictionary.name) - append("(") - - dictionary.allFields().forEachIndexed { i, field -> - append("\n\t\t") - val address = "of + ${structLayout.addressOf(i)}" - val target = when(val declaration = (field.type as ResolvedIdlType.Default).declaration) { - is BuiltinIdlDeclaration -> when(declaration.kind) { - WebIDLBuiltinKind.BYTE -> "HEAP8[$address]" - WebIDLBuiltinKind.BOOLEAN -> "HEAP8[$address] == 1.toByte()" - WebIDLBuiltinKind.SHORT -> "HEAP16[($address) shr 1].toShort()" - WebIDLBuiltinKind.CHAR -> "HEAP16[($address) shr 1]" - WebIDLBuiltinKind.INT -> "HEAP32[($address) shr 2]" - WebIDLBuiltinKind.LONG -> "HEAP64[($address) shr 3]" - WebIDLBuiltinKind.FLOAT -> "HEAPF32[($address) shr 2]" - WebIDLBuiltinKind.DOUBLE -> "HEAPF64[($address) shr 3]" - WebIDLBuiltinKind.STRING -> "extractEmString(_module, $address)" - WebIDLBuiltinKind.LIST -> "extractEmArray(_module, $address)" - else -> throw UnsupportedOperationException(field.type.toString()) - } - is ResolvedIdlEnum -> "HEAP32[($address) shr 2]" - is ResolvedIdlCallbackFunction, - is ResolvedIdlDictionary -> "HEAP32[($address) shr 2]" - else -> throw UnsupportedOperationException(field.type.toString()) + // Function converters + idl.callbacks.values + .associateBy { it.descName() } + .forEach { (desc, callback) -> + append("\nprivate fun $desc(block: (") + buildList { + add("Int") + callback.args.mapTo(this) { toKtJsType(it.type) } + }.joinTo(builder) + append(") -> ${toKtJsType(callback.type)}): JsAny = js(\"block\")") } - append(castToJS(field.type, target, dealloc = false, deallocContent = false, useArena = false)) - append(",") + append("\n") + + // Invoke address + idl.callbacks.values.joinTo(builder, separator = "") { + "\nprivate var _invoke${it.name}: Int = 0" } - append("\n\t).also { if(dealloc) _module._free(of) }\n}\n") - } - private fun printCallbackCast(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - append("\nprivate fun toNativeCallback") - append(callback.name) - append("(of: ${callback.name}) =\n\t") - append("mallocCallback(_module, of, _invoke") - append(callback.name) - append(", _freeCallback)\n") + // init + append("\n\n") + append(""" + private fun _initCallbacks() { + _callbackClone = createCallbackCloneFunction(_module) + _callbackEquals = createCallbackEqualsFunction(_module) + _callbackHashCode = createCallbackHashCodeFunction(_module) + _callbackFree = createCallbackFreeFunction(_module) + + """.trimIndent()) + idl.callbacks.values.forEach{ callback -> + printCallbackInvoke(builder, callback) + } + append("}\n") } private fun printCallbackInvoke( builder: StringBuilder, - callback: ResolvedIdlCallbackFunction, - index: Int + callback: ResolvedIdlCallbackFunction ) = builder.apply { - val args = listOf("_c: Int") + callback.args.map { "${it.name}: ${toSimpleJsType(it.type)}" } - val castedArgs = callback.args.map { castToJS(it.type, it.name, it.isDealloc(), it.isDeallocContent(), false) } + val args = buildList { + add("_c: Int") + callback.args.mapTo(this) { "${it.name}: ${toKtJsType(it.type)}" } + } + val castedArgs = callback.args.map { + castToKotlin(it.type, it.name) + } + val desc = buildString { + append(callback.type.toInternalDesc()) + append("i") + callback.args.joinTo(this, separator = "") { it.type.toInternalDesc() } + } // header - append("\n\t// ").append(callback.name) - append("\n\t_invoke") - append(callback.name) - append(" = _module._setCallback(${index}, _callback${index}Js { ") + append("\n\t_invoke${callback.name} = _module.addFunction(${callback.descName()} { ") args.joinTo(builder) append(" ->\n\t\t") // body - val call = "toKotlinCallback<${callback.name}>(_module, _c, false)(${castedArgs.joinToString()})" - append(castToNative(callback.type, call, dealloc = false, useArena = false, releasable = "true")) + val call = "toKotlinCallback<${callback.name}>(_module, _c)(${castedArgs.joinToString()})" + val casted = castToNative(callback.type, call, useArena = false) + + append(casted) append("\n\t") // footer - append("})\n") + append("}, \"$desc\")\n") + } + + private fun printDictionaryLayout(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + append("\nprivate val _layout${dictionary.name} = CStructLayout(") + buildList { + dictionary.allFields().mapTo(this) { toLayoutType(it.type) } + add("Byte::class") + }.joinTo(builder) + append(")") + } + + private fun printDictionaryCasts(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + val name = dictionary.name + val fields = dictionary.allFields() + + fun heaps(toNative: Boolean) = buildString { + if (fields.any { it.type.isLong() }) + append("\n\tval HEAP64 = BigInt64Array(_module.HEAP8.buffer, 0, _module.HEAP8.buffer.byteLength / 8)") + if (fields.any { it.type.isDouble() }) + append("\n\tval HEAPF64 = _module.HEAPF64") + if (fields.any { + it.type.isInt() || it.type.isDictionary() || it.type.isString() || + it.type.isString() || it.type.isDictionary() || it.type.isEnum() } + ) append("\n\tval HEAP32 = _module.HEAP32") + if (fields.any { it.type.isFloat() }) + append("\n\tval HEAPF32 = _module.HEAPF32") + if (fields.any { it.type.getAlignment() == 2 }) + append("\n\tval HEAP16 = _module.HEAP16") + if (toNative || fields.any { it.type.getAlignment() == 1 }) + append("\n\tval HEAP8 = _module.HEAP8") + } + + fun ref(i: Int, mem: String): String { + return when(val declaration = (fields[i].type as ResolvedIdlType.Default).declaration) { + is BuiltinIdlDeclaration -> when(declaration.kind) { + WebIDLBuiltinKind.BYTE, + WebIDLBuiltinKind.BOOLEAN -> "HEAP8[$mem + _layout$name[$i]]" + WebIDLBuiltinKind.SHORT, + WebIDLBuiltinKind.CHAR -> "HEAP16[($mem + _layout$name[$i]) shr 1]" + WebIDLBuiltinKind.INT, + WebIDLBuiltinKind.STRING, + WebIDLBuiltinKind.LIST -> "HEAP32[($mem + _layout$name[$i]) shr 2]" + WebIDLBuiltinKind.LONG -> "HEAP64[($mem + _layout$name[$i]) shr 3]" + WebIDLBuiltinKind.FLOAT -> "HEAPF32[($mem + _layout$name[$i]) shr 2]" + WebIDLBuiltinKind.DOUBLE -> "HEAPF64[($mem + _layout$name[$i]) shr 3]" + else -> throw UnsupportedOperationException() + } + is ResolvedIdlEnum, + is ResolvedIdlCallbackFunction, + is ResolvedIdlDictionary -> "HEAP32[($mem + _layout$name[$i]) shr 2]" + else -> throw UnsupportedOperationException() + } + } + + // to native (arena) + append("\nfun Arena.toNative${name}OnArena(of: $name) = alloc(_layout$name.size).apply {") + append(heaps(true)) + buildList { + fields.forEachIndexed { i, field -> + val ref = "of.${field.name}" + val casted = when { + field.type.isBoolean() || field.type.isByte() || + field.type.isShort() || field.type.isChar() -> ref + else -> castToNative(field.type, ref, useArena = true) + } + add("\n\t${ref(i, "this")} = $casted") + } + add("\n\tHEAP8[this + _layout$name[${fields.size}]] = 0") + }.joinTo(builder, separator = "") + append("\n}\n") + + // to native + append("\nfun toNative$name(of: $name) = _module._malloc(_layout$name.size).apply {") + append(heaps(true)) + buildList { + fields.forEachIndexed { i, field -> + val ref = "of.${field.name}" + val casted = when { + field.type.isBoolean() || field.type.isByte() || + field.type.isShort() || field.type.isChar() -> ref + else -> castToNative(field.type, ref, useArena = false) + } + add("\n\t${ref(i, "this")} = $casted") + } + add("\n\tHEAP8[this + _layout$name[${fields.size}]] = FLAG_RELEASABLE") + }.joinTo(builder, separator = "") + append("\n}\n") + + // to kotlin + append("\nfun toKotlin$name(of: Int): $name {") + append(heaps(false)) + append("\n\treturn $name(") + fields.mapIndexed { i, field -> + val ref = ref(i, "of") + val casted = when { + field.type.isChar() -> "$ref.toInt().toChar()" + field.type.isByte() || field.type.isShort() -> ref + else -> castToKotlin(field.type, ref(i, "of")) + } + "\n\t\t$casted," + }.joinTo(builder, separator = "") + append("\n\t)\n}\n") } private fun printFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { + val useArena = function.args.any { + it.type.isString() || + it.type.isArray() || + it.type.isDictionary() || + it.type.isCallback() + } + + val args = function.args.joinToString { + castToNative(it.type, it.name, useArena = useArena) + } + + val deallocFunc = if(function.isDealloc()) + freeFuncFor(function.type, "_result_native") + else null + + val call = "_module._${function.name}($args)" + + // === Print === + append('\n') - printFunctionHeader(builder, function, isActual = expectActual, forcePrintVoid = true) - append(" = ") + printFunctionHeader(builder, function, isActual = expectActual) - val useArena = function.args.any { it.type.isString() || it.type.isArray() || it.isDealloc() } + append(when { + useArena -> " = Arena.use(_module) {" + deallocFunc != null -> " {" + else -> " = " + }) - if(useArena) - append("EmArena.use(_module) { arena ->") append("\n\t") + if(deallocFunc != null) { + append("val _result_native = $call") + append("\n\t") + append("val _result_kt = ${castToKotlin(function.type, "_result_native")}") + append("\n\t") + append(deallocFunc) - val args = function.args.joinToString { - castToNative(it.type, it.name, it.isDealloc(), useArena, releasable = "false") - } - val func = "_module.${function.name}" - append(castToJS(function.type, "$func($args)", function.isDealloc(), function.isDeallocContent(), useArena)) + if(function.type !is ResolvedIdlType.Void) { + append("\n\t") + if(!useArena) + append("return ") + append("_result_kt") + } + } else + append(castToKotlin(function.type, call)) - if(useArena) + if(useArena || deallocFunc != null) append("\n}") - append("\n") } @@ -293,115 +358,195 @@ class KotlinJsPrinter( } private external interface Module: EmModule { - fun _setCallback(index: Int, callback: JsAny): Int - """.trimIndent()) + listOf( + "KString", + "KCharArray", + "KBooleanArray", + "KByteArray", + "KShortArray", + "KIntArray", + "KLongArray", + "KFloatArray", + "KDoubleArray", + *idl.dictionaries.values.map { it.name }.toTypedArray() + ).forEach { + append("\n\tfun _${it}_free(self: Int)") + append("\n\tfun _${it}_freeAddr(): Int") + } + append("\n\tfun _KArray_free(self: Int, freeOp: Int)") + append("\n\n") + idl.globalOperators().forEach { function -> - append("\tfun ") - append(function.name) - append("(") - function.args.joinTo(buffer) { "${it.name}: ${toSimpleJsType(it.type)}" } - append(")") + append("\tfun _${function.name}") + function.args.joinTo(buffer, prefix = "(", postfix = ")") { + "${it.name}: ${toKtJsType(it.type)}" + } if(function.type !is ResolvedIdlType.Void) { append(": ") - append(toSimpleJsType(function.type)) + append(toKtJsType(function.type)) } append("\n") } append("}\n") } - private fun castToNative(type: ResolvedIdlType, content: String, dealloc: Boolean, useArena: Boolean, releasable: String): String = when(type) { + private fun freeFuncFor( + type: ResolvedIdlType, + content: String + ) = when { + type.isString() -> "_module._KString_free($content)" + type.isArray() -> (type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> "_module._K${declaration.kind.simpleName()}Array_free($content)" + is ResolvedIdlEnum -> "_module._KIntArray_free($content)" + is ResolvedIdlDictionary -> "_module._KArray_free($content, _module._${declaration.name}_freeAddr())" + else -> throw UnsupportedOperationException(type.toString()) + } + } + type.isCallback() -> "callbackFree($content)" + type.isDictionary() -> "_module._${(type as ResolvedIdlType.Default).declaration.name.capitalized()}_free($content)" + else -> null + } + + private fun castToNative(type: ResolvedIdlType, content: String, useArena: Boolean): String = when(type) { is ResolvedIdlType.Void -> content is ResolvedIdlType.Default -> when(val decl = type.declaration) { is BuiltinIdlDeclaration -> when(decl.kind) { - WebIDLBuiltinKind.CHAR -> "${content}.code" + WebIDLBuiltinKind.BOOLEAN -> "$content.toInt()" + WebIDLBuiltinKind.CHAR -> "$content.code" + WebIDLBuiltinKind.BYTE -> "$content.toInt()" + WebIDLBuiltinKind.SHORT -> "$content.toInt()" WebIDLBuiltinKind.STRING -> - if(useArena) "arena.toNativeString($content)" - else "toNativeString(_module, $content, $releasable)" + if(useArena) "toNativeStringOnArena($content)" + else "toNativeString(_module, $content)" WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - if (useArena) "arena.toNative${name}Array($content)" - else "toNative${name}Array(_module, $content, $releasable)" - } + is BuiltinIdlDeclaration -> + if (useArena) "toNative${declaration.kind.simpleName()}ArrayOnArena($content)" + else "toNative${declaration.kind.simpleName()}Array(_module, $content)" is ResolvedIdlEnum -> - "toNativeEnumArray(_module, $content, $releasable)" + if (useArena) "toNativeEnumArrayOnArena($content)" + else "toNativeEnumArray(_module, $content)" is ResolvedIdlDictionary -> - "toNativeArray(_module, $content, ::toNativeDictionary${declaration.name}, $releasable)" + if (useArena) "toNativeArrayOnArena($content, ::toNative${declaration.name})" + else "toNativeArray(_module, $content, ::toNative${declaration.name})" else -> throw UnsupportedOperationException(type.toString()) } } else -> content } - is ResolvedIdlEnum -> "${content}.ordinal" + is ResolvedIdlEnum -> "$content.ordinal" is ResolvedIdlCallbackFunction -> - if(dealloc) "arena.callback(toNativeCallback${decl.name}($content))" - else "toNativeCallback${decl.name}($content)" - is ResolvedIdlDictionary -> "toNativeDictionary${decl.name}($content, $releasable)" + if(useArena) "toNativeCallbackOnArena($content, _invoke${decl.name})" + else "toNativeCallback($content, _invoke${decl.name})" + is ResolvedIdlDictionary -> + if (useArena) "toNative${decl.name}OnArena($content)" + else "toNative${decl.name}($content)" else -> throw UnsupportedOperationException(type.toString()) } is ResolvedIdlType.Union -> throw UnsupportedOperationException(type.toString()) } - private fun castToJS(type: ResolvedIdlType, content: String, dealloc: Boolean, deallocContent: Boolean, useArena: Boolean): String = when(type) { + private fun castToKotlin(type: ResolvedIdlType, content: String): String = when(type) { is ResolvedIdlType.Void -> content is ResolvedIdlType.Default -> when(val decl = type.declaration) { is BuiltinIdlDeclaration -> when(decl.kind) { - WebIDLBuiltinKind.FLOAT -> "$content.truncF32()" + WebIDLBuiltinKind.BOOLEAN -> "$content.toBoolean()" WebIDLBuiltinKind.CHAR -> "$content.toChar()" - WebIDLBuiltinKind.STRING -> - if(useArena) "arena.toKotlinString($content, $dealloc)" - else "toKotlinString(_module, $content, $dealloc)" + WebIDLBuiltinKind.BYTE -> "$content.toByte()" + WebIDLBuiltinKind.SHORT -> "$content.toShort()" + WebIDLBuiltinKind.FLOAT -> "$content.truncF32()" + WebIDLBuiltinKind.STRING -> "toKotlinString(_module, $content)" WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - if (useArena) "arena.toKotlin${name}Array($content, $dealloc)" - else "toKotlin${name}Array(_module, $content, $dealloc)" - } - is ResolvedIdlEnum -> - "toKotlinEnumArray(_module, $content, $dealloc, ${declaration.name}.entries::get)" - is ResolvedIdlDictionary -> - "toKotlinArray(_module, $content, ::toKotlinDictionary${declaration.name}, $dealloc, $deallocContent)" + is BuiltinIdlDeclaration -> "toKotlin${declaration.kind.simpleName()}Array(_module, $content)" + is ResolvedIdlEnum -> "toKotlinEnumArray<${declaration.name}>(_module, $content)" + is ResolvedIdlDictionary -> "toKotlinArray(_module, $content, ::toKotlin${declaration.name})" else -> throw UnsupportedOperationException(type.toString()) } } else -> content } is ResolvedIdlEnum -> "${decl.name}.entries[$content]" - is ResolvedIdlCallbackFunction -> - if(useArena) "arena.toKotlinCallback<${decl.name}>($content, $dealloc)" - else "toKotlinCallback<${decl.name}>(_module, $content, $dealloc)" - is ResolvedIdlDictionary -> "toKotlinDictionary${decl.name}($content, $dealloc)" + is ResolvedIdlCallbackFunction -> "toKotlinCallback(_module, $content)" + is ResolvedIdlDictionary -> "toKotlin${decl.name}($content)" else -> throw UnsupportedOperationException(type.toString()) } else -> throw UnsupportedOperationException(type.toString()) } - private fun toSimpleJsType(type: ResolvedIdlType): String = when(type) { + private fun toKtJsType(type: ResolvedIdlType): String = when(type) { is ResolvedIdlType.Void -> "Unit" is ResolvedIdlType.Default -> when(val decl = type.declaration) { is BuiltinIdlDeclaration -> when(decl.kind) { - WebIDLBuiltinKind.BOOLEAN -> "Boolean" + WebIDLBuiltinKind.BOOLEAN -> "Int" WebIDLBuiltinKind.FLOAT -> "Float" WebIDLBuiltinKind.CHAR -> "Int" WebIDLBuiltinKind.INT -> "Int" WebIDLBuiltinKind.DOUBLE -> "Double" - WebIDLBuiltinKind.BYTE -> "Byte" - WebIDLBuiltinKind.SHORT -> "Short" + WebIDLBuiltinKind.BYTE -> "Int" + WebIDLBuiltinKind.SHORT -> "Int" WebIDLBuiltinKind.LONG -> "Long" - WebIDLBuiltinKind.STRING -> "EmString" - WebIDLBuiltinKind.LIST -> "EmArray" + WebIDLBuiltinKind.STRING -> "Int" + WebIDLBuiltinKind.LIST -> "Int" else -> throw UnsupportedOperationException() } is ResolvedIdlEnum -> "Int" - is ResolvedIdlCallbackFunction -> "Int" - is ResolvedIdlDictionary -> "Int" else -> "Int" } else -> throw UnsupportedOperationException(type.toString()) } + + private fun toLayoutType(type: ResolvedIdlType): String = when(type) { + is ResolvedIdlType.Void -> throw UnsupportedOperationException(type.toString()) + is ResolvedIdlType.Default -> when(val decl = type.declaration) { + is BuiltinIdlDeclaration -> when(decl.kind) { + WebIDLBuiltinKind.BOOLEAN -> "Boolean::class" + WebIDLBuiltinKind.FLOAT -> "Float::class" + WebIDLBuiltinKind.CHAR -> "Char::class" + WebIDLBuiltinKind.INT -> "Int::class" + WebIDLBuiltinKind.DOUBLE -> "Double::class" + WebIDLBuiltinKind.BYTE -> "Byte::class" + WebIDLBuiltinKind.SHORT -> "Short::class" + WebIDLBuiltinKind.LONG -> "Long::class" + WebIDLBuiltinKind.STRING -> "CStructLayout.Ptr::class" + WebIDLBuiltinKind.LIST -> "CStructLayout.Ptr::class" + else -> throw UnsupportedOperationException() + } + is ResolvedIdlEnum -> "Int::class" + else -> "CStructLayout.Ptr::class" + } + else -> throw UnsupportedOperationException(type.toString()) + } + + private fun ResolvedIdlCallbackFunction.descName() = buildString { + append("_func") + append(type.toInternalDesc().capitalized()) + append("I") + args.joinTo(this, separator = "") { it.type.toInternalDesc().capitalized() } + } + + fun ResolvedIdlType.toInternalDesc(): String = when(this) { + is ResolvedIdlType.Void -> "v" + is ResolvedIdlType.Default -> when(val declaration = declaration) { + is BuiltinIdlDeclaration -> when(declaration.kind) { + WebIDLBuiltinKind.CHAR -> "i" + WebIDLBuiltinKind.BOOLEAN -> "i" + WebIDLBuiltinKind.BYTE -> "i" + WebIDLBuiltinKind.SHORT -> "i" + WebIDLBuiltinKind.INT -> "i" + WebIDLBuiltinKind.LONG -> "j" + WebIDLBuiltinKind.FLOAT -> "f" + WebIDLBuiltinKind.DOUBLE -> "d" + WebIDLBuiltinKind.STRING -> "p" + WebIDLBuiltinKind.LIST -> "p" + else -> throw UnsupportedOperationException(toString()) + } + is ResolvedIdlEnum -> "i" + else -> "p" + } + else -> throw UnsupportedOperationException(toString()) + } } \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinJvmPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinJvmPrinter.kt index 09b9e73..b757676 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinJvmPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinJvmPrinter.kt @@ -34,15 +34,15 @@ class KotlinJvmPrinter( fun invokerChooser(indent: String) = when { useForeignApi && useJNI -> """ $implName = when(NativeKtUtils.getInvoker()) { - NativeKtUtils.Invoker.FOREIGN -> ${moduleName.capitalized()}Foreign() - NativeKtUtils.Invoker.JNI -> ${moduleName.capitalized()}JNI() + NativeKtUtils.Invoker.FOREIGN -> ${moduleName.capitalized()}Foreign(libraryPath) + NativeKtUtils.Invoker.JNI -> ${moduleName.capitalized()}JNI(libraryPath) } """.replaceIndent(indent) useJNI -> """ - $implName = ${moduleName.capitalized()}JNI() + $implName = ${moduleName.capitalized()}JNI(libraryPath) """.replaceIndent(indent) useForeignApi -> """ - $implName = ${moduleName.capitalized()}Foreign() + $implName = ${moduleName.capitalized()}Foreign(libraryPath) """.replaceIndent(indent) else -> "" } @@ -78,7 +78,7 @@ class KotlinJvmPrinter( if(isLib$${moduleName.capitalized()}Loaded_) return isLib$${moduleName.capitalized()}Loaded_ = true - NativeKtUtils.loadLibrary("$$moduleName", $$useUniversalMacOSLib) + val libraryPath = NativeKtUtils.resolveLibraryFile("$$moduleName", $$useUniversalMacOSLib) """.trimIndent()) @@ -89,11 +89,11 @@ class KotlinJvmPrinter( builder.append(""" if(NativeKtUtils.isJvmciAvailable()) - $implName = ${moduleName.capitalized()}JVMCI($implName!!) + $implName = ${moduleName.capitalized()}JVMCI(libraryPath, $implName!!) """.replaceIndent("\t")) } else { builder.append(""" - $implName = ${moduleName.capitalized()}JVMCI() + $implName = ${moduleName.capitalized()}JVMCI(libraryPath) """.replaceIndent("\t")) } } @@ -124,6 +124,7 @@ class KotlinJvmPrinter( private var $implName: $nativeInvoker? = null private sealed interface $nativeInvoker { + fun _address(name: String): Long """.trimIndent()) diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinNativePrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinNativePrinter.kt index 513db9d..b9915de 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinNativePrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/KotlinNativePrinter.kt @@ -10,7 +10,6 @@ class KotlinNativePrinter( target: File, classPath: String, val moduleName: String, - val is32Bit: Boolean, useCoroutines: Boolean, val expectActual: Boolean ) { @@ -22,7 +21,7 @@ class KotlinNativePrinter( val builder = StringBuilder() builder.append(""" @file:OptIn(ExperimentalForeignApi::class) - @file:Suppress("unused") + @file:Suppress("unused", "UNNECESSARY_SAFE_CALL") package $classPath @@ -40,47 +39,11 @@ class KotlinNativePrinter( if(useCoroutines) builder.append("${actual}suspend fun ${asyncFunctionName(moduleName)}() = Unit\n") - printLabel(builder, "String") - builder.append(""" - - private fun toNativeString(of: String, arena: NativeArena) = cValue<$cinteropPath.KString> { - data = of.cstr.getPointer(arena.scope) - length = of.length - releasable = false - released = false - arena.ptr(data!!) - } - - private fun toNativeString(of: String, releasable: Boolean) = - cValue<$cinteropPath.KString> { toNativeString(of, this, releasable) } - - private fun toNativeString(of: String, struct: $cinteropPath.KString, releasable: Boolean) = struct.apply { - this.data = strdup(of) - this.length = of.length - this.releasable = releasable - this.released = false - } - - private fun toKotlinString(of: $cinteropPath.KString, dealloc: Boolean): String = - toKotlinString(of.data!!, dealloc) - - private fun toKotlinString(of: CValue<$cinteropPath.KString>, dealloc: Boolean): String = - of.useContents { toKotlinString(data!!, dealloc) } - - private fun toKotlinString(of: CValue<$cinteropPath.KString>, arena: NativeArena, dealloc: Boolean): String = - arena.toKotlinString(of.useContents { data }!!, dealloc) - - """.trimIndent()) - if(idl.dictionaries.isNotEmpty()) { printLabel(builder, "Dictionary") idl.dictionaries.values.forEach { printDictionaryCasts(builder, it) } } - printLabel(builder, "Arrays") - printArrayCasts(builder) - idl.enums.values.forEach { printEnumCast(builder, it) } - if(idl.callbacks.isNotEmpty()) { printLabel(builder, "Callbacks") idl.callbacks.values.forEach { printCallbackWrap(builder, it) } @@ -97,442 +60,215 @@ class KotlinNativePrinter( private fun printDictionaryCasts(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - // to jvm - append("\nprivate fun toKotlinDictionary") - append(dictionary.name.capitalized()) - append("(of: CPointer<") - append(cinteropPath) - append(".") - append(dictionary.name) - append(">?, dealloc: Boolean) = of!!.pointed.run {\n\t") - append(dictionary.name) - append("(\n\t\t") - - dictionary.allFields().joinTo(builder, separator = ",\n\t\t") { - castFromNative(it.type, it.name, dealloc = false, deallocContent = false, useArena = false) - } - append("\n\t)\n") - append("}.also { if(dealloc) free(of) }\n") - - // to native - append("\nprivate fun toNativeDictionary") - append(dictionary.name) - append("(of: ") - append(dictionary.name) - append(", releasable: Boolean) = allocStruct<") - append(cinteropPath) - append(".") - append(dictionary.name) - append(">().apply {\n\t") - append("val struct = pointed\n\t") - - dictionary.allFields().joinTo(builder, separator = "\n\t") { - if(it.type.isArray() || it.type.isString()) - castToNative(it.type, "of.${it.name}", dealloc = false, useArena = false, struct = "struct.${it.name}", releasable = "releasable") - else - "struct.${it.name} = ${castToNative(it.type, "of.${it.name}", dealloc = false, useArena = false, releasable = "releasable")}" - } + // free handle + append("\nprivate val _handle${dictionary.name}Free = staticCFunction {") + append("\n\t$cinteropPath.${dictionary.name}_free(it!!.reinterpret())") append("\n}\n") - } - private fun printCallbackWrap(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - // header - append("\nprivate fun ") - append("toNativeCallback") - append(callback.name) - append("(of: ") - append(callback.name) - append(") =\n\t") - - // body - append("allocStruct<") - append(cinteropPath) - append(".") - append(callback.name) - append(">().apply {\n\t\t") - append("val struct = pointed\n\t\t") - - // m = - append("struct.m = StableRef.create(of).asCPointer()\n\t\t") - - // invoke = - val args = listOf("_callback") + callback.args.map { it.name } - - val castedArgs = callback.args.joinToString { - castFromNative(it.type, it.name, it.isDealloc(), deallocContent = false, useArena = false) + // native (arena) + + append("\nprivate fun MemScope.toNative${dictionary.name}OnArena(of: ${dictionary.name}): CPointer<$cinteropPath.${dictionary.name}> {") + append("\n\tval mem = alloc<$cinteropPath.${dictionary.name}>()") + + dictionary.allFields().forEach { + append("\n\tmem.${it.name} = ${castToNative(it.type, "of.${it.name}", useArena = true, pin = false)}") } - val call = "_callback!!.pointed.m!!.asStableRef<${callback.name}>().get()($castedArgs)" + append("\n\tmem.__flags = 0") + append("\n\treturn mem.ptr") + append("\n}\n") - append("struct.invoke = staticCFunction { ") - args.joinTo(builder) - append(" ->\n\t\t\t") - append(castToNative(callback.type, call, dealloc = false, useArena = false, releasable = "true")) - append("\n\t\t}\n\t\t") + // native - // free = - append("struct.free = freeCallbackFunction.reinterpret()\n\t}\n") - } + append("\nprivate fun toNative${dictionary.name}(of: ${dictionary.name}): CPointer<$cinteropPath.${dictionary.name}> {") + append("\n\tval mem = malloc(sizeOf<$cinteropPath.${dictionary.name}>().convert())!!.reinterpret<$cinteropPath.${dictionary.name}>().pointed") - private fun printFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { - append('\n') - printFunctionHeader(builder, function, isActual = expectActual) - append(" = ") + dictionary.allFields().forEach { + append("\n\tmem.${it.name} = ${castToNative(it.type, "of.${it.name}", useArena = false, pin = false)}") + } - val useArena = function.args.any { it.type.isString() || it.type.isArray() || it.isDealloc() } + append("\n\tmem.__flags = FLAG_RELEASABLE") + append("\n\treturn mem.ptr") + append("\n}\n") - if(useArena) - append("NativeArena.use { arena ->") - append("\n\t") + // kotlin + + append("\nprivate fun toKotlin${dictionary.name}(of: CPointer<$cinteropPath.${dictionary.name}>): ${dictionary.name} = of.pointed.let { mem -> ${dictionary.name}(") - val args = function.args.joinToString { arg -> - castToNative(arg.type, arg.name, arg.isDealloc(), useArena, releasable = "false") + dictionary.allFields().forEach { + append("\n\t${it.name} = ${castFromNative(it.type, "mem.${it.name}")},") } + append("\n) }\n") + } - val call = "$cinteropPath.${function.name}($args)" - append(castFromNative(function.type, call, function.isDealloc(), function.isDeallocContent(), useArena)) + private fun printCallbackWrap(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - if(useArena) - append("\n}") + // Header + append("\nprivate val _invoke${callback.name.capitalized()}: CPointer") + callback.args.mapTo(this) { it.type.toKnType() } + }.joinTo(builder) + append(") -> ${callback.type.toKnType()}>> =") + + // staticCFunction + append("\n\tstaticCFunction { ") + buildList { + add("_callback") + callback.args.mapTo(this) { it.name } + }.joinTo(builder) + append(" ->") + + // Call + val args = callback.args.joinToString { + castFromNative(it.type, it.name) + } + val call = "toKotlinCallback<${callback.name}>(_callback)($args)" + append("\n\t\t${castToNative(callback.type, call, useArena = false, pin = false)}") - append("\n") + // End + append("\n\t}\n") } - private fun printArrayCasts(builder: StringBuilder) = builder.apply { - arrayOf( - "Byte", "Short", "Int", "Long", "Float", "Double" - ).forEach { type -> - append(""" - - // Array: $type - - private fun toNative${type}Array(array: ${type}Array, arena: NativeArena) = cValue<$cinteropPath.K${type}Array> { - elements = arena.pin(array).addressOf(0) - size = array.size - releasable = false - released = false - arena.ptr(elements!!) - } - - private fun toNative${type}Array(array: ${type}Array, releasable: Boolean) = - cValue<$cinteropPath.K${type}Array> { toNative${type}Array(array, this, releasable) } - - private fun toNative${type}Array(array: ${type}Array, struct: $cinteropPath.K${type}Array, releasable: Boolean) = struct.apply { - val bytes = array.size * ${type}.SIZE_BYTES - this.elements = mallocExact(bytes.toUInt()).reinterpret() - this.size = array.size - this.releasable = releasable - this.released = false - array.usePinned { memcpy(elements, it.addressOf(0), bytes.${if(is32Bit) "toUInt" else "toULong"}()) } - } - - private fun toKotlin${type}Array(struct: CValue<$cinteropPath.K${type}Array>, arena: NativeArena, dealloc: Boolean) = struct.useContents { - ${type}Array(size) { elements!![it] }.also { - if(dealloc) arena.freeMem(elements!!) - } - } - - private fun toKotlin${type}Array(struct: CValue<$cinteropPath.K${type}Array>, dealloc: Boolean) = - struct.useContents { toKotlin${type}Array(this, dealloc) } - - private fun toKotlin${type}Array(struct: $cinteropPath.K${type}Array, dealloc: Boolean) = - ${type}Array(struct.size) { struct.elements!![it] } - .also { if(dealloc) free(struct.elements!!) } - - """.trimIndent()) - } + private fun printFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { - // Char needs some changes: - // 1. .reinterpret() in toNativeCharArray - // 2. .toInt().toChar() in toKotlinCharArray - append(""" - - // Array: Char - - private fun toNativeCharArray(array: CharArray, arena: NativeArena) = cValue<$cinteropPath.KCharArray> { - elements = arena.pin(array).addressOf(0).reinterpret() - size = array.size - releasable = false - released = false - arena.ptr(elements!!) - } - - private fun toNativeCharArray(array: CharArray, releasable: Boolean) = - cValue<$cinteropPath.KCharArray> { toNativeCharArray(array, this, releasable) } - - private fun toNativeCharArray(array: CharArray, struct: $cinteropPath.KCharArray, releasable: Boolean) = struct.apply { - val bytes = array.size * Char.SIZE_BYTES - this.elements = mallocExact(bytes.toUInt()).reinterpret() - this.size = array.size - this.releasable = releasable - this.released = false - array.usePinned { memcpy(elements, it.addressOf(0), bytes.${if (is32Bit) "toUInt" else "toULong"}()) } - } + val useArena = function.args.any { + it.type.isString() || + it.type.isArray() || + it.type.isDictionary() || + it.type.isCallback() + } - private fun toKotlinCharArray(struct: CValue<$cinteropPath.KCharArray>, arena: NativeArena, dealloc: Boolean) = struct.useContents { - CharArray(size) { elements!![it].toInt().toChar() }.also { - if(dealloc) arena.freeMem(elements!!) - } - } - - private fun toKotlinCharArray(struct: CValue<$cinteropPath.KCharArray>, dealloc: Boolean) = - struct.useContents { toKotlinCharArray(this, dealloc) } + val args = function.args.joinToString { + castToNative(it.type, it.name, useArena = useArena, pin = function.isCritical()) + } - private fun toKotlinCharArray(struct: $cinteropPath.KCharArray, dealloc: Boolean) = - CharArray(struct.size) { struct.elements!![it].toInt().toChar() } - .also { if(dealloc) free(struct.elements!!) } - - """.trimIndent()) + val deallocFunc = if(function.isDealloc()) + freeFuncFor(function.type, "_result_native") + else null - // Booleans needs to be cast to bytes - append(""" - - // Array: Boolean - - private fun toNativeBooleanArray(array: BooleanArray, arena: NativeArena) = cValue<$cinteropPath.KBooleanArray> { - val byteArray = array.map { it.toByte() }.toByteArray() - elements = arena.pin(byteArray).addressOf(0).reinterpret() - size = array.size - releasable = false - released = false - arena.ptr(elements!!) - } + val call = "$cinteropPath.${function.name}($args)" - private fun toNativeBooleanArray(array: BooleanArray, releasable: Boolean) = - cValue<$cinteropPath.KBooleanArray> { toNativeBooleanArray(array, this, releasable) } - - private fun toNativeBooleanArray(array: BooleanArray, struct: $cinteropPath.KBooleanArray, releasable: Boolean) = struct.apply { - val bytes = array.size * Byte.SIZE_BYTES - this.elements = mallocExact(bytes.toUInt()).reinterpret() - this.size = array.size - this.releasable = releasable - this.released = false - val byteArray = array.map { it.toByte() }.toByteArray() - byteArray.usePinned { memcpy(elements, it.addressOf(0), bytes.${if (is32Bit) "toUInt" else "toULong"}()) } - } + // === Print === - private fun toKotlinBooleanArray(struct: CValue<$cinteropPath.KBooleanArray>, arena: NativeArena, dealloc: Boolean) = struct.useContents { - BooleanArray(size) { elements!![it].value }.also { - if(dealloc) arena.freeMem(elements!!) - } - } + append('\n') + printFunctionHeader(builder, function, isActual = expectActual) - private fun toKotlinBooleanArray(struct: CValue<$cinteropPath.KBooleanArray>, dealloc: Boolean) = - struct.useContents { toKotlinBooleanArray(this, dealloc) } - - private fun toKotlinBooleanArray(struct: $cinteropPath.KBooleanArray, dealloc: Boolean) = - BooleanArray(struct.size) { struct.elements!![it].value } - .also { if(dealloc) free(struct.elements!!) } - - """.trimIndent()) + append(when { + useArena -> " = memScoped {" + deallocFunc != null -> " {" + else -> " = " + }) - // Enum casts - append(""" - // Array: Enum - - private fun , N: CPrimitiveVar> toNativeEnumArray( - array: Array, - arena: NativeArena, - typeSize: Long, - setter: (from: Array, to: CPointer) -> Unit - ) = cValue<$cinteropPath.KIntArray> { - val bytes = array.size * typeSize - elements = arena.ptr(mallocExact(bytes.toUInt()).reinterpret()) - size = array.size - releasable = false - released = false - setter(array, elements!!.reinterpret()) - } - - private fun , N: CPrimitiveVar> toNativeEnumArray( - array: Array, - typeSize: Long, - setter: (from: Array, to: CPointer) -> Unit, - releasable: Boolean - ) = cValue<$cinteropPath.KIntArray> { - toNativeEnumArray(array, typeSize, setter, this, releasable) - } - - private fun , N: CPrimitiveVar> toNativeEnumArray( - array: Array, - typeSize: Long, - setter: (from: Array, to: CPointer) -> Unit, - struct: $cinteropPath.KIntArray, - releasable: Boolean - ) = struct.apply { - val bytes = array.size * typeSize - this.elements = mallocExact(bytes.toUInt()).reinterpret() - this.size = array.size - this.releasable = releasable - this.released = false - setter(array, elements!!.reinterpret()) - } - - private fun , N: CPrimitiveVar> toKotlinEnumArray( - struct: CValue<$cinteropPath.KIntArray>, - arena: NativeArena, - dealloc: Boolean, - converter: (size: Int, elements: CPointer) -> Array - ) = struct.useContents { - converter(size, this.elements!!.reinterpret()).also { - if(dealloc) arena.freeMem(this.elements!!) - } - } - - private fun , N: CPrimitiveVar> toKotlinEnumArray( - struct: CValue<$cinteropPath.KIntArray>, - dealloc: Boolean, - converter: (size: Int, elements: CPointer) -> Array - ) = struct.useContents { toKotlinEnumArray(this, dealloc, converter) } - - private fun , N: CPrimitiveVar> toKotlinEnumArray( - struct: $cinteropPath.KIntArray, - dealloc: Boolean, - converter: (size: Int, elements: CPointer) -> Array - ) = converter(struct.size, struct.elements!!.reinterpret()) - .also { if(dealloc) free(struct.elements!!) } - - // Object array - - private fun toNativeArray( - array: Array, - converter: (from: T, releasable: Boolean) -> CPointer, - arena: NativeArena? = null, - releasable: Boolean - ) = cValue<$cinteropPath.KArray> { - toNativeArray(array, converter, arena, this, releasable) - } - - private fun toNativeArray( - array: Array, - converter: (from: T, releasable: Boolean) -> CPointer, - arena: NativeArena? = null, - struct: $cinteropPath.KArray, - releasable: Boolean - ) = struct.apply { - this.elements = mallocExact((array.size * size_t.SIZE_BYTES).toUInt()).reinterpret() - this.size = array.size - this.releasable = releasable - this.released = false - for(i in array.indices) - elements!![i] = converter(array[i], releasable) - arena?.ptr(elements!!.reinterpret()) - } - - private fun toKotlinArray( - struct: CValue<$cinteropPath.KArray>, - converter: (from: CPointer, dealloc: Boolean) -> T, - dealloc: Boolean, - deallocContent: Boolean, - arena: NativeArena? = null - ) = struct.useContents { - toKotlinArray(this, converter, dealloc, deallocContent, arena) - } - - @Suppress("unchecked_cast") - private fun toKotlinArray( - struct: $cinteropPath.KArray, - converter: (from: CPointer, dealloc: Boolean) -> T, - dealloc: Boolean, - deallocContent: Boolean, - arena: NativeArena? = null - ): Array { - return Array(struct.size) { - converter(struct.elements!![it]!!.reinterpret(), deallocContent) - }.also { - if(dealloc) arena?.freeMem(struct.elements!!) ?: free(struct.elements!!) - } as Array + append("\n\t") + if(deallocFunc != null) { + append("val _result_native = $call") + append("\n\t") + append("val _result_kt = ${castFromNative(function.type, "_result_native")}") + append("\n\t") + append(deallocFunc) + + if(function.type !is ResolvedIdlType.Void) { + append("\n\t") + if(!useArena) + append("return ") + append("_result_kt") } - - """.trimIndent()) + } else + append(castFromNative(function.type, call)) + + if(useArena || deallocFunc != null) + append("\n}") + append("\n") } - private fun printEnumCast(builder: StringBuilder, enum: ResolvedIdlEnum) = builder.apply { - append(""" - - private val _${enum.name}ToKt = { size: Int, elements: CPointer<${cinteropPath}.${enum.name}.Var> -> - Array(size) { ${enum.name}.entries[elements[it].value.ordinal] } - } - - private val _${enum.name}ToNative = { from: Array<${enum.name}>, to: CPointer<${cinteropPath}.${enum.name}.Var> -> - for(i in from.indices) - to[i].value = ${cinteropPath}.${enum.name}.entries[from[i].ordinal] + private fun freeFuncFor( + type: ResolvedIdlType, + content: String + ) = when { + type.isString() -> "${cinteropPath}.KString_free($content?.reinterpret())" + type.isArray() -> (type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> "${cinteropPath}.K${declaration.kind.simpleName()}Array_free($content?.reinterpret())" + is ResolvedIdlEnum -> "${cinteropPath}.KIntArray_free($content?.reinterpret())" + is ResolvedIdlDictionary -> "${cinteropPath}.KArray_free($content, _handle${declaration.name}Free)" + else -> throw UnsupportedOperationException(type.toString()) } - - """.trimIndent()) + } + type.isCallback() -> "callbackFree($content?.reinterpret())" + type.isDictionary() -> "${cinteropPath}.${(type as ResolvedIdlType.Default).declaration.name.capitalized()}_free($content)" + else -> null } - private fun castFromNative(type: ResolvedIdlType, content: String, dealloc: Boolean, deallocContent: Boolean, useArena: Boolean): String = when(type) { + private fun castFromNative(type: ResolvedIdlType, content: String): String = when(type) { is ResolvedIdlType.Void -> content is ResolvedIdlType.Default -> when(val decl = type.declaration) { is BuiltinIdlDeclaration -> when(decl.kind) { WebIDLBuiltinKind.CHAR -> "$content.toInt().toChar()" - WebIDLBuiltinKind.STRING -> - if(useArena) "toKotlinString($content, arena, $dealloc)" - else "toKotlinString($content, $dealloc)" + WebIDLBuiltinKind.STRING -> "toKotlinString($content!!.reinterpret())" WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - if (useArena) "toKotlin${name}Array($content, arena, $dealloc)" - else "toKotlin${name}Array($content, $dealloc)" - } - is ResolvedIdlEnum -> { - if (useArena) "toKotlinEnumArray($content, arena, $dealloc, _${declaration.name}ToKt)" - else "toKotlinEnumArray($content, $dealloc, _${declaration.name}ToKt)" - } - is ResolvedIdlDictionary -> "toKotlinArray($content, ::toKotlinDictionary${declaration.name}, $dealloc, $deallocContent${if(useArena) ", arena" else ""})" + is BuiltinIdlDeclaration -> "toKotlin${declaration.kind.simpleName()}Array($content!!.reinterpret())" + is ResolvedIdlEnum -> "toKotlinEnumArray<${declaration.name}>($content!!.reinterpret())" + is ResolvedIdlDictionary -> "toKotlinArray($content!!.reinterpret(), ::toKotlin${declaration.name})" else -> throw UnsupportedOperationException(type.toString()) } } else -> content } - is ResolvedIdlCallbackFunction -> - if(useArena) "arena.toKotlinCallback<${decl.name}>($content!!.reinterpret(), $dealloc)" - else "toKotlinCallback<${decl.name}>($content!!.reinterpret(), $dealloc)" + is ResolvedIdlCallbackFunction -> "toKotlinCallback($content!!)" is ResolvedIdlEnum -> "${decl.name}.entries[${content}.ordinal]" - is ResolvedIdlDictionary -> "toKotlinDictionary${decl.name}(${content}, $dealloc)" + is ResolvedIdlDictionary -> "toKotlin${decl.name}(${content}!!)" else -> throw UnsupportedOperationException(type.toString()) } else -> throw UnsupportedOperationException(type.toString()) } - private fun castToNative(type: ResolvedIdlType, content: String, dealloc: Boolean, useArena: Boolean, struct: String? = null, releasable: String): String { - val struct = if(struct != null) ", struct = $struct" else "" - val releasable = "releasable = $releasable" - return when(type) { - is ResolvedIdlType.Void -> content - is ResolvedIdlType.Default -> when(val decl = type.declaration) { - is BuiltinIdlDeclaration -> when(decl.kind) { - WebIDLBuiltinKind.STRING -> - if(useArena) "toNativeString($content, arena)" - else "toNativeString($content$struct, $releasable)" - WebIDLBuiltinKind.CHAR -> "$content.code.toUShort()" - WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> - when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - if (useArena) "toNative${name}Array($content, arena)" - else "toNative${name}Array($content$struct, $releasable)" - } - is ResolvedIdlEnum -> { - if (useArena) "toNativeEnumArray($content, arena, sizeOf<${cinteropPath}.${declaration.name}.Var>(), _${declaration.name}ToNative)" - else "toNativeEnumArray($content, sizeOf<${cinteropPath}.${declaration.name}.Var>(), _${declaration.name}ToNative$struct, $releasable)" - } - is ResolvedIdlDictionary -> "toNativeArray($content, ::toNativeDictionary${declaration.name}${if(useArena) ", arena" else ""}$struct, $releasable)" - else -> throw UnsupportedOperationException(type.toString()) - } + private fun castToNative(type: ResolvedIdlType, content: String, useArena: Boolean, pin: Boolean): String = when(type) { + is ResolvedIdlType.Void -> content + is ResolvedIdlType.Default -> when(val decl = type.declaration) { + is BuiltinIdlDeclaration -> when(decl.kind) { + WebIDLBuiltinKind.STRING -> + if(useArena) "toNativeStringOnArena($content, $pin).reinterpret()" + else "toNativeString($content).reinterpret()" + WebIDLBuiltinKind.CHAR -> "$content.code.toUShort()" + WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> + if (useArena) "toNative${declaration.kind.simpleName()}ArrayOnArena($content, $pin).reinterpret()" + else "toNative${declaration.kind.simpleName()}Array($content).reinterpret()" + is ResolvedIdlEnum -> + if (useArena) "toNativeEnumArrayOnArena($content, $pin).reinterpret()" + else "toNativeEnumArray($content).reinterpret()" + is ResolvedIdlDictionary -> + if (useArena) "toNativeArrayOnArena($content, ::toNative${declaration.name}OnArena).reinterpret()" + else "toNativeArray($content, ::toNative${declaration.name}).reinterpret()" + else -> throw UnsupportedOperationException(type.toString()) } - else -> content } - is ResolvedIdlEnum -> "${cinteropPath}.${decl.name}.entries[${content}.ordinal]" - is ResolvedIdlCallbackFunction -> - if(dealloc) "arena.callback(toNativeCallback${decl.name}($content))" - else "toNativeCallback${decl.name}($content)" - is ResolvedIdlDictionary -> "toNativeDictionary${decl.name}(${content}, $releasable)" - else -> throw UnsupportedOperationException(type.toString()) + else -> content } + is ResolvedIdlEnum -> "${cinteropPath}.${decl.name}.entries[$content.ordinal]" + is ResolvedIdlCallbackFunction -> + if(useArena) "toNativeCallbackOnArena($content, _invoke${decl.name.capitalized()}).reinterpret()" + else "toNativeCallback($content, _invoke${decl.name.capitalized()}).reinterpret()" + is ResolvedIdlDictionary -> + if (useArena) "toNative${decl.name}OnArena($content)" + else "toNative${decl.name}($content)" else -> throw UnsupportedOperationException(type.toString()) } + else -> throw UnsupportedOperationException(type.toString()) + } + + private fun ResolvedIdlType.toKnType(): String = when(this) { + is ResolvedIdlType.Void -> "Unit" + is ResolvedIdlType.Default -> { + val value = "${cinteropPath}.${toCDefType(ptr = false)}" + when { + isString() || isArray() || isCallback() || isDictionary() -> "CPointer<$value>?" + else -> value + } + } + else -> throw UnsupportedOperationException(toString()) } } \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmCIPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmCIPrinter.kt index 4c77a25..4e6eed3 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmCIPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmCIPrinter.kt @@ -18,12 +18,13 @@ class KotlinJvmCIPrinter( if(implementFields) { append(""" - private class $name: $parentClass { + private class $name(libraryPath: String): $parentClass { """.trimIndent()) } else { append(""" private class $name( + libraryPath: String, parent: $parentClass ): $parentClass by parent { @@ -31,16 +32,7 @@ class KotlinJvmCIPrinter( } if(operators.isNotEmpty()) { - append($$""" - companion object { - private fun linkFunction(name: String, alt: Boolean, vararg types: Class<*>) { - JVMCIUtils.linkNativeCall( - $$name::class.java.getDeclaredMethod("_$name", *types), - NativeKtUtils.findAddress("EXPORTED_$${classPath.replace(".", "_")}_$name${if (alt) "_" else ""}") - ) - } - - """.replaceIndent("\t")) + append("\tcompanion object {") operators.forEach { append("\n\t\t@JvmStatic ") @@ -56,17 +48,34 @@ class KotlinJvmCIPrinter( append("\n\t}\n\n") append("\tinit {") + if(implementFields) + append("\n\t\tSystem.load(libraryPath)") + operators.forEach { printFunctionBinding(builder, it) } append("\n\t}\n") - operators.forEach { - printFunctionCall(builder, it) - } + append($$""" + + private fun linkFunction(name: String, alt: Boolean, vararg types: Class<*>) { + JVMCIUtils.linkNativeCall( + $$name::class.java.getDeclaredMethod("_$name", *types), + _address("EXPORTED_$${classPath.replace(".", "_")}_$name${if (alt) "_" else ""}") + ) + } + + """.replaceIndent("\t")) if (implementFields) { + append(""" + + override fun _address(name: String): Long = + NativeKtUtils.findAddress(name) + + """.replaceIndent("\t")) + val nonCritical = idl.globalOperators() .filter { !it.isCritical() } @@ -75,6 +84,10 @@ class KotlinJvmCIPrinter( throw UnsupportedOperationException("JVMCI can not operate with non-critical operations: $list") } } + + operators.forEach { + printFunctionCall(builder, it) + } } append("\n}\n") } @@ -84,10 +97,11 @@ class KotlinJvmCIPrinter( val args = listOf("\"${function.name}\"", function.hasString() || function.hasArray()) + function.args.flatMap { val clazz = "${it.type.toKotlinType(stringAsBytes = true, enumAsInt = true)}::class.java" - - if(it.type.isString() || it.type.isArray()) - listOf(clazz, "Int::class.java") - else listOf(clazz) + when { + it.type.isString() -> listOf(clazz, "Int::class.java", "Int::class.java") + it.type.isArray() -> listOf(clazz, "Int::class.java") + else -> listOf(clazz) + } } append("\n\t\tlinkFunction(${args.joinToString()})") @@ -96,25 +110,35 @@ class KotlinJvmCIPrinter( private fun printFunctionCall(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { append("\n\t") printFunctionHeader(builder, function, - isOverride = true, - forcePrintVoid = true + isOverride = true ) - append(" =\n\t\t") + append(" {") + + function.args.forEach { + if(it.type.isString()) + append("\n\t\tval _bytes_${it.name} = ${it.name}.toByteArray()") + if(it.type.isEnumArray()) + append("\n\t\tval _ints_${it.name} = IntArray(${it.name}.size) { ${it.name}[it].ordinal }") + } + + append("\n\t\t") + if(function.type !is ResolvedIdlType.Void) + append("return ") val args = function.args.joinToString { toNativeCriticalType(it.type, it.name) } val call = "_${function.name}(${args})" append(toKotlinCriticalType(function.type, call)) - append("\n") + append("\n\t}\n") } } internal fun toNativeCriticalType(type: ResolvedIdlType, name: String) = when { - type.isString() -> "${name}.toByteArray(), ${name}.length" - type.isEnum() -> "${name}.ordinal" - type.isEnumArray() -> "IntArray(${name}.size) { ${name}[it].ordinal }, ${name}.size" - type.isArray() -> "${name}, ${name}.size" + type.isString() -> "_bytes_$name, $name.length, _bytes_$name.size" + type.isEnum() -> "$name.ordinal" + type.isEnumArray() -> "_ints_$name, $name.size" + type.isArray() -> "$name, $name.size" else -> name } diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmForeignPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmForeignPrinter.kt index 76e4b55..4864a6d 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmForeignPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmForeignPrinter.kt @@ -15,7 +15,7 @@ class KotlinJvmForeignPrinter( init { builder.append("${indent}private class ") builder.append(name) - builder.append("(prefix: String = \"EXPORTED_") + builder.append("(libraryPath: String, prefix: String = \"EXPORTED_") builder.append(classPath.replace(".", "_")) builder.append("_\")") if(parentClass != null) @@ -25,98 +25,104 @@ class KotlinJvmForeignPrinter( if(idl.callbacks.isNotEmpty()) { builder.append("\tcompanion object {\n") - idl.dictionaries.values.forEach { printDictionaryDesc(builder, it) } + idl.dictionaries.values.forEach { printDictionaryLayout(builder, it) } idl.dictionaries.values.forEach { printDictionaryCasts(builder, it) } - idl.callbacks.values.forEach { printCallbackInvoke(builder, it) } - idl.callbacks.values.forEach { printCallbackMethodHandle(builder, it) } - idl.callbacks.values.forEach { printCallbackDesc(builder, it) } - builder.append("\n") - idl.callbacks.values.forEach { printCallbackToNative(builder, it) } + + builder.append("\n$indent\t\tprivate val lookup = MethodHandles.lookup()\n") + idl.callbacks.values.forEach { printCallbackUpcall(builder, it) } builder.append("\t}\n\n") } + builder.append($$""" + private val handle = SymbolLookup.libraryLookup(java.nio.file.Paths.get(libraryPath), Arena.global()) + + private val addressKArrayFree = ForeignUtils.address(handle, "${prefix}KArray_free") + private val handleKArrayFree = ForeignUtils.handle(addressKArrayFree, false, null, ValueLayout.ADDRESS, ValueLayout.ADDRESS) + + """.replaceIndent("\t")) + + buildList { + addAll(listOf( + "KString", "KCharArray", "KBooleanArray", + "KByteArray", "KShortArray", "KIntArray", + "KLongArray", "KFloatArray", "KDoubleArray" + )) + idl.dictionaries.values.mapTo(this) { it.name } + }.forEach { + builder.append("\n\tprivate val address${it.capitalized()}Free = ForeignUtils.address(handle, \"\${prefix}${it}_free\")") + builder.append("\n\tprivate val handle${it.capitalized()}Free = ForeignUtils.handle(address${it.capitalized()}Free, false, null, ValueLayout.ADDRESS)\n") + } + builder.append("\n") + idl.globalOperators().forEach { printFunctionHandle(builder, it) } + + builder.append(""" + + override fun _address(name: String): Long = + ForeignUtils.address(handle, name).address() + + """.replaceIndent("$indent\t")) + idl.globalOperators().forEach { printFunctionCall(builder, it) } builder.append("${indent}}") } - private fun printDictionaryDesc(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - val structLayout = CStructLayout(dictionary, false) - - val structName = "struct${dictionary.name.capitalized()}" - - append("\n\t\tprivate val ") - append(structName) - append(" = MemoryLayout.structLayout(\n\t\t\t") + private fun printDictionaryLayout(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + append("$indent\t\tprivate val layout${dictionary.name.capitalized()} = CStructLayout(") + buildList { + dictionary.allFields().mapTo(this) { it.type.toForeignType() } + add("ValueLayout.JAVA_BYTE") + }.joinTo(builder) + append(")\n") + } - dictionary.allFields().flatMapIndexed { i, it -> - val field = "${it.type.toForeignType()}.withName(\"${it.name}\")" + private fun printDictionaryCasts(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + val layout = "layout${dictionary.name.capitalized()}" + val name = dictionary.name + val fields = dictionary.allFields() + + // to native (heap) + append("\n\t\tprivate fun toNativeDictionary${name.capitalized()}(of: $name) = ") + append("ForeignUtils.malloc($layout.size).apply {") + + fields.forEachIndexed { i, it -> + val value = castToNative(it.type, "of.${it.name}", useArena = false) + val set = "set(${it.type.toForeignType()}, $layout[$i], ${value})" + append("\n\t\t\t$set") + } + append("\n\t\t\tset(ValueLayout.JAVA_BYTE, $layout[${fields.size}], ForeignUtils.FLAG_RELEASABLE)") - if(structLayout.paddingOf(i) == 0) - listOf(field) - else - listOf("MemoryLayout.paddingLayout(${structLayout.paddingOf(i)})", field) - }.joinTo(builder, separator = ",\n\t\t\t") + append("\n\t\t}\n") - if(structLayout.postPadding != 0) - append(",\n\t\t\tMemoryLayout.paddingLayout(${structLayout.postPadding})") - append("\n\t\t)\n\t\t") + // to native (arena) + append("\n\t\tprivate fun toNativeDictionary${name.capitalized()}OnArena(arena: Arena, of: $name) = ") + append("arena.allocate($layout.size).apply {") - dictionary.allFields().joinTo(builder, separator = "\n\t\t") { - val fieldName = "${structName}Field${it.name.capitalized()}" - val func = if(it.type.isString() || it.type.isArray()) - "byteOffset" else "varHandle" - "private val $fieldName = $structName.$func(MemoryLayout.PathElement.groupElement(\"${it.name}\"))" + fields.forEachIndexed { i, it -> + val value = castToNative(it.type, "of.${it.name}", useArena = true) + val set = "set(${it.type.toForeignType()}, $layout[$i], ${value})" + append("\n\t\t\t$set") } - append("\n") - } + append("\n\t\t\tset(ValueLayout.JAVA_BYTE, $layout[${fields.size}], ForeignUtils.FLAG_ON_STACK)") - private fun printDictionaryCasts(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - val structLayout = CStructLayout(dictionary, false) - - val structName = "struct${dictionary.name.capitalized()}" - - // to native - append("\n\t\tprivate fun toNativeDictionary") - append(dictionary.name.capitalized()) - append("(of: ") - append(dictionary.name) - append(", releasable: Boolean) = Arena.global().allocate(") - append(structName) - append(").apply {\n\t\t\t") - dictionary.allFields().joinTo(builder, separator = "\n\t\t\t") { - val fieldName = "${structName}Field${it.name.capitalized()}" - if(it.type.isString() || it.type.isArray()) { - castToNative(it.type, "of.${it.name}", false, dealloc = false, useArena = false, slice = "asSlice($fieldName)", releasable = "releasable") - } else { - val set = castToNative(it.type, "of.${it.name}", critical = false, dealloc = false, useArena = false, releasable = "releasable") - "$fieldName.set(this, 0L, $set)" - } - } append("\n\t\t}\n") // to jvm - append("\n\t\tprivate fun toJvmDictionary") - append(dictionary.name.capitalized()) - append("(of: MemorySegment, dealloc: Boolean) = of.reinterpret(${structLayout.size}).run {\n\t\t\t") - append(dictionary.name) - append("(\n\t\t\t\t") - dictionary.allFields().joinTo(builder, separator = ",\n\t\t\t\t") { - val fieldName = "${structName}Field${it.name.capitalized()}" - val get = if(it.type.isString() || it.type.isArray()) - "asSlice($fieldName)" - else - "$fieldName.get(this, 0L) as ${it.type.toKotlinForeignType()}" - - castFromNative(it.type, get, dealloc = false, deallocContent = false, useArena = false) + append("\n\t\tprivate fun toJvmDictionary${dictionary.name.capitalized()}(of: MemorySegment)") + append(" = of.reinterpret($layout.size).run { ${dictionary.name}(") + + fields.forEachIndexed { i, it -> + val get = "get(${it.type.toForeignType()}, $layout[$i])" + val value = castFromNative(it.type, get) + append("\n\t\t\t$value,") } - append("\n\t\t\t)\n\t\t}.also { if (dealloc) ForeignUtils.freeHandle.invoke(of) }\n") + append("\n\t\t) }\n") } @@ -125,7 +131,7 @@ class KotlinJvmForeignPrinter( append("${indent}\tprivate val handle") append(function.name.capitalized()) - append($$" = ForeignUtils.lookup(\"${prefix}") + append($$" = ForeignUtils.lookup(handle, \"${prefix}") append(function.name) if(isCriticalAlt) append("_") @@ -135,163 +141,184 @@ class KotlinJvmForeignPrinter( val args = arrayListOf(function.type.toForeignType()) args += function.args.flatMap { - if(isCriticalAlt && (it.type.isString() || it.type.isArray()) && !it.type.isDictionaryArray() && !it.type.isStringArray()) - listOf("ForeignUtils.C_ADDRESS", "ForeignUtils.C_INT") - else listOf(it.type.toForeignType()) + when { + isCriticalAlt && it.type.isString() -> listOf("ValueLayout.ADDRESS", "ValueLayout.JAVA_INT", "ValueLayout.JAVA_INT") + isCriticalAlt && it.type.isArray() -> listOf("ValueLayout.ADDRESS", "ValueLayout.JAVA_INT") + else -> listOf(it.type.toForeignType()) + } } args.joinTo(builder) append(")\n") } private fun printFunctionCall(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { - append("\n${indent}\t") - printFunctionHeader(builder, function, - isOverride = true, - forcePrintVoid = true - ) - append(" = ") - val useArena = !function.isCritical() && ( - function.type.isString() || function.type.isArray() || - function.args.any { !it.type.isDictionary() && !it.type.isDictionaryArray() && (it.type.isString() || it.type.isArray() || it.isDealloc()) }) + val useArena = function.args.any { + (it.type.isString() && !function.isCritical()) || + (it.type.isArray() && !function.isCritical()) || + it.type.isDictionary() || + it.type.isCallback() + } - if(useArena) - append("ForeignArena().use { arena ->\n\t\t") - else append("\n${indent}\t\t") + val transforms = function.args.mapNotNull { + when { + function.isCritical() && it.type.isString() -> + "\n${indent}\t\tval _bytes_${it.name} = ${it.name}.toByteArray()" + function.isCritical() && it.type.isBooleanArray() -> + "\n${indent}\t\tval _bytes_${it.name} = ByteArray(${it.name}.size) { if(${it.name}[it]) 1 else 0 }" + function.isCritical() && it.type.isEnumArray() -> + "\n${indent}\t\tval _ints_${it.name} = IntArray(${it.name}.size) { ${it.name}[it].ordinal }" + else -> null + } + } + + val args = function.args.flatMap { + when { + function.isCritical() && it.type.isString() -> + listOf("MemorySegment.ofArray(_bytes_${it.name})", "${it.name}.length", "_bytes_${it.name}.size") + function.isCritical() && it.type.isBooleanArray() -> + listOf("MemorySegment.ofArray(_bytes_${it.name})", "${it.name}.size") + function.isCritical() && it.type.isEnumArray() -> + listOf("MemorySegment.ofArray(_ints_${it.name})", "${it.name}.size") + function.isCritical() && it.type.isArray() -> + listOf("MemorySegment.ofArray(${it.name})", "${it.name}.size") + else -> + listOf(castToNative(it.type, it.name, useArena = useArena)) + } + }.joinToString() - val type = function.type.toKotlinForeignType() + val deallocFunc = if(function.isDealloc()) + freeFuncFor(function.type, "_result_native") + else null - val args = arrayListOf() - if(function.type.isString() || function.type.isArray()) - args += "arena.heap as SegmentAllocator" + val call = "(handle${function.name.capitalized()}.invokeExact($args) as ${function.type.toKotlinForeignType()})" - args += function.args.flatMap { - val casted = castToNative(it.type, it.name, function.isCritical(), it.isDealloc(), useArena, releasable = "false") + // === Print === - if(it.type.isString() && function.isCritical()) - listOf(casted, "${it.name}.length") - else if(it.type.isArray() && !it.type.isDictionaryArray() && !it.type.isStringArray() && function.isCritical()) - listOf(casted, "${it.name}.size") - else listOf(casted) + append("\n${indent}\t") + printFunctionHeader(builder, function, isOverride = true) + + when { + useArena -> append(" = Arena.ofConfined().use { arena ->") + function.isDealloc() || transforms.isNotEmpty() -> append(" {") + else -> append(" = ") } - val call = "(handle${function.name.capitalized()}.invokeExact(${args.joinToString()}) as $type)" - append(castFromNative(function.type, call, function.isDealloc(), function.isDeallocContent(), useArena)) + transforms.joinTo(builder, separator = "") + append("\n$indent\t\t") + + if(deallocFunc != null) { + append("val _result_native = $call") + append("\n$indent\t\t") + append("val _result_jvm = ${castFromNative(function.type, "_result_native")}") + append("\n$indent\t\t") + append(deallocFunc) - if(useArena) - append("\n\t}") + if(function.type !is ResolvedIdlType.Void) { + append("\n$indent\t\t") + if(useArena) append("_result_jvm") + else append("return _result_jvm") + } + } else { + if(!useArena && transforms.isNotEmpty()) + append("return ") + append(castFromNative(function.type, call)) + } + + if(useArena || deallocFunc != null || transforms.isNotEmpty()) + append("\n$indent\t}") append("\n") } private fun printCallbackInvoke(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - val args = listOf("_callback: MemorySegment") + - callback.args.map { "${it.name}: ${it.type.toKotlinForeignType()}" } - - val lambdaArgs = callback.args.map { castFromNative(it.type, it.name, it.isDealloc(), it.isDeallocContent(), false) } + val args = buildList { + add("_callback: MemorySegment") + callback.args.mapTo(this) { "${it.name}: ${it.type.toKotlinForeignType()}" } + }.joinToString() + val lambdaArgs = callback.args.joinToString { castFromNative(it.type, it.name) } val type = callback.type.toKotlinForeignType() - append("\n\t\t@JvmStatic fun invoke") - append(callback.name) - append("(") - args.joinTo(builder) - append("): ") - append(type) - append(" =\n\t\t\t") - - // body - val call = StringBuilder().apply { - append("(ForeignUtils.callbacks[_callback.address()] as ") - append(callback.name) - append(")(") - lambdaArgs.joinTo(this) - append(")") - } + append("\n\t\t@JvmStatic fun invoke${callback.name}($args): $type =\n\t\t\t") + + val call = "(ForeignUtils.callbacks[_callback.address()] as ${callback.name})($lambdaArgs)" - append(castToNative(callback.type, call.toString(), critical = false, dealloc = false, useArena = false, releasable = "true")) + append(castToNative(callback.type, call, useArena = false)) append("\n") } - private fun printCallbackMethodHandle(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - append("\n\t\tprivate val methodHandle") + private fun printCallbackUpcall(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { + append("\n\t\tprivate val upcall") append(callback.name) - append(" = MethodHandles.lookup().findStatic(\n\t\t\t") - append(name) - append("::class.java,\n\t\t\t") - append("\"invoke") - append(callback.name) - append("\",\n\t\t\t") + append(" = ForeignUtils.upcall(\n\t\t\t") + + // lookup + append("lookup, \"invoke${callback.name}\",\n\t\t\t") append("MethodType.methodType(") val returnType = if(callback.type is ResolvedIdlType.Void) "Void::class.javaPrimitiveType" else "${callback.type.toKotlinForeignType()}::class.java" - val argClasses = listOf(returnType, "MemorySegment::class.java") + - callback.args.map { "${it.type.toKotlinForeignType()}::class.java" } + val argClasses = buildList { + add(returnType) + add("MemorySegment::class.java") + callback.args.mapTo(this) { "${it.type.toKotlinForeignType()}::class.java" } + } argClasses.joinTo(builder) - append(")\n\t\t)\n") - } + append("),\n\t\t\t") + + // desc - private fun printCallbackDesc(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - append("\n\t\tprivate val methodDesc") - append(callback.name) if(callback.type is ResolvedIdlType.Void) - append(" = FunctionDescriptor.ofVoid(") + append("FunctionDescriptor.ofVoid(") else - append(" = FunctionDescriptor.of(").append(callback.type.toForeignType()).append(", ") + append("FunctionDescriptor.of(").append(callback.type.toForeignType()).append(", ") - val args = listOf("ValueLayout.ADDRESS") + - callback.args.map { it.type.toForeignType() } - - args.joinTo(builder) - append(")") + buildList { + add("ValueLayout.ADDRESS") + callback.args.mapTo(this) { it.type.toForeignType() } + }.joinTo(builder) + append(")\n\t\t)\n") } - private fun printCallbackToNative(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - append("\n\t\tfun ") - append("toNativeCallback") - append(callback.name) - append("(c: ") - append(callback.name) - append("): MemorySegment =\n\t\t\t") - append("ForeignUtils.createCallback(c, methodHandle") - append(callback.name) - append(", methodDesc") - append(callback.name) - append(")\n") + private fun freeFuncFor( + type: ResolvedIdlType, + content: String + ) = when { + type.isString() -> "handleKStringFree.invoke($content)" + type.isArray() -> (type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> "handleK${declaration.kind.simpleName()}ArrayFree.invoke($content)" + is ResolvedIdlEnum -> "handleKIntArrayFree.invoke($content)" + is ResolvedIdlDictionary -> "handleKArrayFree.invoke($content, address${declaration.name}Free)" + else -> throw UnsupportedOperationException(type.toString()) + } + } + type.isCallback() -> "ForeignUtils.callbackFree($content)" + type.isDictionary() -> "handle${(type as ResolvedIdlType.Default).declaration.name.capitalized()}Free.invoke($content)" + else -> null } - private fun castFromNative(type: ResolvedIdlType, content: String, dealloc: Boolean, deallocContent: Boolean, useArena: Boolean): String = when(type) { + private fun castFromNative(type: ResolvedIdlType, content: String): String = when(type) { is ResolvedIdlType.Void -> content is ResolvedIdlType.Default -> when(type.declaration) { is BuiltinIdlDeclaration -> when((type.declaration as BuiltinIdlDeclaration).kind) { - WebIDLBuiltinKind.STRING -> - if(useArena) "arena.toJvmString($content, $dealloc)" - else "ForeignUtils.toJvmString($content, $dealloc)" + WebIDLBuiltinKind.STRING -> "ForeignUtils.toJvmString($content)" WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - if (useArena) "arena.toJvm${name}Array($content, $dealloc)" - else "ForeignUtils.toJvm${name}Array($content, $dealloc)" - } - is ResolvedIdlEnum -> { - if (useArena) "arena.toJvmEnumArray($content, $dealloc, ${declaration.name}::class.java)" - else "ForeignUtils.toJvmEnumArray($content, $dealloc, ${declaration.name}::class.java)" - } - is ResolvedIdlDictionary -> "ForeignUtils.toJvmArray($content, ::toJvmDictionary${declaration.name}, ${declaration.name}::class.java, $dealloc, $deallocContent)" + is BuiltinIdlDeclaration -> "ForeignUtils.toJvm${declaration.kind.simpleName()}Array($content)" + is ResolvedIdlEnum -> "ForeignUtils.toJvmEnumArray($content, ${declaration.name}::class.java)" + is ResolvedIdlDictionary -> "ForeignUtils.toJvmArray($content, ::toJvmDictionary${declaration.name}, ${declaration.name}::class.java)" else -> throw UnsupportedOperationException(type.toString()) } } else -> content } - is ResolvedIdlCallbackFunction -> - if(useArena) "arena.toJvmCallback($content, $dealloc)" - else "ForeignUtils.toJvmCallback($content, $dealloc)" + is ResolvedIdlCallbackFunction -> "ForeignUtils.toJvmCallback($content)" is ResolvedIdlEnum -> "${type.declaration.name}.entries[$content]" - is ResolvedIdlDictionary -> "toJvmDictionary${type.declaration.name}(${content}, $dealloc)" + is ResolvedIdlDictionary -> "toJvmDictionary${type.declaration.name}(${content})" else -> throw UnsupportedOperationException(type.toString()) } else -> throw UnsupportedOperationException(type.toString()) @@ -300,45 +327,40 @@ class KotlinJvmForeignPrinter( private fun castToNative( type: ResolvedIdlType, content: String, - critical: Boolean, - dealloc: Boolean, useArena: Boolean, - releasable: String, - slice: String? = null ): String { - val slice = if(slice != null) ", $slice" else "" return when (type) { is ResolvedIdlType.Void -> content is ResolvedIdlType.Default -> when (type.declaration) { is BuiltinIdlDeclaration -> when ((type.declaration as BuiltinIdlDeclaration).kind) { WebIDLBuiltinKind.STRING -> - if (critical) "ForeignUtils.toNativeHeapString($content)" - else if (useArena) "arena.toNativeString($content)" - else "ForeignUtils.toNativeString($content, $releasable$slice)" + if (useArena) "ForeignUtils.toNativeStringOnArena(arena, $content)" + else "ForeignUtils.toNativeString($content)" WebIDLBuiltinKind.LIST -> type.firstParam { _, declaration -> when (declaration) { is BuiltinIdlDeclaration -> { val name = declaration.kind.simpleName() - if (critical) "ForeignUtils.toNativeHeap${name}Array($content)" - else if (useArena) "arena.toNative${name}Array($content)" - else "ForeignUtils.toNative${name}Array($content, $releasable$slice)" - } - is ResolvedIdlEnum -> { - if (critical) "ForeignUtils.toNativeHeapEnumArray($content)" - else if (useArena) "arena.toNativeEnumArray($content)" - else "ForeignUtils.toNativeEnumArray($content, $releasable$slice)" + if (useArena) "ForeignUtils.toNative${name}ArrayOnArena(arena, $content)" + else "ForeignUtils.toNative${name}Array($content)" } - is ResolvedIdlDictionary -> "ForeignUtils.toNativeArray($content, $releasable, ::toNativeDictionary${declaration.name}$slice)" + is ResolvedIdlEnum -> + if (useArena) "ForeignUtils.toNativeEnumArrayOnArena(arena, $content)" + else "ForeignUtils.toNativeEnumArray($content)" + is ResolvedIdlDictionary -> + if (useArena) "ForeignUtils.toNativeArrayOnArena(arena, $content, ::toNativeDictionary${declaration.name}OnArena)" + else "ForeignUtils.toNativeArray($content, ::toNativeDictionary${declaration.name})" else -> throw UnsupportedOperationException(type.toString()) } } else -> content } is ResolvedIdlCallbackFunction -> - if (dealloc) "arena.callback(toNativeCallback${type.declaration.name}($content))" - else "toNativeCallback${type.declaration.name}($content)" + if (useArena) "ForeignUtils.createCallbackOnArena(arena, $content, upcall${type.declaration.name})" + else "ForeignUtils.createCallback($content, upcall${type.declaration.name})" is ResolvedIdlEnum -> "$content.ordinal" - is ResolvedIdlDictionary -> "toNativeDictionary${type.declaration.name}($content, $releasable)" + is ResolvedIdlDictionary -> + if (useArena) "toNativeDictionary${type.declaration.name}OnArena(arena, $content)" + else "toNativeDictionary${type.declaration.name}($content)" else -> throw UnsupportedOperationException(type.toString()) } else -> throw UnsupportedOperationException(type.toString()) @@ -350,20 +372,20 @@ class KotlinJvmForeignPrinter( is ResolvedIdlType.Void -> "null" is ResolvedIdlType.Default -> when(declaration) { is BuiltinIdlDeclaration -> when(val a = (declaration as BuiltinIdlDeclaration).kind) { - WebIDLBuiltinKind.CHAR -> "ForeignUtils.C_CHAR" - WebIDLBuiltinKind.BOOLEAN -> "ForeignUtils.C_BOOLEAN" - WebIDLBuiltinKind.BYTE -> "ForeignUtils.C_BYTE" - WebIDLBuiltinKind.SHORT -> "ForeignUtils.C_SHORT" - WebIDLBuiltinKind.INT -> "ForeignUtils.C_INT" - WebIDLBuiltinKind.LONG -> "ForeignUtils.C_LONG" - WebIDLBuiltinKind.FLOAT -> "ForeignUtils.C_FLOAT" - WebIDLBuiltinKind.DOUBLE -> "ForeignUtils.C_DOUBLE" - WebIDLBuiltinKind.STRING -> "ForeignUtils.STRING_STRUCT" - WebIDLBuiltinKind.LIST -> "ForeignUtils.ARRAY_STRUCT" + WebIDLBuiltinKind.CHAR -> "ValueLayout.JAVA_CHAR" + WebIDLBuiltinKind.BOOLEAN -> "ValueLayout.JAVA_BOOLEAN" + WebIDLBuiltinKind.BYTE -> "ValueLayout.JAVA_BYTE" + WebIDLBuiltinKind.SHORT -> "ValueLayout.JAVA_SHORT" + WebIDLBuiltinKind.INT -> "ValueLayout.JAVA_INT" + WebIDLBuiltinKind.LONG -> "ValueLayout.JAVA_LONG" + WebIDLBuiltinKind.FLOAT -> "ValueLayout.JAVA_FLOAT" + WebIDLBuiltinKind.DOUBLE -> "ValueLayout.JAVA_DOUBLE" + WebIDLBuiltinKind.STRING -> "ValueLayout.ADDRESS" + WebIDLBuiltinKind.LIST -> "ValueLayout.ADDRESS" else -> throw UnsupportedOperationException(a.toString()) } - is ResolvedIdlEnum -> "ForeignUtils.C_INT" - else -> "ForeignUtils.C_ADDRESS" + is ResolvedIdlEnum -> "ValueLayout.JAVA_INT" + else -> "ValueLayout.ADDRESS" } } } \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmJniPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmJniPrinter.kt index 1c1ae93..22220b3 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmJniPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/kotlin/jvm/KotlinJvmJniPrinter.kt @@ -22,19 +22,15 @@ class KotlinJvmJniPrinter( builder.append("class ") builder.append(name) + builder.append("(libraryPath: String)") if(parentClass != null) builder.append(": $parentClass") builder.append(" {\n") if(isAndroid) { // Static functions - builder.append("${indent}\tcompanion object {\n") - builder.append(""" - @JvmStatic external fun JNILoad(${if(isAndroidCriticalEnabled) "critical: Boolean" else ""}) - init { - JNILoad(${if(isAndroidCriticalEnabled) "supportsCritical" else ""}) - } - """.replaceIndent("$indent\t\t")) + builder.append("$indent\tcompanion object {\n") + builder.append("$indent\t\t@JvmStatic external fun JNILoad(${if(isAndroidCriticalEnabled) "critical: Boolean" else ""})") builder.append("\n") idl.globalOperators().forEach { function -> @@ -63,15 +59,27 @@ class KotlinJvmJniPrinter( builder.append("\n") } builder.append("${indent}\t}\n") + builder.append(""" + init { + System.load(libraryPath); + JNILoad(${if (isAndroidCriticalEnabled) "supportsCritical" else ""}) + } + """.replaceIndent("$indent\t")) + builder.append("\n") } else { // Instance methods builder.append(""" companion object { @JvmStatic external fun JNILoad() - init { - JNILoad() - } } + init { + System.load(libraryPath) + JNILoad() + } + + override fun _address(name: String): Long = + NativeKtUtils.findAddress(name) + """.replaceIndent("$indent\t")) builder.append("\n") diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/CStructLayout.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/CStructLayout.kt deleted file mode 100644 index e94d179..0000000 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/CStructLayout.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.huskerdev.nativekt.utils - -import com.huskerdev.webidl.resolver.ResolvedIdlDictionary -import com.huskerdev.webidl.resolver.ResolvedIdlType -import kotlin.math.max - -class CStructLayout( - types: List, - x86: Boolean -) { - var size: Int = 0 - private set - - var postPadding: Int = 0 - private set - - private val address = arrayListOf() - private val padding = arrayListOf() - - constructor( - dictionary: ResolvedIdlDictionary, - x86: Boolean - ): this(dictionary.allFields().map { it.type }, x86) - - init { - var maxAlignment = 0 - types.forEach { type -> - val alignment = type.getAlignment(x86) - val typeSize = if(type.isString() || type.isArray()) { - if (x86) 12 else 16 - } else alignment - - val rem = size % alignment - val padding = if (rem == 0) 0 else alignment - rem - - maxAlignment = max(maxAlignment, alignment) - - this.padding += padding - this.address += size + padding - size += padding + typeSize - } - - val rem = size % maxAlignment - postPadding = if (rem == 0) 0 else maxAlignment - rem - size += postPadding - } - - fun addressOf(i: Int) = address[i] - - fun paddingOf(i: Int) = padding[i] -} \ No newline at end of file diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/PrinterUtils.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/PrinterUtils.kt index df1c919..9df7fa1 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/PrinterUtils.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/PrinterUtils.kt @@ -120,7 +120,8 @@ fun ResolvedIdlType.toCType( fun ResolvedIdlType.toCDefType( longPtr: Boolean = false, - enumAsInt: Boolean = false + enumAsInt: Boolean = false, + ptr: Boolean = true ): String = when(this) { is ResolvedIdlType.Void -> "void" is ResolvedIdlType.Default -> when(declaration) { @@ -133,31 +134,31 @@ fun ResolvedIdlType.toCDefType( WebIDLBuiltinKind.LONG -> "KLong${if (longPtr) "*" else ""}" WebIDLBuiltinKind.FLOAT -> "KFloat" WebIDLBuiltinKind.DOUBLE -> "KDouble" - WebIDLBuiltinKind.STRING -> "KString${if (longPtr) "*" else ""}" + WebIDLBuiltinKind.STRING -> "KString${if(ptr) "*" else ""}" WebIDLBuiltinKind.LIST -> firstParam { _, declaration -> when (declaration) { is BuiltinIdlDeclaration -> when (declaration.kind) { - WebIDLBuiltinKind.CHAR -> "KCharArray" - WebIDLBuiltinKind.BOOLEAN -> "KBooleanArray" - WebIDLBuiltinKind.BYTE -> "KByteArray" - WebIDLBuiltinKind.SHORT -> "KShortArray" - WebIDLBuiltinKind.INT -> "KIntArray" - WebIDLBuiltinKind.LONG -> "KLongArray" - WebIDLBuiltinKind.FLOAT -> "KFloatArray" - WebIDLBuiltinKind.DOUBLE -> "KDoubleArray" - WebIDLBuiltinKind.STRING -> "KStringArray" + WebIDLBuiltinKind.CHAR -> "KCharArray${if(ptr) "*" else ""}" + WebIDLBuiltinKind.BOOLEAN -> "KBooleanArray${if(ptr) "*" else ""}" + WebIDLBuiltinKind.BYTE -> "KByteArray${if(ptr) "*" else ""}" + WebIDLBuiltinKind.SHORT -> "KShortArray${if(ptr) "*" else ""}" + WebIDLBuiltinKind.INT -> "KIntArray${if(ptr) "*" else ""}" + WebIDLBuiltinKind.LONG -> "KLongArray${if(ptr) "*" else ""}" + WebIDLBuiltinKind.FLOAT -> "KFloatArray${if(ptr) "*" else ""}" + WebIDLBuiltinKind.DOUBLE -> "KDoubleArray${if(ptr) "*" else ""}" + WebIDLBuiltinKind.STRING -> "KStringArray${if(ptr) "*" else ""}" else -> throw UnsupportedOperationException() } - is ResolvedIdlEnum -> "KIntArray" - is ResolvedIdlDictionary -> "KArray" + is ResolvedIdlEnum -> "KIntArray${if(ptr) "*" else ""}" + is ResolvedIdlDictionary -> "KArray${if(ptr) "*" else ""}" else -> throw UnsupportedOperationException(declaration.name) } } else -> throw UnsupportedOperationException(toString()) } is ResolvedIdlEnum -> if(enumAsInt) "KInt" else declaration.name - is ResolvedIdlCallbackFunction -> "${declaration.name}*" - is ResolvedIdlDictionary -> "${declaration.name}*" + is ResolvedIdlCallbackFunction -> "${declaration.name}${if(ptr) "*" else ""}" + is ResolvedIdlDictionary -> "${declaration.name}${if(ptr) "*" else ""}" else -> throw UnsupportedOperationException(declaration.name) } else -> throw UnsupportedOperationException(toString()) @@ -266,6 +267,20 @@ fun ResolvedIdlType.getAlignment( } +fun ResolvedIdlType.isPrimitive(): Boolean { + if (this !is ResolvedIdlType.Default || + declaration !is BuiltinIdlDeclaration) return false + return (declaration as BuiltinIdlDeclaration).kind in setOf( + WebIDLBuiltinKind.CHAR, + WebIDLBuiltinKind.BOOLEAN, + WebIDLBuiltinKind.BYTE, + WebIDLBuiltinKind.SHORT, + WebIDLBuiltinKind.INT, + WebIDLBuiltinKind.LONG, + WebIDLBuiltinKind.FLOAT, + WebIDLBuiltinKind.DOUBLE, + ) +} fun ResolvedIdlType.isString(): Boolean { if (this !is ResolvedIdlType.Default || @@ -297,6 +312,30 @@ fun ResolvedIdlType.isFloat(): Boolean { return (declaration as BuiltinIdlDeclaration).kind == WebIDLBuiltinKind.FLOAT } +fun ResolvedIdlType.isBoolean(): Boolean { + if (this !is ResolvedIdlType.Default || + declaration !is BuiltinIdlDeclaration) return false + return (declaration as BuiltinIdlDeclaration).kind == WebIDLBuiltinKind.BOOLEAN +} + +fun ResolvedIdlType.isShort(): Boolean { + if (this !is ResolvedIdlType.Default || + declaration !is BuiltinIdlDeclaration) return false + return (declaration as BuiltinIdlDeclaration).kind == WebIDLBuiltinKind.SHORT +} + +fun ResolvedIdlType.isByte(): Boolean { + if (this !is ResolvedIdlType.Default || + declaration !is BuiltinIdlDeclaration) return false + return (declaration as BuiltinIdlDeclaration).kind == WebIDLBuiltinKind.BYTE +} + +fun ResolvedIdlType.isChar(): Boolean { + if (this !is ResolvedIdlType.Default || + declaration !is BuiltinIdlDeclaration) return false + return (declaration as BuiltinIdlDeclaration).kind == WebIDLBuiltinKind.CHAR +} + fun ResolvedIdlType.isArray(): Boolean { if (this !is ResolvedIdlType.Default || declaration !is BuiltinIdlDeclaration) return false @@ -336,6 +375,14 @@ fun ResolvedIdlType.isDictionaryArray(): Boolean { return firstParam { type, _ -> type.isDictionary() } } +fun ResolvedIdlType.isBooleanArray(): Boolean { + if (this !is ResolvedIdlType.Default || + declaration !is BuiltinIdlDeclaration) return false + if((declaration as BuiltinIdlDeclaration).kind != WebIDLBuiltinKind.LIST) + return false + return firstParam { type, _ -> type.isBoolean() } +} + fun ResolvedIdlOperation.isCritical(): Boolean = this.attributes.any { it is IdlExtendedAttribute.NoArgs && it.name == "Critical" @@ -346,11 +393,6 @@ fun IdlAttributedHolder.isDealloc(): Boolean = it is IdlExtendedAttribute.NoArgs && it.name == "Dealloc" } -fun IdlAttributedHolder.isDeallocContent(): Boolean = - this.attributes.any { - it is IdlExtendedAttribute.NoArgs && it.name == "DeallocContent" - } - fun ResolvedIdlOperation.isCriticalCapable(): Boolean = !type.isArray() && !type.isString() && !type.isDictionary() && !args.any { it.type.isStringArray() || it.type.isDictionaryArray() } @@ -422,11 +464,11 @@ fun printFunctionHeader( append(": ") append(arg.type.toKotlinType(stringAsBytes, callbackAsAny, enumAsInt, dictionaryAsAny)) - if((stringAsBytes && arg.type.isString()) || (arraysLen && arg.type.isArray())) { - append(", __len_") - append(arg.name) - append(": Int") - } + if(stringAsBytes && arg.type.isString()) + append(", __len_${arg.name}: Int, __size_${arg.name}: Int") + + if(arraysLen && arg.type.isArray()) + append(", __len_${arg.name}: Int") if(index != function.args.lastIndex) append(", ") diff --git a/modules/runtime/build.gradle.kts b/modules/runtime/build.gradle.kts index 2dce301..84fb2c0 100644 --- a/modules/runtime/build.gradle.kts +++ b/modules/runtime/build.gradle.kts @@ -1,7 +1,9 @@ @file:OptIn(ExperimentalWasmDsl::class) +import org.apache.tools.ant.taskdefs.condition.Os import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget plugins { alias(libs.plugins.kotlin.multiplatform) @@ -48,27 +50,37 @@ kotlin { linuxX64() linuxArm64() - macosX64() - macosArm64() + if(Os.isFamily(Os.FAMILY_MAC)) { + //macosX64() + macosArm64() - iosX64() - iosArm64() - iosSimulatorArm64() + iosX64() + iosArm64() + iosSimulatorArm64() - watchosX64() - watchosArm32() - watchosArm64() - watchosDeviceArm64() - watchosSimulatorArm64() + //watchosX64() + watchosArm32() + watchosArm64() + watchosDeviceArm64() + watchosSimulatorArm64() - tvosX64() - tvosArm64() - tvosSimulatorArm64() + //tvosX64() + tvosArm64() + tvosSimulatorArm64() + } + /* androidNativeX64() androidNativeX86() androidNativeArm32() androidNativeArm64() + */ + + targets.withType().configureEach { + compilations.getByName("main") { + cinterops.register("api") + } + } } java { @@ -84,6 +96,7 @@ tasks.withType().configureEach { "--add-exports", "jdk.internal.vm.ci/jdk.vm.ci.hotspot=ALL-UNNAMED", "--add-exports", "jdk.internal.vm.ci/jdk.vm.ci.meta=ALL-UNNAMED", "--add-exports", "jdk.internal.vm.ci/jdk.vm.ci.runtime=ALL-UNNAMED", + "--add-exports", "java.base/jdk.internal.foreign=ALL-UNNAMED", )) } @@ -95,7 +108,8 @@ tasks.withType().configureEach { "jdk.internal.vm.ci/jdk.vm.ci.code.site=ALL-UNNAMED", "jdk.internal.vm.ci/jdk.vm.ci.hotspot=ALL-UNNAMED", "jdk.internal.vm.ci/jdk.vm.ci.meta=ALL-UNNAMED", - "jdk.internal.vm.ci/jdk.vm.ci.runtime=ALL-UNNAMED" + "jdk.internal.vm.ci/jdk.vm.ci.runtime=ALL-UNNAMED", + "java.base/jdk.internal.foreign=ALL-UNNAMED", ) } } diff --git a/modules/runtime/src/androidNativeArm32Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeArm32.kt b/modules/runtime/src/androidNativeArm32Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeArm32.kt deleted file mode 100644 index 0e391a1..0000000 --- a/modules/runtime/src/androidNativeArm32Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeArm32.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size)!! - -actual inline fun allocStruct() = - malloc(sizeOf().toUInt())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/androidNativeArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeArm64.kt b/modules/runtime/src/androidNativeArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeArm64.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/androidNativeArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeArm64.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/androidNativeX64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeX64.kt b/modules/runtime/src/androidNativeX64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeX64.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/androidNativeX64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeX64.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/androidNativeX86Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeX86.kt b/modules/runtime/src/androidNativeX86Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeX86.kt deleted file mode 100644 index 0e391a1..0000000 --- a/modules/runtime/src/androidNativeX86Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeX86.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size)!! - -actual inline fun allocStruct() = - malloc(sizeOf().toUInt())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/commonMain/kotlin/com/huskerdev/nativekt/OS.kt b/modules/runtime/src/commonMain/kotlin/com/huskerdev/nativekt/OS.kt index 086214a..c0b6e90 100644 --- a/modules/runtime/src/commonMain/kotlin/com/huskerdev/nativekt/OS.kt +++ b/modules/runtime/src/commonMain/kotlin/com/huskerdev/nativekt/OS.kt @@ -32,14 +32,16 @@ enum class OS( } } -enum class Arch { - X86, - X64, - ARM32, - ARM64, - RISCV32, - RISCV64, - UNKNOWN +enum class Arch( + val ptr64: Boolean +) { + X86(false), + X64(true), + ARM32(false), + ARM64(true), + RISCV32(false), + RISCV64(true), + UNKNOWN(false) ; companion object { @JvmStatic fun current() = currentArch() diff --git a/modules/runtime/src/iosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.ios.kt b/modules/runtime/src/iosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.ios.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/iosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.ios.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/NativeKtUtils.java b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/NativeKtUtils.java index 58ea041..1e7effc 100644 --- a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/NativeKtUtils.java +++ b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/NativeKtUtils.java @@ -93,10 +93,10 @@ public static Invoker getInvoker(){ * * @param baseName base library name, without prefix, extension and arch (e.g. 'some') * @param macosUniversal indicates if macOS lib is universal (fat) - * @return Full library file name (e.g. libsome-x64.dll) + * @return Full library file path (e.g. C:/libsome-x64.dll) * @throws IOException If file not found in resources */ - public static String loadLibrary(String baseName, boolean macosUniversal) throws IOException { + public static String resolveLibraryFile(String baseName, boolean macosUniversal) throws IOException { // Get OS OS os = OS.current(); @@ -125,10 +125,7 @@ public static String loadLibrary(String baseName, boolean macosUniversal) throws Files.copy(input, libPath.toPath(), StandardCopyOption.REPLACE_EXISTING); } - // Load library - System.load(libPath.getAbsolutePath()); - - return fileName; + return libPath.getAbsolutePath(); } /** diff --git a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/CStructLayout.kt b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/CStructLayout.kt new file mode 100644 index 0000000..b13bae2 --- /dev/null +++ b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/CStructLayout.kt @@ -0,0 +1,35 @@ +package com.huskerdev.nativekt.jvm.foreign + +import java.lang.foreign.ValueLayout +import kotlin.math.max + +class CStructLayout( + vararg types: ValueLayout +) { + var size: Long = 0 + private set + + private val address = arrayListOf() + + init { + var maxAlignment = 0 + types.forEach { type -> + val typeSize = type.byteSize().toInt() + + val rem = (size % typeSize).toInt() + val padding = if (rem == 0) 0 else typeSize - rem + + maxAlignment = max(maxAlignment, typeSize) + + this.address += size + padding + size += padding + typeSize + } + + val rem = (size % maxAlignment).toInt() + val postPadding = if (rem == 0) 0 else maxAlignment - rem + size += postPadding + } + + operator fun get(i: Int) = address[i] + +} \ No newline at end of file diff --git a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/ForeignArena.java b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/ForeignArena.java deleted file mode 100644 index 1696117..0000000 --- a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/ForeignArena.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.huskerdev.nativekt.jvm.foreign; - -import java.io.Closeable; -import java.lang.foreign.*; -import java.util.ArrayList; - -@SuppressWarnings("unused") -public class ForeignArena implements Closeable { - - public final Arena heap = Arena.ofConfined(); - private final ArrayList allocated = new ArrayList<>(); - private final ArrayList callbacks = new ArrayList<>(); - - private boolean notContains(long address) { - for(Long segment : allocated) - if(segment == address) - return false; - return true; - } - - // String - - public MemorySegment toNativeString(String of) { - MemorySegment struct = heap.allocate(ForeignUtils.STRING_STRUCT); - MemorySegment data = heap.allocateFrom(of); - ForeignUtils.stringDataVarHandle.set(struct, 0L, data); - ForeignUtils.stringLengthVarHandle.set(struct, 0L, of.length()); - ForeignUtils.stringReleasableVarHandle.set(struct, 0L, false); - ForeignUtils.stringReleasedVarHandle.set(struct, 0L, false); - - allocated.add(data.address()); - return struct; - } - - public String toJvmString(MemorySegment struct, boolean dealloc) throws Throwable { - MemorySegment data = (MemorySegment)ForeignUtils.stringDataVarHandle.get(struct, 0L); - return ForeignUtils.toJvmString(struct, dealloc && notContains(data.address())); - } - - // Array: char - - public MemorySegment toNativeCharArray(char[] arr) { - MemorySegment struct = ForeignUtils.toNativeCharArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public char[] toJvmCharArray(MemorySegment struct, boolean dealloc) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmCharArray(struct, dealloc && notContains(address)); - } - - // Array: boolean - - public MemorySegment toNativeBooleanArray(boolean[] arr) { - MemorySegment struct = ForeignUtils.toNativeBooleanArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public boolean[] toJvmBooleanArray(MemorySegment struct, boolean dealloc) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmBooleanArray(struct, dealloc && notContains(address)); - } - - // Array: byte - - public MemorySegment toNativeByteArray(byte[] arr) { - MemorySegment struct = ForeignUtils.toNativeByteArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public byte[] toJvmByteArray(MemorySegment struct, boolean dealloc) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmByteArray(struct, dealloc && notContains(address)); - } - - // Array: short - - public MemorySegment toNativeShortArray(short[] arr) { - MemorySegment struct = ForeignUtils.toNativeShortArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public short[] toJvmShortArray(MemorySegment struct, boolean dealloc) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmShortArray(struct, dealloc && notContains(address)); - } - - // Array: int - - public MemorySegment toNativeIntArray(int[] arr) { - MemorySegment struct = ForeignUtils.toNativeIntArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public int[] toJvmIntArray(MemorySegment struct, boolean dealloc) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmIntArray(struct, dealloc && notContains(address)); - } - - // Array: long - - public MemorySegment toNativeLongArray(long[] arr) { - MemorySegment struct = ForeignUtils.toNativeLongArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public long[] toJvmLongArray(MemorySegment struct, boolean dealloc) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmLongArray(struct, dealloc && notContains(address)); - } - - // Array: float - - public MemorySegment toNativeFloatArray(float[] arr) { - MemorySegment struct = ForeignUtils.toNativeFloatArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public float[] toJvmFloatArray(MemorySegment struct, boolean dealloc) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmFloatArray(struct, dealloc && notContains(address)); - } - - // Array: double - - public MemorySegment toNativeDoubleArray(double[] arr) { - MemorySegment struct = ForeignUtils.toNativeDoubleArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public double[] toJvmDoubleArray(MemorySegment struct, boolean dealloc) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmDoubleArray(struct, dealloc && notContains(address)); - } - - // Array: enum - - public > MemorySegment toNativeEnumArray(T[] arr) { - MemorySegment struct = ForeignUtils.toNativeEnumArray(arr, false, heap, heap); - allocated.add(ForeignUtils.arrayElementsAddress(struct)); - return struct; - } - - public > T[] toJvmEnumArray( - MemorySegment struct, - boolean dealloc, - Class enumClass - ) throws Throwable { - long address = ForeignUtils.arrayElementsAddress(struct); - return ForeignUtils.toJvmEnumArray(struct, dealloc && notContains(address), enumClass); - } - - // Callbacks - - public MemorySegment callback(MemorySegment callback) { - allocated.add(callback.address()); - callbacks.add(callback); - return callback; - } - - public T toJvmCallback(MemorySegment segment, boolean dealloc) throws Throwable { - return ForeignUtils.toJvmCallback(segment, dealloc && notContains(segment.address())); - } - - public void close() { - for(MemorySegment callback : callbacks) - ForeignUtils.callbackFree(callback); - heap.close(); - } -} diff --git a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/ForeignUtils.java b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/ForeignUtils.java index 442a126..2628970 100644 --- a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/ForeignUtils.java +++ b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/ForeignUtils.java @@ -1,6 +1,8 @@ package com.huskerdev.nativekt.jvm.foreign; -import kotlin.jvm.functions.Function2; +import com.huskerdev.nativekt.jvm.NativeKtUtils; +import com.huskerdev.nativekt.jvm.jvmci.JVMCIUtils; +import jdk.internal.foreign.MemorySessionImpl; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; @@ -8,203 +10,202 @@ import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; import java.nio.charset.StandardCharsets; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.function.BiFunction; import java.util.function.Function; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static com.huskerdev.nativekt.jvm.NativeKtUtils.addExports; +import static java.lang.foreign.ValueLayout.*; @SuppressWarnings("unused") public class ForeignUtils { + static { + if(NativeKtUtils.isAutoExportEnabled()) { + addExports(JVMCIUtils.class.getModule(), "java.base", new String[]{ + "jdk.internal.foreign" + }); + } + } + private static final Linker linker = Linker.nativeLinker(); - // Types - - public static final ValueLayout C_CHAR = ValueLayout.JAVA_CHAR; - public static final ValueLayout C_BYTE = ValueLayout.JAVA_BYTE; - public static final ValueLayout C_BOOLEAN = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout C_SHORT = ValueLayout.JAVA_SHORT; - public static final ValueLayout C_INT = ValueLayout.JAVA_INT; - public static final ValueLayout C_LONG = ValueLayout.JAVA_LONG; - public static final ValueLayout C_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout C_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final ValueLayout C_ADDRESS = ValueLayout.ADDRESS; - - // KString struct - - public static final StructLayout STRING_STRUCT = MemoryLayout.structLayout( - C_ADDRESS.withName("data"), - C_INT.withName("length"), - C_BOOLEAN.withName("releasable"), - C_BOOLEAN.withName("released"), - MemoryLayout.paddingLayout(2) // align to 16 + // Flags + + public static final byte FLAG_RELEASABLE = 1; + public static final byte FLAG_ON_STACK = 2; + + // Layouts + + private static final CStructLayout layoutString = new CStructLayout( + ADDRESS, JAVA_LONG, JAVA_INT, JAVA_BYTE ); - public static final VarHandle stringDataVarHandle = STRING_STRUCT.varHandle(MemoryLayout.PathElement.groupElement("data")); - public static final VarHandle stringLengthVarHandle = STRING_STRUCT.varHandle(MemoryLayout.PathElement.groupElement("length")); - public static final VarHandle stringReleasableVarHandle = STRING_STRUCT.varHandle(MemoryLayout.PathElement.groupElement("releasable")); - public static final VarHandle stringReleasedVarHandle = STRING_STRUCT.varHandle(MemoryLayout.PathElement.groupElement("released")); - - // KArray struct - - public static final StructLayout ARRAY_STRUCT = MemoryLayout.structLayout( - C_ADDRESS.withName("elements"), - C_INT.withName("size"), - C_BOOLEAN.withName("releasable"), - C_BOOLEAN.withName("released"), - MemoryLayout.paddingLayout(2) // align to 16 + private static final CStructLayout layoutArray = new CStructLayout( + ADDRESS, JAVA_LONG, JAVA_INT, JAVA_BYTE + ); + private static final CStructLayout layoutCallback = new CStructLayout( + JAVA_BYTE, ADDRESS, ADDRESS, ADDRESS, ADDRESS, ADDRESS ); - public static final VarHandle arrayElementsVarHandle = ARRAY_STRUCT.varHandle(MemoryLayout.PathElement.groupElement("elements")); - public static final VarHandle arraySizeVarHandle = ARRAY_STRUCT.varHandle(MemoryLayout.PathElement.groupElement("size")); - public static final VarHandle arrayReleasableVarHandle = ARRAY_STRUCT.varHandle(MemoryLayout.PathElement.groupElement("releasable")); - public static final VarHandle arrayReleasedVarHandle = ARRAY_STRUCT.varHandle(MemoryLayout.PathElement.groupElement("released")); // Callbacks - public static final HashMap callbacks = new HashMap<>(); + public static final ConcurrentMap callbacks = new ConcurrentHashMap<>(); + + private static final MemorySegment callbackClone, callbackEquals, callbackHashCode, callbackFree; - private static final StructLayout callbackStructLayout = MemoryLayout.structLayout( - ValueLayout.ADDRESS.withName("m"), - ValueLayout.ADDRESS.withName("invoke"), - ValueLayout.ADDRESS.withName("free") + // Malloc function + + private static final MethodHandle mallocHandle = linker.downcallHandle( + linker.defaultLookup().find("malloc").orElseThrow(), + FunctionDescriptor.of(ADDRESS, ValueLayout.JAVA_LONG) ); - private static final VarHandle callbackInvokeVarHandle = callbackStructLayout.varHandle(MemoryLayout.PathElement.groupElement("invoke")); - private static final VarHandle callbackFreeVarHandle = callbackStructLayout.varHandle(MemoryLayout.PathElement.groupElement("free")); - private static final MemorySegment callbackFree; + public static MemorySegment malloc(long size) { + try { + return ((MemorySegment) mallocHandle.invoke(size)).reinterpret(size); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } // Free function - public static final MethodHandle freeHandle = linker.downcallHandle( + private static final MethodHandle freeHandle = linker.downcallHandle( linker.defaultLookup().find("free").orElseThrow(), - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS) + FunctionDescriptor.ofVoid(ADDRESS) ); - public static MethodHandle lookup(String name, boolean isCritical, MemoryLayout retType, MemoryLayout... argTypes) { + public static void free(MemorySegment mem) { + try { + freeHandle.invoke(mem); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + // Lookup functions + + public static MemorySegment address(SymbolLookup lookup, String name) { + return lookup.find(name).orElseThrow(); + } + + public static MethodHandle handle(MemorySegment address, boolean isCritical, MemoryLayout retType, MemoryLayout... argTypes) { FunctionDescriptor function = retType == null ? FunctionDescriptor.ofVoid(argTypes) : FunctionDescriptor.of(retType, argTypes); - MemorySegment address = SymbolLookup.loaderLookup().find(name).orElseThrow(); - if(isCritical) return linker.downcallHandle(address, function, Linker.Option.critical(true)); else return linker.downcallHandle(address, function); } + public static MethodHandle lookup(SymbolLookup lookup, String name, boolean isCritical, MemoryLayout retType, MemoryLayout... argTypes) { + return handle(address(lookup, name), isCritical, retType, argTypes); + } + // String - public static MemorySegment toNativeHeapString(String str) { - return MemorySegment.ofArray(str.getBytes()); - } + public static MemorySegment toNativeStringOnArena(Arena arena, String of) { + MemorySegment stringMem = arena.allocateFrom(of); - public static MemorySegment toNativeString(String of, boolean releasable) { - return toNativeString(of, releasable, Arena.ofAuto().allocate(STRING_STRUCT)); + MemorySegment struct = arena.allocate(layoutString.getSize()); + struct.set(ADDRESS, layoutString.get(0), stringMem); + struct.set(JAVA_LONG, layoutString.get(1), stringMem.byteSize() - 1); + struct.set(JAVA_INT, layoutString.get(2), of.length()); + struct.set(JAVA_INT, layoutString.get(3), FLAG_ON_STACK); + return struct; } - public static MemorySegment toNativeString(String of, boolean releasable, MemorySegment struct) { - stringDataVarHandle.set(struct, 0L, Arena.global().allocateFrom(of)); - stringLengthVarHandle.set(struct, 0L, of.length()); - stringReleasableVarHandle.set(struct, 0L, releasable); - stringReleasedVarHandle.set(struct, 0L, false); + public static MemorySegment toNativeString(String of) { + byte[] bytes = of.getBytes(StandardCharsets.UTF_8); + MemorySegment stringMem = malloc(bytes.length); + MemorySegment.copy(bytes, 0, stringMem, JAVA_BYTE, 0, bytes.length); + + MemorySegment struct = malloc(layoutString.getSize()); + struct.set(ADDRESS, layoutString.get(0), stringMem); + struct.set(JAVA_LONG, layoutString.get(1), bytes.length); + struct.set(JAVA_INT, layoutString.get(2), of.length()); + struct.set(JAVA_INT, layoutString.get(3), FLAG_RELEASABLE); return struct; } - public static String toJvmString(MemorySegment struct, boolean dealloc) throws Throwable { - MemorySegment data = (MemorySegment)stringDataVarHandle.get(struct, 0L); - int length = (int)stringLengthVarHandle.get(struct, 0L); - - final byte[] bytes = new byte[length]; - MemorySegment.copy(data.reinterpret(length), JAVA_BYTE, 0, bytes, 0, length); - String result = new String(bytes, StandardCharsets.UTF_8); + public static String toJvmString(MemorySegment struct) { + struct = struct.reinterpret(layoutString.getSize()); + MemorySegment data = struct.get(ADDRESS, layoutString.get(0)); + int size = (int) struct.get(JAVA_LONG, layoutString.get(1)); - if(dealloc) - freeHandle.invoke((MemorySegment)ForeignUtils.stringDataVarHandle.get(struct, 0L)); - return result; + final byte[] bytes = new byte[size]; + MemorySegment.copy(data.reinterpret(size), JAVA_BYTE, 0, bytes, 0, size); + return new String(bytes, StandardCharsets.UTF_8); } // Arrays - public static int arraySize(MemorySegment struct) { - return (int)arraySizeVarHandle.get(struct, 0L); + private static MemorySegment fillArray(MemorySegment mem, MemorySegment data, int length, int flag) { + mem.set(ADDRESS, layoutArray.get(0), data); + mem.set(JAVA_LONG, layoutArray.get(1), data.byteSize()); + mem.set(JAVA_INT, layoutArray.get(2), length); + mem.set(JAVA_INT, layoutArray.get(3), flag); + return mem; } - public static MemorySegment arrayElements(MemorySegment struct) { - return (MemorySegment)ForeignUtils.arrayElementsVarHandle.get(struct, 0L); + private static int getLength(MemorySegment array) { + return array.get(JAVA_INT, layoutArray.get(2)); } - public static long arrayElementsAddress(MemorySegment struct) { - return arrayElements(struct).address(); + private static long getSize(MemorySegment array) { + return array.get(JAVA_LONG, layoutArray.get(1)); } - // Array: char - - public static MemorySegment toNativeHeapCharArray(char[] arr) { - return MemorySegment.ofArray(arr); + private static MemorySegment getElements(MemorySegment array) { + return array.get(ADDRESS, layoutArray.get(0)).reinterpret(getSize(array)); } - public static MemorySegment toNativeCharArray(char[] arr, boolean releasable) { - return toNativeCharArray(arr, releasable, Arena.global(), Arena.ofAuto()); + private static void copyElements(MemorySegment array, Object dstArray, int length, ValueLayout srcLayout) { + MemorySegment elements = getElements(array); + MemorySegment.copy(elements, srcLayout, 0L, dstArray, 0, length); } - public static MemorySegment toNativeCharArray(char[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeCharArray(arr, releasable, elementsArena, structArena.allocate(ARRAY_STRUCT)); - } + // Array: char - public static MemorySegment toNativeCharArray(char[] arr, boolean releasable, MemorySegment struct) { - return toNativeCharArray(arr, releasable, Arena.global(), struct); + public static MemorySegment toNativeCharArrayOnArena(Arena arena, char[] arr) { + MemorySegment dataMem = arena.allocateFrom(JAVA_CHAR, arr); + return fillArray(arena.allocate(layoutArray.getSize()), dataMem, arr.length, FLAG_ON_STACK); } - public static MemorySegment toNativeCharArray(char[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { - arrayElementsVarHandle.set(struct, 0L, - elementsArena.allocate((long) arr.length * C_CHAR.byteSize(), C_CHAR.byteAlignment()) - .copyFrom(MemorySegment.ofArray(arr)) - ); - arraySizeVarHandle.set(struct, 0L, arr.length); - arrayReleasableVarHandle.set(struct, 0L, releasable); - arrayReleasedVarHandle.set(struct, 0L, false); - return struct; + public static MemorySegment toNativeCharArray(char[] arr) { + MemorySegment dataMem = malloc(JAVA_CHAR.byteSize() * arr.length); + MemorySegment.copy(arr, 0, dataMem, JAVA_CHAR, 0, arr.length); + return fillArray(malloc(layoutArray.getSize()), dataMem, arr.length, FLAG_RELEASABLE); } - public static char[] toJvmCharArray(MemorySegment struct, boolean dealloc) throws Throwable { - char[] result = new char[arraySize(struct)]; - MemorySegment elements = arrayElements(struct).reinterpret(result.length * C_CHAR.byteSize()); - MemorySegment.copy(elements, C_CHAR, 0L, result, 0, result.length); - if(dealloc) freeHandle.invoke(elements); + public static char[] toJvmCharArray(MemorySegment struct) { + struct = struct.reinterpret(layoutArray.getSize()); + char[] result = new char[getLength(struct)]; + copyElements(struct, result, result.length, JAVA_CHAR); return result; } // Array: boolean - public static MemorySegment toNativeHeapBooleanArray(boolean[] arr) { + public static MemorySegment toNativeBooleanArrayOnArena(Arena arena, boolean[] arr) { byte[] bytes = new byte[arr.length]; for(int i = 0; i < arr.length; i++) bytes[i] = (byte)(arr[i] ? 1 : 0); - return MemorySegment.ofArray(bytes); - } - - public static MemorySegment toNativeBooleanArray(boolean[] arr, boolean releasable) { - return toNativeBooleanArray(arr, releasable, Arena.global(), Arena.ofAuto()); - } - - public static MemorySegment toNativeBooleanArray(boolean[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeBooleanArray(arr, releasable, elementsArena, structArena.allocate(ARRAY_STRUCT)); - } - - public static MemorySegment toNativeBooleanArray(boolean[] arr, boolean releasable, MemorySegment struct) { - return toNativeBooleanArray(arr, releasable, Arena.global(), struct); + return toNativeByteArrayOnArena(arena, bytes); } - public static MemorySegment toNativeBooleanArray(boolean[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { + public static MemorySegment toNativeBooleanArray(boolean[] arr) { byte[] bytes = new byte[arr.length]; for(int i = 0; i < arr.length; i++) bytes[i] = (byte)(arr[i] ? 1 : 0); - return toNativeByteArray(bytes, releasable, elementsArena, struct); + return toNativeByteArray(bytes); } - public static boolean[] toJvmBooleanArray(MemorySegment struct, boolean dealloc) throws Throwable { - byte[] bytes = toJvmByteArray(struct, dealloc); + public static boolean[] toJvmBooleanArray(MemorySegment struct) { + byte[] bytes = toJvmByteArray(struct); boolean[] result = new boolean[bytes.length]; for(int i = 0; i < bytes.length; i++) result[i] = (bytes[i] == 1); @@ -213,262 +214,146 @@ public static boolean[] toJvmBooleanArray(MemorySegment struct, boolean dealloc) // Array: byte - public static MemorySegment toNativeHeapByteArray(byte[] arr) { - return MemorySegment.ofArray(arr); - } - - public static MemorySegment toNativeByteArray(byte[] arr, boolean releasable) { - return toNativeByteArray(arr, releasable, Arena.global(), Arena.ofAuto()); - } - - public static MemorySegment toNativeByteArray(byte[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeByteArray(arr, releasable, elementsArena, structArena.allocate(ARRAY_STRUCT)); - } - - public static MemorySegment toNativeByteArray(byte[] arr, boolean releasable, MemorySegment struct) { - return toNativeByteArray(arr, releasable, Arena.global(), struct); + public static MemorySegment toNativeByteArrayOnArena(Arena arena, byte[] arr) { + MemorySegment dataMem = arena.allocateFrom(JAVA_BYTE, arr); + return fillArray(arena.allocate(layoutArray.getSize()), dataMem, arr.length, FLAG_ON_STACK); } - public static MemorySegment toNativeByteArray(byte[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { - arrayElementsVarHandle.set(struct, 0L, - elementsArena.allocate((long) arr.length * C_BYTE.byteSize(), C_BYTE.byteAlignment()) - .copyFrom(MemorySegment.ofArray(arr)) - ); - arraySizeVarHandle.set(struct, 0L, arr.length); - arrayReleasableVarHandle.set(struct, 0L, releasable); - arrayReleasedVarHandle.set(struct, 0L, false); - return struct; + public static MemorySegment toNativeByteArray(byte[] arr) { + MemorySegment dataMem = malloc(arr.length); + MemorySegment.copy(arr, 0, dataMem, JAVA_BYTE, 0, arr.length); + return fillArray(malloc(layoutArray.getSize()), dataMem, arr.length, FLAG_RELEASABLE); } - public static byte[] toJvmByteArray(MemorySegment struct, boolean dealloc) throws Throwable { - byte[] result = new byte[arraySize(struct)]; - MemorySegment elements = arrayElements(struct).reinterpret(result.length * C_BYTE.byteSize()); - MemorySegment.copy(elements, C_BYTE, 0L, result, 0, result.length); - if(dealloc) freeHandle.invoke(elements); + public static byte[] toJvmByteArray(MemorySegment struct) { + struct = struct.reinterpret(layoutArray.getSize()); + byte[] result = new byte[getLength(struct)]; + copyElements(struct, result, result.length, JAVA_BYTE); return result; } // Array: short - public static MemorySegment toNativeHeapShortArray(short[] arr) { - return MemorySegment.ofArray(arr); - } - - public static MemorySegment toNativeShortArray(short[] arr, boolean releasable) { - return toNativeShortArray(arr, releasable, Arena.global(), Arena.ofAuto()); - } - - public static MemorySegment toNativeShortArray(short[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeShortArray(arr, releasable, elementsArena, structArena.allocate(ARRAY_STRUCT)); - } - - public static MemorySegment toNativeShortArray(short[] arr, boolean releasable, MemorySegment struct) { - return toNativeShortArray(arr, releasable, Arena.global(), struct); + public static MemorySegment toNativeShortArrayOnArena(Arena arena, short[] arr) { + MemorySegment dataMem = arena.allocateFrom(JAVA_SHORT, arr); + return fillArray(arena.allocate(layoutArray.getSize()), dataMem, arr.length, FLAG_ON_STACK); } - public static MemorySegment toNativeShortArray(short[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { - arrayElementsVarHandle.set(struct, 0L, - elementsArena.allocate((long) arr.length * C_SHORT.byteSize(), C_SHORT.byteAlignment()) - .copyFrom(MemorySegment.ofArray(arr)) - ); - arraySizeVarHandle.set(struct, 0L, arr.length); - arrayReleasableVarHandle.set(struct, 0L, releasable); - arrayReleasedVarHandle.set(struct, 0L, false); - return struct; + public static MemorySegment toNativeShortArray(short[] arr) { + MemorySegment dataMem = malloc(JAVA_SHORT.byteSize() * arr.length); + MemorySegment.copy(arr, 0, dataMem, JAVA_SHORT, 0, arr.length); + return fillArray(malloc(layoutArray.getSize()), dataMem, arr.length, FLAG_RELEASABLE); } - public static short[] toJvmShortArray(MemorySegment struct, boolean dealloc) throws Throwable { - short[] result = new short[arraySize(struct)]; - MemorySegment elements = arrayElements(struct).reinterpret(result.length * C_SHORT.byteSize()); - MemorySegment.copy(elements, C_SHORT, 0L, result, 0, result.length); - if(dealloc) freeHandle.invoke(elements); + public static short[] toJvmShortArray(MemorySegment struct) { + struct = struct.reinterpret(layoutArray.getSize()); + short[] result = new short[getLength(struct)]; + copyElements(struct, result, result.length, JAVA_SHORT); return result; } // Array: int - public static MemorySegment toNativeHeapIntArray(int[] arr) { - return MemorySegment.ofArray(arr); - } - - public static MemorySegment toNativeIntArray(int[] arr, boolean releasable) { - return toNativeIntArray(arr, releasable, Arena.global(), Arena.ofAuto()); - } - - public static MemorySegment toNativeIntArray(int[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeIntArray(arr, releasable, elementsArena, structArena.allocate(ARRAY_STRUCT)); - } - - public static MemorySegment toNativeIntArray(int[] arr, boolean releasable, MemorySegment struct) { - return toNativeIntArray(arr, releasable, Arena.global(), struct); + public static MemorySegment toNativeIntArrayOnArena(Arena arena, int[] arr) { + MemorySegment dataMem = arena.allocateFrom(JAVA_INT, arr); + return fillArray(arena.allocate(layoutArray.getSize()), dataMem, arr.length, FLAG_ON_STACK); } - public static MemorySegment toNativeIntArray(int[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { - arrayElementsVarHandle.set(struct, 0L, - elementsArena.allocate((long) arr.length * C_INT.byteSize(), C_INT.byteAlignment()) - .copyFrom(MemorySegment.ofArray(arr)) - ); - arraySizeVarHandle.set(struct, 0L, arr.length); - arrayReleasableVarHandle.set(struct, 0L, releasable); - arrayReleasedVarHandle.set(struct, 0L, false); - return struct; + public static MemorySegment toNativeIntArray(int[] arr) { + MemorySegment dataMem = malloc(JAVA_INT.byteSize() * arr.length); + MemorySegment.copy(arr, 0, dataMem, JAVA_INT, 0, arr.length); + return fillArray(malloc(layoutArray.getSize()), dataMem, arr.length, FLAG_RELEASABLE); } - public static int[] toJvmIntArray(MemorySegment struct, boolean dealloc) throws Throwable { - int[] result = new int[arraySize(struct)]; - MemorySegment elements = arrayElements(struct).reinterpret(result.length * C_INT.byteSize()); - MemorySegment.copy(elements, C_INT, 0L, result, 0, result.length); - if(dealloc) freeHandle.invoke(elements); + public static int[] toJvmIntArray(MemorySegment struct) { + struct = struct.reinterpret(layoutArray.getSize()); + int[] result = new int[getLength(struct)]; + copyElements(struct, result, result.length, JAVA_INT); return result; } // Array: long - public static MemorySegment toNativeHeapLongArray(long[] arr) { - return MemorySegment.ofArray(arr); - } - - public static MemorySegment toNativeLongArray(long[] arr, boolean releasable) { - return toNativeLongArray(arr, releasable, Arena.global(), Arena.ofAuto()); + public static MemorySegment toNativeLongArrayOnArena(Arena arena, long[] arr) { + MemorySegment dataMem = arena.allocateFrom(JAVA_LONG, arr); + return fillArray(arena.allocate(layoutArray.getSize()), dataMem, arr.length, FLAG_ON_STACK); } - public static MemorySegment toNativeLongArray(long[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeLongArray(arr, releasable, Arena.global(), structArena.allocate(ARRAY_STRUCT)); + public static MemorySegment toNativeLongArray(long[] arr) { + MemorySegment dataMem = malloc(JAVA_LONG.byteSize() * arr.length); + MemorySegment.copy(arr, 0, dataMem, JAVA_LONG, 0, arr.length); + return fillArray(malloc(layoutArray.getSize()), dataMem, arr.length, FLAG_RELEASABLE); } - public static MemorySegment toNativeLongArray(long[] arr, boolean releasable, MemorySegment struct) { - return toNativeLongArray(arr, releasable, Arena.global(), struct); - } - - public static MemorySegment toNativeLongArray(long[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { - arrayElementsVarHandle.set(struct, 0L, - elementsArena.allocate((long) arr.length * C_LONG.byteSize(), C_LONG.byteAlignment()) - .copyFrom(MemorySegment.ofArray(arr)) - ); - arraySizeVarHandle.set(struct, 0L, arr.length); - arrayReleasableVarHandle.set(struct, 0L, releasable); - arrayReleasedVarHandle.set(struct, 0L, false); - return struct; - } - - - public static long[] toJvmLongArray(MemorySegment struct, boolean dealloc) throws Throwable { - long[] result = new long[arraySize(struct)]; - MemorySegment elements = arrayElements(struct).reinterpret(result.length * C_LONG.byteSize()); - MemorySegment.copy(elements, C_LONG, 0L, result, 0, result.length); - if(dealloc) freeHandle.invoke(elements); + public static long[] toJvmLongArray(MemorySegment struct) { + struct = struct.reinterpret(layoutArray.getSize()); + long[] result = new long[getLength(struct)]; + copyElements(struct, result, result.length, JAVA_LONG); return result; } // Array: float - public static MemorySegment toNativeHeapFloatArray(float[] arr) { - return MemorySegment.ofArray(arr); - } - - public static MemorySegment toNativeFloatArray(float[] arr, boolean releasable) { - return toNativeFloatArray(arr, releasable, Arena.global(), Arena.ofAuto()); - } - - public static MemorySegment toNativeFloatArray(float[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeFloatArray(arr, releasable, elementsArena, structArena.allocate(ARRAY_STRUCT)); + public static MemorySegment toNativeFloatArrayOnArena(Arena arena, float[] arr) { + MemorySegment dataMem = arena.allocateFrom(JAVA_FLOAT, arr); + return fillArray(arena.allocate(layoutArray.getSize()), dataMem, arr.length, FLAG_ON_STACK); } - public static MemorySegment toNativeFloatArray(float[] arr, boolean releasable, MemorySegment struct) { - return toNativeFloatArray(arr, releasable, Arena.global(), struct); + public static MemorySegment toNativeFloatArray(float[] arr) { + MemorySegment dataMem = malloc(JAVA_FLOAT.byteSize() * arr.length); + MemorySegment.copy(arr, 0, dataMem, JAVA_FLOAT, 0, arr.length); + return fillArray(malloc(layoutArray.getSize()), dataMem, arr.length, FLAG_RELEASABLE); } - public static MemorySegment toNativeFloatArray(float[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { - arrayElementsVarHandle.set(struct, 0L, - elementsArena.allocate((long) arr.length * C_FLOAT.byteSize(), C_FLOAT.byteAlignment()) - .copyFrom(MemorySegment.ofArray(arr)) - ); - arraySizeVarHandle.set(struct, 0L, arr.length); - arrayReleasableVarHandle.set(struct, 0L, releasable); - arrayReleasedVarHandle.set(struct, 0L, false); - return struct; - } - - public static float[] toJvmFloatArray(MemorySegment struct, boolean dealloc) throws Throwable { - float[] result = new float[arraySize(struct)]; - MemorySegment elements = arrayElements(struct).reinterpret(result.length * C_FLOAT.byteSize()); - MemorySegment.copy(elements, C_FLOAT, 0L, result, 0, result.length); - if(dealloc) freeHandle.invoke(elements); + public static float[] toJvmFloatArray(MemorySegment struct) { + struct = struct.reinterpret(layoutArray.getSize()); + float[] result = new float[getLength(struct)]; + copyElements(struct, result, result.length, JAVA_FLOAT); return result; } // Array: double - public static MemorySegment toNativeHeapDoubleArray(double[] arr) { - return MemorySegment.ofArray(arr); - } - - public static MemorySegment toNativeDoubleArray(double[] arr, boolean releasable) { - return toNativeDoubleArray(arr, releasable, Arena.global(), Arena.ofAuto()); - } - - public static MemorySegment toNativeDoubleArray(double[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeDoubleArray(arr, releasable, elementsArena, structArena.allocate(ARRAY_STRUCT)); - } - - public static MemorySegment toNativeDoubleArray(double[] arr, boolean releasable, MemorySegment struct) { - return toNativeDoubleArray(arr, releasable, Arena.global(), struct); + public static MemorySegment toNativeDoubleArrayOnArena(Arena arena, double[] arr) { + MemorySegment dataMem = arena.allocateFrom(JAVA_DOUBLE, arr); + return fillArray(arena.allocate(layoutArray.getSize()), dataMem, arr.length, FLAG_ON_STACK); } - public static MemorySegment toNativeDoubleArray(double[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { - arrayElementsVarHandle.set(struct, 0L, - elementsArena.allocate((long) arr.length * C_DOUBLE.byteSize(), C_DOUBLE.byteAlignment()) - .copyFrom(MemorySegment.ofArray(arr)) - ); - arraySizeVarHandle.set(struct, 0L, arr.length); - arrayReleasableVarHandle.set(struct, 0L, releasable); - arrayReleasedVarHandle.set(struct, 0L, false); - return struct; + public static MemorySegment toNativeDoubleArray(double[] arr) { + MemorySegment dataMem = malloc(JAVA_DOUBLE.byteSize() * arr.length); + MemorySegment.copy(arr, 0, dataMem, JAVA_DOUBLE, 0, arr.length); + return fillArray(malloc(layoutArray.getSize()), dataMem, arr.length, FLAG_RELEASABLE); } - public static double[] toJvmDoubleArray(MemorySegment struct, boolean dealloc) throws Throwable { - double[] result = new double[arraySize(struct)]; - MemorySegment elements = arrayElements(struct).reinterpret(result.length * C_DOUBLE.byteSize()); - MemorySegment.copy(elements, C_DOUBLE, 0L, result, 0, result.length); - if(dealloc) freeHandle.invoke(elements); + public static double[] toJvmDoubleArray(MemorySegment struct) { + struct = struct.reinterpret(layoutArray.getSize()); + double[] result = new double[getLength(struct)]; + copyElements(struct, result, result.length, JAVA_DOUBLE); return result; } // Array: enum - public static > MemorySegment toNativeHeapEnumArray(T[] arr) { + public static > MemorySegment toNativeEnumArrayOnArena(Arena arena, T[] arr) { int[] intEnums = new int[arr.length]; for(int i = 0; i < arr.length; i++) intEnums[i] = arr[i].ordinal(); - return MemorySegment.ofArray(intEnums); - } - - public static > MemorySegment toNativeEnumArray(T[] arr, boolean releasable) { - return toNativeEnumArray(arr, releasable, Arena.global(), Arena.ofAuto()); - } - - public static > MemorySegment toNativeEnumArray(T[] arr, boolean releasable, Arena elementsArena, Arena structArena) { - return toNativeEnumArray(arr, releasable, elementsArena, structArena.allocate(ARRAY_STRUCT)); + return toNativeIntArrayOnArena(arena, intEnums); } - public static > MemorySegment toNativeEnumArray(T[] arr, boolean releasable, MemorySegment struct) { - return toNativeEnumArray(arr, releasable, Arena.global(), struct); - } - - public static > MemorySegment toNativeEnumArray(T[] arr, boolean releasable, Arena elementsArena, MemorySegment struct) { + public static > MemorySegment toNativeEnumArray(T[] arr) { int[] intEnums = new int[arr.length]; for(int i = 0; i < arr.length; i++) intEnums[i] = arr[i].ordinal(); - return toNativeIntArray(intEnums, releasable, elementsArena, struct); + return toNativeIntArray(intEnums); } @SuppressWarnings("unchecked") public static > T[] toJvmEnumArray( MemorySegment struct, - boolean dealloc, Class enumClass - ) throws Throwable { - int[] ordinals = toJvmIntArray(struct, dealloc); + ) { + int[] ordinals = toJvmIntArray(struct); // Convert integers to enum values T[] enumConstants = enumClass.getEnumConstants(); @@ -480,85 +365,149 @@ public static > T[] toJvmEnumArray( // Array: object - public static MemorySegment toNativeArray(T[] elements, boolean releasable, Function2 cast) { - return toNativeArray(elements, releasable, cast, Arena.ofAuto().allocate(ARRAY_STRUCT)); + public static MemorySegment toNativeArrayOnArena(Arena arena, T[] arr, BiFunction cast) { + MemorySegment dataMem = arena.allocate(ADDRESS, arr.length); + VarHandle ptrHandle = ADDRESS.arrayElementVarHandle(); + for(int i = 0; i < arr.length; i++) + ptrHandle.set(dataMem, 0L, (long)i, cast.apply(arena, arr[i])); + return fillArray(arena.allocate(layoutArray.getSize()), dataMem, arr.length, FLAG_ON_STACK); } - public static MemorySegment toNativeArray(T[] elements, boolean releasable, Function2 cast, MemorySegment struct) { - MemorySegment elementsPtr = Arena.global().allocate(MemoryLayout.sequenceLayout(elements.length, ValueLayout.ADDRESS)); - VarHandle ptrHandle = ValueLayout.ADDRESS.arrayElementVarHandle(); - for(int i = 0; i < elements.length; i++) - ptrHandle.set(elementsPtr, 0L, (long)i, cast.invoke(elements[i], releasable)); - - arrayElementsVarHandle.set(struct, 0L, elementsPtr); - arraySizeVarHandle.set(struct, 0L, elements.length); - arrayReleasableVarHandle.set(struct, 0L, releasable); - arrayReleasedVarHandle.set(struct, 0L, false); - return struct; + public static MemorySegment toNativeArray(T[] arr, Function cast) { + MemorySegment dataMem = malloc(ADDRESS.byteSize() * arr.length); + VarHandle ptrHandle = ADDRESS.arrayElementVarHandle(); + for(int i = 0; i < arr.length; i++) + ptrHandle.set(dataMem, 0L, (long)i, cast.apply(arr[i])); + return fillArray(malloc(layoutArray.getSize()), dataMem, arr.length, FLAG_RELEASABLE); } @SuppressWarnings("unchecked") - public static T[] toJvmArray(MemorySegment struct, BiFunction cast, Class clazz, boolean dealloc, boolean deallocContent) throws Throwable { - T[] result = (T[]) java.lang.reflect.Array.newInstance(clazz, arraySize(struct)); + public static T[] toJvmArray(MemorySegment struct, Function cast, Class clazz) { + struct = struct.reinterpret(layoutArray.getSize()); + T[] result = (T[]) java.lang.reflect.Array.newInstance(clazz, getLength(struct)); - MemorySegment elementsPtr = arrayElements(struct).reinterpret(result.length * C_ADDRESS.byteSize()); - VarHandle ptrHandle = ValueLayout.ADDRESS.arrayElementVarHandle(); + MemorySegment elementsPtr = getElements(struct).reinterpret(ADDRESS.byteSize() * result.length); + VarHandle ptrHandle = ADDRESS.arrayElementVarHandle(); for(int i = 0; i < result.length; i++) - result[i] = cast.apply((MemorySegment) ptrHandle.get(elementsPtr, 0L, (long)i), deallocContent); + result[i] = cast.apply((MemorySegment) ptrHandle.get(elementsPtr, 0L, (long)i)); - if(dealloc) freeHandle.invoke(elementsPtr); return result; } // Callbacks - public static MemorySegment createCallback( + public static MemorySegment upcall( + MethodHandles.Lookup lookup, + String name, + MethodType methodType, + FunctionDescriptor functionDescriptor + ) throws Throwable { + return linker.upcallStub( + lookup.findStatic(lookup.lookupClass(), name, methodType), + functionDescriptor, + Arena.global() + ); + } + + public static MemorySegment createCallbackOnArena( + Arena arena, Object callback, - MethodHandle invokeHandle, - FunctionDescriptor invokeDesc + MemorySegment upcall ){ - Arena arena = Arena.global(); - MemorySegment struct = arena.allocate(callbackStructLayout); + MemorySegment struct = arena.allocate(layoutCallback.getSize()); + struct.set(JAVA_BYTE, layoutCallback.get(0), FLAG_ON_STACK); + struct.set(ADDRESS, layoutCallback.get(1), upcall); + struct.set(ADDRESS, layoutCallback.get(2), callbackClone); + struct.set(ADDRESS, layoutCallback.get(3), callbackEquals); + struct.set(ADDRESS, layoutCallback.get(4), callbackHashCode); + struct.set(ADDRESS, layoutCallback.get(5), callbackFree); + + long address = struct.address(); + callbacks.put(address, callback); + + // Remove callback from list when Arena is closed + MemorySessionImpl.toMemorySession(arena).addCloseAction(() -> + callbacks.remove(address) + ); + return struct; + } + + public static MemorySegment createCallback( + Object callback, + MemorySegment upcall + ) { + MemorySegment struct = malloc(layoutCallback.getSize()); + struct.set(JAVA_BYTE, layoutCallback.get(0), FLAG_RELEASABLE); + struct.set(ADDRESS, layoutCallback.get(1), upcall); + struct.set(ADDRESS, layoutCallback.get(2), callbackClone); + struct.set(ADDRESS, layoutCallback.get(3), callbackEquals); + struct.set(ADDRESS, layoutCallback.get(4), callbackHashCode); + struct.set(ADDRESS, layoutCallback.get(5), callbackFree); callbacks.put(struct.address(), callback); + return struct; + } - callbackInvokeVarHandle.set(struct, 0L, linker.upcallStub( - invokeHandle, - invokeDesc, - arena - )); - callbackFreeVarHandle.set(struct, 0L, callbackFree); + public static MemorySegment callbackClone(MemorySegment self) { + MemorySegment struct = malloc(layoutCallback.getSize()); + struct.set(JAVA_BYTE, layoutCallback.get(0), FLAG_RELEASABLE); + struct.set(ADDRESS, layoutCallback.get(1), self.reinterpret(layoutCallback.getSize()).get(ADDRESS, 8L)); + struct.set(ADDRESS, layoutCallback.get(2), callbackClone); + struct.set(ADDRESS, layoutCallback.get(3), callbackEquals); + struct.set(ADDRESS, layoutCallback.get(4), callbackHashCode); + struct.set(ADDRESS, layoutCallback.get(5), callbackFree); + callbacks.put(struct.address(), callbacks.get(self.address())); return struct; } + public static boolean callbackEquals(MemorySegment self, MemorySegment obj) { + return callbacks.get(self.address()).equals(callbacks.get(obj.address())); + } + + public static int callbackHashCode(MemorySegment self) { + return callbacks.get(self.address()).hashCode(); + } + public static void callbackFree(MemorySegment callback) { + callback = callback.reinterpret(layoutCallback.getSize()); callbacks.remove(callback.address()); - try { - freeHandle.invoke(callback); - } catch (Throwable e) { - throw new RuntimeException(e); - } + + if((callback.get(JAVA_INT, 0L) & FLAG_RELEASABLE) == FLAG_RELEASABLE) + free(callback); } @SuppressWarnings("unchecked") - public static T toJvmCallback(MemorySegment segment, boolean dealloc) throws Throwable{ - Object result = callbacks.get(segment.address()); - if(dealloc) - freeHandle.invoke(segment); - return (T) result; + public static T toJvmCallback(MemorySegment segment) { + return (T) callbacks.get(segment.address()); } static { try { - callbackFree = linker.upcallStub( - MethodHandles.lookup().findStatic( - ForeignUtils.class, - "callbackFree", - MethodType.methodType(void.class, MemorySegment.class) - ), - FunctionDescriptor.ofVoid(ValueLayout.ADDRESS), - Arena.global() + MethodHandles.Lookup lookup = MethodHandles.lookup(); + callbackClone = upcall( + lookup, + "callbackClone", + MethodType.methodType(MemorySegment.class, MemorySegment.class), + FunctionDescriptor.of(ADDRESS, ADDRESS) ); - } catch (Exception e) { + callbackEquals = upcall( + lookup, + "callbackEquals", + MethodType.methodType(boolean.class, MemorySegment.class, MemorySegment.class), + FunctionDescriptor.of(ValueLayout.JAVA_BOOLEAN, ADDRESS, ADDRESS) + ); + callbackHashCode = upcall( + lookup, + "callbackHashCode", + MethodType.methodType(int.class, MemorySegment.class), + FunctionDescriptor.of(ValueLayout.JAVA_INT, ADDRESS) + ); + callbackFree = upcall( + lookup, + "callbackFree", + MethodType.methodType(void.class, MemorySegment.class), + FunctionDescriptor.ofVoid(ADDRESS) + ); + } catch (Throwable e) { throw new RuntimeException(e); } } diff --git a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/jvmci/conventions/AMD64LinuxCallingConvention.java b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/jvmci/conventions/AMD64LinuxCallingConvention.java index 32bbe63..e1b2e35 100644 --- a/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/jvmci/conventions/AMD64LinuxCallingConvention.java +++ b/modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/jvmci/conventions/AMD64LinuxCallingConvention.java @@ -29,7 +29,7 @@ void emitRegToReg( // Integers if(intOps.length == 6) { - new RegToReg(RDI, RAX, Integer.class).emit(buf); + new RegToReg(RDI, RAX, intOps[5].type).emit(buf); for(int i = 0; i < intOps.length-1; i++) intOps[i].emit(buf); @@ -40,4 +40,4 @@ void emitRegToReg( op.emit(buf); } } -} +} \ No newline at end of file diff --git a/modules/runtime/src/linuxMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.linux.kt b/modules/runtime/src/linuxMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.linux.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/linuxMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.linux.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/macosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.macos.kt b/modules/runtime/src/macosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.macos.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/macosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.macos.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/mingwMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.mingw.kt b/modules/runtime/src/mingwMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.mingw.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/mingwMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.mingw.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/nativeInterop/cinterop/api.def b/modules/runtime/src/nativeInterop/cinterop/api.def new file mode 100644 index 0000000..9fe7c18 --- /dev/null +++ b/modules/runtime/src/nativeInterop/cinterop/api.def @@ -0,0 +1,109 @@ +package = nativekt.internals +--- + +#include +#include +#include + +typedef int32_t KInt; +typedef int64_t KLong; +typedef float KFloat; +typedef double KDouble; +typedef int8_t KByte; +typedef int16_t KShort; +typedef bool KBoolean; +typedef uint16_t KChar; + +// ╔════════════════╗ +// ║ String ║ +// ╚════════════════╝ + +typedef struct KString { + const char* data; + size_t size; + KInt length; + char __flags; +} KString; + +// ╔════════════════╗ +// ║ Arrays ║ +// ╚════════════════╝ + +typedef struct KCharArray { + const KChar* elements; + size_t size; + KInt length; + char __flags; +} KCharArray; + +typedef struct KBooleanArray { + const KBoolean* elements; + size_t size; + KInt length; + char __flags; +} KBooleanArray; + +typedef struct KByteArray { + const KByte* elements; + size_t size; + KInt length; + char __flags; +} KByteArray; + +typedef struct KShortArray { + const KShort* elements; + size_t size; + KInt length; + char __flags; +} KShortArray; + +typedef struct KIntArray { + const KInt* elements; + size_t size; + KInt length; + char __flags; +} KIntArray; + +typedef struct KLongArray { + const KLong* elements; + size_t size; + KInt length; + char __flags; +} KLongArray; + +typedef struct KFloatArray { + const KFloat* elements; + size_t size; + KInt length; + char __flags; +} KFloatArray; + +typedef struct KDoubleArray { + const KDouble* elements; + size_t size; + KInt length; + char __flags; +} KDoubleArray; + +typedef struct KArray { + const void** elements; + size_t size; + KInt length; + char __flags; +} KArray; + +// ╔══════════════════╗ +// ║ Callback ║ +// ╚══════════════════╝ + +typedef struct _AbstractCallback _AbstractCallback; + +struct _AbstractCallback { + char __flags; + void (*invoke)(_AbstractCallback* self); + _AbstractCallback* (*clone)(_AbstractCallback* self); + KBoolean (*equals)(_AbstractCallback* self, _AbstractCallback* obj); + KInt (*hashCode)(_AbstractCallback* self); + void (*free)(_AbstractCallback* self); + void* __stableRef; +}; \ No newline at end of file diff --git a/modules/runtime/src/nativeMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.kt b/modules/runtime/src/nativeMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.kt index d92c2aa..831209c 100644 --- a/modules/runtime/src/nativeMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.kt +++ b/modules/runtime/src/nativeMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.kt @@ -1,34 +1,390 @@ -@file:OptIn(ExperimentalForeignApi::class) +@file:OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) @file:Suppress("unused") package com.huskerdev.nativekt.kn import kotlinx.cinterop.* +import nativekt.internals.* import platform.posix.* +import kotlin.enums.enumEntries +import kotlin.experimental.and -expect fun mallocExact(size: UInt): COpaquePointer +const val FLAG_RELEASABLE = 1.toByte() +const val FLAG_ON_STACK = 2.toByte() -expect inline fun allocStruct(): CPointer +// ╔════════════════╗ +// ║ String ║ +// ╚════════════════╝ +fun MemScope.toNativeStringOnArena(str: String, pin: Boolean): CPointer { + val bytes = str.cstr + val mem = alloc() + mem.size = bytes.size.convert() + mem.data = if(pin) { + val pinObj = bytes.pin() + defer { pinObj.unpin() } + bytes.ptr + } else bytes.getPointer(this) + mem.length = str.length + mem.__flags = 0 + return mem.ptr +} -fun toKotlinString(of: CPointer, dealloc: Boolean): String { - val result = of.toKString() - if(dealloc) - free(of) - return result +fun toNativeString(str: String): CPointer { + val bytes = str.cstr + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = bytes.size.convert() + mem.data = malloc(mem.size.convert())!!.reinterpret() + mem.length = str.length + mem.__flags = FLAG_RELEASABLE + memcpy(mem.data, bytes, mem.size) + return mem.ptr } -@Suppress("unchecked_cast") -fun toKotlinCallback(callback: CPointer?, dealloc: Boolean): T { - val result = callback!!.pointed.memberAt>(0).value!!.asStableRef().get() - if(dealloc) - free(callback) - return result as T +fun toKotlinString(struct: CPointer): String = + struct.pointed.data!!.toKString() + +// ╔════════════════╗ +// ║ Arrays ║ +// ╚════════════════╝ + +// Char + +fun MemScope.toNativeCharArrayOnArena(arr: CharArray, pin: Boolean): CPointer { + val mem = alloc() + mem.size = (arr.size * Char.SIZE_BYTES).convert() + mem.elements = if(pin) { + val pinObj = arr.pin() + defer { pinObj.unpin() } + pinObj.addressOf(0).reinterpret() + } else allocArray(arr.size) + mem.length = arr.size + mem.__flags = 0 + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toNativeCharArray(arr: CharArray): CPointer { + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = (arr.size * Char.SIZE_BYTES).convert() + mem.elements = malloc(mem.size.convert())!!.reinterpret() + mem.length = arr.size + mem.__flags = FLAG_RELEASABLE + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toKotlinCharArray(struct: CPointer): CharArray = + struct.pointed.run { CharArray(length) { elements!![it].toInt().toChar() } } + +// Boolean + +fun MemScope.toNativeBooleanArrayOnArena(array: BooleanArray, pin: Boolean): CPointer = + toNativeByteArrayOnArena(array.map { it.toByte() }.toByteArray(), pin).reinterpret() + +fun toNativeBooleanArray(array: BooleanArray): CPointer = + toNativeByteArray(array.map { it.toByte() }.toByteArray()).reinterpret() + +fun toKotlinBooleanArray(struct: CPointer): BooleanArray = + struct.pointed.run { BooleanArray(length) { elements!![it].value } } + +// Byte + +fun MemScope.toNativeByteArrayOnArena(arr: ByteArray, pin: Boolean): CPointer { + val mem = alloc() + mem.size = (arr.size * Byte.SIZE_BYTES).convert() + mem.elements = if(pin) { + val pinObj = arr.pin() + defer { pinObj.unpin() } + pinObj.addressOf(0).reinterpret() + } else allocArray(arr.size) + mem.length = arr.size + mem.__flags = 0 + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toNativeByteArray(arr: ByteArray): CPointer { + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = (arr.size * Byte.SIZE_BYTES).convert() + mem.elements = malloc(mem.size.convert())!!.reinterpret() + mem.length = arr.size + mem.__flags = FLAG_RELEASABLE + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toKotlinByteArray(struct: CPointer): ByteArray = + struct.pointed.run { ByteArray(length) { elements!![it] } } + +// Short + +fun MemScope.toNativeShortArrayOnArena(arr: ShortArray, pin: Boolean): CPointer { + val mem = alloc() + mem.size = (arr.size * Short.SIZE_BYTES).convert() + mem.elements = if(pin) { + val pinObj = arr.pin() + defer { pinObj.unpin() } + pinObj.addressOf(0).reinterpret() + } else allocArray(arr.size) + mem.length = arr.size + mem.__flags = 0 + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toNativeShortArray(arr: ShortArray): CPointer { + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = (arr.size * Short.SIZE_BYTES).convert() + mem.elements = malloc(mem.size.convert())!!.reinterpret() + mem.length = arr.size + mem.__flags = FLAG_RELEASABLE + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toKotlinShortArray(struct: CPointer): ShortArray = + struct.pointed.run { ShortArray(length) { elements!![it] } } + +// Int + +fun MemScope.toNativeIntArrayOnArena(arr: IntArray, pin: Boolean): CPointer { + val mem = alloc() + mem.size = (arr.size * Int.SIZE_BYTES).convert() + mem.elements = if(pin) { + val pinObj = arr.pin() + defer { pinObj.unpin() } + pinObj.addressOf(0).reinterpret() + } else allocArray(arr.size) + mem.length = arr.size + mem.__flags = 0 + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toNativeIntArray(arr: IntArray): CPointer { + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = (arr.size * Int.SIZE_BYTES).convert() + mem.elements = malloc(mem.size.convert())!!.reinterpret() + mem.length = arr.size + mem.__flags = FLAG_RELEASABLE + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toKotlinIntArray(struct: CPointer): IntArray = + struct.pointed.run { IntArray(length) { elements!![it] } } + +// Long + +fun MemScope.toNativeLongArrayOnArena(arr: LongArray, pin: Boolean): CPointer { + val mem = alloc() + mem.size = (arr.size * Long.SIZE_BYTES).convert() + mem.elements = if(pin) { + val pinObj = arr.pin() + defer { pinObj.unpin() } + pinObj.addressOf(0).reinterpret() + } else allocArray(arr.size) + mem.length = arr.size + mem.__flags = 0 + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toNativeLongArray(arr: LongArray): CPointer { + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = (arr.size * Long.SIZE_BYTES).convert() + mem.elements = malloc(mem.size.convert())!!.reinterpret() + mem.length = arr.size + mem.__flags = FLAG_RELEASABLE + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toKotlinLongArray(struct: CPointer): LongArray = + struct.pointed.run { LongArray(length) { elements!![it] } } + +// Float + +fun MemScope.toNativeFloatArrayOnArena(arr: FloatArray, pin: Boolean): CPointer { + val mem = alloc() + mem.size = (arr.size * Float.SIZE_BYTES).convert() + mem.elements = if(pin) { + val pinObj = arr.pin() + defer { pinObj.unpin() } + pinObj.addressOf(0).reinterpret() + } else allocArray(arr.size) + mem.length = arr.size + mem.__flags = 0 + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toNativeFloatArray(arr: FloatArray): CPointer { + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = (arr.size * Float.SIZE_BYTES).convert() + mem.elements = malloc(mem.size.convert())!!.reinterpret() + mem.length = arr.size + mem.__flags = FLAG_RELEASABLE + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toKotlinFloatArray(struct: CPointer): FloatArray = + struct.pointed.run { FloatArray(length) { elements!![it] } } + +// Double + +fun MemScope.toNativeDoubleArrayOnArena(arr: DoubleArray, pin: Boolean): CPointer { + val mem = alloc() + mem.size = (arr.size * Double.SIZE_BYTES).convert() + mem.elements = if(pin) { + val pinObj = arr.pin() + defer { pinObj.unpin() } + pinObj.addressOf(0).reinterpret() + } else allocArray(arr.size) + mem.length = arr.size + mem.__flags = 0 + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toNativeDoubleArray(arr: DoubleArray): CPointer { + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = (arr.size * Double.SIZE_BYTES).convert() + mem.elements = malloc(mem.size.convert())!!.reinterpret() + mem.length = arr.size + mem.__flags = FLAG_RELEASABLE + arr.usePinned { memcpy(mem.elements, it.addressOf(0), mem.size) } + return mem.ptr +} + +fun toKotlinDoubleArray(struct: CPointer): DoubleArray = + struct.pointed.run { DoubleArray(length) { elements!![it] } } + +// Enum + +fun > MemScope.toNativeEnumArrayOnArena(arr: Array, pin: Boolean): CPointer = + toNativeIntArrayOnArena(IntArray(arr.size) { arr[it].ordinal }, pin) + +fun > toNativeEnumArray(arr: Array): CPointer = + toNativeIntArray(IntArray(arr.size) { arr[it].ordinal }) + +inline fun > toKotlinEnumArray(struct: CPointer): Array { + val entries = enumEntries() + val ints = toKotlinIntArray(struct) + return Array(ints.size) { entries[ints[it]] } +} + +// Object + +fun MemScope.toNativeArrayOnArena( + arr: Array, + converter: (T) -> CPointer +): CPointer { + val mem = alloc() + mem.size = (arr.size * intptr_t.SIZE_BYTES).convert() + mem.elements = allocArray(arr.size) + mem.length = arr.size + mem.__flags = 0 + arr.forEachIndexed { i, it -> + mem.elements!![i] = converter(it) + } + return mem.ptr } -fun freeCallback(callback: CPointer?) { - callback!!.pointed.memberAt>(0).value!!.asStableRef().dispose() - free(callback) +fun toNativeArray( + arr: Array, + converter: (T) -> CPointer +): CPointer { + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed + mem.size = (arr.size * intptr_t.SIZE_BYTES).convert() + mem.elements = malloc(mem.size.convert())!!.reinterpret() + mem.length = arr.size + mem.__flags = FLAG_RELEASABLE + arr.forEachIndexed { i, it -> + mem.elements!![i] = converter(it) + } + return mem.ptr } -val freeCallbackFunction = staticCFunction(::freeCallback) \ No newline at end of file +inline fun toKotlinArray( + struct: CPointer, + converter: (CPointer) -> T +): Array = struct.pointed.run { Array(length) { converter(elements!![it]!!.reinterpret()) } } + + +// ╔═══════════════════╗ +// ║ Callbacks ║ +// ╚═══════════════════╝ + +private fun callbackClone(self: CPointer<_AbstractCallback>?): CPointer<_AbstractCallback>? { + val mem = malloc(sizeOf<_AbstractCallback>().convert())!!.reinterpret<_AbstractCallback>().pointed + mem.__flags = FLAG_RELEASABLE + mem.invoke = self!!.pointed.invoke + mem.clone = callbackClone + mem.equals = callbackEquals + mem.hashCode = callbackHashCode + mem.free = callbackFree + mem.__stableRef = StableRef.create(toKotlinCallback(self.reinterpret())).asCPointer() + return mem.ptr +} + +private fun callbackEquals(self: CPointer<_AbstractCallback>?, obj: CPointer<_AbstractCallback>?): Boolean = + toKotlinCallback(self!!.reinterpret()) == toKotlinCallback(obj!!.reinterpret()) + +private fun callbackHashCode(self: CPointer<_AbstractCallback>?): Int = + toKotlinCallback(self!!.reinterpret()).hashCode() + +fun callbackFree(self: CPointer<_AbstractCallback>?) { + val pointed = self!!.pointed + if(pointed.__flags and FLAG_RELEASABLE != FLAG_RELEASABLE) + return + + pointed.__stableRef!!.asStableRef().dispose() + free(self) +} + +val callbackClone = staticCFunction(::callbackClone) +val callbackEquals = staticCFunction(::callbackEquals) +val callbackHashCode = staticCFunction(::callbackHashCode) +val callbackFree = staticCFunction(::callbackFree) + + +fun MemScope.toNativeCallbackOnArena( + of: Any, + invoke: COpaquePointer +): COpaquePointer { + val stableRef = StableRef.create(of) + defer { stableRef.dispose() } + + val mem = alloc<_AbstractCallback>() + mem.__flags = 0 + mem.invoke = invoke.reinterpret() + mem.clone = callbackClone + mem.equals = callbackEquals + mem.hashCode = callbackHashCode + mem.free = callbackFree + mem.__stableRef = stableRef.asCPointer() + return mem.ptr +} + +fun toNativeCallback( + of: Any, + invoke: COpaquePointer +): COpaquePointer { + val mem = malloc(sizeOf<_AbstractCallback>().convert())!!.reinterpret<_AbstractCallback>().pointed + mem.__flags = FLAG_RELEASABLE + mem.invoke = invoke.reinterpret() + mem.clone = callbackClone + mem.equals = callbackEquals + mem.hashCode = callbackHashCode + mem.free = callbackFree + mem.__stableRef = StableRef.create(of).asCPointer() + return mem.ptr +} + +@Suppress("unchecked_cast") +fun toKotlinCallback( + callback: COpaquePointer +): T = callback.reinterpret<_AbstractCallback>().pointed.__stableRef!!.asStableRef().get() as T diff --git a/modules/runtime/src/nativeMain/kotlin/com/huskerdev/nativekt/kn/NativeArena.kt b/modules/runtime/src/nativeMain/kotlin/com/huskerdev/nativekt/kn/NativeArena.kt deleted file mode 100644 index 742be0e..0000000 --- a/modules/runtime/src/nativeMain/kotlin/com/huskerdev/nativekt/kn/NativeArena.kt +++ /dev/null @@ -1,60 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.* -import platform.posix.free - -@Suppress("unused") -class NativeArena( - val scope: MemScope -) { - companion object { - fun use(block: (NativeArena) -> T) = memScoped { - NativeArena(this).run { - block(this).also { free() } - } - } - } - private val allocated = hashSetOf() - private val callbacks = arrayListOf>() - private val pinned = arrayListOf>() - - fun pin(obj: T): Pinned = - obj.pin().also { pinned += it } - - fun freeMem(ptr: CPointer<*>) { - if(ptr.rawValue.toLong() !in allocated) - free(ptr) - } - - fun ptr(ptr: CPointer): CPointer { - allocated += ptr.getPointer(scope).rawValue.toLong() - return ptr - } - - fun toKotlinString(mem: CPointer, dealloc: Boolean): String { - val result = mem.toKString() - if(dealloc && mem.rawValue.toLong() !in allocated) - free(mem) - return result - } - - fun toKotlinCallback(callback: CPointer?, dealloc: Boolean): T { - val result = com.huskerdev.nativekt.kn.toKotlinCallback(callback, dealloc) - if(dealloc && callback!!.rawValue.toLong() !in allocated) - freeCallback(callback) - return result - } - - @Suppress("unchecked_cast") - fun callback(callback: CPointer): CPointer { - callbacks += callback as CPointer - return callback - } - - private fun free() { - callbacks.forEach(::freeCallback) - pinned.forEach(Pinned<*>::unpin) - } -} \ No newline at end of file diff --git a/modules/runtime/src/tvosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.tvos.kt b/modules/runtime/src/tvosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.tvos.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/tvosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.tvos.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/watchosArm32Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosArm32.kt b/modules/runtime/src/watchosArm32Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosArm32.kt deleted file mode 100644 index def1bd8..0000000 --- a/modules/runtime/src/watchosArm32Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosArm32.kt +++ /dev/null @@ -1,17 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc -import kotlin.toUInt - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size)!! - -actual inline fun allocStruct() = - malloc(sizeOf().toUInt())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/watchosArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosArm64.kt b/modules/runtime/src/watchosArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosArm64.kt deleted file mode 100644 index 0e391a1..0000000 --- a/modules/runtime/src/watchosArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosArm64.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size)!! - -actual inline fun allocStruct() = - malloc(sizeOf().toUInt())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/watchosDeviceArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosDeviceArm64.kt b/modules/runtime/src/watchosDeviceArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosDeviceArm64.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/watchosDeviceArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosDeviceArm64.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/watchosSimulatorArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosSimulatorArm64.kt b/modules/runtime/src/watchosSimulatorArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosSimulatorArm64.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/watchosSimulatorArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosSimulatorArm64.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/watchosX64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosX64.kt b/modules/runtime/src/watchosX64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosX64.kt deleted file mode 100644 index 2262e84..0000000 --- a/modules/runtime/src/watchosX64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosX64.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:OptIn(ExperimentalForeignApi::class) -@file:Suppress("unused") - -package com.huskerdev.nativekt.kn - -import kotlinx.cinterop.COpaquePointer -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.reinterpret -import kotlinx.cinterop.sizeOf -import platform.posix.malloc - -actual fun mallocExact(size: UInt): COpaquePointer = - malloc(size.toULong())!! - -actual inline fun allocStruct() = - malloc(sizeOf().toULong())!!.reinterpret() \ No newline at end of file diff --git a/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/CStructLayout.kt b/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/CStructLayout.kt new file mode 100644 index 0000000..9926de6 --- /dev/null +++ b/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/CStructLayout.kt @@ -0,0 +1,47 @@ +package com.huskerdev.nativekt.web + +import kotlin.math.max +import kotlin.reflect.KClass + +class CStructLayout( + vararg types: KClass<*> +) { + var size: Int = 0 + private set + + private val address = arrayListOf() + + init { + var maxAlignment = 0 + types.forEach { type -> + val typeSize = when(type) { + Ptr::class -> 4 + Int::class -> 4 + Long::class -> 8 + Float::class -> 4 + Double::class -> 8 + Char::class -> 2 + Short::class -> 2 + Byte::class -> 1 + Boolean::class -> 1 + else -> throw UnsupportedOperationException(type.toString()) + } + + val rem = size % typeSize + val padding = if (rem == 0) 0 else typeSize - rem + + maxAlignment = max(maxAlignment, typeSize) + + this.address += size + padding + size += padding + typeSize + } + + val rem = size % maxAlignment + val postPadding = if (rem == 0) 0 else maxAlignment - rem + size += postPadding + } + + operator fun get(i: Int) = address[i] + + class Ptr +} \ No newline at end of file diff --git a/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmArena.kt b/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmArena.kt index 1d480cc..bbc9993 100644 --- a/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmArena.kt +++ b/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmArena.kt @@ -5,130 +5,37 @@ package com.huskerdev.nativekt.web import kotlin.js.* -class EmArena( - module: JsAny -): AutoCloseable { +class Arena( + val module: EmModule +) { companion object { - fun use(module: JsAny, block: (EmArena) -> T) = - EmArena(module).use { block(it) } - } - - private val module = module.unsafeCast() - private val allocated = hashSetOf() - private val callbacks = hashSetOf() - - private fun ptr(ptr: Int): Int { - allocated += ptr - return ptr - } - - fun malloc(size: Int): Int = - ptr(module._malloc(size)) - - fun toNativeString(str: String): EmString { - val len = module.lengthBytesUTF8(str) + 1 - val strMem = malloc(len) - module.stringToUTF8(str, strMem, len) - - return createJsObject { - data = strMem - length = str.length - releasable = false - released = false + inline fun use(module: EmModule, block: Arena.() -> T): T { + val arena = Arena(module) + try { + return block(arena) + } finally { + arena.close() + } } } - fun toKotlinString(struct: EmString, dealloc: Boolean): String { - val struct = struct.unsafeCast() + private val allocated = arrayListOf() + private val onCloseActions = arrayListOf<() -> Unit>() - val result = module.UTF8ToString(struct.data, struct.length) - if(dealloc && struct.data !in allocated) - module._free(struct.data) - - return result + fun alloc(size: Int): Int { + val addr = module._malloc(size) + allocated += addr + return addr } - // Primitive Arrays - - // Array: char - - fun toNativeCharArray(arr: CharArray) = - toNativeCharArray(module, arr, false).also { ptr(it.elements) } - - fun toKotlinCharArray(struct: EmArray, dealloc: Boolean) = - toKotlinCharArray(module, struct, dealloc && struct.unsafeCast().elements !in allocated) - - // Array: boolean - - fun toNativeBooleanArray(arr: BooleanArray) = - toNativeBooleanArray(module, arr, false).also { ptr(it.elements) } - - fun toKotlinBooleanArray(struct: EmArray, dealloc: Boolean) = - toKotlinBooleanArray(module, struct, dealloc && struct.unsafeCast().elements !in allocated) - - // Array: byte - - fun toNativeByteArray(arr: ByteArray) = - toNativeByteArray(module, arr, false).also { ptr(it.elements) } - - fun toKotlinByteArray(struct: EmArray, dealloc: Boolean) = - toKotlinByteArray(module, struct, dealloc && struct.unsafeCast().elements !in allocated) - - // Array: short - - fun toNativeShortArray(arr: ShortArray) = - toNativeShortArray(module, arr, false).also { ptr(it.elements) } - - fun toKotlinShortArray(struct: EmArray, dealloc: Boolean): ShortArray = - toKotlinShortArray(module, struct, dealloc && struct.unsafeCast().elements !in allocated) - - // Array: int - - fun toNativeIntArray(arr: IntArray) = - toNativeIntArray(module, arr, false).also { ptr(it.elements) } - - fun toKotlinIntArray(struct: EmArray, dealloc: Boolean) = - toKotlinIntArray(module, struct, dealloc && struct.unsafeCast().elements !in allocated) - - // Array: long - - fun toNativeLongArray(arr: LongArray) = - toNativeLongArray(module, arr, false).also { ptr(it.elements) } - - fun toKotlinLongArray(struct: EmArray, dealloc: Boolean): LongArray = - toKotlinLongArray(module, struct, dealloc && struct.unsafeCast().elements !in allocated) - - // Array: float - - fun toNativeFloatArray(arr: FloatArray) = - toNativeFloatArray(module, arr, false).also { ptr(it.elements) } - - fun toKotlinFloatArray(struct: EmArray, dealloc: Boolean): FloatArray = - toKotlinFloatArray(module, struct, dealloc && struct.unsafeCast().elements !in allocated) - - // Array: double - - fun toNativeDoubleArray(arr: DoubleArray) = - toNativeDoubleArray(module, arr, false).also { ptr(it.elements) } - - fun toKotlinDoubleArray(struct: EmArray, dealloc: Boolean): DoubleArray = - toKotlinDoubleArray(module, struct, dealloc && struct.unsafeCast().elements !in allocated) - - - // Callbacks - - fun toKotlinCallback(ptr: Int, dealloc: Boolean): T = - toKotlinCallback(module, ptr, dealloc && ptr !in allocated) - - fun callback(callback: Int): Int { - callbacks += callback - return callback + fun defer(action: () -> Unit) { + onCloseActions += action } - override fun close() = allocated.forEach { - module._free(it) - callbacks.forEach { callback -> - freeCallback(module, callback) + fun close() { + allocated.forEach { + module._free(it) } + onCloseActions.forEach { it() } } } \ No newline at end of file diff --git a/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmTypes.kt b/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmTypes.kt index e48138b..fc1252a 100644 --- a/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmTypes.kt +++ b/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmTypes.kt @@ -55,7 +55,7 @@ abstract external class TypedArray: JsAny { fun set(array: JsArray) } -private fun setTypedArray(array: TypedArray, index: Int, value: Double): Unit = +private fun setTypedArrayAt(array: TypedArray, index: Int, value: Double): Unit = js("array[index] = value") private fun getTypedArray(array: TypedArray, index: Int): Double = @@ -74,10 +74,10 @@ external class Int8Array( ): TypedArray operator fun Int8Array.set(index: Int, value: Byte) = - setTypedArray(this, index, value.toDouble()) + setTypedArrayAt(this, index, value.toDouble()) operator fun Int8Array.set(index: Int, value: Boolean) = - setTypedArray(this, index, value.toInt().toDouble()) + setTypedArrayAt(this, index, value.toInt().toDouble()) operator fun Int8Array.get(index: Int): Byte = getTypedArray(this, index).toInt().toByte() @@ -88,17 +88,29 @@ external class Int16Array( length: Int ): TypedArray -operator fun Int16Array.set(index: Int, value: Char) = - setTypedArray(this, index, value.code.toDouble()) - operator fun Int16Array.set(index: Int, value: Short) = - setTypedArray(this, index, value.toDouble()) + setTypedArrayAt(this, index, value.toDouble()) + +operator fun Int16Array.set(index: Int, value: Char) = + setTypedArrayAt(this, index, value.code.toDouble()) operator fun Int16Array.set(index: Int, value: Int) = - setTypedArray(this, index, value.toDouble()) + setTypedArrayAt(this, index, value.toDouble()) -operator fun Int16Array.get(index: Int): Int = - getTypedArray(this, index).toInt() +operator fun Int16Array.get(index: Int): Short = + getTypedArray(this, index).toInt().toShort() + +external class Uint16Array( + val buffer: ArrayBuffer, + byteOffset: Int, + length: Int +): TypedArray + +operator fun Uint16Array.set(index: Int, value: Char) = + setTypedArrayAt(this, index, value.code.toDouble()) + +operator fun Uint16Array.get(index: Int): Char = + getTypedArray(this, index).toInt().toChar() external class Int32Array( val buffer: ArrayBuffer, @@ -107,7 +119,7 @@ external class Int32Array( ): TypedArray operator fun Int32Array.set(index: Int, value: Int) = - setTypedArray(this, index, value.toDouble()) + setTypedArrayAt(this, index, value.toDouble()) operator fun Int32Array.get(index: Int): Int = getTypedArray(this, index).toInt() @@ -119,7 +131,7 @@ external class Float32Array( ): TypedArray operator fun Float32Array.set(index: Int, value: Float) = - setTypedArray(this, index, value.toDouble()) + setTypedArrayAt(this, index, value.toDouble()) operator fun Float32Array.get(index: Int): Float = getTypedArray(this, index).toFloat() @@ -131,7 +143,7 @@ external class Float64Array( ): TypedArray operator fun Float64Array.set(index: Int, value: Double) = - setTypedArray(this, index, value) + setTypedArrayAt(this, index, value) operator fun Float64Array.get(index: Int): Double = getTypedArray(this, index) diff --git a/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmUtils.kt b/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmUtils.kt index 9786961..17a5615 100644 --- a/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmUtils.kt +++ b/modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/EmUtils.kt @@ -3,18 +3,16 @@ package com.huskerdev.nativekt.web + +import kotlin.collections.set +import kotlin.experimental.and import kotlin.js.* -import kotlin.js.set import kotlin.math.truncate -private val callbacks = hashMapOf, Any>() +const val FLAG_RELEASABLE = 1.toByte() +const val FLAG_ON_STACK = 2.toByte() -private inline fun createJsArray(size: Int, block: (Int) -> T): JsArray { - val arr = JsArray() - for (i in 0 until size) - arr[i] = block(i) - return arr -} +private val callbacks = hashMapOf, Any>() fun Float.truncF32(): Float { val factor = 10_000_000 @@ -25,325 +23,387 @@ fun loadLib(lib: JsAny): Promise = js("'__esModule' in lib ? lib.default() : lib()") fun Boolean.toInt() = if(this) 1 else 0 +fun Int.toBoolean() = this == 1 +fun Byte.toBoolean() = this == 1.toByte() -fun toNativeString(module: EmModule, str: String, releasable: Boolean): EmString { - val len = module.lengthBytesUTF8(str) + 1 - val strMem = module._malloc(len) - module.stringToUTF8(str, strMem, len) +private val layoutString = CStructLayout(CStructLayout.Ptr::class, Int::class, Int::class, Byte::class) +private val layoutArray = CStructLayout(CStructLayout.Ptr::class, Int::class, Int::class, Byte::class) +private val layoutCallback = CStructLayout(Byte::class, CStructLayout.Ptr::class, CStructLayout.Ptr::class, CStructLayout.Ptr::class, CStructLayout.Ptr::class, CStructLayout.Ptr::class) - return createJsObject { - this.data = strMem - this.length = str.length - this.releasable = releasable - this.released = false - } -} -fun toKotlinString(module: EmModule, struct: EmString, dealloc: Boolean): String { - val result = module.UTF8ToString(struct.data, struct.length) - if(dealloc) - module._free(struct.data) - return result +// String + +fun Arena.toNativeStringOnArena(str: String): Int { + val size = module.lengthBytesUTF8(str) + 1 + val strMem = alloc(size) + module.stringToUTF8(str, strMem, size) + + val mem = alloc(layoutString.size) + module.HEAP32[(mem + layoutString[0]) shr 2] = strMem + module.HEAP32[(mem + layoutString[1]) shr 2] = size + module.HEAP32[(mem + layoutString[2]) shr 2] = str.length + module.HEAP8[mem + layoutString[3]] = 0 + return mem } -fun fillEmString(module: EmModule, ptr: Int, str: EmString) { - module.HEAP32[ptr shr 2] = str.data - module.HEAP32[(ptr shr 2) + 1] = str.length - module.HEAP32[(ptr shr 2) + 2] = str.releasable.toInt() - module.HEAP32[(ptr shr 2) + 3] = str.released.toInt() +fun toNativeString(module: EmModule, str: String): Int { + val size = module.lengthBytesUTF8(str) + 1 + val strMem = module._malloc(size) + module.stringToUTF8(str, strMem, size) + + val mem = module._malloc(layoutString.size) + module.HEAP32[(mem + layoutString[0]) shr 2] = strMem + module.HEAP32[(mem + layoutString[1]) shr 2] = size + module.HEAP32[(mem + layoutString[2]) shr 2] = str.length + module.HEAP8[mem + layoutString[3]] = FLAG_RELEASABLE + return mem } -fun extractEmString(module: EmModule, ptr: Int): EmString = createJsObject { - data = module.HEAP32[ptr shr 2] - length = module.HEAP32[(ptr shr 2) + 1] +fun toKotlinString(module: EmModule, mem: Int): String { + return module.UTF8ToString( + module.HEAP32[(mem + layoutString[0]) shr 2], + module.HEAP32[(mem + layoutString[1]) shr 2] + ) } // Array -fun fillEmArray(module: EmModule, ptr: Int, arr: EmArray) { - module.HEAP32[ptr shr 2] = arr.elements - module.HEAP32[(ptr shr 2) + 1] = arr.size - module.HEAP32[(ptr shr 2) + 2] = arr.releasable.toInt() - module.HEAP32[(ptr shr 2) + 3] = arr.released.toInt() +private fun fillArray(module: EmModule, mem: Int, data: Int, size: Int, length: Int, flags: Byte): Int { + module.HEAP32[(mem + layoutArray[0]) shr 2] = data + module.HEAP32[(mem + layoutArray[1]) shr 2] = size + module.HEAP32[(mem + layoutArray[2]) shr 2] = length + module.HEAP8[mem + layoutArray[3]] = flags + return mem } -fun extractEmArray(module: EmModule, ptr: Int): EmArray = createJsObject { - elements = module.HEAP32[ptr shr 2] - size = module.HEAP32[(ptr shr 2) + 1] -} +private fun arrayData(module: EmModule, mem: Int) = module.HEAP32[(mem + layoutArray[0]) shr 2] +private fun arrayLength(module: EmModule,mem: Int) = module.HEAP32[(mem + layoutArray[2]) shr 2] // Array: char -fun toNativeCharArray(module: EmModule, arr: CharArray, releasable: Boolean): EmArray { - val elements = module._malloc(arr.size * Char.SIZE_BYTES) - Int16Array(module.HEAP8.buffer, elements, arr.size) - .set(createJsArray(arr.size) { - arr[it].code.toJsNumber() - }) - return createJsObject { - this.elements = elements - this.size = arr.size - this.releasable = releasable - this.released = false - } -} - -fun toKotlinCharArray(module: EmModule, struct: EmArray, dealloc: Boolean): CharArray { - val result = JsArrayTools.from(Int16Array(module.HEAP8.buffer, struct.elements, struct.size)) - .run { CharArray(struct.size) { - this[it]!!.toInt().toChar() - }} - if(dealloc) module._free(struct.elements) - return result +fun Arena.toNativeCharArrayOnArena(arr: CharArray): Int { + val size = arr.size * 2 + val data = alloc(size) + val heap = Uint16Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, alloc(layoutArray.size), data, size, arr.size, 0) } -// Array: boolean +fun toNativeCharArray(module: EmModule, arr: CharArray): Int { + val size = arr.size * 2 + val data = module._malloc(size) + val heap = Uint16Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, module._malloc(layoutArray.size), data, size, arr.size, FLAG_RELEASABLE) +} -fun toNativeBooleanArray(module: EmModule, arr: BooleanArray, releasable: Boolean): EmArray { - val elements = module._malloc(arr.size * Byte.SIZE_BYTES) - Int8Array(module.HEAP8.buffer, elements, arr.size) - .set(createJsArray(arr.size) { - (if(arr[it]) 1 else 0).toJsNumber() - }) - return createJsObject { - this.elements = elements - this.size = arr.size - this.releasable = releasable - this.released = false - } -} - -fun toKotlinBooleanArray(module: EmModule, struct: EmArray, dealloc: Boolean): BooleanArray { - val result = JsArrayTools.from(Int8Array(module.HEAP8.buffer, struct.elements, struct.size)) - .run { BooleanArray(struct.size) { - this[it]!!.toInt() == 1 - }} - if(dealloc) module._free(struct.elements) - return result +fun toKotlinCharArray(module: EmModule, mem: Int): CharArray { + val length = arrayLength(module, mem) + val arr = Uint16Array(module.HEAP8.buffer, arrayData(module, mem), length) + return CharArray(length) { arr[it] } } +// Array: boolean + +fun Arena.toNativeBooleanArrayOnArena(arr: BooleanArray): Int = + toNativeByteArrayOnArena(ByteArray(arr.size) { if(arr[it]) 1.toByte() else 0.toByte() }) + +fun toNativeBooleanArray(module: EmModule, arr: BooleanArray): Int = + toNativeByteArray(module, ByteArray(arr.size) { if(arr[it]) 1.toByte() else 0.toByte() }) + +fun toKotlinBooleanArray(module: EmModule, mem: Int): BooleanArray = + toKotlinByteArray(module, mem).run { BooleanArray(size) { get(it) == 1.toByte() } } + // Array: byte -fun toNativeByteArray(module: EmModule, arr: ByteArray, releasable: Boolean): EmArray { - val elements = module._malloc(arr.size * Byte.SIZE_BYTES) - Int8Array(module.HEAP8.buffer, elements, arr.size) - .set(createJsArray(arr.size) { - arr[it].toInt().toJsNumber() - }) - return createJsObject { - this.elements = elements - this.size = arr.size - this.releasable = releasable - this.released = false - } -} - -fun toKotlinByteArray(module: EmModule, struct: EmArray, dealloc: Boolean): ByteArray { - val result = JsArrayTools.from(Int8Array(module.HEAP8.buffer, struct.elements, struct.size)) - .run { ByteArray(struct.size) { - this[it]!!.toInt().toByte() - }} - if(dealloc) module._free(struct.elements) - return result +fun Arena.toNativeByteArrayOnArena(arr: ByteArray): Int { + val data = alloc(arr.size) + val heap = Int8Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, alloc(layoutArray.size), data, arr.size, arr.size, 0) +} + +fun toNativeByteArray(module: EmModule, arr: ByteArray): Int { + val data = module._malloc(arr.size) + val heap = Int8Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, module._malloc(layoutArray.size), data, arr.size, arr.size, FLAG_RELEASABLE) +} + +fun toKotlinByteArray(module: EmModule, mem: Int): ByteArray { + val length = arrayLength(module, mem) + val arr = Int8Array(module.HEAP8.buffer, arrayData(module, mem), length) + return ByteArray(length) { arr[it] } } // Array: short -fun toNativeShortArray(module: EmModule, arr: ShortArray, releasable: Boolean): EmArray { - val elements = module._malloc(arr.size * Short.SIZE_BYTES) - Int16Array(module.HEAP8.buffer, elements, arr.size) - .set(createJsArray(arr.size) { - arr[it].toInt().toJsNumber() - }) - return createJsObject { - this.elements = elements - this.size = arr.size - this.releasable = releasable - this.released = false - } -} - -fun toKotlinShortArray(module: EmModule, struct: EmArray, dealloc: Boolean): ShortArray { - val result = JsArrayTools.from(Int16Array(module.HEAP8.buffer, struct.elements, struct.size)) - .run { ShortArray(struct.size) { - this[it]!!.toInt().toShort() - }} - if(dealloc) module._free(struct.elements) - return result +fun Arena.toNativeShortArrayOnArena(arr: ShortArray): Int { + val size = arr.size * 2 + val data = alloc(size) + val heap = Int16Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, alloc(layoutArray.size), data, size, arr.size, 0) +} + +fun toNativeShortArray(module: EmModule, arr: ShortArray): Int { + val size = arr.size * 2 + val data = module._malloc(size) + val heap = Int16Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, module._malloc(layoutArray.size), data, size, arr.size, FLAG_RELEASABLE) +} + +fun toKotlinShortArray(module: EmModule, mem: Int): ShortArray { + val length = arrayLength(module, mem) + val arr = Int16Array(module.HEAP8.buffer, arrayData(module, mem), length) + return ShortArray(length) { arr[it] } } // Array: int -fun toNativeIntArray(module: EmModule, arr: IntArray, releasable: Boolean): EmArray { - val elements = module._malloc(arr.size * Int.SIZE_BYTES) - Int32Array(module.HEAP8.buffer, elements, arr.size) - .set(createJsArray(arr.size) { - arr[it].toJsNumber() - }) - return createJsObject { - this.elements = elements - this.size = arr.size - this.releasable = releasable - this.released = false - } -} - -fun toKotlinIntArray(module: EmModule, struct: EmArray, dealloc: Boolean): IntArray { - val result = JsArrayTools.from(Int32Array(module.HEAP8.buffer, struct.elements, struct.size)) - .run { IntArray(struct.size) { - this[it]!!.toInt() - }} - if(dealloc) module._free(struct.elements) - return result +fun Arena.toNativeIntArrayOnArena(arr: IntArray): Int { + val size = arr.size * 4 + val data = alloc(size) + val heap = Int32Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, alloc(layoutArray.size), data, size, arr.size, 0) +} + +fun toNativeIntArray(module: EmModule, arr: IntArray): Int { + val size = arr.size * 4 + val data = module._malloc(size) + val heap = Int32Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, module._malloc(layoutArray.size), data, size, arr.size, FLAG_RELEASABLE) +} + +fun toKotlinIntArray(module: EmModule, mem: Int): IntArray { + val length = arrayLength(module, mem) + val arr = Int32Array(module.HEAP8.buffer, arrayData(module, mem), length) + return IntArray(length) { arr[it] } } // Array: long -fun toNativeLongArray(module: EmModule, arr: LongArray, releasable: Boolean): EmArray { - val elements = module._malloc(arr.size * Long.SIZE_BYTES) - BigInt64Array(module.HEAP8.buffer, elements, arr.size) - .set(createJsArray(arr.size) { - arr[it].toJsBigInt() - }) - return createJsObject { - this.elements = elements - this.size = arr.size - this.releasable = releasable - this.released = false - } -} - -fun toKotlinLongArray(module: EmModule, struct: EmArray, dealloc: Boolean): LongArray { - val result = JsArrayTools.from(BigInt64Array(module.HEAP8.buffer, struct.elements, struct.size)) - .run { LongArray(struct.size) { - this[it]!!.toLong() - }} - if(dealloc) module._free(struct.elements) - return result +fun Arena.toNativeLongArrayOnArena(arr: LongArray): Int { + val size = arr.size * 8 + val data = alloc(size) + val heap = BigInt64Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, alloc(layoutArray.size), data, size, arr.size, 0) +} + +fun toNativeLongArray(module: EmModule, arr: LongArray): Int { + val size = arr.size * 8 + val data = module._malloc(size) + val heap = BigInt64Array(module.HEAP8.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, module._malloc(layoutArray.size), data, size, arr.size, FLAG_RELEASABLE) +} + +fun toKotlinLongArray(module: EmModule, mem: Int): LongArray { + val length = arrayLength(module, mem) + val arr = BigInt64Array(module.HEAP8.buffer, arrayData(module, mem), length) + return LongArray(length) { arr[it] } } // Array: float -fun toNativeFloatArray(module: EmModule, arr: FloatArray, releasable: Boolean): EmArray { - val elements = module._malloc(arr.size * Float.SIZE_BYTES) - Float32Array(module.HEAPF32.buffer, elements, arr.size) - .set(createJsArray(arr.size) { - arr[it].toDouble().toJsNumber() - }) - return createJsObject { - this.elements = elements - this.size = arr.size - this.releasable = releasable - this.released = false - } -} - -fun toKotlinFloatArray(module: EmModule, struct: EmArray, dealloc: Boolean): FloatArray { - val result = JsArrayTools.from(Float32Array(module.HEAPF32.buffer, struct.elements, struct.size)) - .run { FloatArray(struct.size) { - this[it]!!.toDouble().toFloat().truncF32() - }} - if(dealloc) module._free(struct.elements) - return result +fun Arena.toNativeFloatArrayOnArena(arr: FloatArray): Int { + val size = arr.size * 4 + val data = alloc(size) + val heap = Float32Array(module.HEAPF32.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, alloc(layoutArray.size), data, size, arr.size, 0) +} + +fun toNativeFloatArray(module: EmModule, arr: FloatArray): Int { + val size = arr.size * 4 + val data = module._malloc(size) + val heap = Float32Array(module.HEAPF32.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, module._malloc(layoutArray.size), data, size, arr.size, FLAG_RELEASABLE) +} + +fun toKotlinFloatArray(module: EmModule, mem: Int): FloatArray { + val length = arrayLength(module, mem) + val arr = Float32Array(module.HEAPF32.buffer, arrayData(module, mem), length) + return FloatArray(length) { arr[it] } } // Array: double -fun toNativeDoubleArray(module: EmModule, arr: DoubleArray, releasable: Boolean): EmArray { - val elements = module._malloc(arr.size * Double.SIZE_BYTES) - Float64Array(module.HEAPF32.buffer, elements, arr.size) - .set(createJsArray(arr.size) { - arr[it].toJsNumber() - }) - return createJsObject { - this.elements = elements - this.size = arr.size - this.releasable = releasable - this.released = false - } -} - -fun toKotlinDoubleArray(module: EmModule, struct: EmArray, dealloc: Boolean): DoubleArray { - val result = JsArrayTools.from(Float64Array(module.HEAPF32.buffer, struct.elements, struct.size)) - .run { DoubleArray(struct.size) { - this[it]!!.toDouble() - }} - if(dealloc) module._free(struct.elements) - return result +fun Arena.toNativeDoubleArrayOnArena(arr: DoubleArray): Int { + val size = arr.size * 8 + val data = alloc(size) + val heap = Float64Array(module.HEAPF64.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, alloc(layoutArray.size), data, size, arr.size, 0) +} + +fun toNativeDoubleArray(module: EmModule, arr: DoubleArray): Int { + val size = arr.size * 8 + val data = module._malloc(size) + val heap = Float64Array(module.HEAPF64.buffer, data, arr.size) + arr.forEachIndexed { i, it -> heap[i] = it } + return fillArray(module, module._malloc(layoutArray.size), data, size, arr.size, FLAG_RELEASABLE) +} + +fun toKotlinDoubleArray(module: EmModule, mem: Int): DoubleArray { + val length = arrayLength(module, mem) + val arr = Float64Array(module.HEAPF64.buffer, arrayData(module, mem), length) + return DoubleArray(length) { arr[it] } } // Array: enum -fun > toNativeEnumArray(module: EmModule, arr: Array, releasable: Boolean): EmArray = - toNativeIntArray(module, IntArray(arr.size) { arr[it].ordinal }, releasable) +fun > Arena.toNativeEnumArrayOnArena(arr: Array): Int = + toNativeIntArrayOnArena(IntArray(arr.size) { arr[it].ordinal }) -inline fun > toKotlinEnumArray( - module: EmModule, - struct: EmArray, - dealloc: Boolean, - noinline create: (Int) -> T -): Array = toKotlinIntArray(module, struct, dealloc).run { Array(size, create) } +fun > toNativeEnumArray(module: EmModule, arr: Array): Int = + toNativeIntArray(module, IntArray(arr.size) { arr[it].ordinal }) + +inline fun > toKotlinEnumArray(module: EmModule, mem: Int): Array { + val entries = enumValues() + val ints = toKotlinIntArray(module, mem) + return Array(ints.size) { entries[ints[it]] } +} // Array: objects +fun Arena.toNativeArrayOnArena( + arr: Array, + converter: (T) -> Int +) = toNativeIntArrayOnArena(IntArray(arr.size) { converter(arr[it]) }) + fun toNativeArray( module: EmModule, arr: Array, - converter: (T, Boolean) -> Int, - releasable: Boolean -) = toNativeIntArray(module, IntArray(arr.size) { converter(arr[it], releasable) }, releasable) + converter: (T) -> Int +) = toNativeIntArray(module, IntArray(arr.size) { converter(arr[it]) }) @Suppress("unchecked_cast") fun toKotlinArray( module: EmModule, - struct: EmArray, - converter: (Int, Boolean) -> T, - dealloc: Boolean, - deallocContent: Boolean -) = toKotlinIntArray(module, struct, dealloc).run { Array(size) { converter(this[it], deallocContent) } as Array } + struct: Int, + converter: (Int) -> T +) = toKotlinIntArray(module, struct).run { Array(size) { converter(this[it]) } as Array } // Callbacks -fun mallocCallback( - module: EmModule, +fun Arena.toNativeCallbackOnArena( callback: Any, invoke: Int, + clone: Int, + equals: Int, + hashCode: Int, free: Int ): Int { - val ptr = module._malloc(12) - // 'm' is not used - // module.HEAP32[ptr shr 2] = 0 - module.HEAP32[(ptr shr 2) + 1] = invoke - module.HEAP32[(ptr shr 2) + 2] = free + val module = module + val mem = alloc(layoutCallback.size) + module.HEAP8[mem + layoutCallback[0]] = 0 + module.HEAP32[(mem + layoutCallback[1]) shr 2] = invoke + module.HEAP32[(mem + layoutCallback[2]) shr 2] = clone + module.HEAP32[(mem + layoutCallback[3]) shr 2] = equals + module.HEAP32[(mem + layoutCallback[4]) shr 2] = hashCode + module.HEAP32[(mem + layoutCallback[5]) shr 2] = free + + val key = Pair(module, mem) + callbacks[key] = callback + defer { callbacks -= key } + return mem +} - callbacks[Pair(module, ptr)] = callback - return ptr +fun toNativeCallback( + module: EmModule, + callback: Any, + invoke: Int, + clone: Int, + equals: Int, + hashCode: Int, + free: Int +): Int { + val mem = module._malloc(layoutCallback.size) + module.HEAP8[mem + layoutCallback[0]] = FLAG_RELEASABLE + module.HEAP32[(mem + layoutCallback[1]) shr 2] = invoke + module.HEAP32[(mem + layoutCallback[2]) shr 2] = clone + module.HEAP32[(mem + layoutCallback[3]) shr 2] = equals + module.HEAP32[(mem + layoutCallback[4]) shr 2] = hashCode + module.HEAP32[(mem + layoutCallback[5]) shr 2] = free + callbacks[Pair(module, mem)] = callback + return mem } @Suppress("UNCHECKED_CAST") fun toKotlinCallback( module: EmModule, - ptr: Int, - dealloc: Boolean -): T { - val result = callbacks[Pair(module, ptr)] - if(dealloc) - freeCallback(module, ptr) - return result as T -} + mem: Int, +): T = callbacks[Pair(module, mem)] as T -fun freeCallback( +fun callbackFree( module: EmModule, - ptr: Int + self: Int ) { - callbacks.remove(Pair(module, ptr)) - module._free(ptr) + if(module.HEAP8[self + layoutCallback[0]] and FLAG_RELEASABLE != FLAG_RELEASABLE) + return + + callbacks.remove(Pair(module, self)) + module._free(self) +} + +private fun callbackClone( + module: EmModule, + self: Int +): Int { + val mem = module._malloc(layoutCallback.size) + module.HEAP8[mem + layoutCallback[0]] = FLAG_RELEASABLE + module.HEAP32[(mem + layoutCallback[1]) shr 2] = module.HEAP32[(self + layoutCallback[1]) shr 2] + module.HEAP32[(mem + layoutCallback[2]) shr 2] = module.HEAP32[(self + layoutCallback[2]) shr 2] + module.HEAP32[(mem + layoutCallback[3]) shr 2] = module.HEAP32[(self + layoutCallback[3]) shr 2] + module.HEAP32[(mem + layoutCallback[4]) shr 2] = module.HEAP32[(self + layoutCallback[4]) shr 2] + module.HEAP32[(mem + layoutCallback[5]) shr 2] = module.HEAP32[(self + layoutCallback[5]) shr 2] + callbacks[Pair(module, mem)] = toKotlinCallback(module, self) + return mem } -private fun freeCallbackJs(block: (Int) -> Unit): JsAny = js("block") +private fun callbackEquals( + module: EmModule, + self: Int, + obj: Int +): Boolean = callbacks[Pair(module, self)] == callbacks[Pair(module, obj)] + +private fun callbackHashCode( + module: EmModule, + self: Int +): Int = callbacks[Pair(module, self)]!!.hashCode() + +private fun funcVP(block: (Int) -> Unit): JsAny = js("block") +private fun funcPP(block: (Int) -> Int): JsAny = js("block") +private fun funcIPP(block: (Int, Int) -> Boolean): JsAny = js("block") +private fun funcIP(block: (Int) -> Int): JsAny = js("block") + +fun createCallbackCloneFunction(module: EmModule): Int { + return module.addFunction(funcPP { self: Int -> + callbackClone(module, self) + }, "pp") +} + +fun createCallbackEqualsFunction(module: EmModule): Int { + return module.addFunction(funcIPP { self: Int, obj: Int -> + callbackEquals(module, self, obj) + }, "ipp") +} + +fun createCallbackHashCodeFunction(module: EmModule): Int { + return module.addFunction(funcIP { self: Int -> + callbackHashCode(module, self) + }, "ip") +} -fun createCallbackFreeFunction(module: EmModule): Int = - module.addFunction(freeCallbackJs { callback: Int -> - freeCallback(module, callback) - }, "vp") \ No newline at end of file +fun createCallbackFreeFunction(module: EmModule): Int { + return module.addFunction(funcVP { self: Int -> + callbackFree(module, self) + }, "vp") +} \ No newline at end of file diff --git a/modules/test-js-only/build.gradle.kts b/modules/test-js-only/build.gradle.kts deleted file mode 100644 index 5ff64cf..0000000 --- a/modules/test-js-only/build.gradle.kts +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - alias(libs.plugins.kotlin.js) - - id("com.huskerdev.native-kt") -} - -group = "com.huskerdev" -version = "1.0" - -kotlin { - js { - nodejs() - browser() - compilerOptions { - target = "es2015" - } - } -} - -dependencies { - implementation(libs.kotlinx.coroutines) - implementation(project(":modules:runtime")) - - testImplementation(kotlin("test")) - testImplementation(libs.kotlinx.coroutines.test) -} - -natives { - applyRuntime = false - - create("jsOnlyTest") -} \ No newline at end of file diff --git a/modules/test-js-only/natives/jsOnlyTest/CMakeLists.txt b/modules/test-js-only/natives/jsOnlyTest/CMakeLists.txt deleted file mode 100644 index d0d3eda..0000000 --- a/modules/test-js-only/natives/jsOnlyTest/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.15) - -project("jsOnlyTest") - -add_library(${PROJECT_NAME} STATIC src/jsOnlyTest.c) - -target_include_directories(${PROJECT_NAME} PRIVATE include) \ No newline at end of file diff --git a/modules/test-js-only/natives/jsOnlyTest/api.ndl b/modules/test-js-only/natives/jsOnlyTest/api.ndl deleted file mode 100644 index a01cf55..0000000 --- a/modules/test-js-only/natives/jsOnlyTest/api.ndl +++ /dev/null @@ -1,4 +0,0 @@ - -namespace global { - void helloWorld(); -}; \ No newline at end of file diff --git a/modules/test-js-only/natives/jsOnlyTest/include/api.h b/modules/test-js-only/natives/jsOnlyTest/include/api.h deleted file mode 100644 index 5918965..0000000 --- a/modules/test-js-only/natives/jsOnlyTest/include/api.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * This file was automatically generated by Gradle. - * - * DO NOT EDIT THIS FILE MANUALLY. - * Any changes made to this file will be overwritten the next time - * the project is built. - */ - -#ifndef KOTLIN_NATIVE_JSONLYTEST_H -#define KOTLIN_NATIVE_JSONLYTEST_H - -#include -#include - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// ╔════════════════╗ -// ║ stdlib ║ -// ╚════════════════╝ - -typedef int32_t KInt; -typedef int64_t KLong; -typedef float KFloat; -typedef double KDouble; -typedef int8_t KByte; -typedef int16_t KShort; -typedef bool KBoolean; -typedef uint16_t KChar; - -typedef struct KString { - const char* data; - KInt length; - KBoolean releasable; - KBoolean released; -} KString; - -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; -} - -static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } -} - -#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) -#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) -#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count - -#define KArrayDef(Name, Type, VarargType) \ -typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ -} Name; \ - \ -static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ -} \ - \ -static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ -} \ - \ -static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ -} - -KArrayDef(KCharArray, KChar, int32_t) -KArrayDef(KBooleanArray, KBoolean, int32_t) -KArrayDef(KByteArray, KByte, int32_t) -KArrayDef(KShortArray, KShort, int32_t) -KArrayDef(KIntArray, KInt, int32_t) -KArrayDef(KLongArray, KLong, int64_t) -KArrayDef(KFloatArray, KFloat, double) -KArrayDef(KDoubleArray, KDouble, double) -KArrayDef(KArray, void*, void*) -#undef KArrayDef - -#define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KBooleanArray_of(...) _KBooleanArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KByteArray_of(...) _KByteArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KShortArray_of(...) _KShortArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KIntArray_of(...) _KIntArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KFloatArray_of(...) _KFloatArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ -}; - -// ╔═══════════════════╗ -// ║ Functions ║ -// ╚═══════════════════╝ - -void helloWorld(); - -#ifdef __cplusplus -} -#endif - -#endif // KOTLIN_NATIVE_JSONLYTEST_H \ No newline at end of file diff --git a/modules/test-js-only/natives/jsOnlyTest/src/jsOnlyTest.c b/modules/test-js-only/natives/jsOnlyTest/src/jsOnlyTest.c deleted file mode 100644 index 8d522e3..0000000 --- a/modules/test-js-only/natives/jsOnlyTest/src/jsOnlyTest.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -void helloWorld() { - printf("Hello, World!\n"); - fflush(stdout); -} \ No newline at end of file diff --git a/modules/test-js-only/src/test/kotlin/Test.kt b/modules/test-js-only/src/test/kotlin/Test.kt deleted file mode 100644 index 3f16fcf..0000000 --- a/modules/test-js-only/src/test/kotlin/Test.kt +++ /dev/null @@ -1,12 +0,0 @@ -import kotlinx.coroutines.test.runTest -import natives.jsOnlyTest.loadLibJsOnlyTest -import kotlin.test.Test - -class Test { - - @Test - fun helloWorld() = runTest { - loadLibJsOnlyTest() - natives.jsOnlyTest.helloWorld() - } -} \ No newline at end of file diff --git a/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h b/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h index 18338b1..e9b8c64 100644 --- a/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h +++ b/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h @@ -10,8 +10,6 @@ #define KOTLIN_NATIVE_JVMONLYTEST_H #include -#include - #include #include @@ -19,6 +17,18 @@ extern "C" { #endif +#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) +#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ + 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ + 5, 4, 3, 2, 1, 0) +#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ + _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ + _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ + _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ + _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + // ╔════════════════╗ // ║ stdlib ║ // ╚════════════════╝ @@ -34,72 +44,35 @@ typedef uint16_t KChar; typedef struct KString { const char* data; + size_t size; KInt length; - KBoolean releasable; - KBoolean released; + char __flags; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; -} - -static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } -} - -#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) -#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) -#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count - -#define KArrayDef(Name, Type, VarargType) \ -typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ -} Name; \ - \ -static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ -} \ - \ -static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ -} \ - \ -static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ -} - -KArrayDef(KCharArray, KChar, int32_t) -KArrayDef(KBooleanArray, KBoolean, int32_t) -KArrayDef(KByteArray, KByte, int32_t) -KArrayDef(KShortArray, KShort, int32_t) -KArrayDef(KIntArray, KInt, int32_t) -KArrayDef(KLongArray, KLong, int64_t) -KArrayDef(KFloatArray, KFloat, double) -KArrayDef(KDoubleArray, KDouble, double) -KArrayDef(KArray, void*, void*) +KString* KString_new(const char* data, KInt length, KInt size); +KString* KString_clone(const KString* self); +void KString_free(KString* self); + +#define KArrayDef(Name, Type) \ +typedef struct Name { \ + const Type* elements; \ + size_t size; \ + KInt length; \ + char __flags; \ +} Name; \ + \ +Name* Name##_new(const Type* elements, const KInt length); \ +Name* _##Name##_of(const int n, ...); + +KArrayDef(KCharArray, KChar ) +KArrayDef(KBooleanArray, KBoolean) +KArrayDef(KByteArray, KByte ) +KArrayDef(KShortArray, KShort ) +KArrayDef(KIntArray, KInt ) +KArrayDef(KLongArray, KLong ) +KArrayDef(KFloatArray, KFloat ) +KArrayDef(KDoubleArray, KDouble ) +KArrayDef(KArray, void* ) #undef KArrayDef #define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) @@ -111,11 +84,31 @@ KArrayDef(KArray, void*, void*) #define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ +#define KArrayCloneFreeDef(Name, Type) \ +Name* Name##_clone(const Name* self); \ +void Name##_free(Name* self); + +KArrayCloneFreeDef(KCharArray, KChar) +KArrayCloneFreeDef(KBooleanArray, KBoolean) +KArrayCloneFreeDef(KByteArray, KByte) +KArrayCloneFreeDef(KShortArray, KShort) +KArrayCloneFreeDef(KIntArray, KInt) +KArrayCloneFreeDef(KLongArray, KLong) +KArrayCloneFreeDef(KFloatArray, KFloat) +KArrayCloneFreeDef(KDoubleArray, KDouble) +#undef KArrayCloneFreeDef + +KArray* KArray_clone(const KArray* self, void* (*cloneOp)(void*)); +void KArray_free(const KArray* self, void (*freeOp)(void*)); + +#define KCallbackDef(Name, Type, ...) \ +struct Name { \ + char __flags; \ + Type (*invoke)(Name* self, ##__VA_ARGS__); \ + Name* (*clone)(Name* self); \ + KBoolean (*equals)(Name* self, Name* obj); \ + KInt (*hashCode)(Name* self); \ + void (*free)(Name* self); \ }; // ╔═══════════════════╗ diff --git a/modules/tests/build.gradle.kts b/modules/tests/build.gradle.kts index cd8de7c..8c916df 100644 --- a/modules/tests/build.gradle.kts +++ b/modules/tests/build.gradle.kts @@ -109,4 +109,7 @@ tasks.withType().configureEach { if (name.contains("jvm", ignoreCase = true)) { jvmArgs = listOf("--enable-native-access=ALL-UNNAMED") } + testLogging { + showStandardStreams = true + } } diff --git a/modules/tests/natives/test/api.ndl b/modules/tests/natives/test/api.ndl index 7976b4a..93aed0a 100644 --- a/modules/tests/natives/test/api.ndl +++ b/modules/tests/natives/test/api.ndl @@ -25,7 +25,6 @@ namespace global { long returnLong(); float returnFloat(); double returnDouble(); - string returnStringLiteral(); [Dealloc] string returnString(); MyEnum returnEnum(); MyDictionary returnDictionary(); @@ -135,7 +134,7 @@ namespace global { // Dictionary boolean passBigDictionary(TypeDictionary arg); [Dealloc] TypeDictionary returnBigDictionary(VoidCallback callback); - [Dealloc] TypeDictionary pingBigDictionary(TypeDictionary arg); + TypeDictionary pingBigDictionary(TypeDictionary arg); // Critical [Critical] boolean criticalPrimitives(char a1, boolean a2, byte a3, short a4, int a5, long a6, float a7, double a8); diff --git a/modules/tests/natives/test/include/api.h b/modules/tests/natives/test/include/api.h index 6c95917..a7b3a2d 100644 --- a/modules/tests/natives/test/include/api.h +++ b/modules/tests/natives/test/include/api.h @@ -10,8 +10,6 @@ #define KOTLIN_NATIVE_TEST_H #include -#include - #include #include @@ -19,6 +17,18 @@ extern "C" { #endif +#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) +#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ + 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ + 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ + 5, 4, 3, 2, 1, 0) +#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ + _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ + _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ + _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ + _7, _6, _5, _4, _3, _2, _1, Count, ...) Count + // ╔════════════════╗ // ║ stdlib ║ // ╚════════════════╝ @@ -34,72 +44,35 @@ typedef uint16_t KChar; typedef struct KString { const char* data; + size_t size; KInt length; - KBoolean releasable; - KBoolean released; + char __flags; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; -} - -static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } -} - -#define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) -#define ARG_LENGTH__(...) ARG_LENGTH_(,##__VA_ARGS__, \ - 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,\ - 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,\ - 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,\ - 5, 4, 3, 2, 1, 0) -#define ARG_LENGTH_(_, _63, _62, _61, _60, _59, _58, _57, _56, _55, _54, _53, \ - _52, _51, _50, _49, _48, _47, _46, _45, _44, _43, _42, _41, _40, _39, _38, \ - _37, _36, _35, _34, _33, _32, _31, _30, _29, _28, _27, _26, _25, _24, _23, \ - _22, _21, _20, _19, _18, _17, _16, _15, _14, _13, _12, _11, _10, _9, _8, \ - _7, _6, _5, _4, _3, _2, _1, Count, ...) Count - -#define KArrayDef(Name, Type, VarargType) \ -typedef struct Name { \ - const Type* elements; \ - KInt size; \ - KBoolean releasable; \ - KBoolean released; \ -} Name; \ - \ -static Name Name##_new(const Type* elements, const KInt size) { \ - return (Name){ elements, size, true, false }; \ -} \ - \ -static Name _##Name##_of(const int n, ...) { \ - va_list args; \ - va_start(args, n); \ - Type* elements = (Type*)malloc(n * sizeof(Type)); \ - for (int i = 0; i < n; i++) \ - elements[i] = (Type)va_arg(args, VarargType); \ - va_end(args); \ - return (Name){ (const Type*) elements, n, true, false }; \ -} \ - \ -static void Name##_free(Name* arr) { \ - if(arr->releasable && !arr->released) { \ - free((void*)arr->elements); \ - arr->released = true; \ - } \ -} - -KArrayDef(KCharArray, KChar, int32_t) -KArrayDef(KBooleanArray, KBoolean, int32_t) -KArrayDef(KByteArray, KByte, int32_t) -KArrayDef(KShortArray, KShort, int32_t) -KArrayDef(KIntArray, KInt, int32_t) -KArrayDef(KLongArray, KLong, int64_t) -KArrayDef(KFloatArray, KFloat, double) -KArrayDef(KDoubleArray, KDouble, double) -KArrayDef(KArray, void*, void*) +KString* KString_new(const char* data, KInt length, KInt size); +KString* KString_clone(const KString* self); +void KString_free(KString* self); + +#define KArrayDef(Name, Type) \ +typedef struct Name { \ + const Type* elements; \ + size_t size; \ + KInt length; \ + char __flags; \ +} Name; \ + \ +Name* Name##_new(const Type* elements, const KInt length); \ +Name* _##Name##_of(const int n, ...); + +KArrayDef(KCharArray, KChar ) +KArrayDef(KBooleanArray, KBoolean) +KArrayDef(KByteArray, KByte ) +KArrayDef(KShortArray, KShort ) +KArrayDef(KIntArray, KInt ) +KArrayDef(KLongArray, KLong ) +KArrayDef(KFloatArray, KFloat ) +KArrayDef(KDoubleArray, KDouble ) +KArrayDef(KArray, void* ) #undef KArrayDef #define KCharArray_of(...) _KCharArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) @@ -111,11 +84,31 @@ KArrayDef(KArray, void*, void*) #define KDoubleArray_of(...) _KDoubleArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) #define KArray_of(...) _KArray_of(ARG_LENGTH(__VA_ARGS__), __VA_ARGS__) -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - void *m; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - void (*free)(Name* _); \ +#define KArrayCloneFreeDef(Name, Type) \ +Name* Name##_clone(const Name* self); \ +void Name##_free(Name* self); + +KArrayCloneFreeDef(KCharArray, KChar) +KArrayCloneFreeDef(KBooleanArray, KBoolean) +KArrayCloneFreeDef(KByteArray, KByte) +KArrayCloneFreeDef(KShortArray, KShort) +KArrayCloneFreeDef(KIntArray, KInt) +KArrayCloneFreeDef(KLongArray, KLong) +KArrayCloneFreeDef(KFloatArray, KFloat) +KArrayCloneFreeDef(KDoubleArray, KDouble) +#undef KArrayCloneFreeDef + +KArray* KArray_clone(const KArray* self, void* (*cloneOp)(void*)); +void KArray_free(const KArray* self, void (*freeOp)(void*)); + +#define KCallbackDef(Name, Type, ...) \ +struct Name { \ + char __flags; \ + Type (*invoke)(Name* self, ##__VA_ARGS__); \ + Name* (*clone)(Name* self); \ + KBoolean (*equals)(Name* self, Name* obj); \ + KInt (*hashCode)(Name* self); \ + void (*free)(Name* self); \ }; // ╔═══════════════════╗ @@ -189,15 +182,25 @@ typedef enum { struct ParentDictionary { KInt a; KInt b; + char __flags; }; +ParentDictionary* ParentDictionary_new(KInt a, KInt b); +ParentDictionary* ParentDictionary_clone(const ParentDictionary* self); +void ParentDictionary_free(ParentDictionary* self); + struct MyDictionary { // : ParentDictionary KInt a; KInt b; KInt c; KInt d; + char __flags; }; +MyDictionary* MyDictionary_new(KInt a, KInt b, KInt c, KInt d); +MyDictionary* MyDictionary_clone(const MyDictionary* self); +void MyDictionary_free(MyDictionary* self); + struct TypeDictionary { KChar a1; KBoolean a2; @@ -207,39 +210,26 @@ struct TypeDictionary { KLong a6; KFloat a7; KDouble a8; - KString a9; + KString* a9; MyEnum a10; MyDictionary* a11; VoidCallback* a12; - KCharArray a13; - KBooleanArray a14; - KByteArray a15; - KShortArray a16; - KIntArray a17; - KLongArray a18; - KFloatArray a19; - KDoubleArray a20; - KIntArray a21; - KArray a22; + KCharArray* a13; + KBooleanArray* a14; + KByteArray* a15; + KShortArray* a16; + KIntArray* a17; + KLongArray* a18; + KFloatArray* a19; + KDoubleArray* a20; + KIntArray* a21; + KArray* a22; + char __flags; }; -static ParentDictionary* ParentDictionary_new(const KInt a, const KInt b) { - ParentDictionary* result = (ParentDictionary*)malloc(sizeof(ParentDictionary)); - *result = (ParentDictionary){ a, b }; - return result; -} - -static MyDictionary* MyDictionary_new(const KInt a, const KInt b, const KInt c, const KInt d) { - MyDictionary* result = (MyDictionary*)malloc(sizeof(MyDictionary)); - *result = (MyDictionary){ a, b, c, d }; - return result; -} - -static TypeDictionary* TypeDictionary_new(const KChar a1, const KBoolean a2, const KByte a3, const KShort a4, const KInt a5, const KLong a6, const KFloat a7, const KDouble a8, const KString a9, const MyEnum a10, MyDictionary* a11, VoidCallback* a12, const KCharArray a13, const KBooleanArray a14, const KByteArray a15, const KShortArray a16, const KIntArray a17, const KLongArray a18, const KFloatArray a19, const KDoubleArray a20, const KIntArray a21, const KArray a22) { - TypeDictionary* result = (TypeDictionary*)malloc(sizeof(TypeDictionary)); - *result = (TypeDictionary){ a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22 }; - return result; -} +TypeDictionary* TypeDictionary_new(KChar a1, KBoolean a2, KByte a3, KShort a4, KInt a5, KLong a6, KFloat a7, KDouble a8, KString* a9, MyEnum a10, MyDictionary* a11, VoidCallback* a12, KCharArray* a13, KBooleanArray* a14, KByteArray* a15, KShortArray* a16, KIntArray* a17, KLongArray* a18, KFloatArray* a19, KDoubleArray* a20, KIntArray* a21, KArray* a22); +TypeDictionary* TypeDictionary_clone(const TypeDictionary* self); +void TypeDictionary_free(TypeDictionary* self); // ╔═══════════════════╗ // ║ Functions ║ @@ -254,7 +244,7 @@ KBoolean passInt(KInt arg); KBoolean passLong(KLong arg); KBoolean passFloat(KFloat arg); KBoolean passDouble(KDouble arg); -KBoolean passString(KString arg); +KBoolean passString(KString* arg); KBoolean passEnum(MyEnum arg); KBoolean passDictionary(MyDictionary* arg); void returnVoid(); @@ -266,8 +256,7 @@ KInt returnInt(); KLong returnLong(); KFloat returnFloat(); KDouble returnDouble(); -KString returnStringLiteral(); -KString returnString(); +KString* returnString(); MyEnum returnEnum(); MyDictionary* returnDictionary(); KChar pingChar(KChar arg); @@ -278,7 +267,7 @@ KInt pingInt(KInt arg); KLong pingLong(KLong arg); KFloat pingFloat(KFloat arg); KDouble pingDouble(KDouble arg); -KString pingString(KString arg); +KString* pingString(KString* arg); MyEnum pingEnum(MyEnum arg); MyDictionary* pingDictionary(MyDictionary* arg); void callbackVoid(VoidCallback* arg); @@ -306,36 +295,36 @@ KBoolean callbackReturnString(CallbackReturnString* arg); VoidCallback* callbackReturnCallback(CallbackReturnCallback* arg); KBoolean callbackReturnEnum(CallbackReturnEnum* arg); KBoolean callbackReturnDictionary(CallbackReturnDictionary* arg); -KBoolean passCharArray(KCharArray arg); -KBoolean passBooleanArray(KBooleanArray arg); -KBoolean passByteArray(KByteArray arg); -KBoolean passShortArray(KShortArray arg); -KBoolean passIntArray(KIntArray arg); -KBoolean passLongArray(KLongArray arg); -KBoolean passFloatArray(KFloatArray arg); -KBoolean passDoubleArray(KDoubleArray arg); -KBoolean passEnumArray(KIntArray arg); -KBoolean passDictionaryArray(KArray arg); -KCharArray returnCharArray(); -KBooleanArray returnBooleanArray(); -KByteArray returnByteArray(); -KShortArray returnShortArray(); -KIntArray returnIntArray(); -KLongArray returnLongArray(); -KFloatArray returnFloatArray(); -KDoubleArray returnDoubleArray(); -KIntArray returnEnumArray(); -KArray returnDictionaryArray(); -KCharArray pingCharArray(KCharArray arg); -KBooleanArray pingBooleanArray(KBooleanArray arg); -KByteArray pingByteArray(KByteArray arg); -KShortArray pingShortArray(KShortArray arg); -KIntArray pingIntArray(KIntArray arg); -KLongArray pingLongArray(KLongArray arg); -KFloatArray pingFloatArray(KFloatArray arg); -KDoubleArray pingDoubleArray(KDoubleArray arg); -KIntArray pingEnumArray(KIntArray arg); -KArray pingDictionaryArray(KArray arg); +KBoolean passCharArray(KCharArray* arg); +KBoolean passBooleanArray(KBooleanArray* arg); +KBoolean passByteArray(KByteArray* arg); +KBoolean passShortArray(KShortArray* arg); +KBoolean passIntArray(KIntArray* arg); +KBoolean passLongArray(KLongArray* arg); +KBoolean passFloatArray(KFloatArray* arg); +KBoolean passDoubleArray(KDoubleArray* arg); +KBoolean passEnumArray(KIntArray* arg); +KBoolean passDictionaryArray(KArray* arg); +KCharArray* returnCharArray(); +KBooleanArray* returnBooleanArray(); +KByteArray* returnByteArray(); +KShortArray* returnShortArray(); +KIntArray* returnIntArray(); +KLongArray* returnLongArray(); +KFloatArray* returnFloatArray(); +KDoubleArray* returnDoubleArray(); +KIntArray* returnEnumArray(); +KArray* returnDictionaryArray(); +KCharArray* pingCharArray(KCharArray* arg); +KBooleanArray* pingBooleanArray(KBooleanArray* arg); +KByteArray* pingByteArray(KByteArray* arg); +KShortArray* pingShortArray(KShortArray* arg); +KIntArray* pingIntArray(KIntArray* arg); +KLongArray* pingLongArray(KLongArray* arg); +KFloatArray* pingFloatArray(KFloatArray* arg); +KDoubleArray* pingDoubleArray(KDoubleArray* arg); +KIntArray* pingEnumArray(KIntArray* arg); +KArray* pingDictionaryArray(KArray* arg); KBoolean callbackArgCharArray(CallbackPassCharArray* arg); KBoolean callbackArgBooleanArray(CallbackPassBooleanArray* arg); KBoolean callbackArgByteArray(CallbackPassByteArray* arg); @@ -361,9 +350,9 @@ TypeDictionary* returnBigDictionary(VoidCallback* callback); TypeDictionary* pingBigDictionary(TypeDictionary* arg); KBoolean criticalPrimitives(KChar a1, KBoolean a2, KByte a3, KShort a4, KInt a5, KLong a6, KFloat a7, KDouble a8); KBoolean criticalEnum(MyEnum a1); -KBoolean criticalString(KString a1); -KBoolean criticalPrimitivesArray(KCharArray a1, KBooleanArray a2, KByteArray a3, KShortArray a4, KIntArray a5, KLongArray a6, KFloatArray a7, KDoubleArray a8); -KBoolean criticalEnumArray(KIntArray a1); +KBoolean criticalString(KString* a1); +KBoolean criticalPrimitivesArray(KCharArray* a1, KBooleanArray* a2, KByteArray* a3, KShortArray* a4, KIntArray* a5, KLongArray* a6, KFloatArray* a7, KDoubleArray* a8); +KBoolean criticalEnumArray(KIntArray* a1); KChar criticalReturnChar(); KBoolean criticalReturnBoolean(); KByte criticalReturnByte(); @@ -382,67 +371,67 @@ KBoolean jvmci6(KFloat a1, KFloat a2, KFloat a3, KFloat a4, KFloat a5, KFloat a6 KBoolean jvmci7(KFloat a1, KDouble a2, KFloat a3, KDouble a4, KFloat a5, KDouble a6, KFloat a7, KFloat a8, KDouble a9); KBoolean jvmci8(KInt a1, KDouble a2, KFloat a3, KLong a4); KBoolean jvmci9(KInt a1, KDouble a2, KFloat a3, KLong a4, KLong a5, KDouble a6, KFloat a7, KFloat a8, KInt a9); -KBoolean jvmci10(KString a1, KDouble a2, KFloat a3, KLong a4, KLong a5, KDouble a6, KString a7, KFloat a8, KInt a9); +KBoolean jvmci10(KString* a1, KDouble a2, KFloat a3, KLong a4, KLong a5, KDouble a6, KString* a7, KFloat a8, KInt a9); KBoolean jvmci11(KFloat a1, KInt a2, KFloat a3, KInt a4, KFloat a5, KInt a6, KFloat a7, KInt a8, KFloat a9, KInt a10, KFloat a11, KInt a12, KFloat a13, KInt a14, KFloat a15, KInt a16, KFloat a17); KInt jvmci12(); KLong jvmci13(); KFloat jvmci14(); KDouble jvmci15(); -KBoolean jvmciArray(KIntArray array); -KBoolean jvmciSomeArrays(KIntArray array1, KFloatArray array2, KDoubleArray array3); -KBoolean jvmciEnum(MyEnum enum1, MyEnum enum2, KIntArray enumArray); +KBoolean jvmciArray(KIntArray* array); +KBoolean jvmciSomeArrays(KIntArray* array1, KFloatArray* array2, KDoubleArray* array3); +KBoolean jvmciEnum(MyEnum enum1, MyEnum enum2, KIntArray* enumArray); // ╔═══════════════════╗ // ║ Callbacks ║ // ╚═══════════════════╝ -// ┌───────┬──────────────────────────────┬─────────────┬──────────────────┐ -// │ ... │ Name │ Type │ Args │ -// └───────┴──────────────────────────────┴─────────────┴──────────────────┘ -KCallbackDef(VoidCallback, void ) -KCallbackDef(CallbackPassChar, KBoolean, KChar arg ) -KCallbackDef(CallbackPassBoolean, KBoolean, KBoolean arg ) -KCallbackDef(CallbackPassByte, KBoolean, KByte arg ) -KCallbackDef(CallbackPassShort, KBoolean, KShort arg ) -KCallbackDef(CallbackPassInt, KBoolean, KInt arg ) -KCallbackDef(CallbackPassLong, KBoolean, KLong arg ) -KCallbackDef(CallbackPassFloat, KBoolean, KFloat arg ) -KCallbackDef(CallbackPassDouble, KBoolean, KDouble arg ) -KCallbackDef(CallbackPassString, KBoolean, KString arg ) -KCallbackDef(CallbackPassCallback, KBoolean, VoidCallback* arg) -KCallbackDef(CallbackPassEnum, KBoolean, MyEnum arg ) -KCallbackDef(CallbackPassDictionary, KBoolean, MyDictionary* arg) -KCallbackDef(CallbackReturnChar, KChar ) -KCallbackDef(CallbackReturnBoolean, KBoolean ) -KCallbackDef(CallbackReturnByte, KByte ) -KCallbackDef(CallbackReturnShort, KShort ) -KCallbackDef(CallbackReturnInt, KInt ) -KCallbackDef(CallbackReturnLong, KLong ) -KCallbackDef(CallbackReturnFloat, KFloat ) -KCallbackDef(CallbackReturnDouble, KDouble ) -KCallbackDef(CallbackReturnString, KString ) -KCallbackDef(CallbackReturnCallback, VoidCallback* ) -KCallbackDef(CallbackReturnEnum, MyEnum ) -KCallbackDef(CallbackReturnDictionary, MyDictionary* ) -KCallbackDef(CallbackPassCharArray, KBoolean, KCharArray arg ) -KCallbackDef(CallbackPassBooleanArray, KBoolean, KBooleanArray arg) -KCallbackDef(CallbackPassByteArray, KBoolean, KByteArray arg ) -KCallbackDef(CallbackPassShortArray, KBoolean, KShortArray arg ) -KCallbackDef(CallbackPassIntArray, KBoolean, KIntArray arg ) -KCallbackDef(CallbackPassLongArray, KBoolean, KLongArray arg ) -KCallbackDef(CallbackPassFloatArray, KBoolean, KFloatArray arg ) -KCallbackDef(CallbackPassDoubleArray, KBoolean, KDoubleArray arg ) -KCallbackDef(CallbackPassEnumArray, KBoolean, KIntArray arg ) -KCallbackDef(CallbackPassDictionaryArray, KBoolean, KArray arg ) -KCallbackDef(CallbackReturnCharArray, KCharArray ) -KCallbackDef(CallbackReturnBooleanArray, KBooleanArray ) -KCallbackDef(CallbackReturnByteArray, KByteArray ) -KCallbackDef(CallbackReturnShortArray, KShortArray ) -KCallbackDef(CallbackReturnIntArray, KIntArray ) -KCallbackDef(CallbackReturnLongArray, KLongArray ) -KCallbackDef(CallbackReturnFloatArray, KFloatArray ) -KCallbackDef(CallbackReturnDoubleArray, KDoubleArray ) -KCallbackDef(CallbackReturnEnumArray, KIntArray ) -KCallbackDef(CallbackReturnDictionaryArray, KArray ) +// ┌───────┬──────────────────────────────┬──────────────┬───────────────────┐ +// │ ... │ Name │ Type │ Args │ +// └───────┴──────────────────────────────┴──────────────┴───────────────────┘ +KCallbackDef(VoidCallback, void ) +KCallbackDef(CallbackPassChar, KBoolean, KChar arg ) +KCallbackDef(CallbackPassBoolean, KBoolean, KBoolean arg ) +KCallbackDef(CallbackPassByte, KBoolean, KByte arg ) +KCallbackDef(CallbackPassShort, KBoolean, KShort arg ) +KCallbackDef(CallbackPassInt, KBoolean, KInt arg ) +KCallbackDef(CallbackPassLong, KBoolean, KLong arg ) +KCallbackDef(CallbackPassFloat, KBoolean, KFloat arg ) +KCallbackDef(CallbackPassDouble, KBoolean, KDouble arg ) +KCallbackDef(CallbackPassString, KBoolean, KString* arg ) +KCallbackDef(CallbackPassCallback, KBoolean, VoidCallback* arg ) +KCallbackDef(CallbackPassEnum, KBoolean, MyEnum arg ) +KCallbackDef(CallbackPassDictionary, KBoolean, MyDictionary* arg ) +KCallbackDef(CallbackReturnChar, KChar ) +KCallbackDef(CallbackReturnBoolean, KBoolean ) +KCallbackDef(CallbackReturnByte, KByte ) +KCallbackDef(CallbackReturnShort, KShort ) +KCallbackDef(CallbackReturnInt, KInt ) +KCallbackDef(CallbackReturnLong, KLong ) +KCallbackDef(CallbackReturnFloat, KFloat ) +KCallbackDef(CallbackReturnDouble, KDouble ) +KCallbackDef(CallbackReturnString, KString* ) +KCallbackDef(CallbackReturnCallback, VoidCallback* ) +KCallbackDef(CallbackReturnEnum, MyEnum ) +KCallbackDef(CallbackReturnDictionary, MyDictionary* ) +KCallbackDef(CallbackPassCharArray, KBoolean, KCharArray* arg ) +KCallbackDef(CallbackPassBooleanArray, KBoolean, KBooleanArray* arg) +KCallbackDef(CallbackPassByteArray, KBoolean, KByteArray* arg ) +KCallbackDef(CallbackPassShortArray, KBoolean, KShortArray* arg ) +KCallbackDef(CallbackPassIntArray, KBoolean, KIntArray* arg ) +KCallbackDef(CallbackPassLongArray, KBoolean, KLongArray* arg ) +KCallbackDef(CallbackPassFloatArray, KBoolean, KFloatArray* arg ) +KCallbackDef(CallbackPassDoubleArray, KBoolean, KDoubleArray* arg ) +KCallbackDef(CallbackPassEnumArray, KBoolean, KIntArray* arg ) +KCallbackDef(CallbackPassDictionaryArray, KBoolean, KArray* arg ) +KCallbackDef(CallbackReturnCharArray, KCharArray* ) +KCallbackDef(CallbackReturnBooleanArray, KBooleanArray* ) +KCallbackDef(CallbackReturnByteArray, KByteArray* ) +KCallbackDef(CallbackReturnShortArray, KShortArray* ) +KCallbackDef(CallbackReturnIntArray, KIntArray* ) +KCallbackDef(CallbackReturnLongArray, KLongArray* ) +KCallbackDef(CallbackReturnFloatArray, KFloatArray* ) +KCallbackDef(CallbackReturnDoubleArray, KDoubleArray* ) +KCallbackDef(CallbackReturnEnumArray, KIntArray* ) +KCallbackDef(CallbackReturnDictionaryArray, KArray* ) #undef KCallbackDef diff --git a/modules/tests/natives/test/src/arrays/callback_return_array.c b/modules/tests/natives/test/src/arrays/callback_return_array.c index 28f4d1d..19bd772 100644 --- a/modules/tests/natives/test/src/arrays/callback_return_array.c +++ b/modules/tests/natives/test/src/arrays/callback_return_array.c @@ -1,85 +1,85 @@ #include KBoolean callbackReturnCharArray(CallbackReturnCharArray* arg) { - const KCharArray array = arg->invoke(arg); - return array.releasable && - array.size == 2 && - array.elements[0] == 'a' && - array.elements[1] == 'b'; + const KCharArray* array = arg->invoke(arg); + return (array->__flags & 1) && + array->length == 2 && + array->elements[0] == 'a' && + array->elements[1] == 'b'; } KBoolean callbackReturnBooleanArray(CallbackReturnBooleanArray* arg) { - const KBooleanArray array = arg->invoke(arg); - return array.releasable && - array.size == 2 && - array.elements[0] == true && - array.elements[1] == false; + const KBooleanArray* array = arg->invoke(arg); + return (array->__flags & 1) && + array->length == 2 && + array->elements[0] == true && + array->elements[1] == false; } KBoolean callbackReturnByteArray(CallbackReturnByteArray* arg) { - const KByteArray array = arg->invoke(arg); - return array.releasable && - array.size == 2 && - array.elements[0] == 1 && - array.elements[1] == 2; + const KByteArray* array = arg->invoke(arg); + return (array->__flags & 1) && + array->length == 2 && + array->elements[0] == 1 && + array->elements[1] == 2; } KBoolean callbackReturnShortArray(CallbackReturnShortArray* arg) { - const KShortArray array = arg->invoke(arg); - return array.releasable && - array.size == 2 && - array.elements[0] == 1 && - array.elements[1] == 2; + const KShortArray* array = arg->invoke(arg); + return (array->__flags & 1) && + array->length == 2 && + array->elements[0] == 1 && + array->elements[1] == 2; } KBoolean callbackReturnIntArray(CallbackReturnIntArray* arg) { - const KIntArray array = arg->invoke(arg); - return array.releasable && - array.size == 2 && - array.elements[0] == 1 && - array.elements[1] == 2; + const KIntArray* array = arg->invoke(arg); + return (array->__flags & 1) && + array->length == 2 && + array->elements[0] == 1 && + array->elements[1] == 2; } KBoolean callbackReturnLongArray(CallbackReturnLongArray* arg) { - const KLongArray array = arg->invoke(arg); - return array.releasable && - array.size == 2 && - array.elements[0] == 1 && - array.elements[1] == 2; + const KLongArray* array = arg->invoke(arg); + return (array->__flags & 1) && + array->length == 2 && + array->elements[0] == 1 && + array->elements[1] == 2; } KBoolean callbackReturnFloatArray(CallbackReturnFloatArray* arg) { - const KFloatArray array = arg->invoke(arg); - return array.releasable && - array.size == 2 && - array.elements[0] == 1.1f && - array.elements[1] == 2.2f; + const KFloatArray* array = arg->invoke(arg); + return (array->__flags & 1) && + array->length == 2 && + array->elements[0] == 1.1f && + array->elements[1] == 2.2f; } KBoolean callbackReturnDoubleArray(CallbackReturnDoubleArray* arg) { - const KDoubleArray array = arg->invoke(arg); - return array.releasable && - array.size == 2 && - array.elements[0] == 1.1 && - array.elements[1] == 2.2; + const KDoubleArray* array = arg->invoke(arg); + return (array->__flags & 1) && + array->length == 2 && + array->elements[0] == 1.1 && + array->elements[1] == 2.2; } KBoolean callbackReturnEnumArray(CallbackReturnEnumArray* arg) { - const KIntArray array = arg->invoke(arg); - const MyEnum* elements = (MyEnum*)array.elements; + const KIntArray* array = arg->invoke(arg); + const MyEnum* elements = (MyEnum*)array->elements; - return array.releasable && - array.size == 2 && + return (array->__flags & 1) && + array->length == 2 && elements[0] == MyEnum_CASE1 && elements[1] == MyEnum_CASE2; } KBoolean callbackReturnDictionaryArray(CallbackReturnDictionaryArray* arg) { - const KArray array = arg->invoke(arg); - MyDictionary** elements = (MyDictionary**)array.elements; + const KArray* array = arg->invoke(arg); + MyDictionary** elements = (MyDictionary**)array->elements; - return array.releasable && - array.size == 2 && + return (array->__flags & 1) && + array->length == 2 && elements[0]->a == 1 && elements[0]->b == 2 && elements[0]->c == 3 && diff --git a/modules/tests/natives/test/src/arrays/pass_array.c b/modules/tests/natives/test/src/arrays/pass_array.c index a91139e..b4a21e6 100644 --- a/modules/tests/natives/test/src/arrays/pass_array.c +++ b/modules/tests/natives/test/src/arrays/pass_array.c @@ -1,81 +1,81 @@ #include -KBoolean passArray(const KIntArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == 1 && - arg.elements[1] == 2; +KBoolean passArray(KIntArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == 1 && + arg->elements[1] == 2; } -KBoolean passCharArray(const KCharArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == 'a' && - arg.elements[1] == 'b'; +KBoolean passCharArray(KCharArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == 'a' && + arg->elements[1] == 'b'; } -KBoolean passBooleanArray(const KBooleanArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == true && - arg.elements[1] == false; +KBoolean passBooleanArray(KBooleanArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == true && + arg->elements[1] == false; } -KBoolean passByteArray(const KByteArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == 1 && - arg.elements[1] == 2; +KBoolean passByteArray(KByteArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == 1 && + arg->elements[1] == 2; } -KBoolean passShortArray(const KShortArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == 1 && - arg.elements[1] == 2; +KBoolean passShortArray(KShortArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == 1 && + arg->elements[1] == 2; } -KBoolean passIntArray(const KIntArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == 1 && - arg.elements[1] == 2; +KBoolean passIntArray(KIntArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == 1 && + arg->elements[1] == 2; } -KBoolean passLongArray(const KLongArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == 1 && - arg.elements[1] == 2; +KBoolean passLongArray(KLongArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == 1 && + arg->elements[1] == 2; } -KBoolean passFloatArray(const KFloatArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == 1.1f && - arg.elements[1] == 2.2f; +KBoolean passFloatArray(KFloatArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == 1.1f && + arg->elements[1] == 2.2f; } -KBoolean passDoubleArray(const KDoubleArray arg) { - return !arg.releasable && - arg.size == 2 && - arg.elements[0] == 1.1 && - arg.elements[1] == 2.2; +KBoolean passDoubleArray(KDoubleArray* arg) { + return !(arg->__flags & 1) && + arg->length == 2 && + arg->elements[0] == 1.1 && + arg->elements[1] == 2.2; } -KBoolean passEnumArray(const KIntArray arg) { - const MyEnum* elements = (MyEnum*)arg.elements; +KBoolean passEnumArray(KIntArray* arg) { + const MyEnum* elements = (MyEnum*)arg->elements; - return !arg.releasable && - arg.size == 2 && + return !(arg->__flags & 1) && + arg->length == 2 && elements[0] == MyEnum_CASE1 && elements[1] == MyEnum_CASE2; } -KBoolean passDictionaryArray(const KArray arg) { - MyDictionary** elements = (MyDictionary**)arg.elements; +KBoolean passDictionaryArray(KArray* arg) { + MyDictionary** elements = (MyDictionary**)arg->elements; - return !arg.releasable && - arg.size == 2 && + return !(arg->__flags & 1) && + arg->length == 2 && elements[0]->a == 1 && elements[0]->b == 2 && elements[0]->c == 3 && diff --git a/modules/tests/natives/test/src/arrays/ping_array.c b/modules/tests/natives/test/src/arrays/ping_array.c index 93c3f15..766f7b2 100644 --- a/modules/tests/natives/test/src/arrays/ping_array.c +++ b/modules/tests/natives/test/src/arrays/ping_array.c @@ -1,41 +1,41 @@ #include -KCharArray pingCharArray(const KCharArray arg) { +KCharArray* pingCharArray(KCharArray* arg) { return arg; } -KBooleanArray pingBooleanArray(const KBooleanArray arg) { +KBooleanArray* pingBooleanArray(KBooleanArray* arg) { return arg; } -KByteArray pingByteArray(const KByteArray arg) { +KByteArray* pingByteArray(KByteArray* arg) { return arg; } -KShortArray pingShortArray(const KShortArray arg) { +KShortArray* pingShortArray(KShortArray* arg) { return arg; } -KIntArray pingIntArray(const KIntArray arg) { +KIntArray* pingIntArray(KIntArray* arg) { return arg; } -KLongArray pingLongArray(const KLongArray arg) { +KLongArray* pingLongArray(KLongArray* arg) { return arg; } -KFloatArray pingFloatArray(const KFloatArray arg) { +KFloatArray* pingFloatArray(KFloatArray* arg) { return arg; } -KDoubleArray pingDoubleArray(const KDoubleArray arg) { +KDoubleArray* pingDoubleArray(KDoubleArray* arg) { return arg; } -KIntArray pingEnumArray(const KIntArray arg) { +KIntArray* pingEnumArray(KIntArray* arg) { return arg; } -KArray pingDictionaryArray(const KArray arg) { +KArray* pingDictionaryArray(KArray* arg) { return arg; } \ No newline at end of file diff --git a/modules/tests/natives/test/src/arrays/return_array.c b/modules/tests/natives/test/src/arrays/return_array.c index 4290685..ed42277 100644 --- a/modules/tests/natives/test/src/arrays/return_array.c +++ b/modules/tests/natives/test/src/arrays/return_array.c @@ -1,46 +1,46 @@ #include #include -KCharArray returnCharArray() { +KCharArray* returnCharArray() { return KCharArray_of('a', 'b'); } -KBooleanArray returnBooleanArray() { +KBooleanArray* returnBooleanArray() { return KBooleanArray_of(true, false); } -KByteArray returnByteArray() { +KByteArray* returnByteArray() { return KByteArray_of(1, 2); } -KShortArray returnShortArray() { +KShortArray* returnShortArray() { return KShortArray_of(1, 2); } -KIntArray returnIntArray() { +KIntArray* returnIntArray() { return KIntArray_of(1, 2); } -KLongArray returnLongArray() { +KLongArray* returnLongArray() { KLong* elements = malloc(2 * sizeof(int64_t)); elements[0] = 1; elements[1] = 2; return KLongArray_new(elements, 2); } -KFloatArray returnFloatArray() { +KFloatArray* returnFloatArray() { return KFloatArray_of(1.1f, 2.2f); } -KDoubleArray returnDoubleArray() { +KDoubleArray* returnDoubleArray() { return KDoubleArray_of(1.1, 2.2); } -KIntArray returnEnumArray() { +KIntArray* returnEnumArray() { return KIntArray_of(MyEnum_CASE1, MyEnum_CASE2); } -KArray returnDictionaryArray() { +KArray* returnDictionaryArray() { return KArray_of( MyDictionary_new(1, 2, 3, 4), MyDictionary_new(5, 6, 7, 8) diff --git a/modules/tests/natives/test/src/critical.c b/modules/tests/natives/test/src/critical.c index a39e07f..3c7bb15 100644 --- a/modules/tests/natives/test/src/critical.c +++ b/modules/tests/natives/test/src/critical.c @@ -17,41 +17,41 @@ KBoolean criticalEnum(MyEnum a1) { return a1 == MyEnum_CASE1; } -KBoolean criticalString(KString a1) { - return a1.length == 11 && strncmp(a1.data, "test string", a1.length) == 0; +KBoolean criticalString(KString* a1) { + return a1->length == 11 && strncmp(a1->data, "test string", a1->length) == 0; } -KBoolean criticalPrimitivesArray(KCharArray a1, KBooleanArray a2, KByteArray a3, KShortArray a4, KIntArray a5, KLongArray a6, KFloatArray a7, KDoubleArray a8) { - return a1.size == 2 && - a1.elements[0] == 'a' && - a1.elements[1] == 'b' && - a2.size == 2 && - a2.elements[0] == true && - a2.elements[1] == false && - a3.size == 2 && - a3.elements[0] == 1 && - a3.elements[1] == 2 && - a4.size == 2 && - a4.elements[0] == 1 && - a4.elements[1] == 2 && - a5.size == 2 && - a5.elements[0] == 1 && - a5.elements[1] == 2 && - a6.size == 2 && - a6.elements[0] == 1 && - a6.elements[1] == 2 && - a7.size == 2 && - a7.elements[0] == 1.1f && - a7.elements[1] == 2.2f && - a8.size == 2 && - a8.elements[0] == 1.1 && - a8.elements[1] == 2.2; +KBoolean criticalPrimitivesArray(KCharArray* a1, KBooleanArray* a2, KByteArray* a3, KShortArray* a4, KIntArray* a5, KLongArray* a6, KFloatArray* a7, KDoubleArray* a8) { + return a1->length == 2 && + a1->elements[0] == 'a' && + a1->elements[1] == 'b' && + a2->length == 2 && + a2->elements[0] == true && + a2->elements[1] == false && + a3->length == 2 && + a3->elements[0] == 1 && + a3->elements[1] == 2 && + a4->length == 2 && + a4->elements[0] == 1 && + a4->elements[1] == 2 && + a5->length == 2 && + a5->elements[0] == 1 && + a5->elements[1] == 2 && + a6->length == 2 && + a6->elements[0] == 1 && + a6->elements[1] == 2 && + a7->length == 2 && + a7->elements[0] == 1.1f && + a7->elements[1] == 2.2f && + a8->length == 2 && + a8->elements[0] == 1.1 && + a8->elements[1] == 2.2; } -KBoolean criticalEnumArray(KIntArray a1) { - const MyEnum* elements = (MyEnum*)a1.elements; +KBoolean criticalEnumArray(KIntArray* a1) { + const MyEnum* elements = (MyEnum*)a1->elements; - return a1.size == 2 && + return a1->length == 2 && elements[0] == MyEnum_CASE1 && elements[1] == MyEnum_CASE2; } diff --git a/modules/tests/natives/test/src/dictionaries.c b/modules/tests/natives/test/src/dictionaries.c index f0ea584..d1f1d1c 100644 --- a/modules/tests/natives/test/src/dictionaries.c +++ b/modules/tests/natives/test/src/dictionaries.c @@ -10,46 +10,46 @@ KBoolean passBigDictionary(TypeDictionary* arg) { arg->a6 == 9223372036854775807L && arg->a7 == 123.0f && arg->a8 == 123.4 && - strncmp(arg->a9.data, "test string", arg->a9.length) == 0 && + strncmp(arg->a9->data, "test string", arg->a9->length) == 0 && arg->a10 == MyEnum_CASE2 && arg->a11->a == 1 && arg->a11->b == 2 && arg->a11->c == 3 && arg->a11->d == 4 && // callback (a12) is skipped - arg->a13.size == 2 && - arg->a13.elements[0] == 'a' && - arg->a13.elements[1] == 'b' && - arg->a14.size == 2 && - arg->a14.elements[0] == true && - arg->a14.elements[1] == false && - arg->a15.size == 2 && - arg->a15.elements[0] == 1 && - arg->a15.elements[1] == 2 && - arg->a16.size == 2 && - arg->a16.elements[0] == 1 && - arg->a16.elements[1] == 2 && - arg->a17.size == 2 && - arg->a17.elements[0] == 1 && - arg->a17.elements[1] == 2 && - arg->a18.size == 2 && - arg->a18.elements[0] == 1 && - arg->a18.elements[1] == 2 && - arg->a19.size == 2 && - arg->a19.elements[0] == 1.2f && - arg->a19.elements[1] == 3.4f && - arg->a20.size == 2 && - arg->a20.elements[0] == 1.2 && - arg->a20.elements[1] == 3.4 && - arg->a21.size == 2 && - arg->a21.elements[0] == MyEnum_CASE1 && - arg->a21.elements[1] == MyEnum_CASE2 && - arg->a22.size == 2 && - ((MyDictionary**)arg->a22.elements)[0]->a == 1 && - ((MyDictionary**)arg->a22.elements)[0]->b == 2 && - ((MyDictionary**)arg->a22.elements)[0]->c == 3 && - ((MyDictionary**)arg->a22.elements)[0]->d == 4 && - ((MyDictionary**)arg->a22.elements)[1]->a == 5 && - ((MyDictionary**)arg->a22.elements)[1]->b == 6 && - ((MyDictionary**)arg->a22.elements)[1]->c == 7 && - ((MyDictionary**)arg->a22.elements)[1]->d == 8; + arg->a13->length == 2 && + arg->a13->elements[0] == 'a' && + arg->a13->elements[1] == 'b' && + arg->a14->length == 2 && + arg->a14->elements[0] == true && + arg->a14->elements[1] == false && + arg->a15->length == 2 && + arg->a15->elements[0] == 1 && + arg->a15->elements[1] == 2 && + arg->a16->length == 2 && + arg->a16->elements[0] == 1 && + arg->a16->elements[1] == 2 && + arg->a17->length == 2 && + arg->a17->elements[0] == 1 && + arg->a17->elements[1] == 2 && + arg->a18->length == 2 && + arg->a18->elements[0] == 1 && + arg->a18->elements[1] == 2 && + arg->a19->length == 2 && + arg->a19->elements[0] == 1.2f && + arg->a19->elements[1] == 3.4f && + arg->a20->length == 2 && + arg->a20->elements[0] == 1.2 && + arg->a20->elements[1] == 3.4 && + arg->a21->length == 2 && + arg->a21->elements[0] == MyEnum_CASE1 && + arg->a21->elements[1] == MyEnum_CASE2 && + arg->a22->length == 2 && + ((MyDictionary**)arg->a22->elements)[0]->a == 1 && + ((MyDictionary**)arg->a22->elements)[0]->b == 2 && + ((MyDictionary**)arg->a22->elements)[0]->c == 3 && + ((MyDictionary**)arg->a22->elements)[0]->d == 4 && + ((MyDictionary**)arg->a22->elements)[1]->a == 5 && + ((MyDictionary**)arg->a22->elements)[1]->b == 6 && + ((MyDictionary**)arg->a22->elements)[1]->c == 7 && + ((MyDictionary**)arg->a22->elements)[1]->d == 8; } TypeDictionary* returnBigDictionary(VoidCallback* callback) { @@ -66,7 +66,7 @@ TypeDictionary* returnBigDictionary(VoidCallback* callback) { 9223372036854775807L, 123.0f, 123.4, - KString_new("test string", 11), + KString_new(strdup("test string"), 11, 11), MyEnum_CASE2, MyDictionary_new(1, 2, 3, 4), callback, diff --git a/modules/tests/natives/test/src/jvmci.c b/modules/tests/natives/test/src/jvmci.c index 943a2b7..7409b86 100644 --- a/modules/tests/natives/test/src/jvmci.c +++ b/modules/tests/natives/test/src/jvmci.c @@ -1,6 +1,7 @@ #include #include #include +#include // JVMCI @@ -35,7 +36,7 @@ KBoolean jvmci5( const KInt a1, const KLong a2, const KInt a3, const KLong a4, const KInt a5, const KLong a6, const KInt a7, const KInt a8, const KLong a9 ) { - printf("jvmci5 | a1: %d, a2: %lld, a3: %d, a4: %lld, a5: %d, a6: %lld, a7: %d, a8: %d, a9: %lld\n", a1, a2, a3, a4, a5, a6, a7, a8, a9); + printf("jvmci5 | a1: %d, a2: %" PRId64 ", a3: %d, a4: %" PRId64 ", a5: %d, a6: %" PRId64 ", a7: %d, a8: %d, a9: %" PRId64 "\n", a1, a2, a3, a4, a5, a6, a7, a8, a9); fflush(stdout); return a1 == 1 && a2 == 2 && a3 == 3 && a4 == 4 && a5 == 5 && a6 == 6 && a7 == 7 && a8 == 8 && a9 == 9; } @@ -60,7 +61,7 @@ KBoolean jvmci7( } KBoolean jvmci8(const KInt a1, const KDouble a2, const KFloat a3, const KLong a4) { - printf("jvmci8 | a1: %d, a2: %f, a3: %f, a4: %lld\n", a1, a2, a3, a4); + printf("jvmci8 | a1: %d, a2: %f, a3: %f, a4: %" PRId64 "\n", a1, a2, a3, a4); fflush(stdout); return a1 == 1 && a2 == 2.0 && a3 == 3.0 && a4 == 4.0; } @@ -69,18 +70,18 @@ KBoolean jvmci9( const KInt a1, const KDouble a2, const KFloat a3, const KLong a4, const KLong a5, const KDouble a6, const KFloat a7, const KFloat a8, const KInt a9 ) { - printf("jvmci9 | a1: %d, a2: %f, a3: %f, a4: %lld, a5: %lld, a6: %f, a7: %f, a8: %f, a9: %d\n", a1, a2, a3, a4, a5, a6, a7, a8, a9); + printf("jvmci9 | a1: %d, a2: %f, a3: %f, a4: %" PRId64 ", a5: %" PRId64 ", a6: %f, a7: %f, a8: %f, a9: %d\n", a1, a2, a3, a4, a5, a6, a7, a8, a9); fflush(stdout); return a1 == 1 && a2 == 2.0 && a3 == 3.0 && a4 == 4 && a5 == 5 && a6 == 6.0 && a7 == 7.0 && a8 == 8.0 && a9 == 9; } KBoolean jvmci10( - const KString a1, const KDouble a2, const KFloat a3, const KLong a4, - const KLong a5, const KDouble a6, const KString a7, const KFloat a8, const KInt a9 + KString* a1, const KDouble a2, const KFloat a3, const KLong a4, + const KLong a5, const KDouble a6, KString* a7, const KFloat a8, const KInt a9 ) { - printf("jvmci10 | a1: %s, a2: %f, a3: %f, a4: %lld, a5: %lld, a6: %f, a7: %s, a8: %f, a9: %d\n", a1.data, a2, a3, a4, a5, a6, a7.data, a8, a9); + printf("jvmci10 | a1: %s, a2: %f, a3: %f, a4: %" PRId64 ", a5: %" PRId64 ", a6: %f, a7: %s (%d, %" PRId64 ", %d), a8: %f, a9: %d\n", a1->data, a2, a3, a4, a5, a6, a7->data, a7->length, a7->size, a7->__flags, a8, a9); fflush(stdout); - return strncmp(a1.data, "string1", 7) == 0 && a2 == 2.0 && a3 == 3.0 && a4 == 4 && a5 == 5 && a6 == 6.0 && strncmp(a7.data, "string7", 7) == 0 && a8 == 8.0 && a9 == 9; + return strncmp(a1->data, "string1", 7) == 0 && a2 == 2.0 && a3 == 3.0 && a4 == 4 && a5 == 5 && a6 == 6.0 && strncmp(a7->data, "string7", 7) == 0 && a8 == 8.0 && a9 == 9; } KBoolean jvmci11( @@ -110,33 +111,33 @@ KDouble jvmci15() { return 1.5; } -KBoolean jvmciArray(const KIntArray array) { - return array.size == 3 && - array.elements[0] == 1 && - array.elements[1] == 2 && - array.elements[2] == 3; +KBoolean jvmciArray(KIntArray* array) { + return array->length == 3 && + array->elements[0] == 1 && + array->elements[1] == 2 && + array->elements[2] == 3; } -KBoolean jvmciSomeArrays(const KIntArray array1, const KFloatArray array2, const KDoubleArray array3) { - return array1.size == 3 && - array1.elements[0] == 1 && - array1.elements[1] == 2 && - array1.elements[2] == 3 && - array2.size == 3 && - array2.elements[0] == 4.0 && - array2.elements[1] == 5.0 && - array2.elements[2] == 6.0 && - array3.size == 3 && - array3.elements[0] == 7.0 && - array3.elements[1] == 8.0 && - array3.elements[2] == 9.0; +KBoolean jvmciSomeArrays(KIntArray* array1, KFloatArray* array2, KDoubleArray* array3) { + return array1->length == 3 && + array1->elements[0] == 1 && + array1->elements[1] == 2 && + array1->elements[2] == 3 && + array2->length == 3 && + array2->elements[0] == 4.0 && + array2->elements[1] == 5.0 && + array2->elements[2] == 6.0 && + array3->length == 3 && + array3->elements[0] == 7.0 && + array3->elements[1] == 8.0 && + array3->elements[2] == 9.0; } -KBoolean jvmciEnum(const MyEnum enum1, const MyEnum enum2, const KIntArray enumArray){ - const MyEnum *elements = (MyEnum*)enumArray.elements; +KBoolean jvmciEnum(const MyEnum enum1, const MyEnum enum2, KIntArray* enumArray){ + const MyEnum *elements = (MyEnum*)enumArray->elements; return enum1 == MyEnum_CASE1 && enum2 == MyEnum_CASE2 && - enumArray.size == 3 && + enumArray->length == 3 && elements[0] == MyEnum_CASE1 && elements[1] == MyEnum_CASE2 && elements[2] == MyEnum_CASE1; diff --git a/modules/tests/natives/test/src/primitives/callback_arg_primitive.c b/modules/tests/natives/test/src/primitives/callback_arg_primitive.c index 1aaa788..ec3466d 100644 --- a/modules/tests/natives/test/src/primitives/callback_arg_primitive.c +++ b/modules/tests/natives/test/src/primitives/callback_arg_primitive.c @@ -1,4 +1,5 @@ #include +#include void callbackVoid(VoidCallback* arg) { arg->invoke(arg); @@ -37,7 +38,7 @@ KBoolean callbackArgDouble(CallbackPassDouble* arg) { } KBoolean callbackArgString(CallbackPassString* arg) { - return arg->invoke(arg, KString_new("test string", 11)); + return arg->invoke(arg, KString_new(strdup("test string"), 11, 11)); } KBoolean callbackArgCallback(VoidCallback* pass, CallbackPassCallback* arg) { diff --git a/modules/tests/natives/test/src/primitives/callback_return_primitive.c b/modules/tests/natives/test/src/primitives/callback_return_primitive.c index 427bd92..1668e3b 100644 --- a/modules/tests/natives/test/src/primitives/callback_return_primitive.c +++ b/modules/tests/natives/test/src/primitives/callback_return_primitive.c @@ -34,8 +34,8 @@ KBoolean callbackReturnDouble(CallbackReturnDouble* arg) { } KBoolean callbackReturnString(CallbackReturnString* arg) { - const KString str = arg->invoke(arg); - return str.releasable && strncmp(str.data, "test string", 11) == 0; + const KString* str = arg->invoke(arg); + return (str->__flags & 1) && strncmp(str->data, "test string", str->length) == 0; } VoidCallback* callbackReturnCallback(CallbackReturnCallback* arg) { diff --git a/modules/tests/natives/test/src/primitives/pass_primitive.c b/modules/tests/natives/test/src/primitives/pass_primitive.c index 281369c..121ff93 100644 --- a/modules/tests/natives/test/src/primitives/pass_primitive.c +++ b/modules/tests/natives/test/src/primitives/pass_primitive.c @@ -39,8 +39,8 @@ KBoolean passDouble(const KDouble arg) { return arg == 1.1; } -KBoolean passString(const KString arg) { - return !arg.releasable && arg.length == 11 && strncmp(arg.data, "test string", arg.length) == 0; +KBoolean passString(KString* arg) { + return !(arg->__flags & 1) && arg->length == 11 && strncmp(arg->data, "test string", arg->length) == 0; } KBoolean passEnum(const MyEnum arg) { diff --git a/modules/tests/natives/test/src/primitives/ping_primitive.c b/modules/tests/natives/test/src/primitives/ping_primitive.c index 08e99a9..3245f01 100644 --- a/modules/tests/natives/test/src/primitives/ping_primitive.c +++ b/modules/tests/natives/test/src/primitives/ping_primitive.c @@ -32,7 +32,7 @@ KDouble pingDouble(const KDouble arg) { return arg; } -KString pingString(const KString arg) { +KString* pingString(KString* arg) { return arg; } diff --git a/modules/tests/natives/test/src/primitives/return_primitive.c b/modules/tests/natives/test/src/primitives/return_primitive.c index db83799..cd38597 100644 --- a/modules/tests/natives/test/src/primitives/return_primitive.c +++ b/modules/tests/natives/test/src/primitives/return_primitive.c @@ -37,14 +37,8 @@ KDouble returnDouble() { return 99.0; } -KString returnStringLiteral() { - return KString_new("test string", 11); -} - -KString returnString() { - char* str = malloc(100); - strcpy(str, "test string"); - return KString_new(str, 11); +KString* returnString() { + return KString_new(strdup("test string"), 11, 11); } MyEnum returnEnum() { diff --git a/modules/tests/src/commonTest/kotlin/primitives/ReturnPrimitive.kt b/modules/tests/src/commonTest/kotlin/primitives/ReturnPrimitive.kt index 1b73902..d2a14a2 100644 --- a/modules/tests/src/commonTest/kotlin/primitives/ReturnPrimitive.kt +++ b/modules/tests/src/commonTest/kotlin/primitives/ReturnPrimitive.kt @@ -53,11 +53,6 @@ class ReturnPrimitive { assertEquals(99.0, natives.test.returnDouble()) } - @Test - fun returnStringLiteral() = withLib { - assertEquals("test string", natives.test.returnStringLiteral()) - } - @Test fun returnString() = withLib { assertEquals("test string", natives.test.returnString()) diff --git a/modules/tests/src/jvmTest/kotlin/JVMCI.kt b/modules/tests/src/jvmTest/kotlin/JVMCI.kt index 14af5a7..cf04900 100644 --- a/modules/tests/src/jvmTest/kotlin/JVMCI.kt +++ b/modules/tests/src/jvmTest/kotlin/JVMCI.kt @@ -1,5 +1,6 @@ import com.huskerdev.nativekt.jvm.NativeKtUtils import natives.test.* +import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -8,6 +9,11 @@ import kotlin.test.fail class JVMCI { + @BeforeTest + fun printInfo() { + println("JVM: ${System.getProperty("java.vendor.version")}") + } + fun withJVMCI(block: () -> Unit) { if(!NativeKtUtils.isJvmciAvailable()) fail("JVMCI is unavailable") diff --git a/settings.gradle.kts b/settings.gradle.kts index e71f954..3f965ee 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,7 +7,6 @@ include("modules:android-critical-stub") include("modules:tests") include("modules:test-jvm-only") -include("modules:test-js-only") include("modules:benchmarks") include("modules:examples:glfw")