use crate::host::HostFunction;
use crate::object::function::JSFunction;
use crate::object::object::JSObject;
use crate::runtime::atom::Atom;
use crate::runtime::context::JSContext;
use crate::value::JSValue;
fn throw_type_error(ctx: &mut JSContext, msg: &str) {
let mut err = JSObject::new();
if let Some(proto_ptr) = ctx.get_type_error_prototype() {
err.prototype = Some(proto_ptr);
}
err.set(
ctx.common_atoms.name,
JSValue::new_string(ctx.intern("TypeError")),
);
err.set(
ctx.common_atoms.message,
JSValue::new_string(ctx.intern(msg)),
);
let ptr = Box::into_raw(Box::new(err)) as usize;
ctx.runtime_mut().gc_heap_mut().track(ptr);
ctx.pending_exception = Some(JSValue::new_object(ptr));
}
fn create_builtin_function(ctx: &mut JSContext, name: &str, arity: u32) -> JSValue {
let mut func = JSFunction::new_builtin(ctx.intern(name), arity);
func.set_builtin_marker(ctx, name);
let ptr = Box::into_raw(Box::new(func)) as usize;
ctx.runtime_mut().gc_heap_mut().track_function(ptr);
JSValue::new_function(ptr)
}
pub const NO_DESCRIPTION: Atom = Atom(u32::MAX);
fn create_symbol(ctx: &mut JSContext, description: Option<Atom>) -> JSValue {
let desc = description.unwrap_or(NO_DESCRIPTION);
if desc != NO_DESCRIPTION {
ctx.mark_symbol_atom(desc);
}
let id = ctx.runtime_mut().next_symbol_id();
JSValue::new_symbol_with_id(desc, id)
}
fn symbol_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
let desc = if args.is_empty() {
None
} else {
let desc_val = args[0];
if desc_val.is_undefined() {
None
} else if desc_val.is_symbol() {
throw_type_error(ctx, "Cannot convert a Symbol value to a string");
return JSValue::undefined();
} else if let Some(atom) = to_string_via_vm(ctx, desc_val) {
Some(atom)
} else {
throw_type_error(ctx, "Cannot convert object to primitive value");
return JSValue::undefined();
}
};
create_symbol(ctx, desc)
}
fn to_string_via_vm(ctx: &mut JSContext, value: JSValue) -> Option<Atom> {
if value.is_string() { return Some(value.get_atom()); }
if value.is_int() { return Some(ctx.intern(&value.get_int().to_string())); }
if value.is_float() { return Some(ctx.intern(&value.get_float().to_string())); }
if value.is_bool() { return Some(ctx.intern(if value.get_bool() { "true" } else { "false" })); }
if value.is_undefined() { return Some(ctx.intern("undefined")); }
if value.is_null() { return Some(ctx.intern("null")); }
if value.is_symbol() { return None; }
if value.is_object() {
if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
let obj = value.as_object();
let to_string_val = obj.get(ctx.common_atoms.to_string);
let to_string_is_callable = to_string_val.map_or(false, |v| v.is_function());
if to_string_is_callable {
let f = to_string_val.unwrap();
if let Ok(r) = vm.call_function_with_this(ctx, f, value.clone(), &[]) {
if !r.is_object() {
return to_string_via_vm(ctx, r);
}
} else {
return None;
}
}
let value_of_val = obj.get(ctx.common_atoms.value_of);
let value_of_is_callable = value_of_val.map_or(false, |v| v.is_function());
if value_of_is_callable {
let f = value_of_val.unwrap();
if let Ok(r) = vm.call_function_with_this(ctx, f, value.clone(), &[]) {
if !r.is_object() {
return to_string_via_vm(ctx, r);
}
} else {
return None;
}
}
}
return None;
}
None
}
fn symbol_for(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
let key_val = if args.is_empty() {
JSValue::new_string(ctx.intern("undefined"))
} else {
let v = args[0];
if let Some(atom) = to_string_via_vm(ctx, v) {
JSValue::new_string(atom)
} else if v.is_symbol() {
throw_type_error(ctx, "Can't convert symbol to string");
return JSValue::undefined();
} else {
JSValue::new_string(ctx.intern(""))
}
};
let key = key_val.get_atom();
let reg = if let Some(ptr) = ctx.get_symbol_registry_ptr() {
unsafe { &mut *(ptr as *mut JSObject) }
} else {
let mut new_reg = JSObject::new_array();
new_reg.set(ctx.common_atoms.length, JSValue::new_int(0));
let reg_ptr = Box::into_raw(Box::new(new_reg)) as usize;
ctx.runtime_mut().gc_heap_mut().track(reg_ptr);
ctx.runtime_mut().gc_heap_mut().extra_roots.push(JSValue::new_object(reg_ptr));
ctx.set_symbol_registry_ptr(Some(reg_ptr));
unsafe { &mut *(reg_ptr as *mut JSObject) }
};
let len_atom = ctx.common_atoms.length;
let len = reg.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
for i in 0..len {
let idx_atom = ctx.intern(&i.to_string());
if let Some(entry) = reg.get(idx_atom) {
if entry.is_object() {
let entry_obj = entry.as_object_mut();
let desc_atom = ctx.intern("description");
if let Some(desc) = entry_obj.get(desc_atom) {
if desc.is_string() && desc.get_atom() == key {
let sym_atom = ctx.intern("symbol");
if let Some(sym) = entry_obj.get(sym_atom) {
return sym;
}
}
}
}
}
}
let new_sym = create_symbol(ctx, Some(key));
let mut entry = JSObject::new();
entry.set(ctx.intern("description"), JSValue::new_string(key));
entry.set(ctx.intern("symbol"), new_sym);
let entry_ptr = Box::into_raw(Box::new(entry)) as usize;
let entry_val = JSValue::new_object(entry_ptr);
let idx_atom = ctx.intern(&len.to_string());
reg.set(idx_atom, entry_val);
reg.set(len_atom, JSValue::new_int((len + 1) as i64));
new_sym
}
fn symbol_key_for(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
throw_type_error(ctx, "Symbol.keyFor requires a symbol");
return JSValue::undefined();
}
let sym_val = args[0];
if !sym_val.is_symbol() {
throw_type_error(ctx, "Symbol.keyFor requires a symbol");
return JSValue::undefined();
}
if let Some(ptr) = ctx.get_symbol_registry_ptr() {
let reg = unsafe { &*(ptr as *const JSObject) };
let len_atom = ctx.common_atoms.length;
let len = reg
.get(len_atom)
.map(|v| v.get_int() as usize)
.unwrap_or(0);
for i in 0..len {
let idx_atom = ctx.intern(&i.to_string());
if let Some(entry) = reg.get(idx_atom) {
if entry.is_object() {
let entry_obj = entry.as_object();
let sym_atom = ctx.intern("symbol");
if let Some(sym) = entry_obj.get(sym_atom) {
if sym.strict_eq(&sym_val) {
let desc_atom = ctx.intern("description");
if let Some(desc) = entry_obj.get(desc_atom) {
return desc;
}
}
}
}
}
}
}
JSValue::undefined()
}
fn symbol_description(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
throw_type_error(
ctx,
"Symbol.prototype.description requires that 'this' be a Symbol",
);
return JSValue::undefined();
}
let this_val = &args[0];
let sym = if this_val.is_symbol() {
this_val.clone()
} else if this_val.is_object() {
if let Some(v) = this_val.as_object().get(ctx.common_atoms.__value__) {
if v.is_symbol() {
v
} else {
throw_type_error(
ctx,
"Symbol.prototype.description requires that 'this' be a Symbol",
);
return JSValue::undefined();
}
} else {
throw_type_error(
ctx,
"Symbol.prototype.description requires that 'this' be a Symbol",
);
return JSValue::undefined();
}
} else {
throw_type_error(
ctx,
"Symbol.prototype.description requires that 'this' be a Symbol",
);
return JSValue::undefined();
};
let desc_atom = sym.get_atom();
if desc_atom == NO_DESCRIPTION {
JSValue::undefined()
} else {
JSValue::new_string(desc_atom)
}
}
fn symbol_to_primitive(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
throw_type_error(
ctx,
"Symbol.prototype[Symbol.toPrimitive] requires that 'this' be a Symbol",
);
return JSValue::undefined();
}
let this_val = &args[0];
if this_val.is_symbol() {
return this_val.clone();
}
if this_val.is_object() {
if let Some(v) = this_val.as_object().get(ctx.common_atoms.__value__) {
if v.is_symbol() {
return v;
}
}
}
throw_type_error(
ctx,
"Symbol.prototype[Symbol.toPrimitive] requires that 'this' be a Symbol",
);
JSValue::undefined()
}
fn symbol_value_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if args.is_empty() {
throw_type_error(
ctx,
"Symbol.prototype.valueOf requires that 'this' be a Symbol",
);
return JSValue::undefined();
}
let this_val = &args[0];
if this_val.is_symbol() {
return this_val.clone();
}
if this_val.is_object() {
if let Some(v) = this_val.as_object().get(ctx.common_atoms.__value__) {
if v.is_symbol() {
return v;
}
}
}
throw_type_error(
ctx,
"Symbol.prototype.valueOf requires that 'this' be a Symbol",
);
JSValue::undefined()
}
fn symbol_to_string(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
let this_val = if args.is_empty() {
return JSValue::new_string(ctx.intern("Symbol()"));
} else {
&args[0]
};
let sym = if this_val.is_symbol() {
this_val.clone()
} else if this_val.is_object() {
let inner = this_val.as_object().get(ctx.common_atoms.__value__);
if let Some(v) = inner {
if v.is_symbol() {
v
} else {
throw_type_error(
ctx,
"Symbol.prototype.toString requires that 'this' be a Symbol",
);
return JSValue::undefined();
}
} else {
throw_type_error(
ctx,
"Symbol.prototype.toString requires that 'this' be a Symbol",
);
return JSValue::undefined();
}
} else {
throw_type_error(
ctx,
"Symbol.prototype.toString requires that 'this' be a Symbol",
);
return JSValue::undefined();
};
let desc_atom = sym.get_atom();
if desc_atom == NO_DESCRIPTION {
JSValue::new_string(ctx.intern("Symbol()"))
} else {
let desc = ctx.get_atom_str(desc_atom);
JSValue::new_string(ctx.intern(&format!("Symbol({})", desc)))
}
}
const SYMBOL_ITERATOR_DESC: &str = "Symbol.iterator";
const SYMBOL_TO_STRING_TAG_DESC: &str = "Symbol.toStringTag";
const SYMBOL_SPECIES_DESC: &str = "Symbol.species";
const SYMBOL_TO_PRIMITIVE_DESC: &str = "Symbol.toPrimitive";
const SYMBOL_IS_CONCAT_SPREADABLE_DESC: &str = "Symbol.isConcatSpreadable";
const SYMBOL_MATCH_DESC: &str = "Symbol.match";
const SYMBOL_REPLACE_DESC: &str = "Symbol.replace";
const SYMBOL_SEARCH_DESC: &str = "Symbol.search";
const SYMBOL_SPLIT_DESC: &str = "Symbol.split";
const SYMBOL_UNSCOPABLES_DESC: &str = "Symbol.unscopables";
const SYMBOL_HAS_INSTANCE_DESC: &str = "Symbol.hasInstance";
const SYMBOL_ASYNC_ITERATOR_DESC: &str = "Symbol.asyncIterator";
const SYMBOL_MATCH_ALL_DESC: &str = "Symbol.matchAll";
const SYMBOL_ASYNC_DISPOSE_DESC: &str = "Symbol.asyncDispose";
const SYMBOL_DISPOSE_DESC: &str = "Symbol.dispose";
pub fn get_or_create_well_known_symbol(ctx: &mut JSContext, description: &str) -> JSValue {
let desc_atom = ctx.intern(description);
let global = ctx.global();
if global.is_object() {
let global_obj = global.as_object_mut();
if let Some(sym) = global_obj.get(desc_atom) {
return sym;
}
let sym = create_symbol(ctx, Some(desc_atom));
global_obj.set(desc_atom, sym);
return sym;
}
create_symbol(ctx, Some(desc_atom))
}
pub fn init_symbol(ctx: &mut JSContext) {
let symbol_atom = ctx.intern("Symbol");
let global = ctx.global();
if global.is_object() {
if global.as_object().get(symbol_atom).is_some() {
return;
}
}
let mut symbol_ctor = JSFunction::new_builtin(ctx.intern("Symbol"), 0);
symbol_ctor.set_builtin_marker(ctx, "symbol_constructor");
let for_func = create_builtin_function(ctx, "symbol_for", 1);
let key_for_func = create_builtin_function(ctx, "symbol_keyFor", 1);
symbol_ctor.base.define_property(
ctx.intern("for"),
crate::object::object::PropertyDescriptor {
value: Some(for_func),
writable: true,
enumerable: false,
configurable: true,
get: None,
set: None,
},
);
symbol_ctor.base.define_property(
ctx.intern("keyFor"),
crate::object::object::PropertyDescriptor {
value: Some(key_for_func),
writable: true,
enumerable: false,
configurable: true,
get: None,
set: None,
},
);
let symbol_iter = get_or_create_well_known_symbol(ctx, SYMBOL_ITERATOR_DESC);
symbol_ctor.base.define_property(
ctx.intern("iterator"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_iter),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_to_string_tag = get_or_create_well_known_symbol(ctx, SYMBOL_TO_STRING_TAG_DESC);
symbol_ctor.base.define_property(
ctx.intern("toStringTag"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_to_string_tag),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_species = get_or_create_well_known_symbol(ctx, SYMBOL_SPECIES_DESC);
symbol_ctor.base.define_property(
ctx.intern("species"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_species),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_to_primitive = get_or_create_well_known_symbol(ctx, SYMBOL_TO_PRIMITIVE_DESC);
symbol_ctor.base.define_property(
ctx.intern("toPrimitive"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_to_primitive),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_is_concat_spreadable =
get_or_create_well_known_symbol(ctx, SYMBOL_IS_CONCAT_SPREADABLE_DESC);
symbol_ctor.base.define_property(
ctx.intern("isConcatSpreadable"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_is_concat_spreadable),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_match = get_or_create_well_known_symbol(ctx, SYMBOL_MATCH_DESC);
symbol_ctor.base.define_property(
ctx.intern("match"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_match),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_replace = get_or_create_well_known_symbol(ctx, SYMBOL_REPLACE_DESC);
symbol_ctor.base.define_property(
ctx.intern("replace"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_replace),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_search = get_or_create_well_known_symbol(ctx, SYMBOL_SEARCH_DESC);
symbol_ctor.base.define_property(
ctx.intern("search"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_search),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_split = get_or_create_well_known_symbol(ctx, SYMBOL_SPLIT_DESC);
symbol_ctor.base.define_property(
ctx.intern("split"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_split),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_unscopables = get_or_create_well_known_symbol(ctx, SYMBOL_UNSCOPABLES_DESC);
symbol_ctor.base.define_property(
ctx.intern("unscopables"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_unscopables),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_has_instance = get_or_create_well_known_symbol(ctx, SYMBOL_HAS_INSTANCE_DESC);
symbol_ctor.base.define_property(
ctx.intern("hasInstance"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_has_instance),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_async_iterator = get_or_create_well_known_symbol(ctx, SYMBOL_ASYNC_ITERATOR_DESC);
symbol_ctor.base.define_property(
ctx.intern("asyncIterator"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_async_iterator),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_match_all = get_or_create_well_known_symbol(ctx, SYMBOL_MATCH_ALL_DESC);
symbol_ctor.base.define_property(
ctx.intern("matchAll"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_match_all),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_async_dispose = get_or_create_well_known_symbol(ctx, SYMBOL_ASYNC_DISPOSE_DESC);
symbol_ctor.base.define_property(
ctx.intern("asyncDispose"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_async_dispose),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let symbol_dispose = get_or_create_well_known_symbol(ctx, SYMBOL_DISPOSE_DESC);
symbol_ctor.base.define_property(
ctx.intern("dispose"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_dispose),
writable: false,
enumerable: false,
configurable: false,
get: None,
set: None,
},
);
let mut sym_proto = JSObject::new();
if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
sym_proto.prototype = Some(obj_proto_ptr);
}
let to_string_fn = create_builtin_function(ctx, "symbol_toString", 0);
let value_of_fn = create_builtin_function(ctx, "symbol_valueOf", 0);
let description_fn = create_builtin_function(ctx, "symbol_description", 0);
let to_primitive_fn = create_builtin_function(ctx, "symbol_toPrimitive", 1);
sym_proto.set(ctx.intern("toString"), to_string_fn.clone());
sym_proto.set(ctx.intern("valueOf"), value_of_fn.clone());
let proto_ptr = Box::into_raw(Box::new(sym_proto)) as usize;
ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
let proto_value = JSValue::new_object(proto_ptr);
symbol_ctor.base.set(ctx.intern("prototype"), proto_value);
ctx.set_symbol_prototype(proto_ptr);
let symbol_to_string_tag = get_or_create_well_known_symbol(ctx, SYMBOL_TO_STRING_TAG_DESC);
let tag_key = crate::runtime::atom::Atom(0x40000000 | symbol_to_string_tag.get_symbol_id());
proto_value.as_object_mut().define_property(
tag_key,
crate::object::object::PropertyDescriptor {
value: Some(JSValue::new_string(ctx.intern("Symbol"))),
writable: false,
enumerable: false,
configurable: true,
get: None,
set: None,
},
);
let symbol_ptr = Box::into_raw(Box::new(symbol_ctor)) as usize;
ctx.runtime_mut().gc_heap_mut().track_function(symbol_ptr);
let symbol_ctor_val = JSValue::new_function(symbol_ptr);
proto_value.as_object_mut().define_property(
ctx.intern("constructor"),
crate::object::object::PropertyDescriptor {
value: Some(symbol_ctor_val.clone()),
writable: true,
enumerable: false,
configurable: true,
get: None,
set: None,
},
);
proto_value.as_object_mut().define_property(
ctx.intern("toString"),
crate::object::object::PropertyDescriptor {
value: Some(to_string_fn),
writable: true,
enumerable: false,
configurable: true,
get: None,
set: None,
},
);
proto_value.as_object_mut().define_property(
ctx.intern("valueOf"),
crate::object::object::PropertyDescriptor {
value: Some(value_of_fn),
writable: true,
enumerable: false,
configurable: true,
get: None,
set: None,
},
);
proto_value.as_object_mut().define_property(
ctx.intern("description"),
crate::object::object::PropertyDescriptor {
value: None,
writable: false,
enumerable: false,
configurable: true,
get: Some(description_fn),
set: None,
},
);
let symbol_to_primitive_sym = get_or_create_well_known_symbol(ctx, SYMBOL_TO_PRIMITIVE_DESC);
let to_prim_key =
crate::runtime::atom::Atom(0x40000000 | symbol_to_primitive_sym.get_symbol_id());
proto_value.as_object_mut().define_property(
to_prim_key,
crate::object::object::PropertyDescriptor {
value: Some(to_primitive_fn),
writable: false,
enumerable: false,
configurable: true,
get: None,
set: None,
},
);
let symbol_atom = ctx.intern("Symbol");
let global = ctx.global();
if global.is_object() {
let global_obj = global.as_object_mut();
crate::builtins::global::set_non_enumerable(global_obj, symbol_atom, symbol_ctor_val);
}
}
pub fn register_builtins(ctx: &mut JSContext) {
ctx.register_builtin(
"symbol_constructor",
HostFunction::ctor("Symbol", 0, symbol_constructor),
);
ctx.register_builtin("symbol_for", HostFunction::new("for", 1, symbol_for));
ctx.register_builtin(
"symbol_keyFor",
HostFunction::new("keyFor", 1, symbol_key_for),
);
ctx.register_builtin(
"symbol_valueOf",
HostFunction::method("valueOf", 0, symbol_value_of),
);
ctx.register_builtin(
"symbol_toString",
HostFunction::method("toString", 0, symbol_to_string),
);
ctx.register_builtin(
"symbol_description",
HostFunction::method("description", 0, symbol_description),
);
ctx.register_builtin(
"symbol_toPrimitive",
HostFunction::method("[Symbol.toPrimitive]", 1, symbol_to_primitive),
);
ctx.register_builtin(
"symbol_species_get",
HostFunction::method("get [Symbol.species]", 0, species_getter),
);
}
pub fn fix_prototype_chain(ctx: &mut JSContext) {
if let Some(sym_proto_ptr) = ctx.get_symbol_prototype() {
if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
unsafe {
(*sym_proto_ptr).prototype = Some(obj_proto_ptr);
}
}
}
}
pub fn get_symbol_iterator(ctx: &mut JSContext) -> JSValue {
get_or_create_well_known_symbol(ctx, SYMBOL_ITERATOR_DESC)
}
pub fn get_symbol_iterator_atom(ctx: &mut JSContext) -> Atom {
let sym = get_or_create_well_known_symbol(ctx, SYMBOL_ITERATOR_DESC);
crate::runtime::atom::Atom(0x40000000 | sym.get_symbol_id())
}
pub fn get_symbol_split(ctx: &mut JSContext) -> JSValue {
get_or_create_well_known_symbol(ctx, SYMBOL_SPLIT_DESC)
}
pub fn get_symbol_split_atom(ctx: &mut JSContext) -> Atom {
let sym = get_or_create_well_known_symbol(ctx, SYMBOL_SPLIT_DESC);
crate::runtime::atom::Atom(0x40000000 | sym.get_symbol_id())
}
pub fn get_symbol_unscopables(ctx: &mut JSContext) -> JSValue {
get_or_create_well_known_symbol(ctx, SYMBOL_UNSCOPABLES_DESC)
}
pub fn get_symbol_unscopables_atom(ctx: &mut JSContext) -> Atom {
let sym = get_or_create_well_known_symbol(ctx, SYMBOL_UNSCOPABLES_DESC);
crate::runtime::atom::Atom(0x40000000 | sym.get_symbol_id())
}
pub fn get_symbol_has_instance(ctx: &mut JSContext) -> JSValue {
get_or_create_well_known_symbol(ctx, SYMBOL_HAS_INSTANCE_DESC)
}
pub fn get_symbol_has_instance_atom(ctx: &mut JSContext) -> Atom {
let sym = get_or_create_well_known_symbol(ctx, SYMBOL_HAS_INSTANCE_DESC);
crate::runtime::atom::Atom(0x40000000 | sym.get_symbol_id())
}
pub fn get_symbol_async_iterator(ctx: &mut JSContext) -> JSValue {
get_or_create_well_known_symbol(ctx, SYMBOL_ASYNC_ITERATOR_DESC)
}
pub fn get_symbol_async_iterator_atom(ctx: &mut JSContext) -> Atom {
let sym = get_or_create_well_known_symbol(ctx, SYMBOL_ASYNC_ITERATOR_DESC);
crate::runtime::atom::Atom(0x40000000 | sym.get_symbol_id())
}
pub fn get_symbol_to_string_tag_prop_key(ctx: &mut JSContext) -> Atom {
let sym = get_or_create_well_known_symbol(ctx, SYMBOL_TO_STRING_TAG_DESC);
crate::runtime::atom::Atom(0x40000000 | sym.get_symbol_id())
}
pub fn get_symbol_species(ctx: &mut JSContext) -> JSValue {
get_or_create_well_known_symbol(ctx, SYMBOL_SPECIES_DESC)
}
fn species_getter(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
if let Some(this_val) = args.first() {
*this_val
} else {
JSValue::undefined()
}
}
pub fn install_species_accessor(ctx: &mut JSContext, ctor_val: &JSValue) {
if !ctor_val.is_function() {
return;
}
let species_sym = get_symbol_species(ctx);
if species_sym.is_undefined() {
return;
}
let species_atom = crate::runtime::atom::Atom(0x40000000 | species_sym.get_symbol_id());
let getter_fn = {
let name_atom = ctx.intern("get [Symbol.species]");
let mut f = crate::object::function::JSFunction::new_builtin(
name_atom,
0,
);
f.builtin_atom = Some(ctx.intern("symbol_species_get"));
f.builtin_func = ctx.get_builtin_func("symbol_species_get");
let name_desc = crate::object::object::PropertyDescriptor {
value: Some(JSValue::new_string(name_atom)),
writable: false,
enumerable: false,
configurable: true,
get: None,
set: None,
};
f.base.define_property(ctx.common_atoms.name, name_desc);
let length_desc = crate::object::object::PropertyDescriptor {
value: Some(JSValue::new_int(0)),
writable: false,
enumerable: false,
configurable: true,
get: None,
set: None,
};
f.base.define_property(ctx.common_atoms.length, length_desc);
let ptr = Box::into_raw(Box::new(f)) as usize;
ctx.runtime_mut().gc_heap_mut().track_function(ptr);
JSValue::new_function(ptr)
};
let func_mut = ctor_val.as_function_mut();
let entry = crate::object::object::AccessorEntry {
get: Some(getter_fn),
set: None,
enumerable: false,
configurable: true,
};
func_mut.base.ensure_extra().accessors.get_or_insert_with(|| Box::new(crate::util::FxHashMap::default())).insert(species_atom, entry);
}
pub fn get_symbol_to_string_tag(ctx: &mut JSContext) -> JSValue {
get_or_create_well_known_symbol(ctx, SYMBOL_TO_STRING_TAG_DESC)
}
pub fn is_symbol(val: &JSValue) -> bool {
val.is_symbol()
}