Skip to content

Commit 6d9a4eb

Browse files
committed
fix: foreach sentinel end was not done in JIT code
1 parent fb54e7b commit 6d9a4eb

5 files changed

Lines changed: 23 additions & 5 deletions

File tree

‎src/Jit.zig‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5398,6 +5398,15 @@ fn generateForEach(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t {
53985398
const key_ptr = try self.buildStackPtr(2);
53995399
const value_ptr = try self.buildStackPtr(1);
54005400

5401+
if (self.state.?.ast_node != node and iterable_type_def.?.def_type == .Map) {
5402+
// Mirror bytecode foreach setup: use the sentinel as the internal map
5403+
// iteration marker so a real `null` key does not restart the loop.
5404+
self.MOV(
5405+
try self.LOAD(key_ptr),
5406+
m.MIR_new_uint_op(self.ctx, Value.Sentinel.val),
5407+
);
5408+
}
5409+
54015410
const cond_label = m.MIR_new_label(self.ctx);
54025411
const out_label = m.MIR_new_label(self.ctx);
54035412

‎src/buzz_api.zig‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1473,7 +1473,9 @@ export fn bz_rangeNext(range_value: v.Value, index_slot: v.Value) callconv(.c) v
14731473

14741474
export fn bz_mapNext(map_value: v.Value, key: *v.Value) callconv(.c) v.Value {
14751475
const map = o.ObjMap.cast(map_value.obj()).?;
1476-
const next_key = map.rawNext(if (key.isNull()) v.Value.Sentinel else key.*);
1476+
// Map iteration uses sentinel as the internal start/end marker so real
1477+
// `null` keys remain iterable for both bytecode and JIT/native callers.
1478+
const next_key = map.rawNext(key.*);
14771479
key.* = next_key;
14781480

14791481
if (!next_key.isSentinel()) {

‎src/lib/buzz_api.zig‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const TagNull: Tag = 2;
3636
const TagVoid: Tag = 3;
3737
const TagObj: Tag = 4;
3838
const TagError: Tag = 5;
39+
const TagSentinel: Tag = 6;
3940

4041
/// Most significant bit.
4142
const SignMask: u64 = 1 << 63;
@@ -55,6 +56,7 @@ const IntegerMask: u64 = TaggedValueMask | (@as(u64, TagInteger) << 49);
5556
const NullMask: u64 = TaggedValueMask | (@as(u64, TagNull) << 32);
5657
const VoidMask: u64 = TaggedValueMask | (@as(u64, TagVoid) << 32);
5758
const ErrorMask: u64 = TaggedValueMask | (@as(u64, TagError) << 32);
59+
const SentinelMask: u64 = TaggedValueMask | (@as(u64, TagSentinel) << 32);
5860

5961
const TagMask: u32 = (1 << 3) - 1;
6062
const TaggedPrimitiveMask = TaggedValueMask | (@as(u64, TagMask) << 32) | IntegerMask;
@@ -68,6 +70,7 @@ pub const Value = extern struct {
6870
pub const False = Value{ .val = FalseMask };
6971
// We only need this so that an NativeFn can see the error returned by its raw function
7072
pub const Error = Value{ .val = ErrorMask };
73+
pub const Sentinel = Value{ .val = SentinelMask };
7174

7275
pub fn fromBoolean(val: bool) Value {
7376
return if (val) True else False;
@@ -121,6 +124,10 @@ pub const Value = extern struct {
121124
return self.val == ErrorMask;
122125
}
123126

127+
pub fn isSentinel(self: Value) bool {
128+
return self.val == SentinelMask;
129+
}
130+
124131
pub fn boolean(self: Value) bool {
125132
return self.val == TrueMask;
126133
}

‎src/lib/buzz_http.zig‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ fn innerHttpClientSend(ctx: *api.NativeCtx) !c_int {
109109
const header_values = ctx.vm.bz_peek(1);
110110
var headers = std.ArrayList(std.http.Header).empty;
111111
errdefer if (!request_initialized) headers.deinit(api.VM.allocator);
112-
var next_header_key = api.Value.Null;
112+
var next_header_key = api.Value.Sentinel;
113113
var next_header_value = header_values.bz_mapNext(&next_header_key);
114-
while (next_header_key.val != api.Value.Null.val) : (next_header_value = header_values.bz_mapNext(&next_header_key)) {
114+
while (next_header_key.val != api.Value.Sentinel.val) : (next_header_value = header_values.bz_mapNext(&next_header_key)) {
115115
var key_len: usize = 0;
116116
const key = next_header_key.bz_valueToString(&key_len) orelse return error.OutOfMemory;
117117
var value_len: usize = 0;

‎src/obj.zig‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3510,12 +3510,12 @@ pub const ObjMap = struct {
35103510

35113511
// Sentinel is the internal start/end marker so real `null` keys remain iterable.
35123512
if (key.isSentinel()) {
3513-
return if (map_keys.len > 0) map_keys[0] else Value.Sentinel;
3513+
return if (map_keys.len > 0) map_keys[0] else .Sentinel;
35143514
}
35153515

35163516
const index: usize = self.map.getIndex(key).?;
35173517

3518-
return if (index < map_keys.len - 1) map_keys[index + 1] else Value.Sentinel;
3518+
return if (index < map_keys.len - 1) map_keys[index + 1] else .Sentinel;
35193519
}
35203520

35213521
pub fn deinit(self: *Self, allocator: Allocator) void {

0 commit comments

Comments
 (0)