|
26 | 26 | //! |
27 | 27 | //! ECMAScript implementations of arguments exotic objects have historically contained an accessor property named "caller". Prior to ECMAScript 2017, this specification included the definition of a throwing "caller" property on ordinary arguments objects. Since implementations do not contain this extension any longer, ECMAScript 2017 dropped the requirement for a throwing "caller" accessor. |
28 | 28 |
|
| 29 | +use ahash::AHashMap; |
| 30 | + |
29 | 31 | use crate::{ |
30 | 32 | ecmascript::{ |
31 | | - abstract_operations::operations_on_objects::{ |
32 | | - try_create_data_property_or_throw, try_define_property_or_throw, |
33 | | - }, |
34 | | - execution::{ProtoIntrinsics, agent::Agent}, |
| 33 | + execution::agent::Agent, |
35 | 34 | types::{ |
36 | | - BUILTIN_STRING_MEMORY, IntoFunction, IntoValue, Number, Object, PropertyDescriptor, |
37 | | - PropertyKey, |
| 35 | + BUILTIN_STRING_MEMORY, IntoFunction, IntoObject, IntoValue, Number, Object, |
| 36 | + OrdinaryObject, Value, |
38 | 37 | }, |
39 | 38 | }, |
40 | | - engine::{ |
41 | | - context::{Bindable, NoGcScope}, |
42 | | - unwrap_try, |
43 | | - }, |
44 | | - heap::WellKnownSymbolIndexes, |
| 39 | + engine::context::{Bindable, NoGcScope}, |
| 40 | + heap::{WellKnownSymbolIndexes, element_array::ElementDescriptor}, |
45 | 41 | }; |
46 | 42 |
|
47 | | -use super::ScopedArgumentsList; |
48 | | -use super::ordinary::ordinary_object_create_with_intrinsics; |
| 43 | +use super::{ScopedArgumentsList, ordinary::shape::ObjectShape}; |
49 | 44 |
|
50 | 45 | // 10.4.4.1 [[GetOwnProperty]] ( P ) |
51 | 46 |
|
@@ -132,99 +127,92 @@ pub(crate) fn create_unmapped_arguments_object<'a, 'b>( |
132 | 127 | ) -> Object<'a> { |
133 | 128 | // 1. Let len be the number of elements in argumentsList. |
134 | 129 | let len = arguments_list.len(agent); |
135 | | - let len_value = Number::from_i64(agent, len as i64, gc) |
136 | | - .into_value() |
137 | | - .unbind(); |
| 130 | + // SAFETY: GC is not allowed in this scope, and no other scoped values are |
| 131 | + // accessed during this call. The pointer is not held beyond the current call scope. |
| 132 | + let arguments_non_null_slice = unsafe { arguments_list.as_non_null_slice(agent) }; |
| 133 | + debug_assert!(len < u32::MAX as usize); |
| 134 | + let len = len as u32; |
| 135 | + let len_value = Number::from(len).into_value(); |
138 | 136 | // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%, « [[ParameterMap]] »). |
139 | | - let obj = |
140 | | - ordinary_object_create_with_intrinsics(agent, Some(ProtoIntrinsics::Object), None, gc); |
141 | | - let Object::Object(obj) = obj else { |
142 | | - unreachable!() |
143 | | - }; |
| 137 | + let prototype = agent.current_realm_record().intrinsics().object_prototype(); |
| 138 | + let mut shape = ObjectShape::get_shape_for_prototype(agent, Some(prototype.into_object())); |
| 139 | + shape = shape.get_child_shape(agent, BUILTIN_STRING_MEMORY.length.to_property_key()); |
| 140 | + shape = shape.get_child_shape(agent, BUILTIN_STRING_MEMORY.callee.into()); |
| 141 | + shape = shape.get_child_shape(agent, WellKnownSymbolIndexes::Iterator.into()); |
| 142 | + for index in 0..len { |
| 143 | + shape = shape.get_child_shape(agent, index.into()); |
| 144 | + } |
| 145 | + let obj = OrdinaryObject::create_object_with_shape(agent, shape) |
| 146 | + .expect("Failed to create Arguments object storage"); |
| 147 | + let array_prototype_values = agent |
| 148 | + .current_realm_record() |
| 149 | + .intrinsics() |
| 150 | + .array_prototype_values() |
| 151 | + .bind(gc) |
| 152 | + .into_value(); |
| 153 | + let throw_type_error = agent |
| 154 | + .current_realm_record() |
| 155 | + .intrinsics() |
| 156 | + .throw_type_error() |
| 157 | + .into_function() |
| 158 | + .bind(gc); |
| 159 | + let storage = obj.get_elements_storage_uninit(agent); |
| 160 | + let values = storage.values; |
| 161 | + let descriptors = storage.descriptors.or_insert(AHashMap::with_capacity(3)); |
| 162 | + |
144 | 163 | // 3. Set obj.[[ParameterMap]] to undefined. |
145 | 164 | // 4. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { |
146 | | - let key = PropertyKey::from(BUILTIN_STRING_MEMORY.length); |
147 | | - unwrap_try(try_define_property_or_throw( |
148 | | - agent, |
149 | | - obj, |
150 | | - key, |
151 | | - PropertyDescriptor { |
152 | | - // [[Value]]: 𝔽(len), |
153 | | - value: Some(len_value), |
154 | | - // [[Writable]]: true, |
155 | | - writable: Some(true), |
156 | | - // [[Enumerable]]: false, |
157 | | - enumerable: Some(false), |
158 | | - // [[Configurable]]: true }). |
159 | | - configurable: Some(true), |
160 | | - ..Default::default() |
| 165 | + // [[Value]]: 𝔽(len), |
| 166 | + // [[Writable]]: true, |
| 167 | + // [[Enumerable]]: false, |
| 168 | + // [[Configurable]]: true |
| 169 | + // }). |
| 170 | + |
| 171 | + // "length" |
| 172 | + values[0] = Some(len_value.unbind()); |
| 173 | + // "callee" |
| 174 | + values[1] = None; |
| 175 | + // Iterator |
| 176 | + values[2] = Some(array_prototype_values.unbind()); |
| 177 | + // "length" |
| 178 | + descriptors.insert(0, ElementDescriptor::WritableUnenumerableConfigurableData); |
| 179 | + // "callee" |
| 180 | + descriptors.insert( |
| 181 | + 1, |
| 182 | + ElementDescriptor::ReadWriteUnenumerableUnconfigurableAccessor { |
| 183 | + get: throw_type_error.unbind(), |
| 184 | + set: throw_type_error.unbind(), |
161 | 185 | }, |
162 | | - gc, |
163 | | - )) |
164 | | - .unwrap(); |
| 186 | + ); |
| 187 | + // Iterator |
| 188 | + descriptors.insert(2, ElementDescriptor::WritableUnenumerableConfigurableData); |
165 | 189 | // 5. Let index be 0. |
166 | 190 | // 6. Repeat, while index < len, |
167 | 191 | for index in 0..len { |
168 | 192 | // a. Let val be argumentsList[index]. |
169 | 193 | // b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val). |
170 | | - debug_assert!(index < u32::MAX as usize); |
171 | | - let index = index as u32; |
172 | | - let key = PropertyKey::Integer(index.into()); |
173 | | - let val = arguments_list.get(agent, index, gc); |
174 | | - unwrap_try(try_create_data_property_or_throw(agent, obj, key, val, gc)).unwrap(); |
| 194 | + // SAFETY: arguments slice valid in this call stack and we've not |
| 195 | + // performed GC or touched other scoped data. |
| 196 | + let val = unsafe { arguments_non_null_slice.as_ref() } |
| 197 | + .get(index as usize) |
| 198 | + .cloned() |
| 199 | + .unwrap_or(Value::Undefined); |
| 200 | + values[index as usize + 3] = Some(val); |
175 | 201 | // c. Set index to index + 1. |
176 | 202 | } |
| 203 | + agent[obj].set_len(len + 3); |
177 | 204 | // 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor { |
178 | | - let key = PropertyKey::Symbol(WellKnownSymbolIndexes::Iterator.into()); |
179 | | - unwrap_try(try_define_property_or_throw( |
180 | | - agent, |
181 | | - obj, |
182 | | - key, |
183 | | - PropertyDescriptor { |
184 | | - // [[Value]]: %Array.prototype.values%, |
185 | | - value: Some( |
186 | | - agent |
187 | | - .current_realm_record() |
188 | | - .intrinsics() |
189 | | - .array_prototype_values() |
190 | | - .into_value(), |
191 | | - ), |
192 | | - // [[Writable]]: true, |
193 | | - writable: Some(true), |
194 | | - // [[Enumerable]]: false, |
195 | | - enumerable: Some(false), |
196 | | - // [[Configurable]]: true }). |
197 | | - configurable: Some(true), |
198 | | - ..Default::default() |
199 | | - }, |
200 | | - gc, |
201 | | - )) |
202 | | - .unwrap(); |
203 | | - let throw_type_error = agent |
204 | | - .current_realm_record() |
205 | | - .intrinsics() |
206 | | - .throw_type_error() |
207 | | - .bind(gc); |
| 205 | + // [[Value]]: %Array.prototype.values%, |
| 206 | + // [[Writable]]: true, |
| 207 | + // [[Enumerable]]: false, |
| 208 | + // [[Configurable]]: true |
| 209 | + // }). |
208 | 210 | // 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { |
209 | | - let key = PropertyKey::from(BUILTIN_STRING_MEMORY.callee); |
210 | | - unwrap_try(try_define_property_or_throw( |
211 | | - agent, |
212 | | - obj, |
213 | | - key, |
214 | | - PropertyDescriptor { |
215 | | - // [[Get]]: %ThrowTypeError%, |
216 | | - get: Some(Some(throw_type_error.into_function())), |
217 | | - // [[Set]]: %ThrowTypeError%, |
218 | | - set: Some(Some(throw_type_error.into_function())), |
219 | | - // [[Enumerable]]: false, |
220 | | - enumerable: Some(false), |
221 | | - // [[Configurable]]: false }). |
222 | | - configurable: Some(false), |
223 | | - ..Default::default() |
224 | | - }, |
225 | | - gc, |
226 | | - )) |
227 | | - .unwrap(); |
| 211 | + // [[Get]]: %ThrowTypeError%, |
| 212 | + // [[Set]]: %ThrowTypeError%, |
| 213 | + // [[Enumerable]]: false, |
| 214 | + // [[Configurable]]: false |
| 215 | + // }). |
228 | 216 | // 9. Return obj. |
229 | 217 | Object::Arguments(obj) |
230 | 218 | } |
|
0 commit comments