Skip to content

Commit

Permalink
Added Support for ArrayBuffer to TextEncoder::encodeInto
Browse files Browse the repository at this point in the history
Added BufferSource
Enabled SharedArrayBuffer Constructor, Shared Memory and Atomics
  • Loading branch information
Redfire75369 committed Dec 24, 2023
1 parent 31aed8d commit c2b947f
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 23 deletions.
2 changes: 1 addition & 1 deletion ion/src/functions/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub(crate) unsafe extern "C" fn call_closure_once(cx: *mut JSContext, argc: u32,
__handle_native_function_result(cx, result)
} else {
Error::new("ClosureOnce was called more than once.", ErrorKind::Type).throw(cx);
return false;
false
}
}

Expand Down
15 changes: 10 additions & 5 deletions ion/src/objects/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,16 @@ pub const fn class_reserved_slots(slots: u32) -> u32 {
(slots & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT
}

pub fn new_global<'cx, P: Into<Option<*mut JSPrincipals>>, R: Into<Option<RealmOptions>>>(
cx: &'cx Context, class: &JSClass, principals: P, hook_option: OnNewGlobalHookOption, realm_options: R,
pub fn new_global<'cx>(
cx: &'cx Context, class: &JSClass, principals: Option<*mut JSPrincipals>, hook_option: OnNewGlobalHookOption,
realm_options: Option<RealmOptions>,
) -> Object<'cx> {
let realm_options = realm_options.into().unwrap_or_default();
let realm_options = realm_options.unwrap_or_default();
let global = unsafe {
JS_NewGlobalObject(
cx.as_ptr(),
class,
principals.into().unwrap_or_else(ptr::null_mut),
principals.unwrap_or_else(ptr::null_mut),
hook_option,
&*realm_options,
)
Expand All @@ -55,11 +56,15 @@ pub fn new_global<'cx, P: Into<Option<*mut JSPrincipals>>, R: Into<Option<RealmO
}

pub fn default_new_global(cx: &Context) -> Object {
let mut options = RealmOptions::default();
options.creationOptions_.sharedMemoryAndAtomics_ = true;
options.creationOptions_.defineSharedArrayBufferConstructor_ = true;

new_global(
cx,
&SIMPLE_GLOBAL_CLASS,
None,
OnNewGlobalHookOption::FireOnNewGlobalHook,
None,
Some(options),
)
}
15 changes: 12 additions & 3 deletions runtime/src/globals/encoding/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
*/

use encoding_rs::{Decoder, DecoderResult, Encoding, UTF_8};
use mozjs::typedarray::ArrayBufferView;

use ion::{Error, ErrorKind, Result};
use ion::class::Reflector;
use crate::globals::file::BufferSource;

#[derive(Default, FromValue)]
pub struct TextDecoderOptions {
Expand Down Expand Up @@ -70,11 +70,20 @@ impl TextDecoder {
})
}

