use crate::bytecode::{Instruction, OpCode, Operand};
use crate::executor::vm_impl::stack::drop_with_kind;
use shape_value::heap_value::TypedObjectStorage;
use shape_value::v2::decimal_obj::DecimalObj;
use shape_value::v2::heap_element::HeapElement;
use shape_value::v2::refcount::v2_retain;
use shape_value::v2::string_obj::StringObj;
use shape_value::v2::typed_array::TypedArray;
use shape_value::{HeapKind, NativeKind, VMError};
use super::super::VirtualMachine;
use super::v2_array_detect::{
ELEM_TYPE_BOOL, ELEM_TYPE_CHAR, ELEM_TYPE_DECIMAL, ELEM_TYPE_F32, ELEM_TYPE_F64, ELEM_TYPE_I16,
ELEM_TYPE_I32, ELEM_TYPE_I64, ELEM_TYPE_I8, ELEM_TYPE_STRING, ELEM_TYPE_TYPED_OBJECT,
ELEM_TYPE_U16, ELEM_TYPE_U32, ELEM_TYPE_U8, stamp_elem_type,
};
macro_rules! define_exec_v2_typed_array {
(
scalar_rows: [
$(
{
ops: $s_new:ident / $s_get:ident / $s_push:ident / $s_set:ident,
storage: $s_storage:ty,
elem_tag: $s_tag:expr,
result_kind: $s_kind:expr,
decode( $s_vb:ident ) => $s_decode:expr,
encode( $s_v:ident : $s_vt:ty ) => $s_encode:expr,
}
)+
],
char_rows: [
$(
{
ops: $c_new:ident / $c_get:ident / $c_push:ident / $c_set:ident,
elem_tag: $c_tag:expr,
result_kind: $c_kind:expr,
push_label: $c_push_label:expr,
set_label: $c_set_label:expr,
}
)+
],
heap_rows: [
$(
{
ops: $h_new:ident / $h_get:ident / $h_push:ident / $h_set:ident,
heap_obj: $h_obj:ty,
elem_kind: $h_kind:expr,
elem_tag: $h_tag:expr,
err_label: $h_err:expr,
}
)+
],
) => {
impl VirtualMachine {
pub(crate) fn exec_v2_typed_array(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError> {
match instruction.opcode {
$(
OpCode::$s_new => {
let cap = match instruction.operand {
Some(Operand::Count(n)) => n as u32,
_ => 0,
};
let ptr = TypedArray::<$s_storage>::with_capacity(cap);
unsafe { stamp_elem_type(ptr as *mut u8, $s_tag) };
self.push_kinded(
ptr as usize as u64,
NativeKind::Ptr(HeapKind::TypedArray),
)?;
Ok(())
}
OpCode::$s_get => {
let (idx_bits, _idx_kind) = self.pop_kinded()?;
let index = idx_bits as i64 as u32;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr = arr_bits as usize as *const TypedArray<$s_storage>;
let len = unsafe { TypedArray::len(arr) };
let $s_v: $s_vt = unsafe {
TypedArray::get(arr, index).ok_or(
VMError::IndexOutOfBounds {
index: index as i32,
length: len as usize,
},
)?
};
drop_with_kind(arr_bits, arr_kind);
self.push_kinded($s_encode, $s_kind)?;
Ok(())
}
OpCode::$s_push => {
let ($s_vb, _vk) = self.pop_kinded()?;
let val: $s_storage = $s_decode;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr = arr_bits as usize as *mut TypedArray<$s_storage>;
unsafe { TypedArray::push(arr, val); }
drop_with_kind(arr_bits, arr_kind);
Ok(())
}
OpCode::$s_set => {
let ($s_vb, _vk) = self.pop_kinded()?;
let val: $s_storage = $s_decode;
let (idx_bits, _ik) = self.pop_kinded()?;
let index = idx_bits as i64 as u32;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr = arr_bits as usize as *mut TypedArray<$s_storage>;
unsafe { TypedArray::set(arr, index, val); }
drop_with_kind(arr_bits, arr_kind);
Ok(())
}
)+
$(
OpCode::$c_new => {
let cap = match instruction.operand {
Some(Operand::Count(n)) => n as u32,
_ => 0,
};
let ptr = TypedArray::<char>::with_capacity(cap);
unsafe { stamp_elem_type(ptr as *mut u8, $c_tag) };
self.push_kinded(
ptr as usize as u64,
NativeKind::Ptr(HeapKind::TypedArray),
)?;
Ok(())
}
OpCode::$c_get => {
let (idx_bits, _idx_kind) = self.pop_kinded()?;
let index = idx_bits as i64 as u32;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr = arr_bits as usize as *const TypedArray<char>;
let len = unsafe { TypedArray::len(arr) };
let val = unsafe {
TypedArray::get(arr, index).ok_or(
VMError::IndexOutOfBounds {
index: index as i32,
length: len as usize,
},
)?
};
drop_with_kind(arr_bits, arr_kind);
self.push_kinded(val as u32 as u64, $c_kind)?;
Ok(())
}
OpCode::$c_push => {
let (val_bits, _vk) = self.pop_kinded()?;
let val = char::from_u32(val_bits as u32).ok_or_else(|| {
VMError::RuntimeError(format!(
"{}: invalid char codepoint 0x{:X}",
$c_push_label, val_bits as u32
))
})?;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr = arr_bits as usize as *mut TypedArray<char>;
unsafe { TypedArray::push(arr, val); }
drop_with_kind(arr_bits, arr_kind);
Ok(())
}
OpCode::$c_set => {
let (val_bits, _vk) = self.pop_kinded()?;
let val = char::from_u32(val_bits as u32).ok_or_else(|| {
VMError::RuntimeError(format!(
"{}: invalid char codepoint 0x{:X}",
$c_set_label, val_bits as u32
))
})?;
let (idx_bits, _ik) = self.pop_kinded()?;
let index = idx_bits as i64 as u32;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr = arr_bits as usize as *mut TypedArray<char>;
unsafe { TypedArray::set(arr, index, val); }
drop_with_kind(arr_bits, arr_kind);
Ok(())
}
)+
$(
OpCode::$h_new => {
let cap = match instruction.operand {
Some(Operand::Count(n)) => n as u32,
_ => 0,
};
let ptr =
TypedArray::<*const $h_obj>::with_capacity(cap);
unsafe { stamp_elem_type(ptr as *mut u8, $h_tag) };
self.push_kinded(
ptr as usize as u64,
NativeKind::Ptr(HeapKind::TypedArray),
)?;
Ok(())
}
OpCode::$h_get => {
let (idx_bits, _idx_kind) = self.pop_kinded()?;
let index = idx_bits as i64 as u32;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr =
arr_bits as usize as *const TypedArray<*const $h_obj>;
let len = unsafe { TypedArray::len(arr) };
let elem_ptr = unsafe {
TypedArray::<*const $h_obj>::get(arr, index).ok_or(
VMError::IndexOutOfBounds {
index: index as i32,
length: len as usize,
},
)?
};
unsafe { v2_retain(&(*elem_ptr).header) };
drop_with_kind(arr_bits, arr_kind);
self.push_kinded(elem_ptr as u64, $h_kind)?;
Ok(())
}
OpCode::$h_push => {
let (val_bits, val_kind) = self.pop_kinded()?;
if val_kind != $h_kind {
return Err(VMError::RuntimeError(format!(
"{}: expected {:?}, got {:?}",
$h_err, $h_kind, val_kind
)));
}
let val = val_bits as usize as *const $h_obj;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr =
arr_bits as usize as *mut TypedArray<*const $h_obj>;
unsafe { TypedArray::push(arr, val); }
drop_with_kind(arr_bits, arr_kind);
Ok(())
}
OpCode::$h_set => {
let (val_bits, val_kind) = self.pop_kinded()?;
if val_kind != $h_kind {
return Err(VMError::RuntimeError(format!(
"{}: expected {:?}, got {:?}",
$h_err, $h_kind, val_kind
)));
}
let val = val_bits as usize as *const $h_obj;
let (idx_bits, _ik) = self.pop_kinded()?;
let index = idx_bits as i64 as u32;
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr =
arr_bits as usize as *mut TypedArray<*const $h_obj>;
unsafe {
let old_ptr =
TypedArray::<*const $h_obj>::get_unchecked(arr, index);
<$h_obj as HeapElement>::release_elem(old_ptr);
TypedArray::set(arr, index, val);
}
drop_with_kind(arr_bits, arr_kind);
Ok(())
}
)+
OpCode::NewStringV2 => {
let str_id = match instruction.operand {
Some(Operand::Property(id)) => id as usize,
Some(Operand::Const(id)) => id as usize,
_ => {
return Err(VMError::RuntimeError(
"NewStringV2 requires a Property/Const string-id operand"
.to_string(),
));
}
};
let s = self
.program
.strings
.get(str_id)
.ok_or_else(|| {
VMError::RuntimeError(format!(
"NewStringV2: string id {} out of bounds (pool len = {})",
str_id,
self.program.strings.len()
))
})?
.clone();
let ptr = StringObj::new(&s);
self.push_kinded(ptr as usize as u64, NativeKind::StringV2)?;
Ok(())
}
OpCode::NewDecimalV2 => {
let const_id = match instruction.operand {
Some(Operand::Const(id)) => id as usize,
_ => {
return Err(VMError::RuntimeError(
"NewDecimalV2 requires a Const constant-id operand"
.to_string(),
));
}
};
let constant = self.program.constants.get(const_id).ok_or_else(|| {
VMError::RuntimeError(format!(
"NewDecimalV2: constant id {} out of bounds (pool len = {})",
const_id,
self.program.constants.len()
))
})?;
let d = match constant {
crate::bytecode::Constant::Decimal(d) => *d,
other => {
return Err(VMError::RuntimeError(format!(
"NewDecimalV2: expected Constant::Decimal, got {:?}",
other
)));
}
};
let ptr = DecimalObj::new(d);
self.push_kinded(ptr as usize as u64, NativeKind::DecimalV2)?;
Ok(())
}
OpCode::TypedArrayLen => {
let (arr_bits, arr_kind) = self.pop_kinded()?;
let arr = arr_bits as usize as *const TypedArray<u8>;
let len = unsafe { TypedArray::len(arr) };
drop_with_kind(arr_bits, arr_kind);
self.push_kinded(len as u64, NativeKind::Int64)?;
Ok(())
}
_ => Err(VMError::NotImplemented(format!(
"v2 typed array opcode {:?} not implemented",
instruction.opcode
))),
}
}
}
};
}
define_exec_v2_typed_array! {
scalar_rows: [
{
ops: NewTypedArrayF64 / TypedArrayGetF64
/ TypedArrayPushF64 / TypedArraySetF64,
storage: f64,
elem_tag: ELEM_TYPE_F64,
result_kind: NativeKind::Float64,
decode(vb) => f64::from_bits(vb),
encode(v: f64) => v.to_bits(),
}
{
ops: NewTypedArrayI64 / TypedArrayGetI64
/ TypedArrayPushI64 / TypedArraySetI64,
storage: i64,
elem_tag: ELEM_TYPE_I64,
result_kind: NativeKind::Int64,
decode(vb) => vb as i64,
encode(v: i64) => v as u64,
}
{
ops: NewTypedArrayI32 / TypedArrayGetI32
/ TypedArrayPushI32 / TypedArraySetI32,
storage: i32,
elem_tag: ELEM_TYPE_I32,
result_kind: NativeKind::Int32,
decode(vb) => vb as i64 as i32,
encode(v: i32) => v as i64 as u64,
}
{
ops: NewTypedArrayBool / TypedArrayGetBool
/ TypedArrayPushBool / TypedArraySetBool,
storage: u8,
elem_tag: ELEM_TYPE_BOOL,
result_kind: NativeKind::Bool,
decode(vb) => if vb != 0 { 1u8 } else { 0u8 },
encode(v: u8) => (v != 0) as u64,
}
{
ops: NewTypedArrayI8 / TypedArrayGetI8
/ TypedArrayPushI8 / TypedArraySetI8,
storage: i8,
elem_tag: ELEM_TYPE_I8,
result_kind: NativeKind::Int8,
decode(vb) => vb as i64 as i8,
encode(v: i8) => v as i64 as u64,
}
{
ops: NewTypedArrayU8 / TypedArrayGetU8
/ TypedArrayPushU8 / TypedArraySetU8,
storage: u8,
elem_tag: ELEM_TYPE_U8,
result_kind: NativeKind::UInt8,
decode(vb) => vb as u8,
encode(v: u8) => v as u64,
}
{
ops: NewTypedArrayI16 / TypedArrayGetI16
/ TypedArrayPushI16 / TypedArraySetI16,
storage: i16,
elem_tag: ELEM_TYPE_I16,
result_kind: NativeKind::Int16,
decode(vb) => vb as i64 as i16,
encode(v: i16) => v as i64 as u64,
}
{
ops: NewTypedArrayU16 / TypedArrayGetU16
/ TypedArrayPushU16 / TypedArraySetU16,
storage: u16,
elem_tag: ELEM_TYPE_U16,
result_kind: NativeKind::UInt16,
decode(vb) => vb as u16,
encode(v: u16) => v as u64,
}
{
ops: NewTypedArrayU32 / TypedArrayGetU32
/ TypedArrayPushU32 / TypedArraySetU32,
storage: u32,
elem_tag: ELEM_TYPE_U32,
result_kind: NativeKind::UInt32,
decode(vb) => vb as u32,
encode(v: u32) => v as u64,
}
{
ops: NewTypedArrayF32 / TypedArrayGetF32
/ TypedArrayPushF32 / TypedArraySetF32,
storage: f32,
elem_tag: ELEM_TYPE_F32,
result_kind: NativeKind::Float32,
decode(vb) => f32::from_bits(vb as u32),
encode(v: f32) => v.to_bits() as u64,
}
],
char_rows: [
{
ops: NewTypedArrayChar / TypedArrayGetChar
/ TypedArrayPushChar / TypedArraySetChar,
elem_tag: ELEM_TYPE_CHAR,
result_kind: NativeKind::Char,
push_label: "TypedArrayPushChar",
set_label: "TypedArraySetChar",
}
],
heap_rows: [
{
ops: NewTypedArrayString / TypedArrayGetString
/ TypedArrayPushString / TypedArraySetString,
heap_obj: StringObj,
elem_kind: NativeKind::StringV2,
elem_tag: ELEM_TYPE_STRING,
err_label: "TypedArrayPush/SetString",
}
{
ops: NewTypedArrayDecimal / TypedArrayGetDecimal
/ TypedArrayPushDecimal / TypedArraySetDecimal,
heap_obj: DecimalObj,
elem_kind: NativeKind::DecimalV2,
elem_tag: ELEM_TYPE_DECIMAL,
err_label: "TypedArrayPush/SetDecimal",
}
{
ops: NewTypedArrayTypedObject / TypedArrayGetTypedObject
/ TypedArrayPushTypedObject / TypedArraySetTypedObject,
heap_obj: TypedObjectStorage,
elem_kind: NativeKind::Ptr(HeapKind::TypedObject),
elem_tag: ELEM_TYPE_TYPED_OBJECT,
err_label: "TypedArrayPush/SetTypedObject",
}
],
}