Skip to content

Commit

Permalink
VM: initial support for calling native functions
Browse files Browse the repository at this point in the history
  • Loading branch information
mrunix00 committed Aug 3, 2024
1 parent a2266c5 commit 00c6546
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 21 deletions.
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ flex_target(lexer src/lexer.l "${LEXER_OUT}" DEFINES_FILE ${LEXER_DIR}/lexer.h)
bison_target(parser src/parser.y "${PARSER_OUT}" DEFINES_FILE ${PARSER_DIR}/parser.h)
add_flex_bison_dependency(lexer parser)

add_executable(SPL main.cpp ${SOURCES} ${LEXER_OUT} ${PARSER_OUT}
src/utils.cpp)
add_executable(SPL main.cpp ${SOURCES} ${LEXER_OUT} ${PARSER_OUT})
add_executable(tests ${TEST_SOURCES} ${SOURCES} ${LEXER_OUT} ${PARSER_OUT})
target_link_libraries(SPL linenoise)
target_link_libraries(tests GTest::gtest_main GTest::gmock_main)
23 changes: 23 additions & 0 deletions include/spl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif

typedef struct {
uint64_t argc;
uint64_t *argv;
} ExternArgs;

typedef struct {
uint64_t value;
enum {
SPL_VOID = 0,
SPL_VALUE,
SPL_OBJECT,
} type;
} ExternReturn;

typedef ExternReturn (*NativeFunction)(ExternArgs);
1 change: 1 addition & 0 deletions include/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
throw std::runtime_error("[Node::compile] Identifier not found: " + identifier); \
} \
switch (type->type) { \
case VariableType::Type::NativeLib: \
case VariableType::Type::Array: \
case VariableType::Type::Object: \
instruction.type = isLocal ? Instruction::InstructionType::OPERATION##LocalObject \
Expand Down
25 changes: 23 additions & 2 deletions include/vm.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma once

#include "spl.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <dlfcn.h>
#include <string>
#include <unordered_map>
#include <utility>
Expand Down Expand Up @@ -42,6 +44,8 @@ struct Instruction {
Call,
JumpIfFalse,
Jump,
LoadLib,
CallNative,
Exit
} type{};
union {
Expand All @@ -59,7 +63,8 @@ struct VariableType {
I64,
Object,
Array,
Function
Function,
NativeLib,
} type;
explicit VariableType(Type type) : type(type){};
};
Expand All @@ -79,13 +84,13 @@ struct Object {
Invalid = 0,
String,
Array,
DynamicLib,
} objType;
Object() : objType(Type::Invalid){};
virtual ~Object() = default;
explicit Object(Type type) : objType(type){};
bool marked = false;
};

struct StringObject : public Object {
size_t length;
char *chars;
Expand Down Expand Up @@ -126,6 +131,22 @@ struct ArrayObject : public Object {
return true;
}
};
struct DynamicLibObject : public Object {
void *handle;
char *dlPath;
explicit DynamicLibObject(char *dlPath, void *handle)
: Object(Object::Type::DynamicLib), dlPath(dlPath), handle(handle){};
~DynamicLibObject() override {
dlclose(handle);
}
};
struct DynamicFunctionObject : public Object {
std::string name;
std::vector<VariableType *> arguments;
explicit DynamicFunctionObject(std::string name, std::vector<VariableType *> arguments)
: name(std::move(name)), arguments(std::move(arguments)){};
~DynamicFunctionObject() override = default;
};

