Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: call dictu from native code #755

Merged
merged 13 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:
- uses: actions/checkout@v3
- name: Make dictu and run tests (No HTTP)
run: |
cd examples/ffi-example
cmake -DCMAKE_BUILD_TYPE=Debug -B ./build
cmake --build ./build
cd ../..
cmake -DCMAKE_BUILD_TYPE=Debug -DDISABLE_HTTP=1 -B ./build
cmake --build ./build
./dictu tests/runTests.du ci | tee /dev/stderr | grep -q 'Total bytes lost: 0'
Expand All @@ -54,6 +58,10 @@ jobs:
- uses: actions/checkout@v3
- name: Make dictu and run tests (No HTTP)
run: |
cd examples/ffi-example
cmake -DCMAKE_BUILD_TYPE=Debug -B ./build
cmake --build ./build
cd ../..
cmake -DCMAKE_BUILD_TYPE=Debug -DDISABLE_HTTP=1 -B ./build
cmake --build ./build
./dictu tests/runTests.du ci | tee /dev/stderr | grep -q 'Total bytes lost: 0'
Expand All @@ -76,6 +84,10 @@ jobs:
- uses: actions/checkout@v3
- name: Make dictu and run tests (No HTTP)
run: |
cd examples\ffi-example
cmake -DCMAKE_BUILD_TYPE=Debug -B .\build
cmake --build ./build --config Debug
cd ..\..
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_SYSTEM_VERSION="10.0.18362.0" -DCICD=1 -DDISABLE_HTTP=1 -B build
cmake --build build
Debug\dictu.exe tests/runTests.du ci
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
\.idea/
.cache
build/
\.DS_Store
dictu
Expand Down
8 changes: 8 additions & 0 deletions examples/ffi-example/src/lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@ Value dictu_ffi_test_str(DictuVM *vm, int argCount, Value *args) {
return OBJ_VAL(copyString(vm, "Hello From Dictu FFI module!", 28));
}

Value dictu_ffi_test_callback(DictuVM *vm, int argCount, Value *args) {
if(argCount < 1)
return NIL_VAL;
Value* funcs = args+1;
return callFunction(vm, args[0], argCount-1, funcs);
}

