1010namespace core {
1111
1212/* *
13- * Extract <key,value> pairs from the given value if it is either a
14- * sequence<sequence<Value> or a record<Value, Value >.
13+ * Extract <key,value> pairs from the given value if it is a
14+ * sequence<sequence<Value>>.
1515 */
1616template <typename T, auto validate, auto apply>
17- bool maybe_consume_sequence_or_record (JSContext *cx, JS::HandleValue initv, JS::HandleObject target,
18- bool *consumed, const char *ctor_name,
19- const char *alt_text = " " ) {
20- if (initv.isUndefined ()) {
21- *consumed = true ;
17+ bool maybe_consume_sequence (JSContext *cx, JS::HandleValue initv, JS::HandleObject target,
18+ bool *consumed, const char *ctor_name,
19+ const char *alt_text = " " ) {
20+ *consumed = false ;
21+
22+ if (!initv.isObject ()) {
2223 return true ;
2324 }
2425
2526 JS::RootedValue key (cx);
2627 JS::RootedValue value (cx);
2728
28- // First, try consuming args[0] as a sequence<sequence<Value>>.
29+ // Try consuming args[0] as a sequence<sequence<Value>>.
2930 JS::ForOfIterator it (cx);
3031 if (!it.init (initv, JS::ForOfIterator::AllowNonIterable)) {
3132 return false ;
@@ -34,109 +35,140 @@ bool maybe_consume_sequence_or_record(JSContext *cx, JS::HandleValue initv, JS::
3435 // Note: this currently doesn't treat strings as iterable even though they
3536 // are. We don't have any constructors that want to iterate over strings, and
3637 // this makes things a lot easier.
37- if (initv.isObject () && it.valueIsIterable ()) {
38- JS::RootedValue entry (cx);
39-
40- while (true ) {
41- bool done = false ;
42- if (!it.next (&entry, &done)) {
43- return false ;
44- }
38+ if (!it.valueIsIterable ()) {
39+ return true ;
40+ }
4541
46- if (done) {
47- break ;
48- }
42+ JS::RootedValue entry (cx);
4943
50- if (!entry.isObject ()) {
51- return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text);
52- }
53-
54- JS::ForOfIterator entr_iter (cx);
55- if (!entr_iter.init (entry, JS::ForOfIterator::AllowNonIterable)) {
56- return false ;
57- }
44+ while (true ) {
45+ bool done = false ;
46+ if (!it.next (&entry, &done)) {
47+ return false ;
48+ }
5849
59- if (!entr_iter. valueIsIterable () ) {
60- return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text) ;
61- }
50+ if (done ) {
51+ break ;
52+ }
6253
63- {
64- bool done = false ;
65-
66- // Extract key.
67- if (!entr_iter.next (&key, &done)) {
68- return false ;
69- }
70- if (done) {
71- return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text);
72- }
73-
74- T validated_key = validate (cx, key, ctor_name);
75- if (!validated_key) {
76- return false ;
77- }
78-
79- // Extract value.
80- if (!entr_iter.next (&value, &done)) {
81- return false ;
82- }
83- if (done) {
84- return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text);
85- }
86-
87- // Ensure that there aren't any further entries.
88- if (!entr_iter.next (&entry, &done)) {
89- return false ;
90- }
91- if (!done) {
92- return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text);
93- }
94-
95- if (!apply (cx, target, std::move (validated_key), value, ctor_name)) {
96- return false ;
97- }
98- }
54+ if (!entry.isObject ()) {
55+ return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text);
9956 }
100- *consumed = true ;
101- } else if (initv.isObject ()) {
102- // init isn't an iterator, so if it's an object, it must be a record to be
103- // valid input, following https://webidl.spec.whatwg.org/#js-record exactly.
104- JS::RootedObject init (cx, &initv.toObject ());
105- JS::RootedIdVector ids (cx);
106- if (!js::GetPropertyKeys (cx, init, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
57+
58+ JS::ForOfIterator entr_iter (cx);
59+ if (!entr_iter.init (entry, JS::ForOfIterator::AllowNonIterable)) {
10760 return false ;
10861 }
10962
110- JS::RootedId curId (cx);
63+ if (!entr_iter.valueIsIterable ()) {
64+ return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text);
65+ }
11166
112- JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc (cx);
67+ {
68+ bool done = false ;
11369
114- for (size_t i = 0 ; i < ids.length (); ++i) {
115- curId = ids[i];
116- key = js::IdToValue (curId);
117- if (!JS_GetOwnPropertyDescriptorById (cx, init, curId, &desc)) {
70+ // Extract key.
71+ if (!entr_iter.next (&key, &done)) {
11872 return false ;
11973 }
120- if (desc. isNothing () || !desc-> enumerable () ) {
121- continue ;
74+ if (done ) {
75+ return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text) ;
12276 }
123- // Get call is observable and must come after any value validation
77+
12478 T validated_key = validate (cx, key, ctor_name);
12579 if (!validated_key) {
12680 return false ;
12781 }
128- if (!JS_GetPropertyById (cx, init, curId, &value)) {
82+
83+ // Extract value.
84+ if (!entr_iter.next (&value, &done)) {
12985 return false ;
13086 }
87+ if (done) {
88+ return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text);
89+ }
90+
91+ // Ensure that there aren't any further entries.
92+ if (!entr_iter.next (&entry, &done)) {
93+ return false ;
94+ }
95+ if (!done) {
96+ return api::throw_error (cx, api::Errors::InvalidSequence, ctor_name, alt_text);
97+ }
98+
13199 if (!apply (cx, target, std::move (validated_key), value, ctor_name)) {
132100 return false ;
133101 }
134102 }
135- *consumed = true ;
136- } else {
137- *consumed = false ;
138103 }
139104
105+ *consumed = true ;
106+ return true ;
107+ }
108+
109+ /* *
110+ * Extract <key,value> pairs from the given value if it is a
111+ * record<Value, Value>.
112+ */
113+ template <typename T, auto validate, auto apply>
114+ bool maybe_consume_record (JSContext *cx, JS::HandleValue initv, JS::HandleObject target,
115+ bool *consumed, const char *ctor_name) {
116+ *consumed = false ;
117+
118+ if (!initv.isObject ()) {
119+ return true ;
120+ }
121+
122+ JS::RootedValue key (cx);
123+ JS::RootedValue value (cx);
124+
125+ // If init is iterable, it is not a record.
126+ JS::ForOfIterator it (cx);
127+ if (!it.init (initv, JS::ForOfIterator::AllowNonIterable)) {
128+ return false ;
129+ }
130+ if (it.valueIsIterable ()) {
131+ return true ;
132+ }
133+
134+ // init isn't an iterator, so if it's an object, it must be a record to be
135+ // valid input, following https://webidl.spec.whatwg.org/#js-record exactly.
136+ JS::RootedObject init (cx, &initv.toObject ());
137+ JS::RootedIdVector ids (cx);
138+ if (!js::GetPropertyKeys (cx, init, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
139+ return false ;
140+ }
141+
142+ JS::RootedId curId (cx);
143+ JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc (cx);
144+
145+ for (size_t i = 0 ; i < ids.length (); ++i) {
146+ curId = ids[i];
147+ key = js::IdToValue (curId);
148+
149+ if (!JS_GetOwnPropertyDescriptorById (cx, init, curId, &desc)) {
150+ return false ;
151+ }
152+ if (desc.isNothing () || !desc->enumerable ()) {
153+ continue ;
154+ }
155+
156+ // Get call is observable and must come after any value validation
157+ T validated_key = validate (cx, key, ctor_name);
158+ if (!validated_key) {
159+ return false ;
160+ }
161+
162+ if (!JS_GetPropertyById (cx, init, curId, &value)) {
163+ return false ;
164+ }
165+
166+ if (!apply (cx, target, std::move (validated_key), value, ctor_name)) {
167+ return false ;
168+ }
169+ }
170+
171+ *consumed = true ;
140172 return true ;
141173}
142174
0 commit comments