struct Variable {
std::string name;
Expand Down
49 changes: 37 additions & 12 deletions src/ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,31 @@ bool FunctionCall::operator==(const AbstractSyntaxTree &other) const {
return identifier == otherFunctionCall.identifier;
}
void FunctionCall::compile(Program &program, Segment &segment) const {
if (identifier.token.value == "native") {
arguments.front()->compile(program, segment);
return segment.instructions.push_back({.type = Instruction::LoadLib});
}
VariableType *varType = nullptr;
if (segment.find_local(identifier.token.value) != -1) {
varType = segment.locals[identifier.token.value].type;
} else if (segment.find_local(identifier.token.value) != -1) {
varType = program.segments.front().locals[identifier.token.value].type;
}
if (varType != nullptr && varType->type == VariableType::NativeLib) {
std::vector<VariableType *> funcArgs;
auto argName = (Node *) arguments[0];
auto argList = (List *) arguments[1];
for (auto arg: argList->elements) {
// TODO
}
segment.instructions.push_back({
.type = Instruction::LoadObject,
.params = {.ptr = new DynamicFunctionObject(argName->token.value, funcArgs)},
});
emitLoad(program, segment, identifier.token.value);
segment.instructions.push_back({.type = Instruction::InstructionType::CallNative});
return;
}
auto function = program.find_function(segment, identifier.token.value);
auto functionType = (FunctionType *) function.type;
for (int i = 0; i < arguments.size(); i++) {
Expand Down Expand Up @@ -624,18 +649,18 @@ bool ImportStatement::operator==(const AbstractSyntaxTree &other) const {
return otherImportStatement.path == path;
}
void ImportStatement::compile(Program &program, Segment &segment) const {
std::ifstream importedFile(path);
if (!importedFile.is_open()) {
throw std::runtime_error("Unable to open file: " + path);
}
std::stringstream fileContent;
fileContent << importedFile.rdbuf();
auto statements = parse(fileContent.str().c_str());
for (auto stm : statements) {
if(stm->nodeType == AbstractSyntaxTree::Type::ExportStatement)
stm->compile(program, segment);
delete stm;
}
std::ifstream importedFile(path);
if (!importedFile.is_open()) {
throw std::runtime_error("Unable to open file: " + path);
}
std::stringstream fileContent;
fileContent << importedFile.rdbuf();
auto statements = parse(fileContent.str().c_str());
for (auto stm: statements) {
if (stm->nodeType == AbstractSyntaxTree::Type::ExportStatement)
stm->compile(program, segment);
delete stm;
}
}

ExportStatement::ExportStatement(AbstractSyntaxTree *stm)
Expand Down
5 changes: 4 additions & 1 deletion src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ VariableType *deduceType(Program &program, Segment &segment, AbstractSyntaxTree
}
case AbstractSyntaxTree::Type::FunctionCall: {
auto call = dynamic_cast<FunctionCall *>(ast);
if (call->identifier.token.value == "native") {
return new VariableType(VariableType::NativeLib);
}
auto function = program.find_function(segment, call->identifier.token.value);
return new VariableType(((FunctionType *) function.type)->returnType->type);
}
Expand All @@ -105,7 +108,7 @@ VariableType *deduceType(Program &program, Segment &segment, AbstractSyntaxTree
else
throw std::runtime_error("Identifier not found: " + arrayAccess->identifier.token.value);
auto type = (ArrayObjectType *) (isLocal ? segment.locals[arrayAccess->identifier.token.value].type
: program.segments[0].locals[arrayAccess->identifier.token.value].type);
: program.segments[0].locals[arrayAccess->identifier.token.value].type);
return type->elementType;
} break;
default:
Expand Down
56 changes: 52 additions & 4 deletions src/vm.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#include "vm.h"
#include "utils.h"
#include <bit>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <dlfcn.h>
#include <stdexcept>

Program::Program() {
Expand All @@ -29,6 +28,7 @@ Variable Program::find_function(const Segment &segment, const std::string &ident
void Segment::declare_variable(const std::string &name, VariableType *varType) {
switch (varType->type) {
case VariableType::Object:
case VariableType::NativeLib:
case VariableType::Array:
locals[name] = Variable(name, varType, number_of_local_ptr);
number_of_local_ptr++;
Expand All @@ -52,7 +52,6 @@ void Segment::declare_function(const std::string &name, VariableType *funcType,
functions[name] = function;
locals[name] = function;
}

VM::VM() {
stackCapacity = 1024;
pointersStackCapacity = 1024;
Expand Down Expand Up @@ -185,7 +184,6 @@ void VM::sweep() {
}
});
}

void VM::run(const Program &program) {
if (callStack.front().localsSize != program.segments.front().number_of_locals) {
callStack.front().localsSize = program.segments.front().number_of_locals;
Expand Down Expand Up @@ -373,6 +371,56 @@ void VM::run(const Program &program) {
array->data[array->size++] = val;
pushPointer(array);
} break;
case Instruction::LoadLib: {
auto libPath = (StringObject *) popPointer();
auto handle = dlopen(libPath->chars, RTLD_LAZY);
if (handle == nullptr) {
throw std::runtime_error("Object file not found: " + std::string(libPath->chars));
}
auto lib = new DynamicLibObject(libPath->chars, handle);
pushPointer(lib);
addObject(lib);
} break;
case Instruction::CallNative: {
auto dynamicLib = (DynamicLibObject *) popPointer();
auto funcInfo = (DynamicFunctionObject *) popPointer();
auto func = (NativeFunction) dlsym(dynamicLib->handle, funcInfo->name.c_str());
if (func == nullptr) {
throw std::runtime_error(dlerror());
}
std::vector<uint64_t> args;
for (auto arg: funcInfo->arguments) {
switch (arg->type) {
case VariableType::Bool:
case VariableType::I64: {
auto val = popStack();
args.push_back(val);
} break;
case VariableType::Array:
case VariableType::Object: {
auto val = popPointer();
args.push_back(std::bit_cast<uint64_t>(val));
} break;
default:
throw std::runtime_error("[VM::run] Invalid argument type!");
}
}
auto returnValue = func({
.argc = args.size(),
.argv = args.data(),
});
switch (returnValue.type) {
case ExternReturn::SPL_VOID:
break;
case ExternReturn::SPL_VALUE:
pushStack(returnValue.value);
break;
case ExternReturn::SPL_OBJECT:
pushPointer(std::bit_cast<Object *>(returnValue.value));
addObject(std::bit_cast<Object *>(returnValue.value));
break;
}
} break;
case Instruction::Exit:
return;
}
Expand Down
1 change: 1 addition & 0 deletions stdlib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/testlib.so
10 changes: 10 additions & 0 deletions stdlib/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CXX = c++
CXXFLAGS = -fPIC -shared -I ../include
TARGET = testlib.so
SRCS = testlib.cpp

$(TARGET): $(SRCS)
$(CXX) $(SRCS) $(CXXFLAGS) -o $(TARGET)

clean:
rm -f $(TARGET)
9 changes: 9 additions & 0 deletions stdlib/testlib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "testlib.h"
#include <cassert>
#include <cstdio>

ExternReturn hello_world(ExternArgs args) {
assert(args.argc == 0);
printf("Hello world from C!\n");
return (ExternReturn){0, ExternReturn::SPL_VOID};
}
7 changes: 7 additions & 0 deletions stdlib/testlib.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include "spl.h"

extern "C" {
ExternReturn hello_world(ExternArgs args);
}
4 changes: 4 additions & 0 deletions stdlib/testlib.spl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export define lib = native("./testlib.so");
export define nativeHelloWorld : function() -> void = {
lib("hello_world", []);
}

0 comments on commit 00c6546

Please sign in to comment.