sim_lib_lang_javascript/objects/
space.rs1pub struct JavascriptObjects {
3 heap: JavascriptHeap,
4 properties: PropertyStore<ManagedId, JavascriptPropertyKey, JavascriptValue, Accessor>,
5 prototypes: BTreeMap<ManagedId, ManagedHandle>,
6 functions: BTreeMap<ManagedId, JavascriptFunction>,
7 lexical_this: BTreeMap<ManagedId, JavascriptValue>,
8}
9impl JavascriptObjects {
10 pub fn new(heap: JavascriptHeap) -> Self {
12 Self {
13 heap,
14 properties: PropertyStore::new(),
15 prototypes: BTreeMap::new(),
16 functions: BTreeMap::new(),
17 lexical_this: BTreeMap::new(),
18 }
19 }
20 pub fn ordinary(&mut self) -> Result<ManagedHandle, JavascriptObjectError> {
23 Ok(self
24 .heap
25 .allocate(JavascriptManagedObject::new(JavascriptManagedKind::Object))?)
26 }
27 pub fn function(
29 &mut self,
30 function: JavascriptFunction,
31 lexical_this: Option<JavascriptValue>,
32 ) -> Result<ManagedHandle, JavascriptObjectError> {
33 let h = self.heap.allocate(JavascriptManagedObject::new(
34 JavascriptManagedKind::Function,
35 ))?;
36 for capture in function.captures() {
37 self.heap.connect(h, capture.managed())?;
38 }
39 if let Some(v) = lexical_this {
40 self.lexical_this.insert(h.id(), v);
41 }
42 self.functions.insert(h.id(), function);
43 Ok(h)
44 }
45 pub fn call_this(
47 &self,
48 function: ManagedHandle,
49 receiver: JavascriptValue,
50 ) -> Result<JavascriptThis, JavascriptObjectError> {
51 let f = self
52 .functions
53 .get(&function.id())
54 .ok_or(JavascriptObjectError::NotConstructor)?;
55 Ok(if f.policy.kind == JavascriptFunctionKind::Arrow {
56 JavascriptThis::Lexical(
57 self.lexical_this
58 .get(&function.id())
59 .cloned()
60 .unwrap_or(JavascriptValue::Undefined),
61 )
62 } else {
63 JavascriptThis::Dynamic(receiver)
64 })
65 }
66 pub fn call<T, E>(
70 &self,
71 function: ManagedHandle,
72 receiver: JavascriptValue,
73 arguments: &[JavascriptValue],
74 body: impl FnOnce(
75 &FunctionPlan,
76 &[CapturedBinding],
77 JavascriptThis,
78 &[JavascriptValue],
79 ) -> Result<T, E>,
80 ) -> Result<T, E>
81 where
82 E: From<JavascriptObjectError>,
83 {
84 let metadata = self
85 .functions
86 .get(&function.id())
87 .ok_or(JavascriptObjectError::NotConstructor)?;
88 let this = self.call_this(function, receiver)?;
89 body(metadata.plan(), metadata.captures(), this, arguments)
90 }
91 pub fn construct(
93 &mut self,
94 function: ManagedHandle,
95 ) -> Result<ManagedHandle, JavascriptObjectError> {
96 let f = self
97 .functions
98 .get(&function.id())
99 .ok_or(JavascriptObjectError::NotConstructor)?;
100 if !f.policy.constructable || f.policy.kind == JavascriptFunctionKind::Arrow {
101 return Err(JavascriptObjectError::NotConstructor);
102 }
103 let instance = self.ordinary()?;
104 self.set_prototype(instance, function)?;
105 Ok(instance)
106 }
107 pub fn set_prototype(
109 &mut self,
110 object: ManagedHandle,
111 prototype: ManagedHandle,
112 ) -> Result<(), JavascriptObjectError> {
113 let mut at = Some(prototype);
114 let mut seen = HashSet::new();
115 while let Some(h) = at {
116 if h.id() == object.id() || !seen.insert(h.id()) {
117 return Err(JavascriptObjectError::PrototypeCycle);
118 }
119 at = self.prototypes.get(&h.id()).copied();
120 }
121 self.heap.connect(object, prototype)?;
122 self.prototypes.insert(object.id(), prototype);
123 Ok(())
124 }
125 pub fn define_data(
127 &mut self,
128 object: ManagedHandle,
129 key: JavascriptPropertyKey,
130 value: JavascriptValue,
131 writable: bool,
132 enumerable: bool,
133 configurable: bool,
134 ) -> Result<(), JavascriptObjectError> {
135 self.properties.define(
136 &object.id(),
137 key,
138 Descriptor::Data(DataDescriptor {
139 value,
140 writable,
141 enumerable,
142 configurable,
143 }),
144 )?;
145 Ok(())
146 }
147 pub fn define_accessor(
149 &mut self,
150 object: ManagedHandle,
151 key: JavascriptPropertyKey,
152 get: Option<JavascriptValue>,
153 set: bool,
154 enumerable: bool,
155 configurable: bool,
156 ) -> Result<(), JavascriptObjectError> {
157 self.properties.define(
158 &object.id(),
159 key,
160 Descriptor::Accessor(AccessorDescriptor {
161 get: Some(Accessor { get, set }),
162 set: Some(Accessor { get: None, set }),
163 enumerable,
164 configurable,
165 }),
166 )?;
167 Ok(())
168 }
169 fn chain(
170 &self,
171 object: ManagedHandle,
172 budget: usize,
173 ) -> Result<Vec<ManagedId>, JavascriptObjectError> {
174 let mut out = Vec::new();
175 let mut at = Some(object);
176 let mut seen = HashSet::new();
177 while let Some(h) = at {
178 if out.len() >= budget {
179 return Err(JavascriptObjectError::Access);
180 }
181 if !seen.insert(h.id()) {
182 break;
183 }
184 out.push(h.id());
185 at = self.prototypes.get(&h.id()).copied();
186 }
187 Ok(out)
188 }
189 pub fn get(
191 &self,
192 object: ManagedHandle,
193 key: &JavascriptPropertyKey,
194 budget: usize,
195 ) -> Result<Option<JavascriptValue>, JavascriptObjectError> {
196 let chain = self.chain(object, budget)?;
197 let mut hooks = Hooks::default();
198 self.properties
199 .get(
200 &chain,
201 &object.id(),
202 key,
203 &mut AccessContext::new(budget),
204 &mut hooks,
205 )
206 .map_err(|_| JavascriptObjectError::Access)
207 }
208 pub fn set(
211 &mut self,
212 object: ManagedHandle,
213 key: &JavascriptPropertyKey,
214 value: JavascriptValue,
215 budget: usize,
216 ) -> Result<bool, JavascriptObjectError> {
217 let chain = self.chain(object, budget)?;
218 let mut hooks = Hooks::default();
219 self.properties
220 .set(
221 &chain,
222 &object.id(),
223 key,
224 value,
225 &mut AccessContext::new(budget),
226 &mut hooks,
227 )
228 .map_err(|_| JavascriptObjectError::Access)
229 }
230 pub fn delete(
232 &mut self,
233 object: ManagedHandle,
234 key: &JavascriptPropertyKey,
235 ) -> Result<bool, JavascriptObjectError> {
236 Ok(self.properties.delete(&object.id(), key)?)
237 }
238 pub fn enumerable_keys(&self, object: ManagedHandle) -> Vec<JavascriptPropertyKey> {
241 let keys = self.properties.own_keys(&object.id(), true);
242 let mut indices = Vec::new();
243 let mut strings = Vec::new();
244 let mut symbols = Vec::new();
245 for key in keys {
246 match &key {
247 JavascriptPropertyKey::String(s) => match s.parse::<u32>() {
248 Ok(n) if n != u32::MAX && n.to_string() == *s => indices.push((n, key)),
249 _ => strings.push(key),
250 },
251 JavascriptPropertyKey::Symbol(_) => symbols.push(key),
252 JavascriptPropertyKey::Private { .. } => {}
253 }
254 }
255 indices.sort_by_key(|(n, _)| *n);
256 indices
257 .into_iter()
258 .map(|(_, k)| k)
259 .chain(strings)
260 .chain(symbols)
261 .collect()
262 }
263 pub fn private_key(
265 &self,
266 class: ManagedHandle,
267 name: &str,
268 ) -> Result<JavascriptPropertyKey, JavascriptObjectError> {
269 let f = self
270 .functions
271 .get(&class.id())
272 .ok_or(JavascriptObjectError::PrivateBrand)?;
273 if !f.private_names.iter().any(|n| n == name) {
274 return Err(JavascriptObjectError::PrivateBrand);
275 }
276 Ok(JavascriptPropertyKey::Private {
277 class: class.id(),
278 name: name.into(),
279 })
280 }
281 pub fn collect(
283 &mut self,
284 ) -> Result<Option<sim_lib_gc_tracing::CollectionReceipt>, sim_lib_gc_tracing::CollectionError>
285 {
286 self.heap.collect()
287 }
288 pub fn live_len(&self) -> usize {
290 self.heap.live_len()
291 }
292}
293pub const fn javascript_callable_shape_constraints() -> &'static [&'static str] {
296 &[]
297}
298pub const fn javascript_object_gaps() -> &'static [JavascriptObjectGap] {
300 &[
301 JavascriptObjectGap::ProxyInvariants,
302 JavascriptObjectGap::ExoticObjectInvariants,
303 ]
304}