Repository: trynova/nova (language: rust)

Issue:
bug: Unable to resolve variables
The following code fails sometimes, not always!:

```js
var s1 = "In getter";
var s2 = "In setter";
var s3 = "Modified by setter";
var o;
eval("o = { get foo(){ return s1;}, set foo(arg){return s2 = s3} };");

function eatFoo(a, b){
  if (a === b) {
    // Handle +/-0 vs. -/+0
    return a !== 0 || 1 / a === 1 / b;
  }
}

eatFoo(o.foo, s1);

o.foo=10;

print(o.foo)
```

platform: Windows


Discussion / hints:
Reason turns out to be dropping of the eval string after the eval has been run.

Fix patch:
diff --git a/nova_cli/src/main.rs b/nova_cli/src/main.rs
index 1f751da70..7c8e0b9b2 100644
--- a/nova_cli/src/main.rs
+++ b/nova_cli/src/main.rs
@@ -15,7 +15,7 @@ use nova_vm::ecmascript::{
         Agent,
     },
     scripts_and_modules::script::{parse_script, script_evaluation},
-    types::Object,
+    types::{Object, String as JsString},
 };
 use oxc_parser::Parser;
 use oxc_semantic::{SemanticBuilder, SemanticBuilderReturn};
@@ -115,8 +115,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
             nogc,
             paths,
         } => {
-            let allocator = Default::default();
-
             let host_hooks: &CliHostHooks = &*Box::leak(Box::default());
             let mut agent = GcAgent::new(
                 Options {
@@ -143,11 +141,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
                 agent.run_in_realm(&realm, |agent| -> Result<(), Box<dyn std::error::Error>> {
                     let realm = agent.current_realm_id();
                     let file = std::fs::read_to_string(&path)?;
-                    let script =
-                        match parse_script(&allocator, file.into(), realm, !no_strict, None) {
-                            Ok(script) => script,
-                            Err((file, errors)) => exit_with_parse_errors(errors, &path, &file),
-                        };
+                    let source_text = JsString::from_string(agent, file);
+                    let script = match parse_script(agent, source_text, realm, !no_strict, None) {
+                        Ok(script) => script,
+                        Err(errors) => {
+                            // Borrow the string data from the Agent
+                            let source_text = source_text.as_str(agent);
+                            exit_with_parse_errors(errors, &path, source_text)
+                        }
+                    };
                     let mut result = script_evaluation(agent, script);
 
                     if result.is_ok() {
@@ -179,7 +181,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
             agent.remove_realm(realm);
         }
         Command::Repl {} => {
-            let allocator = Default::default();
             let host_hooks: &CliHostHooks = &*Box::leak(Box::default());
             let mut agent = GcAgent::new(
                 Options {
@@ -212,16 +213,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
                 }
                 placeholder = input.to_string();
                 agent.run_in_realm(&realm, |agent| {
-                    let script = match parse_script(
-                        &allocator,
-                        input.into(),
-                        agent.current_realm_id(),
-                        true,
-                        None,
-                    ) {
+                    let realm = agent.current_realm_id();
+                    let source_text = JsString::from_string(agent, input);
+                    let script = match parse_script(agent, source_text, realm, true, None) {
                         Ok(script) => script,
-                        Err((file, errors)) => {
-                            exit_with_parse_errors(errors, "<stdin>", &file);
+                        Err(errors) => {
+                            exit_with_parse_errors(errors, "<stdin>", &placeholder);
                         }
                     };
                     let result = script_evaluation(agent, script);
diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs
index fcbbbe23d..e69aaa708 100644
--- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs
+++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs
@@ -2,7 +2,10 @@
 // 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 std::ops::{Index, IndexMut};
+use std::{
+    ops::{Index, IndexMut},
+    ptr::NonNull,
+};
 
 use oxc_ast::{
     ast::{FormalParameters, FunctionBody},
@@ -24,7 +27,7 @@ use crate::{
             ECMAScriptCodeEvaluationState, EnvironmentIndex, ExecutionContext, JsResult,
             PrivateEnvironmentIndex, ProtoIntrinsics, RealmIdentifier, ThisBindingStatus,
         },
-        scripts_and_modules::ScriptOrModule,
+        scripts_and_modules::{source_code::SourceCode, ScriptOrModule},
         syntax_directed_operations::{
             function_definitions::evaluate_function_body,
             miscellaneous::instantiate_function_object,
@@ -180,15 +183,15 @@ pub(crate) struct ECMAScriptFunctionObjectHeapData {
 
     /// \[\[FormalParameters]]
     ///
-    /// SAFETY: ScriptOrModule owns the Program which this refers to.
+    /// SAFETY: SourceCode owns the Allocator into which this refers to.
     /// Our GC algorithm keeps it alive as long as this function is alive.
-    pub formal_parameters: &'static FormalParameters<'static>,
+    pub formal_parameters: NonNull<FormalParameters<'static>>,
 
     /// \[\[ECMAScriptCode]]
     ///
-    /// SAFETY: ScriptOrModule owns the Program which this refers to.
+    /// SAFETY: SourceCode owns the Allocator into which this refers to.
     /// Our GC algorithm keeps it alive as long as this function is alive.
-    pub ecmascript_code: &'static FunctionBody<'static>,
+    pub ecmascript_code: NonNull<FunctionBody<'static>>,
 
     /// True if the function body is a ConciseBody (can only be true for arrow
     /// functions).
@@ -216,8 +219,14 @@ pub(crate) struct ECMAScriptFunctionObjectHeapData {
     /// \[\[HomeObject]]
     pub home_object: Option<Object>,
 
-    ///  [[SourceText]]
+    ///  \[\[SourceText]]
     pub source_text: Span,
+
+    /// \[\[SourceCode]]
+    ///
+    /// Nova specific addition: This SourceCode is where \[\[SourceText]]
+    /// refers to.
+    pub source_code: SourceCode,
     // TODO: [[Fields]],  [[PrivateMethods]], [[ClassFieldInitializerName]]
 }
 
@@ -610,6 +619,7 @@ pub(crate) fn prepare_for_ordinary_call(
     let private_environment = ecmascript_function_object.private_environment;
     let is_strict_mode = ecmascript_function_object.strict;
     let script_or_module = ecmascript_function_object.script_or_module;
+    let source_code = ecmascript_function_object.source_code;
     // 1. Let callerContext be the running execution context.
     let _caller_context = agent.running_execution_context();
     // 4. Let calleeRealm be F.[[Realm]].
@@ -626,6 +636,7 @@ pub(crate) fn prepare_for_ordinary_call(
             variable_environment: EnvironmentIndex::Function(local_env),
             private_environment,
             is_strict_mode,
+            source_code,
         }),
         // 3. Set the Function of calleeContext to F.
         function: Some(f.into()),
@@ -713,10 +724,14 @@ pub(crate) fn evaluate_body(
     arguments_list: ArgumentsList,
 ) -> JsResult<Value> {
     let function_heap_data = &agent[function_object].ecmascript_function;
-    // SAFETY: Heap is self-referential: This
     let heap_data = function_heap_data;
-    if heap_data.ecmascript_code.statements.is_empty()
-        && heap_data.formal_parameters.is_simple_parameter_list()
+    // SAFETY: AS the ECMAScriptFunction is alive in the heap, its referred
+    // SourceCode must be as well. Thus the Allocator is live as well, and no
+    // other references to it can exist.
+    if unsafe { heap_data.ecmascript_code.as_ref() }
+        .statements
+        .is_empty()
+        && unsafe { heap_data.formal_parameters.as_ref() }.is_simple_parameter_list()
     {
         // Optimisation: Empty body and only simple parameters means no code will effectively run.
         return Ok(Value::Undefined);
@@ -782,13 +797,10 @@ pub(crate) fn ordinary_function_create<'agent, 'program>(
     agent: &'agent mut Agent,
     params: OrdinaryFunctionCreateParams<'agent, 'program>,
 ) -> ECMAScriptFunction {
+    let running_ecmascript_code = &agent.running_execution_context().ecmascript_code.unwrap();
+    let source_code = running_ecmascript_code.source_code;
     // 7. If the source text matched by Body is strict mode code, let Strict be true; else let Strict be false.
-    let strict = params.body.has_use_strict_directive()
-        || agent
-            .running_execution_context()
-            .ecmascript_code
-            .unwrap()
-            .is_strict_mode;
+    let strict = params.body.has_use_strict_directive() || running_ecmascript_code.is_strict_mode;
 
     // 1. Let internalSlotsList be the internal slots listed in Table 30.
     // 2. Let F be OrdinaryObjectCreate(functionPrototype, internalSlotsList).
@@ -803,19 +815,16 @@ pub(crate) fn ordinary_function_create<'agent, 'program>(
         // and is valid until it gets dropped. Our GC keeps ScriptOrModule
         // alive until this ECMAScriptFunction gets dropped, hence the 'static
         // lifetime here is justified.
-        formal_parameters: unsafe {
-            std::mem::transmute::<
-                &'agent FormalParameters<'program>,
-                &'static FormalParameters<'static>,
-            >(params.parameters_list)
-        },
+        formal_parameters: NonNull::from(unsafe {
+            std::mem::transmute::<&FormalParameters<'program>, &FormalParameters<'static>>(
+                params.parameters_list,
+            )
+        }),
         // 6. Set F.[[ECMAScriptCode]] to Body.
         // SAFETY: Same as above: Self-referential reference to ScriptOrModule.
-        ecmascript_code: unsafe {
-            std::mem::transmute::<&'agent FunctionBody<'program>, &FunctionBody<'static>>(
-                params.body,
-            )
-        },
+        ecmascript_code: NonNull::from(unsafe {
+            std::mem::transmute::<&FunctionBody<'program>, &FunctionBody<'static>>(params.body)
+        }),
         is_concise_arrow_function: params.is_concise_arrow_function,
         // 12. Set F.[[IsClassConstructor]] to false.
         constructor_status: ConstructorStatus::NonConstructor,
@@ -839,6 +848,7 @@ pub(crate) fn ordinary_function_create<'agent, 'program>(
         home_object: None,
         // 4. Set F.[[SourceText]] to sourceText.
         source_text: params.source_text,
+        source_code,
     };
 
     let mut function = ECMAScriptFunctionHeapData {
@@ -1089,7 +1099,13 @@ pub(crate) fn function_declaration_instantiation(
     } = *callee_context.ecmascript_code.as_ref().unwrap();
     // 2. Let code be func.[[ECMAScriptCode]].
     let (var_names, var_declarations, lexical_names, lex_declarations) = {
-        let code = agent[function_object].ecmascript_function.ecmascript_code;
+        // SAFETY: We're alive so SourceCode must be too.
+        let code = unsafe {
+            agent[function_object]
+                .ecmascript_function
+                .ecmascript_code
+                .as_ref()
+        };
         // 9. Let varNames be the VarDeclaredNames of code.
         let var_names = function_body_var_declared_names(code)
             .iter()
@@ -1110,21 +1126,14 @@ pub(crate) fn function_declaration_instantiation(
     // 3. Let strict be func.[[Strict]].
     let strict = heap_data.ecmascript_function.strict;
     // 4. Let formals be func.[[FormalParameters]].
-    let formals = heap_data.ecmascript_function.formal_parameters;
+    // SAFETY: We're alive, thus SourceCode must be alive as well.
+    let formals = unsafe { heap_data.ecmascript_function.formal_parameters.as_ref() };
     let this_mode = heap_data.ecmascript_function.this_mode;
     // 5. Let parameterNames be the BoundNames of formals.
-    let mut parameter_names = Vec::with_capacity(
-        heap_data
-            .ecmascript_function
-            .formal_parameters
-            .parameters_count(),
-    );
-    heap_data
-        .ecmascript_function
-        .formal_parameters
-        .bound_names(&mut |identifier| {
-            parameter_names.push(String::from_str(agent, identifier.name.as_str()));
-        });
+    let mut parameter_names = Vec::with_capacity(formals.parameters_count());
+    formals.bound_names(&mut |identifier| {
+        parameter_names.push(String::from_str(agent, identifier.name.as_str()));
+    });
     // 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false.
     // TODO: Check duplicates
     let has_duplicates = false;
diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_prototype.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_prototype.rs
index 502060a71..57ceb02e8 100644
--- a/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_prototype.rs
+++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/function_objects/function_prototype.rs
@@ -163,7 +163,7 @@ impl FunctionPrototype {
                 let source = data.script_or_module;
                 match source {
                     ScriptOrModule::Script(script) => {
-                        let source_text = agent[script].source_text
+                        let source_text = agent[script].source_code.get_source_text(agent)
                             [(span.start as usize)..(span.end as usize)]
                             .to_string();
                         Ok(Value::from_string(agent, source_text))
diff --git a/nova_vm/src/ecmascript/builtins/global_object.rs b/nova_vm/src/ecmascript/builtins/global_object.rs
index 644d77d81..d9f51ec56 100644
--- a/nova_vm/src/ecmascript/builtins/global_object.rs
+++ b/nova_vm/src/ecmascript/builtins/global_object.rs
@@ -8,8 +8,6 @@ use oxc_ast::{
     ast::{BindingIdentifier, Program, VariableDeclarationKind},
     syntax_directed_operations::BoundNames,
 };
-use oxc_parser::{Parser, ParserReturn};
-use oxc_semantic::{SemanticBuilder, SemanticBuilderReturn};
 use oxc_span::SourceType;
 
 use crate::{
@@ -21,6 +19,7 @@ use crate::{
             ECMAScriptCodeEvaluationState, EnvironmentIndex, ExecutionContext, JsResult,
             PrivateEnvironmentIndex, RealmIdentifier,
         },
+        scripts_and_modules::source_code::SourceCode,
         syntax_directed_operations::{
             miscellaneous::instantiate_function_object,
             scope_analysis::{
@@ -205,54 +204,27 @@ pub fn perform_eval(
 
     // 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection:
     // a. Let script be ParseText(x, Script).
-    // HACK: Because we rely on the allocator outliving the Agent we leak the
-    // allocator here . See https://github.com/trynova/nova/pull/278#discussion_r1663233064
-    // for more information.
-    let allocator = Box::leak(Box::default());
-    let source_text = x.as_str(agent).to_owned();
     let source_type = SourceType::default().with_always_strict(strict_caller);
-    let parser = Parser::new(allocator, &source_text, source_type);
-    let ParserReturn {
-        errors,
-        program: script,
-        ..
-    } = parser.parse();
+    // SAFETY: Script is only kept alive for the duration of this call, and any
+    // references made to it by functions being created in the eval call will
+    // take a copy of the SourceCode. The SourceCode is also kept in the
+    // evaluation context and thus cannot be garbage collected while the eval
+    // call happens.
+    // The Program thus refers to a valid, live Allocator for the duration of
+    // this call.
+    let parse_result = unsafe { SourceCode::parse_source(agent, x, source_type) };
 
     // b. If script is a List of errors, throw a SyntaxError exception.
-    if !errors.is_empty() {
-        // Make sure `script` can't borrow `source_text` so we can return it.
-        drop(script);
-        // SAFETY: It is safe to drop the leaked allocator here because it is known to be unused.
-        drop(unsafe { Box::from_raw(allocator) });
+    let Ok((script, source_code)) = parse_result else {
         // TODO: Include error messages in the exception.
         return Err(agent.throw_exception_with_static_message(
             ExceptionType::SyntaxError,
             "Invalid eval source text.",
         ));
-    }
-
-    let SemanticBuilderReturn { errors, .. } = SemanticBuilder::new(&source_text, source_type)
-        .with_check_syntax_error(true)
-        .build(&script);
-
-    if !errors.is_empty() {
-        // Make sure `script` can't borrow `source_text` so we can return it.
-        drop(script);
-        // SAFETY: It is safe to drop the leaked allocator here because it is known to be unused.
-        drop(unsafe { Box::from_raw(allocator) });
-        // TODO: Include error messages in the exception.
-        return Err(agent.throw_exception_with_static_message(
-            ExceptionType::SyntaxError,
-            "Invalid eval source text.",
-        ));
-    }
+    };
 
     // c. If script Contains ScriptBody is false, return undefined.
     if script.is_empty() {
-        // Make sure `script` can't borrow `source_text` so we can drop the allocator. Because the script is empty this is fine.
-        drop(script);
-        // SAFETY: It is safe to drop the leaked allocator here because it is known to be unused.
-        drop(unsafe { Box::from_raw(allocator) });
         return Ok(Value::Undefined);
     }
 
@@ -297,6 +269,8 @@ pub fn perform_eval(
             // c. Let privateEnv be runningContext's PrivateEnvironment.
             private_environment: running_context_private_env,
             is_strict_mode: strict_eval,
+            // The code running inside eval is defined inside the eval source.
+            source_code,
         }
     } else {
         // 17. Else,
@@ -313,6 +287,8 @@ pub fn perform_eval(
             // c. Let privateEnv be null.
             private_environment: None,
             is_strict_mode: strict_eval,
+            // The code running inside eval is defined inside the eval source.
+            source_code,
         }
     };
 
diff --git a/nova_vm/src/ecmascript/execution/execution_context.rs b/nova_vm/src/ecmascript/execution/execution_context.rs
index 6a77d5be8..d5100baeb 100644
--- a/nova_vm/src/ecmascript/execution/execution_context.rs
+++ b/nova_vm/src/ecmascript/execution/execution_context.rs
@@ -3,7 +3,10 @@
 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
 
 use super::{Agent, EnvironmentIndex, PrivateEnvironmentIndex, RealmIdentifier};
-use crate::ecmascript::{scripts_and_modules::ScriptOrModule, types::*};
+use crate::ecmascript::{
+    scripts_and_modules::{source_code::SourceCode, ScriptOrModule},
+    types::*,
+};
 
 // TODO: Remove this.
 pub(crate) type ECMAScriptCode = ECMAScriptCodeEvaluationState;
@@ -37,6 +40,12 @@ pub(crate) struct ECMAScriptCodeEvaluationState {
     /// [`ECMAScriptCodeEvaluationState`] to track whether some ECMAScript code
     /// is in strict mode.
     pub(crate) is_strict_mode: bool,
+
+    /// Nova-specific piece of data that identifiers in which source code the
+    /// currently evaluated code was defined in. Note that this is also
+    /// defined for builtin functions: A builtin function's source code will
+    /// point to the source code that called it.
+    pub(crate) source_code: SourceCode,
 }
 
 /// ### [9.4 Execution Contexts](https://tc39.es/ecma262/#sec-execution-contexts)
diff --git a/nova_vm/src/ecmascript/scripts_and_modules.rs b/nova_vm/src/ecmascript/scripts_and_modules.rs
index 0ec517862..717da8419 100644
--- a/nova_vm/src/ecmascript/scripts_and_modules.rs
+++ b/nova_vm/src/ecmascript/scripts_and_modules.rs
@@ -10,6 +10,7 @@ use super::builtins::module::Module;
 
 pub mod module;
 pub mod script;
+pub mod source_code;
 
 #[derive(Debug, Clone, Copy)]
 pub(crate) enum ScriptOrModule {
diff --git a/nova_vm/src/ecmascript/scripts_and_modules/script.rs b/nova_vm/src/ecmascript/scripts_and_modules/script.rs
index 94fc37861..c2f116f06 100644
--- a/nova_vm/src/ecmascript/scripts_and_modules/script.rs
+++ b/nova_vm/src/ecmascript/scripts_and_modules/script.rs
@@ -22,22 +22,22 @@ use crate::{
     engine::{Executable, Vm},
     heap::{CompactionLists, HeapMarkAndSweep, WorkQueues},
 };
-use oxc_allocator::Allocator;
 use oxc_ast::{
     ast::{BindingIdentifier, Program, VariableDeclarationKind},
     syntax_directed_operations::BoundNames,
 };
 use oxc_diagnostics::OxcDiagnostic;
-use oxc_parser::{Parser, ParserReturn};
-use oxc_semantic::{SemanticBuilder, SemanticBuilderReturn};
 use oxc_span::SourceType;
 use std::{
     any::Any,
     collections::HashSet,
     marker::PhantomData,
+    mem::ManuallyDrop,
     ops::{Index, IndexMut},
 };
 
+use super::source_code::SourceCode;
+
 pub type HostDefined = &'static mut dyn Any;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -132,7 +132,13 @@ pub struct Script {
     /// ### \[\[ECMAScriptCode]]
     ///
     /// The result of parsing the source text of this script.
-    pub(crate) ecmascript_code: Program<'static>,
+    ///
+    /// Note: The Program's drop code is never run. The referred structures
+    /// live in the SourceCode heap data in its contained Allocator. The bump
+    /// allocator drops all of the data in a single go. All that needs to be
+    /// dropped here is the local Program itself, not any of its referred
+    /// parts.
+    pub(crate) ecmascript_code: ManuallyDrop<Program<'static>>,
 
     /// ### \[\[LoadedModules]]
     ///
@@ -149,37 +155,37 @@ pub struct Script {
 
     /// Source text of the script
     ///
-    /// Parsing a script takes ownership of the text.
-    pub(crate) source_text: Box<str>,
+    /// The source text is kept in the heap strings vector, through the
+    /// SourceCode struct.
+    pub(crate) source_code: SourceCode,
 }
 
 unsafe impl Send for Script {}
 
-pub type ScriptOrErrors = Result<Script, (Box<str>, Vec<OxcDiagnostic>)>;
+pub type ScriptOrErrors = Result<Script, Vec<OxcDiagnostic>>;
 
 impl HeapMarkAndSweep for Script {
     fn mark_values(&self, queues: &mut WorkQueues) {
         self.realm.mark_values(queues);
+        self.source_code.mark_values(queues);
     }
 
     fn sweep_values(&mut self, compactions: &CompactionLists) {
         self.realm.sweep_values(compactions);
+        self.source_code.sweep_values(compactions);
     }
 }
 
 /// ### [16.1.5 ParseScript ( sourceText, realm, hostDefined )](https://tc39.es/ecma262/#sec-parse-script)
 ///
 /// The abstract operation ParseScript takes arguments sourceText (ECMAScript
-/// source text), realm (a Realm Record or undefined), and hostDefined
-/// (anything) and returns a Script Record or a non-empty List of SyntaxError
-/// objects. It creates a Script Record based upon the result of parsing
-/// sourceText as a Script.
-///
-/// NOTE: If parsing fails, the `source_text` is returned so it can be used for
-/// diagnostics.
+/// source text), realm (a Realm Record), and hostDefined (anything) and
+/// returns a Script Record or a non-empty List of SyntaxError objects. It
+/// creates a Script Record based upon the result of parsing sourceText as a
+/// Script.
 pub fn parse_script(
-    allocator: &Allocator,
-    source_text: Box<str>,
+    agent: &mut Agent,
+    source_text: String,
     realm: RealmIdentifier,
     strict_mode: bool,
     host_defined: Option<HostDefined>,
@@ -189,42 +195,30 @@ pub fn parse_script(
     if cfg!(feature = "typescript") {
         source_type = source_type.with_typescript(true);
     }
-    let parser = Parser::new(allocator, &source_text, source_type);
-    let ParserReturn {
-        errors, program, ..
-    } = parser.parse();
 
-    // 2. If script is a List of errors, return script.
-    if !errors.is_empty() {
-        // Make sure `program` can't borrow `source_text` so we can return it.
-        drop(program);
-        return Err((source_text, errors));
-    }
-
-    let SemanticBuilderReturn { errors, .. } = SemanticBuilder::new(&source_text, source_type)
-        .with_check_syntax_error(true)
-        .build(&program);
+    // SAFETY: Script keeps the SourceCode reference alive in the Heap, thus
+    // making the Program's references point to a live Allocator.
+    let parse_result = unsafe { SourceCode::parse_source(agent, source_text, source_type) };
 
-    if !errors.is_empty() {
-        // Make sure `program` can't borrow `source_text` so we can return it.
-        drop(program);
-        return Err((source_text, errors));
-    }
+    let (program, source_code) = match parse_result {
+        // 2. If script is a List of errors, return script.
+        Ok(result) => result,
+        Err(errors) => {
+            return Err(errors);
+        }
+    };
 
     // 3. Return Script Record {
     Ok(Script {
         // [[Realm]]: realm,
         realm,
         // [[ECMAScriptCode]]: script,
-        // SAFETY: Program retains at least a logical connection to `source_text`, possibly even
-        // direct references. This should be safe because we move the `source_text` into the Script
-        // struct, making it self-referential. Hence we must use the 'static lifetime.
-        ecmascript_code: unsafe { std::mem::transmute::<Program<'_>, Program<'static>>(program) },
+        ecmascript_code: ManuallyDrop::new(program),
         // [[LoadedModules]]: « »,
         loaded_modules: (),
         // [[HostDefined]]: hostDefined,
         host_defined,
-        source_text,
+        source_code,
     })
     // }
 }
@@ -237,6 +231,7 @@ pub fn parse_script(
 pub fn script_evaluation(agent: &mut Agent, script: Script) -> JsResult<Value> {
     let realm_id = script.realm;
     let is_strict_mode = script.ecmascript_code.is_strict();
+    let source_code = script.source_code;
     let script = agent.heap.add_script(script);
     let realm = agent.get_realm(realm_id);
 
@@ -265,6 +260,8 @@ pub fn script_evaluation(agent: &mut Agent, script: Script) -> JsResult<Value> {
             private_environment: None,
 
             is_strict_mode,
+
+            source_code,
         }),
     };
 
@@ -535,17 +532,15 @@ mod test {
         },
         SmallInteger,
     };
-    use oxc_allocator::Allocator;
 
     #[test]
     fn empty_script() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
 
         let result = script_evaluation(&mut agent, script).unwrap();
 
@@ -554,13 +549,12 @@ mod test {
 
     #[test]
     fn basic_constants() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "true".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "true");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
 
         let result = script_evaluation(&mut agent, script).unwrap();
 
@@ -569,13 +563,12 @@ mod test {
 
     #[test]
     fn unary_minus() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "-2".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "-2");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
 
         let result = script_evaluation(&mut agent, script).unwrap();
 
@@ -584,14 +577,12 @@ mod test {
 
     #[test]
     fn unary_void() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script =
-            parse_script(&allocator, "void (2 + 2 + 6)".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "void (2 + 2 + 6)");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
 
         let result = script_evaluation(&mut agent, script).unwrap();
 
@@ -600,13 +591,12 @@ mod test {
 
     #[test]
     fn unary_plus() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "+(54)".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "+(54)");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
 
         let result = script_evaluation(&mut agent, script).unwrap();
 
@@ -615,13 +605,12 @@ mod test {
 
     #[test]
     fn logical_not() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "!true".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "!true");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
 
         let result = script_evaluation(&mut agent, script).unwrap();
 
@@ -630,13 +619,12 @@ mod test {
 
     #[test]
     fn bitwise_not() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "~0b1111".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "~0b1111");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
 
         let result = script_evaluation(&mut agent, script).unwrap();
 
@@ -645,68 +633,64 @@ mod test {
 
     #[test]
     fn unary_typeof() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script =
-            parse_script(&allocator, "typeof undefined".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof undefined");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "undefined"));
 
-        let script = parse_script(&allocator, "typeof null".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof null");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "object"));
 
-        let script =
-            parse_script(&allocator, "typeof \"string\"".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof \"string\"");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "string"));
 
-        let script =
-            parse_script(&allocator, "typeof Symbol()".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof Symbol()");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "symbol"));
 
-        let script = parse_script(&allocator, "typeof true".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof true");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "boolean"));
 
-        let script = parse_script(&allocator, "typeof 3".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof 3");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "number"));
 
-        let script = parse_script(&allocator, "typeof 3n".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof 3n");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "bigint"));
 
-        let script = parse_script(&allocator, "typeof {}".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof {}");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "object"));
 
-        let script = parse_script(
-            &allocator,
-            "typeof (function() {})".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "typeof (function() {})");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "function"));
     }
 
     #[test]
     fn binary_add() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "2 + 2 + 6".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "2 + 2 + 6");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
 
         let result = script_evaluation(&mut agent, script).unwrap();
 
@@ -715,26 +699,24 @@ mod test {
 
     #[test]
     fn var_assign() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "var foo = 3;".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "var foo = 3;");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Undefined);
     }
 
     #[test]
     fn empty_object() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "var foo = {};".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "var foo = {};");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_undefined());
         let key = PropertyKey::from_static_str(&mut agent, "foo");
@@ -756,14 +738,12 @@ mod test {
 
     #[test]
     fn non_empty_object() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script =
-            parse_script(&allocator, "var foo = { a: 3 };".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "var foo = { a: 3 };");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_undefined());
         let key = PropertyKey::from_static_str(&mut agent, "foo");
@@ -791,8 +771,6 @@ mod test {
 
     #[test]
     fn empty_array() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
@@ -804,7 +782,8 @@ mod test {
             script_or_module: None,
         });
 
-        let script = parse_script(&allocator, "var foo = [];".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "var foo = [];");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_undefined());
         let foo_key = String::from_static_str(&mut agent, "foo");
@@ -824,20 +803,12 @@ mod test {
 
     #[test]
     fn non_empty_array() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(
-            &allocator,
-            "var foo = [ 'a', 3 ];".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "var foo = [ 'a', 3 ];");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_undefined());
         let foo_key = String::from_static_str(&mut agent, "foo");
@@ -873,96 +844,66 @@ mod test {
 
     #[test]
     fn empty_function() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script =
-            parse_script(&allocator, "function foo() {}".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "function foo() {}");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_function());
     }
 
     #[test]
     fn empty_iife_function_call() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script =
-            parse_script(&allocator, "(function() {})()".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "(function() {})()");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_undefined());
     }
 
     #[test]
     fn empty_named_function_call() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(
-            &allocator,
-            "var f = function() {}; f();".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "var f = function() {}; f();");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_undefined());
     }
 
     #[test]
     fn empty_declared_function_call() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(
-            &allocator,
-            "function f() {}; f();".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "function f() {}; f();");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_undefined());
     }
 
     #[test]
     fn non_empty_iife_function_call() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(
-            &allocator,
-            "(function() { return 3 })()".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "(function() { return 3 })()");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Number::from(3).into_value());
     }
 
     #[test]
     fn builtin_function_call() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
@@ -999,141 +940,108 @@ mod test {
 
         create_data_property_or_throw(&mut agent, global, key, func.into_value()).unwrap();
 
-        let script = parse_script(&allocator, "test(true)".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "test(true)");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from(3));
 
-        let script = parse_script(&allocator, "test()".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "test()");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Null);
 
-        let script = parse_script(&allocator, "test({})".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "test({})");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Null);
     }
 
     #[test]
     fn if_statement() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(&allocator, "if (true) 3".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "if (true) 3");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Number::from(3).into_value());
 
-        let script = parse_script(&allocator, "if (false) 3".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "if (false) 3");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Undefined);
     }
 
     #[test]
     fn if_else_statement() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(
-            &allocator,
-            "var foo = function() { if (true) { return 3; } else { return 5; } }; foo()".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "var foo = function() { if (true) { return 3; } else { return 5; } }; foo()",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Number::from(3).into_value());
 
-        let script = parse_script(
-            &allocator,
-            "var bar = function() { if (false) { return 3; } else { return 5; } }; bar()".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "var bar = function() { if (false) { return 3; } else { return 5; } }; bar()",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Number::from(5).into_value());
     }
 
     #[test]
     fn static_property_access() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(
-            &allocator,
-            "var foo = { a: 3 }; foo.a".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "var foo = { a: 3 }; foo.a");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Number::from(3).into_value());
     }
 
     #[test]
     fn deep_static_property_access() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(
-            &allocator,
-            "var fn = function() { return 3; }; var foo = { a: { b: fn } }; foo.a.b()".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "var fn = function() { return 3; }; var foo = { a: { b: fn } }; foo.a.b()",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Number::from(3).into_value());
     }
 
     #[test]
     fn computed_property_access() {
-        let allocator: Allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
 
-        let script = parse_script(
-            &allocator,
-            "var foo = { a: 3 }; var prop = 'a'; foo[prop]".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text =
+            String::from_static_str(&mut agent, "var foo = { a: 3 }; var prop = 'a'; foo[prop]");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Number::from(3).into_value());
     }
     #[test]
     fn for_loop() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         let realm = create_realm(&mut agent);
         set_realm_global_object(&mut agent, realm, None, None);
-        let script = parse_script(
-            &allocator,
-            "var i = 0; for (; i < 3; i++) {}".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "var i = 0; for (; i < 3; i++) {}");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Undefined);
         let key = PropertyKey::from_static_str(&mut agent, "i");
@@ -1150,20 +1058,12 @@ mod test {
 
     #[test]
     fn lexical_declarations() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "let i = 0; const a = 'foo'; i = 3;".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "let i = 0; const a = 'foo'; i = 3;");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         script_evaluation(&mut agent, script).unwrap();
 
         let global_env = agent.get_realm(realm).global_env.unwrap();
@@ -1187,20 +1087,13 @@ mod test {
 
     #[test]
     fn lexical_declarations_in_block() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "{ let i = 0; const a = 'foo'; i = 3; }".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text =
+            String::from_static_str(&mut agent, "{ let i = 0; const a = 'foo'; i = 3; }");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Undefined);
 
@@ -1213,20 +1106,12 @@ mod test {
 
     #[test]
     fn object_property_assignment() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "var foo = {}; foo.a = 42; foo".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "var foo = {}; foo.a = 42; foo");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         let object = Object::try_from(result).unwrap();
 
@@ -1241,209 +1126,157 @@ mod test {
 
     #[test]
     fn try_catch_not_thrown() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "let a = 0; try { a++; } catch { a = 500; }; a++; a".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "let a = 0; try { a++; } catch { a = 500; }; a++; a",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(2)));
     }
 
     #[test]
     fn try_catch_thrown() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "let a = 0; try { throw null; a = 500 } catch { a++; }; a++; a".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "let a = 0; try { throw null; a = 500 } catch { a++; }; a++; a",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(2)));
     }
 
     #[test]
     fn catch_binding() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "let err; try { throw 'thrown'; } catch(e) { err = e; }; err".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "let err; try { throw 'thrown'; } catch(e) { err = e; }; err",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "thrown"));
     }
 
     #[test]
     fn throwing_in_try_restores_lexical_environment() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "let a = 42; try { let a = 62; throw 'thrown'; } catch { }; a".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "let a = 42; try { let a = 62; throw 'thrown'; } catch { }; a",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(42)));
     }
 
     #[test]
     fn function_argument_bindings() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "const foo = function (a) { return a + 10; }; foo(32)".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "const foo = function (a) { return a + 10; }; foo(32)",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(42)));
     }
 
     #[test]
     fn logical_and() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(&allocator, "true && true".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "true && true");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Boolean(true));
 
