use crate::types::{ObjectFlags, ObjectShape, PropertyInfo, TypeId, TypeParamInfo};
use dashmap::DashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use tracing::trace;
use tsz_common::interner::Atom;
static NEXT_INSTANCE_ID: AtomicU64 = AtomicU64::new(1);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct DefId(pub u32);
impl DefId {
pub const INVALID: Self = Self(0);
pub const FIRST_VALID: u32 = 1;
pub const fn is_valid(self) -> bool {
self.0 >= Self::FIRST_VALID
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum DefKind {
TypeAlias,
Interface,
Class,
Enum,
Namespace,
}
#[derive(Clone, Debug)]
pub struct DefinitionInfo {
pub kind: DefKind,
pub name: Atom,
pub type_params: Vec<TypeParamInfo>,
pub body: Option<TypeId>,
pub instance_shape: Option<Arc<ObjectShape>>,
pub static_shape: Option<Arc<ObjectShape>>,
pub extends: Option<DefId>,
pub implements: Vec<DefId>,
pub enum_members: Vec<(Atom, EnumMemberValue)>,
pub exports: Vec<(Atom, DefId)>,
pub file_id: Option<u32>,
pub span: Option<(u32, u32)>,
pub symbol_id: Option<u32>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum EnumMemberValue {
Number(f64),
String(Atom),
Computed,
}
impl DefinitionInfo {
pub const fn type_alias(name: Atom, type_params: Vec<TypeParamInfo>, body: TypeId) -> Self {
Self {
kind: DefKind::TypeAlias,
name,
type_params,
body: Some(body),
instance_shape: None,
static_shape: None,
extends: None,
implements: Vec::new(),
enum_members: Vec::new(),
exports: Vec::new(),
file_id: None,
span: None,
symbol_id: None,
}
}
pub fn interface(
name: Atom,
type_params: Vec<TypeParamInfo>,
properties: Vec<PropertyInfo>,
) -> Self {
let shape = ObjectShape {
flags: ObjectFlags::empty(),
properties,
string_index: None,
number_index: None,
symbol: None,
};
Self {
kind: DefKind::Interface,
name,
type_params,
body: None, instance_shape: Some(Arc::new(shape)),
static_shape: None,
extends: None,
implements: Vec::new(),
enum_members: Vec::new(),
exports: Vec::new(),
file_id: None,
span: None,
symbol_id: None,
}
}
pub fn class(
name: Atom,
type_params: Vec<TypeParamInfo>,
instance_properties: Vec<PropertyInfo>,
static_properties: Vec<PropertyInfo>,
) -> Self {
let instance_shape = ObjectShape {
flags: ObjectFlags::empty(),
properties: instance_properties,
string_index: None,
number_index: None,
symbol: None,
};
let static_shape = ObjectShape {
flags: ObjectFlags::empty(),
properties: static_properties,
string_index: None,
number_index: None,
symbol: None,
};
Self {
kind: DefKind::Class,
name,
type_params,
body: None,
instance_shape: Some(Arc::new(instance_shape)),
static_shape: Some(Arc::new(static_shape)),
extends: None,
implements: Vec::new(),
enum_members: Vec::new(),
exports: Vec::new(),
file_id: None,
span: None,
symbol_id: None,
}
}
pub const fn enumeration(name: Atom, members: Vec<(Atom, EnumMemberValue)>) -> Self {
Self {
kind: DefKind::Enum,
name,
type_params: Vec::new(),
body: None,
instance_shape: None,
static_shape: None,
extends: None,
implements: Vec::new(),
enum_members: members,
exports: Vec::new(),
file_id: None,
span: None,
symbol_id: None,
}
}
pub const fn namespace(name: Atom, exports: Vec<(Atom, DefId)>) -> Self {
Self {
kind: DefKind::Namespace,
name,
type_params: Vec::new(),
body: None,
instance_shape: None,
static_shape: None,
extends: None,
implements: Vec::new(),
enum_members: Vec::new(),
exports,
file_id: None,
span: None,
symbol_id: None,
}
}
pub const fn with_extends(mut self, parent: DefId) -> Self {
self.extends = Some(parent);
self
}
pub fn with_implements(mut self, interfaces: Vec<DefId>) -> Self {
self.implements = interfaces;
self
}
pub fn with_exports(mut self, exports: Vec<(Atom, DefId)>) -> Self {
self.exports = exports;
self
}
pub fn add_export(&mut self, name: Atom, def_id: DefId) {
self.exports.push((name, def_id));
}
pub fn get_export(&self, name: Atom) -> Option<DefId> {
self.exports
.iter()
.find(|(n, _)| *n == name)
.map(|(_, d)| *d)
}
pub const fn with_file_id(mut self, file_id: u32) -> Self {
self.file_id = Some(file_id);
self
}
pub const fn with_span(mut self, start: u32, end: u32) -> Self {
self.span = Some((start, end));
self
}
}
#[derive(Debug)]
pub struct DefinitionStore {
instance_id: u64,
definitions: DashMap<DefId, DefinitionInfo>,
next_id: AtomicU32,
}
impl Default for DefinitionStore {
fn default() -> Self {
Self::new()
}
}
impl DefinitionStore {
pub fn new() -> Self {
let instance_id = NEXT_INSTANCE_ID.fetch_add(1, Ordering::SeqCst);
trace!(instance_id, "DefinitionStore::new - creating new instance");
Self {
instance_id,
definitions: DashMap::new(),
next_id: AtomicU32::new(DefId::FIRST_VALID),
}
}
fn allocate(&self) -> DefId {
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
trace!(
instance_id = self.instance_id,
allocated_def_id = %id,
next_will_be = %(id + 1),
"DefinitionStore::allocate"
);
DefId(id)
}
pub fn register(&self, info: DefinitionInfo) -> DefId {
let id = self.allocate();
trace!(
instance_id = self.instance_id,
def_id = %id.0,
kind = ?info.kind,
"DefinitionStore::register"
);
self.definitions.insert(id, info);
id
}
pub fn get(&self, id: DefId) -> Option<DefinitionInfo> {
self.definitions.get(&id).as_deref().cloned()
}
pub fn get_symbol_id(&self, id: DefId) -> Option<u32> {
self.definitions.get(&id).and_then(|info| info.symbol_id)
}
pub fn contains(&self, id: DefId) -> bool {
self.definitions.contains_key(&id)
}
pub fn get_kind(&self, id: DefId) -> Option<DefKind> {
self.definitions.get(&id).map(|r| r.kind)
}
pub fn get_type_params(&self, id: DefId) -> Option<Vec<TypeParamInfo>> {
self.definitions.get(&id).map(|r| r.type_params.clone())
}
pub fn get_body(&self, id: DefId) -> Option<TypeId> {
self.definitions.get(&id).and_then(|r| r.body)
}
pub fn get_instance_shape(&self, id: DefId) -> Option<Arc<ObjectShape>> {
self.definitions
.get(&id)
.and_then(|r| r.instance_shape.clone())
}
pub fn get_static_shape(&self, id: DefId) -> Option<Arc<ObjectShape>> {
self.definitions
.get(&id)
.and_then(|r| r.static_shape.clone())
}
pub fn get_extends(&self, id: DefId) -> Option<DefId> {
self.definitions.get(&id).and_then(|r| r.extends)
}
pub fn get_implements(&self, id: DefId) -> Option<Vec<DefId>> {
self.definitions.get(&id).map(|r| r.implements.clone())
}
pub fn set_body(&self, id: DefId, body: TypeId) {
if let Some(mut entry) = self.definitions.get_mut(&id) {
entry.body = Some(body);
}
}
pub fn set_instance_shape(&self, id: DefId, shape: Arc<ObjectShape>) {
if let Some(mut entry) = self.definitions.get_mut(&id) {
entry.instance_shape = Some(shape);
}
}
pub fn len(&self) -> usize {
self.definitions.len()
}
pub fn is_empty(&self) -> bool {
self.definitions.is_empty()
}
pub fn clear(&self) {
self.definitions.clear();
self.next_id.store(DefId::FIRST_VALID, Ordering::SeqCst);
}
pub fn get_exports(&self, id: DefId) -> Option<Vec<(Atom, DefId)>> {
self.definitions.get(&id).map(|r| r.exports.clone())
}
pub fn get_enum_members(&self, id: DefId) -> Option<Vec<(Atom, EnumMemberValue)>> {
self.definitions.get(&id).map(|r| r.enum_members.clone())
}
pub fn get_name(&self, id: DefId) -> Option<Atom> {
self.definitions.get(&id).map(|r| r.name)
}
pub fn set_exports(&self, id: DefId, exports: Vec<(Atom, DefId)>) {
if let Some(mut entry) = self.definitions.get_mut(&id) {
entry.exports = exports;
}
}
pub fn add_export(&self, id: DefId, name: Atom, export_def: DefId) {
if let Some(mut entry) = self.definitions.get_mut(&id) {
entry.add_export(name, export_def);
}
}
pub fn set_enum_members(&self, id: DefId, members: Vec<(Atom, EnumMemberValue)>) {
if let Some(mut entry) = self.definitions.get_mut(&id) {
entry.enum_members = members;
}
}
pub fn all_ids(&self) -> Vec<DefId> {
self.definitions.iter().map(|r| *r.key()).collect()
}
pub fn find_def_by_shape(&self, shape: &ObjectShape) -> Option<DefId> {
self.definitions
.iter()
.find(|entry| {
entry
.value()
.instance_shape
.as_ref()
.map(std::convert::AsRef::as_ref)
== Some(shape)
})
.map(|entry| *entry.key())
}
}
pub struct ContentAddressedDefIds {
hash_to_def: DashMap<u64, DefId>,
next_id: AtomicU32,
}
impl Default for ContentAddressedDefIds {
fn default() -> Self {
Self::new()
}
}
impl ContentAddressedDefIds {
pub fn new() -> Self {
Self {
hash_to_def: DashMap::new(),
next_id: AtomicU32::new(DefId::FIRST_VALID),
}
}
pub fn get_or_create(&self, name: Atom, file_id: u32, span_start: u32) -> DefId {
use std::hash::{Hash, Hasher};
let mut hasher = rustc_hash::FxHasher::default();
name.hash(&mut hasher);
file_id.hash(&mut hasher);
span_start.hash(&mut hasher);
let hash = hasher.finish();
if let Some(existing) = self.hash_to_def.get(&hash) {
return *existing;
}
let id = DefId(self.next_id.fetch_add(1, Ordering::SeqCst));
self.hash_to_def.insert(hash, id);
id
}
pub fn clear(&self) {
self.hash_to_def.clear();
self.next_id.store(DefId::FIRST_VALID, Ordering::SeqCst);
}
}
#[cfg(test)]
#[path = "../tests/def_tests.rs"]
mod tests;