1use std::marker::PhantomData;
2
3use crate::{
4 context::{ContextImpl, ContextPtr},
5 error::JSResult,
6 object::{Object, ObjectImpl},
7 utils::unlikely,
8 value::{JS_GetPrototypePrimitive, JSValue},
9};
10
11pub struct ObjectShapeProtoIter<'a> {
13 obj: Option<Object>,
14 _mark: PhantomData<&'a ()>,
15}
16
17impl<'a> Iterator for ObjectShapeProtoIter<'a> {
18 type Item = Object;
19
20 #[inline]
21 fn next(&mut self) -> Option<Self::Item> {
22 if let Some(o) = self.obj.take() {
23 self.obj = o.shape().and_then(|s| s.proto());
24 Some(o)
25 } else {
26 None
27 }
28 }
29}
30
31impl ObjectImpl {
32 #[inline(always)]
34 pub fn shape_proto(&self) -> Option<Object> {
35 self.shape().and_then(|s| s.proto())
36 }
37
38 #[inline]
42 pub fn shape_proto_iter<'a>(&self, include_self: bool) -> ObjectShapeProtoIter<'a> {
43 ObjectShapeProtoIter {
44 obj: if include_self {
45 Some(unsafe { Object::from_ref_unchecked(self) })
46 } else {
47 self.shape_proto()
48 },
49 _mark: PhantomData,
50 }
51 }
52
53 pub fn prototype(&self, ctx: &ContextImpl) -> Result<Option<Object>, i32> {
60 if self.flags().is_exotic()
61 && let Some(cb) = ctx
62 .rt_ref()
63 .class_registry
64 .get(self.class_id() as usize)
65 .and_then(|c| c.exotic.as_deref())
66 .and_then(|em| em.get_prototype)
67 {
68 cb(
70 unsafe { ContextPtr::from_ref_unchecked(ctx) },
71 JSValue::Object(unsafe { Object::from_ref_unchecked(self) }),
72 )
73 .map(|v| Object::try_from(v).ok())
74 } else {
75 Ok(self.shape_proto())
76 }
77 }
78
79 pub(crate) fn set_prototype_internal(
83 &mut self,
84 ctx: &ContextImpl,
85 proto_val: JSValue,
86 throw_flag: bool,
87 ) -> Result<bool, i32> {
88 let proto = if let JSValue::Object(p) = proto_val {
89 Some(p)
90 } else if proto_val.is_null() {
91 None
92 } else {
93 ctx.throw_type_error_not_an_object();
94 return Err(-1);
95 };
96
97 let obj = unsafe { Object::from_ref_unchecked(self) };
98
99 if self.flags().is_exotic()
100 && let Some(cb) = ctx
101 .rt_ref()
102 .class_registry
103 .get(self.class_id() as usize)
104 .and_then(|c| c.exotic.as_deref())
105 .and_then(|em| em.set_prototype)
106 {
107 let ret = cb(
109 unsafe { ContextPtr::from_ref_unchecked(ctx) },
110 JSValue::Object(obj),
111 proto_val,
112 );
113
114 return if ret == Ok(false) && throw_flag {
115 ctx.throw_type_error("proxy: bad prototype");
116 Err(-1)
117 } else {
118 ret
119 };
120 }
121
122 if self.shape().unwrap().proto == proto {
123 return Ok(true);
124 }
125
126 let flags = self.flags();
127 if flags.immutable_prototype() {
128 return if throw_flag {
129 ctx.throw_type_error("prototype is immutable");
130 Err(-1)
131 } else {
132 Ok(false)
133 };
134 } else if !flags.extensible() {
135 return if throw_flag {
136 ctx.throw_type_error("object is not extensible");
137 Err(-1)
138 } else {
139 Ok(false)
140 };
141 }
142
143 if let Some(up) = proto {
144 let mut p1 = up;
146
147 loop {
148 if p1.id() == self.id() {
149 if throw_flag {
150 ctx.throw_type_error("circular prototype chain");
151 return Err(-1);
152 } else {
153 return Ok(false);
154 }
155 }
156
157 if let Some(p2) = p1.shape().and_then(|s| s.proto()) {
159 p1 = p2;
160 } else {
161 break;
162 }
163 }
164 }
165
166 self.modify_shape(ctx, |sh| {
167 sh.proto = proto;
168 })?;
169
170 Ok(true)
171 }
172}
173
174impl JSValue {
175 pub fn prototype(&self, ctx: &ContextImpl) -> JSResult {
179 if let Self::Object(o) = self {
180 Ok(o.prototype(ctx)?.map_or(JSValue::NULL, JSValue::Object))
181 } else {
182 JS_GetPrototypePrimitive(ctx, self).err()
183 }
184 }
185
186 pub(crate) fn set_prototype_internal(
190 &mut self,
191 ctx: &ContextImpl,
192 proto_val: JSValue,
193 throw_flag: bool,
194 ) -> Result<bool, i32> {
195 if unlikely(throw_flag && self.is_undefined_or_null()) {
196 ctx.throw_type_error_not_an_object();
197 Err(-1)
198 } else if let JSValue::Object(o) = self {
199 o.set_prototype_internal(ctx, proto_val, throw_flag)
200 } else if throw_flag {
201 Ok(true)
202 } else {
203 ctx.throw_type_error_not_an_object();
204 Err(-1)
205 }
206 }
207}