Arguments.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Copyright (c) ChakraCore Project Contributors. All rights reserved.
  4. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  5. //-------------------------------------------------------------------------------------------------------
  6. #pragma once
  7. // To extract variadic args array after known args list:
  8. // argx, callInfo, ...
  9. // NOTE: The last known arg name is hard-coded to "callInfo".
  10. #ifdef _WIN32
  11. #define DECLARE_ARGS_VARARRAY(va, ...) \
  12. va_list _vl; \
  13. va_start(_vl, callInfo); \
  14. Js::Var* va = (Js::Var*)_vl
  15. #elif defined(_ARM64_) && defined(__linux__)
  16. // AAPCS64 (Linux ARM64 ABI) reference:
  17. // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#appendix-variable-argument-lists
  18. #define DECLARE_ARGS_VARARRAY(va, ...) \
  19. va_list _vl; \
  20. va_start(_vl, callInfo); \
  21. Js::Var* va = (Js::Var*)_vl.__stack + 2; \
  22. Assert(*reinterpret_cast<Js::CallInfo*>(va - 1) == callInfo)
  23. #else
  24. // We use a custom calling convention to invoke JavascriptMethod based on
  25. // System ABI. At entry of JavascriptMethod the stack layout is:
  26. // [Return Address] [function] [callInfo] [arg0] [arg1] ...
  27. //
  28. #define DECLARE_ARGS_VARARRAY_N(va, n) \
  29. Js::Var* va = _get_va(_AddressOfReturnAddress(), n); \
  30. Assert(*reinterpret_cast<Js::CallInfo*>(va - 1) == callInfo)
  31. #define DECLARE_ARGS_VARARRAY(va, ...) \
  32. DECLARE_ARGS_VARARRAY_N(va, _count_args(__VA_ARGS__))
  33. inline Js::Var* _get_va(void* addrOfReturnAddress, int n)
  34. {
  35. // All args are right after ReturnAddress by custom calling convention
  36. Js::Var* pArgs = reinterpret_cast<Js::Var*>(addrOfReturnAddress) + 1;
  37. #ifdef _ARM_
  38. n += 2; // ip + fp
  39. #endif
  40. return pArgs + n;
  41. }
  42. inline int _count_args(Js::CallInfo callInfo)
  43. {
  44. // This is to support typical runtime "ARGUMENTS(args, callInfo)" usage.
  45. // Only "callInfo" listed, but we have 2 known args "function, callInfo".
  46. return 2;
  47. }
  48. template <class T1>
  49. inline int _count_args(const T1&, Js::CallInfo callInfo)
  50. {
  51. return 2;
  52. }
  53. template <class T1, class T2>
  54. inline int _count_args(const T1&, const T2&, Js::CallInfo callInfo)
  55. {
  56. return 3;
  57. }
  58. template <class T1, class T2, class T3>
  59. inline int _count_args(const T1&, const T2&, const T3&, Js::CallInfo callInfo)
  60. {
  61. return 4;
  62. }
  63. template <class T1, class T2, class T3, class T4>
  64. inline int _count_args(const T1&, const T2&, const T3&, const T4&, Js::CallInfo callInfo)
  65. {
  66. return 5;
  67. }
  68. template <class T1, class T2, class T3, class T4, class T5>
  69. inline int _count_args(const T1&, const T2&, const T3&, const T4&, const T5&, Js::CallInfo callInfo)
  70. {
  71. return 6;
  72. }
  73. #endif
  74. #ifdef _WIN32
  75. #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
  76. entryPoint(function, callInfo, ##__VA_ARGS__)
  77. #elif defined(_M_X64) || defined(_M_IX86)
  78. // Call an entryPoint (JavascriptMethod) with custom calling convention.
  79. // RDI == function, RSI == callInfo, (RDX/RCX/R8/R9==null/unused),
  80. // all parameters on stack.
  81. #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
  82. entryPoint(function, callInfo, nullptr, nullptr, nullptr, nullptr, \
  83. function, callInfo, ##__VA_ARGS__)
  84. #elif defined(_ARM_)
  85. // xplat-todo: fix me ARM
  86. #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
  87. entryPoint(function, callInfo, ##__VA_ARGS__)
  88. #elif defined (_ARM64_)
  89. #ifdef __linux__
  90. // Linux ARM64 uses AAPCS64: first 8 args in x0-x7, rest via stack.
  91. // Fill x2-x7 with nulls here to force the expected stack layout:
  92. // [RetAddr] [function] [callInfo] [args...]
  93. #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
  94. entryPoint(function, callInfo, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, \
  95. function, callInfo, ##__VA_ARGS__)
  96. #else
  97. // macOS has own bespoke vararg cc (DarwinPCS), varargs always passed via stack.
  98. // Duplicate function/callInfo so they are pushed onto stack as part of varargs.
  99. #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
  100. entryPoint(function, callInfo, function, callInfo, ##__VA_ARGS__)
  101. #endif
  102. #else
  103. #error CALL_ENTRYPOINT_NOASSERT not yet implemented
  104. #endif
  105. #define CALL_FUNCTION_NOASSERT(function, callInfo, ...) \
  106. CALL_ENTRYPOINT_NOASSERT(function->GetEntryPoint(), \
  107. function, callInfo, ##__VA_ARGS__)
  108. #if DBG && ENABLE_JS_REENTRANCY_CHECK
  109. #define SETOBJECT_FOR_MUTATION(reentrancyLock, arrayObject) reentrancyLock.setObjectForMutation(arrayObject)
  110. #define SET_SECOND_OBJECT_FOR_MUTATION(reentrancyLock, arrayObject) reentrancyLock.setSecondObjectForMutation(arrayObject)
  111. #define MUTATE_ARRAY_OBJECT(reentrancyLock) reentrancyLock.MutateArrayObject()
  112. #else
  113. #define SETOBJECT_FOR_MUTATION(reentrancyLock, arrayObject)
  114. #define SET_SECOND_OBJECT_FOR_MUTATION(reentrancyLock, arrayObject)
  115. #define MUTATE_ARRAY_OBJECT(reentrancyLock)
  116. #endif
  117. #if ENABLE_JS_REENTRANCY_CHECK
  118. #define JS_REENTRANCY_CHECK(threadContext, ...) \
  119. (threadContext->CheckAndResetReentrancySafeOrHandled(), \
  120. threadContext->AssertJsReentrancy(), \
  121. __VA_ARGS__);
  122. #if DBG && ENABLE_NATIVE_CODEGEN
  123. #define CALL_FUNCTION(threadContext, function, callInfo, ...) \
  124. (threadContext->CheckAndResetReentrancySafeOrHandled(), \
  125. threadContext->AssertJsReentrancy(), \
  126. CheckIsExecutable(function, function->GetEntryPoint()), \
  127. CALL_FUNCTION_NOASSERT(function, callInfo, ##__VA_ARGS__));
  128. #else
  129. #define CALL_FUNCTION(threadContext, function, callInfo, ...) \
  130. (threadContext->CheckAndResetReentrancySafeOrHandled(), \
  131. threadContext->AssertJsReentrancy(), \
  132. CALL_FUNCTION_NOASSERT(function, callInfo, ##__VA_ARGS__));
  133. #endif
  134. #define CALL_ENTRYPOINT(threadContext, entryPoint, function, callInfo, ...) \
  135. (threadContext->CheckAndResetReentrancySafeOrHandled(), \
  136. threadContext->AssertJsReentrancy(), \
  137. CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ##__VA_ARGS__));
  138. #define JS_REENTRANT(reentrancyLock, ...) \
  139. reentrancyLock.unlock(); \
  140. __VA_ARGS__; \
  141. MUTATE_ARRAY_OBJECT(reentrancyLock); \
  142. reentrancyLock.relock();
  143. #define JS_REENTRANT_NO_MUTATE(reentrancyLock, ...) \
  144. reentrancyLock.unlock(); \
  145. __VA_ARGS__; \
  146. reentrancyLock.relock();
  147. #define JS_REENTRANT_UNLOCK(reentrancyLock, ...) \
  148. reentrancyLock.unlock(); \
  149. __VA_ARGS__;
  150. #define JS_REENTRANCY_LOCK(reentrancyLock, threadContext) \
  151. JsReentLock reentrancyLock(threadContext);
  152. #else
  153. #if DBG && ENABLE_NATIVE_CODEGEN
  154. #define CALL_FUNCTION(threadContext, function, callInfo, ...) \
  155. (threadContext->CheckAndResetReentrancySafeOrHandled(),\
  156. CheckIsExecutable(function, function->GetEntryPoint()), \
  157. CALL_FUNCTION_NOASSERT(function, callInfo, ##__VA_ARGS__));
  158. #else
  159. #define CALL_FUNCTION(threadContext, function, callInfo, ...) \
  160. (threadContext->CheckAndResetReentrancySafeOrHandled(),\
  161. CALL_FUNCTION_NOASSERT(function, callInfo, ##__VA_ARGS__));
  162. #endif
  163. #define JS_REENTRANCY_CHECK(threadContext, ...) \
  164. __VA_ARGS__;
  165. #define CALL_ENTRYPOINT(threadContext, entryPoint, function, callInfo, ...) \
  166. (threadContext->CheckAndResetReentrancySafeOrHandled(), \
  167. CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ##__VA_ARGS__));
  168. #define JS_REENTRANT(reentrancyLock, ...) \
  169. __VA_ARGS__;
  170. #define JS_REENTRANT_NO_MUTATE(reentrancyLock, ...) \
  171. __VA_ARGS__;
  172. #define JS_REENTRANT_UNLOCK(reentrancyLock, ...) \
  173. __VA_ARGS__;
  174. #define JS_REENTRANCY_LOCK(reentrancyLock, threadContext)
  175. #endif // ENABLE_JS_REENTRANCY_CHECK
  176. /*
  177. * RUNTIME_ARGUMENTS is a simple wrapper around the variadic calling convention
  178. * used by JavaScript functions. It is a low level macro that does not try to
  179. * differentiate between script usable Vars and runtime data structures.
  180. * To be able to access only script usable args use the ARGUMENTS macro instead.
  181. *
  182. * The ... list must be
  183. * * "callInfo", typically for JsMethod that has only 2 known args
  184. * "function, callInfo";
  185. * * or the full known args list ending with "callInfo" (for some runtime
  186. * helpers).
  187. */
  188. #define RUNTIME_ARGUMENTS(n, ...) \
  189. DECLARE_ARGS_VARARRAY(_argsVarArray, __VA_ARGS__); \
  190. Js::Arguments n(callInfo, _argsVarArray);
  191. #define ARGUMENTS(n, ...) \
  192. DECLARE_ARGS_VARARRAY(_argsVarArray, __VA_ARGS__); \
  193. Js::ArgumentReader n(&callInfo, _argsVarArray);
  194. namespace Js
  195. {
  196. struct Arguments
  197. {
  198. public:
  199. Arguments(CallInfo callInfo, Var* values) :
  200. Info(callInfo), Values(values) {}
  201. Arguments(ushort count, Var* values) :
  202. Info(count), Values(values) {}
  203. Arguments(VirtualTableInfoCtorEnum v) : Info(v) {}
  204. Arguments(const Arguments& other) : Info(other.Info), Values(other.Values) {}
  205. Var operator [](int idxArg) { return const_cast<Var>(static_cast<const Arguments&>(*this)[idxArg]); }
  206. Var operator [](int idxArg) const
  207. {
  208. AssertMsg((idxArg < (int)Info.Count) && (idxArg >= 0), "Ensure a valid argument index");
  209. return Values[idxArg];
  210. }
  211. bool IsDirectEvalCall() const
  212. {
  213. // This was recognized as an eval call at compile time. The last one or two args are internal to us.
  214. // Argcount will be one of the following when called from global code
  215. // - eval("...") : argcount 3 : this, evalString, frameDisplay
  216. // - eval.call("..."): argcount 2 : this(which is string) , frameDisplay
  217. return (Info.Flags & (CallFlags_ExtraArg | CallFlags_NewTarget)) == CallFlags_ExtraArg; // ExtraArg == 1 && NewTarget == 0
  218. }
  219. bool HasExtraArg() const
  220. {
  221. return Info.HasExtraArg();
  222. }
  223. bool HasArg() const
  224. {
  225. return Info.Count > 0;
  226. }
  227. ushort GetArgCountWithExtraArgs() const
  228. {
  229. return Info.GetArgCountWithExtraArgs();
  230. }
  231. uint GetLargeArgCountWithExtraArgs() const
  232. {
  233. return Info.GetLargeArgCountWithExtraArgs();
  234. }
  235. FrameDisplay* GetFrameDisplay() const
  236. {
  237. AssertOrFailFast((Info.Flags & CallFlags_ExtraArg) && (!this->HasNewTarget()));
  238. // There is an extra arg, so values should have Count + 1 members
  239. return (FrameDisplay*)(this->Values[Info.Count]);
  240. }
  241. bool IsNewCall() const
  242. {
  243. return (Info.Flags & CallFlags_New) == CallFlags_New;
  244. }
  245. bool HasNewTarget() const
  246. {
  247. return Info.HasNewTarget();
  248. }
  249. // New target value is passed as an extra argument which is nto included in the Info.Count
  250. Var GetNewTarget() const
  251. {
  252. return CallInfo::GetNewTarget(Info.Flags, this->Values, Info.Count);
  253. }
  254. // swb: Arguments is mostly used on stack and does not need write barrier.
  255. // It is recycler allocated with ES6 generators. We handle that specially.
  256. FieldNoBarrier(CallInfo) Info;
  257. FieldNoBarrier(Var*) Values;
  258. static uint32 GetCallInfoOffset() { return offsetof(Arguments, Info); }
  259. static uint32 GetValuesOffset() { return offsetof(Arguments, Values); }
  260. // Prevent heap/recycler allocation, so we don't need write barrier for this
  261. static void* operator new (size_t) = delete;
  262. static void* operator new[] (size_t) = delete;
  263. static void operator delete (void*) = delete;
  264. static void operator delete[] (void*) = delete;
  265. };
  266. struct ArgumentReader : public Arguments
  267. {
  268. ArgumentReader(CallInfo *callInfo, Var* values)
  269. : Arguments(*callInfo, values)
  270. {
  271. AdjustArguments(callInfo);
  272. }
  273. private:
  274. void AdjustArguments(CallInfo *callInfo)
  275. {
  276. AssertMsg(!this->HasNewTarget() || (Info.Flags & Js::CallFlags_ExtraArg), "NewTarget flag must be used together with ExtraArg.");
  277. if (Info.Flags & Js::CallFlags_ExtraArg)
  278. {
  279. // If "calling eval" is set, then the last param is the frame display, which only
  280. // the eval built-in should see.
  281. Assert(Info.Count > 0);
  282. // The local version should be consistent. On the other hand, lots of code throughout
  283. // jscript uses the callInfo from stack to get argument list etc. We'll need
  284. // to change all the caller to be aware of the id or somehow make sure they don't use
  285. // the stack version. Both seem risky. It would be safer and more robust to just
  286. // change the stack version.
  287. Info.Flags = (CallFlags)(Info.Flags & ~Js::CallFlags_ExtraArg);
  288. callInfo->Flags = (CallFlags)(callInfo->Flags & ~Js::CallFlags_ExtraArg);
  289. }
  290. }
  291. };
  292. }