diff options
| -rw-r--r-- | demo/script.js | 7 | ||||
| -rw-r--r-- | src/disasm.rs | 4 | ||||
| -rw-r--r-- | src/js.rs | 66 | ||||
| -rw-r--r-- | src/runtime.rs | 61 | 
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..]; @@ -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);  | 
