From a633c9888695af02a539ee95451bf6954458f418 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 21 Feb 2017 14:48:53 +0100 Subject: [PATCH] Use Heap for typed arrays. --- src/typedarray.rs | 54 ++++++++++++---------- tests/typedarray.rs | 110 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 118 insertions(+), 46 deletions(-) diff --git a/src/typedarray.rs b/src/typedarray.rs index 409c61900..b011423e6 100644 --- a/src/typedarray.rs +++ b/src/typedarray.rs @@ -18,8 +18,8 @@ use glue::GetUint8ClampedArrayLengthAndData; use jsapi::GetArrayBufferLengthAndData; use jsapi::GetArrayBufferViewLengthAndData; use jsapi::HandleObject; +use jsapi::Heap; use jsapi::JSContext; -use jsapi::JSObject; use jsapi::JS_GetArrayBufferData; use jsapi::JS_GetArrayBufferViewType; use jsapi::JS_GetFloat32ArrayData; @@ -41,8 +41,8 @@ use jsapi::JS_NewUint16Array; use jsapi::JS_NewUint32Array; use jsapi::JS_NewUint8Array; use jsapi::JS_NewUint8ClampedArray; +use jsapi::JSObject; use jsapi::MutableHandleObject; -use jsapi::Rooted; use jsapi::Type; use jsapi::UnwrapArrayBuffer; use jsapi::UnwrapArrayBufferView; @@ -55,7 +55,6 @@ use jsapi::UnwrapUint16Array; use jsapi::UnwrapUint32Array; use jsapi::UnwrapUint8Array; use jsapi::UnwrapUint8ClampedArray; -use rust::RootedGuard; use std::ptr; use std::slice; @@ -65,32 +64,33 @@ pub enum CreateWith<'a, T: 'a> { } /// A rooted typed array. -pub struct TypedArray<'a, T: 'a + TypedArrayElement> { - object: RootedGuard<'a, *mut JSObject>, +pub struct TypedArray { + object: Box>, computed: Option<(*mut T::Element, u32)>, } -impl<'a, T: TypedArrayElement> TypedArray<'a, T> { +impl TypedArray { /// Create a typed array representation that wraps an existing JS reflector. /// This operation will fail if attempted on a JS object that does not match /// the expected typed array details. pub fn from(cx: *mut JSContext, - root: &'a mut Rooted<*mut JSObject>, object: *mut JSObject) -> Result { + rooted!(in (cx) let object = object); if object.is_null() { return Err(()); } unsafe { - let mut guard = RootedGuard::new(cx, root, object); - let unwrapped = T::unwrap_array(*guard); + let unwrapped = T::unwrap_array(object.get()); if unwrapped.is_null() { return Err(()); } - *guard = unwrapped; + let array = Box::new(Heap::new(ptr::null_mut())); + array.set(unwrapped); + Ok(TypedArray { - object: guard, + object: array, computed: None, }) } @@ -101,7 +101,7 @@ impl<'a, T: TypedArrayElement> TypedArray<'a, T> { return data; } - let data = unsafe { T::length_and_data(*self.object) }; + let data = unsafe { T::length_and_data(self.object.get()) }; self.computed = Some(data); data } @@ -126,9 +126,13 @@ impl<'a, T: TypedArrayElement> TypedArray<'a, T> { let (pointer, length) = self.data(); slice::from_raw_parts_mut(pointer, length as usize) } + + pub fn object(&self) -> &Heap<*mut JSObject> { + &self.object + } } -impl<'a, T: TypedArrayElementCreator + TypedArrayElement> TypedArray<'a, T> { +impl TypedArray { /// Create a new JS typed array, optionally providing initial data that will /// be copied into the newly-allocated buffer. Returns the new JS reflector. pub unsafe fn create(cx: *mut JSContext, @@ -297,29 +301,29 @@ typed_array_element!(ArrayBufferViewU8, GetArrayBufferViewLengthAndData); /// The Uint8ClampedArray type. -pub type Uint8ClampedArray<'a> = TypedArray<'a, ClampedU8>; +pub type Uint8ClampedArray = TypedArray; /// The Uint8Array type. -pub type Uint8Array<'a> = TypedArray<'a, Uint8>; +pub type Uint8Array = TypedArray; /// The Int8Array type. -pub type Int8Array<'a> = TypedArray<'a, Int8>; +pub type Int8Array = TypedArray; /// The Uint16Array type. -pub type Uint16Array<'a> = TypedArray<'a, Uint16>; +pub type Uint16Array = TypedArray; /// The Int16Array type. -pub type Int16Array<'a> = TypedArray<'a, Int16>; +pub type Int16Array = TypedArray; /// The Uint32Array type. -pub type Uint32Array<'a> = TypedArray<'a, Uint32>; +pub type Uint32Array = TypedArray; /// The Int32Array type. -pub type Int32Array<'a> = TypedArray<'a, Int32>; +pub type Int32Array = TypedArray; /// The Float32Array type. -pub type Float32Array<'a> = TypedArray<'a, Float32>; +pub type Float32Array = TypedArray; /// The Float64Array type. -pub type Float64Array<'a> = TypedArray<'a, Float64>; +pub type Float64Array = TypedArray; /// The ArrayBuffer type. -pub type ArrayBuffer<'a> = TypedArray<'a, ArrayBufferU8>; +pub type ArrayBuffer = TypedArray; /// The ArrayBufferView type -pub type ArrayBufferView<'a> = TypedArray<'a, ArrayBufferViewU8>; +pub type ArrayBufferView = TypedArray; -impl<'a> ArrayBufferView<'a> { +impl ArrayBufferView { pub fn get_array_type(&self) -> Type { unsafe { JS_GetArrayBufferViewType(self.object.get()) } } diff --git a/tests/typedarray.rs b/tests/typedarray.rs index b145aa6c9..7b5e7259d 100644 --- a/tests/typedarray.rs +++ b/tests/typedarray.rs @@ -5,23 +5,89 @@ #[macro_use] extern crate js; +use js::glue::CallObjectTracer; use js::jsapi::CompartmentOptions; +use js::jsapi::GCTraceKindToAscii; +use js::jsapi::Heap; +use js::jsapi::JS_AddExtraGCRootsTracer; use js::jsapi::JSAutoCompartment; use js::jsapi::JS_NewGlobalObject; +use js::jsapi::JSObject; +use js::jsapi::JSTracer; use js::jsapi::OnNewGlobalHookOption; +use js::jsapi::TraceKind; use js::jsapi::Type; use js::jsval::UndefinedValue; use js::rust::Runtime as Runtime_; use js::rust::SIMPLE_GLOBAL_CLASS; -use js::typedarray::{CreateWith, Uint32Array}; +use js::typedarray::{ArrayBufferView, CreateWith, TypedArray, TypedArrayElement, Uint8Array, Uint16Array, Uint32Array}; +use std::cell::RefCell; +use std::ops::{Deref, DerefMut}; +use std::os::raw::c_void; use std::ptr; +thread_local!( + static ROOTED_TRACEABLES: RefCell>> = RefCell::new(Vec::new()); +); + +unsafe extern fn trace(tracer: *mut JSTracer, _: *mut c_void) { + ROOTED_TRACEABLES.with(|v| { + for &array in &*v.borrow() { + CallObjectTracer(tracer, + array as *mut _, + GCTraceKindToAscii(TraceKind::Object)); + } + }); +} + +struct Tracer { + array: TypedArray, +} + +impl Tracer { + fn new(array: TypedArray) -> Self { + let ptr = array.object() as *const _; + ROOTED_TRACEABLES.with(|v| { + v.borrow_mut().push(ptr); + }); + Tracer { + array: array, + } + } +} + + +impl Deref for Tracer { + type Target = TypedArray; + fn deref(&self) -> &Self::Target { + &self.array + } +} + +impl DerefMut for Tracer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.array + } +} + +impl Drop for Tracer { + fn drop(&mut self) { + let ptr = ROOTED_TRACEABLES.with(|v| { + v.borrow_mut().pop() + }); + assert_eq!(self.array.object() as *const _, ptr.unwrap()); + } +} + + #[test] fn typedarray() { let rt = Runtime_::new(); let cx = rt.cx(); unsafe { + JS_AddExtraGCRootsTracer(rt.rt(), Some(trace), ptr::null_mut()); + rooted!(in(cx) let global = JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(), OnNewGlobalHookOption::FireOnNewGlobalHook, @@ -35,41 +101,41 @@ fn typedarray() { "test", 1, rval.handle_mut()).is_ok()); assert!(rval.is_object()); - typedarray!(in(cx) let array: Uint8Array = rval.to_object()); - assert_eq!(array.unwrap().as_slice(), &[0, 2, 4][..]); + let mut array = Tracer::new(Uint8Array::from(cx, rval.to_object()).unwrap()); + assert_eq!(array.as_slice(), &[0, 2, 4][..]); - typedarray!(in(cx) let array: Uint16Array = rval.to_object()); + let array = Uint16Array::from(cx, rval.to_object()); assert!(array.is_err()); - typedarray!(in(cx) let view: ArrayBufferView = rval.to_object()); - assert_eq!(view.unwrap().get_array_type(), Type::Uint8); + let view = Tracer::new(ArrayBufferView::from(cx, rval.to_object()).unwrap()); + assert_eq!(view.get_array_type(), Type::Uint8); rooted!(in(cx) let mut rval = ptr::null_mut()); assert!(Uint32Array::create(cx, CreateWith::Slice(&[1, 3, 5]), rval.handle_mut()).is_ok()); - typedarray!(in(cx) let array: Uint32Array = rval.get()); - assert_eq!(array.unwrap().as_slice(), &[1, 3, 5][..]); + let mut array = Tracer::new(Uint32Array::from(cx, rval.get()).unwrap()); + assert_eq!(array.as_slice(), &[1, 3, 5][..]); - typedarray!(in(cx) let mut array: Uint32Array = rval.get()); - array.as_mut().unwrap().update(&[2, 4, 6]); - assert_eq!(array.unwrap().as_slice(), &[2, 4, 6][..]); + let mut array = Tracer::new(Uint32Array::from(cx, rval.get()).unwrap()); + array.update(&[2, 4, 6]); + assert_eq!(array.as_slice(), &[2, 4, 6][..]); rooted!(in(cx) let rval = ptr::null_mut()); - typedarray!(in(cx) let array: Uint8Array = rval.get()); + let array = Uint8Array::from(cx, rval.get()); assert!(array.is_err()); rooted!(in(cx) let mut rval = ptr::null_mut()); assert!(Uint32Array::create(cx, CreateWith::Length(5), rval.handle_mut()).is_ok()); - typedarray!(in(cx) let array: Uint32Array = rval.get()); - assert_eq!(array.unwrap().as_slice(), &[0, 0, 0, 0, 0]); + let mut array = Tracer::new(Uint32Array::from(cx, rval.get()).unwrap()); + assert_eq!(array.as_slice(), &[0, 0, 0, 0, 0]); - typedarray!(in(cx) let mut array: Uint32Array = rval.get()); - array.as_mut().unwrap().update(&[0, 1, 2, 3]); - assert_eq!(array.unwrap().as_slice(), &[0, 1, 2, 3, 0]); + let mut array = Tracer::new(Uint32Array::from(cx, rval.get()).unwrap()); + array.update(&[0, 1, 2, 3]); + assert_eq!(array.as_slice(), &[0, 1, 2, 3, 0]); - typedarray!(in(cx) let view: ArrayBufferView = rval.get()); - assert_eq!(view.unwrap().get_array_type(), Type::Uint32); + let view = Tracer::new(ArrayBufferView::from(cx, rval.get()).unwrap()); + assert_eq!(view.get_array_type(), Type::Uint32); } } @@ -80,6 +146,8 @@ fn typedarray_update_panic() { let cx = rt.cx(); unsafe { + JS_AddExtraGCRootsTracer(rt.rt(), Some(trace), ptr::null_mut()); + rooted!(in(cx) let global = JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(), OnNewGlobalHookOption::FireOnNewGlobalHook, @@ -89,7 +157,7 @@ fn typedarray_update_panic() { let _ac = JSAutoCompartment::new(cx, global.get()); rooted!(in(cx) let mut rval = ptr::null_mut()); let _ = Uint32Array::create(cx, CreateWith::Slice(&[1, 2, 3, 4, 5]), rval.handle_mut()); - typedarray!(in(cx) let mut array: Uint32Array = rval.get()); - array.as_mut().unwrap().update(&[0, 2, 4, 6, 8, 10]); + let mut array = Tracer::new(Uint32Array::from(cx, rval.get()).unwrap()); + array.update(&[0, 2, 4, 6, 8, 10]); } }