* @file
*
* This file implements macro evaluation related apis for macro.
*/
#include "cangjie/Macro/MacroEvaluation.h"
#include "cangjie/Macro/TokenSerialization.h"
using namespace Cangjie;
using namespace AST;
using namespace InvokeRuntime;
namespace {
using TaskFunc = void* (*)(void*);
using InvokeNewTaskFromC = void* (*)(TaskFunc, void*);
using ReleaseHandleFromC = void (*)(void*);
* For compiled macro, evaluate macrocall by invoking method in runtime.
*/
struct ResetNotify {
void* cFunc;
void* param;
ResetNotify(void* notifyFunc, void* notifyParam) : cFunc(notifyFunc), param(notifyParam)
{
}
};
struct MacroInvoke {
public:
MacroCall* macCall;
std::mutex mtx;
std::condition_variable cv;
bool isDataReady{false};
explicit MacroInvoke(MacroCall* mc) : macCall(mc)
{
}
};
void ResetVariableFinishNotify(void* mi)
{
auto mInvoke = static_cast<MacroInvoke*>(mi);
std::unique_lock<std::mutex> lck(mInvoke->mtx);
mInvoke->isDataReady = true;
mInvoke->cv.notify_one();
}
HANDLE InvokeMacroFunc(HANDLE mc)
{
auto pMacCall = static_cast<MacroCall*>(mc);
auto pInvocation = pMacCall->GetInvocation();
auto attrTksBytes = TokenSerialization::GetTokensBytes(pInvocation->attrs);
auto inputTksBytes = TokenSerialization::GetTokensBytes(pInvocation->args);
uint8_t* retBuffer{nullptr};
if (pInvocation->hasAttr) {
auto attrMacroFunc = reinterpret_cast<AttrFuncPtrT>(pMacCall->invokeFunc);
retBuffer = attrMacroFunc(attrTksBytes.data(), static_cast<int64_t>(attrTksBytes.size()),
inputTksBytes.data(), static_cast<int64_t>(inputTksBytes.size()), pMacCall);
} else {
auto commonMacroFunc = reinterpret_cast<CommonFuncPtrT>(pMacCall->invokeFunc);
retBuffer = commonMacroFunc(inputTksBytes.data(), static_cast<int64_t>(inputTksBytes.size()), pMacCall);
}
pInvocation->newTokens = TokenSerialization::GetTokensFromBytes(retBuffer);
if (retBuffer != nullptr) {
free(retBuffer);
retBuffer = nullptr;
}
pMacCall->isDataReady = true;
return nullptr;
}
HANDLE InvokeMacro(HANDLE mi)
{
auto mInvoke = static_cast<MacroInvoke*>(mi);
std::unique_lock<std::mutex> lck(mInvoke->mtx);
(void)InvokeMacroFunc(mInvoke->macCall);
mInvoke->cv.notify_one();
return nullptr;
}
}
void MacroEvaluation::CollectMacroLibs()
{
if (!ci->invocation.globalOptions.macroLib.empty()) {
auto dynFiles = GetMacroDefDynamicFiles();
for (auto& dyfile : dynFiles) {
auto handle = InvokeRuntime::OpenSymbolTable(dyfile);
if (handle == nullptr) {
(void)ci->diag.Diagnose(DiagKind::can_not_open_macro_library, dyfile);
return;
}
InvokeRuntime::SetOpenedLibHandles(handle);
}
}
}
void MacroEvaluation::EvaluateWithRuntime(MacroCall& macCall)
{
if (useChildProcess) {
if (SendMacroCallTask(macCall)) {
if (!enableParallelMacro) {
WaitMacroCallEvalResult(macCall);
}
}
return;
}
auto invokeMethodFuncFromC = reinterpret_cast<InvokeNewTaskFromC>(RuntimeInit::GetInstance().runtimeMethodFunc);
if (enableParallelMacro) {
macCall.coroutineHandle = invokeMethodFuncFromC(InvokeMacroFunc, &macCall);
CJC_NULLPTR_CHECK(macCall.coroutineHandle);
} else {
MacroInvoke mi(&macCall);
std::unique_lock<std::mutex> lck(mi.mtx);
macCall.coroutineHandle = invokeMethodFuncFromC(InvokeMacro, &mi);
CJC_NULLPTR_CHECK(macCall.coroutineHandle);
mi.cv.wait(lck, [&macCall] { return macCall.isDataReady; });
ReleaseThreadHandle(macCall);
}
}
void MacroEvaluation::InitGlobalVariable()
{
auto invokeMethodFuncFromC = reinterpret_cast<InvokeNewTaskFromC>(RuntimeInit::GetInstance().runtimeMethodFunc);
auto invokeReleaseHandleFromC = reinterpret_cast<ReleaseHandleFromC>(RuntimeInit::GetInstance().runtimeReleaseFunc);
for (auto& [mpkgname, hasInit] : usedMacroPkgs) {
if (hasInit) {
continue;
}
auto macrodef = mpkgname;
auto method = MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_PACKAGE_INIT_PREFIX + MangleUtils::GetOptPkgName(macrodef) +
SPECIAL_NAME_FOR_INIT_RESET_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX;
bool findGlobalFunc = false;
for (auto& handle : InvokeRuntime::GetOpenedLibHandles()) {
auto initGlobalFunc = reinterpret_cast<InitGlobalFuncPtr>(GetMethod(handle, method.c_str()));
if (!initGlobalFunc) {
continue;
}
MacroInvoke mi(nullptr);
std::unique_lock<std::mutex> lck(mi.mtx);
auto resetNotify = std::make_unique<ResetNotify>(reinterpret_cast<void*>(ResetVariableFinishNotify), &mi);
auto coroutineHandle = invokeMethodFuncFromC(initGlobalFunc, resetNotify.get());
CJC_NULLPTR_CHECK(coroutineHandle);
mi.cv.wait(lck, [&mi] { return mi.isDataReady; });
invokeReleaseHandleFromC(coroutineHandle);
findGlobalFunc = true;
usedMacroPkgs.at(mpkgname) = true;
break;
}
if (!findGlobalFunc) {
Warningln("could not find global reset method in macro dylib: ", method);
}
}
}
void MacroEvaluation::ReleaseThreadHandle(MacroCall& macCall)
{
auto invokeReleaseHandle =
reinterpret_cast<ReleaseHandleFromC>(RuntimeInit::GetInstance().runtimeReleaseFunc);
invokeReleaseHandle(macCall.coroutineHandle);
}