summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2024-01-01 19:41:30 -0800
committerJesse Morgan <jesse@jesterpm.net>2024-01-01 19:41:30 -0800
commit40ce26ce1e97cf46c598c7a510fa9c8a2162b5fa (patch)
tree05f78878174c7bf4fd1f26d61cc575713ef57c52
parent76420739e01cb925eca57845bb4704e97d6c816a (diff)
Add support for calling functions.HEADmaster
So far this only works if the function is defined before it is called. Otherwise, it fails in confusing ways. The problem is that I currently assume identifiers are always variables, but functions are currently never variables. Functions are currently treated as special case when they are declared. I'll need to rethink that...
-rw-r--r--demo/script.js7
-rw-r--r--src/disasm.rs4
-rw-r--r--src/js.rs66
-rw-r--r--src/runtime.rs61
4 files changed, 112 insertions, 26 deletions
diff --git a/demo/script.js b/demo/script.js
index 5f536fd..9e7e9ad 100644
--- a/demo/script.js
+++ b/demo/script.js
@@ -1,5 +1,5 @@
-function main() {
+function lowest_price() {
const offers = [{ id: 1, price: 2.01}, { id: 2, price: 2.99 }];
let min_price = null;
@@ -15,3 +15,8 @@ function main() {
return best_offer;
}
+function main() {
+ return lowest_price();
+}
+
+
diff --git a/src/disasm.rs b/src/disasm.rs
index 985a88a..8b02771 100644
--- a/src/disasm.rs
+++ b/src/disasm.rs
@@ -48,6 +48,9 @@ pub fn print_disassembly(bytecode: &ByteCode) {
let function_id = iter.next_varuint();
print_op!(iter, pc, "CALL {:08X}", function_id);
},
+ 0x09 => { // OpType::InvokeDynamic
+ print_op!(iter, pc, "INVOKEDYNAMIC");
+ },
0x06 => { // OpType::InvokeGenerator
let function_id = iter.next_varuint();
print_op!(iter, pc, "GEN {:08X}", function_id);
@@ -57,6 +60,7 @@ pub fn print_disassembly(bytecode: &ByteCode) {
},
0x08 => { // OpType::Return
print_op!(iter, pc, "RET");
+ println!();
},
0x10 => { // OpType::Push
let buf = &iter.bytecode()[pc+1..];
diff --git a/src/js.rs b/src/js.rs
index c0a96f8..bd7a36b 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -38,6 +38,13 @@ pub struct JsCompiler {
max_var: Vec<usize>,
bytecode: Vec<u8>,
functions: Vec<Function>,
+ identifiers: Vec<BTreeMap<Sym, IdentifierType>>,
+}
+
+#[derive(Clone)]
+enum IdentifierType {
+ Variable(IonValue),
+ Function(usize),
}
impl JsCompiler {
@@ -50,6 +57,7 @@ impl JsCompiler {
max_var: vec![0],
bytecode: Vec::new(),
functions: Vec::new(),
+ identifiers: Vec::new(),
};
compiler.enter_block();
compiler
@@ -63,6 +71,13 @@ impl JsCompiler {
};
vars.insert("this".to_string(), 0);
self.vars.push(vars);
+
+ let identifiers = if let Some(identifiers) = self.identifiers.last() {
+ identifiers.clone()
+ } else {
+ BTreeMap::new()
+ };
+ self.identifiers.push(identifiers);
}
fn exit_block(&mut self) {
@@ -109,6 +124,15 @@ impl JsCompiler {
self.bytecode.push(OpCode::SwapPop.into());
}
+ fn push_invoke(&mut self, function_id: usize) {
+ self.bytecode.push(OpCode::Invoke.into());
+ self.push_varuint(function_id);
+ }
+
+ fn push_invoke_dynamic(&mut self) {
+ self.bytecode.push(OpCode::InvokeDynamic.into());
+ }
+
fn push_yield(&mut self) {
self.bytecode.push(OpCode::Yield.into());
}
@@ -233,7 +257,9 @@ impl JsCompiler {
self.exit_block();
let name_symbol = if let Some(name) = func.name() {
- self.to_symbol_id(name.sym())
+ let id = self.to_symbol_id(name.sym());
+ self.identifiers.last_mut().unwrap().insert(name.sym(), IdentifierType::Function(id));
+ id
} else {
self.new_symbol_id(format!("anonFunction{}", self.functions.len()))
};
@@ -255,12 +281,20 @@ impl JsCompiler {
match v.binding() {
Binding::Pattern(_) => todo!(),
Binding::Identifier(ident) => {
- let varid = self.get_or_create_variable(ident.sym());
if let Some(init) = v.init() {
+ if decl.is_const() {
+ if let Some(value) = self.get_static_expression(init) {
+ self.identifiers.last_mut()
+ .unwrap()
+ .insert(ident.sym(), IdentifierType::Variable(value));
+ continue;
+ }
+ }
self.compile_expression(init);
} else {
self.push_push(&IonValue::new_null());
}
+ let varid = self.get_or_create_variable(ident.sym());
self.push_store(varid);
}
}
@@ -387,6 +421,13 @@ impl JsCompiler {
fn get_static_expression(&mut self, expression: &Expression) -> Option<IonValue> {
match expression {
+ Expression::Identifier(ident) => {
+ match self.identifiers.last().unwrap().get(&ident.sym()) {
+ Some(IdentifierType::Function(id)) => Some(IonValue::new_symbol(*id)),
+ Some(IdentifierType::Variable(value)) => Some(value.clone()),
+ None => None,
+ }
+ },
Expression::Literal(lit) => Some(self.literal_to_ion(lit)),
Expression::ArrayLiteral(alit) => {
let mut list = Vec::with_capacity(alit.as_ref().len());
@@ -482,7 +523,6 @@ impl JsCompiler {
// I don't expect it will make sense to evaluate these statically.
Expression::This => None,
- Expression::Identifier(_) => None,
Expression::New(_) => None,
Expression::Optional(_) => None,
@@ -495,8 +535,12 @@ impl JsCompiler {
match expression {
Expression::This => self.push_load(0),
Expression::Identifier(ident) => {
- let varid = self.get_or_create_variable(ident.sym());
- self.push_load(varid);
+ if let Some(value) = self.get_static_expression(expression) {
+ self.push_push(&value);
+ } else {
+ let varid = self.get_or_create_variable(ident.sym());
+ self.push_load(varid);
+ }
},
Expression::Literal(lit) => {
let value = self.literal_to_ion(lit);
@@ -602,7 +646,17 @@ impl JsCompiler {
}
},
Expression::New(_) => todo!(),
- Expression::Call(_) => todo!(),
+ Expression::Call(call) => {
+ for arg in call.args() {
+ self.compile_expression(arg);
+ }
+ if let Some(value) = self.get_static_expression(call.function()) {
+ self.push_invoke(value.to_symbol_id());
+ } else {
+ self.compile_expression(call.function());
+ self.push_invoke_dynamic();
+ }
+ },
Expression::SuperCall(_) => todo!(),
Expression::ImportCall(_) => todo!(),
Expression::Optional(_) => todo!(),
diff --git a/src/runtime.rs b/src/runtime.rs
index 97de85a..56b565e 100644
--- a/src/runtime.rs
+++ b/src/runtime.rs
@@ -37,6 +37,11 @@ pub enum OpCode {
/// stack:
Return = 0x08,
+ /// Invoke a function
+ /// operands:
+ /// stack: [arguments..., function id] -> [return value?]
+ InvokeDynamic = 0x09,
+
/// Push a typed Ion value onto the stack.
/// operands: T and L
/// length
@@ -352,6 +357,29 @@ fn execute_frame(mut frame: StackFrame) {
});
}
+#[inline]
+async fn invoke_function(frame: &mut StackFrame, function_id: usize) {
+ let function = frame.runtime.0.functions.get(&function_id)
+ .expect("Undefined function");
+ let mut args = Vec::with_capacity(function.arguments as usize);
+ for _ in 0..function.arguments {
+ args.push(frame.stack.pop_value());
+ }
+ let mut rx = frame.runtime.invoke(function_id, args);
+ let mut previous_ret = None;
+ while let Some(ret) = rx.recv().await {
+ if let Some(previous_ret) = previous_ret.take() {
+ let mut branch = frame.clone();
+ branch.stack.push_value(&previous_ret);
+ execute_frame(branch);
+ }
+ previous_ret = Some(ret);
+ }
+ if let Some(ret) = previous_ret.take() {
+ frame.stack.push_value(&ret);
+ }
+}
+
async fn execute(frame: &mut StackFrame) -> ExecutionState {
if frame.eof() {
return ExecutionState::Halt;
@@ -393,25 +421,11 @@ async fn execute(frame: &mut StackFrame) -> ExecutionState {
},
0x05 => { // OpType::Invoke
let function_id = frame.next_varuint();
- let function = frame.runtime.0.functions.get(&function_id)
- .expect("Undefined function");
- let mut args = Vec::with_capacity(function.arguments as usize);
- for _ in 0..function.arguments {
- args.push(frame.stack.pop_value());
- }
- let mut rx = frame.runtime.invoke(function_id, args);
- let mut previous_ret = None;
- while let Some(ret) = rx.recv().await {
- if let Some(previous_ret) = previous_ret.take() {
- let mut branch = frame.clone();
- branch.stack.push_value(&previous_ret);
- execute_frame(branch);
- }
- previous_ret = Some(ret);
- }
- if let Some(ret) = previous_ret.take() {
- frame.stack.push_value(&ret);
- }
+ invoke_function(frame, function_id).await;
+ },
+ 0x09 => { // OpType::InvokeDynamic
+ let function_id = frame.stack.pop_symbol();
+ invoke_function(frame, function_id).await;
},
0x06 => { // OpType::InvokeGenerator
let function_id = frame.next_varuint();
@@ -785,6 +799,15 @@ impl Stack {
value
}
+ pub fn pop_symbol(&mut self) -> usize {
+ let mut it = IonReader::new(self.0.iter().copied().rev(), 0);
+ let value = it.next_symbol_id();
+ let offset = it.offset();
+ self.0.truncate(self.0.len() - offset);
+ value
+ }
+
+
#[inline]
pub fn push_byte(&mut self, value: u8) {
self.0.push(value);