summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);