blob: bbf1e3d08699014efd85374035db0229f697b4b0 [file] [log] [blame]
use std::prelude::v1::*;
use std::any::Any;
use std::cell::UnsafeCell;
use object::Object;
use executor::ExecutorImpl;
use value::{Value, ValueContext};
use errors::{VMError, FieldNotFoundError};
pub trait TypedArrayElement: Send + Copy + 'static {
fn must_from_value(other: Value) -> Self {
Self::from_value(other).unwrap_or_else(|| panic!(VMError::from("Invalid cast")))
}
fn from_value(other: Value) -> Option<Self>;
fn to_value(&self) -> Value;
}
pub struct TypedArray<T: TypedArrayElement> {
elements: UnsafeCell<Vec<T>>,
default_value: T
}
impl<T: TypedArrayElement> TypedArray<T> {
pub fn new(value: T, len: usize) -> TypedArray<T> {
TypedArray {
elements: UnsafeCell::new(vec![value; len]),
default_value: value
}
}
pub fn resize(&self, len: usize) {
let elements = unsafe { &mut *self.elements.get() };
elements.resize(len, self.default_value);
}
pub fn set(&self, id: usize, v: T) {
let elements = unsafe { &mut *self.elements.get() };
if id < elements.len() {
elements[id] = v;
} else {
panic!(VMError::from("TypedArray index out of bound"));
}
}
pub fn get(&self, id: usize) -> T {
let elements = unsafe { &mut *self.elements.get() };
if id < elements.len() {
elements[id]
} else {
panic!(VMError::from("TypedArray index out of bound"));
}
}
pub fn len(&self) -> usize {
let elements = unsafe { &mut *self.elements.get() };
elements.len()
}
}
impl<T: TypedArrayElement> Object for TypedArray<T> {
fn get_children(&self) -> Vec<usize> {
Vec::new()
}
fn as_any(&self) -> &Any {
self as &Any
}
fn as_any_mut(&mut self) -> &mut Any {
self as &mut Any
}
fn call_field(&self, name: &str, executor: &mut ExecutorImpl) -> Value {
match name {
"__get__" | "get" => {
let index = executor.get_current_frame().must_get_argument(0);
let index = ValueContext::new(
&index,
executor.get_object_pool()
).to_i64() as usize;
self.get(index).to_value()
},
"__set__" | "set" => {
let index = executor.get_current_frame().must_get_argument(0);
let val = executor.get_current_frame().must_get_argument(1);
let index = ValueContext::new(
&index,
executor.get_object_pool()
).to_i64() as usize;
if let Some(v) = T::from_value(val) {
self.set(index, v);
} else {
panic!(VMError::from("Cannot cast to target type"));
}
Value::Null
},
"resize" => {
let new_size = ValueContext::new(
&executor.get_current_frame().must_get_argument(0),
executor.get_object_pool()
).to_i64() as usize;
self.resize(new_size);
Value::Null
},
"__len__" | "len" | "size" => {
Value::Int(self.len() as i64)
},
_ => panic!(VMError::from(FieldNotFoundError::from_field_name(name)))
}
}
}
macro_rules! impl_typed_int {
($type_name:ty) => (
impl TypedArrayElement for $type_name {
fn from_value(v: Value) -> Option<Self> {
match v {
Value::Int(v) => {
if v >= Self::min_value() as i64 && v <= Self::max_value() as i64 {
Some(v as $type_name)
} else {
None
}
},
Value::Float(v) => {
let v = v as i64;
if v >= Self::min_value() as i64 && v <= Self::max_value() as i64 {
Some(v as $type_name)
} else {
None
}
},
_ => None
}
}
fn to_value(&self) -> Value {
Value::Int(*self as i64)
}
}
)
}
impl_typed_int!(i8);
impl_typed_int!(u8);
impl_typed_int!(i16);
impl_typed_int!(u16);
impl_typed_int!(i32);
impl_typed_int!(u32);
impl_typed_int!(i64);
impl_typed_int!(u64);
impl TypedArrayElement for f32 {
fn from_value(v: Value) -> Option<Self> {
match v {
Value::Int(v) => {
let v = v as f64;
if v >= ::std::f32::MIN as f64 && v <= ::std::f32::MAX as f64 {
Some(v as f32)
} else {
None
}
},
Value::Float(v) => {
if v >= ::std::f32::MIN as f64 && v <= ::std::f32::MAX as f64 {
Some(v as f32)
} else {
None
}
},
_ => None
}
}
fn to_value(&self) -> Value {
Value::Float(*self as f64)
}
}
impl TypedArrayElement for f64 {
fn from_value(v: Value) -> Option<Self> {
match v {
Value::Int(v) => {
Some(v as f64)
},
Value::Float(v) => {
Some(v)
},
_ => None
}
}
fn to_value(&self) -> Value {
Value::Float(*self)
}
}