Arguments.h 12 KB

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