Skip to content

Commit

Permalink
Merge #149
Browse files Browse the repository at this point in the history
149: Implement the 'perform:WithArguments' and friends primitives. r=vext01 a=ltratt

This PR implements the various `perform:withArguments` primitives.

Some of the complexity in ef599ea is to make sure that yksom doesn't suffer from the same problems as JavaSOM (see SOM-st/SOM#43 (comment)). bf02eaa is then a simple general optimisation opened up by the first commit. I then factored out the common `perform` code in d58f3e8 which made implementing the other primitives relatively simple.

Co-authored-by: Laurence Tratt <[email protected]>
  • Loading branch information
bors[bot] and ltratt authored Jun 15, 2020
2 parents 3951251 + bc33d99 commit a943d26
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 54 deletions.
18 changes: 18 additions & 0 deletions lang_tests/perform_in_superclass.som
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"
VM:
status: success
stdout:
2
instance of perform_in_superclass
"

perform_in_superclass = (
run = (
self println.
self perform: #println inSuperclass: Object.
)

println = (
2 println.
)
)
20 changes: 20 additions & 0 deletions lang_tests/perform_in_superclass_with_args.som
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"
VM:
status: success
stdout:
2
4
instance of perform_in_superclass_with_args
"

perform_in_superclass_with_args = (
run = (
(self ifNil: 2) println.
(self perform: #ifNil: withArguments: #(3) inSuperclass: Object) println.
)

ifNil: aBlock = (
aBlock println.
^ 4.
)
)
20 changes: 20 additions & 0 deletions lang_tests/perform_witharguments.som
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"
VM:
status: success
stdout:
5
6
7
"

perform_witharguments = (
run = (
self perform: #f:b:c: withArguments: #(5 6 7).
)

f: a b: b c: c = (
a println.
b println.
c println.
)
)
20 changes: 20 additions & 0 deletions lang_tests/perform_witharguments_wrong.som
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"
VM:
status: error
stderr:
Traceback...
...
Tried passing 1 arguments to a function that requires 3.
"

perform_witharguments_wrong = (
run = (
self perform: #f:b:c: withArguments: #(5).
)

f: a b: b c: c = (
a println.
b println.
c println.
)
)
3 changes: 2 additions & 1 deletion src/lib/compiler/ast_to_instrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,10 @@ impl<'a, 'input> Compiler<'a, 'input> {
((pairs[0].0, name), args)
}
};
let args_len = args.len();
let body = self.c_body(vm, astmeth.span, (name.0, &name.1), args, &astmeth.body)?;
let sig = String_::new_sym(vm, name.1.clone());
let meth = Method::new(vm, sig, body);
let meth = Method::new(vm, sig, args_len, body);
Ok((name.1, Val::from_obj(vm, meth)))
}

Expand Down
110 changes: 79 additions & 31 deletions src/lib/vm/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,13 @@ impl VM {
if self.stack.remaining_capacity() < max_stack {
panic!("Not enough stack space to execute method.");
}
let nargs = args.len();
if args.len() != meth.num_params() {
panic!("Passed the wrong number of parameters.");
}
for a in args {
self.stack.push(a);
}
let frame = Frame::new(self, None, rcv, None, num_vars, nargs);
let frame = Frame::new(self, None, rcv, None, num_vars, meth.num_params());
self.frames.push(frame);
let r = self.exec_user(rcv, meth, bytecode_off);
self.frame_pop();
Expand All @@ -346,7 +348,7 @@ impl VM {
}
}

