Skip to content

Commit

Permalink
fix(vm): Don't introduce an Unknown scope when calling functions
Browse files Browse the repository at this point in the history
The extra `Unknown` scope would cause the `execute` loop to return early which would cause the calling extern function (in this case `force`) to not have its own frame at the top of the stack when it regained control.
  • Loading branch information
Marwes committed Jan 28, 2017
1 parent de79a27 commit f4f81fd
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 68 deletions.
2 changes: 1 addition & 1 deletion tests/pass/thread.glu
Expand Up @@ -17,7 +17,7 @@ let thread = spawn (\_ ->
send sender 0
yield ()
send sender 1
yield ()
()
)
resume thread

Expand Down
3 changes: 1 addition & 2 deletions vm/src/api.rs
Expand Up @@ -2,7 +2,7 @@
use {Variants, Error, Result};
use gc::{DataDef, Gc, Traverseable, Move};
use base::symbol::Symbol;
use stack::{State, StackFrame};
use stack::StackFrame;
use vm::{self, Thread, Status, RootStr, RootedValue, Root};
use value::{ArrayRepr, Cloner, DataStruct, ExternFunction, GcStr, Value, ValueArray, Def};
use thread::{self, Context, OwnedContext, RootedThread};
Expand Down Expand Up @@ -1841,7 +1841,6 @@ impl<'vm, T, $($args,)* R> Function<T, fn($($args),*) -> R>
fn call_first(&'vm self $(, $args: $args)*) -> Result<Async<Option<OwnedContext<'vm>>>> {
let vm = self.value.vm();
let mut context = vm.context();
StackFrame::current(&mut context.stack).enter_scope(0, State::Unknown);
context.stack.push(*self.value);
$(
$args.push(&vm, &mut context)?;
Expand Down
84 changes: 26 additions & 58 deletions vm/src/lazy.rs
@@ -1,19 +1,18 @@
use std::any::Any;
use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
use std::sync::Mutex;

use futures::Async;

use base::types;
use base::types::{Type, ArcType};
use gc::{Gc, GcPtr, Move, Traverseable};
use api::{Pushable, Generic, OpaqueValue, Userdata, VmType, RuntimeResult};
use api::{FunctionRef, Getable, OpaqueValue, Userdata, VmType, WithVM, RuntimeResult};
use api::Generic;
use api::generic::A;
use vm::{Status, Thread};
use {Error, Result};
use value::{Value, Cloner};
use stack::StackFrame;
use vm::Thread;
use {Error, Result, Variants};
use value::{Cloner, Value};
use thread::ThreadInternal;

pub struct Lazy<T> {
Expand Down Expand Up @@ -76,57 +75,28 @@ impl<T> VmType for Lazy<T>
}
}

extern "C" fn force(vm: &Thread) -> Status {
let mut context = vm.context();
let value = StackFrame::current(&mut context.stack)[0];
match value {
Value::Userdata(lazy) => {
let lazy = lazy.downcast_ref::<Lazy<A>>().expect("Lazy");
let value = *lazy.value.lock().unwrap();
match value {
Lazy_::Blackhole => {
let result: RuntimeResult<(), _> = RuntimeResult::Panic("<<loop>>");
result.status_push(vm, &mut context)
}
Lazy_::Thunk(value) => {
context.stack.push(value);
context.stack.push(Value::Int(0));
*lazy.value.lock().unwrap() = Lazy_::Blackhole;
let result = vm.call_function(context, 1);
match result {
Ok(Async::Ready(None)) => panic!("Expected stack"),
Ok(Async::Ready(Some(mut context))) => {
let mut stack = StackFrame::current(&mut context.stack);
let value = stack.pop();
while stack.len() > 1 {
stack.pop();
}
*lazy.value.lock().unwrap() = Lazy_::Value(value);
stack.push(value);
Status::Ok
}
Ok(Async::NotReady) => {
let mut context = vm.context();
let err = "Evaluating a lazy value cannot be done asynchronously at \
the moment";
let result = Value::String(context.alloc_ignore_limit(&err[..]));
context.stack.push(result);
Status::Error
}
Err(err) => {
let mut context = vm.context();
let result: RuntimeResult<(), _> = RuntimeResult::Panic(err);
result.status_push(vm, &mut context)
}
}
}
Lazy_::Value(value) => {
StackFrame::current(&mut context.stack)[0] = value;
Status::Ok
fn force(WithVM { vm, value: lazy }: WithVM<&Lazy<A>>)
-> RuntimeResult<Generic<A>, Cow<'static, str>> {
let mut lazy_lock = lazy.value.lock().unwrap();
match *lazy_lock {
Lazy_::Blackhole => RuntimeResult::Panic("<<loop>>".into()),
Lazy_::Thunk(value) => {
*lazy_lock = Lazy_::Blackhole;
let mut function =
unsafe {
FunctionRef::<fn(()) -> Generic<A>>::from_value(vm, Variants::new(&value))
.unwrap()
};
drop(lazy_lock);
match function.call(()) {
Ok(value) => {
*lazy.value.lock().unwrap() = Lazy_::Value(value.0);
RuntimeResult::Return(value)
}
Err(err) => RuntimeResult::Panic(format!("{}", err).into()),
}
}
x => panic!("Expected lazy got {:?}", x),
Lazy_::Value(value) => RuntimeResult::Return(Generic::from(value)),
}
}

Expand All @@ -140,9 +110,7 @@ fn lazy(f: OpaqueValue<&Thread, fn(()) -> A>) -> Lazy<A> {
}

pub fn load<'vm>(vm: &'vm Thread) -> Result<()> {
use api::primitive;
vm.define_global("lazy", primitive!(1 lazy))?;
vm.define_global("force",
primitive::<fn(&'vm Lazy<A>) -> Generic<A>>("force", ::lazy::force))?;
vm.define_global("force", primitive!(1 force))?;
Ok(())
}
7 changes: 0 additions & 7 deletions vm/src/thread.rs
Expand Up @@ -953,15 +953,8 @@ impl<'b> OwnedContext<'b> {
}

fn execute(self) -> Result<Async<Option<OwnedContext<'b>>>> {
let start_frame = self.stack.get_frames().len();
let mut maybe_context = Some(self);
while let Some(mut context) = maybe_context {
// If the starting frame has been removed (because it is finished) then we return
// control back to the caller to continue processing any frames above the start
if context.stack.get_frames().len() < start_frame {
maybe_context = Some(context);
break;
}
debug!("STACK\n{:?}", context.stack.get_frames());
let state = context.borrow_mut().stack.frame.state;

Expand Down

0 comments on commit f4f81fd

Please sign in to comment.