Arguments.h 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. #endif
  60. #ifdef _WIN32
  61. #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
  62. entryPoint(function, callInfo, ##__VA_ARGS__)
  63. #elif defined(_M_X64) || defined(_M_IX86)
  64. // Call an entryPoint (JavascriptMethod) with custom calling convention.
  65. // RDI == function, RSI == callInfo, (RDX/RCX/R8/R9==null/unused),
  66. // all parameters on stack.
  67. #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
  68. entryPoint(function, callInfo, nullptr, nullptr, nullptr, nullptr, \
  69. function, callInfo, ##__VA_ARGS__)
  70. #elif defined(_ARM_)
  71. // xplat-todo: fix me ARM
  72. #define CALL_ENTRYPOINT(entryPoint, function, callInfo, ...) \
  73. entryPoint(function, callInfo, ##__VA_ARGS__)
  74. #else
  75. #error CALL_ENTRYPOINT not yet implemented
  76. #endif
  77. #define CALL_FUNCTION_NOASSERT(function, callInfo, ...) \
  78. CALL_ENTRYPOINT_NOASSERT(function->GetEntryPoint(), \
  79. function, callInfo, ##__VA_ARGS__)
  80. #if ENABLE_JS_REENTRANCY_CHECK
  81. #define CALL_FUNCTION(threadContext, function, callInfo, ...) \
  82. (threadContext->AssertJsReentrancy(), \
  83. CALL_FUNCTION_NOASSERT(function, callInfo, __VA_ARGS__));
  84. #define CALL_ENTRYPOINT(threadContext, entryPoint, function, callInfo, ...) \
  85. (threadContext->AssertJsReentrancy(), \
  86. CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, __VA_ARGS__));
  87. #define JS_REENTRANT(reentrancyLock, ...) \
  88. reentrancyLock.unlock(); \
  89. __VA_ARGS__; \
  90. reentrancyLock.relock();
  91. #define JS_REENTRANT_UNLOCK(reentrancyLock, ...) \
  92. reentrancyLock.unlock(); \
  93. __VA_ARGS__;
  94. #define JS_REENTRANCY_LOCK(reentrancyLock, threadContext) \
  95. JsReentLock reentrancyLock(threadContext);
  96. #else
  97. #define CALL_FUNCTION(threadContext, function, callInfo, ...) \
  98. CALL_FUNCTION_NOASSERT(function, callInfo, __VA_ARGS__);
  99. #define CALL_ENTRYPOINT(threadContext, entryPoint, function, callInfo, ...) \
  100. CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, __VA_ARGS__);
  101. #define JS_REENTRANT(reentrancyLock, ...) \
  102. __VA_ARGS__;
  103. #define JS_REENTRANT_UNLOCK(reentrancyLock, ...) \
  104. __VA_ARGS__;
  105. #define JS_REENTRANCY_LOCK(reentrancyLock, threadContext)
  106. #endif // ENABLE_JS_REENTRANCY_CHECK
  107. /*
  108. * RUNTIME_ARGUMENTS is a simple wrapper around the variadic calling convention
  109. * used by JavaScript functions. It is a low level macro that does not try to
  110. * differentiate between script usable Vars and runtime data structures.
  111. * To be able to access only script usable args use the ARGUMENTS macro instead.
  112. *
  113. * The ... list must be
  114. * * "callInfo", typically for JsMethod that has only 2 known args
  115. * "function, callInfo";
  116. * * or the full known args list ending with "callInfo" (for some runtime
  117. * helpers).
  118. */
  119. #define RUNTIME_ARGUMENTS(n, ...) \
  120. DECLARE_ARGS_VARARRAY(_argsVarArray, __VA_ARGS__); \
  121. Js::Arguments n(callInfo, _argsVarArray);
  122. #define ARGUMENTS(n, ...) \
  123. DECLARE_ARGS_VARARRAY(_argsVarArray, __VA_ARGS__); \
  124. Js::ArgumentReader n(&callInfo, _argsVarArray);
  125. namespace Js
  126. {
  127. struct Arguments
  128. {
  129. public:
  130. Arguments(CallInfo callInfo, Var* values) :
  131. Info(callInfo), Values(values) {}
  132. Arguments(ushort count, Var* values) :
  133. Info(count), Values(values) {}
  134. Arguments(VirtualTableInfoCtorEnum v) : Info(v) {}
  135. Arguments(const Arguments& other) : Info(other.Info), Values(other.Values) {}
  136. Var operator [](int idxArg) { return const_cast<Var>(static_cast<const Arguments&>(*this)[idxArg]); }
  137. const Var operator [](int idxArg) const
  138. {
  139. AssertMsg((idxArg < (int)Info.Count) && (idxArg >= 0), "Ensure a valid argument index");
  140. return Values[idxArg];
  141. }
  142. // swb: Arguments is mostly used on stack and does not need write barrier.
  143. // It is recycler allocated with ES6 generators. We handle that specially.
  144. FieldNoBarrier(CallInfo) Info;
  145. FieldNoBarrier(Var*) Values;
  146. static uint32 GetCallInfoOffset() { return offsetof(Arguments, Info); }
  147. static uint32 GetValuesOffset() { return offsetof(Arguments, Values); }
  148. // Prevent heap/recycler allocation, so we don't need write barrier for this
  149. static void* operator new (size_t) = delete;
  150. static void* operator new[] (size_t) = delete;
  151. static void operator delete (void*) = delete;
  152. static void operator delete[] (void*) = delete;
  153. };
  154. struct ArgumentReader : public Arguments
  155. {
  156. ArgumentReader(CallInfo *callInfo, Var* values)
  157. : Arguments(*callInfo, values)
  158. {
  159. AdjustArguments(callInfo);
  160. }
  161. private:
  162. void AdjustArguments(CallInfo *callInfo)
  163. {
  164. AssertMsg(!(Info.Flags & Js::CallFlags_NewTarget) || (Info.Flags & Js::CallFlags_ExtraArg), "NewTarget flag must be used together with ExtraArg.");
  165. if (Info.Flags & Js::CallFlags_ExtraArg)
  166. {
  167. // If "calling eval" is set, then the last param is the frame display, which only
  168. // the eval built-in should see.
  169. Assert(Info.Count > 0);
  170. // The local version should be consistent. On the other hand, lots of code throughout
  171. // jscript uses the callInfo from stack to get argument list etc. We'll need
  172. // to change all the caller to be aware of the id or somehow make sure they don't use
  173. // the stack version. Both seem risky. It would be safer and more robust to just
  174. // change the stack version.
  175. Info.Flags = (CallFlags)(Info.Flags & ~Js::CallFlags_ExtraArg);
  176. Info.Count--;
  177. callInfo->Flags = (CallFlags)(callInfo->Flags & ~Js::CallFlags_ExtraArg);
  178. callInfo->Count--;
  179. }
  180. }
  181. };
  182. }