From c101c4966738b8f48cdd1a9a850c6e97e052bdef Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Thu, 24 Oct 2024 16:50:28 +0300 Subject: [PATCH] Update modules code after the updates from goja This likely isn't enough, but I can't tease out a case where this isn't working, so I guess it is good enough for now. --- modules.go | 16 ++++++++--- modules_integration_test.go | 4 +-- modules_sourcetext.go | 13 ++++++--- modules_test.go | 54 +++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/modules.go b/modules.go index e570166..b21bf10 100644 --- a/modules.go +++ b/modules.go @@ -48,7 +48,7 @@ type ( CyclicModuleInstance interface { ModuleInstance HasTLA() bool - ExecuteModule(rt *Runtime, res, rej func(interface{})) (CyclicModuleInstance, error) + ExecuteModule(rt *Runtime, res, rej func(interface{}) error) (CyclicModuleInstance, error) } ) @@ -155,7 +155,16 @@ func newEvaluationState() *evaluationState { } // TODO have resolve as part of runtime -func (r *Runtime) CyclicModuleRecordEvaluate(c CyclicModuleRecord, resolve HostResolveImportedModuleFunc) *Promise { +func (r *Runtime) CyclicModuleRecordEvaluate(c CyclicModuleRecord, resolve HostResolveImportedModuleFunc) (promise *Promise) { + defer func() { + if x := recover(); x != nil { + if ex := asUncatchableException(x); ex != nil { + r.evaluationState.topLevelCapability[c].reject(r.ToValue(ex)) + } else { + panic(x) + } + } + }() if r.modules == nil { r.modules = make(map[ModuleRecord]ModuleInstance) } @@ -168,6 +177,7 @@ func (r *Runtime) CyclicModuleRecordEvaluate(c CyclicModuleRecord, resolve HostR return cap.promise.Export().(*Promise) } capability := r.newPromiseCapability(r.getPromise()) + promise = capability.promise.Export().(*Promise) r.evaluationState.topLevelCapability[c] = capability state := r.evaluationState _, err := r.innerModuleEvaluation(state, c, &stackInstance, 0, resolve) @@ -186,7 +196,7 @@ func (r *Runtime) CyclicModuleRecordEvaluate(c CyclicModuleRecord, resolve HostR if len(r.vm.callStack) == 0 { r.leave() } - return state.topLevelCapability[c].promise.Export().(*Promise) + return } func (r *Runtime) innerModuleEvaluation( diff --git a/modules_integration_test.go b/modules_integration_test.go index 0062e17..e19419d 100644 --- a/modules_integration_test.go +++ b/modules_integration_test.go @@ -333,7 +333,7 @@ func (s *cyclicModuleImpl) ResolveExport(exportName string, resolveset ...sobek. } func (s *cyclicModuleImpl) GetExportedNames(callback func([]string), records ...sobek.ModuleRecord) bool { - result := make([]string, len(s.exports)) + result := make([]string, 0, len(s.exports)) for k := range s.exports { result = append(result, k) } @@ -351,7 +351,7 @@ func (si *cyclicModuleInstanceImpl) HasTLA() bool { return false } -func (si *cyclicModuleInstanceImpl) ExecuteModule(rt *sobek.Runtime, res, rej func(interface{})) (sobek.CyclicModuleInstance, error) { +func (si *cyclicModuleInstanceImpl) ExecuteModule(rt *sobek.Runtime, _, _ func(interface{}) error) (sobek.CyclicModuleInstance, error) { si.rt = rt return si, nil } diff --git a/modules_sourcetext.go b/modules_sourcetext.go index 4442173..f92223f 100644 --- a/modules_sourcetext.go +++ b/modules_sourcetext.go @@ -22,7 +22,7 @@ type SourceTextModuleInstance struct { asyncPromise *Promise } -func (s *SourceTextModuleInstance) ExecuteModule(rt *Runtime, res, rej func(interface{})) (CyclicModuleInstance, error) { +func (s *SourceTextModuleInstance) ExecuteModule(rt *Runtime, res, rej func(interface{}) error) (CyclicModuleInstance, error) { promiseP := s.pcap.promise.self.(*Promise) if len(promiseP.fulfillReactions) == 1 { ar := promiseP.fulfillReactions[0].asyncRunner @@ -52,12 +52,17 @@ func (s *SourceTextModuleInstance) ExecuteModule(rt *Runtime, res, rej func(inte panic("sobek bug where an async module was not executed as async") } rt.performPromiseThen(s.asyncPromise, rt.ToValue(func(call FunctionCall) Value { - // fmt.Println("!!!!res") - res(call.Argument(0)) + err := res(call.Argument(0)) + if err != nil { + panic(err) + } return nil }), rt.ToValue(func(call FunctionCall) Value { v := call.Argument(0) - rej(rt.vm.exceptionFromValue(v)) + err := rej(rt.vm.exceptionFromValue(v)) + if err != nil { + panic(err) + } return nil }), nil) return nil, nil diff --git a/modules_test.go b/modules_test.go index aa45832..14d39b1 100644 --- a/modules_test.go +++ b/modules_test.go @@ -475,3 +475,57 @@ func TestModuleAsyncErrorAndPromiseRejection(t *testing.T) { t.Fatalf("zero unhandled exceptions were expected but there were some %+v", unhandledRejectedPromises) } } + +func TestModuleAsyncInterrupt(t *testing.T) { + t.Parallel() + fn := runModules(t, map[string]string{ + `a.js`: ` + import { s } from "dep.js" + s(); + `, + `dep.js`: ` + await 5; + export function s() { + interrupt(); + let buf = ""; + for (let i = 0; i < 10000; i++) { + buf += "a" + "a"; + } + badcall(); + } + `, + }) + + rt := New() + var shouldntHappen bool + rt.Set("interrupt", rt.ToValue(func() { rt.Interrupt("the error we want") })) + rt.Set("badcall", rt.ToValue(func() { shouldntHappen = true })) + + unhandledRejectedPromises := make(map[*Promise]struct{}) + rt.promiseRejectionTracker = func(p *Promise, operation PromiseRejectionOperation) { + switch operation { + case PromiseRejectionReject: + unhandledRejectedPromises[p] = struct{}{} + case PromiseRejectionHandle: + delete(unhandledRejectedPromises, p) + } + } + promise := fn(rt) + rt.performPromiseThen(promise, rt.ToValue(func() {}), rt.ToValue(func() {}), nil) + if promise.state != PromiseStateRejected { + t.Fatalf("expected promise to be rejected %q", promise.state) + } + exc := promise.Result().Export().(*InterruptedError) + expValue := "the error we want\n\tat s (dep.js:4:14(3))\n\tat a.js:3:5(10)\n" + if exc.String() != expValue { + t.Fatalf("Expected values %q but got %q", expValue, exc.String()) + } + + if len(unhandledRejectedPromises) != 0 { + t.Fatalf("zero unhandled exceptions were expected but there were some %+v", unhandledRejectedPromises) + } + if shouldntHappen { + t.Fatal("code was supposed to be interrupted but that din't work") + + } +}