Skip to content

Commit

Permalink
feat(limit): Recursive call limit
Browse files Browse the repository at this point in the history
closes #259
  • Loading branch information
giann committed Jan 23, 2024
1 parent f5b7d1e commit 4060d1a
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var value = <{
out result;
}
```
- `recursive_call_limit` build option limit recursive calls (default to 200)

## Changed
- Map type notation has changed from `{K, V}` to `{K: V}`. Similarly map expression with specified typed went from `{<K, V>, ...}` to `{<K: V>, ...}` (https://github.com/buzz-language/buzz/issues/253)
Expand Down
7 changes: 7 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,15 @@ const BuzzBuildOptions = struct {
jit: BuzzJITOptions,
target: Build.ResolvedTarget,
cycle_limit: ?u128,
recursive_call_limit: u32,

pub fn step(self: @This(), b: *Build) *Build.Module {
var options = b.addOptions();
options.addOption(@TypeOf(self.version), "version", self.version);
options.addOption(@TypeOf(self.sha), "sha", self.sha);
options.addOption(@TypeOf(self.mimalloc), "mimalloc", self.mimalloc);
options.addOption(@TypeOf(self.cycle_limit), "cycle_limit", self.cycle_limit);
options.addOption(@TypeOf(self.recursive_call_limit), "recursive_call_limit", self.recursive_call_limit);

self.debug.step(options);
self.gc.step(options);
Expand Down Expand Up @@ -147,6 +149,11 @@ pub fn build(b: *Build) !void {
"cycle_limit",
"Amount of bytecode (x 1000) the script is allowed to run (WARNING: this disables JIT compilation)",
) orelse null,
.recursive_call_limit = b.option(
u32,
"recursive_call_limit",
"Maximum depth for recursive calls",
) orelse 200,
.mimalloc = b.option(
bool,
"mimalloc",
Expand Down
19 changes: 19 additions & 0 deletions src/buzz_api.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,21 @@ export fn bz_context(ctx: *NativeCtx, closure_value: Value, new_ctx: *NativeCtx,
else
null;

// If recursive call, update counter
ctx.vm.current_fiber.recursive_count = if (closure != null and closure.?.function == ctx.vm.current_fiber.current_compiled_function)
ctx.vm.current_fiber.recursive_count + 1
else
0;

if (ctx.vm.current_fiber.recursive_count > BuildOptions.recursive_call_limit) {
ctx.vm.throw(
VM.Error.ReachedMaximumRecursiveCall,
(ctx.vm.gc.copyString("Maximum recursive call reached") catch @panic("Maximum recursive call reached")).toValue(),
null,
null,
) catch @panic("Maximum recursive call reached");
}

// If bound method, replace closure on the stack by the receiver
if (bound != null) {
(ctx.vm.current_fiber.stack_top - arg_count - 1)[0] = bound.?.receiver;
Expand All @@ -1192,6 +1207,10 @@ export fn bz_context(ctx: *NativeCtx, closure_value: Value, new_ctx: *NativeCtx,
ctx.vm.jit.?.compileFunction(ctx.vm.current_ast, closure.?) catch @panic("Failed compiling function");
}

if (closure) |cls| {
ctx.vm.current_fiber.current_compiled_function = cls.function;
}

return if (closure) |cls| cls.function.native_raw.? else native.?.native;
}

Expand Down
20 changes: 20 additions & 0 deletions src/vm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ pub const Fiber = struct {

frames: std.ArrayList(CallFrame),
frame_count: u64 = 0,
recursive_count: u32 = 0,
current_compiled_function: ?*ObjFunction = null,

stack: []Value,
stack_top: [*]Value,
Expand Down Expand Up @@ -333,6 +335,7 @@ pub const VM = struct {
BadNumber,
ReachedMaximumMemoryUsage,
ReachedMaximumCPUUsage,
ReachedMaximumRecursiveCall,
Custom, // TODO: remove when user can use this set directly in buzz code
} || Allocator.Error || std.fmt.BufPrintError;

Expand Down Expand Up @@ -3802,6 +3805,23 @@ pub const VM = struct {
fn call(self: *Self, closure: *ObjClosure, arg_count: u8, catch_value: ?Value, in_fiber: bool) JIT.Error!void {
closure.function.call_count += 1;

// If recursive call, update counter
self.current_fiber.recursive_count = if (self.currentFrame() != null and self.currentFrame().?.closure.function == closure.function)
self.current_fiber.recursive_count + 1
else
0;

if (self.current_fiber.recursive_count > BuildOptions.recursive_call_limit) {
try self.throw(
VM.Error.ReachedMaximumRecursiveCall,
(try self.gc.copyString("Maximum recursive call reached")).toValue(),
null,
null,
);

return;
}

var native = closure.function.native;
if (self.jit) |*jit| {
jit.call_count += 1;
Expand Down
8 changes: 8 additions & 0 deletions tests/compile_errors/027-recursive-call.buzz
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
| Maximum recursive call reached
fun eternal() > void {
eternal();
}

test "recursive call limit" {
eternal();
}

0 comments on commit 4060d1a

Please sign in to comment.