conversion: Extrapolate array_buffer_view_data

And use it instead of the raw jsapi calls.
This commit is contained in:
Emilio Cobos Álvarez 2016-01-04 14:58:57 +01:00
parent 4d32444bf7
commit 9ad49c8aa1
4 changed files with 76 additions and 49 deletions

View file

@ -48,11 +48,12 @@ use js::jsapi::{JSClass, JSContext, JSObject, MutableHandleValue};
use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetReservedSlot}; use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetReservedSlot};
use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN}; use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN};
use js::jsapi::{JS_StringHasLatin1Chars, JS_WrapValue}; use js::jsapi::{JS_StringHasLatin1Chars, JS_WrapValue};
use js::jsapi::{JS_GetObjectAsArrayBufferView};
use js::jsval::{ObjectValue, StringValue}; use js::jsval::{ObjectValue, StringValue};
use js::rust::ToString; use js::rust::ToString;
use libc; use libc;
use num::Float; use num::Float;
use std::{ptr, slice}; use std::{ptr, mem, slice};
use util::str::DOMString; use util::str::DOMString;
pub use util::str::{StringificationBehavior, jsstring_to_str}; pub use util::str::{StringificationBehavior, jsstring_to_str};
@ -335,3 +336,32 @@ impl<T: Reflectable> ToJSValConvertible for Root<T> {
self.reflector().to_jsval(cx, rval); self.reflector().to_jsval(cx, rval);
} }
} }
/// A JS ArrayBufferView contents can only be viewed as the types marked with this trait
pub unsafe trait ArrayBufferViewContents: Clone {}
unsafe impl ArrayBufferViewContents for u8 {}
unsafe impl ArrayBufferViewContents for i8 {}
unsafe impl ArrayBufferViewContents for u16 {}
unsafe impl ArrayBufferViewContents for i16 {}
unsafe impl ArrayBufferViewContents for u32 {}
unsafe impl ArrayBufferViewContents for i32 {}
unsafe impl ArrayBufferViewContents for f32 {}
unsafe impl ArrayBufferViewContents for f64 {}
/// Returns a mutable slice of the Array Buffer View data, viewed as T
pub unsafe fn array_buffer_view_data<'a, T: ArrayBufferViewContents>(abv: *mut JSObject) -> Option<&'a mut [T]> {
let mut byte_length = 0;
let mut ptr = ptr::null_mut();
let ret = JS_GetObjectAsArrayBufferView(abv, &mut byte_length, &mut ptr);
if ret.is_null() {
return None;
}
Some(slice::from_raw_parts_mut(ptr as *mut T, byte_length as usize / mem::size_of::<T>()))
}
/// Returns a copy of the ArrayBufferView data
pub fn array_buffer_view_to_vec<T: ArrayBufferViewContents>(abv: *mut JSObject) -> Option<Vec<T>> {
unsafe {
array_buffer_view_data(abv).map(|data| data.to_vec())
}
}

View file

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::conversions::array_buffer_view_data;
use dom::bindings::codegen::Bindings::CryptoBinding; use dom::bindings::codegen::Bindings::CryptoBinding;
use dom::bindings::codegen::Bindings::CryptoBinding::CryptoMethods; use dom::bindings::codegen::Bindings::CryptoBinding::CryptoMethods;
use dom::bindings::error::{Error, Fallible}; use dom::bindings::error::{Error, Fallible};
@ -10,9 +11,8 @@ use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root; use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflector, reflect_dom_object};
use js::jsapi::{JSContext, JSObject}; use js::jsapi::{JSContext, JSObject};
use js::jsapi::{JS_GetArrayBufferViewType, JS_GetObjectAsArrayBufferView, Type}; use js::jsapi::{JS_GetArrayBufferViewType, Type};
use rand::{OsRng, Rng}; use rand::{OsRng, Rng};
use std::{ptr, slice};
no_jsmanaged_fields!(OsRng); no_jsmanaged_fields!(OsRng);
@ -43,24 +43,23 @@ impl CryptoMethods for Crypto {
_cx: *mut JSContext, _cx: *mut JSContext,
input: *mut JSObject) input: *mut JSObject)
-> Fallible<*mut JSObject> { -> Fallible<*mut JSObject> {
let mut length = 0; let mut data = match unsafe { array_buffer_view_data::<u8>(input) } {
let mut data = ptr::null_mut(); Some(data) => data,
if unsafe { JS_GetObjectAsArrayBufferView(input, &mut length, &mut data).is_null() } { None => {
return Err(Error::Type("Argument to Crypto.getRandomValues is not an ArrayBufferView" return Err(Error::Type("Argument to Crypto.getRandomValues is not an ArrayBufferView"
.to_owned())); .to_owned()));
} }
};
if !is_integer_buffer(input) { if !is_integer_buffer(input) {
return Err(Error::TypeMismatch); return Err(Error::TypeMismatch);
} }
if length > 65536 {
if data.len() > 65536 {
return Err(Error::QuotaExceeded); return Err(Error::QuotaExceeded);
} }
let mut buffer = unsafe { self.rng.borrow_mut().fill_bytes(&mut data);
slice::from_raw_parts_mut(data, length as usize)
};
self.rng.borrow_mut().fill_bytes(&mut buffer);
Ok(input) Ok(input)
} }

