aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/jq/execute.zig
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-01-31 15:31:15 +0900
committernsfisis <nsfisis@gmail.com>2026-01-31 15:47:23 +0900
commit617ddc62aa4d3153850362526069b85bfaf5e59e (patch)
tree6940c577bbecdbf7856ef3c37ab43b256d33da59 /src/jq/execute.zig
parent6be43138338fbe4623c1cd62cf71138873af3a7a (diff)
downloadzgjq-617ddc62aa4d3153850362526069b85bfaf5e59e.tar.gz
zgjq-617ddc62aa4d3153850362526069b85bfaf5e59e.tar.zst
zgjq-617ddc62aa4d3153850362526069b85bfaf5e59e.zip
use reference counting to manage JSON value lifetime
Diffstat (limited to 'src/jq/execute.zig')
-rw-r--r--src/jq/execute.zig122
1 files changed, 83 insertions, 39 deletions
diff --git a/src/jq/execute.zig b/src/jq/execute.zig
index 5749b0b..3096be2 100644
--- a/src/jq/execute.zig
+++ b/src/jq/execute.zig
@@ -34,52 +34,58 @@ const ValueStack = struct {
}
pub fn pop(self: *Self) jv.Value {
+ // Values beyond the savepoint boundary belong to a previous segment
+ // that may be restored later. We must clone them because restore()
+ // expects those values to be still available.
+ if (self.stack.isBeyondSavepointBoundary()) {
+ return self.stack.pop().clone();
+ }
return self.stack.pop();
}
pub fn popInteger(self: *Self) ExecuteError!i64 {
const value = self.pop();
- return switch (value.kind()) {
- .integer => value.integer(),
+ return switch (value) {
+ .integer => |i| i,
else => error.InvalidType,
};
}
pub fn popNumber(self: *Self) ExecuteError!f64 {
const value = self.pop();
- return switch (value.kind()) {
- .integer => @floatFromInt(value.integer()),
- .float => value.float(),
+ return switch (value) {
+ .integer => |i| @floatFromInt(i),
+ .float => |f| f,
else => error.InvalidType,
};
}
pub fn popString(self: *Self) ExecuteError![]const u8 {
const value = self.pop();
- return switch (value.kind()) {
- .string => value.string(),
+ return switch (value) {
+ .string => |s| s,
else => error.InvalidType,
};
}
pub fn popArray(self: *Self) ExecuteError!jv.Array {
const value = self.pop();
- return switch (value.kind()) {
- .array => value.array(),
+ return switch (value) {
+ .array => |a| a,
else => error.InvalidType,
};
}
pub fn popObject(self: *Self) ExecuteError!jv.Object {
const value = self.pop();
- return switch (value.kind()) {
- .object => value.object(),
+ return switch (value) {
+ .object => |o| o,
else => error.InvalidType,
};
}
pub fn dup(self: *Self) !void {
- const top = self.stack.top().*;
+ const top = self.stack.top().*.clone();
try self.push(top);
}
@@ -96,13 +102,35 @@ const ValueStack = struct {
try self.stack.save();
}
- pub fn restore(self: *Self) void {
+ pub fn restore(self: *Self, allocator: std.mem.Allocator) void {
+ self.discardAllValuesAboveSavepoint(allocator);
self.stack.restore();
}
pub fn ensureSize(self: *Self, n: usize) bool {
return self.stack.ensureSize(n);
}
+
+ // Discard all values pushed above the current savepoint.
+ fn discardAllValuesAboveSavepoint(self: *Self, allocator: std.mem.Allocator) void {
+ if (self.stack.savepoints.items.len == 0) return;
+ const sp = self.stack.savepoints.items[self.stack.savepoints.items.len - 1];
+
+ var seg_idx = self.stack.active_segment_index;
+ while (seg_idx > sp.segment_index) : (seg_idx -= 1) {
+ const seg = &self.stack.segments.items[seg_idx];
+ for (seg.data.items) |item| {
+ item.deinit(allocator);
+ }
+ }
+
+ const seg = &self.stack.segments.items[sp.segment_index];
+ if (seg.data.items.len > sp.offset) {
+ for (seg.data.items[sp.offset..]) |item| {
+ item.deinit(allocator);
+ }
+ }
+ }
};
pub const Runtime = struct {
@@ -122,7 +150,7 @@ pub const Runtime = struct {
try constants.append(allocator, jv.Value.null);
try constants.append(allocator, jv.Value.false);
try constants.append(allocator, jv.Value.true);
- try constants.append(allocator, jv.Value.initArray(jv.Array.init(allocator)));
+ try constants.append(allocator, jv.Value.initArray(try jv.Array.init(allocator)));
return .{
.allocator = allocator,
@@ -136,12 +164,15 @@ pub const Runtime = struct {
}
pub fn deinit(self: *Self) void {
- for (self.variables.items) |*value| {
+ for (self.variables.items) |value| {
value.deinit(self.allocator);
}
self.variables.deinit(self.allocator);
- for (self.constants.items) |*value| {
- value.deinit(self.allocator);
+ for (self.constants.items) |value| {
+ switch (value) {
+ .string => |s| self.allocator.free(s),
+ else => value.deinit(self.allocator),
+ }
}
self.constants.deinit(self.allocator);
self.allocator.free(self.instrs);
@@ -171,7 +202,7 @@ pub const Runtime = struct {
}
pub fn start(self: *Self, input: jv.Value) !void {
- try self.values.push(input);
+ try self.values.push(input.clone());
}
pub fn next(self: *Self) !?jv.Value {
@@ -217,7 +248,7 @@ pub const Runtime = struct {
.pop => {
std.debug.assert(self.values.ensureSize(1));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
},
.subexp_begin => try self.values.dup(),
.subexp_end => try self.values.swap(),
@@ -226,7 +257,9 @@ pub const Runtime = struct {
const base = self.values.pop();
const key = self.values.pop();
- const result = try jv.ops.index(base, key);
+ const result = (try jv.ops.index(base, key)).clone();
+ base.deinit(self.allocator);
+ key.deinit(self.allocator);
try self.values.push(result);
},
.index_opt => {
@@ -234,13 +267,16 @@ pub const Runtime = struct {
const base = self.values.pop();
const key = self.values.pop();
- const result = jv.ops.index(base, key) catch jv.Value.null;
+ const idx_result: jv.Value = jv.ops.index(base, key) catch .null;
+ const result = idx_result.clone();
+ base.deinit(self.allocator);
+ key.deinit(self.allocator);
try self.values.push(result);
},
.add => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = try self.values.popInteger();
const rhs = try self.values.popInteger();
const result = lhs + rhs;
@@ -249,7 +285,7 @@ pub const Runtime = struct {
.sub => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = try self.values.popInteger();
const rhs = try self.values.popInteger();
const result = lhs - rhs;
@@ -258,7 +294,7 @@ pub const Runtime = struct {
.mul => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = try self.values.popInteger();
const rhs = try self.values.popInteger();
const result = lhs * rhs;
@@ -267,7 +303,7 @@ pub const Runtime = struct {
.div => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = try self.values.popInteger();
const rhs = try self.values.popInteger();
const result = @divTrunc(lhs, rhs);
@@ -276,7 +312,7 @@ pub const Runtime = struct {
.mod => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = try self.values.popInteger();
const rhs = try self.values.popInteger();
const result = @mod(lhs, rhs);
@@ -285,7 +321,7 @@ pub const Runtime = struct {
.eq => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = self.values.pop();
const rhs = self.values.pop();
const result = try jv.ops.compare(lhs, rhs, .eq);
@@ -294,7 +330,7 @@ pub const Runtime = struct {
.ne => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = self.values.pop();
const rhs = self.values.pop();
const result = try jv.ops.compare(lhs, rhs, .ne);
@@ -303,7 +339,7 @@ pub const Runtime = struct {
.lt => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = self.values.pop();
const rhs = self.values.pop();
const result = try jv.ops.compare(lhs, rhs, .lt);
@@ -312,7 +348,7 @@ pub const Runtime = struct {
.gt => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = self.values.pop();
const rhs = self.values.pop();
const result = try jv.ops.compare(lhs, rhs, .gt);
@@ -321,7 +357,7 @@ pub const Runtime = struct {
.le => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = self.values.pop();
const rhs = self.values.pop();
const result = try jv.ops.compare(lhs, rhs, .le);
@@ -330,7 +366,7 @@ pub const Runtime = struct {
.ge => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = self.values.pop();
const rhs = self.values.pop();
const result = try jv.ops.compare(lhs, rhs, .ge);
@@ -339,19 +375,25 @@ pub const Runtime = struct {
.alt => {
std.debug.assert(self.values.ensureSize(3));
- _ = self.values.pop();
+ self.values.pop().deinit(self.allocator);
const lhs = self.values.pop();
const rhs = self.values.pop();
- try self.values.push(if (jv.ops.isFalsy(lhs)) rhs else lhs);
+ if (jv.ops.isFalsy(lhs)) {
+ lhs.deinit(self.allocator);
+ try self.values.push(rhs);
+ } else {
+ rhs.deinit(self.allocator);
+ try self.values.push(lhs);
+ }
},
.@"const" => |idx| {
std.debug.assert(self.values.ensureSize(1));
- _ = self.values.pop();
- try self.values.push(self.constants.items[@intFromEnum(idx)]);
+ self.values.pop().deinit(self.allocator);
+ try self.values.push(self.constants.items[@intFromEnum(idx)].clone());
},
.load => |idx| {
- try self.values.push(self.variables.items[@intFromEnum(idx)]);
+ try self.values.push(self.variables.items[@intFromEnum(idx)].clone());
},
.store => |idx| {
std.debug.assert(self.values.ensureSize(1));
@@ -360,12 +402,14 @@ pub const Runtime = struct {
while (self.variables.items.len <= @intFromEnum(idx)) {
try self.variables.append(self.allocator, jv.Value.null);
}
+ self.variables.items[@intFromEnum(idx)].deinit(self.allocator);
self.variables.items[@intFromEnum(idx)] = self.values.pop();
},
.append => |idx| {
std.debug.assert(self.values.ensureSize(1));
- try self.variables.items[@intFromEnum(idx)].arrayAppend(self.values.pop());
+ const var_ptr = &self.variables.items[@intFromEnum(idx)];
+ try var_ptr.arrayAppend(self.allocator, self.values.pop());
},
}
}
@@ -381,7 +425,7 @@ pub const Runtime = struct {
fn restore_stack(self: *Self) bool {
if (self.forks.pop()) |target_pc| {
self.pc = target_pc;
- self.values.restore();
+ self.values.restore(self.allocator);
return true;
}
return false;