Skip to content

Commit db85bc9

Browse files
committed
Define QuotaExceededError and Buffer as native subclasses instead of eval
Replaces the manual QuotaExceededError class and the eval'd Buffer class with a shared define_subclass helper that wires both prototype links the way class-extends does, so static members like Buffer.of are inherited.
1 parent 9d33bce commit db85bc9

5 files changed

Lines changed: 89 additions & 189 deletions

File tree

libs/llrt_utils/src/object.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
use std::collections::BTreeMap;
44

55
use rquickjs::{
6-
atom::PredefinedAtom, function::IntoJsFunc, prelude::Func, Array, Coerced, Ctx, Error,
7-
Exception, FromJs, IntoAtom, IntoJs, Object, Result, Undefined, Value,
6+
atom::PredefinedAtom,
7+
function::{Constructor, IntoJsFunc},
8+
object::Property,
9+
prelude::Func,
10+
Array, Coerced, Ctx, Error, Exception, FromJs, IntoAtom, IntoJs, Object, Result, Undefined,
11+
Value,
812
};
913

1014
use crate::primordials::{BasePrimordials, Primordial};
@@ -145,3 +149,22 @@ pub fn object_from_entries<'js>(ctx: &Ctx<'js>, array: Array<'js>) -> Result<Obj
145149
}
146150
Ok(obj)
147151
}
152+
153+
/// Build a constructor that behaves like `class Name extends Parent`
154+
pub fn define_subclass<'js, F, P>(
155+
ctx: &Ctx<'js>,
156+
name: &str,
157+
parent: &Constructor<'js>,
158+
construct: F,
159+
) -> Result<Constructor<'js>>
160+
where
161+
F: IntoJsFunc<'js, P> + 'js,
162+
{
163+
let parent_proto: Object = parent.get(PredefinedAtom::Prototype)?;
164+
let proto = Object::new(ctx.clone())?;
165+
proto.set_prototype(Some(&parent_proto))?;
166+
let constructor = Constructor::new_prototype(ctx, proto, construct)?;
167+
constructor.set_prototype(parent.as_object())?;
168+
constructor.prop(PredefinedAtom::Name, Property::from(name).configurable())?;
169+
Ok(constructor)
170+
}

modules/llrt_buffer/src/lib.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
// SPDX-License-Identifier: Apache-2.0
33
use llrt_utils::{
44
module::{export_default, ModuleInfo},
5+
object::define_subclass,
56
primordials::{BasePrimordials, Primordial},
67
};
78
use rquickjs::{
8-
function::Constructor,
9+
function::{Args, Constructor, Rest},
910
module::{Declarations, Exports, ModuleDef},
10-
Class, Ctx, Function, IntoJs, Object, Result,
11+
Class, Ctx, Function, IntoJs, Object, Result, Value,
1112
};
1213

1314
pub use self::array_buffer_view::*;
@@ -68,13 +69,21 @@ pub fn init<'js>(ctx: &Ctx<'js>) -> Result<()> {
6869
let globals = ctx.globals();
6970
BasePrimordials::init(ctx)?;
7071

71-
// Buffer
72-
let buffer = ctx.eval::<Object<'js>, &str>(concat!(
73-
"class ",
72+
// Buffer extends the native Uint8Array: it forwards construction to the
73+
// Uint8Array constructor and inherits its static and prototype members.
74+
let uint8array = BasePrimordials::get(ctx)?.constructor_uint8array.clone();
75+
let buffer_ctor = define_subclass(
76+
ctx,
7477
stringify!(Buffer),
75-
" extends Uint8Array {}\n",
76-
stringify!(Buffer),
77-
))?;
78+
&uint8array,
79+
|ctx: Ctx<'js>, args: Rest<Value<'js>>| {
80+
let uint8array = &BasePrimordials::get(&ctx)?.constructor_uint8array;
81+
let mut ctor_args = Args::new(ctx.clone(), args.0.len());
82+
ctor_args.push_args(args.0)?;
83+
ctor_args.construct::<Value>(uint8array)
84+
},
85+
)?;
86+
let buffer: Object = buffer_ctor.into_value().into_object().unwrap();
7887
set_prototype(ctx, buffer)?;
7988

8089
BufferPrimordials::init(ctx)?;