fn send_args_on_stack(&mut self, rcv: Val, method: Gc<Method>, nargs: usize) -> SendReturn {
fn send_args_on_stack(&mut self, rcv: Val, method: Gc<Method>) -> SendReturn {
match method.body {
MethodBody::Primitive(p) => self.exec_primitive(p, rcv),
MethodBody::User {
Expand All @@ -357,7 +359,7 @@ impl VM {
if self.stack.remaining_capacity() < max_stack {
panic!("Not enough stack space to execute method.");
}
let nframe = Frame::new(self, None, rcv, None, num_vars, nargs);
let nframe = Frame::new(self, None, rcv, None, num_vars, method.num_params());
self.frames.push(nframe);
let r = self.exec_user(rcv, method, bytecode_off);
self.frame_pop();
Expand Down Expand Up @@ -387,8 +389,8 @@ impl VM {
// Inside this function, one should use this macro instead of calling `send_args_on_stack`
// directly.
macro_rules! send_args_on_stack {
($send_rcv:expr, $send_method:expr, $nargs:expr) => {{
match self.send_args_on_stack($send_rcv, $send_method, $nargs) {
($send_rcv:expr, $send_method:expr) => {{
match self.send_args_on_stack($send_rcv, $send_method) {
SendReturn::ClosureReturn(d) => {
if d > 0 {
return SendReturn::ClosureReturn(d - 1);
Expand Down Expand Up @@ -448,7 +450,7 @@ impl VM {
let meth = stry!(cls.get_method(self, "escapedBlock:"));
let blk = self.current_frame().block.unwrap();
self.stack.push(blk);
send_args_on_stack!(rcv, meth, 1);
send_args_on_stack!(rcv, meth);
pc += 1;
}
Instr::Double(i) => {
Expand Down Expand Up @@ -479,7 +481,7 @@ impl VM {
};
let v = String_::new_sym(self, name);
self.stack.push(v);
send_args_on_stack!(rcv, meth, 1);
send_args_on_stack!(rcv, meth);
}
pc += 1;
}
Expand Down Expand Up @@ -545,7 +547,7 @@ impl VM {
let sig = String_::new_sym(self, (&*name).to_owned());
self.stack.push(sig);
self.stack.push(arr);
send_args_on_stack!(rcv, meth, 2);
send_args_on_stack!(rcv, meth);
pc += 1;
continue;
}
Expand All @@ -565,7 +567,7 @@ impl VM {

let len = self.stack.len() - nargs;
self.current_frame().set_sp(len);
send_args_on_stack!(send_rcv, meth, nargs);
send_args_on_stack!(send_rcv, meth);
pc += 1;
}
Instr::String(string_off) => {
Expand Down Expand Up @@ -897,30 +899,29 @@ impl VM {
}
Primitive::ObjectSize => unimplemented!(),
Primitive::Perform => {
let args_val = NormalArray::new(self, 0);
let sig_val = self.stack.pop();
let sig = stry!(String_::symbol_to_string_(self, sig_val));
let cls_val = rcv.get_class(self);
let cls = stry!(cls_val.downcast::<Class>(self));
match cls.get_method(self, sig.as_str()) {
Ok(m) => self.send_args_on_stack(rcv, m, 0),
Err(box VMError {
kind: VMErrorKind::UnknownMethod,
..
}) => {
let meth = cls
.get_method(self, "doesNotUnderstand:arguments:")
.unwrap();
self.stack.push(sig_val);
let arr = NormalArray::new(self, 0);
self.stack.push(arr);
self.send_args_on_stack(rcv, meth, 2)
}
Err(e) => return SendReturn::Err(e),
}
self.perform(rcv, cls_val, sig_val, args_val)
}
Primitive::PerformInSuperClass => {
let args_val = NormalArray::new(self, 0);
let cls_val = self.stack.pop();
let sig_val = self.stack.pop();
self.perform(rcv, cls_val, sig_val, args_val)
}
Primitive::PerformWithArguments => {
let args_val = self.stack.pop();
let sig_val = self.stack.pop();
let cls_val = rcv.get_class(self);
self.perform(rcv, cls_val, sig_val, args_val)
}
Primitive::PerformWithArgumentsInSuperClass => {
let cls_val = self.stack.pop();
let args_val = self.stack.pop();
let sig_val = self.stack.pop();
self.perform(rcv, cls_val, sig_val, args_val)
}
Primitive::PerformInSuperClass => unimplemented!(),
Primitive::PerformWithArguments => unimplemented!(),
Primitive::PerformWithArgumentsInSuperClass => unimplemented!(),
Primitive::PositiveInfinity => {
let dbl = Double::new(self, f64::INFINITY);
self.stack.push(dbl);
Expand Down Expand Up @@ -1034,6 +1035,53 @@ impl VM {
}
}

fn perform(&mut self, rcv: Val, cls_val: Val, sig_val: Val, args_val: Val) -> SendReturn {
macro_rules! stry {
($elem:expr) => {{
let e = $elem;
match e {
Ok(o) => o,
Err(e) => return SendReturn::Err(e),
}
}};
}

let args_tobj = stry!(args_val.tobj(self));
let args = stry!(args_tobj.to_array());
let sig = stry!(String_::symbol_to_string_(self, sig_val));
let cls = stry!(cls_val.downcast::<Class>(self));
match cls.get_method(self, sig.as_str()) {
Ok(m) => {
if args_tobj.length() != m.num_params() {
return SendReturn::Err(VMError::new(
self,
VMErrorKind::WrongNumberOfArgs {
wanted: m.num_params(),
got: args_tobj.length(),
},
));
}
for v in args.iter() {
self.stack.push(v);
}
self.send_args_on_stack(rcv, m)
}
Err(box VMError {
kind: VMErrorKind::UnknownMethod,
..
}) => {
let meth = cls
.get_method(self, "doesNotUnderstand:arguments:")
.unwrap();
self.stack.push(sig_val);
let arr = NormalArray::new(self, 0);
self.stack.push(arr);
self.send_args_on_stack(rcv, meth)
}
Err(e) => return SendReturn::Err(e),
}
}

fn current_frame(&mut self) -> &mut Frame {
debug_assert!(!self.frames.is_empty());
let frames_len = self.frames.len();
Expand Down
9 changes: 9 additions & 0 deletions src/lib/vm/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ pub enum VMErrorKind {
UnknownGlobal(String),
/// An unknown method.
UnknownMethod,
/// Tried calling a method with the wrong number of arguments.
WrongNumberOfArgs {
wanted: usize,
got: usize,
},
}

impl VMErrorKind {
Expand Down Expand Up @@ -234,6 +239,10 @@ impl VMErrorKind {
VMErrorKind::ShiftTooBig => Ok("Shift too big".to_owned()),
VMErrorKind::UnknownGlobal(name) => Ok(format!("Unknown global '{}'", name)),
VMErrorKind::UnknownMethod => Ok("Unknown method".to_owned()),
VMErrorKind::WrongNumberOfArgs { wanted, got } => Ok(format!(
"Tried passing {} arguments to a function that requires {}",
got, wanted
)),
}
}
}
Loading

0 comments on commit a943d26

Please sign in to comment.