environment.hpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // sol2
  2. // The MIT License (MIT)
  3. // Copyright (c) 2013-2022 Rapptz, ThePhD and contributors
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy of
  5. // this software and associated documentation files (the "Software"), to deal in
  6. // the Software without restriction, including without limitation the rights to
  7. // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  8. // the Software, and to permit persons to whom the Software is furnished to do so,
  9. // subject to the following conditions:
  10. // The above copyright notice and this permission notice shall be included in all
  11. // copies or substantial portions of the Software.
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  14. // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  15. // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  16. // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  17. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. #ifndef SOL_ENVIRONMENT_HPP
  19. #define SOL_ENVIRONMENT_HPP
  20. #include <sol/table.hpp>
  21. namespace sol {
  22. template <typename base_type>
  23. struct basic_environment : basic_table<base_type> {
  24. private:
  25. typedef basic_table<base_type> base_t;
  26. public:
  27. using base_t::lua_state;
  28. basic_environment() noexcept = default;
  29. basic_environment(const basic_environment&) = default;
  30. basic_environment(basic_environment&&) = default;
  31. basic_environment& operator=(const basic_environment&) = default;
  32. basic_environment& operator=(basic_environment&&) = default;
  33. basic_environment(const stack_reference& r) : basic_environment(r.lua_state(), r.stack_index()) {
  34. }
  35. basic_environment(stack_reference&& r) : basic_environment(r.lua_state(), r.stack_index()) {
  36. }
  37. basic_environment(lua_State* L, new_table nt) : base_t(L, std::move(nt)) {
  38. }
  39. template <bool b>
  40. basic_environment(lua_State* L, new_table t, const basic_reference<b>& fallback) : basic_environment(L, std::move(t)) {
  41. stack_table mt(L, new_table(0, 1));
  42. mt.set(meta_function::index, fallback);
  43. this->set(metatable_key, mt);
  44. mt.pop();
  45. }
  46. basic_environment(env_key_t, const stack_reference& extraction_target)
  47. : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) {
  48. #if SOL_IS_ON(SOL_SAFE_REFERENCES)
  49. constructor_handler handler {};
  50. stack::check<env_key_t>(this->lua_state(), -1, handler);
  51. #endif // Safety
  52. lua_pop(this->lua_state(), 1);
  53. }
  54. template <bool b>
  55. basic_environment(env_key_t, const basic_reference<b>& extraction_target)
  56. : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) {
  57. #if SOL_IS_ON(SOL_SAFE_REFERENCES)
  58. constructor_handler handler {};
  59. stack::check<env_key_t>(this->lua_state(), -1, handler);
  60. #endif // Safety
  61. lua_pop(this->lua_state(), 1);
  62. }
  63. basic_environment(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) {
  64. #if SOL_IS_ON(SOL_SAFE_REFERENCES)
  65. constructor_handler handler {};
  66. stack::check<basic_environment>(L, index, handler);
  67. #endif // Safety
  68. }
  69. basic_environment(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) {
  70. #if SOL_IS_ON(SOL_SAFE_REFERENCES)
  71. auto pp = stack::push_pop(*this);
  72. constructor_handler handler {};
  73. stack::check<basic_environment>(L, -1, handler);
  74. #endif // Safety
  75. }
  76. template <typename T,
  77. meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_environment>>, meta::neg<std::is_same<base_type, stack_reference>>,
  78. meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
  79. basic_environment(T&& r) noexcept : base_t(detail::no_safety, std::forward<T>(r)) {
  80. #if SOL_IS_ON(SOL_SAFE_REFERENCES)
  81. if (!is_environment<meta::unqualified_t<T>>::value) {
  82. auto pp = stack::push_pop(*this);
  83. constructor_handler handler {};
  84. stack::check<basic_environment>(lua_state(), -1, handler);
  85. }
  86. #endif // Safety
  87. }
  88. basic_environment(lua_nil_t r) noexcept : base_t(detail::no_safety, r) {
  89. }
  90. template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
  91. basic_environment(lua_State* L, T&& r) noexcept : base_t(detail::no_safety, L, std::forward<T>(r)) {
  92. #if SOL_IS_ON(SOL_SAFE_REFERENCES)
  93. if (!is_environment<meta::unqualified_t<T>>::value) {
  94. auto pp = stack::push_pop(*this);
  95. constructor_handler handler {};
  96. stack::check<basic_environment>(lua_state(), -1, handler);
  97. }
  98. #endif // Safety
  99. }
  100. template <typename T>
  101. bool set_on(const T& target) const {
  102. lua_State* L = target.lua_state();
  103. auto pp = stack::push_pop(target);
  104. int target_index = pp.index_of(target);
  105. #if SOL_LUA_VERSION_I_ < 502
  106. // Use lua_setfenv
  107. this->push();
  108. int success_result = lua_setfenv(L, target_index);
  109. return success_result != 0;
  110. #else
  111. // If this is a C function, the environment is always placed in
  112. // the first value, as is expected of sol2 (all upvalues have an empty name, "")
  113. if (lua_iscfunction(L, target_index) != 0) {
  114. const char* maybe_upvalue_name = lua_getupvalue(L, target_index, 1);
  115. if (maybe_upvalue_name == nullptr) {
  116. return false;
  117. }
  118. string_view upvalue_name(maybe_upvalue_name);
  119. if (upvalue_name == "") {
  120. this->push();
  121. const char* success = lua_setupvalue(L, target_index, 1);
  122. if (success == nullptr) {
  123. // left things alone on the stack, pop them off
  124. lua_pop(L, 2);
  125. return false;
  126. }
  127. lua_pop(L, 1);
  128. return true;
  129. }
  130. lua_pop(L, 1);
  131. return false;
  132. }
  133. else {
  134. // Must search for the right upvalue target on index
  135. for (int upvalue_index = 1;; ++upvalue_index) {
  136. const char* maybe_upvalue_name = lua_getupvalue(L, target_index, upvalue_index);
  137. if (maybe_upvalue_name == nullptr) {
  138. break;
  139. }
  140. string_view upvalue_name(maybe_upvalue_name);
  141. if (upvalue_name == "_ENV") {
  142. lua_pop(L, 1);
  143. this->push();
  144. const char* success = lua_setupvalue(L, target_index, upvalue_index);
  145. if (success == nullptr) {
  146. // left things alone on the stack, pop them off
  147. lua_pop(L, 1);
  148. break;
  149. }
  150. // whether or not we succeeded, we found _ENV
  151. // so we need to break
  152. return true;
  153. }
  154. lua_pop(L, 1);
  155. }
  156. // if we get here,
  157. // we did not find an _ENV here...
  158. return false;
  159. }
  160. #endif
  161. }
  162. };
  163. template <typename T, typename E>
  164. bool set_environment(const basic_environment<E>& env, const T& target) {
  165. return env.set_on(target);
  166. }
  167. template <typename E = reference, typename T>
  168. basic_environment<E> get_environment(const T& target) {
  169. lua_State* L = target.lua_state();
  170. auto pp = stack::pop_n(L, stack::push_environment_of(target));
  171. return basic_environment<E>(L, -1);
  172. }
  173. struct this_environment {
  174. optional<environment> env;
  175. this_environment() : env(nullopt) {
  176. }
  177. this_environment(environment e) : env(std::move(e)) {
  178. }
  179. this_environment(const this_environment&) = default;
  180. this_environment(this_environment&&) = default;
  181. this_environment& operator=(const this_environment&) = default;
  182. this_environment& operator=(this_environment&&) = default;
  183. explicit operator bool() const {
  184. return static_cast<bool>(env);
  185. }
  186. operator optional<environment>&() {
  187. return env;
  188. }
  189. operator const optional<environment>&() const {
  190. return env;
  191. }
  192. operator environment&() {
  193. return env.value();
  194. }
  195. operator const environment&() const {
  196. return env.value();
  197. }
  198. };
  199. namespace stack {
  200. template <>
  201. struct unqualified_getter<env_key_t> {
  202. static environment get(lua_State* L, int index, record& tracking) {
  203. tracking.use(1);
  204. return get_environment(stack_reference(L, raw_index(index)));
  205. }
  206. };
  207. template <>
  208. struct unqualified_getter<this_environment> {
  209. static this_environment get(lua_State* L, int, record& tracking) {
  210. tracking.use(0);
  211. lua_Debug info;
  212. // Level 0 means current function (this C function, which may or may not be useful for us?)
  213. // Level 1 means next call frame up the stack. (Can be nothing if function called directly from C++ with lua_p/call)
  214. int pre_stack_size = lua_gettop(L);
  215. if (lua_getstack(L, 1, &info) != 1) {
  216. if (lua_getstack(L, 0, &info) != 1) {
  217. lua_settop(L, pre_stack_size);
  218. return this_environment();
  219. }
  220. }
  221. if (lua_getinfo(L, "f", &info) == 0) {
  222. lua_settop(L, pre_stack_size);
  223. return this_environment();
  224. }
  225. stack_reference f(L, -1);
  226. environment env(env_key, f);
  227. if (!env.valid()) {
  228. lua_settop(L, pre_stack_size);
  229. return this_environment();
  230. }
  231. return this_environment(std::move(env));
  232. }
  233. };
  234. } // namespace stack
  235. } // namespace sol
  236. #endif // SOL_ENVIRONMENT_HPP