modules/llrt_crypto/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use std::slice;
3131
use llrt_buffer::Buffer;
3232
use llrt_context::CtxExtension;
3333
use llrt_encoding::{bytes_to_b64_string, bytes_to_hex_string};
34-
use llrt_exceptions::{DOMException, QuotaExceededError};
34+
use llrt_exceptions::DOMException;
3535
use llrt_utils::{
3636
bytes::{get_start_end_indexes, ObjectBytes},
3737
error::ErrorExtensions,
@@ -183,7 +183,7 @@ fn get_random_values<'js>(ctx: Ctx<'js>, obj: Object<'js>) -> Result<Object<'js>
183183
.or_throw(&ctx)?;
184184

185185
if source_length > 0x10000 {
186-
return Err(QuotaExceededError::quota_exceeded_error(
186+
return Err(DOMException::quota_exceeded_error(
187187
&ctx,
188188
"The requested length exceeds 65,536 bytes",
189189
));

modules/llrt_exceptions/src/lib.rs

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
mod quota_exceeded_error;
4-
53
use core::fmt;
64
use std::fmt::Debug;
75

86
use llrt_utils::{
7+
object::define_subclass,
98
option::Undefined,
109
primordials::{BasePrimordials, Primordial},
1110
};
@@ -21,7 +20,6 @@ use rquickjs::{
2120
qjs, Class, Coerced, Ctx, Error, Exception, FromJs, IntoJs, JsLifetime, Object, Result, Value,
2221
};
2322

24-
pub use crate::quota_exceeded_error::QuotaExceededError;
2523
use crate::DOMExceptionName::{NotSupportedError, OperationError, TypeMismatchError};
2624

2725
#[derive(Trace, JsLifetime, Debug)]
@@ -228,6 +226,46 @@ impl<'js> DOMException {
228226
pub fn operation_error(ctx: &Ctx<'js>, message: impl Into<String>) -> Error {
229227
Self::create_error(ctx, OperationError, message)
230228
}
229+
230+
pub fn quota_exceeded_error(ctx: &Ctx<'js>, message: impl Into<String>) -> Error {
231+
let value = Self::create_quota_exceeded(ctx, message.into())
232+
.expect("failed to create QuotaExceededError");
233+
Self::throw_value(ctx, value)
234+
}
235+
236+
fn create_quota_exceeded(ctx: &Ctx<'js>, message: String) -> Result<Value<'js>> {
237+
let ctor: Constructor = ctx.globals().get("QuotaExceededError")?;
238+
ctor.construct((message,))
239+
}
240+
241+
fn define_quota_exceeded_error(ctx: &Ctx<'js>) -> Result<()> {
242+
let dom_exception: Constructor = ctx.globals().get(DOMException::NAME)?;
243+
let quota_exceeded_error = define_subclass(
244+
ctx,
245+
"QuotaExceededError",
246+
&dom_exception,
247+
|ctx, message: Opt<Undefined<Coerced<String>>>| {
248+
let message = match message.0 {
249+
Some(Undefined(Some(m))) => m.0,
250+
_ => String::new(),
251+
};
252+
DOMException::new_with_name(&ctx, DOMExceptionName::QuotaExceededError, message)
253+
},
254+
)?;
255+
let null = Value::new_null(ctx.clone());
256+
let proto: Object = quota_exceeded_error.get(PredefinedAtom::Prototype)?;
257+
proto.prop(
258+
"requested",
259+
Property::from(null.clone()).enumerable().configurable(),
260+
)?;
261+
proto.prop("quota", Property::from(null).enumerable().configurable())?;
262+
ctx.globals().prop(
263+
"QuotaExceededError",
264+
Property::from(quota_exceeded_error)
265+
.writable()
266+
.configurable(),
267+
)
268+
}
231269
}
232270

233271
macro_rules! create_dom_exception {
@@ -349,22 +387,14 @@ pub fn init(ctx: &Ctx<'_>) -> Result<()> {
349387
let primordials = BasePrimordials::get(ctx)?;
350388
dom_ex_proto.set_prototype(Some(&primordials.prototype_error))?;
351389

352-
if let Some(constructor) = Class::<QuotaExceededError>::create_constructor(ctx)? {
353-
// the wpt tests expect this particular property descriptor
354-
globals.prop(
355-
QuotaExceededError::NAME,
356-
Property::from(constructor).writable().configurable(),
357-
)?;
358-
}
359-
360-
let qee_ex_proto = Class::<QuotaExceededError>::prototype(ctx)?.unwrap();
361-
qee_ex_proto.set_prototype(Some(&primordials.prototype_error))?;
390+
DOMException::define_quota_exceeded_error(ctx)?;
362391

363392
// `Error.isError(v)` only returns `true` for objects with QuickJS's
364393
// `[[ErrorData]]` internal slot (class id `JS_CLASS_ERROR`). There is
365394
// no public rquickjs API to tag a class-derived instance with that
366395
// slot, so we replace `Error.isError` with a version that also
367-
// recognizes `DOMException` instances via `instanceof`.
396+
// recognizes `DOMException` instances (and its subclasses) via
397+
// `instanceof`.
368398
primordials
369399
.constructor_error
370400
.set("isError", Func::from(is_error))?;
@@ -380,9 +410,5 @@ fn is_error<'js>(ctx: Ctx<'js>, value: Value<'js>) -> Result<bool> {
380410
return Ok(false);
381411
};
382412
let dom_exception: Value = ctx.globals().get(DOMException::NAME)?;
383-
if obj.is_instance_of(&dom_exception) {
384-
return Ok(true);
385-
}
386-
let quota_exceeded_error: Value = ctx.globals().get(QuotaExceededError::NAME)?;
387-
Ok(obj.is_instance_of(&quota_exceeded_error))
413+
Ok(obj.is_instance_of(&dom_exception))
388414
}

modules/llrt_exceptions/src/quota_exceeded_error.rs

Lines changed: 0 additions & 158 deletions
This file was deleted.

0 commit comments

Comments
 (0)