Skip to content
This repository was archived by the owner on Oct 21, 2022. It is now read-only.

make unsafe usages more safe #19

Merged
merged 1 commit into from
May 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/elements/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ impl Deserialize for VarInt7 {
// expand sign
if u8buf[0] & 0b0100_0000 == 0b0100_0000 { u8buf[0] |= 0b1000_0000 }
// todo check range
Ok(VarInt7(unsafe { ::std::mem::transmute (u8buf[0]) }))
Ok(VarInt7(u8buf[0] as i8))
}
}

Expand Down
16 changes: 8 additions & 8 deletions src/interpreter/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use interpreter::module::{ModuleInstanceInterface, ModuleInstance, ExecutionPara
ItemIndex, CallerContext};
use interpreter::memory::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TransmuteInto};
use interpreter::value::RuntimeValue;
use interpreter::variable::VariableInstance;

/// Memory address, at which stack begins.
Expand Down Expand Up @@ -169,23 +169,23 @@ pub fn env_module(params: EnvParams) -> Result<EnvModuleInstance, Error> {
.build()
.with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE)))
// globals
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE.transmute_into())])))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE as i32)])))
.with_export(ExportEntry::new("STACK_BASE".into(), Internal::Global(INDEX_GLOBAL_STACK_BASE)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE.transmute_into())])))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE as i32)])))
.with_export(ExportEntry::new("STACKTOP".into(), Internal::Global(INDEX_GLOBAL_STACK_TOP)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())])))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)])))
.with_export(ExportEntry::new("STACK_MAX".into(), Internal::Global(INDEX_GLOBAL_STACK_MAX)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())])))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)])))
.with_export(ExportEntry::new("DYNAMIC_BASE".into(), Internal::Global(INDEX_GLOBAL_DYNAMIC_BASE)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())])))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)])))
.with_export(ExportEntry::new("DYNAMICTOP_PTR".into(), Internal::Global(INDEX_GLOBAL_DYNAMICTOP_PTR)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory.transmute_into())])))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory as i32)])))
.with_export(ExportEntry::new("TOTAL_MEMORY".into(), Internal::Global(INDEX_GLOBAL_TOTAL_MEMORY)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(0)])))
.with_export(ExportEntry::new("ABORT".into(), Internal::Global(INDEX_GLOBAL_ABORT)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(0)])))
.with_export(ExportEntry::new("EXITSTATUS".into(), Internal::Global(INDEX_GLOBAL_EXIT_STATUS)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_TABLE_BASE.transmute_into())]))) // TODO: what is this?
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_TABLE_BASE as i32)]))) // TODO: what is this?
.with_export(ExportEntry::new("tableBase".into(), Internal::Global(INDEX_GLOBAL_TABLE_BASE)))
// functions
.function()
Expand Down
6 changes: 3 additions & 3 deletions src/interpreter/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use interpreter::program::ProgramInstanceEssence;
use interpreter::runner::{Interpreter, FunctionContext};
use interpreter::stack::StackWithLimit;
use interpreter::table::TableInstance;
use interpreter::value::{RuntimeValue, TryInto, TransmuteInto};
use interpreter::value::{RuntimeValue, TryInto};
use interpreter::variable::{VariableInstance, VariableType};

#[derive(Default, Clone)]
Expand Down Expand Up @@ -421,8 +421,8 @@ fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports) ->
},
&Opcode::I32Const(val) => Ok(RuntimeValue::I32(val)),
&Opcode::I64Const(val) => Ok(RuntimeValue::I64(val)),
&Opcode::F32Const(val) => Ok(RuntimeValue::F32(val.transmute_into())),
&Opcode::F64Const(val) => Ok(RuntimeValue::F64(val.transmute_into())),
&Opcode::F32Const(val) => Ok(RuntimeValue::decode_f32(val)),
&Opcode::F64Const(val) => Ok(RuntimeValue::decode_f64(val)),
_ => Err(Error::Initialization(format!("not-supported {:?} instruction in instantiation-time initializer", first_opcode))),
}
}
6 changes: 4 additions & 2 deletions src/interpreter/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ use elements::{Opcode, BlockType, FunctionType};
use interpreter::Error;
use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex};
use interpreter::stack::StackWithLimit;
use interpreter::value::{RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto, TransmuteInto,
ArithmeticOps, Integer, Float, LittleEndianConvert};
use interpreter::value::{
RuntimeValue, TryInto, WrapInto, TryTruncateInto, ExtendInto,
ArithmeticOps, Integer, Float, LittleEndianConvert, TransmuteInto,
};
use interpreter::variable::VariableInstance;

