| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #pragma once
- // To extract variadic args array after known args list:
- // argx, callInfo, ...
- // NOTE: The last known arg name is hard-coded to "callInfo".
- #ifdef _WIN32
- #define DECLARE_ARGS_VARARRAY(va, ...) \
- va_list _vl; \
- va_start(_vl, callInfo); \
- Js::Var* va = (Js::Var*)_vl
- #else
- // We use a custom calling convention to invoke JavascriptMethod based on
- // System ABI. At entry of JavascriptMethod the stack layout is:
- // [Return Address] [function] [callInfo] [arg0] [arg1] ...
- //
- #define DECLARE_ARGS_VARARRAY_N(va, n) \
- Js::Var* va = _get_va(_AddressOfReturnAddress(), n); \
- Assert(*reinterpret_cast<Js::CallInfo*>(va - 1) == callInfo)
- #define DECLARE_ARGS_VARARRAY(va, ...) \
- DECLARE_ARGS_VARARRAY_N(va, _count_args(__VA_ARGS__))
- inline Js::Var* _get_va(void* addrOfReturnAddress, int n)
- {
- // All args are right after ReturnAddress by custom calling convention
- Js::Var* pArgs = reinterpret_cast<Js::Var*>(addrOfReturnAddress) + 1;
- #ifdef _ARM_
- n += 2; // ip + fp
- #endif
- return pArgs + n;
- }
- inline int _count_args(Js::CallInfo callInfo)
- {
- // This is to support typical runtime "ARGUMENTS(args, callInfo)" usage.
- // Only "callInfo" listed, but we have 2 known args "function, callInfo".
- return 2;
- }
- template <class T1>
- inline int _count_args(const T1&, Js::CallInfo callInfo)
- {
- return 2;
- }
- template <class T1, class T2>
- inline int _count_args(const T1&, const T2&, Js::CallInfo callInfo)
- {
- return 3;
- }
- template <class T1, class T2, class T3>
- inline int _count_args(const T1&, const T2&, const T3&, Js::CallInfo callInfo)
- {
- return 4;
- }
- template <class T1, class T2, class T3, class T4>
- inline int _count_args(const T1&, const T2&, const T3&, const T4&, Js::CallInfo callInfo)
- {
- return 5;
- }
- #endif
- #ifdef _WIN32
- #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
- entryPoint(function, callInfo, ##__VA_ARGS__)
- #elif defined(_M_X64) || defined(_M_IX86)
- // Call an entryPoint (JavascriptMethod) with custom calling convention.
- // RDI == function, RSI == callInfo, (RDX/RCX/R8/R9==null/unused),
- // all parameters on stack.
- #define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \
- entryPoint(function, callInfo, nullptr, nullptr, nullptr, nullptr, \
- function, callInfo, ##__VA_ARGS__)
- #elif defined(_ARM_)
- // xplat-todo: fix me ARM
- #define CALL_ENTRYPOINT(entryPoint, function, callInfo, ...) \
- entryPoint(function, callInfo, ##__VA_ARGS__)
- #else
- #error CALL_ENTRYPOINT not yet implemented
- #endif
- #define CALL_FUNCTION_NOASSERT(function, callInfo, ...) \
- CALL_ENTRYPOINT_NOASSERT(function->GetEntryPoint(), \
- function, callInfo, ##__VA_ARGS__)
- #if ENABLE_JS_REENTRANCY_CHECK
- #define CALL_FUNCTION(threadContext, function, callInfo, ...) \
- (threadContext->AssertJsReentrancy(), \
- CALL_FUNCTION_NOASSERT(function, callInfo, __VA_ARGS__));
- #define CALL_ENTRYPOINT(threadContext, entryPoint, function, callInfo, ...) \
- (threadContext->AssertJsReentrancy(), \
- CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, __VA_ARGS__));
- #define JS_REENTRANT(reentrancyLock, ...) \
- reentrancyLock.unlock(); \
- __VA_ARGS__; \
- reentrancyLock.relock();
- #define JS_REENTRANT_UNLOCK(reentrancyLock, ...) \
- reentrancyLock.unlock(); \
- __VA_ARGS__;
- #define JS_REENTRANCY_LOCK(reentrancyLock, threadContext) \
- JsReentLock reentrancyLock(threadContext);
- #else
- #define CALL_FUNCTION(threadContext, function, callInfo, ...) \
- CALL_FUNCTION_NOASSERT(function, callInfo, __VA_ARGS__);
- #define CALL_ENTRYPOINT(threadContext, entryPoint, function, callInfo, ...) \
- CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, __VA_ARGS__);
- #define JS_REENTRANT(reentrancyLock, ...) \
- __VA_ARGS__;
- #define JS_REENTRANT_UNLOCK(reentrancyLock, ...) \
- __VA_ARGS__;
- #define JS_REENTRANCY_LOCK(reentrancyLock, threadContext)
- #endif // ENABLE_JS_REENTRANCY_CHECK
- /*
- * RUNTIME_ARGUMENTS is a simple wrapper around the variadic calling convention
- * used by JavaScript functions. It is a low level macro that does not try to
- * differentiate between script usable Vars and runtime data structures.
- * To be able to access only script usable args use the ARGUMENTS macro instead.
- *
- * The ... list must be
- * * "callInfo", typically for JsMethod that has only 2 known args
- * "function, callInfo";
- * * or the full known args list ending with "callInfo" (for some runtime
- * helpers).
- */
- #define RUNTIME_ARGUMENTS(n, ...) \
- DECLARE_ARGS_VARARRAY(_argsVarArray, __VA_ARGS__); \
- Js::Arguments n(callInfo, _argsVarArray);
- #define ARGUMENTS(n, ...) \
- DECLARE_ARGS_VARARRAY(_argsVarArray, __VA_ARGS__); \
- Js::ArgumentReader n(&callInfo, _argsVarArray);
- namespace Js
- {
- struct Arguments
- {
- public:
- Arguments(CallInfo callInfo, Var* values) :
- Info(callInfo), Values(values) {}
- Arguments(ushort count, Var* values) :
- Info(count), Values(values) {}
- Arguments(VirtualTableInfoCtorEnum v) : Info(v) {}
- Arguments(const Arguments& other) : Info(other.Info), Values(other.Values) {}
- Var operator [](int idxArg) { return const_cast<Var>(static_cast<const Arguments&>(*this)[idxArg]); }
- const Var operator [](int idxArg) const
- {
- AssertMsg((idxArg < (int)Info.Count) && (idxArg >= 0), "Ensure a valid argument index");
- return Values[idxArg];
- }
- // swb: Arguments is mostly used on stack and does not need write barrier.
- // It is recycler allocated with ES6 generators. We handle that specially.
- FieldNoBarrier(CallInfo) Info;
- FieldNoBarrier(Var*) Values;
- static uint32 GetCallInfoOffset() { return offsetof(Arguments, Info); }
- static uint32 GetValuesOffset() { return offsetof(Arguments, Values); }
- // Prevent heap/recycler allocation, so we don't need write barrier for this
- static void* operator new (size_t) = delete;
- static void* operator new[] (size_t) = delete;
- static void operator delete (void*) = delete;
- static void operator delete[] (void*) = delete;
- };
- struct ArgumentReader : public Arguments
- {
- ArgumentReader(CallInfo *callInfo, Var* values)
- : Arguments(*callInfo, values)
- {
- AdjustArguments(callInfo);
- }
- private:
- void AdjustArguments(CallInfo *callInfo)
- {
- AssertMsg(!(Info.Flags & Js::CallFlags_NewTarget) || (Info.Flags & Js::CallFlags_ExtraArg), "NewTarget flag must be used together with ExtraArg.");
- if (Info.Flags & Js::CallFlags_ExtraArg)
- {
- // If "calling eval" is set, then the last param is the frame display, which only
- // the eval built-in should see.
- Assert(Info.Count > 0);
- // The local version should be consistent. On the other hand, lots of code throughout
- // jscript uses the callInfo from stack to get argument list etc. We'll need
- // to change all the caller to be aware of the id or somehow make sure they don't use
- // the stack version. Both seem risky. It would be safer and more robust to just
- // change the stack version.
- Info.Flags = (CallFlags)(Info.Flags & ~Js::CallFlags_ExtraArg);
- Info.Count--;
- callInfo->Flags = (CallFlags)(callInfo->Flags & ~Js::CallFlags_ExtraArg);
- callInfo->Count--;
- }
- }
- };
- }
|