pub fn decode(&mut self, buffer: ArrayBufferView, options: Option<TextDecodeOptions>) -> Result<String> {
pub fn decode(
&mut self, #[ion(convert = true)] buffer: BufferSource, options: Option<TextDecodeOptions>,
) -> Result<String> {
let mut string = String::with_capacity(self.decoder.max_utf8_buffer_length(buffer.len()).unwrap());
let stream = options.unwrap_or_default().stream;
if self.fatal {
let buffer = unsafe { buffer.as_slice() };
let vec_buffer;
let buffer = if buffer.is_shared() {
vec_buffer = buffer.to_vec();
&vec_buffer
} else {
unsafe { buffer.as_slice() }
};

let (result, _) = self.decoder.decode_to_string_without_replacement(buffer, &mut string, !stream);
if let DecoderResult::Malformed(_, _) = result {
return Err(Error::new("TextDecoder.decode: Decoding Failed", ErrorKind::Type));
Expand Down
6 changes: 3 additions & 3 deletions runtime/src/globals/fetch/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use mozjs::jsval::JSVal;
use ion::{Context, Error, ErrorKind, Result, Value};
use ion::conversions::FromValue;

use crate::globals::file::{Blob, buffer_source_to_bytes};
use crate::globals::file::{Blob, BufferSource};
use crate::globals::url::URLSearchParams;

#[derive(Debug, Clone, Traceable)]
Expand Down Expand Up @@ -112,9 +112,9 @@ impl<'cx> FromValue<'cx> for FetchBody {
kind: Some(FetchBodyKind::String),
});
} else if value.handle().is_object() {
if let Ok(bytes) = buffer_source_to_bytes(&value.to_object(cx)) {
if let Ok(source) = BufferSource::from_value(cx, value, strict, false) {
return Ok(FetchBody {
body: FetchBodyInner::Bytes(bytes),
body: FetchBodyInner::Bytes(source.to_bytes()),
source: Some(Heap::boxed(value.get())),
kind: None,
});
Expand Down
74 changes: 64 additions & 10 deletions runtime/src/globals/file/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use std::marker::PhantomData;
use std::str::FromStr;

use bytes::Bytes;
Expand All @@ -19,14 +20,67 @@ use ion::format::NEWLINE;

use crate::promise::future_to_promise;

pub fn buffer_source_to_bytes(object: &Object) -> Result<Bytes> {
let obj = object.handle().get();
if let Ok(arr) = ArrayBuffer::from(obj) {
Ok(Bytes::copy_from_slice(unsafe { arr.as_slice() }))
} else if let Ok(arr) = ArrayBufferView::from(obj) {
Ok(Bytes::copy_from_slice(unsafe { arr.as_slice() }))
} else {
Err(Error::new("Object is not a buffer source.", ErrorKind::Type))
enum BufferSourceInner {
Buffer(ArrayBuffer),
View(ArrayBufferView),
}

pub struct BufferSource<'cx> {
source: BufferSourceInner,
lifetime: PhantomData<&'cx Object<'cx>>,
}

impl BufferSource<'_> {
pub fn len(&self) -> usize {
unsafe { self.as_slice().len() }
}

pub fn is_shared(&self) -> bool {
match &self.source {
BufferSourceInner::Buffer(buffer) => buffer.is_shared(),
BufferSourceInner::View(view) => view.is_shared(),
}
}

pub unsafe fn as_slice(&self) -> &[u8] {
match &self.source {
BufferSourceInner::Buffer(buffer) => unsafe { buffer.as_slice() },
BufferSourceInner::View(view) => unsafe { view.as_slice() },
}
}

pub fn to_vec(&self) -> Vec<u8> {
unsafe { self.as_slice().to_vec() }
}

pub fn to_bytes(&self) -> Bytes {
Bytes::copy_from_slice(unsafe { self.as_slice() })
}
}

impl<'cx> FromValue<'cx> for BufferSource<'cx> {
type Config = bool;
fn from_value(cx: &'cx Context, value: &Value, strict: bool, allow_shared: bool) -> Result<BufferSource<'cx>> {
let obj = Object::from_value(cx, value, strict, ())?.handle().get();
if let Ok(buffer) = ArrayBuffer::from(obj) {
if buffer.is_shared() && !allow_shared {
return Err(Error::new("Buffer Source cannot be shared", ErrorKind::Type));
}
Ok(BufferSource {
source: BufferSourceInner::Buffer(buffer),
lifetime: PhantomData,
})
} else if let Ok(view) = ArrayBufferView::from(obj) {
if view.is_shared() && !allow_shared {
return Err(Error::new("Buffer Source cannot be shared", ErrorKind::Type));
}
Ok(BufferSource {
source: BufferSourceInner::View(view),
lifetime: PhantomData,
})
} else {
Err(Error::new("Object is not a buffer source.", ErrorKind::Type))
}
}
}

Expand All @@ -39,8 +93,8 @@ impl<'cx> FromValue<'cx> for BlobPart {
if value.handle().is_string() {
return Ok(BlobPart(Bytes::from(String::from_value(cx, value, true, ())?)));
} else if value.handle().is_object() {
if let Ok(bytes) = buffer_source_to_bytes(&value.to_object(cx)) {
return Ok(BlobPart(bytes));
if let Ok(buffer_source) = BufferSource::from_value(cx, value, strict, false) {
return Ok(BlobPart(buffer_source.to_bytes()));
} else if let Ok(blob) = <&Blob>::from_value(cx, value, strict, ()) {
return Ok(BlobPart(blob.bytes.clone()));
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/globals/file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use chrono::{DateTime, TimeZone, Utc};
use mozjs::conversions::ConversionBehavior;

pub use blob::{Blob, buffer_source_to_bytes};
pub use blob::{Blob, BufferSource};
use ion::{ClassDefinition, Context, Object};

use crate::globals::file::blob::{BlobOptions, BlobPart};
Expand Down

0 comments on commit c2b947f

Please sign in to comment.