const DEFAULT_MEMORY_INDEX: u32 = 0;
Expand Down
113 changes: 76 additions & 37 deletions src/interpreter/value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::{i32, i64, u32, u64, f32};
use std::io;
use std::mem;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use interpreter::Error;
use interpreter::variable::VariableType;
Expand Down Expand Up @@ -54,9 +53,9 @@ pub trait TransmuteInto<T> {

/// Convert from and to little endian.
pub trait LittleEndianConvert where Self: Sized {
/// Convert to little endian bufer.
/// Convert to little endian buffer.
fn into_little_endian(self) -> Vec<u8>;
/// Convert from little endian bufer.
/// Convert from little endian buffer.
fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error>;
}

Expand Down Expand Up @@ -124,12 +123,12 @@ impl RuntimeValue {

/// Creates new value by interpreting passed u32 as f32.
pub fn decode_f32(val: u32) -> Self {
RuntimeValue::F32(val.transmute_into())
RuntimeValue::F32(f32_from_bits(val))
}

/// Creates new value by interpreting passed u64 as f64.
pub fn decode_f64(val: u64) -> Self {
RuntimeValue::F64(val.transmute_into())
RuntimeValue::F64(f64_from_bits(val))
}

/// Returns true if value is null.
Expand Down Expand Up @@ -224,7 +223,6 @@ impl TryInto<f32, Error> for RuntimeValue {
impl TryInto<f64, Error> for RuntimeValue {
fn try_into(self) -> Result<f64, Error> {
match self {
//RuntimeValue::F32(val) => Some(val as f64),
RuntimeValue::F64(val) => Ok(val),
_ => Err(Error::Value(format!("64-bit float value expected"))),
}
Expand All @@ -234,9 +232,7 @@ impl TryInto<f64, Error> for RuntimeValue {
impl TryInto<u32, Error> for RuntimeValue {
fn try_into(self) -> Result<u32, Error> {
match self {
RuntimeValue::I32(val) => Ok(unsafe {
mem::transmute(val)
}),
RuntimeValue::I32(val) => Ok(val as u32),
_ => Err(Error::Value(format!("32-bit int value expected"))),
}
}
Expand All @@ -245,9 +241,7 @@ impl TryInto<u32, Error> for RuntimeValue {
impl TryInto<u64, Error> for RuntimeValue {
fn try_into(self) -> Result<u64, Error> {
match self {
RuntimeValue::I64(val) => Ok(unsafe {
mem::transmute(val)
}),
RuntimeValue::I64(val) => Ok(val as u64),
_ => Err(Error::Value(format!("64-bit int value expected"))),
}
}
Expand Down Expand Up @@ -347,39 +341,48 @@ impl_transmute_into_self!(i64);
impl_transmute_into_self!(f32);
impl_transmute_into_self!(f64);

macro_rules! impl_transmute_into {
macro_rules! impl_transmute_into_as {
($from: ident, $into: ident) => {
impl TransmuteInto<$into> for $from {
fn transmute_into(self) -> $into {
unsafe {
mem::transmute(self)
}
self as $into
}
}
}
}

impl_transmute_into!(i8, u8);
impl_transmute_into!(u8, i8);
impl_transmute_into!(i32, u32);
impl_transmute_into!(u32, i32);
impl_transmute_into!(u32, f32);
impl_transmute_into!(i32, f32);
impl_transmute_into!(f32, i32);
impl_transmute_into!(i64, u64);
impl_transmute_into!(u64, i64);
impl_transmute_into!(u64, f64);
impl_transmute_into!(i64, f64);
impl_transmute_into!(f64, i64);
impl_transmute_into_as!(i8, u8);
impl_transmute_into_as!(u8, i8);
impl_transmute_into_as!(i32, u32);
impl_transmute_into_as!(u32, i32);
impl_transmute_into_as!(i64, u64);
impl_transmute_into_as!(u64, i64);

// TODO: rewrite these safely when `f32/f32::to_bits/from_bits` stabilized.
impl TransmuteInto<i32> for f32 {
fn transmute_into(self) -> i32 { unsafe { ::std::mem::transmute(self) } }
}

impl TransmuteInto<i64> for f64 {
fn transmute_into(self) -> i64 { unsafe { ::std::mem::transmute(self) } }
}

impl TransmuteInto<f32> for i32 {
fn transmute_into(self) -> f32 { f32_from_bits(self as _) }
}

impl TransmuteInto<f64> for i64 {
fn transmute_into(self) -> f64 { f64_from_bits(self as _) }
}

impl LittleEndianConvert for i8 {
fn into_little_endian(self) -> Vec<u8> {
vec![self.transmute_into()]
vec![self as u8]
}

fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
buffer.get(0)
.map(|v| v.transmute_into())
.map(|v| *v as i8)
.ok_or(Error::Value("invalid little endian buffer".into()))
}
}
Expand Down Expand Up @@ -469,33 +472,69 @@ impl LittleEndianConvert for i64 {
impl LittleEndianConvert for f32 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(4);
vec.write_i32::<LittleEndian>(self.transmute_into())
.expect("i32 is written without any errors");
vec.write_f32::<LittleEndian>(self)
.expect("f32 is written without any errors");
vec
}

fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_i32::<LittleEndian>()
.map(TransmuteInto::transmute_into)
io::Cursor::new(buffer).read_u32::<LittleEndian>()
.map(f32_from_bits)
.map_err(|e| Error::Value(e.to_string()))
}
}

impl LittleEndianConvert for f64 {
fn into_little_endian(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(8);
vec.write_i64::<LittleEndian>(self.transmute_into())
vec.write_f64::<LittleEndian>(self)
.expect("i64 is written without any errors");
vec
}

fn from_little_endian(buffer: Vec<u8>) -> Result<Self, Error> {
io::Cursor::new(buffer).read_i64::<LittleEndian>()
.map(TransmuteInto::transmute_into)
io::Cursor::new(buffer).read_u64::<LittleEndian>()
.map(f64_from_bits)
.map_err(|e| Error::Value(e.to_string()))
}
}

// Convert u32 to f32 safely, masking out sNAN
fn f32_from_bits(mut v: u32) -> f32 {
const EXP_MASK: u32 = 0x7F800000;
const QNAN_MASK: u32 = 0x00400000;
const FRACT_MASK: u32 = 0x007FFFFF;

if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
// If we have a NaN value, we
// convert signaling NaN values to quiet NaN
// by setting the the highest bit of the fraction
// TODO: remove when https://github.com/BurntSushi/byteorder/issues/71 closed.
// or `f32::from_bits` stabilized.
v |= QNAN_MASK;
}

unsafe { ::std::mem::transmute(v) }
}

// Convert u64 to f64 safely, masking out sNAN
fn f64_from_bits(mut v: u64) -> f64 {
const EXP_MASK: u64 = 0x7FF0000000000000;
const QNAN_MASK: u64 = 0x0001000000000000;
const FRACT_MASK: u64 = 0x000FFFFFFFFFFFFF;

if v & EXP_MASK == EXP_MASK && v & FRACT_MASK != 0 {
// If we have a NaN value, we
// convert signaling NaN values to quiet NaN
// by setting the the highest bit of the fraction
// TODO: remove when https://github.com/BurntSushi/byteorder/issues/71 closed.
// or `f64::from_bits` stabilized.
v |= QNAN_MASK;
}

unsafe { ::std::mem::transmute(v) }
}

macro_rules! impl_integer_arithmetic_ops {
($type: ident) => {
impl ArithmeticOps<$type> for $type {
Expand Down