#![allow(dead_code)]
use super::package::PkgVarPairs;
use goscript_parser::ast::*;
use goscript_parser::objects::{EntityKey, Objects as AstObjects};
use goscript_vm::instruction::*;
use goscript_vm::objects::{key_to_u64, EntIndex, FunctionVal};
use goscript_vm::value::*;
use std::convert::TryFrom;
#[derive(Clone, Copy, Debug)]
pub enum CallStyle {
Default,
Async,
Defer,
}
#[derive(Clone, Copy, Debug)]
pub enum IndexSelType {
Indexing,
StructField,
}
#[derive(Clone, Copy, Debug)]
pub struct IndexSelInfo {
pub index: i8,
pub imm_index: Option<OpIndex>, pub t1: ValueType,
pub t2: Option<ValueType>, pub typ: IndexSelType,
}
impl IndexSelInfo {
pub fn new(
index: i8,
imm_index: Option<OpIndex>,
t1: ValueType,
t2: Option<ValueType>,
typ: IndexSelType,
) -> IndexSelInfo {
IndexSelInfo {
index: index,
imm_index: imm_index,
t1: t1,
t2: t2,
typ: typ,
}
}
pub fn with_index(&self, i: OpIndex) -> IndexSelInfo {
let mut v = *self;
v.index = i8::try_from(i).unwrap();
v
}
pub fn stack_space(&self) -> OpIndex {
if self.t2.is_some() {
2
} else {
1
}
}
}
#[derive(Clone, Debug)]
pub enum LeftHandSide {
Primitive(EntIndex),
IndexSelExpr(IndexSelInfo),
Deref(OpIndex),
}
pub enum RightHandSide<'a> {
Nothing,
Values(&'a Vec<Expr>),
Range(&'a Expr),
SelectRecv(&'a Expr),
}
pub struct Emitter<'a> {
f: &'a mut FunctionVal,
}
impl<'a> Emitter<'a> {
pub fn new(f: &mut FunctionVal) -> Emitter {
Emitter { f }
}
pub fn add_const(&mut self, entity: Option<EntityKey>, cst: GosValue) -> EntIndex {
self.f.add_const(entity, cst)
}
pub fn add_params(&mut self, fl: &FieldList, o: &AstObjects) -> usize {
fl.list
.iter()
.map(|f| {
let names = &o.fields[*f].names;
if names.len() == 0 {
self.f.add_local(None);
1
} else {
names
.iter()
.map(|n| {
let ident = &o.idents[*n];
self.f.add_local(ident.entity.clone().into_key());
})
.count()
}
})
.sum()
}
pub fn emit_load(
&mut self,
index: EntIndex,
patch_info: Option<(&mut PkgVarPairs, FunctionKey)>,
typ: ValueType,
pos: Option<usize>,
) {
match index {
EntIndex::Const(i) => match self.f.const_val(i).clone() {
GosValue::Bool(b) if b => self.f.emit_code(Opcode::PUSH_TRUE, pos),
GosValue::Bool(b) if !b => self.f.emit_code(Opcode::PUSH_FALSE, pos),
GosValue::Int(i) if OpIndex::try_from(i).ok().is_some() => {
let imm: OpIndex = OpIndex::try_from(i).unwrap();
self.emit_push_imm(typ, imm, pos);
}
_ => {
self.f
.emit_inst(Opcode::PUSH_CONST, [Some(typ), None, None], Some(i), pos);
}
},
EntIndex::LocalVar(i) => {
self.f
.emit_inst(Opcode::LOAD_LOCAL, [Some(typ), None, None], Some(i), pos);
}
EntIndex::UpValue(i) => {
self.f
.emit_inst(Opcode::LOAD_UPVALUE, [Some(typ), None, None], Some(i), pos);
}
EntIndex::PackageMember(pkg, ident) => {
self.f.emit_inst(
Opcode::LOAD_PKG_FIELD,
[Some(typ), None, None],
Some(0),
pos,
);
self.f.emit_raw_inst(key_to_u64(pkg), pos);
let (pairs, func) = patch_info.unwrap();
pairs.add_pair(pkg, ident, func, self.f.code().len() - 2, false);
}
EntIndex::BuiltInVal(op) => self.f.emit_code(op, pos),
EntIndex::BuiltInType(m) => {
let i = self.f.add_const(None, GosValue::Metadata(m));
self.emit_load(i, None, ValueType::Metadata, pos);
}
EntIndex::Blank => unreachable!(),
}
}
pub fn emit_store(
&mut self,
lhs: &LeftHandSide,
rhs_index: OpIndex,
op: Option<(Opcode, Option<ValueType>)>,
patch_info: Option<(&mut PkgVarPairs, FunctionKey)>,
typ: ValueType,
pos: Option<usize>,
) {
if let LeftHandSide::Primitive(index) = lhs {
if EntIndex::Blank == *index {
return;
}
}
let mut pkg_info = None;
let (code, int32, t1, t2, int8) = match lhs {
LeftHandSide::Primitive(index) => match index {
EntIndex::Const(_) => unreachable!(),
EntIndex::LocalVar(i) => (Opcode::STORE_LOCAL, *i, None, None, None),
EntIndex::UpValue(i) => (Opcode::STORE_UPVALUE, *i, None, None, None),
EntIndex::PackageMember(pkg, ident) => {
pkg_info = Some((*pkg, *ident));
(
Opcode::STORE_PKG_FIELD,
0,
Some(ValueType::Package),
None,
None,
)
}
EntIndex::BuiltInVal(_) => unreachable!(),
EntIndex::BuiltInType(_) => unreachable!(),
EntIndex::Blank => unreachable!(),
},
LeftHandSide::IndexSelExpr(info) => match info.typ {
IndexSelType::Indexing => match info.imm_index {
Some(i) => (
Opcode::STORE_INDEX_IMM,
i,
Some(info.t1),
None,
Some(info.index),
),
None => (
Opcode::STORE_INDEX,
info.index as i32,
Some(info.t1),
info.t2,
None,
),
},
IndexSelType::StructField => (
Opcode::STORE_STRUCT_FIELD,
info.imm_index.unwrap(),
Some(info.t1),
None,
Some(info.index),
),
},
LeftHandSide::Deref(i) => (Opcode::STORE_DEREF, *i, None, None, None),
};
let mut inst = Instruction::new(code, Some(typ), t1, t2, None);
if let Some(i) = int8 {
inst.set_t2_with_index(i);
}
assert!(rhs_index == -1 || op.is_none());
let imm0 = op.map_or(rhs_index, |(code, shift_t)| {
if let Some(t) = shift_t {
self.emit_cast(ValueType::Uint32, t, None, -1, 0, pos);
}
Instruction::code2index(code)
});
inst.set_imm824(imm0, int32);
self.f.push_inst_pos(inst, pos);
if let Some((pkg, ident)) = pkg_info {
self.f.emit_raw_inst(key_to_u64(pkg), pos);
let (pairs, func) = patch_info.unwrap();
pairs.add_pair(pkg, ident, func, self.f.code().len() - 2, true);
}
}
pub fn emit_cast(
&mut self,
t0: ValueType,
t1: ValueType,
t2: Option<ValueType>,
rhs: OpIndex,
m_index: OpIndex,
pos: Option<usize>,
) {
let mut inst = Instruction::new(Opcode::CAST, Some(t0), Some(t1), t2, None);
inst.set_imm824(rhs, m_index);
self.f.push_inst_pos(inst, pos);
}
pub fn emit_import(&mut self, index: OpIndex, pkg: PackageKey, pos: Option<usize>) {
self.f
.emit_inst(Opcode::IMPORT, [None, None, None], Some(index), pos);
let cd = vec![
Instruction::new(
Opcode::LOAD_PKG_FIELD,
Some(ValueType::Int),
None,
None,
Some(0),
),
Instruction::from_u64(key_to_u64(pkg)),
Instruction::new(Opcode::PRE_CALL, Some(ValueType::Closure), None, None, None),
Instruction::new(Opcode::CALL, None, None, None, None),
];
let offset = cd.len() as OpIndex;
self.f
.emit_inst(Opcode::JUMP_IF_NOT, [None, None, None], Some(offset), pos);
for i in cd.into_iter() {
self.f.push_inst_pos(i, pos);
}
}
pub fn emit_pop(&mut self, count: OpIndex, pos: Option<usize>) {
self.f
.emit_inst(Opcode::POP, [None, None, None], Some(count), pos);
}
pub fn emit_load_struct_field(&mut self, imm: OpIndex, typ: ValueType, pos: Option<usize>) {
self.f.emit_inst(
Opcode::LOAD_STRUCT_FIELD,
[Some(typ), None, None],
Some(imm),
pos,
);
}
pub fn emit_load_index(
&mut self,
typ: ValueType,
index_type: ValueType,
comma_ok: bool,
pos: Option<usize>,
) {
let mut inst =
Instruction::new(Opcode::LOAD_INDEX, Some(typ), Some(index_type), None, None);
inst.set_t2_with_index(if comma_ok { 1 } else { 0 });
self.f.push_inst_pos(inst, pos);
}
pub fn emit_load_index_imm(
&mut self,
imm: OpIndex,
typ: ValueType,
comma_ok: bool,
pos: Option<usize>,
) {
let mut inst = Instruction::new(Opcode::LOAD_INDEX_IMM, Some(typ), None, None, Some(imm));
inst.set_t2_with_index(if comma_ok { 1 } else { 0 });
self.f.push_inst_pos(inst, pos);
}
pub fn emit_return(&mut self, pkg_index: Option<OpIndex>, pos: Option<usize>) {
let inst_flag = match self.f.flag {
FuncFlag::Default => ValueType::Zero,
FuncFlag::PkgCtor => ValueType::FlagA,
FuncFlag::HasDefer => ValueType::FlagB,
};
self.f.emit_inst(
Opcode::RETURN,
[Some(inst_flag), None, None],
pkg_index,
pos,
);
}
pub fn emit_pre_call(&mut self, pos: Option<usize>) {
self.f
.emit_inst(Opcode::PRE_CALL, [None, None, None], None, pos);
}
pub fn emit_call(&mut self, style: CallStyle, has_ellipsis: bool, pos: Option<usize>) {
let style_flag = match style {
CallStyle::Default => ValueType::Zero,
CallStyle::Async => ValueType::FlagA,
CallStyle::Defer => ValueType::FlagB,
};
let elli_flag = if has_ellipsis {
Some(ValueType::FlagA)
} else {
None
};
self.f
.emit_inst(Opcode::CALL, [Some(style_flag), elli_flag, None], None, pos);
}
pub fn emit_literal(&mut self, typ: ValueType, index: OpIndex, pos: Option<usize>) {
self.f
.emit_inst(Opcode::LITERAL, [Some(typ), None, None], Some(index), pos);
}
pub fn emit_push_imm(&mut self, typ: ValueType, imm: OpIndex, pos: Option<usize>) {
self.f
.emit_inst(Opcode::PUSH_IMM, [Some(typ), None, None], Some(imm), pos);
}
}