-        let script = parse_script(
-            &allocator,
-            "true && false && true".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "true && false && true");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Boolean(false));
     }
 
     #[test]
     fn logical_or() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(&allocator, "false || false".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "false || false");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Boolean(false));
 
-        let script = parse_script(
-            &allocator,
-            "true || false || true".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "true || false || true");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Boolean(true));
     }
 
     #[test]
     fn nullish_coalescing() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(&allocator, "null ?? 42".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "null ?? 42");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(42)));
 
-        let script = parse_script(&allocator, "'foo' ?? 12".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "'foo' ?? 12");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "foo"));
 
-        let script =
-            parse_script(&allocator, "undefined ?? null".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "undefined ?? null");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Null);
     }
 
     #[test]
     fn string_concat() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script =
-            parse_script(&allocator, "'foo' + '' + 'bar'".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "'foo' + '' + 'bar'");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "foobar"));
 
-        let script = parse_script(
-            &allocator,
-            "'foo' + ' a heap string'".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "'foo' + ' a heap string'");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(
             result,
             Value::from_static_str(&mut agent, "foo a heap string")
         );
 
-        let script = parse_script(
-            &allocator,
-            "'Concatenating ' + 'two heap strings'".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text =
+            String::from_static_str(&mut agent, "'Concatenating ' + 'two heap strings'");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(
             result,
@@ -1453,85 +1286,66 @@ mod test {
 
     #[test]
     fn property_access_on_functions() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "function foo() {}; foo.bar".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "function foo() {}; foo.bar");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Undefined);
 
-        let script = parse_script(
-            &allocator,
-            "foo.bar = 42; foo.bar".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "foo.bar = 42; foo.bar");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(42)));
 
