Skip to content

Commit

Permalink
Update modules code after the updates from goja
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mstoykov committed Oct 24, 2024
1 parent ca82c83 commit c101c49
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 9 deletions.
16 changes: 13 additions & 3 deletions modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
)

Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
Expand All @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions modules_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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
}
Expand Down
13 changes: 9 additions & 4 deletions modules_sourcetext.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
54 changes: 54 additions & 0 deletions modules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")

}
}

0 comments on commit c101c49

Please sign in to comment.