diff --git a/server/runtime_javascript.go b/server/runtime_javascript.go index 8b661b9f29..5532236baa 100644 --- a/server/runtime_javascript.go +++ b/server/runtime_javascript.go @@ -2270,7 +2270,7 @@ func (rp *RuntimeProviderJS) StorageIndexFilter(ctx context.Context, indexName s if jsFn == "" { rp.Put(r) rp.logger.Error("JavaScript runtime function invalid.", zap.String("key", jsFn), zap.Error(err)) - return false, errors.New("Could not run Subscription Notification Google hook.") + return false, errors.New("Could not run Storage Index Filter hook.") } fn, ok := goja.AssertFunction(r.vm.Get(jsFn)) @@ -2284,7 +2284,7 @@ func (rp *RuntimeProviderJS) StorageIndexFilter(ctx context.Context, indexName s if err != nil { rp.Put(r) rp.logger.Error("Could not instantiate js logger.", zap.Error(err)) - return false, errors.New("Could not run Subscription Notification Google hook.") + return false, errors.New("Could not run Storage Index Filter hook.") } objectMap := make(map[string]interface{}, 7) @@ -2345,7 +2345,7 @@ func evalRuntimeModules(rp *RuntimeProviderJS, modCache *RuntimeJSModuleCache, m } modName := modCache.Names[0] - initializer := NewRuntimeJavascriptInitModule(logger, storageIndex, callbacks, matchHandlers, announceCallbackFn) + initializer := NewRuntimeJavascriptInitModule(logger, modCache.Modules[modName].Ast, storageIndex, callbacks, matchHandlers, announceCallbackFn) init, err := initializer.Constructor(r) if err != nil { return nil, err diff --git a/server/runtime_javascript_init.go b/server/runtime_javascript_init.go index 3dbacb11a2..44d603a747 100644 --- a/server/runtime_javascript_init.go +++ b/server/runtime_javascript_init.go @@ -22,11 +22,14 @@ import ( "sync" "github.com/dop251/goja" + "github.com/dop251/goja/ast" "go.uber.org/zap" ) const INIT_MODULE_FN_NAME = "InitModule" +var inlinedFunctionError = errors.New("function literal found: javascript functions cannot be inlined") + type RuntimeJavascriptMatchHandlers struct { sync.RWMutex mapping map[string]*jsMatchHandlers @@ -76,17 +79,19 @@ type RuntimeJavascriptInitModule struct { Logger *zap.Logger Callbacks *RuntimeJavascriptCallbacks MatchCallbacks *RuntimeJavascriptMatchHandlers - storageIndex StorageIndex announceCallbackFn func(RuntimeExecutionMode, string) + storageIndex StorageIndex + ast *ast.Program } -func NewRuntimeJavascriptInitModule(logger *zap.Logger, storageIndex StorageIndex, callbacks *RuntimeJavascriptCallbacks, matchCallbacks *RuntimeJavascriptMatchHandlers, announceCallbackFn func(RuntimeExecutionMode, string)) *RuntimeJavascriptInitModule { +func NewRuntimeJavascriptInitModule(logger *zap.Logger, ast *ast.Program, storageIndex StorageIndex, callbacks *RuntimeJavascriptCallbacks, matchCallbacks *RuntimeJavascriptMatchHandlers, announceCallbackFn func(RuntimeExecutionMode, string)) *RuntimeJavascriptInitModule { return &RuntimeJavascriptInitModule{ Logger: logger, storageIndex: storageIndex, announceCallbackFn: announceCallbackFn, Callbacks: callbacks, MatchCallbacks: matchCallbacks, + ast: ast, } } @@ -279,29 +284,25 @@ func (im *RuntimeJavascriptInitModule) registerRpc(r *goja.Runtime) func(goja.Fu if goja.IsNull(fName) || goja.IsUndefined(fName) { panic(r.NewTypeError("expects a non empty string")) } - key := fName.String() + key, ok := fName.Export().(string) + if !ok { + panic(r.NewTypeError("expects a non empty string")) + } if key == "" { panic(r.NewTypeError("expects a non empty string")) } fn := f.Argument(1) - _, ok := goja.AssertFunction(fn) + _, ok = goja.AssertFunction(fn) if !ok { panic(r.NewTypeError("expects a function")) } - fnObj, ok := fn.(*goja.Object) - if !ok { - panic(r.NewTypeError("expects an object")) - } - - v := fnObj.Get("name") - if v == nil || v.String() == "" { - panic(r.NewTypeError("function key could not be extracted: cannot register an anonymous function")) + fnKey, err := im.extractRpcFn(r, key) + if err != nil { + panic(r.NewGoError(err)) } - fnKey := strings.Clone(v.String()) - lKey := strings.ToLower(key) im.registerCallbackFn(RuntimeExecutionModeRPC, lKey, fnKey) im.announceCallbackFn(RuntimeExecutionModeRPC, lKey) @@ -310,625 +311,779 @@ func (im *RuntimeJavascriptInitModule) registerRpc(r *goja.Runtime) func(goja.Fu } } +func (im *RuntimeJavascriptInitModule) extractRpcFn(r *goja.Runtime, rpcFnName string) (string, error) { + bs, initFnVarName, err := im.getInitModuleFn() + if err != nil { + return "", err + } + + globalFnId, err := im.getRegisteredRpcFnIdentifier(r, bs, initFnVarName, rpcFnName) + if err != nil { + return "", fmt.Errorf("js %s function key could not be extracted: %s", rpcFnName, err.Error()) + } + + return globalFnId, nil +} + +func (im *RuntimeJavascriptInitModule) extractStorageIndexFilterFn(r *goja.Runtime, indexName string) (string, error) { + bs, initFnVarName, err := im.getInitModuleFn() + if err != nil { + return "", err + } + + globalFnId, err := im.getRegisteredFnIdentifier(r, bs, initFnVarName, indexName, "registerStorageIndexFilter") + if err != nil { + return "", fmt.Errorf("js %s function key could not be extracted: %s", indexName, err.Error()) + } + + return globalFnId, nil +} + +func (im *RuntimeJavascriptInitModule) getRegisteredRpcFnIdentifier(r *goja.Runtime, bs *ast.BlockStatement, initFnVarName, rpcFnName string) (string, error) { + return im.getRegisteredFnIdentifier(r, bs, initFnVarName, rpcFnName, "registerRpc") +} + +func (im *RuntimeJavascriptInitModule) getRegisteredFnIdentifier(r *goja.Runtime, bs *ast.BlockStatement, initFnVarName, rpcFnName, registerFnName string) (string, error) { + for _, exp := range bs.List { + if try, ok := exp.(*ast.TryStatement); ok { + if s, err := im.getRegisteredRpcFnIdentifier(r, try.Body, initFnVarName, rpcFnName); err != nil { + continue + } else { + return s, nil + } + } + if expStat, ok := exp.(*ast.ExpressionStatement); ok { + if callExp, ok := expStat.Expression.(*ast.CallExpression); ok { + if callee, ok := callExp.Callee.(*ast.DotExpression); ok { + if callee.Left.(*ast.Identifier).Name.String() == initFnVarName && callee.Identifier.Name.String() == registerFnName { + if modNameArg, ok := callExp.ArgumentList[0].(*ast.Identifier); ok { + id := modNameArg.Name.String() + if r.Get(id).String() != rpcFnName { + continue + } + } else if modNameArg, ok := callExp.ArgumentList[0].(*ast.StringLiteral); ok { + if modNameArg.Value.String() != rpcFnName { + continue + } + } + + if modNameArg, ok := callExp.ArgumentList[1].(*ast.Identifier); ok { + return modNameArg.Name.String(), nil + } else if modNameArg, ok := callExp.ArgumentList[1].(*ast.StringLiteral); ok { + return modNameArg.Value.String(), nil + } else { + return "", inlinedFunctionError + } + } + } + } + } + } + + return "", errors.New("not found") +} + func (im *RuntimeJavascriptInitModule) registerBeforeGetAccount(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "getaccount") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeGetAccount", "getaccount") } func (im *RuntimeJavascriptInitModule) registerAfterGetAccount(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "getaccount") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterGetAccount", "getaccount") } func (im *RuntimeJavascriptInitModule) registerBeforeUpdateAccount(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "updateaccount") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUpdateAccount", "updateaccount") } func (im *RuntimeJavascriptInitModule) registerAfterUpdateAccount(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "updateaccount") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUpdateAccount", "updateaccount") } func (im *RuntimeJavascriptInitModule) registerBeforeDeleteAccount(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "deleteaccount") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeDeleteAccount", "deleteaccount") } func (im *RuntimeJavascriptInitModule) registerAfterDeleteAccount(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "deleteaccount") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterDeleteAccount", "deleteaccount") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticateapple") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateApple", "authenticateapple") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticateapple") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateApple", "authenticateapple") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateCustom(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticatecustom") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateCustom", "authenticatecustom") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateCustom(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticatecustom") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateCustom", "authenticatecustom") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateDevice(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticatedevice") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateDevice", "authenticatedevice") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateDevice(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticatedevice") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateDevice", "authenticatedevice") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateEmail(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticateemail") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateEmail", "authenticateemail") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateEmail(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticateemail") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateEmail", "authenticateemail") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateFacebook(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticatefacebook") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateFacebook", "authenticatefacebook") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateFacebook(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticatefacebook") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateFacebook", "authenticatefacebook") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateFacebookInstantGame(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticatefacebookinstantgame") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateFacebookInstantGame", "authenticatefacebookinstantgame") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateFacebookInstantGame(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticatefacebookinstantgame") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateFacebookInstantGame", "authenticatefacebookinstantgame") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateGameCenter(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticategamecenter") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateGameCenter", "authenticategamecenter") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateGameCenter(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticategamecenter") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateGameCenter", "authenticategamecenter") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticategoogle") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateGoogle", "authenticategoogle") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticategoogle") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateGoogle", "authenticategoogle") } func (im *RuntimeJavascriptInitModule) registerBeforeAuthenticateSteam(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "authenticatesteam") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAuthenticateSteam", "authenticatesteam") } func (im *RuntimeJavascriptInitModule) registerAfterAuthenticateSteam(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "authenticatesteam") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAuthenticateSteam", "authenticatesteam") } func (im *RuntimeJavascriptInitModule) registerBeforeListChannelMessages(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listchannelmessages") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListChannelMessages", "listchannelmessages") } func (im *RuntimeJavascriptInitModule) registerAfterListChannelMessages(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listchannelmessages") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListChannelMessages", "listchannelmessages") } func (im *RuntimeJavascriptInitModule) registerBeforeListFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listfriends") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListFriends", "listfriends") } func (im *RuntimeJavascriptInitModule) registerAfterListFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listfriends") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListFriends", "listfriends") } func (im *RuntimeJavascriptInitModule) registerBeforeAddFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "addfriends") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAddFriends", "addfriends") } func (im *RuntimeJavascriptInitModule) registerAfterAddFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "addfriends") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAddFriends", "addfriends") } func (im *RuntimeJavascriptInitModule) registerBeforeDeleteFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "deletefriends") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeDeleteFriends", "deletefriends") } func (im *RuntimeJavascriptInitModule) registerAfterDeleteFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "deletefriends") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterDeleteFriends", "deletefriends") } func (im *RuntimeJavascriptInitModule) registerBeforeBlockFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "blockfriends") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeBlockFriends", "blockfriends") } func (im *RuntimeJavascriptInitModule) registerAfterBlockFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "blockfriends") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterBlockFriends", "blockfriends") } func (im *RuntimeJavascriptInitModule) registerBeforeImportFacebookFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "importfacebookfriends") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeImportFacebookFriends", "importfacebookfriends") } func (im *RuntimeJavascriptInitModule) registerAfterImportFacebookFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "importfacebookfriends") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterImportFacebookFriends", "importfacebookfriends") } func (im *RuntimeJavascriptInitModule) registerBeforeImportSteamFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "importsteamfriends") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeImportSteamFriends", "importsteamfriends") } func (im *RuntimeJavascriptInitModule) registerAfterImportSteamFriends(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "importsteamfriends") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterImportSteamFriends", "importsteamfriends") } func (im *RuntimeJavascriptInitModule) registerBeforeCreateGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "creategroup") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeCreateGroup", "creategroup") } func (im *RuntimeJavascriptInitModule) registerAfterCreateGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "creategroup") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterCreateGroup", "creategroup") } func (im *RuntimeJavascriptInitModule) registerBeforeUpdateGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "updategroup") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUpdateGroup", "updategroup") } func (im *RuntimeJavascriptInitModule) registerAfterUpdateGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "updategroup") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUpdateGroup", "updategroup") } func (im *RuntimeJavascriptInitModule) registerBeforeDeleteGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "deletegroup") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeDeleteGroup", "deletegroup") } func (im *RuntimeJavascriptInitModule) registerAfterDeleteGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "deletegroup") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterDeleteGroup", "deletegroup") } func (im *RuntimeJavascriptInitModule) registerBeforeJoinGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "joingroup") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeJoinGroup", "joingroup") } func (im *RuntimeJavascriptInitModule) registerAfterJoinGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "joingroup") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterJoinGroup", "joingroup") } func (im *RuntimeJavascriptInitModule) registerBeforeLeaveGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "leavegroup") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLeaveGroup", "leavegroup") } func (im *RuntimeJavascriptInitModule) registerAfterLeaveGroup(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "leavegroup") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLeaveGroup", "leavegroup") } func (im *RuntimeJavascriptInitModule) registerBeforeAddGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "addgroupusers") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeAddGroupUsers", "addgroupusers") } func (im *RuntimeJavascriptInitModule) registerAfterAddGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "addgroupusers") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterAddGroupUsers", "addgroupusers") } func (im *RuntimeJavascriptInitModule) registerBeforeBanGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "bangroupusers") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeBanGroupUsers", "bangroupusers") } func (im *RuntimeJavascriptInitModule) registerAfterBanGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "bangroupusers") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterBanGroupUsers", "bangroupusers") } func (im *RuntimeJavascriptInitModule) registerBeforeKickGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "kickgroupusers") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeKickGroupUsers", "kickgroupusers") } func (im *RuntimeJavascriptInitModule) registerAfterKickGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "kickgroupusers") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterKickGroupUsers", "kickgroupusers") } func (im *RuntimeJavascriptInitModule) registerBeforePromoteGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "promotegroupusers") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforePromoteGroupUsers", "promotegroupusers") } func (im *RuntimeJavascriptInitModule) registerAfterPromoteGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "promotegroupusers") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterPromoteGroupUsers", "promotegroupusers") } func (im *RuntimeJavascriptInitModule) registerBeforeDemoteGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "demotegroupusers") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeDemoteGroupUsers", "demotegroupusers") } func (im *RuntimeJavascriptInitModule) registerAfterDemoteGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "demotegroupusers") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterDemoteGroupUsers", "demotegroupusers") } func (im *RuntimeJavascriptInitModule) registerBeforeListGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listgroupusers") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListGroupUsers", "listgroupusers") } func (im *RuntimeJavascriptInitModule) registerAfterListGroupUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listgroupusers") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListGroupUsers", "listgroupusers") } func (im *RuntimeJavascriptInitModule) registerBeforeListUserGroups(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listusergroups") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListUserGroups", "listusergroups") } func (im *RuntimeJavascriptInitModule) registerAfterListUserGroups(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listusergroups") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListUserGroups", "listusergroups") } func (im *RuntimeJavascriptInitModule) registerBeforeListGroups(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listgroups") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListGroups", "listgroups") } func (im *RuntimeJavascriptInitModule) registerAfterListGroups(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listgroups") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListGroups", "listgroups") } func (im *RuntimeJavascriptInitModule) registerBeforeDeleteLeaderboardRecord(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "deleteleaderboardrecord") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeDeleteLeaderboardRecord", "deleteleaderboardrecord") } func (im *RuntimeJavascriptInitModule) registerAfterDeleteLeaderboardRecord(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "deleteleaderboardrecord") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterDeleteLeaderboardRecord", "deleteleaderboardrecord") } func (im *RuntimeJavascriptInitModule) registerBeforeDeleteTournamentRecord(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "deletetournamentrecord") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeDeleteTournamentRecord", "deletetournamentrecord") } func (im *RuntimeJavascriptInitModule) registerAfterDeleteTournamentRecord(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "deletetournamentrecord") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterDeleteTournamentRecord", "deletetournamentrecord") } func (im *RuntimeJavascriptInitModule) registerBeforeListLeaderboardRecords(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listleaderboardrecords") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListLeaderboardRecords", "listleaderboardrecords") } func (im *RuntimeJavascriptInitModule) registerAfterListLeaderboardRecords(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listleaderboardrecords") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListLeaderboardRecords", "listleaderboardrecords") } func (im *RuntimeJavascriptInitModule) registerBeforeWriteLeaderboardRecord(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "writeleaderboardrecord") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeWriteLeaderboardRecord", "writeleaderboardrecord") } func (im *RuntimeJavascriptInitModule) registerAfterWriteLeaderboardRecord(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "writeleaderboardrecord") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterWriteLeaderboardRecord", "writeleaderboardrecord") } func (im *RuntimeJavascriptInitModule) registerBeforeListLeaderboardRecordsAroundOwner(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listleaderboardrecordsaroundowner") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListLeaderboardRecordsAroundOwner", "listleaderboardrecordsaroundowner") } func (im *RuntimeJavascriptInitModule) registerAfterListLeaderboardRecordsAroundOwner(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listleaderboardrecordsaroundowner") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListLeaderboardRecordsAroundOwner", "listleaderboardrecordsaroundowner") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linkapple") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkApple", "linkapple") } func (im *RuntimeJavascriptInitModule) registerAfterLinkApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linkapple") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkApple", "linkapple") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkCustom(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linkcustom") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkCustom", "linkcustom") } func (im *RuntimeJavascriptInitModule) registerAfterLinkCustom(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linkcustom") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkCustom", "linkcustom") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkDevice(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linkdevice") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkDevice", "linkdevice") } func (im *RuntimeJavascriptInitModule) registerAfterLinkDevice(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linkdevice") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkDevice", "linkdevice") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkEmail(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linkemail") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkEmail", "linkemail") } func (im *RuntimeJavascriptInitModule) registerAfterLinkEmail(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linkemail") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkEmail", "linkemail") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkFacebook(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linkfacebook") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkFacebook", "linkfacebook") } func (im *RuntimeJavascriptInitModule) registerAfterLinkFacebook(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linkfacebook") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkFacebook", "linkfacebook") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkFacebookInstantGame(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linkfacebookinstantgame") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkFacebookInstantGame", "linkfacebookinstantgame") } func (im *RuntimeJavascriptInitModule) registerAfterLinkFacebookInstantGame(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linkfacebookinstantgame") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkFacebookInstantGame", "linkfacebookinstantgame") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkGameCenter(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linkgamecenter") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkGameCenter", "linkgamecenter") } func (im *RuntimeJavascriptInitModule) registerAfterLinkGameCenter(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linkgamecenter") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkGameCenter", "linkgamecenter") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linkgoogle") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkGoogle", "linkgoogle") } func (im *RuntimeJavascriptInitModule) registerAfterLinkGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linkgoogle") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkGoogle", "linkgoogle") } func (im *RuntimeJavascriptInitModule) registerBeforeLinkSteam(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "linksteam") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeLinkSteam", "linksteam") } func (im *RuntimeJavascriptInitModule) registerAfterLinkSteam(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "linksteam") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterLinkSteam", "linksteam") } func (im *RuntimeJavascriptInitModule) registerBeforeListMatches(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listmatches") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListMatches", "listmatches") } func (im *RuntimeJavascriptInitModule) registerAfterListMatches(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listmatches") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListMatches", "listmatches") } func (im *RuntimeJavascriptInitModule) registerBeforeListNotifications(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listnotifications") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListNotifications", "listnotifications") } func (im *RuntimeJavascriptInitModule) registerAfterListNotifications(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listnotifications") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListNotifications", "listnotifications") } func (im *RuntimeJavascriptInitModule) registerBeforeDeleteNotifications(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "deletenotifications") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeDeleteNotifications", "deletenotifications") } func (im *RuntimeJavascriptInitModule) registerAfterDeleteNotifications(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "deletenotifications") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterDeleteNotifications", "deletenotifications") } func (im *RuntimeJavascriptInitModule) registerBeforeListStorageObjects(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "liststorageobjects") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListStorageObjects", "liststorageobjects") } func (im *RuntimeJavascriptInitModule) registerAfterListStorageObjects(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "liststorageobjects") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListStorageObjects", "liststorageobjects") } func (im *RuntimeJavascriptInitModule) registerBeforeReadStorageObjects(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "readstorageobjects") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeReadStorageObjects", "readstorageobjects") } func (im *RuntimeJavascriptInitModule) registerAfterReadStorageObjects(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "readstorageobjects") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterReadStorageObjects", "readstorageobjects") } func (im *RuntimeJavascriptInitModule) registerBeforeWriteStorageObjects(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "writestorageobjects") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeWriteStorageObjects", "writestorageobjects") } func (im *RuntimeJavascriptInitModule) registerAfterWriteStorageObjects(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "writestorageobjects") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterWriteStorageObjects", "writestorageobjects") } func (im *RuntimeJavascriptInitModule) registerBeforeDeleteStorageObjects(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "deletestorageobjects") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeDeleteStorageObjects", "deletestorageobjects") } func (im *RuntimeJavascriptInitModule) registerAfterDeleteStorageObjects(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "deletestorageobjects") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterDeleteStorageObjects", "deletestorageobjects") } func (im *RuntimeJavascriptInitModule) registerBeforeJoinTournament(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "jointournament") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeJoinTournament", "jointournament") } func (im *RuntimeJavascriptInitModule) registerAfterJoinTournament(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "jointournament") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterJoinTournament", "jointournament") } func (im *RuntimeJavascriptInitModule) registerBeforeListTournamentRecords(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listtournamentrecords") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListTournamentRecords", "listtournamentrecords") } func (im *RuntimeJavascriptInitModule) registerAfterListTournamentRecords(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listtournamentrecords") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListTournamentRecords", "listtournamentrecords") } func (im *RuntimeJavascriptInitModule) registerBeforeListTournaments(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listtournaments") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListTournaments", "listtournaments") } func (im *RuntimeJavascriptInitModule) registerAfterListTournaments(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listtournaments") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListTournaments", "listtournaments") } func (im *RuntimeJavascriptInitModule) registerBeforeWriteTournamentRecord(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "writetournamentrecord") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeWriteTournamentRecord", "writetournamentrecord") } func (im *RuntimeJavascriptInitModule) registerAfterWriteTournamentRecord(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "writetournamentrecord") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterWriteTournamentRecord", "writetournamentrecord") } func (im *RuntimeJavascriptInitModule) registerBeforeListTournamentRecordsAroundOwner(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listtournamentrecordsaroundowner") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListTournamentRecordsAroundOwner", "listtournamentrecordsaroundowner") } func (im *RuntimeJavascriptInitModule) registerAfterListTournamentRecordsAroundOwner(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listtournamentrecordsaroundowner") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListTournamentRecordsAroundOwner", "listtournamentrecordsaroundowner") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinkapple") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkApple", "unlinkapple") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinkapple") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkApple", "unlinkapple") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkCustom(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinkcustom") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkCustom", "unlinkcustom") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkCustom(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinkcustom") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkCustom", "unlinkcustom") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkDevice(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinkdevice") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkDevice", "unlinkdevice") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkDevice(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinkdevice") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkDevice", "unlinkdevice") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkEmail(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinkemail") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkEmail", "unlinkemail") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkEmail(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinkemail") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkEmail", "unlinkemail") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkFacebook(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinkfacebook") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkFacebook", "unlinkfacebook") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkFacebook(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinkfacebook") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkFacebook", "unlinkfacebook") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkFacebookInstantGame(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinkfacebookinstantgame") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkFacebookInstantGame", "unlinkfacebookinstantgame") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkFacebookInstantGame(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinkfacebookinstantgame") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkFacebookInstantGame", "unlinkfacebookinstantgame") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkGameCenter(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinkgamecenter") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkGameCenter", "unlinkgamecenter") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkGameCenter(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinkgamecenter") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkGameCenter", "unlinkgamecenter") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinkgoogle") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkGoogle", "unlinkgoogle") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinkgoogle") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkGoogle", "unlinkgoogle") } func (im *RuntimeJavascriptInitModule) registerBeforeUnlinkSteam(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "unlinksteam") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeUnlinkSteam", "unlinksteam") } func (im *RuntimeJavascriptInitModule) registerAfterUnlinkSteam(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "unlinksteam") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterUnlinkSteam", "unlinksteam") } func (im *RuntimeJavascriptInitModule) registerBeforeGetUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "getusers") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeGetUsers", "getusers") } func (im *RuntimeJavascriptInitModule) registerAfterGetUsers(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "getusers") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterGetUsers", "getusers") } func (im *RuntimeJavascriptInitModule) registerBeforeValidatePurchaseApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "validatepurchaseapple") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeValidatePurchaseApple", "validatepurchaseapple") } func (im *RuntimeJavascriptInitModule) registerAfterValidatePurchaseApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "validatepurchaseapple") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterValidatePurchaseApple", "validatepurchaseapple") } func (im *RuntimeJavascriptInitModule) registerBeforeValidateSubscriptionApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "validatesubscriptionapple") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeValidateSubscriptionApple", "validatesubscriptionapple") } func (im *RuntimeJavascriptInitModule) registerAfterValidateSubscriptionApple(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "validatesubscriptionapple") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterValidateSubscriptionApple", "validatesubscriptionapple") } func (im *RuntimeJavascriptInitModule) registerBeforeValidatePurchaseGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "validatepurchasegoogle") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeValidatePurchaseGoogle", "validatepurchasegoogle") } func (im *RuntimeJavascriptInitModule) registerAfterValidatePurchaseGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "validatepurchasegoogle") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterValidatePurchaseGoogle", "validatepurchasegoogle") } func (im *RuntimeJavascriptInitModule) registerBeforeValidateSubscriptionGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "validatesubscriptiongoogle") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeValidateSubscriptionGoogle", "validatesubscriptiongoogle") } func (im *RuntimeJavascriptInitModule) registerAfterValidateSubscriptionGoogle(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "validatesubscriptiongoogle") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterValidateSubscriptionGoogle", "validatesubscriptiongoogle") } func (im *RuntimeJavascriptInitModule) registerBeforeValidatePurchaseHuawei(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "validatepurchasehuawei") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeValidatePurchaseHuawei", "validatepurchasehuawei") } func (im *RuntimeJavascriptInitModule) registerAfterValidatePurchaseHuawei(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "validatepurchasehuawei") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterValidatePurchaseHuawei", "validatepurchasehuawei") } func (im *RuntimeJavascriptInitModule) registerBeforeListSubscriptions(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "listsubscriptions") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeListSubscriptions", "listsubscriptions") } func (im *RuntimeJavascriptInitModule) registerAfterListSubscriptions(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "listsubscriptions") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterListSubscriptions", "listsubscriptions") } func (im *RuntimeJavascriptInitModule) registerBeforeGetSubscription(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "getsubscription") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeGetSubscription", "getsubscription") } func (im *RuntimeJavascriptInitModule) registerAfterGetSubscription(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "getsubscription") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterGetSubscription", "getsubscription") } func (im *RuntimeJavascriptInitModule) registerBeforeEvent(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeBefore, "event") + return im.registerHook(r, RuntimeExecutionModeBefore, "registerBeforeEvent", "event") } func (im *RuntimeJavascriptInitModule) registerAfterEvent(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return im.registerHook(r, RuntimeExecutionModeAfter, "event") + return im.registerHook(r, RuntimeExecutionModeAfter, "registerAfterEvent", "event") } -func (im *RuntimeJavascriptInitModule) registerHook(r *goja.Runtime, execMode RuntimeExecutionMode, fnName string) func(goja.FunctionCall) goja.Value { +func (im *RuntimeJavascriptInitModule) registerStorageIndex(r *goja.Runtime) func(call goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { - fn := f.Argument(0) + idxName := getJsString(r, f.Argument(0)) + idxCollection := getJsString(r, f.Argument(1)) + + var idxKey string + if !goja.IsUndefined(f.Argument(2)) && !goja.IsNull(f.Argument(2)) { + idxKey = getJsString(r, f.Argument(2)) + } + + var fields []string + ownersArray := f.Argument(3) + if goja.IsUndefined(ownersArray) || goja.IsNull(ownersArray) { + panic(r.NewTypeError("expects an array of fields")) + } + fieldsSlice, ok := ownersArray.Export().([]interface{}) + if !ok { + panic(r.NewTypeError("expects an array of fields")) + } + if len(fieldsSlice) < 1 { + panic(r.NewTypeError("expects at least one field to be set")) + } + fields = make([]string, 0, len(fieldsSlice)) + for _, field := range fieldsSlice { + fieldStr, ok := field.(string) + if !ok { + panic(r.NewTypeError("expects a string field")) + } + fields = append(fields, fieldStr) + } + + idxMaxEntries := int(getJsInt(r, f.Argument(4))) + + indexOnly := false + if !goja.IsUndefined(f.Argument(5)) && !goja.IsNull(f.Argument(5)) { + indexOnly = getJsBool(r, f.Argument(5)) + } + + if err := im.storageIndex.CreateIndex(context.Background(), idxName, idxCollection, idxKey, fields, idxMaxEntries, indexOnly); err != nil { + panic(r.NewGoError(fmt.Errorf("Failed to register storage index: %s", err.Error()))) + } + + return goja.Undefined() + } +} + +func (im *RuntimeJavascriptInitModule) registerStorageIndexFilter(r *goja.Runtime) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + fName := f.Argument(0) + if goja.IsNull(fName) || goja.IsUndefined(fName) { + panic(r.NewTypeError("expects a non empty string")) + } + key, ok := fName.Export().(string) + if !ok { + panic(r.NewTypeError("expects a non empty string")) + } + if key == "" { + panic(r.NewTypeError("expects a non empty string")) + } + + fn := f.Argument(1) + _, ok = goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractStorageIndexFilterFn(r, key) if err != nil { - panic(r.NewTypeError("failed to register %q %s hook: %s", fnName, execMode.String(), err.Error())) + panic(r.NewGoError(err)) + } + + lKey := strings.ToLower(key) + im.registerCallbackFn(RuntimeExecutionModeStorageIndexFilter, lKey, fnKey) + im.announceCallbackFn(RuntimeExecutionModeStorageIndexFilter, lKey) + + return goja.Undefined() + } +} + +func (im *RuntimeJavascriptInitModule) registerHook(r *goja.Runtime, execMode RuntimeExecutionMode, registerFnName, fnName string) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) } lKey := strings.ToLower(API_PREFIX + fnName) + fnKey, err := im.extractHookFn(registerFnName) + if err != nil { + panic(r.NewGoError(err)) + } im.registerCallbackFn(execMode, lKey, fnKey) im.announceCallbackFn(execMode, lKey) @@ -936,24 +1091,100 @@ func (im *RuntimeJavascriptInitModule) registerHook(r *goja.Runtime, execMode Ru } } +func (im *RuntimeJavascriptInitModule) extractHookFn(registerFnName string) (string, error) { + bs, initFnVarName, err := im.getInitModuleFn() + if err != nil { + return "", err + } + + globalFnId, err := im.getHookFnIdentifier(bs, initFnVarName, registerFnName) + if err != nil { + return "", fmt.Errorf("js %s function key could not be extracted: %s", registerFnName, err.Error()) + } + + return globalFnId, nil +} + +func (im *RuntimeJavascriptInitModule) getInitModuleFn() (*ast.BlockStatement, string, error) { + var fl *ast.FunctionLiteral + for _, dec := range im.ast.Body { + if funDecl, ok := dec.(*ast.FunctionDeclaration); ok && funDecl.Function.Name.Name == INIT_MODULE_FN_NAME { + fl = funDecl.Function + break + } else if varStat, ok := dec.(*ast.VariableStatement); ok { + if id, ok := varStat.List[0].Target.(*ast.Identifier); ok && id.Name == INIT_MODULE_FN_NAME { + if fnLit, ok := varStat.List[0].Initializer.(*ast.FunctionLiteral); ok { + fl = fnLit + } + } + } + } + + if fl == nil { + return nil, "", errors.New("failed to find InitModule function") + } + if len(fl.ParameterList.List) < 4 { + return nil, "", errors.New("InitModule function is missing params") + } + + initFnName := fl.ParameterList.List[3].Target.(*ast.Identifier).Name.String() // Initializer is the 4th argument of InitModule + + return fl.Body, initFnName, nil +} + +func (im *RuntimeJavascriptInitModule) getHookFnIdentifier(bs *ast.BlockStatement, initVarName, registerFnName string) (string, error) { + for _, exp := range bs.List { + if try, ok := exp.(*ast.TryStatement); ok { + if s, err := im.getHookFnIdentifier(try.Body, initVarName, registerFnName); err != nil { + continue + } else { + return s, nil + } + } + if expStat, ok := exp.(*ast.ExpressionStatement); ok { + if callExp, ok := expStat.Expression.(*ast.CallExpression); ok { + if callee, ok := callExp.Callee.(*ast.DotExpression); ok { + if callee.Left.(*ast.Identifier).Name.String() == initVarName && callee.Identifier.Name.String() == registerFnName { + if modNameArg, ok := callExp.ArgumentList[0].(*ast.Identifier); ok { + return modNameArg.Name.String(), nil + } else if modNameArg, ok := callExp.ArgumentList[0].(*ast.StringLiteral); ok { + return modNameArg.Value.String(), nil + } else { + return "", errors.New("not found") + } + } + } + } + } + } + + return "", errors.New("not found") +} + func (im *RuntimeJavascriptInitModule) registerRtBefore(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fName := f.Argument(0) if goja.IsNull(fName) || goja.IsUndefined(fName) { panic(r.NewTypeError("expects a non empty string")) } - key := fName.String() + key, ok := fName.Export().(string) + if !ok { + panic(r.NewTypeError("expects a non empty string")) + } if key == "" { panic(r.NewTypeError("expects a non empty string")) } fn := f.Argument(1) + _, ok = goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractRtHookFn(r, "registerRtBefore", key) if err != nil { - panic(r.NewTypeError("failed to rt before hook on %q: %s", fName, err.Error())) + panic(r.NewGoError(err)) } - lKey := strings.ToLower(RTAPI_PREFIX + key) im.registerCallbackFn(RuntimeExecutionModeBefore, lKey, fnKey) im.announceCallbackFn(RuntimeExecutionModeBefore, lKey) @@ -968,18 +1199,24 @@ func (im *RuntimeJavascriptInitModule) registerRtAfter(r *goja.Runtime) func(goj if goja.IsNull(fName) || goja.IsUndefined(fName) { panic(r.NewTypeError("expects a non empty string")) } - key := fName.String() + key, ok := fName.Export().(string) + if !ok { + panic(r.NewTypeError("expects a non empty string")) + } if key == "" { panic(r.NewTypeError("expects a non empty string")) } fn := f.Argument(1) + _, ok = goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractRtHookFn(r, "registerRtAfter", key) if err != nil { - panic(r.NewTypeError("failed to rt after hook on %q: %s", fName, err.Error())) + panic(r.NewGoError(err)) } - lKey := strings.ToLower(RTAPI_PREFIX + key) im.registerCallbackFn(RuntimeExecutionModeAfter, lKey, fnKey) im.announceCallbackFn(RuntimeExecutionModeAfter, lKey) @@ -988,15 +1225,72 @@ func (im *RuntimeJavascriptInitModule) registerRtAfter(r *goja.Runtime) func(goj } } +func (im *RuntimeJavascriptInitModule) extractRtHookFn(r *goja.Runtime, registerFnName, fnName string) (string, error) { + bs, initFnVarName, err := im.getInitModuleFn() + if err != nil { + return "", err + } + + globalFnId, err := im.getRtHookFnIdentifier(r, bs, initFnVarName, registerFnName, fnName) + if err != nil { + return "", fmt.Errorf("js realtime %s hook function key could not be extracted: %s", registerFnName, err.Error()) + } + + return globalFnId, nil +} + +func (im *RuntimeJavascriptInitModule) getRtHookFnIdentifier(r *goja.Runtime, bs *ast.BlockStatement, initVarName, registerFnName, rtFnName string) (string, error) { + for _, exp := range bs.List { + if try, ok := exp.(*ast.TryStatement); ok { + if s, err := im.getRtHookFnIdentifier(r, try.Body, initVarName, registerFnName, rtFnName); err != nil { + continue + } else { + return s, nil + } + } + if expStat, ok := exp.(*ast.ExpressionStatement); ok { + if callExp, ok := expStat.Expression.(*ast.CallExpression); ok { + if callee, ok := callExp.Callee.(*ast.DotExpression); ok { + if callee.Left.(*ast.Identifier).Name.String() == initVarName && callee.Identifier.Name.String() == registerFnName { + if modNameArg, ok := callExp.ArgumentList[0].(*ast.Identifier); ok { + id := modNameArg.Name.String() + if r.Get(id).String() != rtFnName { + continue + } + } else if modNameArg, ok := callExp.ArgumentList[0].(*ast.StringLiteral); ok { + if modNameArg.Value.String() != rtFnName { + continue + } + } + + if modNameArg, ok := callExp.ArgumentList[1].(*ast.Identifier); ok { + return modNameArg.Name.String(), nil + } else if modNameArg, ok := callExp.ArgumentList[1].(*ast.StringLiteral); ok { + return modNameArg.Value.String(), nil + } else { + return "", errors.New("not found") + } + } + } + } + } + } + + return "", errors.New("not found") +} + func (im *RuntimeJavascriptInitModule) registerMatchmakerMatched(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractHookFn("registerMatchmakerMatched") if err != nil { - panic(r.NewTypeError("failed to register matchmakerMatched hook: %s", err.Error())) + panic(r.NewGoError(err)) } - im.registerCallbackFn(RuntimeExecutionModeMatchmaker, "", fnKey) im.announceCallbackFn(RuntimeExecutionModeMatchmaker, "") @@ -1007,12 +1301,15 @@ func (im *RuntimeJavascriptInitModule) registerMatchmakerMatched(r *goja.Runtime func (im *RuntimeJavascriptInitModule) registerTournamentEnd(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractHookFn("registerTournamentEnd") if err != nil { - panic(r.NewTypeError("failed to register tournamentEnd hook: %s", err.Error())) + panic(r.NewGoError(err)) } - im.registerCallbackFn(RuntimeExecutionModeTournamentEnd, "", fnKey) im.announceCallbackFn(RuntimeExecutionModeTournamentEnd, "") @@ -1023,12 +1320,15 @@ func (im *RuntimeJavascriptInitModule) registerTournamentEnd(r *goja.Runtime) fu func (im *RuntimeJavascriptInitModule) registerTournamentReset(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractHookFn("registerTournamentReset") if err != nil { - panic(r.NewTypeError("failed to register tournamentReset hook: %s", err.Error())) + panic(r.NewGoError(err)) } - im.registerCallbackFn(RuntimeExecutionModeTournamentReset, "", fnKey) im.announceCallbackFn(RuntimeExecutionModeTournamentReset, "") @@ -1039,12 +1339,15 @@ func (im *RuntimeJavascriptInitModule) registerTournamentReset(r *goja.Runtime) func (im *RuntimeJavascriptInitModule) registerLeaderboardReset(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractHookFn("registerLeaderboardReset") if err != nil { - panic(r.NewTypeError("failed to register leaderboardReset hook: %s", err.Error())) + panic(r.NewGoError(err)) } - im.registerCallbackFn(RuntimeExecutionModeLeaderboardReset, "", fnKey) im.announceCallbackFn(RuntimeExecutionModeLeaderboardReset, "") @@ -1055,12 +1358,15 @@ func (im *RuntimeJavascriptInitModule) registerLeaderboardReset(r *goja.Runtime) func (im *RuntimeJavascriptInitModule) registerPurchaseNotificationApple(r *goja.Runtime) func(call goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractHookFn("registerPurchaseNotificationApple") if err != nil { - panic(r.NewTypeError("failed to register purchaseNotificationApple hook: %s", err.Error())) + panic(r.NewGoError(err)) } - im.registerCallbackFn(RuntimeExecutionModePurchaseNotificationApple, "", fnKey) im.announceCallbackFn(RuntimeExecutionModePurchaseNotificationApple, "") @@ -1071,12 +1377,15 @@ func (im *RuntimeJavascriptInitModule) registerPurchaseNotificationApple(r *goja func (im *RuntimeJavascriptInitModule) registerSubscriptionNotificationApple(r *goja.Runtime) func(call goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractHookFn("registerSubscriptionNotificationApple") if err != nil { - panic(r.NewTypeError("failed to register subscriptionNotificationApple hook: %s", err.Error())) + panic(r.NewGoError(err)) } - im.registerCallbackFn(RuntimeExecutionModeSubscriptionNotificationApple, "", fnKey) im.announceCallbackFn(RuntimeExecutionModeSubscriptionNotificationApple, "") @@ -1087,12 +1396,15 @@ func (im *RuntimeJavascriptInitModule) registerSubscriptionNotificationApple(r * func (im *RuntimeJavascriptInitModule) registerPurchaseNotificationGoogle(r *goja.Runtime) func(call goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractHookFn("registerPurchaseNotificationGoogle") if err != nil { - panic(r.NewTypeError("failed to register purchaseNotificationGoogle hook: %s", err.Error())) + panic(r.NewGoError(err)) } - im.registerCallbackFn(RuntimeExecutionModePurchaseNotificationGoogle, "", fnKey) im.announceCallbackFn(RuntimeExecutionModePurchaseNotificationGoogle, "") @@ -1103,12 +1415,15 @@ func (im *RuntimeJavascriptInitModule) registerPurchaseNotificationGoogle(r *goj func (im *RuntimeJavascriptInitModule) registerSubscriptionNotificationGoogle(r *goja.Runtime) func(call goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { fn := f.Argument(0) + _, ok := goja.AssertFunction(fn) + if !ok { + panic(r.NewTypeError("expects a function")) + } - fnKey, err := im.getFnKey(r, fn) + fnKey, err := im.extractHookFn("registerSubscriptionNotificationGoogle") if err != nil { - panic(r.NewTypeError("failed to register subscriptionNotificationGoogle hook: %s", err.Error())) + panic(r.NewGoError(err)) } - im.registerCallbackFn(RuntimeExecutionModeSubscriptionNotificationGoogle, "", fnKey) im.announceCallbackFn(RuntimeExecutionModeSubscriptionNotificationGoogle, "") @@ -1116,162 +1431,119 @@ func (im *RuntimeJavascriptInitModule) registerSubscriptionNotificationGoogle(r } } -func (im *RuntimeJavascriptInitModule) registerStorageIndex(r *goja.Runtime) func(call goja.FunctionCall) goja.Value { +func (im *RuntimeJavascriptInitModule) registerMatch(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { - idxName := getJsString(r, f.Argument(0)) - idxCollection := getJsString(r, f.Argument(1)) + name := getJsString(r, f.Argument(0)) - var idxKey string - if !goja.IsUndefined(f.Argument(2)) && !goja.IsNull(f.Argument(2)) { - idxKey = getJsString(r, f.Argument(2)) + funcObj := f.Argument(1) + if goja.IsNull(funcObj) || goja.IsUndefined(funcObj) { + panic(r.NewTypeError("expects an object")) } - var fields []string - ownersArray := f.Argument(3) - if goja.IsUndefined(ownersArray) || goja.IsNull(ownersArray) { - panic(r.NewTypeError("expects an array of fields")) - } - fieldsSlice, ok := ownersArray.Export().([]interface{}) + funcMap, ok := funcObj.Export().(map[string]interface{}) if !ok { - panic(r.NewTypeError("expects an array of fields")) - } - if len(fieldsSlice) < 1 { - panic(r.NewTypeError("expects at least one field to be set")) - } - fields = make([]string, 0, len(fieldsSlice)) - for _, field := range fieldsSlice { - fieldStr, ok := field.(string) - if !ok { - panic(r.NewTypeError("expects a string field")) - } - fields = append(fields, fieldStr) + panic(r.NewTypeError("expects an object")) } - idxMaxEntries := int(getJsInt(r, f.Argument(4))) - - indexOnly := false - if !goja.IsUndefined(f.Argument(5)) && !goja.IsNull(f.Argument(5)) { - indexOnly = getJsBool(r, f.Argument(5)) - } + functions := &jsMatchHandlers{} - if err := im.storageIndex.CreateIndex(context.Background(), idxName, idxCollection, idxKey, fields, idxMaxEntries, indexOnly); err != nil { - panic(r.NewGoError(fmt.Errorf("Failed to register storage index: %s", err.Error()))) + fnValue, ok := funcMap[string(MatchInit)] + if !ok { + panic(r.NewTypeError(string(MatchInit) + " not found")) } - - return goja.Undefined() - } -} - -func (im *RuntimeJavascriptInitModule) registerStorageIndexFilter(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return func(f goja.FunctionCall) goja.Value { - fName := f.Argument(0) - if goja.IsNull(fName) || goja.IsUndefined(fName) { - panic(r.NewTypeError("expects a non empty string")) + _, ok = goja.AssertFunction(r.ToValue(fnValue)) + if !ok { + panic(r.NewTypeError(string(MatchInit) + " value not a valid function")) } - key := fName.String() - if key == "" { - panic(r.NewTypeError("expects a non empty string")) + fnKey, err := im.extractMatchFnKey(r, name, MatchInit) + if err != nil { + panic(r.NewGoError(err)) } + functions.initFn = fnKey - fn := f.Argument(1) - _, ok := goja.AssertFunction(fn) + fnValue, ok = funcMap[string(MatchJoinAttempt)] if !ok { - panic(r.NewTypeError("expects a function")) + panic(r.NewTypeError(string(MatchJoinAttempt) + " not found")) } - - fnObj, ok := fn.(*goja.Object) + _, ok = goja.AssertFunction(r.ToValue(fnValue)) if !ok { - panic(r.NewTypeError("expects an object")) + panic(r.NewTypeError(string(MatchJoinAttempt) + " value not a valid function")) } - - v := fnObj.Get("name") - if v == nil { - panic(r.NewTypeError("function key could not be extracted")) + fnKey, err = im.extractMatchFnKey(r, name, MatchJoinAttempt) + if err != nil { + panic(r.NewGoError(err)) } + functions.joinAttemptFn = fnKey - fnKey := strings.Clone(v.String()) - - im.registerCallbackFn(RuntimeExecutionModeStorageIndexFilter, key, fnKey) - im.announceCallbackFn(RuntimeExecutionModeStorageIndexFilter, key) - - return goja.Undefined() - } -} - -func (im *RuntimeJavascriptInitModule) getFnKey(r *goja.Runtime, fn goja.Value) (string, error) { - if fn == nil { - return "", errors.New("not found") - } - - _, ok := goja.AssertFunction(fn) - if !ok { - return "", errors.New("value is not a valid function") - } - - fnObj := fn.ToObject(r) - - v := fnObj.Get("name") - if v == nil || v.String() == "" { - return "", errors.New("function object 'name' property not found or empty") - } - - return strings.Clone(v.String()), nil -} - -func (im *RuntimeJavascriptInitModule) registerMatch(r *goja.Runtime) func(goja.FunctionCall) goja.Value { - return func(f goja.FunctionCall) goja.Value { - name := getJsString(r, f.Argument(0)) - - funcs := f.Argument(1) - if goja.IsNull(funcs) || goja.IsUndefined(funcs) { - panic(r.NewTypeError("expects an object")) + fnValue, ok = funcMap[string(MatchJoin)] + if !ok { + panic(r.NewTypeError(string(MatchJoin) + " not found")) } - - funcObj := funcs.ToObject(r) - - functions := &jsMatchHandlers{} - - key, err := im.getFnKey(r, funcObj.Get(string(MatchInit))) - if err != nil { - panic(r.NewTypeError("match handler required function %q invalid: %s", string(MatchInit), err.Error())) + _, ok = goja.AssertFunction(r.ToValue(fnValue)) + if !ok { + panic(r.NewTypeError(string(MatchJoin) + " value not a valid function")) } - functions.initFn = key - - key, err = im.getFnKey(r, funcObj.Get(string(MatchJoinAttempt))) + fnKey, err = im.extractMatchFnKey(r, name, MatchJoin) if err != nil { - panic(r.NewTypeError("match handler required function %q invalid: %s", string(MatchJoinAttempt), err.Error())) + panic(r.NewGoError(err)) } - functions.joinAttemptFn = key + functions.joinFn = fnKey - key, err = im.getFnKey(r, funcObj.Get(string(MatchJoin))) - if err != nil { - panic(r.NewTypeError("match handler required function %q invalid: %s", string(MatchJoin), err.Error())) + fnValue, ok = funcMap[string(MatchLeave)] + if !ok { + panic(r.NewTypeError(string(MatchLeave) + " not found")) } - functions.joinFn = key - - key, err = im.getFnKey(r, funcObj.Get(string(MatchLeave))) + _, ok = goja.AssertFunction(r.ToValue(fnValue)) + if !ok { + panic(r.NewTypeError(string(MatchLeave) + " value not a valid function")) + } + fnKey, err = im.extractMatchFnKey(r, name, MatchLeave) if err != nil { - panic(r.NewTypeError("match handler required function %q invalid: %s", string(MatchLeave), err.Error())) + panic(r.NewGoError(err)) } - functions.leaveFn = key + functions.leaveFn = fnKey - key, err = im.getFnKey(r, funcObj.Get(string(MatchLoop))) + fnValue, ok = funcMap[string(MatchLoop)] + if !ok { + panic(r.NewTypeError(string(MatchLoop) + " not found")) + } + _, ok = goja.AssertFunction(r.ToValue(fnValue)) + if !ok { + panic(r.NewTypeError(string(MatchLoop) + " value not a valid function")) + } + fnKey, err = im.extractMatchFnKey(r, name, MatchLoop) if err != nil { - panic(r.NewTypeError("match handler required function %q invalid: %s", string(MatchLoop), err.Error())) + panic(r.NewGoError(err)) } - functions.loopFn = key + functions.loopFn = fnKey - key, err = im.getFnKey(r, funcObj.Get(string(MatchTerminate))) + fnValue, ok = funcMap[string(MatchTerminate)] + if !ok { + panic(r.NewTypeError(string(MatchTerminate) + " not found")) + } + _, ok = goja.AssertFunction(r.ToValue(fnValue)) + if !ok { + panic(r.NewTypeError(string(MatchTerminate) + " value not a valid function")) + } + fnKey, err = im.extractMatchFnKey(r, name, MatchTerminate) if err != nil { - panic(r.NewTypeError("match handler required function %q invalid: %s", string(MatchTerminate), err.Error())) + panic(r.NewGoError(err)) } - functions.terminateFn = key + functions.terminateFn = fnKey - key, err = im.getFnKey(r, funcObj.Get(string(MatchSignal))) + fnValue, ok = funcMap[string(MatchSignal)] + if !ok { + panic(r.NewTypeError(string(MatchSignal) + " not found")) + } + _, ok = goja.AssertFunction(r.ToValue(fnValue)) + if !ok { + panic(r.NewTypeError(string(MatchSignal) + " value not a valid function")) + } + fnKey, err = im.extractMatchFnKey(r, name, MatchSignal) if err != nil { - panic(r.NewTypeError("match handler required function %q invalid: %s", string(MatchSignal), err.Error())) + panic(r.NewGoError(err)) } - functions.signalFn = key + functions.signalFn = fnKey im.MatchCallbacks.Add(name, functions) @@ -1291,6 +1563,82 @@ const ( MatchSignal MatchFnId = "matchSignal" ) +func (im *RuntimeJavascriptInitModule) extractMatchFnKey(r *goja.Runtime, modName string, matchFnId MatchFnId) (string, error) { + bs, initFnVarName, err := im.getInitModuleFn() + if err != nil { + return "", err + } + + globalFnId, err := im.getMatchHookFnIdentifier(r, bs, initFnVarName, modName, matchFnId) + if err != nil { + return "", fmt.Errorf("js match handler %q function for module %q global id could not be extracted: %s", string(matchFnId), modName, err.Error()) + } + + return globalFnId, nil +} + +func (im *RuntimeJavascriptInitModule) getMatchHookFnIdentifier(r *goja.Runtime, bs *ast.BlockStatement, initFnVarName, modName string, matchfnId MatchFnId) (string, error) { + for _, exp := range bs.List { + if try, ok := exp.(*ast.TryStatement); ok { + if s, err := im.getMatchHookFnIdentifier(r, try.Body, initFnVarName, modName, matchfnId); err != nil { + continue + } else { + return s, nil + } + } + if expStat, ok := exp.(*ast.ExpressionStatement); ok { + if callExp, ok := expStat.Expression.(*ast.CallExpression); ok { + if callee, ok := callExp.Callee.(*ast.DotExpression); ok { + if callee.Left.(*ast.Identifier).Name.String() == initFnVarName && callee.Identifier.Name == "registerMatch" { + if modNameArg, ok := callExp.ArgumentList[0].(*ast.Identifier); ok { + id := modNameArg.Name.String() + if r.Get(id).String() != modName { + continue + } + } else if modNameArg, ok := callExp.ArgumentList[0].(*ast.StringLiteral); ok { + if modNameArg.Value.String() != modName { + continue + } + } + + var obj *ast.ObjectLiteral + if matchHandlerId, ok := callExp.ArgumentList[1].(*ast.Identifier); ok { + // We know the obj is an identifier, we need to lookup it's definition in the AST + matchHandlerIdStr := matchHandlerId.Name.String() + for _, mhDec := range im.ast.DeclarationList { + if mhDecId, ok := mhDec.List[0].Target.(*ast.Identifier); ok && mhDecId.Name.String() == matchHandlerIdStr { + objLiteral, ok := mhDec.List[0].Initializer.(*ast.ObjectLiteral) + if ok { + obj = objLiteral + } + } + } + } else { + obj, _ = callExp.ArgumentList[1].(*ast.ObjectLiteral) + } + + for _, prop := range obj.Value { + key, _ := prop.(*ast.PropertyKeyed).Key.(*ast.StringLiteral) + if key.Literal == string(matchfnId) { + if sl, ok := prop.(*ast.PropertyKeyed).Value.(*ast.StringLiteral); ok { + return sl.Literal, nil + } else if id, ok := prop.(*ast.PropertyKeyed).Value.(*ast.Identifier); ok { + return id.Name.String(), nil + } else { + return "", inlinedFunctionError + } + } + } + break + } + } + } + } + } + + return "", errors.New("not found") +} + func (im *RuntimeJavascriptInitModule) registerCallbackFn(mode RuntimeExecutionMode, key string, fn string) { switch mode { case RuntimeExecutionModeRPC: diff --git a/server/runtime_javascript_match_core.go b/server/runtime_javascript_match_core.go index 88b2738492..1c779b4bd4 100644 --- a/server/runtime_javascript_match_core.go +++ b/server/runtime_javascript_match_core.go @@ -277,7 +277,11 @@ func (rm *RuntimeJavaScriptMatchCore) MatchJoinAttempt(tick int64, state interfa } pointerizeSlices(state) - args := []goja.Value{ctxObj, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(state), presenceObj, rm.vm.ToValue(metadata)} + stateObject := rm.vm.NewObject() + for k, v := range state.(map[string]any) { + _ = stateObject.Set(k, v) + } + args := []goja.Value{ctxObj, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(stateObject), presenceObj, rm.vm.ToValue(metadata)} retVal, err := rm.joinAttemptFn(goja.Null(), args...) if err != nil { return nil, false, "", err @@ -314,7 +318,10 @@ func (rm *RuntimeJavaScriptMatchCore) MatchJoinAttempt(tick int64, state interfa newState, ok := retMap["state"] if !ok { - return nil, false, "", errors.New("matchJoinAttempt is expected to return an object with 'state' property") + return nil, false, "", errors.New("matchJoinAttempt is expected to return an object with 'state' object property") + } + if _, ok = newState.(map[string]any); !ok { + return nil, false, "", errors.New("matchJoinAttempt is expected to return an object with 'state' object property") } return newState, allow, rejectMsg, nil @@ -334,7 +341,11 @@ func (rm *RuntimeJavaScriptMatchCore) MatchJoin(tick int64, state interface{}, j } pointerizeSlices(state) - args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(state), rm.vm.ToValue(presences)} + stateObject := rm.vm.NewObject() + for k, v := range state.(map[string]any) { + _ = stateObject.Set(k, v) + } + args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(stateObject), rm.vm.ToValue(presences)} retVal, err := rm.joinFn(goja.Null(), args...) if err != nil { return nil, err @@ -351,7 +362,10 @@ func (rm *RuntimeJavaScriptMatchCore) MatchJoin(tick int64, state interface{}, j newState, ok := retMap["state"] if !ok { - return nil, errors.New("matchJoin is expected to return an object with 'state' property") + return nil, errors.New("matchJoin is expected to return an object with 'state' object property") + } + if _, ok = newState.(map[string]any); !ok { + return nil, errors.New("matchJoin is expected to return an object with 'state' object property") } return newState, nil @@ -371,7 +385,12 @@ func (rm *RuntimeJavaScriptMatchCore) MatchLeave(tick int64, state interface{}, } pointerizeSlices(state) - args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(state), rm.vm.ToValue(presences)} + s := state.(map[string]any) + o := rm.vm.NewObject() + for k, v := range s { + _ = o.Set(k, v) + } + args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(o), rm.vm.ToValue(presences)} retVal, err := rm.leaveFn(goja.Null(), args...) if err != nil { return nil, err @@ -388,7 +407,10 @@ func (rm *RuntimeJavaScriptMatchCore) MatchLeave(tick int64, state interface{}, newState, ok := retMap["state"] if !ok { - return nil, errors.New("matchLeave is expected to return an object with 'state' property") + return nil, errors.New("matchLeave is expected to return an object with 'state' object property") + } + if _, ok = newState.(map[string]any); !ok { + return nil, errors.New("matchLeave is expected to return an object with 'state' object property") } return newState, nil @@ -421,7 +443,11 @@ func (rm *RuntimeJavaScriptMatchCore) MatchLoop(tick int64, state interface{}, i } pointerizeSlices(state) - args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(state), rm.vm.ToValue(inputs)} + stateObject := rm.vm.NewObject() + for k, v := range state.(map[string]any) { + _ = stateObject.Set(k, v) + } + args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(stateObject), rm.vm.ToValue(inputs)} retVal, err := rm.loopFn(goja.Null(), args...) if err != nil { return nil, err @@ -438,7 +464,10 @@ func (rm *RuntimeJavaScriptMatchCore) MatchLoop(tick int64, state interface{}, i newState, ok := retMap["state"] if !ok { - return nil, errors.New("matchLoop is expected to return an object with 'state' property") + return nil, errors.New("matchLoop is expected to return an object with 'state' object property") + } + if _, ok = newState.(map[string]any); !ok { + return nil, errors.New("matchLeave is expected to return an object with 'state' object property") } return newState, nil @@ -446,7 +475,11 @@ func (rm *RuntimeJavaScriptMatchCore) MatchLoop(tick int64, state interface{}, i func (rm *RuntimeJavaScriptMatchCore) MatchTerminate(tick int64, state interface{}, graceSeconds int) (interface{}, error) { pointerizeSlices(state) - args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(state), rm.vm.ToValue(graceSeconds)} + stateObject := rm.vm.NewObject() + for k, v := range state.(map[string]any) { + _ = stateObject.Set(k, v) + } + args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(stateObject), rm.vm.ToValue(graceSeconds)} retVal, err := rm.terminateFn(goja.Null(), args...) if err != nil { return nil, err @@ -463,7 +496,10 @@ func (rm *RuntimeJavaScriptMatchCore) MatchTerminate(tick int64, state interface newState, ok := retMap["state"] if !ok { - return nil, errors.New("matchTerminate is expected to return an object with 'state' property") + return nil, errors.New("matchTerminate is expected to return an object with 'state' object property") + } + if _, ok = newState.(map[string]any); !ok { + return nil, errors.New("matchTerminate is expected to return an object with 'state' object property") } return newState, nil @@ -471,7 +507,11 @@ func (rm *RuntimeJavaScriptMatchCore) MatchTerminate(tick int64, state interface func (rm *RuntimeJavaScriptMatchCore) MatchSignal(tick int64, state interface{}, data string) (interface{}, string, error) { pointerizeSlices(state) - args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(state), rm.vm.ToValue(data)} + stateObject := rm.vm.NewObject() + for k, v := range state.(map[string]any) { + _ = stateObject.Set(k, v) + } + args := []goja.Value{rm.ctx, rm.loggerModule, rm.nakamaModule, rm.dispatcher, rm.vm.ToValue(tick), rm.vm.ToValue(stateObject), rm.vm.ToValue(data)} retVal, err := rm.signalFn(goja.Null(), args...) if err != nil { return nil, "", err @@ -490,6 +530,9 @@ func (rm *RuntimeJavaScriptMatchCore) MatchSignal(tick int64, state interface{}, if !ok { return nil, "", errors.New("matchSignal is expected to return an object with 'state' property") } + if _, ok = newState.(map[string]any); !ok { + return nil, "", errors.New("matchSignal is expected to return an object with 'state' object property") + } responseDataRet, ok := retMap["data"] var responseData string