-        let script = parse_script(&allocator, "foo.name".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "foo.name");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "foo"));
 
-        let script = parse_script(&allocator, "foo.length".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "foo.length");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::zero()));
 
-        let script = parse_script(&allocator, "foo.prototype".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "foo.prototype");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert!(result.is_object())
     }
 
     #[test]
     fn name_and_length_on_builtin_functions() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(&allocator, "TypeError.name".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "TypeError.name");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::from_static_str(&mut agent, "TypeError"));
 
-        let script =
-            parse_script(&allocator, "TypeError.length".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "TypeError.length");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(1)));
     }
 
     #[test]
     fn constructor() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "function foo() {}; foo.prototype".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "function foo() {}; foo.prototype");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         let foo_prototype = Object::try_from(result).unwrap();
 
-        let script = parse_script(&allocator, "new foo()".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "new foo()");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = match script_evaluation(&mut agent, script) {
             Ok(result) => result,
             Err(err) => panic!("{}", err.to_string(&mut agent).as_str(&agent)),
@@ -1545,145 +1359,91 @@ mod test {
 
     #[test]
     fn this_expression() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "function foo() { this.bar = 42; }; new foo().bar".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "function foo() { this.bar = 42; }; new foo().bar",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(42)));
 
-        let script = parse_script(
-            &allocator,
-            "foo.prototype.baz = function() { return this.bar + 10; }; (new foo()).baz()".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(
+            &mut agent,
+            "foo.prototype.baz = function() { return this.bar + 10; }; (new foo()).baz()",
+        );
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Integer(SmallInteger::from(52)));
     }
 
     #[test]
     fn symbol_stringification() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(&allocator, "+Symbol()".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "+Symbol()");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         assert!(script_evaluation(&mut agent, script).is_err());
 
