Skip to content

Fixed CLFuncFn and broken macros in cyber.h #99

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

Merged
merged 6 commits into from
Nov 24, 2024

Conversation

ccleavinger
Copy link
Contributor

Noticed differences between parameter types expressed in the docs and in use in cyber.h

Changed

typedef CLValue (*CLFuncFn)(CLVM* vm);

to

typedef CLValue (*CLFuncFn)(CLVM*, const CLValue*, uint8_t);

In addition I also updated the type macros CL_CUSTOM_TYPE and CL_CUSTOM_PRE_TYPE to use the proper union and enum.

This change made module loading somewhat functional on Windows. Still am dealing with errors regarding pointer casting in the getOrBufPrintValueStr function in vm.zig when trying to run a c-standard compliant version of the bind_module.c example. I'm not sure what could be causing this but it looks like a nasty issue.

Errors:

thread 15080 panic: cast causes pointer to be null
D:\test\cyber-src\cyber\src\vm.zig:1563:25: 0xea1c82 in getOrBufPrintValueStr (cyber.lib)
        if (val.isString()) {
                        ^
D:\test\cyber-src\cyber\src\builtins\builtins.zig:766:46: 0x10a55c2 in print_c (cyber.lib)
        const str = ctx.getOrBufPrintValueStr(&cy.tempBuf, arg) catch |err| {
                                             ^
D:\test\cyber-src\cyber\src\builtins\builtins.zig:757:24: 0x10a54fc in print (cyber.lib)
    const err = print_c(vm, vm.getValue(0));
                       ^
D:\test\cyber-src\cyber\src\vm.zig:2917:41: 0xeda75c in zCallSym (cyber.lib)
    const res = @call(.always_inline, VM.callSym, .{vm, pc, framePtr, func, ret}) catch |err| {
                                        ^
D:\test\cyber-src\cyber\src\vm.c:1162:0: 0x11fe869 in execBytecode (cyber.lib)
        PcFpResult res = zCallSym(vm, pc, stack, symId, ret);

D:\test\cyber-src\cyber\src\vm.zig:1885:41: 0xecfdb9 in evalLoopGrowStack (cyber.lib)
            const res = vmc.execBytecode(@ptrCast(vm));
                                        ^
D:\test\cyber-src\cyber\src\vm.zig:884:9: 0xe9ed99 in evalByteCode (cyber.lib)
        try @call(.never_inline, evalLoopGrowStack, .{self, true});
        ^
D:\test\cyber-src\cyber\src\vm.zig:713:37: 0xe9e751 in eval (cyber.lib)
            return self.evalByteCode(res.vm);
                                    ^
D:\test\cyber-src\cyber\src\lib.zig:111:23: 0xe1b12c in clEvalExt (cyber.lib)
    outVal.* = vm.eval(c.fromStr(uri), c.fromStr(src), config) catch |err| b: {
                      ^
D:\test\cyber-src\cyber\src\lib.zig:106:21: 0xe1b017 in clEval (cyber.lib)
    return clEvalExt(vm, c.toStr(uri), src, c.defaultEvalConfig(), outVal);
                    ^
D:\boring-lang\test\main.c:173:0: 0xe023f7 in main (main.obj)
    CLResultCode res = clEval(vm, main, &resv);

C:\Users\caleb\.zvm\0.12.0\lib\libc\mingw\crt\crtexe.c:267:0: 0xe028c4 in __tmainCRTStartup (crt2.obj)
    mainret = _tmain (argc, argv, envp);

C:\Users\caleb\.zvm\0.12.0\lib\libc\mingw\crt\crtexe.c:188:0: 0xe0291b in mainCRTStartup (crt2.obj)
  ret = __tmainCRTStartup ();

???:?:?: 0x7ffb8665259c in ??? (KERNEL32.DLL)
???:?:?: 0x7ffb86faaf37 in ??? (ntdll.dll)
PS D:\boring-lang\test> zvm run 0.12.0 cc main.c cyber.lib
PS D:\boring-lang\test> .\a.exe
Set up loaders!
Set up vars!
2.23
Success!
Live objects: 0
PS D:\boring-lang\test> zvm run 0.12.0 cc main.c cyber.lib
PS D:\boring-lang\test> .\a.exe
Set up loaders!
Set up vars!
thread 22172 panic: cast causes pointer to be null
D:\test\cyber-src\cyber\src\vm.zig:1563:25: 0x4d1c82 in getOrBufPrintValueStr (cyber.lib)
        if (val.isString()) {
                        ^
D:\test\cyber-src\cyber\src\builtins\builtins.zig:766:46: 0x6d55c2 in print_c (cyber.lib)
        const str = ctx.getOrBufPrintValueStr(&cy.tempBuf, arg) catch |err| {
                                             ^
D:\test\cyber-src\cyber\src\builtins\builtins.zig:757:24: 0x6d54fc in print (cyber.lib)
    const err = print_c(vm, vm.getValue(0));
                       ^
D:\test\cyber-src\cyber\src\vm.zig:2917:41: 0x50a75c in zCallSym (cyber.lib)
    const res = @call(.always_inline, VM.callSym, .{vm, pc, framePtr, func, ret}) catch |err| {
                                        ^
D:\test\cyber-src\cyber\src\vm.c:1162:0: 0x82e869 in execBytecode (cyber.lib)
        PcFpResult res = zCallSym(vm, pc, stack, symId, ret);

D:\test\cyber-src\cyber\src\vm.zig:1885:41: 0x4ffdb9 in evalLoopGrowStack (cyber.lib)
            const res = vmc.execBytecode(@ptrCast(vm));
                                        ^
D:\test\cyber-src\cyber\src\vm.zig:884:9: 0x4ced99 in evalByteCode (cyber.lib)
        try @call(.never_inline, evalLoopGrowStack, .{self, true});
        ^
D:\test\cyber-src\cyber\src\vm.zig:713:37: 0x4ce751 in eval (cyber.lib)
            return self.evalByteCode(res.vm);
                                    ^
D:\test\cyber-src\cyber\src\lib.zig:111:23: 0x44b12c in clEvalExt (cyber.lib)
    outVal.* = vm.eval(c.fromStr(uri), c.fromStr(src), config) catch |err| b: {
                      ^
D:\test\cyber-src\cyber\src\lib.zig:106:21: 0x44b017 in clEval (cyber.lib)
    return clEvalExt(vm, c.toStr(uri), src, c.defaultEvalConfig(), outVal);
                    ^
D:\boring-lang\test\main.c:180:0: 0x4323f7 in main (main.obj)
    CLResultCode res = clEval(vm, main, &resv);

C:\Users\caleb\.zvm\0.12.0\lib\libc\mingw\crt\crtexe.c:267:0: 0x4328c4 in __tmainCRTStartup (crt2.obj)
    mainret = _tmain (argc, argv, envp);

C:\Users\caleb\.zvm\0.12.0\lib\libc\mingw\crt\crtexe.c:188:0: 0x43291b in mainCRTStartup (crt2.obj)
  ret = __tmainCRTStartup ();

???:?:?: 0x7ffb8665259c in ??? (KERNEL32.DLL)
???:?:?: 0x7ffb86faaf37 in ??? (ntdll.dll)

Here's the code I've been using for testing purposes. The only real changes are for non-static initialization (zig cc didn't like setting members of structs via functions outside of the call stack) and I also added code ensuring all printed strings are null-terminated.

#include <string.h>
#include <stdio.h>
#include "cyber.h"

// This example shows how to setup a module loader for custom modules.
// Declarations from a module's source code invokes callbacks to bind with the host's functions and types.
// To see how to inject symbols programmatically, see `inject_module.c`.

// Compile this program with a C compiler. `zig cc` is used here as an example.
// zig cc bind_module.c -I ../../src/include ../../zig-out/lib/libcyber.a -o main

// Convenience macros to deal with Cyber string slices.
#define STR(s) ((CLStr){ s, strlen(s) })

CLValue add(CLVM* vm, const CLValue* args, uint8_t nargs) {
    double res = clAsFloat(args[0]) + clAsFloat(args[1]);
    return clFloat(res);
}

// Forward declaration.
CLValue myNodeNew(CLVM* vm, const CLValue* args, uint8_t nargs);
CLValue myNodeAsList(CLVM* vm, const CLValue* args, uint8_t nargs);

CLHostFuncEntry funcs[3];

// C has limited static initializers (and objects need a vm instance) so initialize them in `main`.
typedef struct { char* n; CLValue v; } NameValue;
NameValue vars[2];

bool varLoader(CLVM* vm, CLVarInfo info, CLValue* out) {
    // Check that the name matches before setting the value.
    if (strncmp(vars[info.idx].n, info.name.ptr, info.name.len) == 0) {
        // Objects are consumed by the module.
        *out = vars[info.idx].v;
        return true;
    } else {
        return false;
    }
}

// Binding a C struct with it's own children and finalizer.
// This struct retains 2 VM values and has 2 arbitrary data values unrelated to the VM.
typedef struct MyNode {
    CLValue val1;
    CLValue val2;
    int a;
    double b;
} MyNode;

CLType myNodeId;

// Implement the `new` function in MyNode.
CLValue myNodeNew(CLVM* vm, const CLValue* args, uint8_t nargs) {
    // Instantiate our object.
    CLValue new = clNewHostObject(vm, myNodeId, sizeof(MyNode));
    MyNode* my = (MyNode*)clAsHostObject(new);

    // Assign the constructor args passed in and retain them since the new object now references them.
    clRetain(vm, args[0]);
    my->val1 = args[0];
    clRetain(vm, args[1]);
    my->val2 = args[1];

    // Assign non VM values.
    my->a = 123;
    my->b = 9999.999;
    return new;
}

// Implement the `asList` method in MyNode.
CLValue myNodeAsList(CLVM* vm, const CLValue* args, uint8_t nargs) {
    // First argument is `self`.
    MyNode* my = (MyNode*)clAsHostObject(args[0]);

    CLValue vals[4] = {my->val1, my->val2, clInt(my->a), clFloat(my->b)};
    return clNewListDyn(vm, &vals[0], 4);
}

CLValueSlice myNodeGetChildren(CLVM* vm, void* obj) {
    MyNode* my = (MyNode*)obj;
    return (CLValueSlice){ .ptr = &my->val1, .len = 2 };
}

void myNodeFinalizer(CLVM* vm, void* obj) {
    printf("MyNode finalizer was called.\n");
}

CLHostTypeEntry types[1];

void initializeTypes() {
    types[0] = CL_CUSTOM_TYPE("MyNode", &myNodeId, myNodeGetChildren, myNodeFinalizer);
}

void initializeFuncs() {
     /*{
    {.name = {.ptr = "add", . 4}, add},
    {{"MyNode.asList", 14}, myNodeAsList},
    {{"MyNode.new", 11}, myNodeNew},
    };*/

    funcs[0] = (CLHostFuncEntry){STR("add"), add};
    funcs[1] = (CLHostFuncEntry){STR("MyNode.asList"), myNodeAsList};
    funcs[2] = (CLHostFuncEntry){STR("MyNode.new"), myNodeNew};
}

// This module loader provides the source code and callbacks to load @host funcs, vars, and types.
bool modLoader(CLVM* vm, CLStr spec, CLModule* res) {
    if (strncmp("my_mod", spec.ptr, spec.len) == 0) {
        CLStr src = STR(
            "@host func add(a float, b float) float\n"
            "@host var .MyConstant float\n"
            "@host var .MyList     List[dyn]\n"
            "\n"
            "@host\n"
            "type MyNode _:\n"
            "    @host func asList(self) any"
            "\n"
            "@host func MyNode.new(a any, b any) MyNode\n"
        );
        *res = clCreateModule(vm, spec, src);
        CLModuleConfig config = (CLModuleConfig){
            .funcs = (CLSlice){ .ptr = funcs, .len = 3 },
            .types = (CLSlice){ .ptr = types, .len = 1 },
            .varLoader = varLoader,
        };
        clSetModuleConfig(vm, *res, &config);
        return true;
    } else {
        // Fallback to the default module loader to load `builtins`.
        return clDefaultModuleLoader(vm, spec, res);
    }
}

void printer(CLVM* vm, CLStr str) {
    if (str.len == 1 && str.ptr[0] == '\n') {
        // Skip second invocation by `print` for the new line character.
        return;
    }
    char str_buf[str.len + 1];

    for (int i = 0; i < str.len; i++) {
        str_buf[i] = str.ptr[i];
    }
    str_buf[str.len] = '\0';

    printf("%s\n", str_buf);
}

int main() {
    initializeTypes();
    initializeFuncs();
    CLVM* vm = clCreate();
    clSetModuleLoader(vm, modLoader);
    clSetPrinter(vm, printer);

    printf("Set up loaders!\n");

    // Initialize var array for loader.
    vars[0] = (NameValue){"MyConstant", clFloat(1.23)};
    CLValue myInt = clInt(123);
    vars[1] = (NameValue){"MyList", clNewListDyn(vm, &myInt, 1)};

    printf("Set up vars!\n");

    CLStr main = STR(
        "use m 'my_mod'\n"
        "\n"
        "var a = 1.0\n"
        "print m.add(a, 2)\n"
        "print(-m.MyConstant)\n"
        "\n"
        "m.MyList.append(3)\n"
        "print m.MyList.len()\n"
        "\n"
        "-- Instantiate a new MyNode.\n"
        "var n = m.MyNode.new(1, 2)\n"
        "dump n.asList()"
    );
    CLValue resv;
    CLResultCode res = clEval(vm, main, &resv);
    if (res == CL_SUCCESS) {
        printf("Success!\n");
    } else {
        CLStr s = clNewLastErrorSummary(vm);
        printf("%.*s\n", (int)s.len, s.ptr);
        clFree(vm, s);
    }
    clRelease(vm, vars[1].v);
    clDeinit(vm);

    // Check that all references were accounted for. (Should be 0)
    printf("Live objects: %zu\n", clCountObjects(vm));
    clDestroy(vm);

    return 0;
}

@fubark
Copy link
Owner

fubark commented Nov 24, 2024

I would avoid interacting with C-API for now, the runtime is currently being transitioned to unboxed types. Hopefully I can get it out soon, but it's a huge commit and I just recently got some tests to pass.

@fubark fubark merged commit 33fffeb into fubark:master Nov 24, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants