use bitflags::bitflags;
use crate::objects::tagged::TaggedValue;
#[repr(u16)]
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InstanceType {
Map = 0x0080,
FixedArray = 0x0088,
ByteArray = 0x0090,
SharedFunctionInfo = 0x00c0,
Code = 0x00c8,
FunctionTemplate = 0x00d0,
HeapNumber = 0x0400,
BigInt = 0x0408,
Symbol = 0x0410,
JsString = 0x0800,
InternalizedString = 0x0808,
ThinString = 0x0810,
JsObject = 0x1000,
JsArray = 0x1008,
JsFunction = 0x1010,
JsRegExp = 0x1018,
JsDate = 0x1020,
JsMap = 0x1028,
JsSet = 0x1030,
JsWeakMap = 0x1038,
JsWeakSet = 0x1040,
JsPromise = 0x1048,
JsProxy = 0x1050,
JsError = 0x1058,
JsArguments = 0x1060,
JsGeneratorObject = 0x1068,
JsAsyncFunctionObject = 0x1070,
}
bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub struct PropertyAttributes: u8 {
const WRITABLE = 0b001;
const ENUMERABLE = 0b010;
const CONFIGURABLE = 0b100;
}
}
pub struct PropertyDescriptor {
key: String,
attributes: PropertyAttributes,
}
impl PropertyDescriptor {
pub fn new(key: impl Into<String>, attributes: PropertyAttributes) -> Self {
Self {
key: key.into(),
attributes,
}
}
pub fn key(&self) -> &str {
&self.key
}
pub fn attributes(&self) -> PropertyAttributes {
self.attributes
}
}
pub struct Map {
instance_type: InstanceType,
instance_size: u32,
prototype: TaggedValue,
descriptors: Vec<PropertyDescriptor>,
}
impl Map {
pub fn new(instance_type: InstanceType, instance_size: u32) -> Self {
Self {
instance_type,
instance_size,
prototype: TaggedValue(0),
descriptors: Vec::new(),
}
}
#[inline]
pub fn instance_type(&self) -> InstanceType {
self.instance_type
}
#[inline]
pub fn instance_size(&self) -> u32 {
self.instance_size
}
#[inline]
pub fn prototype(&self) -> TaggedValue {
self.prototype
}
pub fn set_prototype(&mut self, prototype: TaggedValue) {
self.prototype = prototype;
}
pub fn descriptors(&self) -> &[PropertyDescriptor] {
&self.descriptors
}
pub fn add_descriptor(&mut self, descriptor: PropertyDescriptor) {
self.descriptors.push(descriptor);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_map_instance_type_roundtrip() {
let map = Map::new(InstanceType::Map, 0);
assert_eq!(map.instance_type(), InstanceType::Map);
}
#[test]
fn test_map_instance_size() {
let map = Map::new(InstanceType::JsObject, 32);
assert_eq!(map.instance_type(), InstanceType::JsObject);
assert_eq!(map.instance_size(), 32);
}
#[test]
fn test_map_prototype_default_is_zero() {
let map = Map::new(InstanceType::JsObject, 16);
assert_eq!(map.prototype(), TaggedValue(0));
}
#[test]
fn test_map_set_prototype() {
let mut map = Map::new(InstanceType::JsObject, 16);
let proto = TaggedValue(0x1000);
map.set_prototype(proto);
assert_eq!(map.prototype(), proto);
}
#[test]
fn test_map_descriptors_empty_by_default() {
let map = Map::new(InstanceType::JsObject, 16);
assert!(map.descriptors().is_empty());
}
#[test]
fn test_map_add_and_lookup_descriptor() {
let mut map = Map::new(InstanceType::JsObject, 16);
let desc = PropertyDescriptor::new(
"x",
PropertyAttributes::WRITABLE | PropertyAttributes::ENUMERABLE,
);
map.add_descriptor(desc);
assert_eq!(map.descriptors().len(), 1);
assert_eq!(map.descriptors()[0].key(), "x");
assert!(
map.descriptors()[0]
.attributes()
.contains(PropertyAttributes::WRITABLE)
);
assert!(
map.descriptors()[0]
.attributes()
.contains(PropertyAttributes::ENUMERABLE)
);
assert!(
!map.descriptors()[0]
.attributes()
.contains(PropertyAttributes::CONFIGURABLE)
);
}
#[test]
fn test_map_multiple_descriptors() {
let mut map = Map::new(InstanceType::JsObject, 32);
map.add_descriptor(PropertyDescriptor::new("a", PropertyAttributes::WRITABLE));
map.add_descriptor(PropertyDescriptor::new(
"b",
PropertyAttributes::ENUMERABLE | PropertyAttributes::CONFIGURABLE,
));
assert_eq!(map.descriptors().len(), 2);
assert_eq!(map.descriptors()[0].key(), "a");
assert_eq!(map.descriptors()[1].key(), "b");
}
#[test]
fn test_all_instance_type_discriminants_are_unique() {
let types: &[InstanceType] = &[
InstanceType::Map,
InstanceType::FixedArray,
InstanceType::ByteArray,
InstanceType::SharedFunctionInfo,
InstanceType::Code,
InstanceType::FunctionTemplate,
InstanceType::HeapNumber,
InstanceType::BigInt,
InstanceType::Symbol,
InstanceType::JsString,
InstanceType::InternalizedString,
InstanceType::ThinString,
InstanceType::JsObject,
InstanceType::JsArray,
InstanceType::JsFunction,
InstanceType::JsRegExp,
InstanceType::JsDate,
InstanceType::JsMap,
InstanceType::JsSet,
InstanceType::JsWeakMap,
InstanceType::JsWeakSet,
InstanceType::JsPromise,
InstanceType::JsProxy,
InstanceType::JsError,
InstanceType::JsArguments,
InstanceType::JsGeneratorObject,
InstanceType::JsAsyncFunctionObject,
];
assert_eq!(types.len(), 27);
let discriminants: std::collections::HashSet<u16> =
types.iter().map(|&t| t as u16).collect();
assert_eq!(
discriminants.len(),
types.len(),
"duplicate discriminant values found"
);
}
}