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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
// 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/.
use oxc_span::Span;
use crate::{
ecmascript::{
Agent, BUILTIN_STRING_MEMORY, BuiltinConstructorRecord, Environment, ExceptionType,
ExecutionContext, Function, FunctionInternalProperties, JsResult, Object, OrdinaryObject,
PrivateEnvironment, PropertyKey, SourceCode, String, Value, base_class_default_constructor,
derived_class_default_constructor, function_handle,
},
engine::{Bindable, Executable, GcScope, NoGcScope, bindable_handle},
heap::{
ArenaAccess, ArenaAccessMut, BaseIndex, CompactionLists, CreateHeapData, Heap,
HeapIndexHandle, HeapMarkAndSweep, HeapSweepWeakReference, ObjectEntry,
ObjectEntryPropertyDescriptor, WorkQueues, arena_vec_access,
},
ndt,
};
use super::ArgumentsList;
/// ### [4.4.36 built-in constructor](https://tc39.es/ecma262/#sec-built-in-constructor)
///
/// A class built-in default constructor created in step 14 of
/// [ClassDefinitionEvaluation].
///
/// #### Examples
///
/// ```javascript
/// class Foo {}.constructor;
/// class Bar extends Foo {}.constructor;
/// ```
///
/// [ClassDefinitionEvaluation]: https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct BuiltinConstructorFunction<'a>(BaseIndex<'a, BuiltinConstructorRecord<'static>>);
function_handle!(BuiltinConstructorFunction);
arena_vec_access!(
BuiltinConstructorFunction,
'a,
BuiltinConstructorRecord,
builtin_constructors
);
impl<'a> FunctionInternalProperties<'a> for BuiltinConstructorFunction<'a> {
fn get_name(self, agent: &Agent) -> &String<'a> {
&self.get(agent).class_name
}
fn get_length(self, _: &Agent) -> u8 {
unreachable!();
}
#[inline(always)]
fn get_function_backing_object(self, agent: &Agent) -> Option<OrdinaryObject<'static>> {
self.get(agent).backing_object.unbind()
}
fn set_function_backing_object(
self,
agent: &mut Agent,
backing_object: OrdinaryObject<'static>,
) {
assert!(
self.get_mut(agent)
.backing_object
.replace(backing_object)
.is_none()
);
}
/// ### [10.3.1 \[\[Call\]\] ( thisArgument, argumentsList )](https://tc39.es/ecma262/#sec-built-in-function-objects-call-thisargument-argumentslist)
///
/// The [[Call]] internal method of a built-in function object F takes
/// arguments thisArgument (an ECMAScript language value) and argumentsList
/// (a List of ECMAScript language values) and returns either a normal
/// completion containing an ECMAScript language value or a throw
/// completion.
fn function_call<'gc>(
self,
agent: &mut Agent,
_: Value,
_: ArgumentsList,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Value<'gc>> {
// 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined).
// ii. If NewTarget is undefined, throw a TypeError exception.
Err(agent.throw_exception_with_static_message(
ExceptionType::TypeError,
"class constructors must be invoked with 'new'",
gc.into_nogc(),
))
}
/// ### [10.3.2 \[\[Construct\]\] ( argumentsList, newTarget )](https://tc39.es/ecma262/#sec-built-in-function-objects-construct-argumentslist-newtarget)
///
/// The [[Construct]] internal method of a built-in function object F (when
/// the method is present) takes arguments argumentsList (a List of
/// ECMAScript language values) and newTarget (a constructor) and returns
/// either a normal completion containing an Object or a throw completion.
fn function_construct<'gc>(
self,
agent: &mut Agent,
arguments_list: ArgumentsList,
new_target: Function,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Object<'gc>> {
let mut id = 0;
ndt::builtin_constructor_start!(|| {
id = create_id(agent, self);
let name = self.get_name(agent).to_string_lossy_(agent);
(name, id)
});
// 1. Return ? BuiltinCallOrConstruct(F, uninitialized, argumentsList, newTarget).
let result = builtin_call_or_construct(agent, self, arguments_list, new_target, gc);
ndt::builtin_constructor_done!(|| id);
result
}
}
#[inline(never)]
fn create_id(agent: &Agent, f: BuiltinConstructorFunction) -> u64 {
((f.0.get_index_u32() as u64) << 32) | f.get(agent).source_text.start as u64
}
/// ### [10.3.3 BuiltinCallOrConstruct ( F, thisArgument, argumentsList, newTarget )](https://tc39.es/ecma262/#sec-builtincallorconstruct)
///
/// The abstract operation BuiltinCallOrConstruct takes arguments F (a built-in
/// function object), thisArgument (an ECMAScript language value or
/// uninitialized), argumentsList (a List of ECMAScript language values), and
/// newTarget (a constructor or undefined) and returns either a normal
/// completion containing an ECMAScript language value or a throw completion.
fn builtin_call_or_construct<'a>(
agent: &mut Agent,
f: BuiltinConstructorFunction,
arguments_list: ArgumentsList,
new_target: Function,
gc: GcScope<'a, '_>,
) -> JsResult<'a, Object<'a>> {
let f = f.bind(gc.nogc());
let arguments_list = arguments_list.bind(gc.nogc());
let new_target = new_target.bind(gc.nogc());
// 1. Let callerContext be the running execution context.
let caller_context = agent.running_execution_context();
// 2. If callerContext is not already suspended, suspend callerContext.
caller_context.suspend();
// 5. Let calleeRealm be F.[[Realm]].
let heap_data = &f.get(agent);
let callee_realm = heap_data.realm;
let is_derived = heap_data.is_derived;
// 3. Let calleeContext be a new execution context.
let callee_context = ExecutionContext {
// 8. Perform any necessary implementation-defined initialization of calleeContext.
ecmascript_code: None,
// 4. Set the Function of calleeContext to F.
function: Some(f.unbind().into()),
// 6. Set the Realm of calleeContext to calleeRealm.
realm: callee_realm.unbind(),
// 7. Set the ScriptOrModule of calleeContext to null.
script_or_module: None,
};
// 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
agent.push_execution_context(callee_context);
// 10. Let result be the Completion Record that is the result of evaluating F in a manner that conforms to
// the specification of F. If thisArgument is uninitialized, the this value is uninitialized; otherwise,
// thisArgument provides the this value. argumentsList provides the named parameters. newTarget provides the NewTarget value.
let result = if is_derived {
derived_class_default_constructor(
agent,
arguments_list.unbind(),
new_target.unbind().into(),
gc,
)
} else {
base_class_default_constructor(agent, new_target.unbind().into(), gc)
};
// 11. NOTE: If F is defined in this document, “the specification of F” is the behaviour specified for it via
// algorithm steps or other means.
// 12. Remove calleeContext from the execution context stack and restore callerContext as the running
// execution context.
// Note
// When calleeContext is removed from the execution context stack it must not be destroyed if it has been
// suspended and retained by an accessible Generator for later resumption.
let _callee_context = agent.pop_execution_context();
// 13. Return ? result.
result
}
pub(crate) struct BuiltinConstructorArgs<'a> {
pub(crate) is_derived: bool,
pub(crate) class_name: String<'a>,
pub(crate) prototype: Option<Object<'a>>,
pub(crate) prototype_property: Object<'a>,
pub(crate) compiled_initializer_bytecode: Option<Executable<'a>>,
pub(crate) env: Environment<'a>,
pub(crate) private_env: Option<PrivateEnvironment<'a>>,
pub(crate) source_code: SourceCode<'a>,
pub(crate) source_text: Span,
}
/// ### [10.3.4 CreateBuiltinFunction ( behaviour, length, name, additionalInternalSlotsList \[ , realm \[ , prototype \[ , prefix \] \] \] )](https://tc39.es/ecma262/#sec-createbuiltinfunction)
///
/// The abstract operation CreateBuiltinFunction takes arguments behaviour (an
/// Abstract Closure, a set of algorithm steps, or some other definition of a
/// function's behaviour provided in this specification), length (a
/// non-negative integer or +∞), name (a property key or a Private Name), and
/// additionalInternalSlotsList (a List of names of internal slots) and
/// optional arguments realm (a Realm Record), prototype (an Object or null),
/// and prefix (a String) and returns a function object.
/// additionalInternalSlotsList contains the names of additional internal slots
/// that must be defined as part of the object. This operation creates a
/// built-in function object.
pub(crate) fn create_builtin_constructor<'a>(
agent: &mut Agent,
args: BuiltinConstructorArgs,
gc: NoGcScope<'a, '_>,
) -> BuiltinConstructorFunction<'a> {
// 1. If realm is not present, set realm to the current Realm Record.
let realm = agent.current_realm(gc);
// 9. Set func.[[InitialName]] to null.
// 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]].
// 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3
// requires for the built-in function object that is about to be created.
// 4. Append to internalSlotsList the elements of additionalInternalSlotsList.
// * [[ConstructorKind]] and [[SourceText]] for class constructors.
// 5. Let func be a new built-in function object that, when called, performs the action
// described by behaviour using the provided arguments as the values of the corresponding
// parameters specified by behaviour. The new function object has internal slots whose names
// are the elements of internalSlotsList, and an [[InitialName]] internal slot.
// 7. Set func.[[Extensible]] to true.
let length_entry = ObjectEntry {
key: PropertyKey::from(BUILTIN_STRING_MEMORY.length),
value: ObjectEntryPropertyDescriptor::Data {
value: 0.into(),
writable: false,
enumerable: false,
configurable: true,
},
};
let name_entry = ObjectEntry {
key: PropertyKey::from(BUILTIN_STRING_MEMORY.name),
value: ObjectEntryPropertyDescriptor::Data {
value: args.class_name.into(),
writable: false,
enumerable: false,
configurable: true,
},
};
let prototype_entry = ObjectEntry {
key: PropertyKey::from(BUILTIN_STRING_MEMORY.prototype),
value: ObjectEntryPropertyDescriptor::Data {
value: args.prototype_property.into(),
writable: false,
enumerable: false,
configurable: false,
},
};
let entries = [length_entry, name_entry, prototype_entry];
let backing_object = OrdinaryObject::create_intrinsic_object(agent, args.prototype, &entries)
.expect("Should perform GC here");
// 13. Return func.
agent
.heap
.create(BuiltinConstructorRecord {
// 10. Perform SetFunctionLength(func, length).
// Skipped as length of builtin constructors is always 0.
// 8. Set func.[[Realm]] to realm.
realm,
compiled_initializer_bytecode: args.compiled_initializer_bytecode,
is_derived: args.is_derived,
backing_object: Some(backing_object),
environment: args.env,
private_environment: args.private_env,
source_text: args.source_text,
source_code: args.source_code,
class_name: args.class_name,
})
.bind(gc)
}
impl<'a> CreateHeapData<BuiltinConstructorRecord<'a>, BuiltinConstructorFunction<'a>> for Heap {
fn create(&mut self, data: BuiltinConstructorRecord) -> BuiltinConstructorFunction<'a> {
self.builtin_constructors.push(data.unbind());
self.alloc_counter += core::mem::size_of::<BuiltinConstructorRecord<'static>>();
BuiltinConstructorFunction(BaseIndex::last(&self.builtin_constructors))
}
}
impl HeapMarkAndSweep for BuiltinConstructorFunction<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
queues.builtin_constructors.push(*self);
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
compactions.builtin_constructors.shift_index(&mut self.0);
}
}
impl HeapSweepWeakReference for BuiltinConstructorFunction<'static> {
fn sweep_weak_reference(self, compactions: &CompactionLists) -> Option<Self> {
compactions
.builtin_constructors
.shift_weak_index(self.0)
.map(Self)
}
}
bindable_handle!(BuiltinConstructorRecord);
impl HeapMarkAndSweep for BuiltinConstructorRecord<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
let Self {
backing_object: object_index,
realm,
is_derived: _,
compiled_initializer_bytecode,
environment,
private_environment,
source_text: _,
source_code,
class_name,
} = self;
realm.mark_values(queues);
object_index.mark_values(queues);
environment.mark_values(queues);
private_environment.mark_values(queues);
source_code.mark_values(queues);
compiled_initializer_bytecode.mark_values(queues);
class_name.mark_values(queues);
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let Self {
backing_object: object_index,
realm,
is_derived: _,
compiled_initializer_bytecode,
environment,
private_environment,
source_text: _,
source_code,
class_name,
} = self;
realm.sweep_values(compactions);
object_index.sweep_values(compactions);
environment.sweep_values(compactions);
private_environment.sweep_values(compactions);
source_code.sweep_values(compactions);
compiled_initializer_bytecode.sweep_values(compactions);
class_name.sweep_values(compactions);
}
}