Skip to content

Commit 1c504b5

Browse files
authored
Merge pull request #6 from schultyy/implement-stackframes
Implement stack frames
2 parents 063a89d + 59cff6b commit 1c504b5

File tree

2 files changed

+136
-68
lines changed

2 files changed

+136
-68
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/vm.rs

Lines changed: 135 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub enum VMError {
3737
MissingFunctionName,
3838
MissingContext,
3939
InvalidInstruction(u8),
40+
MissingStackFrame,
4041
}
4142

4243
impl std::error::Error for VMError {}
@@ -65,6 +66,7 @@ impl std::fmt::Display for VMError {
6566
VMError::InvalidInstruction(instruction) => {
6667
write!(f, "Invalid instruction: {}", instruction)
6768
}
69+
VMError::MissingStackFrame => write!(f, "Missing stack frame"),
6870
}
6971
}
7072
}
@@ -114,7 +116,7 @@ const LENGTH_OFFSET: usize = std::mem::size_of::<usize>();
114116

115117
pub struct VM {
116118
code: Vec<u8>,
117-
stack: Vec<StackValue>,
119+
stack: Vec<Vec<StackValue>>,
118120
vars: HashMap<String, StackValue>,
119121
label_jump_map: HashMap<String, usize>,
120122
label_index_map: HashMap<usize, String>,
@@ -164,7 +166,7 @@ impl VM {
164166
code,
165167
label_jump_map,
166168
label_index_map,
167-
stack: Vec::new(),
169+
stack: vec![Vec::new()],
168170
vars: HashMap::new(),
169171
ip: 0,
170172
print_tx,
@@ -238,6 +240,7 @@ impl VM {
238240

239241
async fn handle_local_call(&mut self, label: String) -> Result<(), VMError> {
240242
self.return_addresses.push(self.ip);
243+
self.stack.push(Vec::new());
241244
self.ip = *self
242245
.label_jump_map
243246
.get(&label)
@@ -254,31 +257,38 @@ impl VM {
254257
(start, end, length)
255258
}
256259

260+
fn current_stackframe(&mut self) -> Result<&mut Vec<StackValue>, VMError> {
261+
self.stack.last_mut().ok_or(VMError::MissingStackFrame)
262+
}
263+
257264
async fn execute_instruction(&mut self) -> Result<(), VMError> {
258265
let instruction = self.code[self.ip];
259266
match instruction {
260267
PUSH_STRING_CODE => {
261268
let (_start, end, str_len) = self.extract_length();
262269
let str = &self.code[end..end + str_len];
263270
let str = String::from_utf8(str.to_vec()).unwrap();
264-
self.stack.push(StackValue::String(str));
271+
self.current_stackframe()?.push(StackValue::String(str));
265272
self.ip = end + str_len;
266273
}
267274
PUSH_INT_CODE => {
268275
let (_start, end, int_len) = self.extract_length();
269276
let int = &self.code[end..end + int_len];
270277
let int = u64::from_le_bytes(int.try_into().unwrap());
271-
self.stack.push(StackValue::Int(int));
278+
self.current_stackframe()?.push(StackValue::Int(int));
272279
self.ip = end + int_len;
273280
}
274281
POP_CODE => {
275282
self.stack.pop();
276283
self.ip += 1;
277284
}
278285
DEC_CODE => {
279-
let top = self.stack.pop().ok_or(VMError::StackUnderflow)?;
286+
let top = self
287+
.current_stackframe()?
288+
.pop()
289+
.ok_or(VMError::StackUnderflow)?;
280290
match top {
281-
StackValue::Int(n) => self.stack.push(StackValue::Int(n - 1)),
291+
StackValue::Int(n) => self.current_stackframe()?.push(StackValue::Int(n - 1)),
282292
_ => return Err(VMError::InvalidStackValue),
283293
}
284294
self.ip += 1;
@@ -287,7 +297,10 @@ impl VM {
287297
let (_start, end, jump_to_label_len) = self.extract_length();
288298
let jump_to_label_bytes = &self.code[end..end + jump_to_label_len];
289299
let jump_to_label = String::from_utf8(jump_to_label_bytes.to_vec()).unwrap();
290-
let top = self.stack.pop().ok_or(VMError::StackUnderflow)?;
300+
let top = self
301+
.current_stackframe()?
302+
.pop()
303+
.ok_or(VMError::StackUnderflow)?;
291304
match top {
292305
StackValue::Int(n) => {
293306
if n == 0 {
@@ -307,7 +320,10 @@ impl VM {
307320
self.ip = end + label_len;
308321
}
309322
STDOUT_CODE => {
310-
let str = self.stack.pop().ok_or(VMError::StackUnderflow)?;
323+
let str = self
324+
.current_stackframe()?
325+
.pop()
326+
.ok_or(VMError::StackUnderflow)?;
311327
match str {
312328
StackValue::String(s) => self
313329
.print_tx
@@ -323,7 +339,10 @@ impl VM {
323339
self.ip += 1;
324340
}
325341
STDERR_CODE => {
326-
let top = self.stack.pop().ok_or(VMError::StackUnderflow)?;
342+
let top = self
343+
.current_stackframe()?
344+
.pop()
345+
.ok_or(VMError::StackUnderflow)?;
327346
match top {
328347
StackValue::String(s) => {
329348
self.print_tx
@@ -364,13 +383,18 @@ impl VM {
364383
let value = self
365384
.vars
366385
.get(&key)
367-
.ok_or(VMError::MissingVar(key.clone()))?;
368-
self.stack.push(value.clone());
386+
.ok_or(VMError::MissingVar(key.clone()))?
387+
.clone();
388+
self.current_stackframe()?.push(value);
369389
self.ip = end + key_len;
370390
}
371391
DUP_CODE => {
372-
let top = self.stack.last().ok_or(VMError::StackUnderflow)?;
373-
self.stack.push(top.clone());
392+
let top = self
393+
.current_stackframe()?
394+
.last()
395+
.ok_or(VMError::StackUnderflow)?
396+
.clone();
397+
self.current_stackframe()?.push(top);
374398
self.ip += 1;
375399
}
376400
JUMP_CODE => {
@@ -384,8 +408,14 @@ impl VM {
384408
.to_owned();
385409
}
386410
PRINTF_CODE => {
387-
let var = self.stack.pop().ok_or(VMError::StackUnderflow)?;
388-
let template = self.stack.pop().ok_or(VMError::StackUnderflow)?;
411+
let var = self
412+
.current_stackframe()?
413+
.pop()
414+
.ok_or(VMError::StackUnderflow)?;
415+
let template = self
416+
.current_stackframe()?
417+
.pop()
418+
.ok_or(VMError::StackUnderflow)?;
389419
let template = match template {
390420
StackValue::String(s) => s,
391421
_ => return Err(VMError::InvalidStackValue),
@@ -397,70 +427,79 @@ impl VM {
397427
_ => return Err(VMError::InvalidStackValue),
398428
};
399429
let formatted = template.replace("%s", &var);
400-
self.stack.push(StackValue::String(formatted));
430+
self.current_stackframe()?
431+
.push(StackValue::String(formatted));
401432
} else if template.contains("%d") {
402433
let var = match var {
403434
StackValue::Int(i) => i,
404435
_ => return Err(VMError::InvalidStackValue),
405436
};
406437
let formatted = template.replace("%d", &var.to_string());
407-
self.stack.push(StackValue::String(formatted));
438+
self.current_stackframe()?
439+
.push(StackValue::String(formatted));
408440
} else {
409441
return Err(VMError::InvalidTemplate(template.clone()));
410442
}
411443
self.ip += 1;
412444
}
413445
REMOTE_CALL_CODE => {
414-
if let Some(remote_call_tx) = self.remote_call_tx.as_ref() {
415-
let remote_method = self.stack.pop().ok_or(VMError::StackUnderflow)?;
416-
let remote_service = self.stack.pop().ok_or(VMError::StackUnderflow)?;
417-
let local_function_name = self
418-
.find_current_function_name()
419-
.ok_or(VMError::MissingFunctionName)?;
420-
let mut cx = None;
421-
422-
if let Some(tracer_provider) = self.tracer.as_ref() {
423-
if let Some(otel_cx) = self.otel_context.as_ref() {
424-
let tracer = tracer_provider.tracer(self.service_name.clone());
425-
426-
let span = tracer
427-
.span_builder(format!(
428-
"{}/{}",
429-
self.service_name, local_function_name
430-
))
431-
.with_kind(SpanKind::Client)
432-
.with_attributes(vec![KeyValue::new(
433-
SERVICE_NAME,
434-
self.service_name.clone(),
435-
)])
436-
.start(&tracer);
437-
438-
cx = Some(otel_cx.with_span(span));
439-
let mut metadata = HashMap::new();
440-
let propagator = TraceContextPropagator::new();
441-
propagator.inject_context(&cx.clone().unwrap(), &mut metadata);
442-
} else {
443-
return Err(VMError::MissingContext);
444-
}
445-
}
446-
447-
remote_call_tx
448-
.send(ServiceMessage::Call {
449-
to: remote_service.to_string(),
450-
function: remote_method.to_string(),
451-
context: cx.clone().unwrap_or(opentelemetry::Context::current()),
452-
})
453-
.await
454-
.map_err(|e| VMError::RemoteCallError(e.to_string()))?;
446+
let remote_call_tx = self
447+
.remote_call_tx
448+
.as_ref()
449+
.ok_or(VMError::RemoteCallError(
450+
"Remote call tx not set".to_string(),
451+
))?
452+
.clone();
453+
454+
let remote_method = self
455+
.current_stackframe()?
456+
.pop()
457+
.ok_or(VMError::StackUnderflow)?
458+
.clone();
459+
let remote_service = self
460+
.current_stackframe()?
461+
.pop()
462+
.ok_or(VMError::StackUnderflow)?
463+
.clone();
464+
let local_function_name = self
465+
.find_current_function_name()
466+
.ok_or(VMError::MissingFunctionName)?;
467+
let mut cx = None;
455468

456-
if let Some(cx) = cx {
457-
cx.span()
458-
.set_attributes(vec![KeyValue::new("response", "OK")]);
469+
if let Some(tracer_provider) = self.tracer.as_ref() {
470+
if let Some(otel_cx) = self.otel_context.as_ref() {
471+
let tracer = tracer_provider.tracer(self.service_name.clone());
472+
473+
let span = tracer
474+
.span_builder(format!("{}/{}", self.service_name, local_function_name))
475+
.with_kind(SpanKind::Client)
476+
.with_attributes(vec![KeyValue::new(
477+
SERVICE_NAME,
478+
self.service_name.clone(),
479+
)])
480+
.start(&tracer);
481+
482+
cx = Some(otel_cx.with_span(span));
483+
let mut metadata = HashMap::new();
484+
let propagator = TraceContextPropagator::new();
485+
propagator.inject_context(&cx.clone().unwrap(), &mut metadata);
486+
} else {
487+
return Err(VMError::MissingContext);
459488
}
460-
} else {
461-
return Err(VMError::RemoteCallError(
462-
"Remote call tx not set".to_string(),
463-
));
489+
}
490+
491+
remote_call_tx
492+
.send(ServiceMessage::Call {
493+
to: remote_service.to_string(),
494+
function: remote_method.to_string(),
495+
context: cx.clone().unwrap_or(opentelemetry::Context::current()),
496+
})
497+
.await
498+
.map_err(|e| VMError::RemoteCallError(e.to_string()))?;
499+
500+
if let Some(cx) = cx {
501+
cx.span()
502+
.set_attributes(vec![KeyValue::new("response", "OK")]);
464503
}
465504
self.ip += 1;
466505
}
@@ -502,6 +541,7 @@ impl VM {
502541
}
503542
RET_CODE => {
504543
self.ip = self.return_addresses.pop().unwrap();
544+
self.stack.pop();
505545
}
506546
_ => {
507547
return Err(VMError::InvalidInstruction(instruction));
@@ -735,7 +775,7 @@ mod tests {
735775
Instruction::Stdout,
736776
Instruction::Stdout,
737777
];
738-
let (print_tx, mut print_rx) = mpsc::channel(10);
778+
let (print_tx, print_rx) = mpsc::channel(10);
739779
let mut vm = VM::new(code.clone(), "test", print_tx).with_max_execution_counter(5);
740780
match vm.run().await {
741781
Ok(_) => {
@@ -827,7 +867,7 @@ mod tests {
827867
Instruction::Printf,
828868
Instruction::Stdout,
829869
];
830-
let (print_tx, print_rx) = mpsc::channel(10);
870+
let (print_tx, _print_rx) = mpsc::channel(10);
831871
let mut vm = VM::new(code.clone(), "test", print_tx).with_max_execution_counter(4);
832872
match vm.run().await {
833873
Ok(_) => {
@@ -1054,4 +1094,32 @@ mod tests {
10541094
}
10551095
}
10561096
}
1097+
1098+
#[tokio::test]
1099+
async fn test_vm_creates_new_stackframe_on_call() {
1100+
let code = vec![
1101+
Instruction::Jump("main".to_string()),
1102+
Instruction::Label("start_function".to_string()),
1103+
Instruction::Stdout,
1104+
Instruction::Ret,
1105+
Instruction::Label("end_function".to_string()),
1106+
Instruction::Label("main".to_string()),
1107+
Instruction::Push(StackValue::String("world".to_string())),
1108+
Instruction::Call("start_function".to_string()),
1109+
Instruction::Stdout,
1110+
];
1111+
1112+
let (print_tx, print_rx) = mpsc::channel(5);
1113+
let mut vm = VM::new(code.clone(), "test", print_tx).with_max_execution_counter(15);
1114+
1115+
match vm.run().await {
1116+
Ok(_) => {
1117+
assert!(false, "VM should have failed because of missing stackframe");
1118+
}
1119+
Err(e) => {
1120+
assert_eq!(e, VMError::StackUnderflow);
1121+
assert_eq!(print_rx.len(), 0);
1122+
}
1123+
}
1124+
}
10571125
}

0 commit comments

Comments
 (0)