Browse Source

LibWeb: Make more JS modules actually run

First, we had a logic typo where we were checking parse errors for
non-empty instead of non-null. Fixing this caused more modules to
actually start executing.

As usual, this tripped on some "empty backup incumbent settings object
stack" bugs, so this patch also pushes a module execution context in
two places where it makes sense.

Co-Authored-By: networkException <networkexception@serenityos.org>
Andreas Kling 1 year ago
parent
commit
2d69a009fb

+ 20 - 6
Userland/Libraries/LibWeb/Bindings/MainThreadVM.cpp

@@ -480,18 +480,22 @@ ErrorOr<void> initialize_main_thread_vm()
             // 1. Let completion be null.
             // 1. Let completion be null.
             // NOTE: Our JS::Completion does not support non JS::Value types for its [[Value]], a such we
             // NOTE: Our JS::Completion does not support non JS::Value types for its [[Value]], a such we
             //       use JS::ThrowCompletionOr here.
             //       use JS::ThrowCompletionOr here.
+
+            auto& vm = realm.vm();
+            JS::GCPtr<JS::Module> module = nullptr;
+
             auto completion = [&]() -> JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Module>> {
             auto completion = [&]() -> JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Module>> {
                 // 2. If moduleScript is null, then set completion to Completion Record { [[Type]]: throw, [[Value]]: a new TypeError, [[Target]]: empty }.
                 // 2. If moduleScript is null, then set completion to Completion Record { [[Type]]: throw, [[Value]]: a new TypeError, [[Target]]: empty }.
                 if (!module_script) {
                 if (!module_script) {
                     return JS::throw_completion(JS::TypeError::create(realm, DeprecatedString::formatted("Loading imported module '{}' failed.", module_request.module_specifier)));
                     return JS::throw_completion(JS::TypeError::create(realm, DeprecatedString::formatted("Loading imported module '{}' failed.", module_request.module_specifier)));
                 }
                 }
                 // 3. Otherwise, if moduleScript's parse error is not null, then:
                 // 3. Otherwise, if moduleScript's parse error is not null, then:
-                else if (!module_script->parse_error().is_empty()) {
+                else if (!module_script->parse_error().is_null()) {
                     // 1. Let parseError be moduleScript's parse error.
                     // 1. Let parseError be moduleScript's parse error.
                     auto parse_error = module_script->parse_error();
                     auto parse_error = module_script->parse_error();
 
 
                     // 2. Set completion to Completion Record { [[Type]]: throw, [[Value]]: parseError, [[Target]]: empty }.
                     // 2. Set completion to Completion Record { [[Type]]: throw, [[Value]]: parseError, [[Target]]: empty }.
-                    return JS::throw_completion(parse_error);
+                    auto completion = JS::throw_completion(parse_error);
 
 
                     // 3. If loadState is not undefined and loadState.[[ParseError]] is null, set loadState.[[ParseError]] to parseError.
                     // 3. If loadState is not undefined and loadState.[[ParseError]] is null, set loadState.[[ParseError]] to parseError.
                     if (load_state) {
                     if (load_state) {
@@ -500,18 +504,28 @@ ErrorOr<void> initialize_main_thread_vm()
                             load_state_as_fetch_context.parse_error = parse_error;
                             load_state_as_fetch_context.parse_error = parse_error;
                         }
                         }
                     }
                     }
+
+                    return completion;
                 }
                 }
                 // 4. Otherwise, set completion to Completion Record { [[Type]]: normal, [[Value]]: result's record, [[Target]]: empty }.
                 // 4. Otherwise, set completion to Completion Record { [[Type]]: normal, [[Value]]: result's record, [[Target]]: empty }.
                 else {
                 else {
-                    auto* record = static_cast<HTML::JavaScriptModuleScript&>(*module_script).record();
-
-                    return JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Module>>(*record);
+                    module = static_cast<HTML::JavaScriptModuleScript&>(*module_script).record();
+                    return JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Module>>(*module);
                 }
                 }
             }();
             }();
 
 
             // 5. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion).
             // 5. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, completion).
-            HTML::TemporaryExecutionContext context { host_defined_environment_settings_object(realm) };
+            // NON-STANDARD: To ensure that LibJS can find the module on the stack, we push a new execution context.
+
+            auto module_execution_context = JS::ExecutionContext::create(realm.heap());
+            module_execution_context->realm = realm;
+            if (module)
+                module_execution_context->script_or_module = JS::NonnullGCPtr { *module };
+            vm.push_execution_context(*module_execution_context);
+
             JS::finish_loading_imported_module(referrer, module_request, payload, completion);
             JS::finish_loading_imported_module(referrer, module_request, payload, completion);
+
+            vm.pop_execution_context();
         });
         });
 
 
         // 13. Fetch a single imported module script given url, fetchClient, destination, fetchOptions, settingsObject, fetchReferrer,
         // 13. Fetch a single imported module script given url, fetchClient, destination, fetchOptions, settingsObject, fetchReferrer,

+ 9 - 0
Userland/Libraries/LibWeb/HTML/Scripting/ModuleScript.cpp

@@ -147,6 +147,12 @@ JS::Promise* JavaScriptModuleScript::run(PreventErrorReporting)
         auto record = m_record;
         auto record = m_record;
         VERIFY(record);
         VERIFY(record);
 
 
+        // NON-STANDARD: To ensure that LibJS can find the module on the stack, we push a new execution context.
+        auto module_execution_context = JS::ExecutionContext::create(heap());
+        module_execution_context->realm = &settings.realm();
+        module_execution_context->script_or_module = JS::NonnullGCPtr<JS::Module> { *record };
+        vm().push_execution_context(*module_execution_context);
+
         // 2. Set evaluationPromise to record.Evaluate().
         // 2. Set evaluationPromise to record.Evaluate().
         auto elevation_promise_or_error = record->evaluate(vm());
         auto elevation_promise_or_error = record->evaluate(vm());
 
 
@@ -161,6 +167,9 @@ JS::Promise* JavaScriptModuleScript::run(PreventErrorReporting)
         } else {
         } else {
             evaluation_promise = elevation_promise_or_error.value();
             evaluation_promise = elevation_promise_or_error.value();
         }
         }
+
+        // NON-STANDARD: Pop the execution context mentioned above.
+        vm().pop_execution_context();
     }
     }
 
 
     // FIXME: 7. If preventErrorReporting is false, then upon rejection of evaluationPromise with reason, report the exception given by reason for script.
     // FIXME: 7. If preventErrorReporting is false, then upon rejection of evaluationPromise with reason, report the exception given by reason for script.