this is a painful change. we should really add EvalState or EvalMemory as an argument to various functions as we need it, but because we want to preserve the stablity API, we hack it in as a field of nix_value.
296 lines
8.0 KiB
C++
296 lines
8.0 KiB
C++
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
#include "nix/expr/eval.hh"
|
|
#include "nix/expr/eval-gc.hh"
|
|
#include "nix/store/globals.hh"
|
|
#include "nix/expr/eval-settings.hh"
|
|
#include "nix/util/ref.hh"
|
|
|
|
#include "nix_api_expr.h"
|
|
#include "nix_api_expr_internal.h"
|
|
#include "nix_api_store.h"
|
|
#include "nix_api_store_internal.h"
|
|
#include "nix_api_util.h"
|
|
#include "nix_api_util_internal.h"
|
|
|
|
#if NIX_USE_BOEHMGC
|
|
# include <boost/unordered/concurrent_flat_map.hpp>
|
|
#endif
|
|
|
|
/**
|
|
* @brief Allocate and initialize using self-reference
|
|
*
|
|
* This allows a brace initializer to reference the object being constructed.
|
|
*
|
|
* @warning Use with care, as the pointer points to an object that is not fully constructed yet.
|
|
*
|
|
* @tparam T Type to allocate
|
|
* @tparam F A function type for `init`, taking a T* and returning the initializer for T
|
|
* @param init Function that takes a T* and returns the initializer for T
|
|
* @return Pointer to allocated and initialized object
|
|
*/
|
|
template<typename T, typename F>
|
|
static T * unsafe_new_with_self(F && init)
|
|
{
|
|
// Allocate
|
|
void * p = ::operator new(sizeof(T), static_cast<std::align_val_t>(alignof(T)));
|
|
// Initialize with placement new
|
|
return new (p) T(init(static_cast<T *>(p)));
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
nix_err nix_libexpr_init(nix_c_context * context)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
{
|
|
auto ret = nix_libutil_init(context);
|
|
if (ret != NIX_OK)
|
|
return ret;
|
|
}
|
|
{
|
|
auto ret = nix_libstore_init(context);
|
|
if (ret != NIX_OK)
|
|
return ret;
|
|
}
|
|
try {
|
|
nix::initGC();
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
nix_err nix_expr_eval_from_string(
|
|
nix_c_context * context, EvalState * state, const char * expr, const char * path, nix_value * value)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path)));
|
|
state->state.eval(parsedExpr, *value->value);
|
|
state->state.forceValue(*value->value, nix::noPos);
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, nix_value * arg, nix_value * value)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
state->state.callFunction(*fn->value, *arg->value, *value->value, nix::noPos);
|
|
state->state.forceValue(*value->value, nix::noPos);
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
nix_err nix_value_call_multi(
|
|
nix_c_context * context, EvalState * state, nix_value * fn, size_t nargs, nix_value ** args, nix_value * value)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
|
|
std::vector<nix::Value *> internal_args;
|
|
internal_args.reserve(nargs);
|
|
for (size_t i = 0; i < nargs; i++)
|
|
internal_args.push_back(args[i]->value);
|
|
|
|
try {
|
|
state->state.callFunction(*fn->value, {internal_args.data(), nargs}, *value->value, nix::noPos);
|
|
state->state.forceValue(*value->value, nix::noPos);
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value * value)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
state->state.forceValue(*value->value, nix::noPos);
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
state->state.forceValueDeep(*value->value);
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Store * store)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
return unsafe_new_with_self<nix_eval_state_builder>([&](auto * self) {
|
|
return nix_eval_state_builder{
|
|
.store = nix::ref<nix::Store>(store->ptr),
|
|
.settings = nix::EvalSettings{/* &bool */ self->readOnlyMode},
|
|
.fetchSettings = nix::fetchers::Settings{},
|
|
.readOnlyMode = true,
|
|
};
|
|
});
|
|
}
|
|
NIXC_CATCH_ERRS_NULL
|
|
}
|
|
|
|
void nix_eval_state_builder_free(nix_eval_state_builder * builder)
|
|
{
|
|
if (builder)
|
|
builder->~nix_eval_state_builder();
|
|
operator delete(builder, static_cast<std::align_val_t>(alignof(nix_eval_state_builder)));
|
|
}
|
|
|
|
nix_err nix_eval_state_builder_load(nix_c_context * context, nix_eval_state_builder * builder)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
// TODO: load in one go?
|
|
builder->settings.readOnlyMode = nix::settings.readOnlyMode;
|
|
loadConfFile(builder->settings);
|
|
loadConfFile(builder->fetchSettings);
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
nix_err nix_eval_state_builder_set_lookup_path(
|
|
nix_c_context * context, nix_eval_state_builder * builder, const char ** lookupPath_c)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
nix::Strings lookupPath;
|
|
if (lookupPath_c != nullptr)
|
|
for (size_t i = 0; lookupPath_c[i] != nullptr; i++)
|
|
lookupPath.push_back(lookupPath_c[i]);
|
|
builder->lookupPath = nix::LookupPath::parse(lookupPath);
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder * builder)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
return unsafe_new_with_self<EvalState>([&](auto * self) {
|
|
return EvalState{
|
|
.fetchSettings = std::move(builder->fetchSettings),
|
|
.settings = std::move(builder->settings),
|
|
.state = nix::EvalState(builder->lookupPath, builder->store, self->fetchSettings, self->settings),
|
|
};
|
|
});
|
|
}
|
|
NIXC_CATCH_ERRS_NULL
|
|
}
|
|
|
|
EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c, Store * store)
|
|
{
|
|
auto builder = nix_eval_state_builder_new(context, store);
|
|
if (builder == nullptr)
|
|
return nullptr;
|
|
|
|
if (nix_eval_state_builder_load(context, builder) != NIX_OK)
|
|
return nullptr;
|
|
|
|
if (nix_eval_state_builder_set_lookup_path(context, builder, lookupPath_c) != NIX_OK)
|
|
return nullptr;
|
|
|
|
auto * state = nix_eval_state_build(context, builder);
|
|
nix_eval_state_builder_free(builder);
|
|
return state;
|
|
}
|
|
|
|
void nix_state_free(EvalState * state)
|
|
{
|
|
if (state)
|
|
state->~EvalState();
|
|
operator delete(state, static_cast<std::align_val_t>(alignof(EvalState)));
|
|
}
|
|
|
|
#if NIX_USE_BOEHMGC
|
|
boost::concurrent_flat_map<
|
|
const void *,
|
|
unsigned int,
|
|
std::hash<const void *>,
|
|
std::equal_to<const void *>,
|
|
traceable_allocator<std::pair<const void * const, unsigned int>>>
|
|
nix_refcounts{};
|
|
|
|
nix_err nix_gc_incref(nix_c_context * context, const void * p)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
nix_refcounts.insert_or_visit({p, 1}, [](auto & kv) { kv.second++; });
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
nix_err nix_gc_decref(nix_c_context * context, const void * p)
|
|
{
|
|
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
try {
|
|
bool fail = true;
|
|
nix_refcounts.erase_if(p, [&](auto & kv) {
|
|
fail = false;
|
|
return !--kv.second;
|
|
});
|
|
if (fail)
|
|
throw std::runtime_error("nix_gc_decref: object was not referenced");
|
|
}
|
|
NIXC_CATCH_ERRS
|
|
}
|
|
|
|
void nix_gc_now()
|
|
{
|
|
GC_gcollect();
|
|
}
|
|
|
|
#else
|
|
nix_err nix_gc_incref(nix_c_context * context, const void *)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
return NIX_OK;
|
|
}
|
|
|
|
nix_err nix_gc_decref(nix_c_context * context, const void *)
|
|
{
|
|
if (context)
|
|
context->last_err_code = NIX_OK;
|
|
return NIX_OK;
|
|
}
|
|
|
|
void nix_gc_now() {}
|
|
#endif
|
|
|
|
nix_err nix_value_incref(nix_c_context * context, nix_value * x)
|
|
{
|
|
return nix_gc_incref(context, (const void *) x);
|
|
}
|
|
|
|
nix_err nix_value_decref(nix_c_context * context, nix_value * x)
|
|
{
|
|
return nix_gc_decref(context, (const void *) x);
|
|
}
|
|
|
|
void nix_gc_register_finalizer(void * obj, void * cd, void (*finalizer)(void * obj, void * cd))
|
|
{
|
|
#if NIX_USE_BOEHMGC
|
|
GC_REGISTER_FINALIZER(obj, finalizer, cd, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
} // extern "C"
|