1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! ### [13.2.8 Template Literals](https://tc39.es/ecma262/#sec-template-literals)
use std::ptr::NonNull;
use ahash::AHashMap;
use oxc_ast::ast;
use crate::{
ecmascript::{
Agent, Array, BUILTIN_STRING_MEMORY, InternalMethods, InternalSlots, OrdinaryObject,
String, Value, array_create, unwrap_try,
},
engine::{Bindable, NoGcScope},
heap::{ElementDescriptor, ObjectEntry, ObjectEntryPropertyDescriptor},
};
/// ### [13.2.8.4 GetTemplateObject ( templateLiteral )](https://tc39.es/ecma262/#sec-gettemplateobject)
///
/// The abstract operation GetTemplateObject takes argument templateLiteral (a
/// Parse Node) and returns an Array.
pub(super) fn get_template_object<'a>(
agent: &mut Agent,
template_literal: &ast::TemplateLiteral,
gc: NoGcScope<'a, '_>,
) -> Array<'a> {
// 1. Let realm be the current Realm Record.
// 2. Let templateRegistry be realm.[[TemplateMap]].
// 3. For each element e of templateRegistry, do
// a. If e.[[Site]] is the same Parse Node as templateLiteral, then
// i. Return e.[[Array]].
// 4. Let rawStrings be the TemplateStrings of templateLiteral with argument true.
// 5. Assert: rawStrings is a List of Strings.
// 6. Let cookedStrings be the TemplateStrings of templateLiteral with argument false.
// 7. Let count be the number of elements in the List cookedStrings.
// 8. Assert: count ≤ 2**32 - 1.
let len = template_literal.quasis.len();
debug_assert!(len < 2usize.pow(32));
// 9. Let template be ! ArrayCreate(count).
let template = array_create(agent, len, len, None, gc).unwrap();
// 10. Let rawObj be ! ArrayCreate(count).
let raw_obj = array_create(agent, len, len, None, gc).unwrap();
// 11. Let index be 0.
// 12. Repeat, while index < count,
// First, ensure that template Array descriptors exist.
let template_storage = template.get_storage_mut(agent);
template_storage
.descriptors
.insert_entry(AHashMap::with_capacity(len));
// Second, ensure that raw_obj Array descriptors exist and grab the
// pointers to the values and descriptors.
let raw_obj_storage = raw_obj.get_storage_mut(agent);
let mut raw_obj_values = NonNull::from(raw_obj_storage.values);
let mut raw_obj_descriptors = NonNull::from(
raw_obj_storage
.descriptors
.insert_entry(AHashMap::with_capacity(len))
.into_mut(),
);
// Third, get the template values and descriptors; since they already
// exist, this cannot move the raw_obj descriptors.
let template_storage = template
.get_elements(&agent.heap.arrays)
.get_storage_mut(&mut agent.heap.elements);
let template_values = template_storage.values;
let template_descriptors = template_storage
.descriptors
.insert_entry(AHashMap::with_capacity(len))
.into_mut();
// SAFETY: Finally, get the raw_obj values and descriptors; they cannot
// have moved as per the above comment, and they're different from the
// template values and descriptors so this is not mutable aliasing.
let raw_obj_values = unsafe { raw_obj_values.as_mut() };
let raw_obj_descriptors = unsafe { raw_obj_descriptors.as_mut() };
let strings = &mut agent.heap.strings;
let string_lookup_table = &mut agent.heap.string_lookup_table;
let string_hasher = &mut agent.heap.string_hasher;
let alloc_counter = &mut agent.heap.alloc_counter;
for (prop, quasi) in template_literal.quasis.iter().enumerate() {
// a. Let prop be ! ToString(𝔽(index)).
// b. Let cookedValue be cookedStrings[index].
let cooked_value = quasi.value.cooked.map_or(Value::Undefined, |cooked_value| {
String::from_str_direct(
strings,
string_lookup_table,
string_hasher,
alloc_counter,
cooked_value.as_str(),
gc,
)
.into()
});
// d. Let rawValue be the String value rawStrings[index].
let raw_value = String::from_str_direct(
strings,
string_lookup_table,
string_hasher,
alloc_counter,
quasi.value.raw.as_str(),
gc,
);
// c. Perform ! DefinePropertyOrThrow(template, prop,
// PropertyDescriptor {
// [[Value]]: cookedValue,
template_values[prop] = Some(cooked_value.unbind());
// [[Writable]]: false,
// [[Enumerable]]: true,
// [[Configurable]]: false
template_descriptors.insert(
prop as u32,
ElementDescriptor::ReadOnlyEnumerableUnconfigurableData,
);
// }).
// e. Perform ! DefinePropertyOrThrow(rawObj, prop,
// PropertyDescriptor {
// [[Value]]: rawValue,
raw_obj_values[prop] = Some(raw_value.unbind().into());
// [[Writable]]: false,
// [[Enumerable]]: true,
// [[Configurable]]: false
raw_obj_descriptors.insert(
prop as u32,
ElementDescriptor::ReadOnlyEnumerableUnconfigurableData,
);
// }).
// f. Set index to index + 1.
}
// 13. Perform ! SetIntegrityLevel(rawObj, frozen).
unwrap_try(raw_obj.try_prevent_extensions(agent, gc));
let prototype = template.internal_prototype(agent).unwrap();
// 14. Perform ! DefinePropertyOrThrow(template,
let template_backing_object = OrdinaryObject::create_object(
agent,
Some(prototype),
&[ObjectEntry {
// "raw",
key: BUILTIN_STRING_MEMORY.raw.to_property_key(),
// PropertyDescriptor {
value: ObjectEntryPropertyDescriptor::Data {
// [[Value]]: rawObj,
value: raw_obj.into(),
// [[Writable]]: false,
writable: false,
// [[Enumerable]]: false,
enumerable: false,
// [[Configurable]]: false
configurable: false,
},
// }).
}],
)
.expect("Should perform GC here");
template.set_backing_object(agent, template_backing_object.unbind());
// 15. Perform ! SetIntegrityLevel(template, frozen).
unwrap_try(template.try_prevent_extensions(agent, gc));
// 16. Append the Record { [[Site]]: templateLiteral, [[Array]]: template }
// to realm.[[TemplateMap]].
// 17. Return template.
template
}