-        let script = parse_script(&allocator, "+Symbol('foo')".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "+Symbol('foo')");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         assert!(script_evaluation(&mut agent, script).is_err());
 
-        let script =
-            parse_script(&allocator, "String(Symbol())".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "String(Symbol())");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let value = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(value, Value::from_static_str(&mut agent, "Symbol()"));
 
-        let script = parse_script(
-            &allocator,
-            "String(Symbol('foo'))".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "String(Symbol('foo'))");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let value = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(value, Value::from_static_str(&mut agent, "Symbol(foo)"));
     }
 
     #[test]
     fn instanceof() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script =
-            parse_script(&allocator, "3 instanceof Number".into(), realm, false, None).unwrap();
+        let source_text = String::from_static_str(&mut agent, "3 instanceof Number");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         assert_eq!(script_evaluation(&mut agent, script).unwrap(), false.into());
 
-        let script = parse_script(
-            &allocator,
-            "'foo' instanceof String".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "'foo' instanceof String");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         assert_eq!(script_evaluation(&mut agent, script).unwrap(), false.into());
 
-        let script = parse_script(
-            &allocator,
-            "({}) instanceof Object".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "({}) instanceof Object");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         assert_eq!(script_evaluation(&mut agent, script).unwrap(), true.into());
 
-        let script = parse_script(
-            &allocator,
-            "({}) instanceof Array".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "({}) instanceof Array");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         assert_eq!(script_evaluation(&mut agent, script).unwrap(), false.into());
 
