blob: 25e3c3e2eb13a9095218014741918083ddf5f254 [file]
//-------------------------------------------------------------------------------------------------------
// 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_NOASSERT(entryPoint, function, callInfo, ...) \
entryPoint(function, callInfo, ##__VA_ARGS__)
#else
#error CALL_ENTRYPOINT_NOASSERT 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--;
}
}
};
}