From 037607a9d38775f15b51310f2d5557bb525a31b8 Mon Sep 17 00:00:00 2001 From: fubark Date: Thu, 28 Sep 2023 14:21:52 -0400 Subject: [PATCH] Instantiate host objects. More endpoints. Update example. --- docs/hugo/content/docs/toc/modules.md | 1 + examples/c-embedded/bind_module.c | 109 ++++++++++++++++++++++---- src/arc.zig | 6 +- src/builtins/bindings.zig | 22 +++--- src/builtins/builtins.cy | 1 + src/builtins/builtins.zig | 23 ++++-- src/cyber.zig | 2 +- src/heap.zig | 70 ++++++++--------- src/include/cyber.h | 10 ++- src/lib.zig | 60 +++++++++++++- src/sema.zig | 16 +++- src/std/fs.zig | 50 ++++++------ src/std/os.zig | 6 +- src/value.zig | 28 +++++-- src/vm.c | 20 ++--- src/vm.h | 11 ++- src/vm.zig | 2 +- test/behavior_test.zig | 4 +- 18 files changed, 317 insertions(+), 124 deletions(-) diff --git a/docs/hugo/content/docs/toc/modules.md b/docs/hugo/content/docs/toc/modules.md index a819bf8d4..529847e35 100644 --- a/docs/hugo/content/docs/toc/modules.md +++ b/docs/hugo/content/docs/toc/modules.md @@ -99,6 +99,7 @@ print id | `arrayFill(val any, n int) List` | Creates a list with initial capacity of `n` and values set to `val`. If the value is an object, it is shallow copied `n` times. | | `boolean(val any) boolean` | Converts a value to either `true` or `false`. | | `copy(val any) any` | Copies a primitive value or creates a shallow copy of an object value. | +| `dump(val any) none` | Prints the result of `toCyon` on a value. | | `error(e (enum \| symbol)) error` | Create an error from an enum or symbol. | | `evalJS(val string) none` | Evals JS from the host environment. This is only available in a web WASM build of Cyber. | | `float(val any) float` | Casts or converts the value to a `float`. Panics if type conversion fails. | diff --git a/examples/c-embedded/bind_module.c b/examples/c-embedded/bind_module.c index 1aa52d980..cfc48b0bc 100644 --- a/examples/c-embedded/bind_module.c +++ b/examples/c-embedded/bind_module.c @@ -17,14 +17,21 @@ CsValue add(CsVM* vm, const CsValue* args, uint8_t nargs) { double res = csAsFloat(args[0]) + csAsFloat(args[1]); return csFloat(res); } -struct { char* n; CsHostFuncFn fn; } funcs[] = { + +// Forward declaration. +CsValue myCollectionNew(CsVM* vm, const CsValue* args, uint8_t nargs); +CsValue myCollectionAsList(CsVM* vm, const CsValue* args, uint8_t nargs); + +struct { char* n; CsFuncFn fn; } funcs[] = { {"add", add}, + {"new", myCollectionNew}, + {"asList", myCollectionAsList}, }; -bool funcLoader(CsVM* vm, CsHostFuncInfo funcInfo, CsHostFuncResult* out) { +bool funcLoader(CsVM* vm, CsFuncInfo info, CsFuncResult* out) { // Check that the name matches before setting the function pointer. - if (strncmp(funcs[funcInfo.idx].n, funcInfo.name.buf, funcInfo.name.len) == 0) { - out->ptr = funcs[funcInfo.idx].fn; + if (strncmp(funcs[info.idx].n, info.name.buf, info.name.len) == 0) { + out->ptr = funcs[info.idx].fn; return true; } else { return false; @@ -35,27 +42,93 @@ bool funcLoader(CsVM* vm, CsHostFuncInfo funcInfo, CsHostFuncResult* out) { typedef struct { char* n; CsValue v; } NameValue; NameValue vars[2]; -bool varLoader(CsVM* vm, CsHostVarInfo varInfo, CsValue* out) { +bool varLoader(CsVM* vm, CsVarInfo info, CsValue* out) { // Check that the name matches before setting the value. - if (strncmp(vars[varInfo.idx].n, varInfo.name.buf, varInfo.name.len) == 0) { + if (strncmp(vars[info.idx].n, info.name.buf, info.name.len) == 0) { // Objects are consumed by the module. - *out = vars[varInfo.idx].v; + *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 MyCollection { + CsValue val1; + CsValue val2; + int a; + double b; +} MyCollection; + +CsTypeId myCollectionId; + +// Implement the `new` function in MyCollection. +CsValue myCollectionNew(CsVM* vm, const CsValue* args, uint8_t nargs) { + // Instantiate our object. + CsValue new = csNewHostObject(vm, myCollectionId, sizeof(MyCollection)); + MyCollection* my = (MyCollection*)csAsHostObject(new); + + // Assign the constructor args passed in and retain them since our object now + // has shared ownership. + csRetain(vm, args[0]); + my->val1 = args[0]; + csRetain(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 MyCollection. +CsValue myCollectionAsList(CsVM* vm, const CsValue* args, uint8_t nargs) { + // First argument is `self`. + MyCollection* my = (MyCollection*)csAsHostObject(args[0]); + + CsValue vals[4] = {my->val1, my->val2, csInteger(my->a), csFloat(my->b)}; + return csNewList(vm, &vals[0], 4); +} + +CsValueSlice myCollectionGetChildren(CsVM* vm, void* obj) { + MyCollection* my = (MyCollection*)obj; + return (CsValueSlice){ .ptr = &my->val1, .len = 2 }; +} + +void myCollectionFinalizer(CsVM* vm, void* obj) { + printf("MyCollection finalizer was called.\n"); +} + +bool typeLoader(CsVM* vm, CsTypeInfo info, CsTypeResult* out) { + if (strncmp("MyCollection", info.name.buf, info.name.len) == 0) { + out->data.object.outTypeId = &myCollectionId; + out->data.object.getChildren = myCollectionGetChildren; + out->data.object.finalizer = myCollectionFinalizer; return true; } else { - return NULL; + return false; } } +// This module loader provides the source code and callbacks to load @host funcs, vars, and types. bool modLoader(CsVM* vm, CsStr spec, CsModuleLoaderResult* out) { if (strncmp("my_engine", spec.buf, spec.len) == 0) { out->src = STR( "@host func add(a float, b float) float\n" "@host var MyConstant float\n" "@host var MyList List\n" + "\n" + "@host\n" + "type MyCollection object:\n" + " @host func new(a, b) MyCollection\n" + " @host func asList(self) any" ); out->srcIsStatic = true; out->funcLoader = funcLoader; out->varLoader = varLoader; + out->typeLoader = typeLoader; return true; } else { // Fallback to the default module loader to load `builtins`. @@ -75,23 +148,29 @@ int main() { // Initialize var array for loader. vars[0] = (NameValue){"MyConstant", csFloat(1.23)}; - vars[1] = (NameValue){"MyList", csNewList(vm)}; + CsValue myInt = csInteger(123); + vars[1] = (NameValue){"MyList", csNewList(vm, &myInt, 1)}; CsStr main = STR( - "import mod 'my_engine'\n" + "import eng 'my_engine'\n" "\n" "var a = 1.0\n" - "print mod.add(a, 2)\n" - "print(-mod.MyConstant)\n" - "mod.MyList.append(3)\n" - "print mod.MyList.len()\n" + "print eng.add(a, 2)\n" + "print(-eng.MyConstant)\n" + "\n" + "eng.MyList.append(3)\n" + "print eng.MyList.len()\n" + "\n" + "-- Instantiate a new MyCollection.\n" + "var myc = eng.MyCollection.new(1, 2)\n" + "dump myc.asList()" ); CsValue resv; CsResultCode res = csEval(vm, main, &resv); if (res == CS_SUCCESS) { printf("Success!\n"); } else { - CsStr err = csAllocLastErrorReport(vm); + CsStr err = csNewLastErrorReport(vm); PRINTS(err); csFreeStr(vm, err); } diff --git a/src/arc.zig b/src/arc.zig index 2abfb48bd..87a2355ce 100644 --- a/src/arc.zig +++ b/src/arc.zig @@ -383,10 +383,10 @@ fn markValue(vm: *cy.VM, v: cy.Value) void { } }, rt.ListIteratorT => { - markValue(vm, cy.Value.initPtr(obj.listIter.list)); + markValue(vm, cy.Value.initNoCycPtr(obj.listIter.list)); }, rt.MapIteratorT => { - markValue(vm, cy.Value.initPtr(obj.mapIter.map)); + markValue(vm, cy.Value.initNoCycPtr(obj.mapIter.map)); }, rt.ClosureT => { const vals = obj.closure.getCapturedValuesPtr()[0..obj.closure.numCaptured]; @@ -474,7 +474,7 @@ pub fn checkGlobalRC(vm: *cy.VM) !void { const typeName = vm.getTypeName(it.key_ptr.*.getTypeId()); const msg = try std.fmt.bufPrint(&buf, "Init alloc: {*}, type: {s}, rc: {} at pc: {}\nval={s}", .{ it.key_ptr.*, typeName, it.key_ptr.*.head.rc, trace.allocPc, - vm.valueToTempString(cy.Value.initPtr(it.key_ptr.*)), + vm.valueToTempString(cy.Value.initNoCycPtr(it.key_ptr.*)), }); try cy.debug.printTraceAtPc(vm, trace.allocPc, msg); } diff --git a/src/builtins/bindings.zig b/src/builtins/bindings.zig index 9706056f9..8a0f9d41d 100644 --- a/src/builtins/bindings.zig +++ b/src/builtins/bindings.zig @@ -582,7 +582,7 @@ pub fn listJoinString(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(c std.mem.copy(u8, buf[dst..dst+slice.len], slice); dst += slice.len; } - return Value.initPtr(newObj); + return Value.initNoCycPtr(newObj); } else { // Empty string. return Value.initStaticAstring(0, 0); @@ -768,7 +768,7 @@ pub fn stringUpper(comptime T: cy.StringType) cy.ZHostFuncFn { const new = vm.allocUnsetRawStringObject(str.len) catch fatal(); const newBuf = new.rawstring.getSlice(); _ = std.ascii.upperString(newBuf, str); - return Value.initPtr(new); + return Value.initNoCycPtr(new); } else fatal(); } }; @@ -794,7 +794,7 @@ pub fn stringLower(comptime T: cy.StringType) cy.ZHostFuncFn { const new = vm.allocUnsetRawStringObject(str.len) catch fatal(); const newBuf = new.rawstring.getSlice(); _ = std.ascii.lowerString(newBuf, str); - return Value.initPtr(new); + return Value.initNoCycPtr(new); } else fatal(); } }; @@ -985,7 +985,7 @@ fn rawStringInsertByteCommon(vm: *cy.UserVM, str: []const u8, indexv: Value, val std.mem.copy(u8, buf[0..uidx], str[0..uidx]); buf[uidx] = byte; std.mem.copy(u8, buf[uidx+1..], str[uidx..]); - return Value.initPtr(new); + return Value.initNoCycPtr(new); } fn rawStringInsertByte(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.Section) Value { @@ -1070,7 +1070,7 @@ fn stringRepeat(comptime T: cy.StringType) cy.ZHostFuncFn { dst += @intCast(str.len); } - return Value.initPtr(new); + return Value.initNoCycPtr(new); } else { if (un == 0) { if (isRawStringObject(T)) { @@ -1081,7 +1081,7 @@ fn stringRepeat(comptime T: cy.StringType) cy.ZHostFuncFn { } else { if (isHeapString(T)) { vm.retainObject(obj); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } else { return args[0]; } @@ -1307,7 +1307,7 @@ fn stringReplace(comptime T: cy.StringType) cy.ZHostFuncFn { } else { if (T != .staticAstring) { vm.retainObject(obj); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } else { return args[0]; } @@ -1318,7 +1318,7 @@ fn stringReplace(comptime T: cy.StringType) cy.ZHostFuncFn { } else { if (T != .staticUstring) { vm.retainObject(obj); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } else { return args[0]; } @@ -1337,10 +1337,10 @@ fn stringReplace(comptime T: cy.StringType) cy.ZHostFuncFn { const newBuf = new.rawstring.getSlice(); const idxes = @as([*]const u32, @ptrCast(idxBuf.buf.ptr))[0..numIdxes]; cy.replaceAtIdxes(newBuf, str, @intCast(needle.len), replacement, idxes); - return Value.initPtr(new); + return Value.initNoCycPtr(new); } else { vm.retainObject(obj); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } else fatal(); } @@ -1537,7 +1537,7 @@ fn stringInsert(comptime T: cy.StringType) cy.ZHostFuncFn { std.mem.copy(u8, buf[0..uidx], str[0..uidx]); std.mem.copy(u8, buf[uidx..uidx+insert.len], insert); std.mem.copy(u8, buf[uidx+insert.len..], str[uidx..]); - return Value.initPtr(new); + return Value.initNoCycPtr(new); } else fatal(); } }; diff --git a/src/builtins/builtins.cy b/src/builtins/builtins.cy index 31a3dcd8a..5734f9855 100644 --- a/src/builtins/builtins.cy +++ b/src/builtins/builtins.cy @@ -113,6 +113,7 @@ type MapIterator object: @host func bool(val any) boolean @host func char(val any) any @host func copy(val any) any +@host func dump(val any) string @host func errorReport() string @host func isAlpha(val int) boolean @host func isDigit(val int) boolean diff --git a/src/builtins/builtins.zig b/src/builtins/builtins.zig index fb69d54d4..a08cef225 100644 --- a/src/builtins/builtins.zig +++ b/src/builtins/builtins.zig @@ -114,6 +114,7 @@ const funcs = [_]NameFunc{ .{"bool", btBool, .standard}, .{"char", char, .standard}, .{"copy", copy, .standard}, + .{"dump", dump, .standard}, .{"errorReport", errorReport, .standard}, .{"isAlpha", isAlpha, .standard}, .{"isDigit", isDigit, .standard}, @@ -270,7 +271,22 @@ pub fn runestr(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSe } } +pub fn dump(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { + const alloc = vm.allocator(); + const res = allocToCyon(vm, alloc, args[0]) catch cy.fatal(); + defer alloc.free(res); + vm.internal().print(vm, cy.Str.initSlice(res)); + return Value.None; +} + pub fn toCyon(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { + const alloc = vm.allocator(); + const res = allocToCyon(vm, alloc, args[0]) catch cy.fatal(); + defer alloc.free(res); + return vm.allocStringInfer(res) catch fatal(); +} + +fn allocToCyon(vm: *cy.UserVM, alloc: std.mem.Allocator, root: Value) ![]const u8 { const S = struct { fn encodeMap(ctx: *cy.EncodeMapContext, val: cy.Value) anyerror!void { const uservm = cy.ptrAlignCast(*cy.UserVM, ctx.user_ctx); @@ -370,12 +386,7 @@ pub fn toCyon(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSec } } }; - const root = args[0]; - const alloc = vm.allocator(); - - const cyon = cy.encodeCyon(alloc, vm, root, S.encodeRoot) catch fatal(); - defer alloc.free(cyon); - return vm.allocStringInfer(cyon) catch fatal(); + return try cy.encodeCyon(alloc, vm, root, S.encodeRoot); } pub fn parseCyber(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { diff --git a/src/cyber.zig b/src/cyber.zig index fecf11710..1f81e658d 100644 --- a/src/cyber.zig +++ b/src/cyber.zig @@ -243,7 +243,7 @@ pub const HostTypeResult = extern struct { data: extern union { object: extern struct { typeId: *rt.TypeId, - semaTypeId: *types.TypeId, + semaTypeId: ?*types.TypeId, getChildren: ?ObjectGetChildrenFn, finalizer: ?ObjectFinalizerFn, }, diff --git a/src/heap.zig b/src/heap.zig index 90b1c8a31..a2a7b841b 100644 --- a/src/heap.zig +++ b/src/heap.zig @@ -692,7 +692,7 @@ pub fn allocMetaType(self: *cy.VM, symType: u8, symId: u32) !Value { .type = symType, .symId = symId, }; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocEmptyList(self: *cy.VM) linksection(cy.Section) !Value { @@ -749,7 +749,7 @@ pub fn allocListFill(self: *cy.VM, val: Value, n: u32) linksection(cy.StdSection return Value.initCycPtr(obj); } -pub fn allocHostObject(vm: *cy.VM, typeId: rt.TypeId, numBytes: usize) linksection(cy.HotSection) !*anyopaque { +pub fn allocHostNoCycObject(vm: *cy.VM, typeId: rt.TypeId, numBytes: usize) linksection(cy.HotSection) !*anyopaque { if (numBytes <= MaxPoolObjectUserBytes) { const obj = try allocPoolObject(vm); obj.head = .{ @@ -939,7 +939,7 @@ pub fn allocLambda(self: *cy.VM, funcPc: usize, numParams: u8, stackSize: u8, fu .stackSize = stackSize, .funcSigId = funcSigId, }; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocStringTemplate(self: *cy.VM, strs: []const cy.Inst, vals: []const Value) !Value { @@ -982,7 +982,7 @@ pub fn allocRawStringConcat(self: *cy.VM, str: []const u8, str2: []const u8) lin const dst = @as([*]u8, @ptrCast(&obj.rawstring.bufStart))[0..len]; std.mem.copy(u8, dst[0..str.len], str); std.mem.copy(u8, dst[str.len..], str2); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } fn allocUstringConcat3Object(self: *cy.VM, str1: []const u8, str2: []const u8, str3: []const u8, charLen: u32) linksection(cy.Section) !*HeapObject { @@ -1036,16 +1036,16 @@ pub fn getOrAllocAstringConcat(self: *cy.VM, str: []const u8, str2: []const u8) const res = try self.strInterns.getOrPutAdapted(self.alloc, concat, ctx); if (res.found_existing) { cy.arc.retainObject(self, res.value_ptr.*); - return Value.initPtr(res.value_ptr.*); + return Value.initNoCycPtr(res.value_ptr.*); } else { const obj = try allocAstringConcatObject(self, str, str2); res.key_ptr.* = obj.astring.getConstSlice(); res.value_ptr.* = obj; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } else { const obj = try allocAstringConcatObject(self, str, str2); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } @@ -1060,16 +1060,16 @@ pub fn getOrAllocAstringConcat3(self: *cy.VM, str1: []const u8, str2: []const u8 const res = try self.strInterns.getOrPutAdapted(self.alloc, concat, ctx); if (res.found_existing) { cy.arc.retainObject(self, res.value_ptr.*); - return Value.initPtr(res.value_ptr.*); + return Value.initNoCycPtr(res.value_ptr.*); } else { const obj = try allocAstringConcat3Object(self, str1, str2, str3); res.key_ptr.* = obj.astring.getConstSlice(); res.value_ptr.* = obj; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } else { const obj = try allocAstringConcat3Object(self, str1, str2, str3); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } @@ -1084,16 +1084,16 @@ pub fn getOrAllocUstringConcat3(self: *cy.VM, str1: []const u8, str2: []const u8 const res = try self.strInterns.getOrPutAdapted(self.alloc, concat, ctx); if (res.found_existing) { cy.arc.retainObject(self, res.value_ptr.*); - return Value.initPtr(res.value_ptr.*); + return Value.initNoCycPtr(res.value_ptr.*); } else { const obj = try allocUstringConcat3Object(self, str1, str2, str3, charLen); res.key_ptr.* = obj.ustring.getConstSlice(); res.value_ptr.* = obj; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } else { const obj = try allocUstringConcat3Object(self, str1, str2, str3, charLen); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } @@ -1107,16 +1107,16 @@ pub fn getOrAllocUstringConcat(self: *cy.VM, str: []const u8, str2: []const u8, const res = try self.strInterns.getOrPutAdapted(self.alloc, concat, ctx); if (res.found_existing) { cy.arc.retainObject(self, res.value_ptr.*); - return Value.initPtr(res.value_ptr.*); + return Value.initNoCycPtr(res.value_ptr.*); } else { const obj = try allocUstringConcatObject(self, str, str2, charLen); res.key_ptr.* = obj.ustring.getConstSlice(); res.value_ptr.* = obj; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } else { const obj = try allocUstringConcatObject(self, str, str2, charLen); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } @@ -1130,14 +1130,14 @@ pub fn getOrAllocOwnedString(self: *cy.VM, obj: *HeapObject, str: []const u8) li if (res.found_existing) { cy.arc.releaseObject(self, obj); cy.arc.retainObject(self, res.value_ptr.*); - return Value.initPtr(res.value_ptr.*); + return Value.initNoCycPtr(res.value_ptr.*); } else { res.key_ptr.* = str; res.value_ptr.* = obj; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } else { - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } @@ -1157,7 +1157,7 @@ pub fn allocAstring(self: *cy.VM, str: []const u8) linksection(cy.Section) !Valu const obj = try allocUnsetAstringObject(self, str.len); const dst = obj.astring.getSlice(); std.mem.copy(u8, dst, str); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocUnsetAstringObject(self: *cy.VM, len: usize) linksection(cy.Section) !*HeapObject { @@ -1181,16 +1181,16 @@ pub fn getOrAllocUstring(self: *cy.VM, str: []const u8, charLen: u32) linksectio const res = try self.strInterns.getOrPut(self.alloc, str); if (res.found_existing) { cy.arc.retainObject(self, res.value_ptr.*); - return Value.initPtr(res.value_ptr.*); + return Value.initNoCycPtr(res.value_ptr.*); } else { const obj = try allocUstringObject(self, str, charLen); res.key_ptr.* = obj.ustring.getConstSlice(); res.value_ptr.* = obj; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } else { const obj = try allocUstringObject(self, str, charLen); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } @@ -1199,22 +1199,22 @@ pub fn getOrAllocAstring(self: *cy.VM, str: []const u8) linksection(cy.Section) const res = try self.strInterns.getOrPut(self.alloc, str); if (res.found_existing) { cy.arc.retainObject(self, res.value_ptr.*); - return Value.initPtr(res.value_ptr.*); + return Value.initNoCycPtr(res.value_ptr.*); } else { const obj = try allocAstringObject(self, str); res.key_ptr.* = obj.astring.getConstSlice(); res.value_ptr.* = obj; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } else { const obj = try allocAstringObject(self, str); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } } pub fn allocUstring(self: *cy.VM, str: []const u8, charLen: u32) linksection(cy.Section) !Value { const obj = try allocUstringObject(self, str, charLen); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocUstringObject(self: *cy.VM, str: []const u8, charLen: u32) linksection(cy.Section) !*HeapObject { @@ -1255,7 +1255,7 @@ pub fn allocUstringSlice(self: *cy.VM, slice: []const u8, charLen: u32, parent: .uMruCharIdx = 0, .extra = @as(u63, @intCast(@intFromPtr(parent))), }; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocAstringSlice(self: *cy.VM, slice: []const u8, parent: *HeapObject) !Value { @@ -1270,7 +1270,7 @@ pub fn allocAstringSlice(self: *cy.VM, slice: []const u8, parent: *HeapObject) ! .uMruCharIdx = undefined, .extra = @as(u64, @intFromPtr(parent)) | (1 << 63), }; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocUnsetRawStringObject(self: *cy.VM, len: usize) linksection(cy.Section) !*HeapObject { @@ -1293,7 +1293,7 @@ pub fn allocRawString(self: *cy.VM, str: []const u8) linksection(cy.Section) !Va const obj = try allocUnsetRawStringObject(self, str.len); const dst = @as([*]u8, @ptrCast(&obj.rawstring.bufStart))[0..str.len]; std.mem.copy(u8, dst, str); - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocRawStringSlice(self: *cy.VM, slice: []const u8, parent: *HeapObject) !Value { @@ -1305,7 +1305,7 @@ pub fn allocRawStringSlice(self: *cy.VM, slice: []const u8, parent: *HeapObject) .len = @intCast(slice.len), .parent = @ptrCast(parent), }; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocBox(vm: *cy.VM, val: Value) !Value { @@ -1333,7 +1333,7 @@ pub fn allocNativeFunc1(self: *cy.VM, func: cy.ZHostFuncFn, numParams: u32, func obj.nativeFunc1.tccState = state; obj.nativeFunc1.hasTccState = true; } - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocTccState(self: *cy.VM, state: *tcc.TCCState, lib: *std.DynLib) linksection(cy.StdSection) !Value { @@ -1344,7 +1344,7 @@ pub fn allocTccState(self: *cy.VM, state: *tcc.TCCState, lib: *std.DynLib) links .state = state, .lib = lib, }; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } pub fn allocPointer(self: *cy.VM, ptr: ?*anyopaque) !Value { @@ -1354,7 +1354,7 @@ pub fn allocPointer(self: *cy.VM, ptr: ?*anyopaque) !Value { .rc = 1, .ptr = ptr, }; - return Value.initPtr(obj); + return Value.initNoCycPtr(obj); } /// Allocates an object outside of the object pool. @@ -1424,7 +1424,7 @@ pub fn allocFuncFromSym(self: *cy.VM, symId: cy.vm.SymbolId) !Value { }, .closure => { cy.arc.retainObject(self, @ptrCast(sym.inner.closure)); - return Value.initPtr(sym.inner.closure); + return Value.initCycPtr(sym.inner.closure); }, .none => return Value.None, } diff --git a/src/include/cyber.h b/src/include/cyber.h index c962f7d20..8ecced198 100644 --- a/src/include/cyber.h +++ b/src/include/cyber.h @@ -187,8 +187,11 @@ typedef struct CsTypeResult { // The created runtime type id will be written to `outTypeId`. // This typeId is then used to allocate a new instance of the object. CsTypeId* outTypeId; + // The created semantic type id will be written to `outSemaTypeId`. + // Defaults to null. CsSemaTypeId* outSemaTypeId; + // Pointer to callback or null. CsObjectGetChildrenFn getChildren; // Pointer to callback or null. @@ -270,7 +273,7 @@ CsResultCode csEval(CsVM* vm, CsStr src, CsValue* outVal); CsResultCode csValidate(CsVM* vm, CsStr src); /// After receiving an error CsResultCode, this returns the error report. Call `csFreeStr` afterwards. -CsStr csAllocLastErrorReport(CsVM* vm); +CsStr csNewLastErrorReport(CsVM* vm); // Attach a userdata pointer inside the VM. void* csGetUserData(CsVM* vm); @@ -316,6 +319,9 @@ size_t csCountObjects(CsVM* vm); // that is handed over to Cyber so it knows how to free it. // This is also used to manage accessible buffers when embedding WASM. void* csAlloc(CsVM* vm, size_t size); + +// When using the Zig allocator, you'll need to pass the original memory size. +// For all other allocators, use 1 for `len`. void csFree(CsVM* vm, void* ptr, size_t len); void csFreeStr(CsVM* vm, CsStr str); @@ -336,6 +342,7 @@ CsValue csInteger(int64_t n); CsValue csInteger32(int32_t n); CsValue csFloat(double f); CsValue csHostObject(void* ptr); +CsValue csVmObject(void* ptr); CsValue csSymbol(CsVM* vm, CsStr str); // `csNewString` is the recommended way to create a new string. Use `Astring` or `Ustring` if you know it @@ -344,6 +351,7 @@ CsValue csNewString(CsVM* vm, CsStr str); CsValue csNewAstring(CsVM* vm, CsStr str); CsValue csNewUstring(CsVM* vm, CsStr str, uint32_t charLen); +CsValue csNewTuple(CsVM* vm, const CsValue* vals, size_t len); CsValue csNewEmptyList(CsVM* vm); CsValue csNewList(CsVM* vm, const CsValue* vals, size_t len); CsValue csNewEmptyMap(CsVM* vm); diff --git a/src/lib.zig b/src/lib.zig index 3017a5104..e4c77be14 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -118,7 +118,7 @@ test "csValidate()" { var tempBuf: [1024]u8 align(8) = undefined; -export fn csAllocLastErrorReport(vm: *cy.UserVM) cy.Str { +export fn csNewLastErrorReport(vm: *cy.UserVM) cy.Str { const report = vm.allocLastErrorReport() catch fatal(); return cy.Str.initSlice(report); } @@ -211,6 +211,14 @@ export fn csInteger32(n: i32) Value { return Value.initInt(n); } +export fn csHostObject(ptr: *anyopaque) Value { + return Value.initHostPtr(ptr); +} + +export fn csVmObject(ptr: *anyopaque) Value { + return Value.initPtr(ptr); +} + export fn csNewString(vm: *cy.UserVM, cstr: cy.Str) Value { return vm.allocStringInfer(cstr.slice()) catch fatal(); } @@ -223,11 +231,27 @@ export fn csNewUstring(vm: *cy.UserVM, cstr: cy.Str, charLen: u32) Value { return vm.allocUstring(cstr.slice(), charLen) catch fatal(); } -export fn csNewList(vm: *cy.UserVM) Value { +export fn csNewTuple(vm: *cy.UserVM, ptr: [*]const Value, len: usize) Value { + const elems = ptr[0..len]; + for (elems) |elem| { + cy.arc.retain(vm.internal(), elem); + } + return cy.heap.allocTuple(vm.internal(), elems) catch fatal(); +} + +export fn csNewEmptyList(vm: *cy.UserVM) Value { return vm.allocEmptyList() catch fatal(); } -export fn csNewMap(vm: *cy.UserVM) Value { +export fn csNewList(vm: *cy.UserVM, ptr: [*]const Value, len: usize) Value { + const elems = ptr[0..len]; + for (elems) |elem| { + cy.arc.retain(vm.internal(), elem); + } + return vm.allocList(elems) catch fatal(); +} + +export fn csNewEmptyMap(vm: *cy.UserVM) Value { return vm.allocEmptyMap() catch fatal(); } @@ -244,6 +268,32 @@ test "csNewFunc()" { try t.eq(c.csGetTypeId(val), rt.NativeFuncT); } +export fn csNewHostObject(vm: *cy.UserVM, typeId: rt.TypeId, size: usize) Value { + const ptr = cy.heap.allocHostCycObject(vm.internal(), typeId, size) catch cy.fatal(); + return Value.initHostCycPtr(ptr); +} + +export fn csNewHostObjectPtr(vm: *cy.UserVM, typeId: rt.TypeId, size: usize) *anyopaque { + return cy.heap.allocHostCycObject(vm.internal(), typeId, size) catch cy.fatal(); +} + +export fn csNewVmObject(uvm: *cy.UserVM, typeId: rt.TypeId, fieldsPtr: [*]const Value, numFields: usize) Value { + const vm = uvm.internal(); + const entry = &vm.types.buf[typeId]; + std.debug.assert(!entry.isHostObject); + + std.debug.assert(numFields == entry.data.numFields); + const fields = fieldsPtr[0..numFields]; + for (fields) |field| { + cy.arc.retain(vm, field); + } + if (numFields <= 4) { + return cy.heap.allocObjectSmall(vm, typeId, fields) catch cy.fatal(); + } else { + return cy.heap.allocObject(vm, typeId, fields) catch cy.fatal(); + } +} + export fn csSymbol(vm: *cy.UserVM, str: cy.Str) Value { const id = vm.internal().ensureSymbolExt(str.slice(), true) catch fatal(); return Value.initSymbol(@intCast(id)); @@ -362,6 +412,10 @@ test "Constants." { try t.eq(c.CS_TYPE_METATYPE, rt.MetaTypeT); } +export fn csAsHostObject(val: Value) *anyopaque { + return val.asHostObject(*anyopaque); +} + export fn csGetTypeId(val: Value) c.CsTypeId { return val.getTypeId(); } diff --git a/src/sema.zig b/src/sema.zig index 69fa2dfb5..32bf2c5f4 100644 --- a/src/sema.zig +++ b/src/sema.zig @@ -839,9 +839,17 @@ pub fn declareHostObject(c: *cy.Chunk, nodeId: cy.NodeId) !void { }; c.curHostTypeIdx += 1; var res: cy.HostTypeResult = .{ - .data = undefined, + .data = .{ + .object = .{ + .typeId = undefined, + .semaTypeId = null, + .getChildren = null, + .finalizer = null, + }, + }, .type = .object, }; + log.tracev("Invoke type loader for: {s}", .{name}); if (typeLoader(@ptrCast(c.compiler.vm), info, &res)) { switch (res.type) { .object => { @@ -853,7 +861,9 @@ pub fn declareHostObject(c: *cy.Chunk, nodeId: cy.NodeId) !void { const key = ResolvedSymKey.initResolvedSymKey(c.getModule().resolvedRootSymId, nameId); const objType = try resolveObjectSym(c.compiler, key, objModId); res.data.object.typeId.* = objType.typeId; - res.data.object.semaTypeId.* = objType.sTypeId; + if (res.data.object.semaTypeId) |semaTypeId| { + semaTypeId.* = objType.sTypeId; + } c.compiler.vm.types.buf[objType.typeId].isHostObject = true; c.compiler.vm.types.buf[objType.typeId].data = .{ @@ -1180,6 +1190,7 @@ pub fn declareHostFunc(c: *cy.Chunk, modId: cy.ModuleId, nodeId: cy.NodeId) !voi }; c.curHostFuncIdx += 1; if (c.funcLoader) |funcLoader| { + log.tracev("Invoke func loader for: {s}", .{name}); var res: cy.HostFuncResult = .{ .ptr = null, .type = .standard, @@ -1309,6 +1320,7 @@ fn declareHostVar(c: *cy.Chunk, nodeId: cy.NodeId) !void { }; c.curHostVarIdx += 1; if (c.varLoader) |varLoader| { + log.tracev("Invoke var loader for: {s}", .{name}); var out: cy.Value = cy.Value.None; if (varLoader(@ptrCast(c.compiler.vm), info, &out)) { // var type. diff --git a/src/std/fs.zig b/src/std/fs.zig index bafe5ebb6..ee308c230 100644 --- a/src/std/fs.zig +++ b/src/std/fs.zig @@ -120,7 +120,7 @@ pub const DirIterator = extern struct { }; pub fn allocFile(vm: *cy.VM, fd: std.os.fd_t) linksection(cy.StdSection) !Value { - const file: *File = @ptrCast(@alignCast(try cy.heap.allocHostObject(vm, FileT, @sizeOf(File)))); + const file: *File = @ptrCast(@alignCast(try cy.heap.allocHostNoCycObject(vm, FileT, @sizeOf(File)))); file.* = .{ .fd = fd, .curPos = 0, @@ -132,27 +132,27 @@ pub fn allocFile(vm: *cy.VM, fd: std.os.fd_t) linksection(cy.StdSection) !Value .closed = false, .closeOnFree = true, }; - return Value.initHostPtr(file); + return Value.initHostNoCycPtr(file); } pub fn allocDir(vm: *cy.VM, fd: std.os.fd_t, iterable: bool) linksection(cy.StdSection) !Value { - const dir: *Dir = @ptrCast(@alignCast(try cy.heap.allocHostObject(vm, DirT, @sizeOf(Dir)))); + const dir: *Dir = @ptrCast(@alignCast(try cy.heap.allocHostNoCycObject(vm, DirT, @sizeOf(Dir)))); dir.* = .{ .fd = fd, .iterable = iterable, .closed = false, }; - return Value.initHostPtr(dir); + return Value.initHostNoCycPtr(dir); } pub fn allocDirIterator(vm: *cy.VM, dirv: Value, recursive: bool) linksection(cy.StdSection) !Value { - const dirIter: *DirIterator = @ptrCast(@alignCast(try cy.heap.allocHostObject(vm, DirIterT, @sizeOf(DirIterator)))); + const dirIter: *DirIterator = @ptrCast(@alignCast(try cy.heap.allocHostNoCycObject(vm, DirIterT, @sizeOf(DirIterator)))); dirIter.* = .{ .dir = dirv, .inner = undefined, .recursive = recursive, }; - const dir = dirv.asHostObject(Dir); + const dir = dirv.asHostObject(*Dir); if (recursive) { const walker = cy.ptrAlignCast(*std.fs.IterableDir.Walker, &dirIter.inner.walker); walker.* = try dir.getStdIterableDir().walk(vm.alloc); @@ -160,7 +160,7 @@ pub fn allocDirIterator(vm: *cy.VM, dirv: Value, recursive: bool) linksection(cy const iter = cy.ptrAlignCast(*std.fs.IterableDir.Iterator, &dirIter.inner.iter); iter.* = dir.getStdIterableDir().iterate(); } - return Value.initHostPtr(dirIter); + return Value.initHostNoCycPtr(dirIter); } test "fs internals" { @@ -190,7 +190,7 @@ pub fn fileStreamLines(vm: *cy.UserVM, args: [*]const Value, nargs: u8) linksect pub fn fileStreamLines1(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); // Don't need to release obj since it's being returned. - const file = args[0].asHostObject(File); + const file = args[0].asHostObject(*File); const bufSize: usize = @intCast(args[1].asInteger()); var createReadBuf = true; if (file.hasReadBuf) { @@ -214,7 +214,7 @@ pub fn fileStreamLines1(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection pub fn dirWalk(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const dir = args[0].asHostObject(Dir); + const dir = args[0].asHostObject(*Dir); if (dir.iterable) { vm.retain(args[0]); return vm.allocDirIterator(args[0], true) catch cy.fatal(); @@ -225,7 +225,7 @@ pub fn dirWalk(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSe pub fn dirIterator(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const dir = args[0].asHostObject(Dir); + const dir = args[0].asHostObject(*Dir); if (dir.iterable) { vm.retain(args[0]); return vm.allocDirIterator(args[0], false) catch cy.fatal(); @@ -238,7 +238,7 @@ pub fn fileIterator(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy. if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); // Don't need to release obj since it's being returned. - const file = args[0].asHostObject(File); + const file = args[0].asHostObject(*File); file.curPos = 0; file.readBufEnd = 0; return args[0]; @@ -247,7 +247,7 @@ pub fn fileIterator(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy. pub fn fileSeekFromEnd(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const fileo = args[0].asHostObject(File); + const fileo = args[0].asHostObject(*File); if (fileo.closed) { return prepareThrowSymbol(vm, .Closed); } @@ -267,7 +267,7 @@ pub fn fileSeekFromEnd(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection( pub fn fileSeekFromCur(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const fileo = args[0].asHostObject(File); + const fileo = args[0].asHostObject(*File); if (fileo.closed) { return prepareThrowSymbol(vm, .Closed); } @@ -284,7 +284,7 @@ pub fn fileSeekFromCur(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection( pub fn fileSeek(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const fileo = args[0].asHostObject(File); + const fileo = args[0].asHostObject(*File); if (fileo.closed) { return prepareThrowSymbol(vm, .Closed); } @@ -305,7 +305,7 @@ pub fn fileSeek(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdS pub fn fileWrite(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const fileo = args[0].asHostObject(File); + const fileo = args[0].asHostObject(*File); if (fileo.closed) { return prepareThrowSymbol(vm, .Closed); } @@ -322,7 +322,7 @@ pub fn fileWrite(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.Std pub fn fileClose(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const file = args[0].asHostObject(File); + const file = args[0].asHostObject(*File); file.close(); return Value.None; } @@ -330,7 +330,7 @@ pub fn fileClose(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.Std pub fn fileRead(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const fileo = args[0].asHostObject(File); + const fileo = args[0].asHostObject(*File); if (fileo.closed) { return prepareThrowSymbol(vm, .Closed); } @@ -359,7 +359,7 @@ pub fn fileRead(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdS pub fn fileReadToEnd(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const fileo = args[0].asHostObject(File); + const fileo = args[0].asHostObject(*File); if (fileo.closed) { return prepareThrowSymbol(vm, .Closed); } @@ -398,12 +398,12 @@ pub fn fileOrDirStat(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy const obj = args[0].asHeapObject(); const typeId = obj.getTypeId(); if (typeId == FileT) { - const file = args[0].asHostObject(File); + const file = args[0].asHostObject(*File); if (file.closed) { return prepareThrowSymbol(vm, .Closed); } } else if (typeId == DirT) { - const dir = args[0].asHostObject(Dir); + const dir = args[0].asHostObject(*Dir); if (dir.closed) { return prepareThrowSymbol(vm, .Closed); } @@ -412,7 +412,7 @@ pub fn fileOrDirStat(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy } // File/Dir share the same fd member offset. - const fileo = args[0].asHostObject(File); + const fileo = args[0].asHostObject(*File); const file = fileo.getStdFile(); const stat = file.stat() catch |err| { return cy.apiUnsupportedError(vm, "stat", err, @errorReturnTrace()); @@ -452,7 +452,7 @@ pub fn fileOrDirStat(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy pub fn fileNext(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value { if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); - const fileo = args[0].asHostObject(File); + const fileo = args[0].asHostObject(*File); if (fileo.iterLines) { const alloc = vm.allocator(); const readBuf = fileo.readBuf[0..fileo.readBufCap]; @@ -481,7 +481,7 @@ pub fn fileNext(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdS // End of stream. fileo.iterLines = false; if (lineBuf.len > 0) { - return Value.initPtr(lineBuf.ownObject(alloc)); + return Value.initNoCycPtr(lineBuf.ownObject(alloc)); } else { return Value.None; } @@ -494,7 +494,7 @@ pub fn fileNext(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdS fileo.curPos = @intCast(end); fileo.readBufEnd = @intCast(bytesRead); - return Value.initPtr(lineBuf.ownObject(alloc)); + return Value.initNoCycPtr(lineBuf.ownObject(alloc)); } else { lineBuf.appendString(alloc, readBuf[0..bytesRead]) catch cy.fatal(); @@ -512,7 +512,7 @@ pub fn dirIteratorNext(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection( if (!cy.hasStdFiles) return vm.returnPanic("Unsupported."); const ivm = vm.internal(); - const iter = args[0].asHostObject(DirIterator); + const iter = args[0].asHostObject(*DirIterator); if (iter.recursive) { const walker = cy.ptrAlignCast(*std.fs.IterableDir.Walker, &iter.inner.walker); const entryOpt = walker.next() catch |err| { diff --git a/src/std/os.zig b/src/std/os.zig index 70d18ad9c..3a0a98247 100644 --- a/src/std/os.zig +++ b/src/std/os.zig @@ -143,13 +143,13 @@ pub fn zPostTypeLoad(c: *cy.VMcompiler, modId: cy.ModuleId) !void { } if (cy.hasStdFiles) { const stdin = try fs.allocFile(c.vm, std.io.getStdIn().handle); - stdin.asHostObject(fs.File).closeOnFree = false; + stdin.asHostObject(*fs.File).closeOnFree = false; vars[2] = .{ "stdin", stdin }; const stdout = try fs.allocFile(c.vm, std.io.getStdOut().handle); - stdout.asHostObject(fs.File).closeOnFree = false; + stdout.asHostObject(*fs.File).closeOnFree = false; vars[3] = .{ "stdout", stdout }; const stderr = try fs.allocFile(c.vm, std.io.getStdErr().handle); - stderr.asHostObject(fs.File).closeOnFree = false; + stderr.asHostObject(*fs.File).closeOnFree = false; vars[4] = .{ "stderr", stderr }; } else { vars[2] = .{ "stdin", Value.None }; diff --git a/src/value.zig b/src/value.zig index 104c84d04..00cef60e4 100644 --- a/src/value.zig +++ b/src/value.zig @@ -327,7 +327,7 @@ pub const Value = packed union { return false; } - pub inline fn asHostObject(self: *const Value, comptime T: type) *T { + pub inline fn asHostObject(self: *const Value, comptime T: type) T { return @ptrFromInt(@as(usize, @intCast(self.val & ~vmc.POINTER_MASK)) + 8); } @@ -415,19 +415,37 @@ pub const Value = packed union { } pub inline fn initHostPtr(ptr: ?*anyopaque) Value { - return .{ .val = vmc.NOCYC_POINTER_MASK | (@intFromPtr(ptr) - 8) }; + const obj: *cy.HeapObject = @ptrFromInt(@intFromPtr(ptr) - 8); + if (obj.isCyclable()) { + return .{ .val = vmc.CYC_POINTER_MASK | (@intFromPtr(obj) & vmc.POINTER_PAYLOAD_MASK) }; + } else { + return .{ .val = vmc.NOCYC_POINTER_MASK | (@intFromPtr(obj) & vmc.POINTER_PAYLOAD_MASK) }; + } + } + + pub inline fn initHostNoCycPtr(ptr: ?*anyopaque) Value { + return .{ .val = vmc.NOCYC_POINTER_MASK | ((@intFromPtr(ptr) & vmc.POINTER_PAYLOAD_MASK) - 8) }; } pub inline fn initHostCycPtr(ptr: ?*anyopaque) Value { - return .{ .val = vmc.CYC_POINTER_MASK | (@intFromPtr(ptr) - 8) }; + return .{ .val = vmc.CYC_POINTER_MASK | ((@intFromPtr(ptr) & vmc.POINTER_PAYLOAD_MASK) - 8) }; } pub inline fn initPtr(ptr: ?*anyopaque) Value { - return .{ .val = vmc.NOCYC_POINTER_MASK | @intFromPtr(ptr) }; + const obj: *cy.HeapObject = @ptrCast(@alignCast(ptr)); + if (obj.isCyclable()) { + return .{ .val = vmc.CYC_POINTER_MASK | (@intFromPtr(obj) & vmc.POINTER_PAYLOAD_MASK) }; + } else { + return .{ .val = vmc.NOCYC_POINTER_MASK | (@intFromPtr(obj) & vmc.POINTER_PAYLOAD_MASK) }; + } + } + + pub inline fn initNoCycPtr(ptr: ?*anyopaque) Value { + return .{ .val = vmc.NOCYC_POINTER_MASK | (@intFromPtr(ptr) & vmc.POINTER_PAYLOAD_MASK) }; } pub inline fn initCycPtr(ptr: ?*anyopaque) Value { - return .{ .val = vmc.CYC_POINTER_MASK | @intFromPtr(ptr) }; + return .{ .val = vmc.CYC_POINTER_MASK | (@intFromPtr(ptr) & vmc.POINTER_PAYLOAD_MASK) }; } pub inline fn initStaticAstring(start: u32, len: u15) Value { diff --git a/src/vm.c b/src/vm.c index a6a658d4a..a605b7f12 100644 --- a/src/vm.c +++ b/src/vm.c @@ -34,8 +34,8 @@ #define VALUE_FALSE FALSE_MASK #define VALUE_INTERRUPT (ERROR_MASK | 0xffff) #define VALUE_RAW(u) u -#define VALUE_PTR(ptr) (NOCYC_POINTER_MASK | (u64)ptr) -#define VALUE_CYCPTR(ptr) (CYC_POINTER_MASK | (u64)ptr) +#define VALUE_NOCYC_PTR(ptr) (NOCYC_POINTER_MASK | ((size_t)ptr & POINTER_PAYLOAD_MASK)) +#define VALUE_CYC_PTR(ptr) (CYC_POINTER_MASK | ((size_t)ptr & POINTER_PAYLOAD_MASK)) #define VALUE_STATIC_STRING_SLICE(v) ((IndexSlice){ .start = v & 0xffffffff, .len = (((u32)(v >> 32)) & BEFORE_TAG_MASK) >> 3 }) #define VALUE_SYMBOL(symId) (SYMBOL_MASK | symId) @@ -309,7 +309,7 @@ static inline ValueResult allocObject(VM* vm, TypeId typeId, Value* fields, u8 n Value* dst = objectGetValuesPtr(&res.obj->object); memcpy(dst, fields, numFields * sizeof(Value)); - return (ValueResult){ .val = VALUE_CYCPTR(res.obj), .code = RES_CODE_SUCCESS }; + return (ValueResult){ .val = VALUE_CYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; } static inline ValueResult allocEmptyMap(VM* vm) { @@ -328,7 +328,7 @@ static inline ValueResult allocEmptyMap(VM* vm) { .available = 0, }, }; - return (ValueResult){ .val = VALUE_CYCPTR(res.obj), .code = RES_CODE_SUCCESS }; + return (ValueResult){ .val = VALUE_CYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; } static inline ValueResult allocClosure( @@ -366,7 +366,7 @@ static inline ValueResult allocClosure( retain(vm, fp[local]); dst[i] = fp[local]; } - return (ValueResult){ .val = VALUE_CYCPTR(res.obj), .code = RES_CODE_SUCCESS }; + return (ValueResult){ .val = VALUE_CYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; } static inline ValueResult allocLambda(VM* vm, uint32_t funcPc, uint8_t numParams, uint8_t stackSize, uint16_t rFuncSigId) { @@ -382,7 +382,7 @@ static inline ValueResult allocLambda(VM* vm, uint32_t funcPc, uint8_t numParams .stackSize = stackSize, .rFuncSigId = rFuncSigId, }; - return (ValueResult){ .val = VALUE_PTR(res.obj), .code = RES_CODE_SUCCESS }; + return (ValueResult){ .val = VALUE_NOCYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; } static inline ValueResult allocBox(VM* vm, Value val) { @@ -395,7 +395,7 @@ static inline ValueResult allocBox(VM* vm, Value val) { .rc = 1, .val = val, }; - return (ValueResult){ .val = VALUE_CYCPTR(res.obj), .code = RES_CODE_SUCCESS }; + return (ValueResult){ .val = VALUE_CYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; } static inline ValueResult allocMetaType(VM* vm, uint8_t symType, uint32_t symId) { @@ -409,7 +409,7 @@ static inline ValueResult allocMetaType(VM* vm, uint8_t symType, uint32_t symId) .type = symType, .symId = symId, }; - return (ValueResult){ .val = VALUE_PTR(res.obj), .code = RES_CODE_SUCCESS }; + return (ValueResult){ .val = VALUE_NOCYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; } static inline ValueResult allocHostFunc(VM* vm, void* func, u32 numParams, u32 rFuncSigId) { @@ -422,7 +422,7 @@ static inline ValueResult allocHostFunc(VM* vm, void* func, u32 numParams, u32 r .rFuncSigId = rFuncSigId, .hasTccState = false, }; - return (ValueResult){ .val = VALUE_PTR(res.obj), .code = RES_CODE_SUCCESS }; + return (ValueResult){ .val = VALUE_NOCYC_PTR(res.obj), .code = RES_CODE_SUCCESS }; } static inline ValueResult allocFuncFromSym(VM* vm, FuncId funcId) { @@ -442,7 +442,7 @@ static inline ValueResult allocFuncFromSym(VM* vm, FuncId funcId) { } case FUNC_SYM_CLOSURE: { retainObject(vm, sym.inner.closure); - return (ValueResult){ .val = VALUE_PTR(sym.inner.closure), .code = RES_CODE_SUCCESS }; + return (ValueResult){ .val = VALUE_CYC_PTR(sym.inner.closure), .code = RES_CODE_SUCCESS }; } default: zFatal(); diff --git a/src/vm.h b/src/vm.h index cbbd13bf9..b97240818 100644 --- a/src/vm.h +++ b/src/vm.h @@ -45,9 +45,15 @@ typedef struct IndexSlice { #define NOCYC_POINTER_MASK (TAGGED_VALUE_MASK | SIGN_MASK) // 1111111111111110: Extra bit indicating cyclable pointer. +// GC uses this to skip an expensive dereference. #define CYC_POINTER_MASK (NOCYC_POINTER_MASK | ((u64)1 << 49)) #define POINTER_MASK (CYC_POINTER_MASK) +#if IS_32BIT + #define POINTER_PAYLOAD_MASK 0xFFFFFFFF +#else + #define POINTER_PAYLOAD_MASK 0xFFFFFFFFFFFF +#endif // 0111111111111101 #define TAGGED_INTEGER_MASK (TAGGED_VALUE_MASK | INTEGER_MASK) @@ -85,7 +91,10 @@ typedef struct IndexSlice { // 0010000000000000: Pool object bit. VM checks this bit to free host objects (allocated from embedded API). #define POOL_TYPE_MASK ((u32)0x20000000) -// 0100000000000000: Cyclable type bit. +// 0100000000000000: Cyclable type bit. Currently has two purposes. +// 1. Since heap pages don't segregate non-cyc from cyc, it's used +// to check which objects to visit. +// 2. Re-encapsulate an object pointer back to a Value. #define CYC_TYPE_MASK ((u32)0x40000000) // 1000000000000000: Mark bit. diff --git a/src/vm.zig b/src/vm.zig index 8294d7ef0..c9fa21590 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -1332,7 +1332,7 @@ pub const VM = struct { newFp[3] = Value{ .retFramePtr = framePtr }; // Copy closure value to new frame pointer local. - newFp[sym.inner.closure.local] = Value.initPtr(sym.inner.closure); + newFp[sym.inner.closure.local] = Value.initNoCycPtr(sym.inner.closure); return cy.fiber.PcSp{ .pc = cy.fiber.toVmPc(self, sym.inner.closure.funcPc), diff --git a/test/behavior_test.zig b/test/behavior_test.zig index 09120de8b..467a54f32 100644 --- a/test/behavior_test.zig +++ b/test/behavior_test.zig @@ -2254,7 +2254,7 @@ test "Local variable assignment." { try evalPass(.{ .preEval = struct { fn func(run: *VMrunner) void { - run.vm.fillUndefinedStackSpace(cy.Value.initPtr(null)); + run.vm.fillUndefinedStackSpace(cy.Value.initNoCycPtr(null)); } }.func, }, @@ -2268,7 +2268,7 @@ test "Local variable assignment." { try evalPass(.{ .preEval = struct { fn func(run: *VMrunner) void { - run.vm.fillUndefinedStackSpace(cy.Value.initPtr(null)); + run.vm.fillUndefinedStackSpace(cy.Value.initNoCycPtr(null)); } }.func, },