From e49b1ff3da1251935efc18b6f6824c65595866ea Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Tue, 27 Jun 2023 14:38:42 +1000 Subject: [PATCH] Implement yield* instruction --- valuescript_compiler/src/assembly_parser.rs | 2 +- .../src/expression_compiler.rs | 102 +---------- valuescript_vm/src/generator.rs | 169 ++++++++++++++++-- 3 files changed, 163 insertions(+), 110 deletions(-) diff --git a/valuescript_compiler/src/assembly_parser.rs b/valuescript_compiler/src/assembly_parser.rs index 093f2580..8177de0a 100644 --- a/valuescript_compiler/src/assembly_parser.rs +++ b/valuescript_compiler/src/assembly_parser.rs @@ -751,7 +751,7 @@ impl<'a> AssemblyParser<'a> { ), Cat => Instruction::Cat(self.assemble_value(), self.assemble_register()), Yield => Instruction::Yield(self.assemble_value(), self.assemble_register()), - YieldStar => Instruction::Yield(self.assemble_value(), self.assemble_register()), + YieldStar => Instruction::YieldStar(self.assemble_value(), self.assemble_register()), }; self.parse_line(); diff --git a/valuescript_compiler/src/expression_compiler.rs b/valuescript_compiler/src/expression_compiler.rs index 780a456b..b05b729b 100644 --- a/valuescript_compiler/src/expression_compiler.rs +++ b/valuescript_compiler/src/expression_compiler.rs @@ -3,7 +3,7 @@ use std::mem::take; use queues::*; use swc_common::Spanned; -use crate::asm::{Array, Builtin, Instruction, Label, Number, Object, Register, Value}; +use crate::asm::{Array, Instruction, Label, Number, Object, Register, Value}; use crate::diagnostic::{Diagnostic, DiagnosticLevel}; use crate::function_compiler::{FunctionCompiler, Functionish, QueuedFunction}; use crate::scope::{NameId, OwnerId}; @@ -1191,13 +1191,6 @@ impl<'a> ExpressionCompiler<'a> { yield_expr: &swc_ecma_ast::YieldExpr, target_register: Option, ) -> CompiledExpression { - // TODO: Implement and use the yield* instruction instead - // It will use the stack within the generator which avoids reloading the generator's stack each - // time. - if yield_expr.delegate { - return self.yield_star_expr(yield_expr, target_register); - } - let mut nested_registers = Vec::::new(); let arg_compiled = match &yield_expr.arg { @@ -1214,100 +1207,13 @@ impl<'a> ExpressionCompiler<'a> { } }; - let instr = Instruction::Yield(self.fnc.use_(arg_compiled), dst.clone()); - - self.fnc.push(instr); - - return CompiledExpression::new(Value::Register(dst), nested_registers); - } - - pub fn yield_star_expr( - &mut self, - yield_expr: &swc_ecma_ast::YieldExpr, - target_register: Option, - ) -> CompiledExpression { - let mut nested_registers = Vec::::new(); - - let arg_compiled = match &yield_expr.arg { - Some(arg) => self.compile(arg, None), - None => CompiledExpression::empty(), - }; - - let dst = match target_register { - Some(t) => t, - None => { - let tmp = self.fnc.allocate_tmp(); - nested_registers.push(tmp.clone()); - tmp - } + let instr = match yield_expr.delegate { + false => Instruction::Yield(self.fnc.use_(arg_compiled), dst.clone()), + true => Instruction::YieldStar(self.fnc.use_(arg_compiled), dst.clone()), }; - let iter_reg = self.fnc.allocate_numbered_reg(&"_iter".to_string()); - let iter_res_reg = self.fnc.allocate_numbered_reg(&"_iter_res".to_string()); - let done_reg = self.fnc.allocate_numbered_reg(&"_done".to_string()); - - let test_label = Label { - name: self - .fnc - .label_allocator - .allocate_numbered(&"yield_star_test".to_string()), - }; - - let next_label = Label { - name: self - .fnc - .label_allocator - .allocate_numbered(&"yield_star_next".to_string()), - }; - - let end_label = Label { - name: self - .fnc - .label_allocator - .allocate_numbered(&"yield_star_end".to_string()), - }; - - let instr = Instruction::ConstSubCall( - self.fnc.use_(arg_compiled), - Value::Builtin(Builtin { - name: "SymbolIterator".to_string(), - }), - Value::Array(Box::new(Array::default())), - iter_reg.clone(), - ); - self.fnc.push(instr); - self.fnc.push(Instruction::Jmp(next_label.ref_())); - - self.fnc.label(test_label.clone()); - - self.fnc.push(Instruction::JmpIf( - Value::Register(done_reg.clone()), - end_label.ref_(), - )); - - self.fnc.push(Instruction::Yield( - Value::Register(dst.clone()), - Register::ignore(), - )); - - self.fnc.label(next_label); - - self - .fnc - .push(Instruction::Next(iter_reg, iter_res_reg.clone())); - - self.fnc.push(Instruction::UnpackIterRes( - iter_res_reg, - dst.clone(), - done_reg, - )); - - self.fnc.push(Instruction::Jmp(test_label.ref_())); - - self.fnc.label(end_label); - return CompiledExpression::new(Value::Register(dst), nested_registers); } diff --git a/valuescript_vm/src/generator.rs b/valuescript_vm/src/generator.rs index e6496fa0..1c41a4a4 100644 --- a/valuescript_vm/src/generator.rs +++ b/valuescript_vm/src/generator.rs @@ -1,4 +1,8 @@ -use std::{fmt, mem::take, rc::Rc}; +use std::{ + fmt, + mem::{swap, take}, + rc::Rc, +}; use num_bigint::BigInt; @@ -6,6 +10,7 @@ use crate::{ builtins::{error_builtin::ToError, type_error_builtin::ToTypeError}, iteration::{iteration_result::IterationResult, return_this::RETURN_THIS}, native_frame_function::NativeFrameFunction, + native_function::ThisWrapper, stack_frame::{CallResult, FrameStepOk, FrameStepResult, StackFrame, StackFrameTrait}, vs_array::VsArray, vs_class::VsClass, @@ -17,8 +22,6 @@ use crate::{ #[derive(Clone, Default)] pub struct Generator { frame: StackFrame, - - #[allow(dead_code)] // TODO stack: Vec, } @@ -156,14 +159,22 @@ impl StackFrameTrait for GeneratorFrame { match fsr { Err(_) => fsr, // TODO: Stack unwind internal stack first Ok(FrameStepOk::Continue) | Ok(FrameStepOk::Push(_)) => fsr, - Ok(FrameStepOk::Pop(call_result)) => Ok(FrameStepOk::Pop(CallResult { - return_: IterationResult { - value: call_result.return_, // TODO: Assert call_result.this is undefined? - done: true, + Ok(FrameStepOk::Pop(call_result)) => match self.generator.stack.pop() { + Some(mut frame) => { + frame.apply_call_result(call_result); + swap(&mut frame, &mut self.generator.frame); + + Ok(FrameStepOk::Continue) } - .to_dynamic_val(), - this: take(&mut self.generator).to_dynamic_val(), - })), + None => Ok(FrameStepOk::Pop(CallResult { + return_: IterationResult { + value: call_result.return_, // TODO: Assert call_result.this is undefined? + done: true, + } + .to_dynamic_val(), + this: take(&mut self.generator).to_dynamic_val(), + })), + }, Ok(FrameStepOk::Yield(val)) => Ok(FrameStepOk::Pop(CallResult { return_: IterationResult { value: val, @@ -172,7 +183,30 @@ impl StackFrameTrait for GeneratorFrame { .to_dynamic_val(), this: take(&mut self.generator).to_dynamic_val(), })), - Ok(FrameStepOk::YieldStar(_)) => panic!("TODO: yield* (as instruction)"), + Ok(FrameStepOk::YieldStar(iterable)) => { + let make_iter = iterable.sub(&VsSymbol::ITERATOR.to_val())?; + + let mut frame = 'f: { + if let Val::Function(make_iter) = &make_iter { + if make_iter.is_generator { + let mut frame: StackFrame = Box::new(make_iter.make_bytecode_frame()); + frame.write_this(true, iterable)?; + + break 'f frame; + } + } + + Box::new(YieldStarFrame { + iter: YieldStarIter::MakeIterator(iterable, make_iter), + iter_result: None, + }) + }; + + swap(&mut frame, &mut self.generator.frame); + self.generator.stack.push(frame); + + return Ok(FrameStepOk::Continue); + } } } @@ -192,3 +226,116 @@ impl StackFrameTrait for GeneratorFrame { Box::new(self.clone()) } } + +#[derive(Clone, Default)] +struct YieldStarFrame { + iter: YieldStarIter, + iter_result: Option, +} + +#[derive(Clone)] +enum YieldStarIter { + MakeIterator(Val, Val), + Iterator(Val), +} + +impl Default for YieldStarIter { + fn default() -> Self { + YieldStarIter::MakeIterator(Val::default(), Val::default()) + } +} + +impl StackFrameTrait for YieldStarFrame { + fn write_this(&mut self, _const: bool, _this: Val) -> Result<(), Val> { + panic!("Not appropriate for YieldStarFrame") + } + + fn write_param(&mut self, _param: Val) { + panic!("Not appropriate for YieldStarFrame") + } + + fn step(&mut self) -> FrameStepResult { + let iter_result = take(&mut self.iter_result); + + if let Some(iter_result) = iter_result { + let value = iter_result.sub(&"value".to_val())?; // TODO: mutable subscript to avoid cloning + + return match iter_result.sub(&"done".to_val())?.is_truthy() { + false => Ok(FrameStepOk::Yield(value)), + true => Ok(FrameStepOk::Pop(CallResult { + return_: value, + this: Val::Undefined, + })), + }; + } + + match &mut self.iter { + YieldStarIter::MakeIterator(this, make_iter) => match make_iter.load_function() { + LoadFunctionResult::NotAFunction => Err("yield* argument is not iterable".to_type_error()), + LoadFunctionResult::NativeFunction(native_fn) => { + let iterator = native_fn(ThisWrapper::new(true, this), vec![])?; + + self.iter = YieldStarIter::Iterator(iterator); + + Ok(FrameStepOk::Continue) + } + LoadFunctionResult::StackFrame(frame) => Ok(FrameStepOk::Push(frame)), + }, + YieldStarIter::Iterator(iterator) => { + let next_fn = iterator.sub(&"next".to_val())?; + + match next_fn.load_function() { + LoadFunctionResult::NotAFunction => { + Err("iterator.next is not a function".to_type_error()) + } + LoadFunctionResult::NativeFunction(native_fn) => { + let iter_result = native_fn(ThisWrapper::new(false, iterator), vec![])?; + let value = iter_result.sub(&"value".to_val())?; + + match iter_result.sub(&"done".to_val())?.is_truthy() { + false => Ok(FrameStepOk::Yield(value)), + true => Ok(FrameStepOk::Pop(CallResult { + return_: value, + this: Val::Undefined, + })), + } + } + LoadFunctionResult::StackFrame(mut frame) => { + frame.write_this(false, take(iterator))?; + + Ok(FrameStepOk::Push(frame)) + } + } + } + } + } + + fn apply_call_result(&mut self, call_result: CallResult) { + let iter = &mut self.iter; + let mut iter_result = None; + + match iter { + YieldStarIter::MakeIterator(..) => { + *iter = YieldStarIter::Iterator(call_result.return_); + } + YieldStarIter::Iterator(iterator) => { + iter_result = Some(call_result.return_); + *iterator = call_result.this; + } + } + + self.iter_result = iter_result; + } + + fn get_call_result(&mut self) -> CallResult { + panic!("Not appropriate for YieldStarFrame") + } + + fn catch_exception(&mut self, _exception: Val) -> bool { + false + } + + fn clone_to_stack_frame(&self) -> StackFrame { + Box::new(self.clone()) + } +}