View file

@ -4,6 +4,7 @@
use dom::bindings::codegen::Bindings::TextDecoderBinding; use dom::bindings::codegen::Bindings::TextDecoderBinding;
use dom::bindings::codegen::Bindings::TextDecoderBinding::TextDecoderMethods; use dom::bindings::codegen::Bindings::TextDecoderBinding::TextDecoderMethods;
use dom::bindings::conversions::array_buffer_view_data;
use dom::bindings::error::{Error, Fallible}; use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root; use dom::bindings::js::Root;
@ -13,10 +14,8 @@ use dom::bindings::trace::JSTraceable;
use encoding::Encoding; use encoding::Encoding;
use encoding::label::encoding_from_whatwg_label; use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, EncodingRef}; use encoding::types::{DecoderTrap, EncodingRef};
use js::jsapi::JS_GetObjectAsArrayBufferView;
use js::jsapi::{JSContext, JSObject}; use js::jsapi::{JSContext, JSObject};
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::{ptr, slice};
use util::str::DOMString; use util::str::DOMString;
#[dom_struct] #[dom_struct]
@ -89,21 +88,20 @@ impl TextDecoderMethods for TextDecoder {
None => return Ok(USVString("".to_owned())), None => return Ok(USVString("".to_owned())),
}; };
let mut length = 0; let data = match unsafe { array_buffer_view_data::<u8>(input) } {
let mut data = ptr::null_mut(); Some(data) => data,
if unsafe { JS_GetObjectAsArrayBufferView(input, &mut length, &mut data).is_null() } { None => {
return Err(Error::Type("Argument to TextDecoder.decode is not an ArrayBufferView".to_owned())); return Err(Error::Type("Argument to TextDecoder.decode is not an ArrayBufferView".to_owned()));
} }
let buffer = unsafe {
slice::from_raw_parts(data as *const _, length as usize)
}; };
let trap = if self.fatal { let trap = if self.fatal {
DecoderTrap::Strict DecoderTrap::Strict
} else { } else {
DecoderTrap::Replace DecoderTrap::Replace
}; };
match self.encoding.decode(buffer, trap) {
match self.encoding.decode(data, trap) {
Ok(s) => Ok(USVString(s)), Ok(s) => Ok(USVString(s)),
Err(_) => Err(Error::Type("Decoding failed".to_owned())), Err(_) => Err(Error::Type("Decoding failed".to_owned())),
} }

View file

