修复并发执行JsCode时导致的goja map冲突

This commit is contained in:
Star 2025-07-10 23:17:41 +08:00
parent 08092112ee
commit e8219eb1ec
3 changed files with 62 additions and 14 deletions

View File

@ -241,7 +241,6 @@ func go2js(in any, n int) any {
if err, ok := outValue.Interface().(error); ok { if err, ok := outValue.Interface().(error); ok {
panic(vm.NewGoError(err)) panic(vm.NewGoError(err))
} else if err, ok := outValue.Interface().(*u.Err); ok { } else if err, ok := outValue.Interface().(*u.Err); ok {
// panic(vm.NewGoError(errors.New(err.Message+"|:|"+u.Json(err.Stack))))
panic(vm.NewGoError(err)) panic(vm.NewGoError(err))
} else { } else {
panic(vm.NewGoError(errors.New(u.String(outValue.Interface())))) panic(vm.NewGoError(errors.New(u.String(outValue.Interface()))))
@ -253,6 +252,20 @@ func go2js(in any, n int) any {
// 如果返回Struct自动转换为Map首字母小写 // 如果返回Struct自动转换为Map首字母小写
if outType.Kind() == reflect.Struct || (outType.Kind() == reflect.Ptr && outType.Elem().Kind() == reflect.Struct) { if outType.Kind() == reflect.Struct || (outType.Kind() == reflect.Ptr && outType.Elem().Kind() == reflect.Struct) {
outs = append(outs, ToMap(outValue.Interface())) outs = append(outs, ToMap(outValue.Interface()))
} else if outType.Kind() == reflect.Slice && outType.Elem().Kind() == reflect.Struct {
// 如果返回[]Struct自动转换为[]Map首字母小写
sliceMap := make([]map[string]any, 0)
for i := 0; i < outValue.Len(); i++ {
sliceMap = append(sliceMap, ToMap(outValue.Index(i).Interface()))
}
outs = append(outs, sliceMap)
} else if outType.Kind() == reflect.Map && outType.Elem().Kind() == reflect.Struct {
// 如果返回map[string]Struct自动转换为map[string]Map首字母小写
mapMap := make(map[string]map[string]any)
for _, key := range outValue.MapKeys() {
mapMap[key.String()] = ToMap(outValue.MapIndex(key).Interface())
}
outs = append(outs, mapMap)
} else { } else {
outs = append(outs, outValue.Interface()) outs = append(outs, outValue.Interface())
} }

View File

@ -559,8 +559,11 @@ func (r *Runtime) NewGoError(err error) *Object {
if strings.Contains(stack, "/goja/") { if strings.Contains(stack, "/goja/") {
continue continue
} }
if strings.Contains(stack, "/gojs") { if strings.Contains(stack, "/gojs@") {
stack = "**" + stack[strings.LastIndex(stack, "/gojs"):] stack = "**" + stack[strings.LastIndex(stack, "/gojs@"):]
}
if strings.Contains(stack, "/gojs/") {
stack = "**" + stack[strings.LastIndex(stack, "/gojs/"):]
} }
callStacks = append(callStacks, stack) callStacks = append(callStacks, stack)
} }
@ -582,15 +585,19 @@ func (r *Runtime) NewGoError(err error) *Object {
if strings.Contains(file, "/goja/") { if strings.Contains(file, "/goja/") {
continue continue
} }
if strings.Contains(file, "/gojs") { if strings.Contains(file, "/gojs@") {
file = "**" + file[strings.LastIndex(file, "/gojs"):] file = "**" + file[strings.LastIndex(file, "/gojs@"):]
} else if strings.Contains(file, "/gojs/") {
file = "**" + file[strings.LastIndex(file, "/gojs/"):]
} else if strings.Contains(file, "/ssgo") { } else if strings.Contains(file, "/ssgo") {
file = "**" + file[strings.LastIndex(file, "/ssgo"):] file = "**" + file[strings.LastIndex(file, "/ssgo"):]
} }
callStacks = append(callStacks, fmt.Sprintf("%s:%d", file, line)) callStacks = append(callStacks, fmt.Sprintf("%s:%d", file, line))
} }
} }
e := r.newError(r.getGoError(), err.Error()+"|:|"+u.Json(callStacks)).(*Object) errStr := err.Error() + "|:|" + u.Json(callStacks)
// errStr = strings.ReplaceAll(errStr, "GoError: ", "")
e := r.newError(r.getGoError(), errStr).(*Object)
e.Set("value", err) e.Set("value", err)
return e return e
} }

