use crate::utils::{
module::{export_default, ModuleInfo},
object::define_subclass,
primordials::{BasePrimordials, Primordial},
};
use rquickjs::{
function::{Args, Constructor, Rest},
module::{Declarations, Exports, ModuleDef},
Ctx, Function, IntoJs, Object, Result, Value,
};
pub use self::array_buffer_view::*;
pub use self::blob::*;
pub use self::class::*;
pub use self::file::*;
mod array_buffer_view;
mod blob;
mod class;
mod file;
pub struct BufferModule;
impl ModuleDef for BufferModule {
fn declare(declare: &Declarations) -> Result<()> {
declare.declare(stringify!(Buffer))?;
declare.declare("atob")?;
declare.declare("btoa")?;
declare.declare("constants")?;
declare.declare("default")?;
Ok(())
}
fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
let globals = ctx.globals();
let buf: Constructor = globals.get(stringify!(Buffer))?;
let constants = Object::new(ctx.clone())?;
constants.set("MAX_LENGTH", u32::MAX)?; constants.set("MAX_STRING_LENGTH", (1 << 30) - 1)?;
let atob: Function = ctx.globals().get("atob")?;
let btoa: Function = ctx.globals().get("btoa")?;
export_default(ctx, exports, |default| {
default.set(stringify!(Buffer), buf)?;
default.set("atob", atob.into_js(ctx)?)?;
default.set("btoa", btoa.into_js(ctx)?)?;
default.set("constants", constants)?;
Ok(())
})?;
Ok(())
}
}
impl From<BufferModule> for ModuleInfo<BufferModule> {
fn from(val: BufferModule) -> Self {
ModuleInfo {
name: "buffer",
module: val,
}
}
}
pub fn init<'js>(ctx: &Ctx<'js>) -> Result<()> {
BasePrimordials::init(ctx)?;
let uint8array = BasePrimordials::get(ctx)?.constructor_uint8array.clone();
let buffer_ctor = define_subclass(
ctx,
stringify!(Buffer),
&uint8array,
|ctx: Ctx<'js>, args: Rest<Value<'js>>| {
let uint8array = &BasePrimordials::get(&ctx)?.constructor_uint8array;
let mut ctor_args = Args::new(ctx.clone(), args.0.len());
ctor_args.push_args(args.0)?;
ctor_args.construct::<Value>(uint8array)
},
)?;
let buffer: Object = buffer_ctor.into_value().into_object().unwrap();
set_prototype(ctx, buffer)?;
BufferPrimordials::init(ctx)?;
add_missing_prototype_methods(ctx)?;
rquickjs::Class::<Blob>::define(&ctx.globals())?;
rquickjs::Class::<File>::define(&ctx.globals())?;
chain_file_to_blob(ctx)?;
let _ = BufferPrimordials::get(ctx)?;
Ok(())
}
fn add_missing_prototype_methods<'js>(ctx: &Ctx<'js>) -> Result<()> {
let buffer: Constructor<'js> = ctx.globals().get(stringify!(Buffer))?;
let prototype: Object<'js> = buffer.get(rquickjs::atom::PredefinedAtom::Prototype)?;
prototype.set(
"equals",
Function::new(ctx.clone(), |this: rquickjs::function::This<Object<'js>>, other: Object<'js>| -> Result<bool> {
let (a, b) = (bytes_of(&this.0)?, bytes_of(&other)?);
Ok(a == b)
})?,
)?;
prototype.set(
"toJSON",
Function::new(ctx.clone(), |ctx: Ctx<'js>, this: rquickjs::function::This<Object<'js>>| -> Result<Object<'js>> {
let json = Object::new(ctx.clone())?;
json.set("type", "Buffer")?;
json.set("data", bytes_of(&this.0)?)?;
Ok(json)
})?,
)?;
Ok(())
}
fn bytes_of<'js>(object: &Object<'js>) -> Result<Vec<u8>> {
match crate::utils::bytes::ObjectBytes::from_array_buffer(object)? {
Some(bytes) => Ok(bytes.as_bytes(object.ctx())?.to_vec()),
None => Ok(Vec::new()),
}
}
fn chain_file_to_blob<'js>(ctx: &Ctx<'js>) -> Result<()> {
let blob_proto = rquickjs::Class::<Blob<'js>>::prototype(ctx)?;
let file_proto = rquickjs::Class::<File<'js>>::prototype(ctx)?;
if let (Some(blob_proto), Some(file_proto)) = (blob_proto, file_proto) {
file_proto.set_prototype(Some(&blob_proto))?;
}
Ok(())
}