diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7af5f62 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +CC=gcc +CFLAGS= +OBJ = ffi_nif.o +TARGET = ffi_nif.so +LIBS += -ldl -lffi +ERTS_INCLUDE_PATH=/home/josh/Playground/erlangs/erts-6.0/include +DIR=c_src + +$(DIR)/%.o: $(DIR)/%.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) -I$(ERTS_INCLUDE_PATH) + +$(TARGET): $(DIR)/$(OBJ) + $(CC) -shared -o $@ $^ $(CFLAGS) $(LIBS) + +.PHONY : $(TARGET) + +clean : + $(RM) $(DIR)/*.o *.so diff --git a/c_src/ffi_nif.c b/c_src/ffi_nif.c new file mode 100644 index 0000000..9bdbb18 --- /dev/null +++ b/c_src/ffi_nif.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +#define MAXLEN 1024 + +enum { + VOID = 0, + STRING, + INT +} TYPE; + +static void call(int argc, ffi_type **args, void **values, ffi_type* returnType) { + ffi_cif cif; + int returnValue; + + if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, argc, returnType, args) == FFI_OK) { + ffi_call(&cif, (void*)puts, &returnValue, values); + } +} + +static ERL_NIF_TERM nif_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + char library[MAXLEN], + function[MAXLEN]; + + int argumentsLength, valuesLength, returnType, returnValue, i; + + ErlNifCharEncoding encoding = ERL_NIF_LATIN1; + + if (!enif_get_atom(env, argv[0], library, MAXLEN, encoding)) { + return enif_make_badarg(env); + } + + if (!enif_get_atom(env, argv[1], function, MAXLEN, encoding)) { + return enif_make_badarg(env); + } + + if (!enif_get_list_length(env, argv[2], &argumentsLength)) { + return enif_make_badarg(env); + } + + if (!enif_get_int(env, argv[3], &returnType)) { + return enif_make_badarg(env); + } + + if (!enif_get_list_length(env, argv[4], &valuesLength)) { + return enif_make_badarg(env); + } + + if (valuesLength != argumentsLength) { + return enif_make_badarg(env); + } + + ffi_type *ffiArgs[argumentsLength], *ffiReturnType = &ffi_type_sint; + int args[argumentsLength]; + void *ffiValues[valuesLength]; + ERL_NIF_TERM head, tail = argv[2]; + uint type; + + for (i=0;enif_get_list_cell(env, tail, &head, &tail);i++) { + ffi_type *ffiType; + + enif_get_uint(env, head, &type); + + args[i] = type; + + switch (type) { + case VOID: + ffiType = &ffi_type_void; + break; + case STRING: + ffiType = &ffi_type_pointer; + break; + case INT: + ffiType = &ffi_type_sint; + break; + } + + ffiArgs[i] = ffiType; + } + + tail = argv[4]; + + int intData; + char charData[MAXLEN]; + char *s = charData; + + for (i=0;enif_get_list_cell(env, tail, &head, &tail);i++) { + type = args[i]; + + switch (type) { + case VOID: + break; + case STRING: + enif_get_string(env, head, charData, MAXLEN, encoding); + + ffiValues[i] = &s; + + break; + case INT: + enif_get_int(env, head, &intData); + + ffiValues[i] = &intData; + + break; + } + } + + switch (returnType) { + case VOID: + ffiReturnType = &ffi_type_void; + break; + case STRING: + ffiReturnType = &ffi_type_pointer; + break; + case INT: + ffiReturnType = &ffi_type_sint; + break; + } + + printf("%s.%s() %d %d\n", library, function, argumentsLength, returnType); + + call(argumentsLength, ffiArgs, ffiValues, ffiReturnType); + + returnValue = 100; + + return enif_make_int(env, returnValue); +} + +static ErlNifFunc nif_funcs[] = { + {"nif_call", 5, nif_call} +}; + +ERL_NIF_INIT(Elixir.FFI, nif_funcs, NULL, NULL, NULL, NULL) diff --git a/c_src/ffi_nif.o b/c_src/ffi_nif.o new file mode 100644 index 0000000..8c1655a Binary files /dev/null and b/c_src/ffi_nif.o differ