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
use std::unreachable;

use crate::{
    module::ModuleKind,
    vm::{opcode::Operation, ActiveRunnable, CompletionType},
    Context, JsObject, JsResult, JsValue,
};

/// `NewTarget` implements the Opcode Operation for `Opcode::NewTarget`
///
/// Operation:
///  - Push the current new target to the stack.
#[derive(Debug, Clone, Copy)]
pub(crate) struct NewTarget;

impl Operation for NewTarget {
    const NAME: &'static str = "NewTarget";
    const INSTRUCTION: &'static str = "INST - NewTarget";

    fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
        let new_target = if let Some(new_target) = context
            .vm
            .environments
            .get_this_environment()
            .as_function()
            .and_then(|env| env.slots().new_target().cloned())
        {
            new_target.into()
        } else {
            JsValue::undefined()
        };
        context.vm.push(new_target);
        Ok(CompletionType::Normal)
    }
}

/// `ImportMeta` implements the Opcode Operation for `Opcode::ImportMeta`
///
/// Operation:
///  - Push the current `import.meta` to the stack
#[derive(Debug, Clone, Copy)]
pub(crate) struct ImportMeta;

impl Operation for ImportMeta {
    const NAME: &'static str = "ImportMeta";
    const INSTRUCTION: &'static str = "INST - ImportMeta";

    fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
        // Meta Properties
        //
        // ImportMeta : import . meta
        //
        // https://tc39.es/ecma262/#sec-meta-properties

        // 1. Let module be GetActiveScriptOrModule().

        let Some(ActiveRunnable::Module(module)) = context.vm.active_runnable.clone() else {
            unreachable!("2. Assert: module is a Source Text Module Record.");
        };

        let ModuleKind::SourceText(src) = module.kind() else {
            unreachable!("2. Assert: module is a Source Text Module Record.");
        };

        // 3. Let importMeta be module.[[ImportMeta]].
        // 4. If importMeta is empty, then
        // 5. Else,
        //     a. Assert: importMeta is an Object.
        let import_meta = src
            .import_meta()
            .borrow_mut()
            .get_or_insert_with(|| {
                // a. Set importMeta to OrdinaryObjectCreate(null).
                let import_meta = JsObject::with_null_proto();

                // b. Let importMetaValues be HostGetImportMetaProperties(module).
                // c. For each Record { [[Key]], [[Value]] } p of importMetaValues, do
                //     i. Perform ! CreateDataPropertyOrThrow(importMeta, p.[[Key]], p.[[Value]]).
                // d. Perform HostFinalizeImportMeta(importMeta, module).
                context
                    .module_loader()
                    .init_import_meta(&import_meta, &module, context);

                // e. Set module.[[ImportMeta]] to importMeta.
                import_meta
            })
            .clone();

        //     b. Return importMeta.
        //     f. Return importMeta.
        context.vm.push(import_meta);

        Ok(CompletionType::Normal)
    }
}