@@ -13,8 +13,14 @@ use module_semantics::{
1313
1414use crate :: {
1515 ecmascript:: {
16- abstract_operations:: type_conversion:: { to_string, to_string_primitive} ,
16+ abstract_operations:: {
17+ operations_on_objects:: {
18+ enumerable_own_properties, enumerable_properties_kind:: EnumerateKeysAndValues , get,
19+ } ,
20+ type_conversion:: to_string,
21+ } ,
1722 builtins:: {
23+ Array ,
1824 promise:: Promise ,
1925 promise_objects:: {
2026 promise_abstract_operations:: {
@@ -26,13 +32,16 @@ use crate::{
2632 } ,
2733 execution:: {
2834 Agent , JsResult ,
29- agent:: { get_active_script_or_module, unwrap_try} ,
35+ agent:: { ExceptionType , get_active_script_or_module, unwrap_try} ,
3036 } ,
31- types:: { IntoValue , Primitive , Value } ,
37+ scripts_and_modules:: module:: module_semantics:: all_import_attributes_supported,
38+ types:: { BUILTIN_STRING_MEMORY , IntoValue , Object , String , Value } ,
3239 } ,
3340 engine:: {
41+ Scoped ,
3442 context:: { Bindable , GcScope , NoGcScope } ,
3543 rootable:: Scopable ,
44+ typeof_operator,
3645 } ,
3746} ;
3847pub mod module_semantics;
@@ -54,63 +63,158 @@ pub(crate) fn evaluate_import_call<'gc>(
5463) -> Promise < ' gc > {
5564 let specifier = specifier. bind ( gc. nogc ( ) ) ;
5665 let mut options = options. bind ( gc. nogc ( ) ) ;
66+ if options. is_some_and ( |opt| opt. is_undefined ( ) ) {
67+ options. take ( ) ;
68+ }
5769 // 7. Let promiseCapability be ! NewPromiseCapability(%Promise%).
5870 let promise_capability = PromiseCapability :: new ( agent, gc. nogc ( ) ) ;
5971 let scoped_promise = promise_capability. promise . scope ( agent, gc. nogc ( ) ) ;
6072 // 8. Let specifierString be Completion(ToString(specifier)).
61- let specifier = if let Ok ( specifier) = Primitive :: try_from ( specifier) {
62- to_string_primitive ( agent, specifier, gc. nogc ( ) )
63- . unbind ( )
64- . bind ( gc. nogc ( ) )
73+ let specifier = if let Ok ( specifier) = String :: try_from ( specifier) {
74+ specifier
6575 } else {
6676 let scoped_options = options. map ( |o| o. scope ( agent, gc. nogc ( ) ) ) ;
6777 let specifier = to_string ( agent, specifier. unbind ( ) , gc. reborrow ( ) )
6878 . unbind ( )
6979 . bind ( gc. nogc ( ) ) ;
7080 // SAFETY: not shared.
7181 options = scoped_options. map ( |o| unsafe { o. take ( agent) } . bind ( gc. nogc ( ) ) ) ;
72- specifier
82+ // 9. IfAbruptRejectPromise(specifierString, promiseCapability).
83+ let promise_capability = PromiseCapability {
84+ promise : scoped_promise. get ( agent) . bind ( gc. nogc ( ) ) ,
85+ must_be_unresolved : true ,
86+ } ;
87+ if_abrupt_reject_promise_m ! ( agent, specifier, promise_capability, gc)
7388 } ;
74- // 9. IfAbruptRejectPromise(specifierString, promiseCapability).
75- let promise_capability = PromiseCapability {
76- promise : scoped_promise. get ( agent) . bind ( gc. nogc ( ) ) ,
77- must_be_unresolved : true ,
78- } ;
79- let specifier = if_abrupt_reject_promise_m ! ( agent, specifier, promise_capability, gc) ;
8089 // 10. Let attributes be a new empty List.
81- let attributes: Vec < ImportAttributeRecord > = vec ! [ ] ;
8290 // 11. If options is not undefined, then
83- if let Some ( _options ) = options {
91+ let ( promise , specifier , attributes , gc ) = if let Some ( options ) = options {
8492 // a. If options is not an Object, then
85- // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
86- // ii. Return promiseCapability.[[Promise]].
93+ let Ok ( options) = Object :: try_from ( options) else {
94+ // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
95+ // ii. Return promiseCapability.[[Promise]].
96+ return reject_import_not_object_or_undefined (
97+ agent,
98+ scoped_promise,
99+ options. unbind ( ) ,
100+ gc. into_nogc ( ) ,
101+ ) ;
102+ } ;
103+ let specifier = specifier. scope ( agent, gc. nogc ( ) ) ;
87104 // b. Let attributesObj be Completion(Get(options, "with")).
105+ let attributes_obj = get (
106+ agent,
107+ options. unbind ( ) ,
108+ BUILTIN_STRING_MEMORY . with . to_property_key ( ) ,
109+ gc. reborrow ( ) ,
110+ )
111+ . unbind ( )
112+ . bind ( gc. nogc ( ) ) ;
113+
114+ let promise_capability = PromiseCapability {
115+ promise : scoped_promise. get ( agent) . bind ( gc. nogc ( ) ) ,
116+ must_be_unresolved : true ,
117+ } ;
88118 // c. IfAbruptRejectPromise(attributesObj, promiseCapability).
119+ let attributes_obj =
120+ if_abrupt_reject_promise_m ! ( agent, attributes_obj, promise_capability, gc) ;
89121 // d. If attributesObj is not undefined, then
90- // i. If attributesObj is not an Object, then
91- // 1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
92- // 2. Return promiseCapability.[[Promise]].
93- // ii. Let entries be Completion(EnumerableOwnProperties(attributesObj, key+value)).
94- // iii. IfAbruptRejectPromise(entries, promiseCapability).
95- // iv. For each element entry of entries, do
96- // 1. Let key be ! Get(entry, "0").
97- // 2. Let value be ! Get(entry, "1").
98- // 3. If key is a String, then
99- // a. If value is not a String, then
100- // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
101- // ii. Return promiseCapability.[[Promise]].
102- // b. Append the ImportAttribute Record { [[Key]]: key, [[Value]]: value } to attributes.
103- // e. If AllImportAttributesSupported(attributes) is false, then
104- // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
105- // ii. Return promiseCapability.[[Promise]].
106- // f. Sort attributes according to the lexicographic order of their [[Key]] field, treating the value of each such field as a sequence of UTF-16 code unit values. NOTE: This sorting is observable only in that hosts are prohibited from changing behaviour based on the order in which attributes are enumerated.
107- todo ! ( )
108- }
109- let specifier = specifier. unbind ( ) ;
110- let attributes = attributes. unbind ( ) ;
111- let gc = gc. into_nogc ( ) ;
112- let specifier = specifier. bind ( gc) ;
113- let attributes = attributes. bind ( gc) ;
122+ if !attributes_obj. is_undefined ( ) {
123+ // i. If attributesObj is not an Object, then
124+ let Ok ( attributes_obj) = Object :: try_from ( attributes_obj) else {
125+ // 1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
126+ // 2. Return promiseCapability.[[Promise]].
127+ return reject_import_not_object_or_undefined (
128+ agent,
129+ scoped_promise,
130+ attributes_obj. unbind ( ) ,
131+ gc. into_nogc ( ) ,
132+ ) ;
133+ } ;
134+ // ii. Let entries be Completion(EnumerableOwnProperties(attributesObj, key+value)).
135+ let entries = enumerable_own_properties :: < EnumerateKeysAndValues > (
136+ agent,
137+ attributes_obj. unbind ( ) ,
138+ gc. reborrow ( ) ,
139+ )
140+ . unbind ( ) ;
141+ let gc = gc. into_nogc ( ) ;
142+ let entries = entries. bind ( gc) ;
143+
144+ let promise = unsafe { scoped_promise. take ( agent) } . bind ( gc) ;
145+ let promise_capability = PromiseCapability {
146+ promise,
147+ must_be_unresolved : true ,
148+ } ;
149+ // iii. IfAbruptRejectPromise(entries, promiseCapability).
150+ // 1. Assert: value is a Completion Record.
151+ let entries = match entries {
152+ // 2. If value is an abrupt completion, then
153+ Err ( err) => {
154+ // a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
155+ promise_capability. reject ( agent, err. value ( ) . unbind ( ) , gc) ;
156+ // b. Return capability.[[Promise]].
157+ return promise_capability. promise ;
158+ }
159+ // 3. Else,
160+ Ok ( value) => {
161+ // a. Set value to ! value.
162+ value
163+ }
164+ } ;
165+ let mut attributes: Vec < ImportAttributeRecord > = Vec :: with_capacity ( entries. len ( ) ) ;
166+ // iv. For each element entry of entries, do
167+ for entry in entries {
168+ let entry = Array :: try_from ( entry) . unwrap ( ) ;
169+ let entry = entry. get_storage ( agent) . values ;
170+ // 1. Let key be ! Get(entry, "0").
171+ let key = entry[ 0 ] . unwrap ( ) ;
172+ // 2. Let value be ! Get(entry, "1").
173+ let value = entry[ 0 ] . unwrap ( ) ;
174+ // 3. If key is a String, then
175+ if let Ok ( key) = String :: try_from ( key) {
176+ // a. If value is not a String, then
177+ let Ok ( value) = String :: try_from ( value) else {
178+ // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
179+ // ii. Return promiseCapability.[[Promise]].
180+ return reject_unsupported_import_attribute (
181+ agent,
182+ promise_capability,
183+ key. unbind ( ) ,
184+ gc. into_nogc ( ) ,
185+ ) ;
186+ } ;
187+ // b. Append the ImportAttribute Record { [[Key]]: key, [[Value]]: value } to attributes.
188+ attributes. push ( ImportAttributeRecord { key, value } ) ;
189+ }
190+ }
191+ // e. If AllImportAttributesSupported(attributes) is false, then
192+ if !all_import_attributes_supported ( agent, & attributes) {
193+ // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
194+ // ii. Return promiseCapability.[[Promise]].
195+ return reject_unsupported_import_attributes ( agent, promise_capability, gc) ;
196+ }
197+ // f. Sort attributes according to the lexicographic order of their
198+ // [[Key]] field, treating the value of each such field as a sequence
199+ // of UTF-16 code unit values. NOTE: This sorting is observable only
200+ // in that hosts are prohibited from changing behaviour based on the
201+ // order in which attributes are enumerated.
202+ attributes. sort_by ( |a, b| a. key . as_wtf8 ( agent) . cmp ( b. key . as_wtf8 ( agent) ) ) ;
203+ let specifier = unsafe { specifier. take ( agent) } . bind ( gc) ;
204+ ( promise, specifier, attributes. into_boxed_slice ( ) , gc)
205+ } else {
206+ let gc = gc. into_nogc ( ) ;
207+ let specifier = unsafe { specifier. take ( agent) } . bind ( gc) ;
208+ let promise = unsafe { scoped_promise. take ( agent) } . bind ( gc) ;
209+ ( promise, specifier, Default :: default ( ) , gc)
210+ }
211+ } else {
212+ let specifier = specifier. unbind ( ) ;
213+ let gc = gc. into_nogc ( ) ;
214+ let specifier = specifier. bind ( gc) ;
215+ let promise = unsafe { scoped_promise. take ( agent) } . bind ( gc) ;
216+ ( promise, specifier, Default :: default ( ) , gc)
217+ } ;
114218 // 12. Let moduleRequest be a new ModuleRequest Record {
115219 let module_request = ModuleRequest :: new_dynamic (
116220 agent, // [[Specifier]]: specifierString,
@@ -125,8 +229,6 @@ pub(crate) fn evaluate_import_call<'gc>(
125229 . unwrap_or_else ( || agent. current_realm ( gc) . into ( ) ) ;
126230 // 13. Perform HostLoadImportedModule(referrer, moduleRequest, empty, promiseCapability).
127231 // Note: this is against the spec. We'll fix it in post.
128- // SAFETY: scoped_promise is not shared.
129- let promise = unsafe { scoped_promise. take ( agent) } . bind ( gc) ;
130232 let mut payload = GraphLoadingStateRecord :: from_promise ( promise) ;
131233 agent
132234 . host_hooks
@@ -135,6 +237,68 @@ pub(crate) fn evaluate_import_call<'gc>(
135237 promise
136238}
137239
240+ #[ cold]
241+ #[ inline( never) ]
242+ fn reject_import_not_object_or_undefined < ' gc > (
243+ agent : & mut Agent ,
244+ scoped_promise : Scoped < Promise > ,
245+ value : Value ,
246+ gc : NoGcScope < ' gc , ' _ > ,
247+ ) -> Promise < ' gc > {
248+ let value = value. bind ( gc) ;
249+ let promise_capability = PromiseCapability {
250+ // SAFETY: not shared.
251+ promise : unsafe { scoped_promise. take ( agent) } . bind ( gc) ,
252+ must_be_unresolved : true ,
253+ } ;
254+ // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
255+ let message = format ! (
256+ "import: expected object or undefined, got {}" ,
257+ typeof_operator( agent, value, gc) . to_string_lossy( agent)
258+ ) ;
259+ let error = agent. throw_exception ( ExceptionType :: TypeError , message, gc) ;
260+ promise_capability. reject ( agent, error. value ( ) , gc) ;
261+ // ii. Return promiseCapability.[[Promise]].
262+ promise_capability. promise
263+ }
264+
265+ #[ cold]
266+ #[ inline( never) ]
267+ fn reject_unsupported_import_attribute < ' gc > (
268+ agent : & mut Agent ,
269+ promise_capability : PromiseCapability < ' gc > ,
270+ key : String < ' gc > ,
271+ gc : NoGcScope < ' gc , ' _ > ,
272+ ) -> Promise < ' gc > {
273+ // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
274+ let message = format ! (
275+ "Unsupported import attribute: {}" ,
276+ key. to_string_lossy( agent)
277+ ) ;
278+ let error = agent. throw_exception ( ExceptionType :: TypeError , message, gc) ;
279+ promise_capability. reject ( agent, error. value ( ) , gc) ;
280+ // ii. Return promiseCapability.[[Promise]].
281+ promise_capability. promise
282+ }
283+
284+ #[ cold]
285+ #[ inline( never) ]
286+ fn reject_unsupported_import_attributes < ' gc > (
287+ agent : & mut Agent ,
288+ promise_capability : PromiseCapability < ' gc > ,
289+ gc : NoGcScope < ' gc , ' _ > ,
290+ ) -> Promise < ' gc > {
291+ // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
292+ let error = agent. throw_exception_with_static_message (
293+ ExceptionType :: TypeError ,
294+ "Unsupported import attributes" ,
295+ gc,
296+ ) ;
297+ promise_capability. reject ( agent, error. value ( ) , gc) ;
298+ // ii. Return promiseCapability.[[Promise]].
299+ promise_capability. promise
300+ }
301+
138302/// ### [13.3.10.3 ContinueDynamicImport ( promiseCapability, moduleCompletion )](https://tc39.es/ecma262/#sec-ContinueDynamicImport)
139303///
140304/// The abstract operation ContinueDynamicImport takes arguments
0 commit comments