use crate::{
builtins::{
iterable::iterable_to_list, BuiltInBuilder, BuiltInConstructor, BuiltInObject,
IntrinsicObject,
},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError,
js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject},
property::Attribute,
realm::Realm,
string::common::StaticJsStrings,
symbol::JsSymbol,
value::{JsValue, Numeric},
Context, JsArgs, JsResult, JsString,
};
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
mod builtin;
mod element;
mod object;
pub(crate) use builtin::{is_valid_integer_index, BuiltinTypedArray};
pub(crate) use element::{Atomic, ClampedU8, Element};
pub use object::TypedArray;
pub(crate) trait TypedArrayMarker {
type Element: Element;
const ERASED: TypedArrayKind;
}
impl<T: TypedArrayMarker> IntrinsicObject for T {
fn get(intrinsics: &Intrinsics) -> JsObject {
Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
}
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let get_species = BuiltInBuilder::callable(realm, BuiltinTypedArray::get_species)
.name(js_string!("get [Symbol.species]"))
.build();
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.prototype(
realm
.intrinsics()
.constructors()
.typed_array()
.constructor(),
)
.inherits(Some(
realm.intrinsics().constructors().typed_array().prototype(),
))
.static_accessor(
JsSymbol::species(),
Some(get_species),
None,
Attribute::CONFIGURABLE,
)
.property(
js_string!("BYTES_PER_ELEMENT"),
std::mem::size_of::<T::Element>(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
)
.static_property(
js_string!("BYTES_PER_ELEMENT"),
std::mem::size_of::<T::Element>(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
)
.build();
}
}
impl<T: TypedArrayMarker> BuiltInObject for T {
const NAME: JsString = <Self as TypedArrayMarker>::ERASED.js_name();
const ATTRIBUTE: Attribute = Attribute::WRITABLE
.union(Attribute::NON_ENUMERABLE)
.union(Attribute::CONFIGURABLE);
}
impl<T: TypedArrayMarker> BuiltInConstructor for T {
const LENGTH: usize = 3;
const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
<Self as TypedArrayMarker>::ERASED.standard_constructor();
fn constructor(
new_target: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
if new_target.is_undefined() {
return Err(JsNativeError::typ()
.with_message(format!(
"new target was undefined when constructing an {}",
T::ERASED.name()
))
.into());
}
let number_of_args = args.len();
if number_of_args == 0 {
return Ok(BuiltinTypedArray::allocate::<T>(new_target, 0, context)?.into());
}
let first_argument = &args[0];
let Some(first_argument) = first_argument.as_object() else {
let element_length = first_argument.to_index(context)?;
return BuiltinTypedArray::allocate::<T>(new_target, element_length, context)
.map(JsValue::from);
};
let first_argument = first_argument.clone();
let proto = get_prototype_from_constructor(new_target, T::STANDARD_CONSTRUCTOR, context)?;
let first_argument = match first_argument.downcast::<TypedArray>() {
Ok(arr) => {
return BuiltinTypedArray::initialize_from_typed_array::<T>(proto, &arr, context)
.map(JsValue::from);
}
Err(obj) => obj,
};
let first_argument = match first_argument.into_buffer_object() {
Ok(buf) => {
let byte_offset = args.get_or_undefined(1);
let length = args.get_or_undefined(2);
return BuiltinTypedArray::initialize_from_array_buffer::<T>(
proto,
buf,
byte_offset,
length,
context,
)
.map(JsValue::from);
}
Err(obj) => obj,
};
let using_iterator = first_argument.get_method(JsSymbol::iterator(), context)?;
if let Some(using_iterator) = using_iterator {
let values = iterable_to_list(context, &first_argument.into(), Some(using_iterator))?;
BuiltinTypedArray::initialize_from_list::<T>(proto, values, context)
} else {
BuiltinTypedArray::initialize_from_array_like::<T>(proto, &first_argument, context)
}
.map(JsValue::from)
}
}
#[derive(Debug, Copy, Clone)]
pub struct Int8Array;
impl TypedArrayMarker for Int8Array {
type Element = i8;
const ERASED: TypedArrayKind = TypedArrayKind::Int8;
}
#[derive(Debug, Copy, Clone)]
pub struct Uint8Array;
impl TypedArrayMarker for Uint8Array {
type Element = u8;
const ERASED: TypedArrayKind = TypedArrayKind::Uint8;
}
#[derive(Debug, Copy, Clone)]
pub struct Uint8ClampedArray;
impl TypedArrayMarker for Uint8ClampedArray {
type Element = ClampedU8;
const ERASED: TypedArrayKind = TypedArrayKind::Uint8Clamped;
}
#[derive(Debug, Copy, Clone)]
pub struct Int16Array;
impl TypedArrayMarker for Int16Array {
type Element = i16;
const ERASED: TypedArrayKind = TypedArrayKind::Int16;
}
#[derive(Debug, Copy, Clone)]
pub struct Uint16Array;
impl TypedArrayMarker for Uint16Array {
type Element = u16;
const ERASED: TypedArrayKind = TypedArrayKind::Uint16;
}
#[derive(Debug, Copy, Clone)]
pub struct Int32Array;
impl TypedArrayMarker for Int32Array {
type Element = i32;
const ERASED: TypedArrayKind = TypedArrayKind::Int32;
}
#[derive(Debug, Copy, Clone)]
pub struct Uint32Array;
impl TypedArrayMarker for Uint32Array {
type Element = u32;
const ERASED: TypedArrayKind = TypedArrayKind::Uint32;
}
#[derive(Debug, Copy, Clone)]
pub struct BigInt64Array;
impl TypedArrayMarker for BigInt64Array {
type Element = i64;
const ERASED: TypedArrayKind = TypedArrayKind::BigInt64;
}
#[derive(Debug, Copy, Clone)]
pub struct BigUint64Array;
impl TypedArrayMarker for BigUint64Array {
type Element = u64;
const ERASED: TypedArrayKind = TypedArrayKind::BigUint64;
}
#[derive(Debug, Copy, Clone)]
pub struct Float32Array;
impl TypedArrayMarker for Float32Array {
type Element = f32;
const ERASED: TypedArrayKind = TypedArrayKind::Float32;
}
#[derive(Debug, Copy, Clone)]
pub struct Float64Array;
impl TypedArrayMarker for Float64Array {
type Element = f64;
const ERASED: TypedArrayKind = TypedArrayKind::Float64;
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum ContentType {
Number,
BigInt,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Trace, Finalize)]
#[boa_gc(empty_trace)]
pub(crate) enum TypedArrayKind {
Int8,
Uint8,
Uint8Clamped,
Int16,
Uint16,
Int32,
Uint32,
BigInt64,
BigUint64,
Float32,
Float64,
}
impl TypedArrayKind {
pub(crate) const fn js_name(self) -> JsString {
match self {
TypedArrayKind::Int8 => StaticJsStrings::INT8_ARRAY,
TypedArrayKind::Uint8 => StaticJsStrings::UINT8_ARRAY,
TypedArrayKind::Uint8Clamped => StaticJsStrings::UINT8_CLAMPED_ARRAY,
TypedArrayKind::Int16 => StaticJsStrings::INT16_ARRAY,
TypedArrayKind::Uint16 => StaticJsStrings::UINT16_ARRAY,
TypedArrayKind::Int32 => StaticJsStrings::INT32_ARRAY,
TypedArrayKind::Uint32 => StaticJsStrings::UINT32_ARRAY,
TypedArrayKind::BigInt64 => StaticJsStrings::BIG_INT64_ARRAY,
TypedArrayKind::BigUint64 => StaticJsStrings::BIG_UINT64_ARRAY,
TypedArrayKind::Float32 => StaticJsStrings::FLOAT32_ARRAY,
TypedArrayKind::Float64 => StaticJsStrings::FLOAT64_ARRAY,
}
}
pub(crate) const fn name(self) -> &'static str {
match self {
TypedArrayKind::Int8 => "Int8",
TypedArrayKind::Uint8 => "Uint8",
TypedArrayKind::Uint8Clamped => "Uint8Clamped",
TypedArrayKind::Int16 => "Int16",
TypedArrayKind::Uint16 => "Uint16",
TypedArrayKind::Int32 => "Int32",
TypedArrayKind::Uint32 => "Uint32",
TypedArrayKind::BigInt64 => "BigInt64",
TypedArrayKind::BigUint64 => "BigUint64",
TypedArrayKind::Float32 => "Float32",
TypedArrayKind::Float64 => "Float64",
}
}
pub(crate) const fn standard_constructor(
self,
) -> fn(&StandardConstructors) -> &StandardConstructor {
match self {
TypedArrayKind::Int8 => StandardConstructors::typed_int8_array,
TypedArrayKind::Uint8 => StandardConstructors::typed_uint8_array,
TypedArrayKind::Uint8Clamped => StandardConstructors::typed_uint8clamped_array,
TypedArrayKind::Int16 => StandardConstructors::typed_int16_array,
TypedArrayKind::Uint16 => StandardConstructors::typed_uint16_array,
TypedArrayKind::Int32 => StandardConstructors::typed_int32_array,
TypedArrayKind::Uint32 => StandardConstructors::typed_uint32_array,
TypedArrayKind::BigInt64 => StandardConstructors::typed_bigint64_array,
TypedArrayKind::BigUint64 => StandardConstructors::typed_biguint64_array,
TypedArrayKind::Float32 => StandardConstructors::typed_float32_array,
TypedArrayKind::Float64 => StandardConstructors::typed_float64_array,
}
}
pub(crate) fn supports_atomic_ops(self) -> bool {
match self {
TypedArrayKind::Int8
| TypedArrayKind::Uint8
| TypedArrayKind::Int16
| TypedArrayKind::Uint16
| TypedArrayKind::Int32
| TypedArrayKind::Uint32
| TypedArrayKind::BigInt64
| TypedArrayKind::BigUint64 => true,
TypedArrayKind::Uint8Clamped | TypedArrayKind::Float32 | TypedArrayKind::Float64 => {
false
}
}
}
pub(crate) const fn element_size(self) -> u64 {
match self {
TypedArrayKind::Int8 | TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => {
std::mem::size_of::<u8>() as u64
}
TypedArrayKind::Int16 | TypedArrayKind::Uint16 => std::mem::size_of::<u16>() as u64,
TypedArrayKind::Int32 | TypedArrayKind::Uint32 | TypedArrayKind::Float32 => {
std::mem::size_of::<u32>() as u64
}
TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 | TypedArrayKind::Float64 => {
std::mem::size_of::<u64>() as u64
}
}
}
pub(crate) const fn content_type(self) -> ContentType {
match self {
TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => ContentType::BigInt,
TypedArrayKind::Int8
| TypedArrayKind::Uint8
| TypedArrayKind::Uint8Clamped
| TypedArrayKind::Int16
| TypedArrayKind::Uint16
| TypedArrayKind::Int32
| TypedArrayKind::Uint32
| TypedArrayKind::Float32
| TypedArrayKind::Float64 => ContentType::Number,
}
}
pub(crate) fn get_element(
self,
value: &JsValue,
context: &mut Context,
) -> JsResult<TypedArrayElement> {
match self {
TypedArrayKind::Int8 => value.to_int8(context).map(TypedArrayElement::Int8),
TypedArrayKind::Uint8 => value.to_uint8(context).map(TypedArrayElement::Uint8),
TypedArrayKind::Uint8Clamped => value
.to_uint8_clamp(context)
.map(|u| TypedArrayElement::Uint8Clamped(ClampedU8(u))),
TypedArrayKind::Int16 => value.to_int16(context).map(TypedArrayElement::Int16),
TypedArrayKind::Uint16 => value.to_uint16(context).map(TypedArrayElement::Uint16),
TypedArrayKind::Int32 => value.to_i32(context).map(TypedArrayElement::Int32),
TypedArrayKind::Uint32 => value.to_u32(context).map(TypedArrayElement::Uint32),
TypedArrayKind::BigInt64 => {
value.to_big_int64(context).map(TypedArrayElement::BigInt64)
}
TypedArrayKind::BigUint64 => value
.to_big_uint64(context)
.map(TypedArrayElement::BigUint64),
TypedArrayKind::Float32 => value
.to_number(context)
.map(|f| TypedArrayElement::Float32(f as f32)),
TypedArrayKind::Float64 => value.to_number(context).map(TypedArrayElement::Float64),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum TypedArrayElement {
Int8(i8),
Uint8(u8),
Uint8Clamped(ClampedU8),
Int16(i16),
Uint16(u16),
Int32(i32),
Uint32(u32),
BigInt64(i64),
BigUint64(u64),
Float32(f32),
Float64(f64),
}
impl TypedArrayElement {
pub(crate) fn to_bits(self) -> u64 {
#[allow(clippy::cast_lossless)]
match self {
TypedArrayElement::Int8(num) => num as u64,
TypedArrayElement::Uint8(num) => num as u64,
TypedArrayElement::Uint8Clamped(num) => num.0 as u64,
TypedArrayElement::Int16(num) => num as u64,
TypedArrayElement::Uint16(num) => num as u64,
TypedArrayElement::Int32(num) => num as u64,
TypedArrayElement::Uint32(num) => num as u64,
TypedArrayElement::BigInt64(num) => num as u64,
TypedArrayElement::BigUint64(num) => num,
TypedArrayElement::Float32(num) => num.to_bits() as u64,
TypedArrayElement::Float64(num) => num.to_bits(),
}
}
}
impl From<i8> for TypedArrayElement {
fn from(value: i8) -> Self {
Self::Int8(value)
}
}
impl From<u8> for TypedArrayElement {
fn from(value: u8) -> Self {
Self::Uint8(value)
}
}
impl From<ClampedU8> for TypedArrayElement {
fn from(value: ClampedU8) -> Self {
Self::Uint8Clamped(value)
}
}
impl From<i16> for TypedArrayElement {
fn from(value: i16) -> Self {
Self::Int16(value)
}
}
impl From<u16> for TypedArrayElement {
fn from(value: u16) -> Self {
Self::Uint16(value)
}
}
impl From<i32> for TypedArrayElement {
fn from(value: i32) -> Self {
Self::Int32(value)
}
}
impl From<u32> for TypedArrayElement {
fn from(value: u32) -> Self {
Self::Uint32(value)
}
}
impl From<i64> for TypedArrayElement {
fn from(value: i64) -> Self {
Self::BigInt64(value)
}
}
impl From<u64> for TypedArrayElement {
fn from(value: u64) -> Self {
Self::BigUint64(value)
}
}
impl From<f32> for TypedArrayElement {
fn from(value: f32) -> Self {
Self::Float32(value)
}
}
impl From<f64> for TypedArrayElement {
fn from(value: f64) -> Self {
Self::Float64(value)
}
}
impl From<TypedArrayElement> for JsValue {
fn from(value: TypedArrayElement) -> Self {
match value {
TypedArrayElement::Int8(value) => Numeric::from(value),
TypedArrayElement::Uint8(value) => Numeric::from(value),
TypedArrayElement::Uint8Clamped(value) => Numeric::from(value),
TypedArrayElement::Int16(value) => Numeric::from(value),
TypedArrayElement::Uint16(value) => Numeric::from(value),
TypedArrayElement::Int32(value) => Numeric::from(value),
TypedArrayElement::Uint32(value) => Numeric::from(value),
TypedArrayElement::BigInt64(value) => Numeric::from(value),
TypedArrayElement::BigUint64(value) => Numeric::from(value),
TypedArrayElement::Float32(value) => Numeric::from(value),
TypedArrayElement::Float64(value) => Numeric::from(value),
}
.into()
}
}