int dictu_ffi_init(DictuVM *vm, Table *method_table) {
defineNative(vm, method_table, "dictuFFITestAdd", dictu_ffi_test);
defineNative(vm, method_table, "dictuFFITestStr", dictu_ffi_test_str);
defineNative(vm, method_table, "dictuFFITestCallback", dictu_ffi_test_callback);
defineNativeProperty(
vm, method_table, "test",
OBJ_VAL(copyString(vm, "Dictu!", 6)));
Expand Down
11 changes: 9 additions & 2 deletions src/include/dictu_ffi_include.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extern "C" {

// This is used ti determine if we can safely load the function pointers without
// UB.
#define FFI_MOD_API_VERSION 2
#define FFI_MOD_API_VERSION 3

#define UNUSED(__x__) (void)__x__

Expand Down Expand Up @@ -343,7 +343,7 @@ struct _vm {
};

#define DICTU_MAJOR_VERSION "0"
#define DICTU_MINOR_VERSION "29"
#define DICTU_MINOR_VERSION "30"
#define DICTU_PATCH_VERSION "0"

#define DICTU_STRING_VERSION \
Expand All @@ -364,6 +364,7 @@ struct sObjString {
int length;
char *chars;
uint32_t hash;
int character_len;
};

struct sObjList {
Expand Down Expand Up @@ -550,6 +551,9 @@ typedef void defineNative_t(DictuVM *vm, Table *table, const char *name,

typedef void defineNativeProperty_t(DictuVM *vm, Table *table, const char *name,
Value value);

typedef Value callFunction_t(DictuVM* vm, Value function, int argCount, Value* args);

reallocate_t * reallocate = NULL;

copyString_t *copyString = NULL;
Expand Down Expand Up @@ -616,6 +620,8 @@ defineNative_t *defineNative = NULL;

defineNativeProperty_t *defineNativeProperty = NULL;

callFunction_t *callFunction = NULL;

// This needs to be implemented by the user and register all functions
int dictu_ffi_init(DictuVM *vm, Table *method_table);

Expand Down Expand Up @@ -665,6 +671,7 @@ int dictu_internal_ffi_init(void **function_ptrs, DictuVM *vm,
defineNative = (defineNative_t *)function_ptrs[count++];
defineNativeProperty = (defineNativeProperty_t *)function_ptrs[count++];
reallocate = (reallocate_t *)function_ptrs[count++];
callFunction = (callFunction_t *)function_ptrs[count++];
int initResult = dictu_ffi_init(vm, methodTable);
if (initResult > 0)
return 3 + initResult;
Expand Down
3 changes: 2 additions & 1 deletion src/optionals/ffi/ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ void *ffi_function_pointers[] = {&copyString,
&compareStringGreater,
&defineNative,
&defineNativeProperty,
&reallocate};
&reallocate,
&callFunction};

void freeFFI(DictuVM *vm, ObjAbstract *abstract) {
FFIInstance *instance = (FFIInstance *)abstract->data;
Expand Down
2 changes: 1 addition & 1 deletion src/optionals/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

// This is used to determine if we can safely load the function pointers without UB,
// if this is greater then the version from the mod we error in the internal mod load function.
#define DICTU_FFI_API_VERSION 2
#define DICTU_FFI_API_VERSION 3


Value createFFIModule(DictuVM *vm);
Expand Down
50 changes: 49 additions & 1 deletion src/vm/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "datatypes/enums.h"
#include "natives.h"
#include "../optionals/optionals.h"
#include "value.h"

static void resetStack(DictuVM *vm) {
vm->stackTop = vm->stack;
Expand Down Expand Up @@ -58,6 +59,10 @@ void runtimeError(DictuVM *vm, const char *format, ...) {
for (int i = vm->frameCount - 1; i >= 0; i--) {
CallFrame *frame = &vm->frames[i];

if(frame->closure == NULL) {
// synthetic frame created by callFunction
continue;
}
ObjFunction *function = frame->closure->function;

// -1 because the IP is sitting on the next instruction to be
Expand Down Expand Up @@ -866,7 +871,9 @@ static void copyAnnotations(DictuVM *vm, ObjDict *superAnnotations, ObjDict *kla
}
}

static DictuInterpretResult run(DictuVM *vm) {


static DictuInterpretResult runWithBreakFrame(DictuVM *vm, int breakFrame) {
CallFrame *frame = &vm->frames[vm->frameCount - 1];
register uint8_t* ip = frame->ip;

Expand Down Expand Up @@ -2270,6 +2277,10 @@ static DictuInterpretResult run(DictuVM *vm) {

frame = &vm->frames[vm->frameCount - 1];
ip = frame->ip;
if (breakFrame != -1 && vm->frameCount == breakFrame) {
return INTERPRET_OK;
}

DISPATCH();
}

Expand Down Expand Up @@ -2435,6 +2446,9 @@ static DictuInterpretResult run(DictuVM *vm) {

return INTERPRET_RUNTIME_ERROR;
}
static DictuInterpretResult run(DictuVM *vm) {
return runWithBreakFrame(vm, -1);
}

DictuInterpretResult dictuInterpret(DictuVM *vm, char *moduleName, char *source) {
ObjString *name = copyString(vm, moduleName, strlen(moduleName));
Expand All @@ -2457,3 +2471,37 @@ DictuInterpretResult dictuInterpret(DictuVM *vm, char *moduleName, char *source)

return result;
}
Value callFunction(DictuVM* vm, Value function, int argCount, Value* args) {
if(!IS_FUNCTION(function) && !IS_CLOSURE(function)){
if(IS_NATIVE(function)) {
NativeFn native = AS_NATIVE(function);
return native(vm, argCount, args);
}
runtimeError(vm, "Value passed to callFunction is not callable");
return EMPTY_VAL;
}
int currentFrameCount = vm->frameCount;
Value* currentStack = vm->stackTop;
if (vm->frameCount == vm->frameCapacity) {
int oldCapacity = vm->frameCapacity;
vm->frameCapacity = GROW_CAPACITY(vm->frameCapacity);
vm->frames = GROW_ARRAY(vm, vm->frames, CallFrame,
oldCapacity, vm->frameCapacity);
}
CallFrame *frame = &vm->frames[vm->frameCount++];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we need to be careful we actually have enough frames, we may need to reallocate if not

uint8_t code[4] = {OP_CALL, argCount, 0, OP_RETURN};
frame->ip = code;
frame->closure = NULL;
push(vm, function);
for(int i = 0; i < argCount; i++) {
push(vm, args[i]);
}
DictuInterpretResult result = runWithBreakFrame(vm, currentFrameCount+1);
if(result != INTERPRET_OK) {
exit(70);
}
Value v = pop(vm);
vm->stackTop = currentStack;
vm->frameCount--;
return v;
}
2 changes: 2 additions & 0 deletions src/vm/vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ bool isFalsey(Value value);

ObjClosure *compileModuleToClosure(DictuVM *vm, char *name, char *source);

Value callFunction(DictuVM* vm, Value function, int argCount, Value* args);

#endif
41 changes: 36 additions & 5 deletions tests/ffi/ffi.du
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,49 @@ import FFI;
import Path;

class TestFFIModule < UnitTest {
var mod = nil;
setUp() {
if(this.mod != nil)
return;
const parts = ["examples", "ffi-example", "build"];
if(System.platform == "windows"){
parts.push("Debug");
parts.push("ffi-test.dll");
} else {
parts.push("libffi-test{}".format(FFI.suffix));
}

const path = Path.join(parts);
this.mod = FFI.load(path);
}
testFFIModule() {
const path = Path.join(Path.dirname(__file__), "libs", "test-lib{}{}".format(
System.platform == "darwin" ? System.arch == "x86_64" ? "_64" : "_arm" : "",
FFI.suffix
));
const mod = FFI.load(path);
const mod = this.mod;
this.assertEquals(mod.test, "Dictu!");
for(var i = 0; i < 40; i+=2) {
this.assertEquals(mod.dictuFFITestAdd(i, i), i+i);
}
this.assertEquals(mod.dictuFFITestStr(), "Hello From Dictu FFI module!");
}
testFFIModuleCallback() {
const mod = this.mod;
var count = 0;
const list = [];
def callback(value){
count += value;
list.push(value);
return value * value;
}
def greet(name) {
return "Hello, {}!".format(name);
}
for(var i = 0; i < 5; i+=1){
this.assertEquals(mod.dictuFFITestCallback(callback, i * i), i * i * i * i);
}
// 1, 4, 9, 16
this.assertEquals(list, [0,1,4,9,16]);
this.assertEquals(count, 1 + 4 + 9 + 16);
this.assertEquals(mod.dictuFFITestCallback(greet, "World"), "Hello, World!");
}
}

TestFFIModule().run();
Binary file removed tests/ffi/libs/test-lib.dll
Binary file not shown.
Binary file removed tests/ffi/libs/test-lib.so
Binary file not shown.
Binary file removed tests/ffi/libs/test-lib_64.dylib
Binary file not shown.
Binary file removed tests/ffi/libs/test-lib_arm.dylib
Binary file not shown.
Loading