@@ -256,8 +256,10 @@ void SetGlobalArrayInternal(lua_State* lua, const char* name, Interpreter::Slice
256
256
/* In case the error set into the Lua stack by PushError() was generated
257
257
* by the non-error-trapping version of redis.pcall(), which is redis.call(),
258
258
* this function will raise the Lua error so that the execution of the
259
- * script will be halted. */
260
- int RaiseError (lua_State* lua) {
259
+ * script will be halted.
260
+ * This function never returns, it unwinds the Lua call stack until an error handler is found or the
261
+ * script exits */
262
+ int RaiseErrorAndAbort (lua_State* lua) {
261
263
lua_pushstring (lua, " err" );
262
264
lua_gettable (lua, -2 );
263
265
return lua_error (lua);
@@ -467,7 +469,7 @@ int RedisLogCommand(lua_State* lua) {
467
469
int argc = lua_gettop (lua);
468
470
if (argc < 2 ) {
469
471
PushError (lua, " redis.log() requires two arguments or more." );
470
- return RaiseError (lua);
472
+ return RaiseErrorAndAbort (lua);
471
473
}
472
474
473
475
return 0 ;
@@ -891,40 +893,12 @@ void Interpreter::RunGC() {
891
893
lua_gc (lua_, LUA_GCCOLLECT);
892
894
}
893
895
894
- // Returns number of results, which is always 1 in this case.
895
- // Please note that lua resets the stack once the function returns so no need
896
- // to unwind the stack manually in the function (though lua allows doing this).
897
- int Interpreter::RedisGenericCommand (bool raise_error, bool async, ObjectExplorer* explorer) {
898
- /* By using Lua debug hooks it is possible to trigger a recursive call
899
- * to luaRedisGenericCommand(), which normally should never happen.
900
- * To make this function reentrant is futile and makes it slower, but
901
- * we should at least detect such a misuse, and abort. */
902
- if (cmd_depth_) {
903
- const char * recursion_warning =
904
- " luaRedisGenericCommand() recursive call detected. "
905
- " Are you doing funny stuff with Lua debug hooks?" ;
906
- PushError (lua_, recursion_warning);
907
- return 1 ;
908
- }
909
-
910
- if (!redis_func_) {
911
- PushError (lua_, " internal error - redis function not defined" );
912
- return raise_error ? RaiseError (lua_) : 1 ;
913
- }
914
-
915
- cmd_depth_++;
896
+ std::optional<absl::FixedArray<std::string_view, 4 >> Interpreter::PrepareArgs () {
916
897
int argc = lua_gettop (lua_);
917
-
918
- #define RETURN_ERROR (err ) \
919
- { \
920
- PushError (lua_, err); \
921
- cmd_depth_--; \
922
- return raise_error ? RaiseError (lua_) : 1 ; \
923
- }
924
-
925
898
/* Require at least one argument */
926
899
if (argc == 0 ) {
927
- RETURN_ERROR (" Please specify at least one argument for redis.call()" );
900
+ PushError (lua_, " Please specify at least one argument for redis.call()" );
901
+ return std::nullopt;
928
902
}
929
903
930
904
size_t blob_len = 0 ;
@@ -947,21 +921,22 @@ int Interpreter::RedisGenericCommand(bool raise_error, bool async, ObjectExplore
947
921
blob_len += lua_rawlen (lua_, idx) + 1 ;
948
922
continue ;
949
923
default :
950
- RETURN_ERROR (" Lua redis() command arguments must be strings or integers" );
924
+ PushError (lua_, " Lua redis() command arguments must be strings or integers" );
925
+ return std::nullopt;
951
926
}
952
927
}
953
928
954
- char name_buffer[32 ]; // backing storage for cmd name
955
929
absl::FixedArray<string_view, 4 > args (argc);
956
930
957
931
// Copy command name to name_buffer and set it as first arg.
958
932
unsigned name_len = lua_rawlen (lua_, 1 );
959
- if (name_len >= sizeof (name_buffer)) {
960
- RETURN_ERROR (" Lua redis() command name too long" );
933
+ if (name_len >= sizeof (name_buffer_)) {
934
+ PushError (lua_, " Lua redis() command name too long" );
935
+ return std::nullopt;
961
936
}
962
937
963
- memcpy (name_buffer , lua_tostring (lua_, 1 ), name_len);
964
- args[0 ] = {name_buffer , name_len};
938
+ memcpy (name_buffer_ , lua_tostring (lua_, 1 ), name_len);
939
+ args[0 ] = {name_buffer_ , name_len};
965
940
buffer_.resize (blob_len + 4 , ' \0 ' ); // backing storage for args
966
941
967
942
char * cur = buffer_.data ();
@@ -993,7 +968,13 @@ int Interpreter::RedisGenericCommand(bool raise_error, bool async, ObjectExplore
993
968
/* Pop all arguments from the stack, we do not need them anymore
994
969
* and this way we guaranty we will have room on the stack for the result. */
995
970
lua_pop (lua_, argc);
971
+ return args;
972
+ }
996
973
974
+ // Calls redis function
975
+ // Returns false if error needs to be raised.
976
+ bool Interpreter::CallRedisFunction (bool raise_error, bool async, ObjectExplorer* explorer,
977
+ SliceSpan args) {
997
978
// Calling with custom explorer is not supported with errors or async
998
979
DCHECK (explorer == nullptr || (!raise_error && !async));
999
980
@@ -1003,8 +984,8 @@ int Interpreter::RedisGenericCommand(bool raise_error, bool async, ObjectExplore
1003
984
translator.emplace (lua_);
1004
985
explorer = &*translator;
1005
986
}
1006
-
1007
- redis_func_ (CallArgs{SliceSpan{ args} , &buffer_, explorer, async, raise_error, &raise_error});
987
+ cmd_depth_++;
988
+ redis_func_ (CallArgs{args, &buffer_, explorer, async, raise_error, &raise_error});
1008
989
cmd_depth_--;
1009
990
1010
991
// Shrink reusable buffer if it's too big.
@@ -1014,18 +995,57 @@ int Interpreter::RedisGenericCommand(bool raise_error, bool async, ObjectExplore
1014
995
}
1015
996
1016
997
if (!translator)
1017
- return 0 ;
998
+ return true ;
1018
999
1019
1000
// Raise error for regular 'call' command if needed.
1020
1001
if (raise_error && translator->HasError ()) {
1021
1002
// error is already on top of stack
1022
- return RaiseError (lua_) ;
1003
+ return false ;
1023
1004
}
1024
1005
1025
1006
if (!async)
1026
1007
DCHECK_EQ (1 , lua_gettop (lua_));
1027
1008
1028
- return 1 ;
1009
+ return true ;
1010
+ }
1011
+
1012
+ // Returns number of results, which is always 1 in this case.
1013
+ // Please note that lua resets the stack once the function returns so no need
1014
+ // to unwind the stack manually in the function (though lua allows doing this).
1015
+ int Interpreter::RedisGenericCommand (bool raise_error, bool async, ObjectExplorer* explorer) {
1016
+ /* By using Lua debug hooks it is possible to trigger a recursive call
1017
+ * to luaRedisGenericCommand(), which normally should never happen.
1018
+ * To make this function reentrant is futile and makes it slower, but
1019
+ * we should at least detect such a misuse, and abort. */
1020
+ if (cmd_depth_) {
1021
+ const char * recursion_warning =
1022
+ " luaRedisGenericCommand() recursive call detected. "
1023
+ " Are you doing funny stuff with Lua debug hooks?" ;
1024
+ PushError (lua_, recursion_warning);
1025
+ return 1 ;
1026
+ }
1027
+
1028
+ if (!redis_func_) {
1029
+ PushError (lua_, " internal error - redis function not defined" );
1030
+ if (raise_error) {
1031
+ return RaiseErrorAndAbort (lua_);
1032
+ }
1033
+ return 1 ;
1034
+ }
1035
+
1036
+ // IMPORTANT! all allocations within this funciton must be freed
1037
+ // BEFORE calling RaiseErrorAndAbort in case of script error. RaiseErrorAndAbort
1038
+ // uses longjmp which bypasses stack unwinding and skips the destruction of objects.
1039
+ {
1040
+ std::optional<absl::FixedArray<std::string_view, 4 >> args = PrepareArgs ();
1041
+ if (args.has_value ()) {
1042
+ raise_error = !CallRedisFunction (raise_error, async, explorer, SliceSpan{*args});
1043
+ }
1044
+ }
1045
+ if (!raise_error) {
1046
+ return 1 ;
1047
+ }
1048
+ return RaiseErrorAndAbort (lua_); // this function never returns, it unwinds the Lua call stack
1029
1049
}
1030
1050
1031
1051
int Interpreter::RedisCallCommand (lua_State* lua) {
0 commit comments