From a261503131e796d6b1fd0741434b6a1ae3a78504 Mon Sep 17 00:00:00 2001 From: nshtengauer Date: Mon, 1 Jun 2026 00:10:27 +0300 Subject: [PATCH 1/8] Removed Arena in JNI --- .../natives/foreignBindings/include/api.h | 163 ++++-- .../natives/jniBindings/include/api.h | 163 ++++-- .../natives/jvmciBindings/include/api.h | 161 ++++-- .../natives/freetypeBindings/include/api.h | 159 ++++-- .../glfw/natives/glfwBindings/include/api.h | 159 ++++-- .../configurators/AndroidConfigurator.kt | 6 - .../nativekt/configurators/JvmConfigurator.kt | 6 - .../nativekt/printers/c/CHeaderPrinter.kt | 288 ++++++++-- .../nativekt/printers/c/CJniArenaPrinter.kt | 160 ------ .../nativekt/printers/c/CJniPrinter.kt | 226 +++++--- .../nativekt/printers/c/CJniUtilsPrinter.kt | 539 +++++++++++++----- .../huskerdev/nativekt/utils/PrinterUtils.kt | 38 +- .../natives/jsOnlyTest/include/api.h | 159 ++++-- .../natives/jvmOnlyTest/include/api.h | 159 ++++-- modules/tests/build.gradle.kts | 3 + modules/tests/natives/test/include/api.h | 472 ++++++++++----- .../test/src/arrays/callback_return_array.c | 96 ++-- .../natives/test/src/arrays/pass_array.c | 106 ++-- .../natives/test/src/arrays/ping_array.c | 20 +- .../natives/test/src/arrays/return_array.c | 20 +- modules/tests/natives/test/src/critical.c | 60 +- modules/tests/natives/test/src/dictionaries.c | 76 +-- modules/tests/natives/test/src/jvmci.c | 50 +- .../src/primitives/callback_arg_primitive.c | 2 +- .../primitives/callback_return_primitive.c | 4 +- .../test/src/primitives/pass_primitive.c | 4 +- .../test/src/primitives/ping_primitive.c | 2 +- .../test/src/primitives/return_primitive.c | 10 +- .../kotlin/primitives/ReturnPrimitive.kt | 5 - 29 files changed, 2231 insertions(+), 1085 deletions(-) delete mode 100644 modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CJniArenaPrinter.kt diff --git a/modules/benchmarks/natives/foreignBindings/include/api.h b/modules/benchmarks/natives/foreignBindings/include/api.h index 28afab9..7255eb7 100644 --- a/modules/benchmarks/natives/foreignBindings/include/api.h +++ b/modules/benchmarks/natives/foreignBindings/include/api.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,6 +24,12 @@ extern "C" { // ║ stdlib ║ // ╚════════════════╝ +#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) + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -33,21 +40,33 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; +static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; +} + +static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -62,33 +81,63 @@ static void KString_free(KString* str) { _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; \ - } \ +#define KArrayDef(Name, Type, VarargType) \ +typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ +} Name; \ + \ +static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ +} \ + \ +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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ +} + +#define KArrayCloneDef(Name, Type) \ +static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ +} \ + \ +static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -111,10 +160,48 @@ 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__) +KArrayCloneDef(KCharArray, KChar) +KArrayCloneDef(KBooleanArray, KBoolean) +KArrayCloneDef(KByteArray, KByte) +KArrayCloneDef(KShortArray, KShort) +KArrayCloneDef(KIntArray, KInt) +KArrayCloneDef(KLongArray, KLong) +KArrayCloneDef(KFloatArray, KFloat) +KArrayCloneDef(KDoubleArray, KDouble) + +static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; +} + +static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); +} + #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; @@ -124,10 +211,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/jniBindings/include/api.h b/modules/benchmarks/natives/jniBindings/include/api.h index 66605ae..296e47b 100644 --- a/modules/benchmarks/natives/jniBindings/include/api.h +++ b/modules/benchmarks/natives/jniBindings/include/api.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,6 +24,12 @@ extern "C" { // ║ stdlib ║ // ╚════════════════╝ +#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) + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -33,21 +40,33 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; +static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; +} + +static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -62,33 +81,63 @@ static void KString_free(KString* str) { _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; \ - } \ +#define KArrayDef(Name, Type, VarargType) \ +typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ +} Name; \ + \ +static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ +} \ + \ +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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ +} + +#define KArrayCloneDef(Name, Type) \ +static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ +} \ + \ +static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -111,10 +160,48 @@ 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__) +KArrayCloneDef(KCharArray, KChar) +KArrayCloneDef(KBooleanArray, KBoolean) +KArrayCloneDef(KByteArray, KByte) +KArrayCloneDef(KShortArray, KShort) +KArrayCloneDef(KIntArray, KInt) +KArrayCloneDef(KLongArray, KLong) +KArrayCloneDef(KFloatArray, KFloat) +KArrayCloneDef(KDoubleArray, KDouble) + +static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; +} + +static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); +} + #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; @@ -124,9 +211,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/jvmciBindings/include/api.h b/modules/benchmarks/natives/jvmciBindings/include/api.h index ec4540a..30de2ac 100644 --- a/modules/benchmarks/natives/jvmciBindings/include/api.h +++ b/modules/benchmarks/natives/jvmciBindings/include/api.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,6 +24,12 @@ extern "C" { // ║ stdlib ║ // ╚════════════════╝ +#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) + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -33,21 +40,33 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; +static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; +} + +static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -62,33 +81,63 @@ static void KString_free(KString* str) { _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; \ - } \ +#define KArrayDef(Name, Type, VarargType) \ +typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ +} Name; \ + \ +static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ +} \ + \ +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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ +} + +#define KArrayCloneDef(Name, Type) \ +static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ +} \ + \ +static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -111,10 +160,48 @@ 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__) +KArrayCloneDef(KCharArray, KChar) +KArrayCloneDef(KBooleanArray, KBoolean) +KArrayCloneDef(KByteArray, KByte) +KArrayCloneDef(KShortArray, KShort) +KArrayCloneDef(KIntArray, KInt) +KArrayCloneDef(KLongArray, KLong) +KArrayCloneDef(KFloatArray, KFloat) +KArrayCloneDef(KDoubleArray, KDouble) + +static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; +} + +static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); +} + #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; @@ -124,7 +211,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/examples/freetype/natives/freetypeBindings/include/api.h b/modules/examples/freetype/natives/freetypeBindings/include/api.h index 70082b2..137e055 100644 --- a/modules/examples/freetype/natives/freetypeBindings/include/api.h +++ b/modules/examples/freetype/natives/freetypeBindings/include/api.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,6 +24,12 @@ extern "C" { // ║ stdlib ║ // ╚════════════════╝ +#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) + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -33,21 +40,33 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; +static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; +} + +static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -62,33 +81,63 @@ static void KString_free(KString* str) { _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; \ - } \ +#define KArrayDef(Name, Type, VarargType) \ +typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ +} Name; \ + \ +static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ +} \ + \ +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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ +} + +#define KArrayCloneDef(Name, Type) \ +static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ +} \ + \ +static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -111,10 +160,48 @@ 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__) +KArrayCloneDef(KCharArray, KChar) +KArrayCloneDef(KBooleanArray, KBoolean) +KArrayCloneDef(KByteArray, KByte) +KArrayCloneDef(KShortArray, KShort) +KArrayCloneDef(KIntArray, KInt) +KArrayCloneDef(KLongArray, KLong) +KArrayCloneDef(KFloatArray, KFloat) +KArrayCloneDef(KDoubleArray, KDouble) + +static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; +} + +static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); +} + #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; diff --git a/modules/examples/glfw/natives/glfwBindings/include/api.h b/modules/examples/glfw/natives/glfwBindings/include/api.h index 51d1dcb..0d1e40d 100644 --- a/modules/examples/glfw/natives/glfwBindings/include/api.h +++ b/modules/examples/glfw/natives/glfwBindings/include/api.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,6 +24,12 @@ extern "C" { // ║ stdlib ║ // ╚════════════════╝ +#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) + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -33,21 +40,33 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; +static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; +} + +static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -62,33 +81,63 @@ static void KString_free(KString* str) { _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; \ - } \ +#define KArrayDef(Name, Type, VarargType) \ +typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ +} Name; \ + \ +static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ +} \ + \ +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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ +} + +#define KArrayCloneDef(Name, Type) \ +static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ +} \ + \ +static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -111,10 +160,48 @@ 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__) +KArrayCloneDef(KCharArray, KChar) +KArrayCloneDef(KBooleanArray, KBoolean) +KArrayCloneDef(KByteArray, KByte) +KArrayCloneDef(KShortArray, KShort) +KArrayCloneDef(KIntArray, KInt) +KArrayCloneDef(KLongArray, KLong) +KArrayCloneDef(KFloatArray, KFloat) +KArrayCloneDef(KDoubleArray, KDouble) + +static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; +} + +static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); +} + #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; 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..3195a56 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 @@ -6,7 +6,6 @@ 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.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.* @@ -194,11 +193,6 @@ private abstract class PrepareNativesAndroid: DefaultTask() { isAndroidCriticalEnabled = useAndroidCriticalNative ) - CJniArenaPrinter( - target = File(nativesBuildDir, "jni_arena.h"), - callbacks = idl.callbacks.isNotEmpty() - ) - CHeaderPrinter( idl = idl, target = File(nativesBuildDir, "api.h") 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..723ce1c 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 @@ -6,7 +6,6 @@ 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.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 @@ -238,11 +237,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()) { 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/CHeaderPrinter.kt index d1710bf..c4afe9b 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/CHeaderPrinter.kt @@ -1,16 +1,23 @@ package com.huskerdev.nativekt.printers.c import com.huskerdev.nativekt.utils.allFields +import com.huskerdev.nativekt.utils.firstParam import com.huskerdev.nativekt.utils.globalOperators +import com.huskerdev.nativekt.utils.isArray import com.huskerdev.nativekt.utils.isCallback import com.huskerdev.nativekt.utils.isDictionary +import com.huskerdev.nativekt.utils.isPrimitive +import com.huskerdev.nativekt.utils.isString import com.huskerdev.nativekt.utils.printLabel +import com.huskerdev.nativekt.utils.simpleName import com.huskerdev.nativekt.utils.toCDefType +import com.huskerdev.webidl.resolver.BuiltinIdlDeclaration 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.webidl.resolver.ResolvedIdlType import java.io.File import kotlin.math.max @@ -59,6 +66,22 @@ class CHeaderPrinter( printCallbacks(builder, idl.callbacks.values) } + if(idl.dictionaries.isNotEmpty()) { + printLabel(builder, "Struct functions") + + idl.dictionaries.values.forEach { + builder.append("\nstatic ${it.name}* ${it.name}_clone(const ${it.name}* of);") + } + idl.dictionaries.values.forEach { + builder.append("\nstatic void ${it.name}_free(${it.name}* of);") + } + builder.append("\n") + idl.dictionaries.values.forEach { + printStructClone(builder, it) + printStructFree(builder, it) + } + } + printFooter(builder) target.writeText(builder.toString().replace("\n", System.lineSeparator())) @@ -96,45 +119,101 @@ class CHeaderPrinter( append(" // : ").append(dictionary.implements!!.name) append("\n\t") - dictionary.allFields().joinTo(builder, separator = "\n\t") { field -> - "${field.type.toCDefType()} ${field.name};" - } + buildList { + add("char __flags;") + dictionary.allFields().mapTo(this) { field -> + "${field.type.toCDefType()} ${field.name};" + } + }.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(") + append("\nstatic ${dictionary.name}* ${dictionary.name}_new(") + dictionary.allFields().joinTo(builder) { field -> - val const = if( - !field.type.isDictionary() && - !field.type.isCallback() - ) "const " else "" + val const = if(field.type.isPrimitive()) + "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("){ ") - dictionary.allFields().joinTo(builder) { field -> - field.name - } + append("${dictionary.name}* result = (${dictionary.name}*) malloc(sizeof(${dictionary.name}));\n\t") + + // fill + append("*result = (${dictionary.name}) { ") + buildList { + add("K_FLAG_RELEASABLE") + dictionary.allFields().mapTo(this) { it.name } + }.joinTo(builder) append(" };\n\t") + + // return + append("return result;\n}\n") + } + + private fun printStructClone(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + append("\nstatic ${dictionary.name}* ${dictionary.name}_clone(const ${dictionary.name}* of) {\n\t") + + // malloc + append("${dictionary.name}* result = (${dictionary.name}*) malloc(sizeof(${dictionary.name}));\n\t") + + // fill + append("*result = (${dictionary.name}) {\n\t\t") + buildList { + add("K_FLAG_RELEASABLE") + dictionary.allFields().mapTo(this) { field -> + when { + field.type.isString() -> "KString_clone(of->${field.name})" + field.type.isArray() -> { + (field.type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> { + val name = declaration.kind.simpleName() + "K${name}Array_clone(of->${field.name})" + } + is ResolvedIdlEnum -> "KIntArray_clone(of->${field.name})" + is ResolvedIdlDictionary -> "KArray_clone(of->${field.name}, (void*) ${declaration.name}_clone)" + else -> throw UnsupportedOperationException(field.type.toString()) + } + } + } + field.type.isCallback() -> "of->${field.name}->clone(of->${field.name})" + field.type.isDictionary() -> "${(field.type as ResolvedIdlType.Default).declaration.name}_clone(of->${field.name})" + else -> "of->${field.name}" + } + } + }.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 { + append("\nstatic void ${dictionary.name}_free(${dictionary.name}* of) {\n") + append(""" + if(!K_OBJECT_IS_RELEASABLE(of->__flags)) + return; + """.replaceIndent("\t")) + + dictionary.allFields().forEach { field -> + freeFuncFor( + field.type, + "of->${field.name}" + )?.apply { append("\n\t$this;") } + } + append(""" + + if(!K_OBJECT_IS_ON_STACK(of->__flags)) + free((void*) of); + """.replaceIndent("\t")) + + append("\n}\n") + } + private fun printStructTypedef(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { append("\ntypedef struct ") append(dictionary.name) @@ -235,6 +314,7 @@ class CHeaderPrinter( #include #include + #include #ifdef __cplusplus extern "C" { @@ -258,6 +338,12 @@ class CHeaderPrinter( private fun printStdLib(builder: StringBuilder){ 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) typedef int32_t KInt; typedef int64_t KLong; @@ -269,21 +355,33 @@ class CHeaderPrinter( typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; - static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; + static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; + } + + static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -298,33 +396,63 @@ class CHeaderPrinter( _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; \ - } \ + #define KArrayDef(Name, Type, VarargType) \ + typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ + } Name; \ + \ + static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ + } \ + \ + 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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ + } + + #define KArrayCloneDef(Name, Type) \ + static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ + } \ + \ + static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -346,11 +474,49 @@ 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__) + + KArrayCloneDef(KCharArray, KChar) + KArrayCloneDef(KBooleanArray, KBoolean) + KArrayCloneDef(KByteArray, KByte) + KArrayCloneDef(KShortArray, KShort) + KArrayCloneDef(KIntArray, KInt) + KArrayCloneDef(KLongArray, KLong) + KArrayCloneDef(KFloatArray, KFloat) + KArrayCloneDef(KDoubleArray, KDouble) + + static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; + } + + static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); + } #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; 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..a1aa288 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()) @@ -86,42 +86,97 @@ class CJniPrinter( add("JNIEnv *env") add("jclass __cls") addAll(function.args.map { - "${it.type.toJNIType()} __arg_${it.name}" + "${it.type.toJNIType()} __arg_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)} __arg_native_${it.name} = ${castJavaToJNI( + it.type, + "__arg_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()) + "__arg_jvm_${it.name}" + else "__arg_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;") } + } + + // ================== + // Release args + // ================== + + function.args.forEach { arg -> + when { + arg.type.isString() -> "JNI_releaseStringOnStack(env, __arg_native_${arg.name})" + arg.type.isArray() -> { + (arg.type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> { + val name = declaration.kind.simpleName() + "JNI_release${name}ArrayOnStack(env, __arg_native_${arg.name})" + } + is ResolvedIdlEnum -> "free((void*)__arg_native_${arg.name}->elements)" + is ResolvedIdlDictionary -> { + "JNI_forceFreeKArray(__arg_native_${arg.name}, (void*) JNI_forceFree${declaration.name})" + } + else -> throw UnsupportedOperationException(arg.type.toString()) + } + } + } + arg.type.isDictionary() -> "JNI_forceFree${(arg.type as ResolvedIdlType.Default).declaration.name}(__arg_native_${arg.name})" + else -> return@forEach + }.apply { append("\n\t$this;") } } - append("}\n") + // ================== + // Return + // ================== + + if(returns && needReleases) + append("\n\treturn __result_jvm;") + append("\n}\n") } private fun printCriticalFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { @@ -156,67 +211,108 @@ class CJniPrinter( "Java_${classPath.replace(".", "_")}_${this@CJniPrinter.name}_$name" } -internal fun castJniToJava(type: ResolvedIdlType, content: String, dealloc: Boolean, deallocContent: Boolean, useArena: Boolean): String { +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() -> "$content->free($content)" + type.isDictionary() -> "${(type as ResolvedIdlType.Default).declaration.name}_free(${content})" + else -> null +} + +internal fun forceFreeFuncFor( + type: ResolvedIdlType, + content: String +) = when { + type.isString() -> "JNI_forceFreeKString($content)" + type.isArray() -> (type as ResolvedIdlType.Default).firstParam { _, declaration -> + when (declaration) { + is BuiltinIdlDeclaration -> "JNI_forceFreeK${declaration.kind.simpleName()}Array($content)" + is ResolvedIdlEnum -> "JNI_forceFreeKIntArray($content)" + is ResolvedIdlDictionary -> "JNI_forceFreeKArray($content, (void*) JNI_forceFree${declaration.name})" + else -> throw UnsupportedOperationException(type.toString()) + } + } + type.isCallback() || type.isDictionary() -> + "JNI_forceFree${(type as ResolvedIdlType.Default).declaration.name}($content)" + else -> null +} + + +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, (JNI_Callback*)$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(JNI_CallbackSize))" + 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..414a71e 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,78 @@ 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; + const size_t JNI_StringStackSize = sizeof(KString) + 2; + + jstring JNI_toKotlinString(JNIEnv *env, KString* str) { + if(K_OBJECT_IS_ON_STACK(str->__flags)) + return (jstring)((size_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, stringUTF8Const); + (*env)->DeleteLocalRef(env, bytes); + return result; + } + + 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) { + K_FLAG_ON_STACK, + (const char*)(*env)->GetByteArrayElements(env, bytes, JNI_FALSE), + (*env)->GetStringLength(env, obj), + (*env)->GetArrayLength(env, bytes) + }; + 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); + jsize size = (*env)->GetArrayLength(env, bytes); - jbyteArray bytes = (*env)->NewByteArray(env, length); - (*env)->SetByteArrayRegion(env, bytes, 0, length, (jbyte*)str.data); + jbyte* str = (*env)->GetByteArrayElements(env, bytes, JNI_FALSE); + void* strCopy = malloc(size); + memcpy(strCopy, (void*)str, size); - jstring result = (jstring)(*env)->NewObject(env, stringClass, stringConstructor, bytes); + (*env)->ReleaseByteArrayElements(env, bytes, str, JNI_ABORT); (*env)->DeleteLocalRef(env, bytes); - if(dealloc) free((void*)str.data); + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { + flags, + (const char*) strCopy, + length, + size + }; return result; } - KString JNI_toNativeString(JNIEnv *env, jstring obj, bool releasable) { - 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 }; + void JNI_forceFreeKString(KString* str) { + str->__flags |= K_FLAG_RELEASABLE; + KString_free(str); } """.trimIndent()) @@ -66,21 +108,57 @@ 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) { \ + flags, \ + (K##Name*)elementsCopy, \ + length, \ + size \ + }; \ + 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_FLAG_ON_STACK, \ + (K##Name*)(*env)->Get##Name##ArrayElements(env, arr, JNI_FALSE), \ + length, \ + length * sizeof(JType) \ + }; \ + 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; \ + } \ + \ + void JNI_forceFreeK##Name##Array(K##Name##Array* arr) { \ + arr->__flags |= K_FLAG_RELEASABLE; \ + K##Name##Array_free(arr); \ } KArrayCast(Char, jchar) @@ -99,33 +177,75 @@ 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) { + flags, + (const void**) elements, + length, + length * sizeof(void*) + }; 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) { + K_FLAG_ON_STACK, + (const void**) elements, + length, + length * sizeof(void*) + }; + 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; + } + + void JNI_forceFreeKArray(KArray* arr, void* (*freeOp)(void*)) { + arr->__flags |= K_FLAG_RELEASABLE; + KArray_free(arr, freeOp); } """.trimIndent()) @@ -141,43 +261,84 @@ class CJniUtilsPrinter( "enum${it.name}Values" } 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++) - elements[i] = JNI_toNativeEnum(env, (*env)->GetObjectArrayElement(env, src, i)); - return (KIntArray){ elements, length, releasable, false }; + 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) { + flags, + (const KInt*) elements, + length, + length * sizeof(KInt) + }; + return result; + } + + KIntArray* JNI_toNativeEnumArrayOnStack( + JNIEnv *env, + jobjectArray src, + void* mem + ) { + ((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) { + K_FLAG_ON_STACK, + (const KInt*) elements, + length, + length * sizeof(KInt) + }; + 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; } @@ -189,50 +350,122 @@ class CJniUtilsPrinter( builder.append(""" typedef struct JNI_Callback { - void *m; + char __flags; void (*invoke)(); + struct JNI_Callback* (*clone)(struct JNI_Callback*); + KBoolean (*equals)(struct JNI_Callback*, struct JNI_Callback* obj); + KInt (*hashCode)(struct JNI_Callback*); void (*free)(struct JNI_Callback*); } JNI_Callback; + const size_t JNI_CallbackSize = sizeof(JNI_Callback) + 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, (void**)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(JNI_Callback* ref, JNI_Callback* with) { + jobject obj = (jobject)((size_t*)((char*)ref + sizeof(JNI_Callback)))[0]; + jobject obj2 = (jobject)((size_t*)((char*)with + sizeof(JNI_Callback)))[0]; + JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(JNI_Callback)))[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(JNI_Callback* ref) { + jobject obj = (jobject)((size_t*)((char*)ref + sizeof(JNI_Callback)))[0]; + JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(JNI_Callback)))[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(JNI_Callback* 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(JNI_Callback)))[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 JNI_Callback* JNI_CALLBACK_clone(JNI_Callback* ref) { + jobject obj = (jobject)((size_t*)((char*)ref + sizeof(JNI_Callback)))[0]; + JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(JNI_Callback)))[1]; + jint status = env == NULL ? JVM_attach(&env) : JNI_OK; + + JNI_Callback* callback = (JNI_Callback*) malloc(JNI_CallbackSize); + memcpy(callback, ref, sizeof(JNI_Callback)); + callback->__flags = K_FLAG_RELEASABLE; + + ((size_t*)((char*)callback + sizeof(JNI_Callback)))[0] = (size_t)(*env)->NewGlobalRef(env, obj); + ((size_t*)((char*)callback + sizeof(JNI_Callback)))[1] = (size_t) NULL; + + if(status == JNI_EDETACHED) JVM_detach(status); + return callback; + } + + jobject JNI_toKotlinCallback(JNIEnv *env, JNI_Callback* callback) { + return (jobject)((size_t*)((char*)callback + sizeof(JNI_Callback)))[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; + JNI_Callback* JNI_toNativeCallbackOnStack(JNIEnv *env, jobject obj, void (*invoke)(), void* mem) { + ((size_t*)((char*)mem + sizeof(JNI_Callback)))[0] = (size_t) obj; + ((size_t*)((char*)mem + sizeof(JNI_Callback)))[1] = (size_t) env; + JNI_Callback* callback = (JNI_Callback*) mem; + *callback = (JNI_Callback) { + K_FLAG_ON_STACK, + invoke, + JNI_CALLBACK_clone, + JNI_CALLBACK_equals, + JNI_CALLBACK_hashCode, + JNI_CALLBACK_free + }; + return callback; + } + + JNI_Callback* JNI_toNativeCallback(JNIEnv *env, jobject obj, void (*invoke)(), char flags) { + void* mem = malloc(JNI_CallbackSize); + ((size_t*)((char*)mem + sizeof(JNI_Callback)))[0] = (size_t) (*env)->NewGlobalRef(env, obj); + ((size_t*)((char*)mem + sizeof(JNI_Callback)))[1] = (size_t) NULL; + JNI_Callback* callback = (JNI_Callback*) mem; + *callback = (JNI_Callback) { + 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) + } + idl.callbacks.values.forEach { + printCallbackFreeDef(builder, it) + } } if(idl.dictionaries.isNotEmpty()) { @@ -250,6 +483,7 @@ class CJniUtilsPrinter( idl.callbacks.values.forEach { callback -> printCallbackInvoke(builder, callback) + printCallbackForceFree(builder, callback) } } @@ -290,42 +524,38 @@ 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 { + add("flags") + 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") + } + }.joinTo(builder, separator = ",\n\t\t") append("\n\t};") append("\n\treturn result;\n}\n") + + // Force free + append("\nvoid JNI_forceFree${struct.name}(${struct.name}* of) {") + struct.allFields().forEach { field -> + forceFreeFuncFor( + field.type, "of->${field.name}" + )?.apply { append("\n\t$this;") } + } + append("\n\tfree((void*) of);\n}\n") } } @@ -336,43 +566,61 @@ class CJniUtilsPrinter( append("${callback.type.toCDefType()} JNI_CALLBACK_INVOKE_${callback.name}(${args.joinToString()});\n") } + private fun printCallbackFreeDef(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { + append("void JNI_forceFree${callback.name}(${callback.name}* of);\n") + } + 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(JNI_Callback)))[0]; + JNIEnv *env = (JNIEnv*)((size_t*)((char*)callback + sizeof(JNI_Callback)))[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") } + private fun printCallbackForceFree(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { + append(""" + + void JNI_forceFree${callback.name}(${callback.name}* of) { + of->__flags |= K_FLAG_RELEASABLE; + of->free(of); + } + """.trimIndent()) + append("\n") + } + private fun printRegisterFunction(builder: StringBuilder) = builder.apply { printLabel(builder, "Init function") append(""" @@ -383,9 +631,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/utils/PrinterUtils.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/PrinterUtils.kt index df1c919..e4bc20e 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 @@ -133,23 +133,23 @@ 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 (longPtr) "*" 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*" + WebIDLBuiltinKind.BOOLEAN -> "KBooleanArray*" + WebIDLBuiltinKind.BYTE -> "KByteArray*" + WebIDLBuiltinKind.SHORT -> "KShortArray*" + WebIDLBuiltinKind.INT -> "KIntArray*" + WebIDLBuiltinKind.LONG -> "KLongArray*" + WebIDLBuiltinKind.FLOAT -> "KFloatArray*" + WebIDLBuiltinKind.DOUBLE -> "KDoubleArray*" + WebIDLBuiltinKind.STRING -> "KStringArray*" else -> throw UnsupportedOperationException() } - is ResolvedIdlEnum -> "KIntArray" - is ResolvedIdlDictionary -> "KArray" + is ResolvedIdlEnum -> "KIntArray*" + is ResolvedIdlDictionary -> "KArray*" else -> throw UnsupportedOperationException(declaration.name) } } @@ -266,6 +266,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 || diff --git a/modules/test-js-only/natives/jsOnlyTest/include/api.h b/modules/test-js-only/natives/jsOnlyTest/include/api.h index 5918965..b614a28 100644 --- a/modules/test-js-only/natives/jsOnlyTest/include/api.h +++ b/modules/test-js-only/natives/jsOnlyTest/include/api.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,6 +24,12 @@ extern "C" { // ║ stdlib ║ // ╚════════════════╝ +#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) + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -33,21 +40,33 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; +static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; +} + +static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -62,33 +81,63 @@ static void KString_free(KString* str) { _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; \ - } \ +#define KArrayDef(Name, Type, VarargType) \ +typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ +} Name; \ + \ +static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ +} \ + \ +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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ +} + +#define KArrayCloneDef(Name, Type) \ +static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ +} \ + \ +static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -111,10 +160,48 @@ 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__) +KArrayCloneDef(KCharArray, KChar) +KArrayCloneDef(KBooleanArray, KBoolean) +KArrayCloneDef(KByteArray, KByte) +KArrayCloneDef(KShortArray, KShort) +KArrayCloneDef(KIntArray, KInt) +KArrayCloneDef(KLongArray, KLong) +KArrayCloneDef(KFloatArray, KFloat) +KArrayCloneDef(KDoubleArray, KDouble) + +static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; +} + +static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); +} + #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; diff --git a/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h b/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h index 18338b1..70eb4b9 100644 --- a/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h +++ b/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,6 +24,12 @@ extern "C" { // ║ stdlib ║ // ╚════════════════╝ +#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) + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -33,21 +40,33 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; +static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; +} + +static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -62,33 +81,63 @@ static void KString_free(KString* str) { _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; \ - } \ +#define KArrayDef(Name, Type, VarargType) \ +typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ +} Name; \ + \ +static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ +} \ + \ +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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ +} + +#define KArrayCloneDef(Name, Type) \ +static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ +} \ + \ +static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -111,10 +160,48 @@ 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__) +KArrayCloneDef(KCharArray, KChar) +KArrayCloneDef(KBooleanArray, KBoolean) +KArrayCloneDef(KByteArray, KByte) +KArrayCloneDef(KShortArray, KShort) +KArrayCloneDef(KIntArray, KInt) +KArrayCloneDef(KLongArray, KLong) +KArrayCloneDef(KFloatArray, KFloat) +KArrayCloneDef(KDoubleArray, KDouble) + +static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; +} + +static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); +} + #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; diff --git a/modules/tests/build.gradle.kts b/modules/tests/build.gradle.kts index cd8de7c..5ea5768 100644 --- a/modules/tests/build.gradle.kts +++ b/modules/tests/build.gradle.kts @@ -88,6 +88,9 @@ natives { useJsBigInt = true useJvmRecord = false + useJVMCI = false + useForeignApi = false + if(project.hasProperty("disableForeign")) { println("Disable: Foreign") useForeignApi = false diff --git a/modules/tests/natives/test/include/api.h b/modules/tests/natives/test/include/api.h index 6c95917..727eaee 100644 --- a/modules/tests/natives/test/include/api.h +++ b/modules/tests/natives/test/include/api.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -23,6 +24,12 @@ extern "C" { // ║ stdlib ║ // ╚════════════════╝ +#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) + typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -33,21 +40,33 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { + char __flags; const char* data; KInt length; - KBoolean releasable; - KBoolean released; + size_t size; } KString; -static KString KString_new(const char* data, const KInt length) { - return (KString) { data, length, true, false }; +static KString* KString_new(const char* data, const KInt length, const KInt size) { + KString* result = (KString*) malloc(sizeof(KString)); + *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; + return result; +} + +static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; + return result; } static void KString_free(KString* str) { - if(str->releasable && !str->released) { - free((void*)str->data); - str->released = true; - } + if(!K_OBJECT_IS_RELEASABLE(str->__flags)) + return; + free((void*) str->data); + if(!K_OBJECT_IS_ON_STACK(str->__flags)) + free((void*) str); } #define ARG_LENGTH(...) ARG_LENGTH__(__VA_ARGS__) @@ -62,33 +81,63 @@ static void KString_free(KString* str) { _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; \ - } \ +#define KArrayDef(Name, Type, VarargType) \ +typedef struct Name { \ + char __flags; \ + const Type* elements; \ + KInt length; \ + size_t size; \ +} Name; \ + \ +static Name* Name##_new(const Type* elements, const KInt length) { \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + elements, \ + length, \ + length * sizeof(Name) \ + }; \ + return result; \ +} \ + \ +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); \ + Name* result = (Name*) malloc(sizeof(Name)); \ + *result = (Name){ \ + K_FLAG_RELEASABLE, \ + (const Type*) elements, \ + n, \ + n * sizeof(Name) \ + }; \ + return result; \ +} + +#define KArrayCloneDef(Name, Type) \ +static 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) { \ + K_FLAG_RELEASABLE, \ + (Type*) elements, \ + of->length, \ + of->size \ + }; \ + return result; \ +} \ + \ +static 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); \ } KArrayDef(KCharArray, KChar, int32_t) @@ -111,10 +160,48 @@ 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__) +KArrayCloneDef(KCharArray, KChar) +KArrayCloneDef(KBooleanArray, KBoolean) +KArrayCloneDef(KByteArray, KByte) +KArrayCloneDef(KShortArray, KShort) +KArrayCloneDef(KIntArray, KInt) +KArrayCloneDef(KLongArray, KLong) +KArrayCloneDef(KFloatArray, KFloat) +KArrayCloneDef(KDoubleArray, KDouble) + +static 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) { + K_FLAG_RELEASABLE, + (const void**) elements, + of->length, + of->size + }; + return result; +} + +static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { + if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) + return; + const void** elements = arr->elements; + for (int i = 0; i < arr->length; i++) + freeOp((void*) elements[i]); + free((void*) elements); + if(!K_OBJECT_IS_ON_STACK(arr->__flags)) + free((void*) arr); +} + #define KCallbackDef(Name, Type, ...) \ struct Name { \ - void *m; \ + char __flags; \ Type (*invoke)(Name* _, ##__VA_ARGS__); \ + Name* (*clone)(Name* _); \ + KBoolean (*equals)(Name* _, Name* obj); \ + KInt (*hashCode)(Name* _); \ void (*free)(Name* _); \ }; @@ -187,11 +274,13 @@ typedef enum { // ╚═════════════════╝ struct ParentDictionary { + char __flags; KInt a; KInt b; }; struct MyDictionary { // : ParentDictionary + char __flags; KInt a; KInt b; KInt c; @@ -199,6 +288,7 @@ struct MyDictionary { // : ParentDictionary }; struct TypeDictionary { + char __flags; KChar a1; KBoolean a2; KByte a3; @@ -207,37 +297,37 @@ 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; }; static ParentDictionary* ParentDictionary_new(const KInt a, const KInt b) { - ParentDictionary* result = (ParentDictionary*)malloc(sizeof(ParentDictionary)); - *result = (ParentDictionary){ a, b }; + ParentDictionary* result = (ParentDictionary*) malloc(sizeof(ParentDictionary)); + *result = (ParentDictionary) { K_FLAG_RELEASABLE, 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 }; + MyDictionary* result = (MyDictionary*) malloc(sizeof(MyDictionary)); + *result = (MyDictionary) { K_FLAG_RELEASABLE, 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 }; +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, 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* result = (TypeDictionary*) malloc(sizeof(TypeDictionary)); + *result = (TypeDictionary) { K_FLAG_RELEASABLE, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22 }; return result; } @@ -254,7 +344,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 +356,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 +367,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 +395,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 +450,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,69 +471,166 @@ 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 +// ╔══════════════════════════╗ +// ║ Struct functions ║ +// ╚══════════════════════════╝ + +static ParentDictionary* ParentDictionary_clone(const ParentDictionary* of); +static MyDictionary* MyDictionary_clone(const MyDictionary* of); +static TypeDictionary* TypeDictionary_clone(const TypeDictionary* of); +static void ParentDictionary_free(ParentDictionary* of); +static void MyDictionary_free(MyDictionary* of); +static void TypeDictionary_free(TypeDictionary* of); + +static ParentDictionary* ParentDictionary_clone(const ParentDictionary* of) { + ParentDictionary* result = (ParentDictionary*) malloc(sizeof(ParentDictionary)); + *result = (ParentDictionary) { + K_FLAG_RELEASABLE, + of->a, + of->b + }; + return result; +} + +static void ParentDictionary_free(ParentDictionary* of) { + if(!K_OBJECT_IS_RELEASABLE(of->__flags)) + return; + if(!K_OBJECT_IS_ON_STACK(of->__flags)) + free((void*) of); +} + +static MyDictionary* MyDictionary_clone(const MyDictionary* of) { + MyDictionary* result = (MyDictionary*) malloc(sizeof(MyDictionary)); + *result = (MyDictionary) { + K_FLAG_RELEASABLE, + of->a, + of->b, + of->c, + of->d + }; + return result; +} + +static void MyDictionary_free(MyDictionary* of) { + if(!K_OBJECT_IS_RELEASABLE(of->__flags)) + return; + if(!K_OBJECT_IS_ON_STACK(of->__flags)) + free((void*) of); +} + +static TypeDictionary* TypeDictionary_clone(const TypeDictionary* of) { + TypeDictionary* result = (TypeDictionary*) malloc(sizeof(TypeDictionary)); + *result = (TypeDictionary) { + K_FLAG_RELEASABLE, + of->a1, + of->a2, + of->a3, + of->a4, + of->a5, + of->a6, + of->a7, + of->a8, + KString_clone(of->a9), + of->a10, + MyDictionary_clone(of->a11), + of->a12->clone(of->a12), + KCharArray_clone(of->a13), + KBooleanArray_clone(of->a14), + KByteArray_clone(of->a15), + KShortArray_clone(of->a16), + KIntArray_clone(of->a17), + KLongArray_clone(of->a18), + KFloatArray_clone(of->a19), + KDoubleArray_clone(of->a20), + KIntArray_clone(of->a21), + KArray_clone(of->a22, (void*) MyDictionary_clone) + }; + return result; +} + +static void TypeDictionary_free(TypeDictionary* of) { + if(!K_OBJECT_IS_RELEASABLE(of->__flags)) + return; + KString_free(of->a9); + MyDictionary_free(of->a11); + of->a12->free(of->a12); + KCharArray_free(of->a13); + KBooleanArray_free(of->a14); + KByteArray_free(of->a15); + KShortArray_free(of->a16); + KIntArray_free(of->a17); + KLongArray_free(of->a18); + KFloatArray_free(of->a19); + KDoubleArray_free(of->a20); + KIntArray_free(of->a21); + KArray_free(of->a22, (void*) MyDictionary_free); + if(!K_OBJECT_IS_ON_STACK(of->__flags)) + free((void*) of); +} + #ifdef __cplusplus } 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..b707123 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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 K_OBJECT_IS_RELEASABLE(array->__flags) && + 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..a95db3a 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && + 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..fca01bc 100644 --- a/modules/tests/natives/test/src/jvmci.c +++ b/modules/tests/natives/test/src/jvmci.c @@ -75,12 +75,12 @@ KBoolean jvmci9( } 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: %lld, a5: %lld, a6: %f, a7: %s, a8: %f, a9: %d\n", a1->data, a2, a3, a4, a5, a6, a7->data, 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 +110,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..640a1d5 100644 --- a/modules/tests/natives/test/src/primitives/callback_arg_primitive.c +++ b/modules/tests/natives/test/src/primitives/callback_arg_primitive.c @@ -37,7 +37,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..2bd3e45 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 K_OBJECT_IS_RELEASABLE(str->__flags) && 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..fd22287 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 !K_OBJECT_IS_RELEASABLE(arg->__flags) && 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()) From ed3415f4cd0addc69794e631b955568109557b42 Mon Sep 17 00:00:00 2001 From: nshtengauer Date: Tue, 9 Jun 2026 00:40:16 +0300 Subject: [PATCH 2/8] Refactored native calling in all platforms --- libs.versions.toml | 7 +- modules/benchmarks/build.gradle.kts | 4 + .../natives/foreignBindings/include/api.h | 220 ++---- .../natives/foreignBindings/src/bindings.c | 4 +- .../natives/jniBindings/include/api.h | 220 ++---- .../natives/jniBindings/src/bindings.c | 4 +- .../natives/jvmciBindings/include/api.h | 220 ++---- .../natives/jvmciBindings/src/bindings.c | 2 +- .../src/jvmMain/kotlin/benchmark/Benchmark.kt | 1 + .../natives/freetypeBindings/include/api.h | 220 ++---- .../glfw/natives/glfwBindings/include/api.h | 220 ++---- .../configurators/AndroidConfigurator.kt | 16 +- .../nativekt/configurators/JsConfigurator.kt | 77 +- .../nativekt/configurators/JvmConfigurator.kt | 16 +- .../configurators/NativeConfigurator.kt | 24 +- .../huskerdev/nativekt/plugin/Configurator.kt | 6 +- .../com/huskerdev/nativekt/plugin/InitTask.kt | 4 +- .../nativekt/printers/c/CApiHeaderPrinter.kt | 437 ++++++++++++ .../nativekt/printers/c/CApiImplPrinter.kt | 304 ++++++++ .../nativekt/printers/c/CEmscriptenPrinter.kt | 226 +----- .../nativekt/printers/c/CExportedPrinter.kt | 149 ++-- .../nativekt/printers/c/CHeaderPrinter.kt | 525 -------------- .../nativekt/printers/c/CJniPrinter.kt | 115 +-- .../nativekt/printers/c/CJniUtilsPrinter.kt | 157 ++--- .../printers/kotlin/KotlinCommonPrinter.kt | 2 +- .../printers/kotlin/KotlinJsPrinter.kt | 543 ++++++++------ .../printers/kotlin/KotlinJvmPrinter.kt | 15 +- .../printers/kotlin/KotlinNativePrinter.kt | 582 +++++---------- .../printers/kotlin/jvm/KotlinJvmCIPrinter.kt | 76 +- .../kotlin/jvm/KotlinJvmForeignPrinter.kt | 430 +++++------ .../kotlin/jvm/KotlinJvmJniPrinter.kt | 28 +- .../huskerdev/nativekt/utils/CStructLayout.kt | 25 +- .../huskerdev/nativekt/utils/PrinterUtils.kt | 73 +- modules/runtime/build.gradle.kts | 42 +- .../nativekt/kn/KnUtils.androidNativeArm32.kt | 16 - .../nativekt/kn/KnUtils.androidNativeArm64.kt | 16 - .../nativekt/kn/KnUtils.androidNativeX64.kt | 16 - .../nativekt/kn/KnUtils.androidNativeX86.kt | 16 - .../kotlin/com/huskerdev/nativekt/OS.kt | 18 +- .../com/huskerdev/nativekt/kn/KnUtils.ios.kt | 16 - .../huskerdev/nativekt/jvm/NativeKtUtils.java | 9 +- .../nativekt/jvm/foreign/CStructLayout.kt | 35 + .../nativekt/jvm/foreign/ForeignArena.java | 178 ----- .../nativekt/jvm/foreign/ForeignUtils.java | 667 ++++++++---------- .../huskerdev/nativekt/kn/KnUtils.linux.kt | 16 - .../huskerdev/nativekt/kn/KnUtils.macos.kt | 16 - .../huskerdev/nativekt/kn/KnUtils.mingw.kt | 16 - .../src/nativeInterop/cinterop/api.def | 109 +++ .../com/huskerdev/nativekt/kn/KnUtils.kt | 390 +++++++++- .../com/huskerdev/nativekt/kn/NativeArena.kt | 60 -- .../com/huskerdev/nativekt/kn/KnUtils.tvos.kt | 16 - .../nativekt/kn/KnUtils.watchosArm32.kt | 17 - .../nativekt/kn/KnUtils.watchosArm64.kt | 16 - .../nativekt/kn/KnUtils.watchosDeviceArm64.kt | 16 - .../kn/KnUtils.watchosSimulatorArm64.kt | 16 - .../nativekt/kn/KnUtils.watchosX64.kt | 16 - .../huskerdev/nativekt/web/CStructLayout.kt | 47 ++ .../com/huskerdev/nativekt/web/EmArena.kt | 137 +--- .../com/huskerdev/nativekt/web/EmTypes.kt | 38 +- .../com/huskerdev/nativekt/web/EmUtils.kt | 564 ++++++++------- modules/test-js-only/build.gradle.kts | 32 - .../natives/jsOnlyTest/CMakeLists.txt | 7 - .../test-js-only/natives/jsOnlyTest/api.ndl | 4 - .../natives/jsOnlyTest/include/api.h | 218 ------ .../natives/jsOnlyTest/src/jsOnlyTest.c | 7 - modules/test-js-only/src/test/kotlin/Test.kt | 12 - .../natives/jvmOnlyTest/include/api.h | 220 ++---- modules/tests/build.gradle.kts | 3 - modules/tests/natives/test/include/api.h | 351 ++------- .../test/src/arrays/callback_return_array.c | 20 +- .../natives/test/src/arrays/pass_array.c | 22 +- .../src/primitives/callback_arg_primitive.c | 1 + .../primitives/callback_return_primitive.c | 2 +- .../test/src/primitives/pass_primitive.c | 2 +- settings.gradle.kts | 1 - 75 files changed, 3689 insertions(+), 4684 deletions(-) create mode 100644 modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt create mode 100644 modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiImplPrinter.kt delete mode 100644 modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CHeaderPrinter.kt delete mode 100644 modules/runtime/src/androidNativeArm32Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeArm32.kt delete mode 100644 modules/runtime/src/androidNativeArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeArm64.kt delete mode 100644 modules/runtime/src/androidNativeX64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeX64.kt delete mode 100644 modules/runtime/src/androidNativeX86Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.androidNativeX86.kt delete mode 100644 modules/runtime/src/iosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.ios.kt create mode 100644 modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/CStructLayout.kt delete mode 100644 modules/runtime/src/jvmMain/java/com/huskerdev/nativekt/jvm/foreign/ForeignArena.java delete mode 100644 modules/runtime/src/linuxMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.linux.kt delete mode 100644 modules/runtime/src/macosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.macos.kt delete mode 100644 modules/runtime/src/mingwMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.mingw.kt create mode 100644 modules/runtime/src/nativeInterop/cinterop/api.def delete mode 100644 modules/runtime/src/nativeMain/kotlin/com/huskerdev/nativekt/kn/NativeArena.kt delete mode 100644 modules/runtime/src/tvosMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.tvos.kt delete mode 100644 modules/runtime/src/watchosArm32Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosArm32.kt delete mode 100644 modules/runtime/src/watchosArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosArm64.kt delete mode 100644 modules/runtime/src/watchosDeviceArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosDeviceArm64.kt delete mode 100644 modules/runtime/src/watchosSimulatorArm64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosSimulatorArm64.kt delete mode 100644 modules/runtime/src/watchosX64Main/kotlin/com/huskerdev/nativekt/kn/KnUtils.watchosX64.kt create mode 100644 modules/runtime/src/webMain/kotlin/com/huskerdev/nativekt/web/CStructLayout.kt delete mode 100644 modules/test-js-only/build.gradle.kts delete mode 100644 modules/test-js-only/natives/jsOnlyTest/CMakeLists.txt delete mode 100644 modules/test-js-only/natives/jsOnlyTest/api.ndl delete mode 100644 modules/test-js-only/natives/jsOnlyTest/include/api.h delete mode 100644 modules/test-js-only/natives/jsOnlyTest/src/jsOnlyTest.c delete mode 100644 modules/test-js-only/src/test/kotlin/Test.kt 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 7255eb7..1848d0c 100644 --- a/modules/benchmarks/natives/foreignBindings/include/api.h +++ b/modules/benchmarks/natives/foreignBindings/include/api.h @@ -10,26 +10,29 @@ #define KOTLIN_NATIVE_FOREIGNBINDINGS_H #include -#include - #include #include -#include #ifdef __cplusplus 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 ║ // ╚════════════════╝ -#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) - typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -40,115 +43,36 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { - char __flags; const char* data; - KInt length; size_t size; + KInt length; + char __flags; } KString; -static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; -} - -static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; -} - -static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); -} - -#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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ -} Name; \ - \ -static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ -} \ - \ -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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ -} - -#define KArrayCloneDef(Name, Type) \ -static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ -} \ - \ -static 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); \ -} - -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__) @@ -160,49 +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__) -KArrayCloneDef(KCharArray, KChar) -KArrayCloneDef(KBooleanArray, KBoolean) -KArrayCloneDef(KByteArray, KByte) -KArrayCloneDef(KShortArray, KShort) -KArrayCloneDef(KIntArray, KInt) -KArrayCloneDef(KLongArray, KLong) -KArrayCloneDef(KFloatArray, KFloat) -KArrayCloneDef(KDoubleArray, KDouble) - -static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; -} - -static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); -} - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - 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/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 296e47b..14c9b66 100644 --- a/modules/benchmarks/natives/jniBindings/include/api.h +++ b/modules/benchmarks/natives/jniBindings/include/api.h @@ -10,26 +10,29 @@ #define KOTLIN_NATIVE_JNIBINDINGS_H #include -#include - #include #include -#include #ifdef __cplusplus 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 ║ // ╚════════════════╝ -#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) - typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -40,115 +43,36 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { - char __flags; const char* data; - KInt length; size_t size; + KInt length; + char __flags; } KString; -static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; -} - -static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; -} - -static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); -} - -#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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ -} Name; \ - \ -static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ -} \ - \ -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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ -} - -#define KArrayCloneDef(Name, Type) \ -static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ -} \ - \ -static 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); \ -} - -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__) @@ -160,49 +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__) -KArrayCloneDef(KCharArray, KChar) -KArrayCloneDef(KBooleanArray, KBoolean) -KArrayCloneDef(KByteArray, KByte) -KArrayCloneDef(KShortArray, KShort) -KArrayCloneDef(KIntArray, KInt) -KArrayCloneDef(KLongArray, KLong) -KArrayCloneDef(KFloatArray, KFloat) -KArrayCloneDef(KDoubleArray, KDouble) - -static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; -} - -static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); -} - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - 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/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 30de2ac..28611aa 100644 --- a/modules/benchmarks/natives/jvmciBindings/include/api.h +++ b/modules/benchmarks/natives/jvmciBindings/include/api.h @@ -10,26 +10,29 @@ #define KOTLIN_NATIVE_JVMCIBINDINGS_H #include -#include - #include #include -#include #ifdef __cplusplus 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 ║ // ╚════════════════╝ -#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) - typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -40,115 +43,36 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { - char __flags; const char* data; - KInt length; size_t size; + KInt length; + char __flags; } KString; -static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; -} - -static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; -} - -static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); -} - -#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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ -} Name; \ - \ -static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ -} \ - \ -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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ -} - -#define KArrayCloneDef(Name, Type) \ -static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ -} \ - \ -static 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); \ -} - -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__) @@ -160,49 +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__) -KArrayCloneDef(KCharArray, KChar) -KArrayCloneDef(KBooleanArray, KBoolean) -KArrayCloneDef(KByteArray, KByte) -KArrayCloneDef(KShortArray, KShort) -KArrayCloneDef(KIntArray, KInt) -KArrayCloneDef(KLongArray, KLong) -KArrayCloneDef(KFloatArray, KFloat) -KArrayCloneDef(KDoubleArray, KDouble) - -static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; -} - -static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); -} - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - 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/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 137e055..c33fe2d 100644 --- a/modules/examples/freetype/natives/freetypeBindings/include/api.h +++ b/modules/examples/freetype/natives/freetypeBindings/include/api.h @@ -10,26 +10,29 @@ #define KOTLIN_NATIVE_FREETYPEBINDINGS_H #include -#include - #include #include -#include #ifdef __cplusplus 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 ║ // ╚════════════════╝ -#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) - typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -40,115 +43,36 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { - char __flags; const char* data; - KInt length; size_t size; + KInt length; + char __flags; } KString; -static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; -} - -static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; -} - -static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); -} - -#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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ -} Name; \ - \ -static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ -} \ - \ -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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ -} - -#define KArrayCloneDef(Name, Type) \ -static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ -} \ - \ -static 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); \ -} - -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__) @@ -160,49 +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__) -KArrayCloneDef(KCharArray, KChar) -KArrayCloneDef(KBooleanArray, KBoolean) -KArrayCloneDef(KByteArray, KByte) -KArrayCloneDef(KShortArray, KShort) -KArrayCloneDef(KIntArray, KInt) -KArrayCloneDef(KLongArray, KLong) -KArrayCloneDef(KFloatArray, KFloat) -KArrayCloneDef(KDoubleArray, KDouble) - -static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; -} - -static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); -} - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - 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 0d1e40d..18ff713 100644 --- a/modules/examples/glfw/natives/glfwBindings/include/api.h +++ b/modules/examples/glfw/natives/glfwBindings/include/api.h @@ -10,26 +10,29 @@ #define KOTLIN_NATIVE_GLFWBINDINGS_H #include -#include - #include #include -#include #ifdef __cplusplus 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 ║ // ╚════════════════╝ -#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) - typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -40,115 +43,36 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { - char __flags; const char* data; - KInt length; size_t size; + KInt length; + char __flags; } KString; -static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; -} - -static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; -} - -static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); -} - -#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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ -} Name; \ - \ -static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ -} \ - \ -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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ -} - -#define KArrayCloneDef(Name, Type) \ -static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ -} \ - \ -static 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); \ -} - -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__) @@ -160,49 +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__) -KArrayCloneDef(KCharArray, KChar) -KArrayCloneDef(KBooleanArray, KBoolean) -KArrayCloneDef(KByteArray, KByte) -KArrayCloneDef(KShortArray, KShort) -KArrayCloneDef(KIntArray, KInt) -KArrayCloneDef(KLongArray, KLong) -KArrayCloneDef(KFloatArray, KFloat) -KArrayCloneDef(KDoubleArray, KDouble) - -static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; -} - -static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); -} - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - 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 3195a56..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,7 +4,8 @@ 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.CJniPrinter import com.huskerdev.nativekt.printers.c.CJniUtilsPrinter @@ -163,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( @@ -193,9 +194,16 @@ private abstract class PrepareNativesAndroid: DefaultTask() { isAndroidCriticalEnabled = useAndroidCriticalNative ) - CHeaderPrinter( + CApiHeaderPrinter( idl = idl, - target = File(nativesBuildDir, "api.h") + target = File(nativesBuildDir, "api.h"), + isInternal = true + ) + + CApiImplPrinter( + idl = idl, + 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 723ce1c..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,7 +4,8 @@ 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.CJniPrinter import com.huskerdev.nativekt.printers.c.CJniUtilsPrinter @@ -198,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 @@ -278,9 +279,16 @@ private abstract class PrepareNativesJvm: DefaultTask() { ) } - CHeaderPrinter( + CApiHeaderPrinter( idl = idl, - target = File(nativesBuildDir, "api.h") + target = File(nativesBuildDir, "api.h"), + isInternal = true + ) + + CApiImplPrinter( + idl = idl, + 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/CApiHeaderPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt new file mode 100644 index 0000000..1bd0602 --- /dev/null +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt @@ -0,0 +1,437 @@ +package com.huskerdev.nativekt.printers.c + +import com.huskerdev.nativekt.utils.allFields +import com.huskerdev.nativekt.utils.firstParam +import com.huskerdev.nativekt.utils.globalOperators +import com.huskerdev.nativekt.utils.isArray +import com.huskerdev.nativekt.utils.isCallback +import com.huskerdev.nativekt.utils.isDictionary +import com.huskerdev.nativekt.utils.isPrimitive +import com.huskerdev.nativekt.utils.isString +import com.huskerdev.nativekt.utils.printLabel +import com.huskerdev.nativekt.utils.simpleName +import com.huskerdev.nativekt.utils.toCDefType +import com.huskerdev.webidl.resolver.BuiltinIdlDeclaration +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.webidl.resolver.ResolvedIdlType +import java.io.File +import kotlin.math.max + +class CApiHeaderPrinter( + idl: IdlResolver, + target: File, + val guardName: String? = null, + val isInternal: Boolean = false +) { + private val defName = "KOTLIN_NATIVE_${guardName}_H" + + init { + target.parentFile.mkdirs() + + val builder = StringBuilder() + printHeader(builder) + + printLabel(builder, "stdlib") + printStdLib(builder) + + if(idl.callbacks.isNotEmpty()) { + printLabel(builder, "Type defs") + 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") + } + + if(idl.enums.isNotEmpty()) { + builder.append("\n") + printLabel(builder, "Enums") + idl.enums.values.forEach { printEnum(builder, it) } + } + + if(idl.dictionaries.isNotEmpty()) { + builder.append("\n") + printLabel(builder, "Structs") + idl.dictionaries.values.forEach { + printStruct(builder, it) + printStructFunctions(builder, it) + } + } + + printLabel(builder, "Functions") + idl.globalOperators().forEach { printFunction(builder, it) } + + if(idl.callbacks.isNotEmpty()) { + builder.append("\n") + printLabel(builder, "Callbacks") + printCallbacks(builder, idl.callbacks.values) + } + + printFooter(builder) + + target.writeText(builder.toString().replace("\n", System.lineSeparator())) + } + + private fun printFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { + append("\n") + append(function.type.toCDefType()) + append(" ") + append(function.name) + append("(") + + function.args.joinTo(builder) { + "${it.type.toCDefType()} ${it.name}" + } + + append(");") + } + + private fun printEnum(builder: StringBuilder, enum: ResolvedIdlEnum) = builder.apply { + append("\ntypedef enum {\n\t") + enum.elements.joinTo(builder, separator = ",\n\t") { + "${enum.name}_${it}" + } + append("\n} ") + append(enum.name) + append(";\n") + } + + private fun printStruct(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + append("\nstruct ") + append(dictionary.name) + append(" {") + if(dictionary.implements != null) + append(" // : ").append(dictionary.implements!!.name) + append("\n\t") + + buildList { + dictionary.allFields().mapTo(this) { field -> + "${field.type.toCDefType()} ${field.name};" + } + add("char __flags;") + }.joinTo(builder, separator = "\n\t") + + append("\n};\n") + } + + private fun printStructFunctions(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { + val name = dictionary.name + + append("\n$name* ${name}_new(") + + dictionary.allFields().joinTo(builder) { field -> + "${field.type.toCDefType()} ${field.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 { + val column1 = "Name" + val column2 = "Type" + val column3 = "Args" + + val names = arrayListOf() + val types = arrayListOf() + val args = arrayListOf() + + callbacks.forEach { callback -> + names += callback.name + "," + types += callback.type.toCDefType() + if(callback.args.isNotEmpty()) "," else "" + args += callback.args.joinToString { "${it.type.toCDefType()} ${it.name}" } + } + + val width1 = max(column1.length, names.maxOf { it.length }) + val width2 = max(column2.length, types.maxOf { it.length }) + val width3 = max(column3.length, args.maxOf { it.length }) + + // ┌───────┬─────────────────────┬────────────────┬─────────────────────────┐ + // │ │ Name │ Type │ Args │ + // └───────┴─────────────────────┴────────────────┴─────────────────────────┘ + // table + append("// ┌───────┬") + append("─".repeat(width1)).append("┬") + append("─".repeat(width2)).append("┬") + append("─".repeat(width3+1)).append("┐\n") + append("// │ ... │ ") + append(column1).append(" ".repeat(width1 - column1.length - 1)).append("│ ") + append(column2).append(" ".repeat(width2 - column2.length - 1)).append("│ ") + append(column3).append(" ".repeat(width3 - column3.length)).append("│\n") + append("// └───────┴") + append("─".repeat(width1)).append("┴") + append("─".repeat(width2)).append("┴") + append("─".repeat(width3+1)).append("┘\n") + + for(i in callbacks.indices) { + append("KCallbackDef(") + + // name + append(names[i]) + append(" ".repeat(width1 - names[i].length)) + + // type + append(" ") + append(types[i]) + append(" ".repeat(width2 - types[i].length )) + + // args + if(args.isNotEmpty()) { + append(" ") + append(args[i]) + append(" ".repeat(width3 - args[i].length)) + } + 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){ + builder.append(""" + /* + * 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. + */ + + + """.trimIndent()) + + if(guardName != null) { + builder.append("#ifndef $defName\n") + builder.append("#define $defName\n") + } + + builder.append(""" + + #include + #include + #include + + #ifdef __cplusplus + 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){ + builder.append(""" + + + #ifdef __cplusplus + } + #endif + """.trimIndent()) + + if(guardName != null) + builder.append("\n\n#endif // $defName") + } + + private fun printStdLib(builder: StringBuilder) = builder.apply { + append(""" + + 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; + size_t size; + KInt length; + char __flags; + } KString; + + 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__) + #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 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..aa0981d --- /dev/null +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiImplPrinter.kt @@ -0,0 +1,304 @@ +package com.huskerdev.nativekt.printers.c + +import com.huskerdev.nativekt.utils.allFields +import com.huskerdev.nativekt.utils.firstParam +import com.huskerdev.nativekt.utils.isArray +import com.huskerdev.nativekt.utils.isCallback +import com.huskerdev.nativekt.utils.isDictionary +import com.huskerdev.nativekt.utils.isPrimitive +import com.huskerdev.nativekt.utils.isString +import com.huskerdev.nativekt.utils.printLabel +import com.huskerdev.nativekt.utils.simpleName +import com.huskerdev.nativekt.utils.toCDefType +import com.huskerdev.webidl.resolver.BuiltinIdlDeclaration +import com.huskerdev.webidl.resolver.IdlResolver +import com.huskerdev.webidl.resolver.ResolvedIdlDictionary +import com.huskerdev.webidl.resolver.ResolvedIdlEnum +import com.huskerdev.webidl.resolver.ResolvedIdlType +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") + + val classPathPrefix = classPath.replace(".", "_") + + 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/CHeaderPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CHeaderPrinter.kt deleted file mode 100644 index c4afe9b..0000000 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CHeaderPrinter.kt +++ /dev/null @@ -1,525 +0,0 @@ -package com.huskerdev.nativekt.printers.c - -import com.huskerdev.nativekt.utils.allFields -import com.huskerdev.nativekt.utils.firstParam -import com.huskerdev.nativekt.utils.globalOperators -import com.huskerdev.nativekt.utils.isArray -import com.huskerdev.nativekt.utils.isCallback -import com.huskerdev.nativekt.utils.isDictionary -import com.huskerdev.nativekt.utils.isPrimitive -import com.huskerdev.nativekt.utils.isString -import com.huskerdev.nativekt.utils.printLabel -import com.huskerdev.nativekt.utils.simpleName -import com.huskerdev.nativekt.utils.toCDefType -import com.huskerdev.webidl.resolver.BuiltinIdlDeclaration -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.webidl.resolver.ResolvedIdlType -import java.io.File -import kotlin.math.max - -class CHeaderPrinter( - idl: IdlResolver, - target: File, - val guardName: String? = null -) { - private val defName = "KOTLIN_NATIVE_${guardName}_H" - - init { - target.parentFile.mkdirs() - - val builder = StringBuilder() - printHeader(builder) - - printLabel(builder, "stdlib") - printStdLib(builder) - - if(idl.callbacks.isNotEmpty()) { - printLabel(builder, "Type defs") - idl.dictionaries.values.forEach { printStructTypedef(builder, it) } - idl.callbacks.values.forEach { printCallbackTypedef(builder, it) } - builder.append("\n") - } - - if(idl.enums.isNotEmpty()) { - builder.append("\n") - printLabel(builder, "Enums") - idl.enums.values.forEach { printEnum(builder, it) } - } - - if(idl.dictionaries.isNotEmpty()) { - builder.append("\n") - printLabel(builder, "Structs") - idl.dictionaries.values.forEach { printStruct(builder, it) } - idl.dictionaries.values.forEach { printStructNew(builder, it) } - } - - printLabel(builder, "Functions") - idl.globalOperators().forEach { printFunction(builder, it) } - - if(idl.callbacks.isNotEmpty()) { - builder.append("\n") - printLabel(builder, "Callbacks") - printCallbacks(builder, idl.callbacks.values) - } - - if(idl.dictionaries.isNotEmpty()) { - printLabel(builder, "Struct functions") - - idl.dictionaries.values.forEach { - builder.append("\nstatic ${it.name}* ${it.name}_clone(const ${it.name}* of);") - } - idl.dictionaries.values.forEach { - builder.append("\nstatic void ${it.name}_free(${it.name}* of);") - } - builder.append("\n") - idl.dictionaries.values.forEach { - printStructClone(builder, it) - printStructFree(builder, it) - } - } - - printFooter(builder) - - target.writeText(builder.toString().replace("\n", System.lineSeparator())) - } - - private fun printFunction(builder: StringBuilder, function: ResolvedIdlOperation) = builder.apply { - append("\n") - append(function.type.toCDefType()) - append(" ") - append(function.name) - append("(") - - function.args.joinTo(builder) { - "${it.type.toCDefType()} ${it.name}" - } - - append(");") - } - - private fun printEnum(builder: StringBuilder, enum: ResolvedIdlEnum) = builder.apply { - append("\ntypedef enum {\n\t") - enum.elements.joinTo(builder, separator = ",\n\t") { - "${enum.name}_${it}" - } - append("\n} ") - append(enum.name) - append(";\n") - } - - private fun printStruct(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - append("\nstruct ") - append(dictionary.name) - append(" {") - if(dictionary.implements != null) - append(" // : ").append(dictionary.implements!!.name) - append("\n\t") - - buildList { - add("char __flags;") - dictionary.allFields().mapTo(this) { field -> - "${field.type.toCDefType()} ${field.name};" - } - }.joinTo(builder, separator = "\n\t") - - append("\n};\n") - } - - private fun printStructNew(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - append("\nstatic ${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 { - add("K_FLAG_RELEASABLE") - dictionary.allFields().mapTo(this) { it.name } - }.joinTo(builder) - append(" };\n\t") - - // return - append("return result;\n}\n") - } - - private fun printStructClone(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - append("\nstatic ${dictionary.name}* ${dictionary.name}_clone(const ${dictionary.name}* of) {\n\t") - - // malloc - append("${dictionary.name}* result = (${dictionary.name}*) malloc(sizeof(${dictionary.name}));\n\t") - - // fill - append("*result = (${dictionary.name}) {\n\t\t") - buildList { - add("K_FLAG_RELEASABLE") - dictionary.allFields().mapTo(this) { field -> - when { - field.type.isString() -> "KString_clone(of->${field.name})" - field.type.isArray() -> { - (field.type as ResolvedIdlType.Default).firstParam { _, declaration -> - when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - "K${name}Array_clone(of->${field.name})" - } - is ResolvedIdlEnum -> "KIntArray_clone(of->${field.name})" - is ResolvedIdlDictionary -> "KArray_clone(of->${field.name}, (void*) ${declaration.name}_clone)" - else -> throw UnsupportedOperationException(field.type.toString()) - } - } - } - field.type.isCallback() -> "of->${field.name}->clone(of->${field.name})" - field.type.isDictionary() -> "${(field.type as ResolvedIdlType.Default).declaration.name}_clone(of->${field.name})" - else -> "of->${field.name}" - } - } - }.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 { - append("\nstatic void ${dictionary.name}_free(${dictionary.name}* of) {\n") - append(""" - if(!K_OBJECT_IS_RELEASABLE(of->__flags)) - return; - """.replaceIndent("\t")) - - dictionary.allFields().forEach { field -> - freeFuncFor( - field.type, - "of->${field.name}" - )?.apply { append("\n\t$this;") } - } - append(""" - - if(!K_OBJECT_IS_ON_STACK(of->__flags)) - free((void*) of); - """.replaceIndent("\t")) - - append("\n}\n") - } - - private fun printStructTypedef(builder: StringBuilder, dictionary: ResolvedIdlDictionary) = builder.apply { - append("\ntypedef struct ") - append(dictionary.name) - append(" ") - append(dictionary.name) - append(";") - } - - private fun printCallbackTypedef(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - append("\ntypedef struct ") - append(callback.name) - append(" ") - append(callback.name) - append(";") - } - - private fun printCallbacks(builder: StringBuilder, callbacks: Collection) = builder.apply { - val column1 = "Name" - val column2 = "Type" - val column3 = "Args" - - val names = arrayListOf() - val types = arrayListOf() - val args = arrayListOf() - - callbacks.forEach { callback -> - names += callback.name + "," - types += callback.type.toCDefType() + if(callback.args.isNotEmpty()) "," else "" - args += callback.args.joinToString { "${it.type.toCDefType()} ${it.name}" } - } - - val width1 = max(column1.length, names.maxOf { it.length }) - val width2 = max(column2.length, types.maxOf { it.length }) - val width3 = max(column3.length, args.maxOf { it.length }) - - // ┌───────┬─────────────────────┬────────────────┬─────────────────────────┐ - // │ │ Name │ Type │ Args │ - // └───────┴─────────────────────┴────────────────┴─────────────────────────┘ - // table - append("// ┌───────┬") - append("─".repeat(width1)).append("┬") - append("─".repeat(width2)).append("┬") - append("─".repeat(width3+1)).append("┐\n") - append("// │ ... │ ") - append(column1).append(" ".repeat(width1 - column1.length - 1)).append("│ ") - append(column2).append(" ".repeat(width2 - column2.length - 1)).append("│ ") - append(column3).append(" ".repeat(width3 - column3.length)).append("│\n") - append("// └───────┴") - append("─".repeat(width1)).append("┴") - append("─".repeat(width2)).append("┴") - append("─".repeat(width3+1)).append("┘\n") - - for(i in callbacks.indices) { - append("KCallbackDef(") - - // name - append(names[i]) - append(" ".repeat(width1 - names[i].length)) - - // type - append(" ") - append(types[i]) - append(" ".repeat(width2 - types[i].length )) - - // args - if(args.isNotEmpty()) { - append(" ") - append(args[i]) - append(" ".repeat(width3 - args[i].length)) - } - append(")\n") - } - append("#undef KCallbackDef\n") - } - - private fun printHeader(builder: StringBuilder){ - builder.append(""" - /* - * 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. - */ - - - """.trimIndent()) - - if(guardName != null) { - builder.append("#ifndef $defName\n") - builder.append("#define $defName\n") - } - - builder.append(""" - - #include - #include - - #include - #include - #include - - #ifdef __cplusplus - extern "C" { - #endif - - """.trimIndent()) - } - - private fun printFooter(builder: StringBuilder){ - builder.append(""" - - - #ifdef __cplusplus - } - #endif - """.trimIndent()) - - if(guardName != null) - builder.append("\n\n#endif // $defName") - } - - private fun printStdLib(builder: StringBuilder){ - 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) - - 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 { - char __flags; - const char* data; - KInt length; - size_t size; - } KString; - - static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; - } - - static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; - } - - static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); - } - - #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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ - } Name; \ - \ - static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ - } \ - \ - 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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ - } - - #define KArrayCloneDef(Name, Type) \ - static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ - } \ - \ - static 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); \ - } - - 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__) - - KArrayCloneDef(KCharArray, KChar) - KArrayCloneDef(KBooleanArray, KBoolean) - KArrayCloneDef(KByteArray, KByte) - KArrayCloneDef(KShortArray, KShort) - KArrayCloneDef(KIntArray, KInt) - KArrayCloneDef(KLongArray, KLong) - KArrayCloneDef(KFloatArray, KFloat) - KArrayCloneDef(KDoubleArray, KDouble) - - static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; - } - - static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); - } - - #define KCallbackDef(Name, Type, ...) \ - struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - void (*free)(Name* _); \ - }; - - """.trimIndent()) - } -} \ 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 a1aa288..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 @@ -84,9 +84,9 @@ class CJniPrinter( buildList { add("JNIEnv *env") - add("jclass __cls") + add("jclass cls") addAll(function.args.map { - "${it.type.toJNIType()} __arg_jvm_${it.name}" + "${it.type.toJNIType()} __jvm_${it.name}" }) }.joinTo(builder, prefix = "(", postfix = ") {") @@ -102,9 +102,9 @@ class CJniPrinter( // write args argsToCast.joinTo(this, separator = "") { - "\n\t${it.type.toCDefType().padEnd(typeMaxLength)} __arg_native_${it.name} = ${castJavaToJNI( + "\n\t${it.type.toCDefType().padEnd(typeMaxLength)} __native_${it.name} = ${castJavaToJNI( it.type, - "__arg_jvm_${it.name}", + "__jvm_${it.name}", onStack = true, flags = "0" )};" @@ -116,18 +116,18 @@ class CJniPrinter( val args = function.args.joinToString { if(it.type.isPrimitive()) - "__arg_jvm_${it.name}" - else "__arg_native_${it.name}" + "__jvm_${it.name}" + else "__native_${it.name}" } val call = "${function.name}($args)" 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")};") + 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)};") + 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;") @@ -139,7 +139,7 @@ class CJniPrinter( if(function.isDealloc()) { freeFuncFor( function.type, - "__result_native" + "result_native" )?.apply { append("\n\t$this;") } } @@ -149,23 +149,16 @@ class CJniPrinter( function.args.forEach { arg -> when { - arg.type.isString() -> "JNI_releaseStringOnStack(env, __arg_native_${arg.name})" - arg.type.isArray() -> { - (arg.type as ResolvedIdlType.Default).firstParam { _, declaration -> - when (declaration) { - is BuiltinIdlDeclaration -> { - val name = declaration.kind.simpleName() - "JNI_release${name}ArrayOnStack(env, __arg_native_${arg.name})" - } - is ResolvedIdlEnum -> "free((void*)__arg_native_${arg.name}->elements)" - is ResolvedIdlDictionary -> { - "JNI_forceFreeKArray(__arg_native_${arg.name}, (void*) JNI_forceFree${declaration.name})" - } - else -> throw UnsupportedOperationException(arg.type.toString()) - } + 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() -> "JNI_forceFree${(arg.type as ResolvedIdlType.Default).declaration.name}(__arg_native_${arg.name})" + arg.type.isDictionary() -> forceFreeFuncFor(arg.type, "__native_${arg.name}") else -> return@forEach }.apply { append("\n\t$this;") } } @@ -175,79 +168,23 @@ class CJniPrinter( // ================== if(returns && needReleases) - append("\n\treturn __result_jvm;") + append("\n\treturn result_jvm;") append("\n}\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}" - - if(it.type.isArray() || it.type.isString()) - listOf(arg, "jint __length_${it.name}") - else listOf(arg) - - }.joinTo(builder, prefix = "(", postfix = ")") - - append(" {\n\t") - - if(function.type !is ResolvedIdlType.Void) - append("return ") - - // == Function call == - val args = function.args.joinToString { - castToKTypeFromCritical(it.type, it.name) - } - append("${function.name}($args);\n}\n") + printCriticalNativeFunctionContent( + builder, + name = "${function.jniName()}_", + function + ) } private fun ResolvedIdlOperation.jniName() = "Java_${classPath.replace(".", "_")}_${this@CJniPrinter.name}_$name" } -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() -> "$content->free($content)" - type.isDictionary() -> "${(type as ResolvedIdlType.Default).declaration.name}_free(${content})" - else -> null -} - -internal fun forceFreeFuncFor( - type: ResolvedIdlType, - content: String -) = when { - type.isString() -> "JNI_forceFreeKString($content)" - type.isArray() -> (type as ResolvedIdlType.Default).firstParam { _, declaration -> - when (declaration) { - is BuiltinIdlDeclaration -> "JNI_forceFreeK${declaration.kind.simpleName()}Array($content)" - is ResolvedIdlEnum -> "JNI_forceFreeKIntArray($content)" - is ResolvedIdlDictionary -> "JNI_forceFreeKArray($content, (void*) JNI_forceFree${declaration.name})" - else -> throw UnsupportedOperationException(type.toString()) - } - } - type.isCallback() || type.isDictionary() -> - "JNI_forceFree${(type as ResolvedIdlType.Default).declaration.name}($content)" - else -> null -} - - internal fun castJniToJava(type: ResolvedIdlType, content: String): String { return when(type) { is ResolvedIdlType.Void -> content @@ -266,7 +203,7 @@ internal fun castJniToJava(type: ResolvedIdlType, content: String): String { } else -> content } - is ResolvedIdlCallbackFunction -> "JNI_toKotlinCallback(env, (JNI_Callback*)$content)" + 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)" else -> throw UnsupportedOperationException(type.toString()) @@ -307,7 +244,7 @@ internal fun castJavaToJNI( else -> content } is ResolvedIdlCallbackFunction -> { - if (onStack) "(${decl.name}*) JNI_toNativeCallbackOnStack(env, $content, (void(*)())JNI_CALLBACK_INVOKE_${decl.name}, alloca(JNI_CallbackSize))" + 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)" 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 414a71e..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 @@ -41,11 +41,11 @@ class CJniUtilsPrinter( jstring stringUTF8Const; typedef struct KString KString; - const size_t JNI_StringStackSize = sizeof(KString) + 2; + 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)((size_t*)((char*)str + sizeof(KString)))[0]; + return (jstring)((intptr_t*)((char*)str + sizeof(KString)))[0]; int32_t size = str->size; @@ -63,10 +63,10 @@ class CJniUtilsPrinter( ((size_t*)((char*)mem + sizeof(KString)))[1] = (size_t) bytes; KString* result = (KString*)mem; *result = (KString) { - K_FLAG_ON_STACK, (const char*)(*env)->GetByteArrayElements(env, bytes, JNI_FALSE), + (*env)->GetArrayLength(env, bytes), (*env)->GetStringLength(env, obj), - (*env)->GetArrayLength(env, bytes) + K_FLAG_ON_STACK }; return result; } @@ -90,19 +90,14 @@ class CJniUtilsPrinter( KString* result = (KString*) malloc(sizeof(KString)); *result = (KString) { - flags, (const char*) strCopy, + size, length, - size + flags }; return result; } - void JNI_forceFreeKString(KString* str) { - str->__flags |= K_FLAG_RELEASABLE; - KString_free(str); - } - """.trimIndent()) printLabel(builder, "Primitive Arrays") @@ -122,10 +117,10 @@ class CJniUtilsPrinter( \ K##Name##Array* result = (K##Name##Array*) malloc(sizeof(K##Name##Array)); \ *result = (K##Name##Array) { \ - flags, \ (K##Name*)elementsCopy, \ + size, \ length, \ - size \ + flags \ }; \ return result; \ } \ @@ -135,10 +130,10 @@ class CJniUtilsPrinter( KInt length = (*env)->GetArrayLength(env, arr); \ K##Name##Array* result = (K##Name##Array*)mem; \ *result = (K##Name##Array) { \ - K_FLAG_ON_STACK, \ (K##Name*)(*env)->Get##Name##ArrayElements(env, arr, JNI_FALSE), \ + length * sizeof(JType), \ length, \ - length * sizeof(JType) \ + K_FLAG_ON_STACK \ }; \ return result; \ } \ @@ -154,11 +149,6 @@ class CJniUtilsPrinter( JType##Array result = (*env)->New##Name##Array(env, arr->length); \ (*env)->Set##Name##ArrayRegion(env, result, 0, arr->length, (JType*)arr->elements); \ return result; \ - } \ - \ - void JNI_forceFreeK##Name##Array(K##Name##Array* arr) { \ - arr->__flags |= K_FLAG_RELEASABLE; \ - K##Name##Array_free(arr); \ } KArrayCast(Char, jchar) @@ -193,10 +183,10 @@ class CJniUtilsPrinter( KArray* result = (KArray*) malloc(sizeof(KArray)); *result = (KArray) { - flags, (const void**) elements, + length * sizeof(void*), length, - length * sizeof(void*) + flags, }; return result; } @@ -219,10 +209,10 @@ class CJniUtilsPrinter( KArray* result = (KArray*)mem; *result = (KArray) { - K_FLAG_ON_STACK, (const void**) elements, + length * sizeof(void*), length, - length * sizeof(void*) + K_FLAG_ON_STACK }; return result; } @@ -243,11 +233,6 @@ class CJniUtilsPrinter( return result; } - void JNI_forceFreeKArray(KArray* arr, void* (*freeOp)(void*)) { - arr->__flags |= K_FLAG_RELEASABLE; - KArray_free(arr, freeOp); - } - """.trimIndent()) if(idl.enums.isNotEmpty()) { @@ -261,6 +246,7 @@ class CJniUtilsPrinter( "enum${it.name}Values" } builder.append(""" + KInt JNI_toNativeEnum(JNIEnv* env, jobject of) { return (*env)->CallIntMethod(env, of, enumOrdinal); @@ -293,10 +279,10 @@ class CJniUtilsPrinter( KIntArray* result = (KIntArray*) malloc(sizeof(KIntArray)); *result = (KIntArray) { - flags, (const KInt*) elements, + length * sizeof(KInt), length, - length * sizeof(KInt) + flags }; return result; } @@ -318,10 +304,10 @@ class CJniUtilsPrinter( KIntArray* result = (KIntArray*)mem; *result = (KIntArray) { - K_FLAG_ON_STACK, (const KInt*) elements, + length * sizeof(KInt), length, - length * sizeof(KInt) + K_FLAG_ON_STACK }; return result; } @@ -349,21 +335,12 @@ class CJniUtilsPrinter( printLabel(builder, "Callback casts") builder.append(""" - typedef struct JNI_Callback { - char __flags; - void (*invoke)(); - struct JNI_Callback* (*clone)(struct JNI_Callback*); - KBoolean (*equals)(struct JNI_Callback*, struct JNI_Callback* obj); - KInt (*hashCode)(struct JNI_Callback*); - void (*free)(struct JNI_Callback*); - } JNI_Callback; - - const size_t JNI_CallbackSize = sizeof(JNI_Callback) + sizeof(size_t)*2; + 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, (void**)env, NULL); + (*jvm)->AttachCurrentThread(jvm, ${if(!isAndroid) "(void**) " else "" }env, NULL); return status; } @@ -372,10 +349,10 @@ class CJniUtilsPrinter( (*jvm)->DetachCurrentThread(jvm); } - static inline KBoolean JNI_CALLBACK_equals(JNI_Callback* ref, JNI_Callback* with) { - jobject obj = (jobject)((size_t*)((char*)ref + sizeof(JNI_Callback)))[0]; - jobject obj2 = (jobject)((size_t*)((char*)with + sizeof(JNI_Callback)))[0]; - JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(JNI_Callback)))[1]; + 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); @@ -383,9 +360,9 @@ class CJniUtilsPrinter( return result; } - static inline KInt JNI_CALLBACK_hashCode(JNI_Callback* ref) { - jobject obj = (jobject)((size_t*)((char*)ref + sizeof(JNI_Callback)))[0]; - JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(JNI_Callback)))[1]; + 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); @@ -393,45 +370,45 @@ class CJniUtilsPrinter( return result; } - static void JNI_CALLBACK_free(JNI_Callback* callback) { + 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); - jobject obj = (jobject)((size_t*)((char*)callback + sizeof(JNI_Callback)))[0]; + jobject obj = (jobject)((size_t*)((char*)callback + sizeof(_AbstractCallback)))[0]; (*env)->DeleteGlobalRef(env, obj); free((void*)callback); JVM_detach(status); } - static JNI_Callback* JNI_CALLBACK_clone(JNI_Callback* ref) { - jobject obj = (jobject)((size_t*)((char*)ref + sizeof(JNI_Callback)))[0]; - JNIEnv *env = (JNIEnv*)((size_t*)((char*)ref + sizeof(JNI_Callback)))[1]; + 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; - JNI_Callback* callback = (JNI_Callback*) malloc(JNI_CallbackSize); - memcpy(callback, ref, sizeof(JNI_Callback)); + _AbstractCallback* callback = (_AbstractCallback*) malloc(_AbstractCallbackSize); + memcpy(callback, ref, sizeof(_AbstractCallback)); callback->__flags = K_FLAG_RELEASABLE; - ((size_t*)((char*)callback + sizeof(JNI_Callback)))[0] = (size_t)(*env)->NewGlobalRef(env, obj); - ((size_t*)((char*)callback + sizeof(JNI_Callback)))[1] = (size_t) NULL; + ((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, JNI_Callback* callback) { - return (jobject)((size_t*)((char*)callback + sizeof(JNI_Callback)))[0]; + jobject JNI_toKotlinCallback(JNIEnv *env, _AbstractCallback* callback) { + return (jobject)((size_t*)((char*)callback + sizeof(_AbstractCallback)))[0]; } - JNI_Callback* JNI_toNativeCallbackOnStack(JNIEnv *env, jobject obj, void (*invoke)(), void* mem) { - ((size_t*)((char*)mem + sizeof(JNI_Callback)))[0] = (size_t) obj; - ((size_t*)((char*)mem + sizeof(JNI_Callback)))[1] = (size_t) env; - JNI_Callback* callback = (JNI_Callback*) mem; - *callback = (JNI_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, @@ -442,12 +419,12 @@ class CJniUtilsPrinter( return callback; } - JNI_Callback* JNI_toNativeCallback(JNIEnv *env, jobject obj, void (*invoke)(), char flags) { - void* mem = malloc(JNI_CallbackSize); - ((size_t*)((char*)mem + sizeof(JNI_Callback)))[0] = (size_t) (*env)->NewGlobalRef(env, obj); - ((size_t*)((char*)mem + sizeof(JNI_Callback)))[1] = (size_t) NULL; - JNI_Callback* callback = (JNI_Callback*) mem; - *callback = (JNI_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, @@ -463,9 +440,6 @@ class CJniUtilsPrinter( idl.callbacks.values.forEach { printCallbackInvokeDef(builder, it) } - idl.callbacks.values.forEach { - printCallbackFreeDef(builder, it) - } } if(idl.dictionaries.isNotEmpty()) { @@ -483,7 +457,6 @@ class CJniUtilsPrinter( idl.callbacks.values.forEach { callback -> printCallbackInvoke(builder, callback) - printCallbackForceFree(builder, callback) } } @@ -538,24 +511,15 @@ class CJniUtilsPrinter( append("*result = (${struct.name}) {\n\t\t") buildList { - add("flags") 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") - - // Force free - append("\nvoid JNI_forceFree${struct.name}(${struct.name}* of) {") - struct.allFields().forEach { field -> - forceFreeFuncFor( - field.type, "of->${field.name}" - )?.apply { append("\n\t$this;") } - } - append("\n\tfree((void*) of);\n}\n") } } @@ -566,10 +530,6 @@ class CJniUtilsPrinter( append("${callback.type.toCDefType()} JNI_CALLBACK_INVOKE_${callback.name}(${args.joinToString()});\n") } - private fun printCallbackFreeDef(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - append("void JNI_forceFree${callback.name}(${callback.name}* of);\n") - } - private fun printCallbackInvoke(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { val args = buildList { add("${callback.name}* callback") @@ -588,8 +548,8 @@ class CJniUtilsPrinter( append(""" ${callback.type.toCDefType()} JNI_CALLBACK_INVOKE_${callback.name}(${args}) { - jobject obj = (jobject)((size_t*)((char*)callback + sizeof(JNI_Callback)))[0]; - JNIEnv *env = (JNIEnv*)((size_t*)((char*)callback + sizeof(JNI_Callback)))[1]; + 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()) @@ -610,17 +570,6 @@ class CJniUtilsPrinter( append("}\n") } - private fun printCallbackForceFree(builder: StringBuilder, callback: ResolvedIdlCallbackFunction) = builder.apply { - append(""" - - void JNI_forceFree${callback.name}(${callback.name}* of) { - of->__flags |= K_FLAG_RELEASABLE; - of->free(of); - } - """.trimIndent()) - append("\n") - } - private fun printRegisterFunction(builder: StringBuilder) = builder.apply { printLabel(builder, "Init function") append(""" 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 index e94d179..31ff544 100644 --- 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 @@ -1,13 +1,17 @@ package com.huskerdev.nativekt.utils +import com.huskerdev.webidl.resolver.BuiltinIdlDeclaration import com.huskerdev.webidl.resolver.ResolvedIdlDictionary import com.huskerdev.webidl.resolver.ResolvedIdlType +import com.huskerdev.webidl.resolver.WebIDLBuiltinKind import kotlin.math.max class CStructLayout( types: List, x86: Boolean ) { + var count: Int = types.size + var size: Int = 0 private set @@ -20,20 +24,25 @@ class CStructLayout( constructor( dictionary: ResolvedIdlDictionary, x86: Boolean - ): this(dictionary.allFields().map { it.type }, x86) + ): this( + types = dictionary.allFields() + .map { it.type } + .toMutableList() + .apply { + add(ResolvedIdlType.Default(BuiltinIdlDeclaration("int", WebIDLBuiltinKind.INT), emptyList(), false)) + }, + 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 typeSize = type.getAlignment(x86) - val rem = size % alignment - val padding = if (rem == 0) 0 else alignment - rem + val rem = size % typeSize + val padding = if (rem == 0) 0 else typeSize - rem - maxAlignment = max(maxAlignment, alignment) + maxAlignment = max(maxAlignment, typeSize) this.padding += padding this.address += size + padding 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 e4bc20e..961d580 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()) @@ -311,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 @@ -350,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" @@ -436,11 +469,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/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..e4d24e7 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 @@ -4,31 +4,387 @@ 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(bytes.size.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 b614a28..0000000 --- a/modules/test-js-only/natives/jsOnlyTest/include/api.h +++ /dev/null @@ -1,218 +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 -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// ╔════════════════╗ -// ║ stdlib ║ -// ╚════════════════╝ - -#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) - -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 { - char __flags; - const char* data; - KInt length; - size_t size; -} KString; - -static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; -} - -static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; -} - -static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); -} - -#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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ -} Name; \ - \ -static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ -} \ - \ -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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ -} - -#define KArrayCloneDef(Name, Type) \ -static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ -} \ - \ -static 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); \ -} - -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__) - -KArrayCloneDef(KCharArray, KChar) -KArrayCloneDef(KBooleanArray, KBoolean) -KArrayCloneDef(KByteArray, KByte) -KArrayCloneDef(KShortArray, KShort) -KArrayCloneDef(KIntArray, KInt) -KArrayCloneDef(KLongArray, KLong) -KArrayCloneDef(KFloatArray, KFloat) -KArrayCloneDef(KDoubleArray, KDouble) - -static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; -} - -static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); -} - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - 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 70eb4b9..e9b8c64 100644 --- a/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h +++ b/modules/test-jvm-only/natives/jvmOnlyTest/include/api.h @@ -10,26 +10,29 @@ #define KOTLIN_NATIVE_JVMONLYTEST_H #include -#include - #include #include -#include #ifdef __cplusplus 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 ║ // ╚════════════════╝ -#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) - typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -40,115 +43,36 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { - char __flags; const char* data; - KInt length; size_t size; + KInt length; + char __flags; } KString; -static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; -} - -static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; -} - -static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); -} - -#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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ -} Name; \ - \ -static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ -} \ - \ -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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ -} - -#define KArrayCloneDef(Name, Type) \ -static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ -} \ - \ -static 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); \ -} - -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__) @@ -160,49 +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__) -KArrayCloneDef(KCharArray, KChar) -KArrayCloneDef(KBooleanArray, KBoolean) -KArrayCloneDef(KByteArray, KByte) -KArrayCloneDef(KShortArray, KShort) -KArrayCloneDef(KIntArray, KInt) -KArrayCloneDef(KLongArray, KLong) -KArrayCloneDef(KFloatArray, KFloat) -KArrayCloneDef(KDoubleArray, KDouble) - -static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; -} - -static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); -} - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - 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 5ea5768..cd8de7c 100644 --- a/modules/tests/build.gradle.kts +++ b/modules/tests/build.gradle.kts @@ -88,9 +88,6 @@ natives { useJsBigInt = true useJvmRecord = false - useJVMCI = false - useForeignApi = false - if(project.hasProperty("disableForeign")) { println("Disable: Foreign") useForeignApi = false diff --git a/modules/tests/natives/test/include/api.h b/modules/tests/natives/test/include/api.h index 727eaee..a7b3a2d 100644 --- a/modules/tests/natives/test/include/api.h +++ b/modules/tests/natives/test/include/api.h @@ -10,26 +10,29 @@ #define KOTLIN_NATIVE_TEST_H #include -#include - #include #include -#include #ifdef __cplusplus 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 ║ // ╚════════════════╝ -#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) - typedef int32_t KInt; typedef int64_t KLong; typedef float KFloat; @@ -40,115 +43,36 @@ typedef bool KBoolean; typedef uint16_t KChar; typedef struct KString { - char __flags; const char* data; - KInt length; size_t size; + KInt length; + char __flags; } KString; -static KString* KString_new(const char* data, const KInt length, const KInt size) { - KString* result = (KString*) malloc(sizeof(KString)); - *result = (KString) { K_FLAG_RELEASABLE, data, length, size }; - return result; -} - -static 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) { K_FLAG_RELEASABLE, (const char*) data, of->length, size }; - return result; -} - -static void KString_free(KString* str) { - if(!K_OBJECT_IS_RELEASABLE(str->__flags)) - return; - free((void*) str->data); - if(!K_OBJECT_IS_ON_STACK(str->__flags)) - free((void*) str); -} - -#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 { \ - char __flags; \ - const Type* elements; \ - KInt length; \ - size_t size; \ -} Name; \ - \ -static Name* Name##_new(const Type* elements, const KInt length) { \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - elements, \ - length, \ - length * sizeof(Name) \ - }; \ - return result; \ -} \ - \ -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); \ - Name* result = (Name*) malloc(sizeof(Name)); \ - *result = (Name){ \ - K_FLAG_RELEASABLE, \ - (const Type*) elements, \ - n, \ - n * sizeof(Name) \ - }; \ - return result; \ -} - -#define KArrayCloneDef(Name, Type) \ -static 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) { \ - K_FLAG_RELEASABLE, \ - (Type*) elements, \ - of->length, \ - of->size \ - }; \ - return result; \ -} \ - \ -static 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); \ -} - -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__) @@ -160,49 +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__) -KArrayCloneDef(KCharArray, KChar) -KArrayCloneDef(KBooleanArray, KBoolean) -KArrayCloneDef(KByteArray, KByte) -KArrayCloneDef(KShortArray, KShort) -KArrayCloneDef(KIntArray, KInt) -KArrayCloneDef(KLongArray, KLong) -KArrayCloneDef(KFloatArray, KFloat) -KArrayCloneDef(KDoubleArray, KDouble) - -static 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) { - K_FLAG_RELEASABLE, - (const void**) elements, - of->length, - of->size - }; - return result; -} - -static void KArray_free(const KArray* arr, void* (*freeOp)(void*)) { - if(!K_OBJECT_IS_RELEASABLE(arr->__flags)) - return; - const void** elements = arr->elements; - for (int i = 0; i < arr->length; i++) - freeOp((void*) elements[i]); - free((void*) elements); - if(!K_OBJECT_IS_ON_STACK(arr->__flags)) - free((void*) arr); -} - -#define KCallbackDef(Name, Type, ...) \ -struct Name { \ - char __flags; \ - Type (*invoke)(Name* _, ##__VA_ARGS__); \ - Name* (*clone)(Name* _); \ - KBoolean (*equals)(Name* _, Name* obj); \ - KInt (*hashCode)(Name* _); \ - 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); \ }; // ╔═══════════════════╗ @@ -274,21 +180,28 @@ typedef enum { // ╚═════════════════╝ struct ParentDictionary { - char __flags; 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 - char __flags; 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 { - char __flags; KChar a1; KBoolean a2; KByte a3; @@ -311,25 +224,12 @@ struct TypeDictionary { 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) { K_FLAG_RELEASABLE, 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) { K_FLAG_RELEASABLE, 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, 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* result = (TypeDictionary*) malloc(sizeof(TypeDictionary)); - *result = (TypeDictionary) { K_FLAG_RELEASABLE, 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 ║ @@ -534,103 +434,6 @@ KCallbackDef(CallbackReturnEnumArray, KIntArray* ) KCallbackDef(CallbackReturnDictionaryArray, KArray* ) #undef KCallbackDef -// ╔══════════════════════════╗ -// ║ Struct functions ║ -// ╚══════════════════════════╝ - -static ParentDictionary* ParentDictionary_clone(const ParentDictionary* of); -static MyDictionary* MyDictionary_clone(const MyDictionary* of); -static TypeDictionary* TypeDictionary_clone(const TypeDictionary* of); -static void ParentDictionary_free(ParentDictionary* of); -static void MyDictionary_free(MyDictionary* of); -static void TypeDictionary_free(TypeDictionary* of); - -static ParentDictionary* ParentDictionary_clone(const ParentDictionary* of) { - ParentDictionary* result = (ParentDictionary*) malloc(sizeof(ParentDictionary)); - *result = (ParentDictionary) { - K_FLAG_RELEASABLE, - of->a, - of->b - }; - return result; -} - -static void ParentDictionary_free(ParentDictionary* of) { - if(!K_OBJECT_IS_RELEASABLE(of->__flags)) - return; - if(!K_OBJECT_IS_ON_STACK(of->__flags)) - free((void*) of); -} - -static MyDictionary* MyDictionary_clone(const MyDictionary* of) { - MyDictionary* result = (MyDictionary*) malloc(sizeof(MyDictionary)); - *result = (MyDictionary) { - K_FLAG_RELEASABLE, - of->a, - of->b, - of->c, - of->d - }; - return result; -} - -static void MyDictionary_free(MyDictionary* of) { - if(!K_OBJECT_IS_RELEASABLE(of->__flags)) - return; - if(!K_OBJECT_IS_ON_STACK(of->__flags)) - free((void*) of); -} - -static TypeDictionary* TypeDictionary_clone(const TypeDictionary* of) { - TypeDictionary* result = (TypeDictionary*) malloc(sizeof(TypeDictionary)); - *result = (TypeDictionary) { - K_FLAG_RELEASABLE, - of->a1, - of->a2, - of->a3, - of->a4, - of->a5, - of->a6, - of->a7, - of->a8, - KString_clone(of->a9), - of->a10, - MyDictionary_clone(of->a11), - of->a12->clone(of->a12), - KCharArray_clone(of->a13), - KBooleanArray_clone(of->a14), - KByteArray_clone(of->a15), - KShortArray_clone(of->a16), - KIntArray_clone(of->a17), - KLongArray_clone(of->a18), - KFloatArray_clone(of->a19), - KDoubleArray_clone(of->a20), - KIntArray_clone(of->a21), - KArray_clone(of->a22, (void*) MyDictionary_clone) - }; - return result; -} - -static void TypeDictionary_free(TypeDictionary* of) { - if(!K_OBJECT_IS_RELEASABLE(of->__flags)) - return; - KString_free(of->a9); - MyDictionary_free(of->a11); - of->a12->free(of->a12); - KCharArray_free(of->a13); - KBooleanArray_free(of->a14); - KByteArray_free(of->a15); - KShortArray_free(of->a16); - KIntArray_free(of->a17); - KLongArray_free(of->a18); - KFloatArray_free(of->a19); - KDoubleArray_free(of->a20); - KIntArray_free(of->a21); - KArray_free(of->a22, (void*) MyDictionary_free); - if(!K_OBJECT_IS_ON_STACK(of->__flags)) - free((void*) of); -} - #ifdef __cplusplus } 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 b707123..19bd772 100644 --- a/modules/tests/natives/test/src/arrays/callback_return_array.c +++ b/modules/tests/natives/test/src/arrays/callback_return_array.c @@ -2,7 +2,7 @@ KBoolean callbackReturnCharArray(CallbackReturnCharArray* arg) { const KCharArray* array = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && array->elements[0] == 'a' && array->elements[1] == 'b'; @@ -10,7 +10,7 @@ KBoolean callbackReturnCharArray(CallbackReturnCharArray* arg) { KBoolean callbackReturnBooleanArray(CallbackReturnBooleanArray* arg) { const KBooleanArray* array = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && array->elements[0] == true && array->elements[1] == false; @@ -18,7 +18,7 @@ KBoolean callbackReturnBooleanArray(CallbackReturnBooleanArray* arg) { KBoolean callbackReturnByteArray(CallbackReturnByteArray* arg) { const KByteArray* array = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && array->elements[0] == 1 && array->elements[1] == 2; @@ -26,7 +26,7 @@ KBoolean callbackReturnByteArray(CallbackReturnByteArray* arg) { KBoolean callbackReturnShortArray(CallbackReturnShortArray* arg) { const KShortArray* array = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && array->elements[0] == 1 && array->elements[1] == 2; @@ -34,7 +34,7 @@ KBoolean callbackReturnShortArray(CallbackReturnShortArray* arg) { KBoolean callbackReturnIntArray(CallbackReturnIntArray* arg) { const KIntArray* array = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && array->elements[0] == 1 && array->elements[1] == 2; @@ -42,7 +42,7 @@ KBoolean callbackReturnIntArray(CallbackReturnIntArray* arg) { KBoolean callbackReturnLongArray(CallbackReturnLongArray* arg) { const KLongArray* array = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && array->elements[0] == 1 && array->elements[1] == 2; @@ -50,7 +50,7 @@ KBoolean callbackReturnLongArray(CallbackReturnLongArray* arg) { KBoolean callbackReturnFloatArray(CallbackReturnFloatArray* arg) { const KFloatArray* array = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && array->elements[0] == 1.1f && array->elements[1] == 2.2f; @@ -58,7 +58,7 @@ KBoolean callbackReturnFloatArray(CallbackReturnFloatArray* arg) { KBoolean callbackReturnDoubleArray(CallbackReturnDoubleArray* arg) { const KDoubleArray* array = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && array->elements[0] == 1.1 && array->elements[1] == 2.2; @@ -68,7 +68,7 @@ KBoolean callbackReturnEnumArray(CallbackReturnEnumArray* arg) { const KIntArray* array = arg->invoke(arg); const MyEnum* elements = (MyEnum*)array->elements; - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && elements[0] == MyEnum_CASE1 && elements[1] == MyEnum_CASE2; @@ -78,7 +78,7 @@ KBoolean callbackReturnDictionaryArray(CallbackReturnDictionaryArray* arg) { const KArray* array = arg->invoke(arg); MyDictionary** elements = (MyDictionary**)array->elements; - return K_OBJECT_IS_RELEASABLE(array->__flags) && + return (array->__flags & 1) && array->length == 2 && elements[0]->a == 1 && elements[0]->b == 2 && diff --git a/modules/tests/natives/test/src/arrays/pass_array.c b/modules/tests/natives/test/src/arrays/pass_array.c index a95db3a..b4a21e6 100644 --- a/modules/tests/natives/test/src/arrays/pass_array.c +++ b/modules/tests/natives/test/src/arrays/pass_array.c @@ -1,62 +1,62 @@ #include KBoolean passArray(KIntArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == 1 && arg->elements[1] == 2; } KBoolean passCharArray(KCharArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == 'a' && arg->elements[1] == 'b'; } KBoolean passBooleanArray(KBooleanArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == true && arg->elements[1] == false; } KBoolean passByteArray(KByteArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == 1 && arg->elements[1] == 2; } KBoolean passShortArray(KShortArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == 1 && arg->elements[1] == 2; } KBoolean passIntArray(KIntArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == 1 && arg->elements[1] == 2; } KBoolean passLongArray(KLongArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == 1 && arg->elements[1] == 2; } KBoolean passFloatArray(KFloatArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == 1.1f && arg->elements[1] == 2.2f; } KBoolean passDoubleArray(KDoubleArray* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && arg->elements[0] == 1.1 && arg->elements[1] == 2.2; @@ -65,7 +65,7 @@ KBoolean passDoubleArray(KDoubleArray* arg) { KBoolean passEnumArray(KIntArray* arg) { const MyEnum* elements = (MyEnum*)arg->elements; - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && elements[0] == MyEnum_CASE1 && elements[1] == MyEnum_CASE2; @@ -74,7 +74,7 @@ KBoolean passEnumArray(KIntArray* arg) { KBoolean passDictionaryArray(KArray* arg) { MyDictionary** elements = (MyDictionary**)arg->elements; - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && + return !(arg->__flags & 1) && arg->length == 2 && elements[0]->a == 1 && elements[0]->b == 2 && 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 640a1d5..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); 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 2bd3e45..1668e3b 100644 --- a/modules/tests/natives/test/src/primitives/callback_return_primitive.c +++ b/modules/tests/natives/test/src/primitives/callback_return_primitive.c @@ -35,7 +35,7 @@ KBoolean callbackReturnDouble(CallbackReturnDouble* arg) { KBoolean callbackReturnString(CallbackReturnString* arg) { const KString* str = arg->invoke(arg); - return K_OBJECT_IS_RELEASABLE(str->__flags) && strncmp(str->data, "test string", str->length) == 0; + 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 fd22287..121ff93 100644 --- a/modules/tests/natives/test/src/primitives/pass_primitive.c +++ b/modules/tests/natives/test/src/primitives/pass_primitive.c @@ -40,7 +40,7 @@ KBoolean passDouble(const KDouble arg) { } KBoolean passString(KString* arg) { - return !K_OBJECT_IS_RELEASABLE(arg->__flags) && arg->length == 11 && strncmp(arg->data, "test string", arg->length) == 0; + return !(arg->__flags & 1) && arg->length == 11 && strncmp(arg->data, "test string", arg->length) == 0; } KBoolean passEnum(const MyEnum arg) { 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") From f5075873603e61db325076346b395cd325b815a5 Mon Sep 17 00:00:00 2001 From: nshtengauer Date: Tue, 9 Jun 2026 00:54:15 +0300 Subject: [PATCH 3/8] Refactored native calling in all platforms --- .../nativekt/printers/c/CApiHeaderPrinter.kt | 20 +------ .../nativekt/printers/c/CApiImplPrinter.kt | 11 ---- .../huskerdev/nativekt/utils/CStructLayout.kt | 60 ------------------- .../huskerdev/nativekt/utils/PrinterUtils.kt | 5 -- 4 files changed, 2 insertions(+), 94 deletions(-) delete mode 100644 modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/CStructLayout.kt diff --git a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt index 1bd0602..79cb52d 100644 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt +++ b/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/printers/c/CApiHeaderPrinter.kt @@ -1,23 +1,7 @@ package com.huskerdev.nativekt.printers.c -import com.huskerdev.nativekt.utils.allFields -import com.huskerdev.nativekt.utils.firstParam -import com.huskerdev.nativekt.utils.globalOperators -import com.huskerdev.nativekt.utils.isArray -import com.huskerdev.nativekt.utils.isCallback -import com.huskerdev.nativekt.utils.isDictionary -import com.huskerdev.nativekt.utils.isPrimitive -import com.huskerdev.nativekt.utils.isString -import com.huskerdev.nativekt.utils.printLabel -import com.huskerdev.nativekt.utils.simpleName -import com.huskerdev.nativekt.utils.toCDefType -import com.huskerdev.webidl.resolver.BuiltinIdlDeclaration -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.webidl.resolver.ResolvedIdlType +import com.huskerdev.nativekt.utils.* +import com.huskerdev.webidl.resolver.* import java.io.File import kotlin.math.max 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 index aa0981d..392d964 100644 --- 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 @@ -1,20 +1,11 @@ package com.huskerdev.nativekt.printers.c import com.huskerdev.nativekt.utils.allFields -import com.huskerdev.nativekt.utils.firstParam -import com.huskerdev.nativekt.utils.isArray -import com.huskerdev.nativekt.utils.isCallback -import com.huskerdev.nativekt.utils.isDictionary import com.huskerdev.nativekt.utils.isPrimitive -import com.huskerdev.nativekt.utils.isString import com.huskerdev.nativekt.utils.printLabel -import com.huskerdev.nativekt.utils.simpleName import com.huskerdev.nativekt.utils.toCDefType -import com.huskerdev.webidl.resolver.BuiltinIdlDeclaration import com.huskerdev.webidl.resolver.IdlResolver import com.huskerdev.webidl.resolver.ResolvedIdlDictionary -import com.huskerdev.webidl.resolver.ResolvedIdlEnum -import com.huskerdev.webidl.resolver.ResolvedIdlType import java.io.File class CApiImplPrinter( @@ -154,8 +145,6 @@ class CApiImplPrinter( private fun printStdLib(builder: StringBuilder) = builder.apply { printLabel(builder, "stdlib") - val classPathPrefix = classPath.replace(".", "_") - builder.append(""" // String 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 31ff544..0000000 --- a/modules/gradle-plugin/src/main/kotlin/com/huskerdev/nativekt/utils/CStructLayout.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.huskerdev.nativekt.utils - -import com.huskerdev.webidl.resolver.BuiltinIdlDeclaration -import com.huskerdev.webidl.resolver.ResolvedIdlDictionary -import com.huskerdev.webidl.resolver.ResolvedIdlType -import com.huskerdev.webidl.resolver.WebIDLBuiltinKind -import kotlin.math.max - -class CStructLayout( - types: List, - x86: Boolean -) { - var count: Int = types.size - - 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( - types = dictionary.allFields() - .map { it.type } - .toMutableList() - .apply { - add(ResolvedIdlType.Default(BuiltinIdlDeclaration("int", WebIDLBuiltinKind.INT), emptyList(), false)) - }, - x86 - ) - - init { - var maxAlignment = 0 - types.forEach { type -> - val typeSize = type.getAlignment(x86) - - val rem = size % typeSize - val padding = if (rem == 0) 0 else typeSize - rem - - maxAlignment = max(maxAlignment, typeSize) - - 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 961d580..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 @@ -393,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() } From ed5f0f32944c27c8462b26db2b4372a1b4940863 Mon Sep 17 00:00:00 2001 From: nstengauer Date: Tue, 9 Jun 2026 01:20:36 +0300 Subject: [PATCH 4/8] Removed returnStringLiteral test --- modules/tests/natives/test/api.ndl | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/tests/natives/test/api.ndl b/modules/tests/natives/test/api.ndl index 7976b4a..81207d5 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(); From 2777b0c8215c7f562254d396d769914332990a14 Mon Sep 17 00:00:00 2001 From: nstengauer Date: Tue, 9 Jun 2026 01:38:35 +0300 Subject: [PATCH 5/8] Fixed native String creation --- .../nativeMain/kotlin/com/huskerdev/nativekt/kn/KnUtils.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 e4d24e7..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,4 +1,4 @@ -@file:OptIn(ExperimentalForeignApi::class) +@file:OptIn(ExperimentalForeignApi::class, UnsafeNumber::class) @file:Suppress("unused") package com.huskerdev.nativekt.kn @@ -32,7 +32,7 @@ fun MemScope.toNativeStringOnArena(str: String, pin: Boolean): CPointer fun toNativeString(str: String): CPointer { val bytes = str.cstr - val mem = malloc(bytes.size.convert())!!.reinterpret().pointed + val mem = malloc(sizeOf().convert())!!.reinterpret().pointed mem.size = bytes.size.convert() mem.data = malloc(mem.size.convert())!!.reinterpret() mem.length = str.length From fe16c3fe49473eba7b086e9d884aadca13f8c757 Mon Sep 17 00:00:00 2001 From: nstengauer Date: Tue, 9 Jun 2026 02:00:13 +0300 Subject: [PATCH 6/8] Fixed Android lib initialization --- .../huskerdev/nativekt/printers/kotlin/KotlinAndroidPrinter.kt | 1 + 1 file changed, 1 insertion(+) 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) { From c9d9b6fda85ac9dee02ed888e954bc330b0aaf12 Mon Sep 17 00:00:00 2001 From: nshtengauer Date: Tue, 9 Jun 2026 13:33:30 +0300 Subject: [PATCH 7/8] Fixed JVMCI mappings for array 6th --- .../jvmci/conventions/AMD64LinuxCallingConvention.java | 4 ++-- modules/tests/build.gradle.kts | 3 +++ modules/tests/natives/test/api.ndl | 2 +- modules/tests/natives/test/src/jvmci.c | 9 +++++---- modules/tests/src/jvmTest/kotlin/JVMCI.kt | 6 ++++++ 5 files changed, 17 insertions(+), 7 deletions(-) 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/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 81207d5..93aed0a 100644 --- a/modules/tests/natives/test/api.ndl +++ b/modules/tests/natives/test/api.ndl @@ -134,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/src/jvmci.c b/modules/tests/natives/test/src/jvmci.c index fca01bc..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,7 +70,7 @@ 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; } @@ -78,7 +79,7 @@ KBoolean jvmci10( 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; } 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") From 5fa49dceeea2ddfd74d1ae46b465f34b348d6f96 Mon Sep 17 00:00:00 2001 From: nshtengauer Date: Tue, 9 Jun 2026 13:54:26 +0300 Subject: [PATCH 8/8] Renamed tests action --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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