#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum JavascriptPropertyKey {
String(String),
Symbol(u64),
Private {
class: ManagedId,
name: String,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum JavascriptFunctionKind {
Function,
Arrow,
ClassConstructor,
}
#[derive(Clone, Debug)]
pub struct JavascriptFunctionPolicy {
pub kind: JavascriptFunctionKind,
pub constructable: bool,
pub defaults: BTreeMap<Symbol, JavascriptValue>,
pub asynchronous: bool,
pub generator: bool,
pub realm: Symbol,
pub error_origin: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct JavascriptCallError {
pub origin: String,
pub message: String,
}
#[derive(Clone, Debug)]
pub struct JavascriptFunction {
plan: FunctionPlan,
captures: Vec<CapturedBinding>,
policy: JavascriptFunctionPolicy,
pub private_names: Vec<String>,
}
impl JavascriptFunction {
pub fn new(
plan: FunctionPlan,
captures: Vec<CapturedBinding>,
policy: JavascriptFunctionPolicy,
private_names: Vec<String>,
) -> Result<Self, InstanceError> {
validate_capture_bindings(&plan, &captures)?;
Ok(Self {
plan,
captures,
policy,
private_names,
})
}
pub const fn plan(&self) -> &FunctionPlan {
&self.plan
}
pub fn captures(&self) -> &[CapturedBinding] {
&self.captures
}
pub const fn policy(&self) -> &JavascriptFunctionPolicy {
&self.policy
}
pub fn bind_arguments(
&self,
arguments: &[JavascriptValue],
) -> Result<BTreeMap<Symbol, Vec<JavascriptValue>>, JavascriptCallError> {
let mut bound = BTreeMap::new();
let mut at = 0;
for parameter in self.plan.parameters() {
let values = match parameter.kind() {
ParameterKind::Remainder => {
let values = arguments[at..].to_vec();
at = arguments.len();
values
}
ParameterKind::Required => {
let Some(value) = arguments.get(at) else {
return self
.bind_error(format!("missing required argument {}", parameter.name()));
};
at += 1;
vec![value.clone()]
}
ParameterKind::Optional => {
let supplied = arguments.get(at);
let value = match supplied {
Some(JavascriptValue::Undefined) | None => self
.policy
.defaults
.get(parameter.name())
.cloned()
.or_else(|| supplied.cloned()),
Some(value) => Some(value.clone()),
};
if supplied.is_some() {
at += 1;
}
vec![value.unwrap_or(JavascriptValue::Undefined)]
}
};
bound.insert(parameter.name().clone(), values);
}
if at != arguments.len() {
return self.bind_error("too many arguments".into());
}
Ok(bound)
}
fn bind_error<T>(&self, message: String) -> Result<T, JavascriptCallError> {
Err(JavascriptCallError {
origin: self.policy.error_origin.clone(),
message,
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum JavascriptThis {
Lexical(JavascriptValue),
Dynamic(JavascriptValue),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum JavascriptObjectGap {
ProxyInvariants,
ExoticObjectInvariants,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum JavascriptObjectError {
Arena(ArenaError),
ManagedGraph(JavascriptManagedMutationError),
Descriptor(DefineError),
Access,
PrototypeCycle,
NotConstructor,
PrivateBrand,
}
impl From<ArenaError> for JavascriptObjectError {
fn from(v: ArenaError) -> Self {
Self::Arena(v)
}
}
impl From<JavascriptManagedMutationError> for JavascriptObjectError {
fn from(value: JavascriptManagedMutationError) -> Self {
Self::ManagedGraph(value)
}
}
impl From<DefineError> for JavascriptObjectError {
fn from(v: DefineError) -> Self {
Self::Descriptor(v)
}
}
#[derive(Clone, Debug, PartialEq)]
struct Accessor {
get: Option<JavascriptValue>,
set: bool,
}
#[derive(Default)]
struct Hooks {
writes: Vec<(ManagedId, JavascriptPropertyKey, JavascriptValue)>,
}
impl PropertyHook<ManagedId, JavascriptPropertyKey, JavascriptValue, Accessor> for Hooks {
type Error = ();
fn get(
&mut self,
_: &mut AccessContext<ManagedId, JavascriptPropertyKey>,
hook: &Accessor,
_: &ManagedId,
_: &JavascriptPropertyKey,
) -> Result<JavascriptValue, AccessError<()>> {
hook.get.clone().ok_or(AccessError::Hook(()))
}
fn set(
&mut self,
_: &mut AccessContext<ManagedId, JavascriptPropertyKey>,
hook: &Accessor,
receiver: &ManagedId,
key: &JavascriptPropertyKey,
value: JavascriptValue,
) -> Result<(), AccessError<()>> {
if !hook.set {
return Err(AccessError::Hook(()));
}
self.writes.push((*receiver, key.clone(), value));
Ok(())
}
}