optimizing invocations

This commit is contained in:
Patrick MARIE 2024-09-01 11:11:29 +02:00
parent 0bc5f495b9
commit 22ed27a931
6 changed files with 67 additions and 2 deletions

View File

@ -0,0 +1,17 @@
class Oops {
init() {
fun f() {
print "not a method";
return "returned value from f";
}
this.field = f;
}
blah() {
return "returned value from blah";
}
}
var oops = Oops();
print oops.field();
print oops.blah();

View File

@ -154,6 +154,7 @@ pub const Chunk = struct {
@intFromEnum(OpCode.OP_METHOD) => return utils.constant_instruction("OP_METHOD", self, offset), @intFromEnum(OpCode.OP_METHOD) => return utils.constant_instruction("OP_METHOD", self, offset),
@intFromEnum(OpCode.OP_INDEX_GET) => return utils.simple_instruction("OP_INDEX_GET", offset), @intFromEnum(OpCode.OP_INDEX_GET) => return utils.simple_instruction("OP_INDEX_GET", offset),
@intFromEnum(OpCode.OP_INDEX_SET) => return utils.simple_instruction("OP_INDEX_SET", offset), @intFromEnum(OpCode.OP_INDEX_SET) => return utils.simple_instruction("OP_INDEX_SET", offset),
@intFromEnum(OpCode.OP_INVOKE) => return utils.invoke_instruction("OP_INVOKE", self, offset),
else => { else => {
debug.print("unknown opcode {d}\n", .{instruction}); debug.print("unknown opcode {d}\n", .{instruction});
return offset + 1; return offset + 1;

View File

@ -939,6 +939,10 @@ pub const Parser = struct {
if (can_assign and self.match(TokenType.EQUAL)) { if (can_assign and self.match(TokenType.EQUAL)) {
try self.expression(); try self.expression();
try self.emit_bytes(@intFromEnum(OpCode.OP_SET_PROPERTY), name); try self.emit_bytes(@intFromEnum(OpCode.OP_SET_PROPERTY), name);
} else if (self.match(TokenType.LEFT_PAREN)) {
const arg_count = try self.argument_list();
try self.emit_bytes(@intFromEnum(OpCode.OP_INVOKE), name);
try self.emit_byte(@intCast(arg_count));
} else { } else {
try self.emit_bytes(@intFromEnum(OpCode.OP_GET_PROPERTY), name); try self.emit_bytes(@intFromEnum(OpCode.OP_GET_PROPERTY), name);
} }

View File

@ -34,4 +34,5 @@ pub const OpCode = enum(u8) {
OP_METHOD, OP_METHOD,
OP_INDEX_SET, OP_INDEX_SET,
OP_INDEX_GET, OP_INDEX_GET,
OP_INVOKE,
}; };

View File

@ -55,3 +55,14 @@ pub fn identifiers_equals(a: Token, b: Token) bool {
return std.mem.eql(u8, a.start[0..a.length], b.start[0..b.length]); return std.mem.eql(u8, a.start[0..a.length], b.start[0..b.length]);
} }
pub fn invoke_instruction(opcode_name: []const u8, chunk: Chunk, offset: usize) usize {
const constant = chunk.code[offset + 1];
const arg_count = chunk.code[offset + 2];
std.debug.print("{s:<16} ({d} args) {d:4} '", .{ opcode_name, arg_count, constant });
chunk.constants.values[constant].print();
std.debug.print("'\n", .{});
return offset + 3;
}

View File

@ -372,6 +372,13 @@ pub const VM = struct {
_ = try self.push(Value.obj_val(&self.take_string(str).obj)); _ = try self.push(Value.obj_val(&self.take_string(str).obj));
}, },
@intFromEnum(OpCode.OP_INVOKE) => {
const method = self.read_constant().as_string();
const arg_count = self.read_byte();
if (!self.invoke(method, arg_count)) {
return InterpretResult.RUNTIME_ERROR;
}
},
else => { else => {
debug.print("Invalid instruction: {d}\n", .{instruction}); debug.print("Invalid instruction: {d}\n", .{instruction});
return InterpretResult.RUNTIME_ERROR; return InterpretResult.RUNTIME_ERROR;
@ -586,9 +593,7 @@ pub const VM = struct {
if (arg_count != closure.function.arity) { if (arg_count != closure.function.arity) {
const err_msg = std.fmt.allocPrint(self.allocator, "Expected {d} arguments but got {d}.", .{ closure.function.arity, arg_count }) catch unreachable; const err_msg = std.fmt.allocPrint(self.allocator, "Expected {d} arguments but got {d}.", .{ closure.function.arity, arg_count }) catch unreachable;
defer self.allocator.free(err_msg); defer self.allocator.free(err_msg);
self.runtime_error(err_msg); self.runtime_error(err_msg);
return false; return false;
} }
@ -659,4 +664,30 @@ pub const VM = struct {
_ = class.methods.set(name, method); _ = class.methods.set(name, method);
_ = self.pop(); _ = self.pop();
} }
fn invoke(self: *VM, name: *Obj.String, arg_count: usize) bool {
const receiver = self.peek(arg_count);
const instance = receiver.as_obj().as_instance();
var value = Value.nil_val();
if (instance.fields.get(name, &value)) {
self.stack[self.stack_top - arg_count - 1] = value;
return self.call_value(value, arg_count);
}
return self.invoke_from_class(instance.class, name, arg_count);
}
fn invoke_from_class(self: *VM, class: *Obj.Class, name: *Obj.String, arg_count: usize) bool {
var method = Value.nil_val();
if (!class.methods.get(name, &method)) {
const err_msg = std.fmt.allocPrint(self.allocator, "Undefined property '{s}'.", .{name.chars}) catch unreachable;
defer self.allocator.free(err_msg);
self.runtime_error(err_msg);
return false;
}
return self.call(method.as_obj().as_closure(), arg_count);
}
}; };