@ -9,7 +9,7 @@ use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderi
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{WebGLRenderingContextMethods}; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{WebGLRenderingContextMethods};
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes}; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes};
use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement; use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::conversions::{ToJSValConvertible, array_buffer_view_to_vec};
use dom::bindings::global::{GlobalField, GlobalRef}; use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root}; use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
@ -29,7 +29,6 @@ use dom::webgluniformlocation::WebGLUniformLocation;
use euclid::size::Size2D; use euclid::size::Size2D;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::{JSContext, JSObject, RootedValue}; use js::jsapi::{JSContext, JSObject, RootedValue};
use js::jsapi::{JS_GetObjectAsArrayBufferView};
use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue}; use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue};
use net_traits::image::base::PixelFormat; use net_traits::image::base::PixelFormat;
use net_traits::image_cache_task::ImageResponse; use net_traits::image_cache_task::ImageResponse;
@ -37,7 +36,6 @@ use offscreen_gl_context::GLContextAttributes;
use script_traits::ScriptMsg as ConstellationMsg; use script_traits::ScriptMsg as ConstellationMsg;
use std::cell::Cell; use std::cell::Cell;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::{ptr, slice, mem};
use util::str::DOMString; use util::str::DOMString;
use util::vec::byte_swap; use util::vec::byte_swap;
@ -166,20 +164,6 @@ impl WebGLRenderingContext {
self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage); self.canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
} }
#[allow(unsafe_code)]
fn array_buffer_view_to_vec<T: Clone>(&self, data: *mut JSObject) -> Option<Vec<T>> {
unsafe {
let mut byte_length = 0;
let mut ptr = ptr::null_mut();
let buffer_data = JS_GetObjectAsArrayBufferView(data, &mut byte_length, &mut ptr);
if buffer_data.is_null() {
self.webgl_error(InvalidValue); // https://github.com/servo/servo/issues/5014
return None;
}
Some(slice::from_raw_parts(ptr as *mut T, byte_length as usize / mem::size_of::<T>()).to_vec())
}
}
fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) { fn vertex_attrib(&self, indx: u32, x: f32, y: f32, z: f32, w: f32) {
self.ipc_renderer self.ipc_renderer
.send(CanvasMsg::WebGL(CanvasWebGLMsg::VertexAttrib(indx, x, y, z, w))) .send(CanvasMsg::WebGL(CanvasWebGLMsg::VertexAttrib(indx, x, y, z, w)))
@ -456,8 +440,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Some(data) => data, Some(data) => data,
None => return self.webgl_error(InvalidValue), None => return self.webgl_error(InvalidValue),
}; };
if let Some(data_vec) = self.array_buffer_view_to_vec::<u8>(data) { if let Some(data_vec) = array_buffer_view_to_vec::<u8>(data) {
handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data_vec, usage)); handle_potential_webgl_error!(self, bound_buffer.buffer_data(target, &data_vec, usage));
} else {
// NB: array_buffer_view_to_vec should never fail when
// we have WebIDL support for Float32Array etc.
self.webgl_error(InvalidValue);
} }
} }
@ -480,13 +468,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
if offset < 0 { if offset < 0 {
return self.webgl_error(InvalidValue); return self.webgl_error(InvalidValue);
} }
if let Some(data_vec) = self.array_buffer_view_to_vec::<u8>(data) { if let Some(data_vec) = array_buffer_view_to_vec::<u8>(data) {
if (offset as usize) + data_vec.len() > bound_buffer.capacity() { if (offset as usize) + data_vec.len() > bound_buffer.capacity() {
return self.webgl_error(InvalidValue); return self.webgl_error(InvalidValue);
} }
self.ipc_renderer self.ipc_renderer
.send(CanvasMsg::WebGL(CanvasWebGLMsg::BufferSubData(target, offset as isize, data_vec))) .send(CanvasMsg::WebGL(CanvasWebGLMsg::BufferSubData(target, offset as isize, data_vec)))
.unwrap() .unwrap()
} else {
self.webgl_error(InvalidValue);
} }
} }
@ -958,7 +948,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
None => return, None => return,
}; };
if let Some(data_vec) = self.array_buffer_view_to_vec::<f32>(data) { if let Some(data_vec) = array_buffer_view_to_vec::<f32>(data) {
if data_vec.len() < 4 { if data_vec.len() < 4 {
return self.webgl_error(InvalidOperation); return self.webgl_error(InvalidOperation);
} }
@ -966,6 +956,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
self.ipc_renderer self.ipc_renderer
.send(CanvasMsg::WebGL(CanvasWebGLMsg::Uniform4fv(uniform_id, data_vec))) .send(CanvasMsg::WebGL(CanvasWebGLMsg::Uniform4fv(uniform_id, data_vec)))
.unwrap() .unwrap()
} else {
self.webgl_error(InvalidValue);
} }
} }
@ -984,11 +976,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)] #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn VertexAttrib1fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) { fn VertexAttrib1fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
if let Some(data_vec) = self.array_buffer_view_to_vec::<f32>(data) { if let Some(data_vec) = array_buffer_view_to_vec::<f32>(data) {
if data_vec.len() < 4 { if data_vec.len() < 4 {
return self.webgl_error(InvalidOperation); return self.webgl_error(InvalidOperation);
} }
self.vertex_attrib(indx, data_vec[0], 0f32, 0f32, 1f32) self.vertex_attrib(indx, data_vec[0], 0f32, 0f32, 1f32)
} else {
self.webgl_error(InvalidValue);
} }
} }
@ -1000,11 +994,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)] #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn VertexAttrib2fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) { fn VertexAttrib2fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
if let Some(data_vec) = self.array_buffer_view_to_vec::<f32>(data) { if let Some(data_vec) = array_buffer_view_to_vec::<f32>(data) {
if data_vec.len() < 2 { if data_vec.len() < 2 {
return self.webgl_error(InvalidOperation); return self.webgl_error(InvalidOperation);
} }
self.vertex_attrib(indx, data_vec[0], data_vec[1], 0f32, 1f32) self.vertex_attrib(indx, data_vec[0], data_vec[1], 0f32, 1f32)
} else {
self.webgl_error(InvalidValue);
} }
} }
@ -1016,11 +1012,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)] #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn VertexAttrib3fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) { fn VertexAttrib3fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
if let Some(data_vec) = self.array_buffer_view_to_vec::<f32>(data) { if let Some(data_vec) = array_buffer_view_to_vec::<f32>(data) {
if data_vec.len() < 3 { if data_vec.len() < 3 {
return self.webgl_error(InvalidOperation); return self.webgl_error(InvalidOperation);
} }
self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], 1f32) self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], 1f32)
} else {
self.webgl_error(InvalidValue);
} }
} }
@ -1032,11 +1030,13 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
#[allow(unsafe_code)] #[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn VertexAttrib4fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) { fn VertexAttrib4fv(&self, _cx: *mut JSContext, indx: u32, data: *mut JSObject) {
if let Some(data_vec) = self.array_buffer_view_to_vec::<f32>(data) { if let Some(data_vec) = array_buffer_view_to_vec::<f32>(data) {
if data_vec.len() < 4 { if data_vec.len() < 4 {
return self.webgl_error(InvalidOperation); return self.webgl_error(InvalidOperation);
} }
self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], data_vec[3]) self.vertex_attrib(indx, data_vec[0], data_vec[1], data_vec[2], data_vec[3])
} else {
self.webgl_error(InvalidValue);
} }
} }