44
gojs.go
View File

@ -79,27 +79,33 @@ func MakeError(err error) error {
if err == nil { if err == nil {
return nil return nil
} }
var newErr *Error
if gojsErr, ok := err.(*Error); ok { if gojsErr, ok := err.(*Error); ok {
return gojsErr newErr = gojsErr
} else if gojaErr, ok := err.(*goja.Exception); ok { } else if gojaErr, ok := err.(*goja.Exception); ok {
stack := make([]string, len(gojaErr.Stack())) stack := make([]string, len(gojaErr.Stack()))
for i, v := range gojaErr.Stack() { for i, v := range gojaErr.Stack() {
stack[i] = fmt.Sprintf("%s @%s", v.Position().String(), v.FuncName()) stack[i] = fmt.Sprintf("%s @%s", v.Position().String(), v.FuncName())
} }
msg, fullMsg, stack1 := parseMessageAndStack(gojaErr.Value().String(), stack) msg, fullMsg, stack1 := parseMessageAndStack(gojaErr.Value().String(), stack)
return &Error{Message: msg, fullMessage: fullMsg, Stack: stack1, err: err, exceprion: gojaErr} newErr = &Error{Message: msg, fullMessage: fullMsg, Stack: stack1, err: err, exceprion: gojaErr}
} else { } else {
msg, fullMsg, stack := parseMessageAndStack(err.Error(), []string{}) msg, fullMsg, stack := parseMessageAndStack(err.Error(), []string{})
return &Error{Message: msg, fullMessage: fullMsg, Stack: stack, err: err, exceprion: nil} newErr = &Error{Message: msg, fullMessage: fullMsg, Stack: stack, err: err, exceprion: nil}
} }
// newErr.Message = strings.ReplaceAll(newErr.Message, "GoError: ", "")
newErr.fullMessage = strings.ReplaceAll(newErr.fullMessage, "GoError: ", "")
return newErr
} }
func parseMessageAndStack(msg string, stack []string) (string, string, []string) { func parseMessageAndStack(msg string, stack []string) (string, string, []string) {
if strings.Contains(msg, "|:|") { if strings.Contains(msg, "|:|") {
a := strings.SplitN(msg, "|:|", 2) a := strings.Split(msg, "|:|")
stack1 := []string{} for i := 1; i < len(a); i++ {
u.UnJson(a[1], &stack1) stack1 := []string{}
stack = append(stack, stack1...) u.UnJson(a[i], &stack1)
stack = append(stack, stack1...)
}
fullMsg := a[0] fullMsg := a[0]
if len(stack) > 0 { if len(stack) > 0 {
fullMsg += "\n" + strings.Join(stack, "\n") fullMsg += "\n" + strings.Join(stack, "\n")
@ -107,7 +113,7 @@ func parseMessageAndStack(msg string, stack []string) (string, string, []string)
if curDir, err := os.Getwd(); err == nil && curDir != "" { if curDir, err := os.Getwd(); err == nil && curDir != "" {
fullMsg = strings.ReplaceAll(fullMsg, curDir, ".") fullMsg = strings.ReplaceAll(fullMsg, curDir, ".")
} }
fullMsg = strings.ReplaceAll(fullMsg, "SyntaxError: SyntaxError:", "SyntaxError:") // fullMsg = strings.ReplaceAll(fullMsg, "SyntaxError: SyntaxError:", "SyntaxError:")
return a[0], fullMsg, stack return a[0], fullMsg, stack
} }
return msg, msg, []string{} return msg, msg, []string{}
@ -301,10 +307,12 @@ type Runtime struct {
} }
func (rt *Runtime) lock() { func (rt *Runtime) lock() {
// fmt.Println(u.Cyan(">>>>Lock"), u.BCyan(u.String(rt.GetGoData("vmId"))))
rt.vm.Locker.Lock() rt.vm.Locker.Lock()
} }
func (rt *Runtime) unlock() { func (rt *Runtime) unlock() {
// fmt.Println(u.Green("\u200B <<<<UnLock"), u.BGreen(u.String(rt.GetGoData("vmId"))))
rt.vm.Locker.Unlock() rt.vm.Locker.Unlock()
} }
@ -328,6 +336,8 @@ func (rt *Runtime) GetCallStack() []string {
return callStacks return callStacks
} }
var jsCodeLock = sync.Mutex{} // goja有bugvm已经lock了当还是会报错 fatal error: concurrent map read and map write @object_gomap.go:45 vm.go:2225
func (rt *Runtime) requireMod(modName, realModName string, inVM bool) error { func (rt *Runtime) requireMod(modName, realModName string, inVM bool) error {
if rt.required[modName] { if rt.required[modName] {
return nil return nil
@ -350,7 +360,9 @@ func (rt *Runtime) requireMod(modName, realModName string, inVM bool) error {
err = rt.vm.Set(realModName, mod.Object) err = rt.vm.Set(realModName, mod.Object)
} }
if mod.JsCode != "" { if mod.JsCode != "" {
jsCodeLock.Lock()
_, err = rt.SafeRunString(strings.ReplaceAll(mod.JsCode, "$MOD$.", realModName+".")) _, err = rt.SafeRunString(strings.ReplaceAll(mod.JsCode, "$MOD$.", realModName+"."))
jsCodeLock.Unlock()
} }
if !inVM { if !inVM {
rt.unlock() rt.unlock()
@ -495,6 +507,9 @@ func (rt *Runtime) StartFromFile(file string) error {
} }
} }
var vmId = 0
var vmIdLock = sync.Mutex{}
func (rt *Runtime) StartFromCode(code, refFile string) error { func (rt *Runtime) StartFromCode(code, refFile string) error {
if refFile != "" { if refFile != "" {
rt.file = refFile rt.file = refFile
@ -507,6 +522,12 @@ func (rt *Runtime) StartFromCode(code, refFile string) error {
rt.file = absFile rt.file = absFile
} }
refPath := filepath.Dir(refFile) refPath := filepath.Dir(refFile)
vmIdLock.Lock()
vmId++
vmId1 := vmId
vmIdLock.Unlock()
rt.SetGoData("vmId", u.String(vmId1))
rt.SetGoData("startFile", refFile) rt.SetGoData("startFile", refFile)
rt.SetGoData("startPath", refPath) rt.SetGoData("startPath", refPath)
rt.vm.Set("__startFile", refFile) rt.vm.Set("__startFile", refFile)
@ -614,6 +635,13 @@ func (rt *Runtime) StartFromProgram(prg *Program) error {
if modErr != nil { if modErr != nil {
return modErr return modErr
} }
vmIdLock.Lock()
vmId++
vmId1 := vmId
vmIdLock.Unlock()
rt.SetGoData("vmId", u.String(vmId1))
rt.SetGoData("vmId", u.String(vmId1))
rt.SetGoData("startFile", prg.startFile) rt.SetGoData("startFile", prg.startFile)
rt.SetGoData("startPath", filepath.Dir(prg.startFile)) rt.SetGoData("startPath", filepath.Dir(prg.startFile))
rt.lock() rt.lock()