-        let script = parse_script(
-            &allocator,
-            "([]) instanceof Object".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "([]) instanceof Object");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         assert_eq!(script_evaluation(&mut agent, script).unwrap(), true.into());
 
-        let script = parse_script(
-            &allocator,
-            "([]) instanceof Array".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "([]) instanceof Array");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         assert_eq!(script_evaluation(&mut agent, script).unwrap(), true.into());
     }
 
     #[test]
     fn array_binding_pattern() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "const [a, b, , c] = [1, 2, 3, 4];".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "const [a, b, , c] = [1, 2, 3, 4];");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         script_evaluation(&mut agent, script).unwrap();
         let a_key = String::from_static_str(&mut agent, "a");
         let b_key = String::from_static_str(&mut agent, "b");
@@ -1714,19 +1474,13 @@ mod test {
 
     #[test]
     fn do_while() {
-        let allocator = Allocator::default();
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "let i = 0; do { i++ } while(i < 10)".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text =
+            String::from_static_str(&mut agent, "let i = 0; do { i++ } while(i < 10)");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         script_evaluation(&mut agent, script).unwrap();
 
         let i_key = String::from_static_str(&mut agent, "i");
@@ -1742,20 +1496,12 @@ mod test {
 
     #[test]
     fn no_implicit_return() {
-        let allocator = Allocator::default();
-
         let mut agent = Agent::new(Options::default(), &DefaultHostHooks);
         initialize_default_realm(&mut agent);
         let realm = agent.current_realm_id();
 
-        let script = parse_script(
-            &allocator,
-            "function foo() { 42; }; foo()".into(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_static_str(&mut agent, "function foo() { 42; }; foo()");
+        let script = parse_script(&mut agent, source_text, realm, false, None).unwrap();
         let result = script_evaluation(&mut agent, script).unwrap();
         assert_eq!(result, Value::Undefined);
     }
diff --git a/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs b/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs
new file mode 100644
index 000000000..9519cb91e
--- /dev/null
+++ b/nova_vm/src/ecmascript/scripts_and_modules/source_code.rs
@@ -0,0 +1,198 @@
+// 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/.
+
+//! SourceCode is a Nova-engine specific concept to capture and keep any
+//! `eval(source)` source strings alive after the eval call for the case where
+//! that the eval call defines functions. Those functions will refer to the
+//! SourceCode for their function source text.
+
+use std::{fmt::Debug, ops::Index, ptr::NonNull};
+
+use oxc_allocator::Allocator;
+use oxc_ast::ast::Program;
+use oxc_diagnostics::OxcDiagnostic;
+use oxc_parser::{Parser, ParserReturn};
+use oxc_semantic::{SemanticBuilder, SemanticBuilderReturn};
+use oxc_span::SourceType;
+
+use crate::{
+    ecmascript::{
+        execution::Agent,
+        types::{HeapString, String},
+    },
+    heap::{
+        indexes::BaseIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues,
+    },
+};
+
+type SourceCodeIndex = BaseIndex<SourceCodeHeapData>;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub(crate) struct SourceCode(SourceCodeIndex);
+
+impl SourceCode {
+    /// Parses the given source string as JavaScript code and returns the
+    /// parsed result and a SourceCode heap reference.
+    ///
+    /// ### Safety
+    ///
+    /// The caller must keep the SourceCode from being garbage collected until
+    /// they drop the parsed code.
+    pub(crate) unsafe fn parse_source(
+        agent: &mut Agent,
+        source: String,
+        source_type: SourceType,
+    ) -> Result<(Program<'static>, Self), Vec<OxcDiagnostic>> {
+        // If the source code is not a heap string, pad it with whitespace and
+        // allocate it on the heap. This makes it safe (for some definition of
+        // "safe") for the any functions created referring to this source code to
+        // keep references to the string buffer.
+        let (source, source_text) = match source {
+            String::String(source) => {
+                // SAFETY: Caller guarantees to keep SourceCode from being
+                // garbage collected until the parsed Program is dropped.
+                // Thus the source text is kept from garbage collection.
+                let source_text =
+                    unsafe { std::mem::transmute::<&str, &'static str>(source.as_str(agent)) };
+                (source, source_text)
+            }
+            String::SmallString(source) => {
+                // Add 10 whitespace bytes to the end of the eval string. This
+                // should guarantee that the string gets heap-allocated.
+                let original_length = source.len();
+                let data = format!("{}          ", source.as_str());
+                let source = String::from_string(agent, data);
+                let String::String(source) = source else {
+                    unreachable!()
+                };
+                // SAFETY: Caller guarantees to keep SourceCode from being
+                // garbage collected until the parsed Program is dropped.
+                // Thus the source text is kept from garbage collection.
+                let source_text =
+                    unsafe { std::mem::transmute::<&str, &'static str>(source.as_str(agent)) };
+                // Slice the source text back to the original length so that the
+                // whitespace we added doesn't get fed to the parser: It shouldn't
+                // need it.
+                let source_text = &source_text[..original_length];
+                (source, source_text)
+            }
+        };
+
+        let mut allocator = NonNull::from(Box::leak(Box::default()));
+        // SAFETY: Parser is dropped before allocator.
+        let parser = Parser::new(unsafe { allocator.as_mut() }, source_text, source_type);
+
+        let ParserReturn {
+            errors, program, ..
+        } = parser.parse();
+
+        if !errors.is_empty() {
+            // Drop program before dropping allocator.
+            drop(program);
+            // SAFETY: No references to allocator exist anymore. It is safe to
+            // drop it.
+            drop(unsafe { Box::from_raw(allocator.as_mut()) });
+            // TODO: Include error messages in the exception.
+            return Err(errors);
+        }
+
+        let SemanticBuilderReturn { errors, .. } = SemanticBuilder::new(source_text, source_type)
+            .with_check_syntax_error(true)
+            .build(&program);
+
+        if !errors.is_empty() {
+            // Drop program before dropping allocator.
+            drop(program);
+            // SAFETY: No references to allocator exist anymore. It is safe to
+            // drop it.
+            drop(unsafe { Box::from_raw(allocator.as_mut()) });
+            // TODO: Include error messages in the exception.
+            return Err(errors);
+        }
+        // SAFETY: Caller guarantees that they will drop the Program before
+        // SourceCode can be garbage collected.
+        let program = unsafe { std::mem::transmute::<Program<'_>, Program<'static>>(program) };
+        let source_code = agent.heap.create(SourceCodeHeapData { source, allocator });
+
+        Ok((program, source_code))
+    }
+
+    pub(crate) fn get_source_text(self, agent: &Agent) -> &str {
+        agent[agent[self].source].as_str()
+    }
+
+    pub(crate) fn get_index(self) -> usize {
+        self.0.into_index()
+    }
+}
+
+pub(crate) struct SourceCodeHeapData {
+    /// The source JavaScript string data the eval was called with. The string
+    /// is known and required to be a HeapString because functions created
+    /// in the eval call may keep references to the string data. If the eval
+    /// string was small-string optimised and on the stack, then those
+    /// references would necessarily and definitely be invalid.
+    source: HeapString,
+    /// The arena that contains the parsed data of the eval source.
+    allocator: NonNull<Allocator>,
+}
+
+unsafe impl Send for SourceCodeHeapData {}
+
+impl Debug for SourceCodeHeapData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("SourceCodeHeapData")
+            .field("source", &self.source)
+            .field("allocator", &"[binary data]")
+            .finish()
+    }
+}
+
+impl Drop for SourceCodeHeapData {
+    fn drop(&mut self) {
+        // SAFETY: All references to this SourceCode should have been dropped
+        // before we drop this.
+        drop(unsafe { Box::from_raw(self.allocator.as_mut()) });
+    }
+}
+
+impl Index<SourceCode> for Agent {
+    type Output = SourceCodeHeapData;
+
+    fn index(&self, index: SourceCode) -> &Self::Output {
+        self.heap
+            .source_codes
+            .get(index.get_index())
+            .expect("SourceCode out of bounds")
+            .as_ref()
+            .expect("SourceCode slot empty")
+    }
+}
+
+impl CreateHeapData<SourceCodeHeapData, SourceCode> for Heap {
+    fn create(&mut self, data: SourceCodeHeapData) -> SourceCode {
+        self.source_codes.push(Some(data));
+        SourceCode(SourceCodeIndex::last(&self.source_codes))
+    }
+}
+
+impl HeapMarkAndSweep for SourceCodeHeapData {
+    fn mark_values(&self, queues: &mut WorkQueues) {
+        self.source.mark_values(queues);
+    }
+
+    fn sweep_values(&mut self, compactions: &CompactionLists) {
+        self.source.sweep_values(compactions);
+    }
+}
+
+impl HeapMarkAndSweep for SourceCode {
+    fn mark_values(&self, queues: &mut WorkQueues) {
+        queues.source_codes.push(*self);
+    }
+
+    fn sweep_values(&mut self, compactions: &CompactionLists) {
+        compactions.source_codes.shift_index(&mut self.0);
+    }
+}
diff --git a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs
index 483a6f854..6a37127bf 100644
--- a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs
+++ b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs
@@ -145,7 +145,13 @@ pub(crate) fn evaluate_function_body(
     // 1. Perform ? FunctionDeclarationInstantiation(functionObject, argumentsList).
     function_declaration_instantiation(agent, function_object, arguments_list)?;
     // 2. Return ? Evaluation of FunctionStatementList.
-    let body = agent[function_object].ecmascript_function.ecmascript_code;
+    // SAFETY: We're alive so SourceCode must be too.
+    let body = unsafe {
+        agent[function_object]
+            .ecmascript_function
+            .ecmascript_code
+            .as_ref()
+    };
     let is_concise_arrow_function = agent[function_object]
         .ecmascript_function
         .is_concise_arrow_function;
diff --git a/nova_vm/src/ecmascript/types/language/string.rs b/nova_vm/src/ecmascript/types/language/string.rs
index 191a523cb..3a409d1ec 100644
--- a/nova_vm/src/ecmascript/types/language/string.rs
+++ b/nova_vm/src/ecmascript/types/language/string.rs
@@ -23,6 +23,10 @@ pub use data::StringHeapData;
 pub struct HeapString(pub(crate) StringIndex);
 
 impl HeapString {
+    pub fn len(self, agent: &Agent) -> usize {
+        agent[self].len()
+    }
+
     pub(crate) const fn _def() -> Self {
         HeapString(StringIndex::from_u32_index(0))
     }
diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs
index a6597f5a4..398555b17 100644
--- a/nova_vm/src/heap.rs
+++ b/nova_vm/src/heap.rs
@@ -51,6 +51,7 @@ use crate::ecmascript::{
         weak_ref::data::WeakRefHeapData,
         weak_set::data::WeakSetHeapData,
     },
+    scripts_and_modules::source_code::SourceCodeHeapData,
     types::{HeapNumber, HeapString, OrdinaryObject, BUILTIN_STRINGS_LIST},
 };
 use crate::ecmascript::{
@@ -87,7 +88,6 @@ pub struct Heap {
     pub finalization_registrys: Vec<Option<FinalizationRegistryHeapData>>,
     pub globals: Vec<Value>,
     pub maps: Vec<Option<MapHeapData>>,
-    pub modules: Vec<Option<ModuleHeapData>>,
     pub numbers: Vec<Option<NumberHeapData>>,
     pub objects: Vec<Option<ObjectHeapData>>,
     pub primitive_objects: Vec<Option<PrimitiveObjectHeapData>>,
@@ -97,15 +97,21 @@ pub struct Heap {
     pub proxys: Vec<Option<ProxyHeapData>>,
     pub realms: Vec<Option<Realm>>,
     pub regexps: Vec<Option<RegExpHeapData>>,
-    pub scripts: Vec<Option<Script>>,
     pub sets: Vec<Option<SetHeapData>>,
     pub shared_array_buffers: Vec<Option<SharedArrayBufferHeapData>>,
-    pub strings: Vec<Option<StringHeapData>>,
     pub symbols: Vec<Option<SymbolHeapData>>,
     pub typed_arrays: Vec<Option<TypedArrayHeapData>>,
     pub weak_maps: Vec<Option<WeakMapHeapData>>,
     pub weak_refs: Vec<Option<WeakRefHeapData>>,
     pub weak_sets: Vec<Option<WeakSetHeapData>>,
+    pub modules: Vec<Option<ModuleHeapData>>,
+    pub scripts: Vec<Option<Script>>,
+    // Parsed ASTs referred by functions must be dropped after functions.
+    // These are held in the SourceCodeHeapData structs.
+    pub(crate) source_codes: Vec<Option<SourceCodeHeapData>>,
+    // But: Source code string data is in the string heap. We need to thus drop
+    // the strings only after the source ASTs drop.
+    pub strings: Vec<Option<StringHeapData>>,
 }
 
 pub trait CreateHeapData<T, F> {
@@ -160,6 +166,7 @@ impl Heap {
             embedder_objects: Vec::with_capacity(0),
             environments: Default::default(),
             errors: Vec::with_capacity(1024),
+            source_codes: Vec::with_capacity(0),
             finalization_registrys: Vec::with_capacity(0),
             globals: Vec::with_capacity(1024),
             maps: Vec::with_capacity(128),
diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs
index ddc460a7e..b289a3822 100644
--- a/nova_vm/src/heap/heap_bits.rs
+++ b/nova_vm/src/heap/heap_bits.rs
@@ -38,7 +38,7 @@ use crate::ecmascript::{
         DeclarativeEnvironmentIndex, EnvironmentIndex, FunctionEnvironmentIndex,
         GlobalEnvironmentIndex, ObjectEnvironmentIndex, RealmIdentifier,
     },
-    scripts_and_modules::script::ScriptIdentifier,
+    scripts_and_modules::{script::ScriptIdentifier, source_code::SourceCode},
     types::{
         bigint::HeapBigInt, HeapNumber, HeapString, OrdinaryObject, Symbol, Value,
         BUILTIN_STRINGS_LIST,
@@ -66,6 +66,7 @@ pub struct HeapBits {
     pub ecmascript_functions: Box<[bool]>,
     pub embedder_objects: Box<[bool]>,
     pub errors: Box<[bool]>,
+    pub source_codes: Box<[bool]>,
     pub finalization_registrys: Box<[bool]>,
     pub function_environments: Box<[bool]>,
     pub global_environments: Box<[bool]>,
@@ -112,6 +113,7 @@ pub(crate) struct WorkQueues {
     pub e_2_8: Vec<(ElementIndex, u32)>,
     pub ecmascript_functions: Vec<ECMAScriptFunction>,
     pub embedder_objects: Vec<EmbedderObject>,
+    pub source_codes: Vec<SourceCode>,
     pub errors: Vec<Error>,
     pub finalization_registrys: Vec<FinalizationRegistry>,
     pub function_environments: Vec<FunctionEnvironmentIndex>,
@@ -160,6 +162,7 @@ impl HeapBits {
         let ecmascript_functions = vec![false; heap.ecmascript_functions.len()];
         let embedder_objects = vec![false; heap.embedder_objects.len()];
         let errors = vec![false; heap.errors.len()];
+        let source_codes = vec![false; heap.source_codes.len()];
         let finalization_registrys = vec![false; heap.finalization_registrys.len()];
         let function_environments = vec![false; heap.environments.function.len()];
         let global_environments = vec![false; heap.environments.global.len()];
@@ -204,6 +207,7 @@ impl HeapBits {
             ecmascript_functions: ecmascript_functions.into_boxed_slice(),
             embedder_objects: embedder_objects.into_boxed_slice(),
             errors: errors.into_boxed_slice(),
+            source_codes: source_codes.into_boxed_slice(),
             finalization_registrys: finalization_registrys.into_boxed_slice(),
             function_environments: function_environments.into_boxed_slice(),
             global_environments: global_environments.into_boxed_slice(),
@@ -254,6 +258,7 @@ impl WorkQueues {
             ecmascript_functions: Vec::with_capacity(heap.ecmascript_functions.len() / 4),
             embedder_objects: Vec::with_capacity(heap.embedder_objects.len() / 4),
             errors: Vec::with_capacity(heap.errors.len() / 4),
+            source_codes: Vec::with_capacity(heap.source_codes.len() / 4),
             finalization_registrys: Vec::with_capacity(heap.finalization_registrys.len() / 4),
             function_environments: Vec::with_capacity(heap.environments.function.len() / 4),
             global_environments: Vec::with_capacity(heap.environments.global.len() / 4),
@@ -540,44 +545,45 @@ impl Default for CompactionListBuilder {
 }
 
 pub(crate) struct CompactionLists {
-    pub modules: CompactionList,
-    pub scripts: CompactionList,
-    pub realms: CompactionList,
+    pub array_buffers: CompactionList,
+    pub arrays: CompactionList,
+    pub bigints: CompactionList,
+    pub bound_functions: CompactionList,
+    pub builtin_functions: CompactionList,
+    pub data_views: CompactionList,
+    pub dates: CompactionList,
     pub declarative_environments: CompactionList,
-    pub function_environments: CompactionList,
-    pub global_environments: CompactionList,
-    pub object_environments: CompactionList,
-    pub e_2_4: CompactionList,
-    pub e_2_6: CompactionList,
-    pub e_2_8: CompactionList,
     pub e_2_10: CompactionList,
     pub e_2_12: CompactionList,
     pub e_2_16: CompactionList,
     pub e_2_24: CompactionList,
     pub e_2_32: CompactionList,
-    pub arrays: CompactionList,
-    pub array_buffers: CompactionList,
-    pub bigints: CompactionList,
-    pub bound_functions: CompactionList,
-    pub builtin_functions: CompactionList,
+    pub e_2_4: CompactionList,
+    pub e_2_6: CompactionList,
+    pub e_2_8: CompactionList,
     pub ecmascript_functions: CompactionList,
     pub embedder_objects: CompactionList,
-    pub data_views: CompactionList,
-    pub dates: CompactionList,
+    pub source_codes: CompactionList,
     pub errors: CompactionList,
     pub finalization_registrys: CompactionList,
+    pub function_environments: CompactionList,
+    pub global_environments: CompactionList,
     pub maps: CompactionList,
+    pub modules: CompactionList,
     pub numbers: CompactionList,
+    pub object_environments: CompactionList,
     pub objects: CompactionList,
     pub primitive_objects: CompactionList,
     pub promise_reaction_records: CompactionList,
     pub promise_resolving_functions: CompactionList,
     pub promises: CompactionList,
     pub proxys: CompactionList,
+    pub realms: CompactionList,
     pub regexps: CompactionList,
+    pub scripts: CompactionList,
     pub sets: CompactionList,
-    pub strings: CompactionList,
     pub shared_array_buffers: CompactionList,
+    pub strings: CompactionList,
     pub symbols: CompactionList,
     pub typed_arrays: CompactionList,
     pub weak_maps: CompactionList,
@@ -621,6 +627,7 @@ impl CompactionLists {
             builtin_functions: CompactionList::from_mark_bits(&bits.builtin_functions),
             ecmascript_functions: CompactionList::from_mark_bits(&bits.ecmascript_functions),
             embedder_objects: CompactionList::from_mark_bits(&bits.embedder_objects),
+            source_codes: CompactionList::from_mark_bits(&bits.source_codes),
             dates: CompactionList::from_mark_bits(&bits.dates),
             errors: CompactionList::from_mark_bits(&bits.errors),
             maps: CompactionList::from_mark_bits(&bits.maps),
diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs
index dedc27766..b366341be 100644
--- a/nova_vm/src/heap/heap_gc.rs
+++ b/nova_vm/src/heap/heap_gc.rs
@@ -44,7 +44,7 @@ use crate::ecmascript::{
         DeclarativeEnvironmentIndex, Environments, FunctionEnvironmentIndex,
         GlobalEnvironmentIndex, ObjectEnvironmentIndex, RealmIdentifier,
     },
-    scripts_and_modules::script::ScriptIdentifier,
+    scripts_and_modules::{script::ScriptIdentifier, source_code::SourceCode},
     types::{
         bigint::HeapBigInt, HeapNumber, HeapString, OrdinaryObject, Symbol, Value,
         BUILTIN_STRINGS_LIST,
@@ -106,6 +106,7 @@ pub fn heap_gc(heap: &mut Heap, root_realms: &mut [Option<RealmIdentifier>]) {
             embedder_objects,
             environments,
             errors,
+            source_codes,
             finalization_registrys,
             globals: _,
             maps,
@@ -323,6 +324,19 @@ pub fn heap_gc(heap: &mut Heap, root_realms: &mut [Option<RealmIdentifier>]) {
                 errors.get(index).mark_values(&mut queues);
             }
         });
+        let mut source_code_marks: Box<[SourceCode]> = queues.source_codes.drain(..).collect();
+        source_code_marks.sort();
+        source_code_marks.iter().for_each(|&idx| {
+            let index = idx.get_index();
+            if let Some(marked) = bits.source_codes.get_mut(index) {
+                if *marked {
+                    // Already marked, ignore
+                    return;
+                }
+                *marked = true;
+                source_codes.get(index).mark_values(&mut queues);
+            }
+        });
         let mut builtin_functions_marks: Box<[BuiltinFunction]> =
             queues.builtin_functions.drain(..).collect();
         builtin_functions_marks.sort();
@@ -812,6 +826,7 @@ fn sweep(heap: &mut Heap, bits: &HeapBits, root_realms: &mut [Option<RealmIdenti
         embedder_objects,
         environments,
         errors,
+        source_codes,
         finalization_registrys,
         globals,
         maps,
@@ -1132,6 +1147,11 @@ fn sweep(heap: &mut Heap, bits: &HeapBits, root_realms: &mut [Option<RealmIdenti
                 );
             });
         }
+        if !source_codes.is_empty() {
+            s.spawn(|| {
+                sweep_heap_vector_values(source_codes, &compactions, &bits.source_codes);
+            });
+        }
         if !strings.is_empty() {
             s.spawn(|| {
                 sweep_heap_vector_values(strings, &compactions, &bits.strings);


Test patch:
diff --git a/nova_vm/tests/garbage_collection_tests.rs b/nova_vm/tests/garbage_collection_tests.rs
index dace9604b..52348d478 100644
--- a/nova_vm/tests/garbage_collection_tests.rs
+++ b/nova_vm/tests/garbage_collection_tests.rs
@@ -6,9 +6,8 @@ use nova_vm::ecmascript::{
         Agent, DefaultHostHooks,
     },
     scripts_and_modules::script::{parse_script, script_evaluation},
-    types::{Object, Value},
+    types::{Object, String, Value},
 };
-use oxc_allocator::Allocator;
 
 fn initialize_global_object(agent: &mut Agent, global: Object) {
     use nova_vm::ecmascript::{
@@ -67,7 +66,6 @@ fn garbage_collection_tests() {
     let call_contents =
         fs::read_to_string(d.clone()).expect("Should have been able to read the file");
 
-    let allocator = Allocator::default();
     let mut agent = GcAgent::new(Options::default(), &DefaultHostHooks);
     let create_global_object: Option<fn(&mut Agent) -> Object> = None;
     let create_global_this_value: Option<fn(&mut Agent) -> Object> = None;
@@ -78,14 +76,8 @@ fn garbage_collection_tests() {
     );
     agent.run_in_realm(&realm, |agent| {
         let realm = agent.current_realm_id();
-        let script = parse_script(
-            &allocator,
-            header_contents.into_boxed_str(),
-            realm,
-            false,
-            None,
-        )
-        .unwrap();
+        let source_text = String::from_string(agent, header_contents);
+        let script = parse_script(agent, source_text, realm, false, None).unwrap();
         let _ = script_evaluation(agent, script).unwrap_or_else(|err| {
             panic!(
                 "Header evaluation failed: '{}' failed: {:?}",
@@ -99,14 +91,8 @@ fn garbage_collection_tests() {
     for i in 0..2 {
         agent.run_in_realm(&realm, |agent| {
             let realm = agent.current_realm_id();
-            let script = parse_script(
-                &allocator,
-                call_contents.clone().into_boxed_str(),
-                realm,
-                false,
-                None,
-            )
-            .unwrap();
+            let source_text = String::from_string(agent, call_contents.clone());
+            let script = parse_script(agent, source_text, realm, false, None).unwrap();
             let _ = script_evaluation(agent, script).unwrap_or_else(|err| {
                 println!("Error kind: {:?}", err.value());
                 panic!(
diff --git a/nova_vm/tests/object_prototype_tests.rs b/nova_vm/tests/object_prototype_tests.rs
index ef816f511..248caf683 100644
--- a/nova_vm/tests/object_prototype_tests.rs
+++ b/nova_vm/tests/object_prototype_tests.rs
@@ -10,8 +10,8 @@ use nova_vm::ecmascript::{
         DefaultHostHooks,
     },
     scripts_and_modules::script::{parse_script, script_evaluation},
+    types::String,
 };
-use oxc_allocator::Allocator;
 
 #[test]
 fn object_prototype_tests() {
@@ -25,13 +25,12 @@ fn object_prototype_tests() {
     .collect();
     let contents = fs::read_to_string(d.clone()).expect("Should have been able to read the file");
 
-    let allocator = Allocator::default();
     let mut agent = GcAgent::new(Options::default(), &DefaultHostHooks);
     let realm = agent.create_default_realm();
     agent.run_in_realm(&realm, |agent| {
         let realm = agent.current_realm_id();
-        let script =
-            parse_script(&allocator, contents.into_boxed_str(), realm, false, None).unwrap();
+        let source_text = String::from_string(agent, contents);
+        let script = parse_script(agent, source_text, realm, false, None).unwrap();
         let _ = script_evaluation(agent, script).unwrap_or_else(|err| {
             panic!(
                 "Test '{}' failed: {:?}",
diff --git a/tests/expectations.json b/tests/expectations.json
index 0100038b5..6319b9a2e 100644
--- a/tests/expectations.json
+++ b/tests/expectations.json
@@ -20510,8 +20510,6 @@
   "language/expressions/new/spread-sngl-iter.js": "CRASH",
   "language/expressions/new/spread-sngl-literal.js": "CRASH",
   "language/expressions/new/spread-sngl-obj-ident.js": "CRASH",
-  "language/expressions/object/11.1.5-0-1.js": "PASS",
-  "language/expressions/object/11.1.5-0-2.js": "FAIL",
   "language/expressions/object/__proto__-duplicate-computed.js": "CRASH",
   "language/expressions/object/__proto__-fn-name.js": "CRASH",
   "language/expressions/object/__proto__-permitted-dup-shorthand.js": "CRASH",