Files
lix/lix/libexpr/value.cc
T
skye 9a408cbeb7 libexpr: Replace Value::mkPrimOp with constructor calls
Change-Id: I19b05e260945e237f7856b5d644a43076a6a6964
2026-03-01 18:02:58 -05:00

217 lines
7.5 KiB
C++

#include "lix/libexpr/value.hh"
#include <ostream>
#include "libexpr/gc-alloc.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/print.hh"
namespace nix
{
static const Value::List emptyListData{.size = 0};
Value Value::EMPTY_LIST{Value::list_t{}, &emptyListData};
const Value::Null Value::NULL_ACB = {{Value::Acb::tNull}};
static_assert(alignof(Value::String) >= Value::TAG_ALIGN);
static_assert(alignof(Bindings) >= Value::TAG_ALIGN);
static_assert(alignof(Value::List) >= Value::TAG_ALIGN);
static_assert(alignof(Value::Thunk) >= Value::TAG_ALIGN);
static_assert(alignof(Value::App) >= Value::TAG_ALIGN);
static_assert(alignof(Value::External) >= Value::Acb::TAG_ALIGN);
static_assert(alignof(Value::Float) >= Value::Acb::TAG_ALIGN);
static_assert(alignof(Value::Null) >= Value::Acb::TAG_ALIGN);
static_assert(alignof(Value::PrimOp) >= Value::Acb::TAG_ALIGN);
static_assert(alignof(Value::Int) >= Value::Acb::TAG_ALIGN);
static_assert(alignof(Value::Lambda) >= Value::Acb::TAG_ALIGN);
static char const ** copyContext(const NixStringContext & context)
{
if (context.empty()) {
return nullptr;
}
auto contextPtr = gcAllocType<char const *>(context.size() + 1);
size_t n = 0;
for (auto & i : context) {
contextPtr[n++] = gcCopyStringIfNeeded(i.to_string());
}
contextPtr[n] = nullptr;
return contextPtr;
}
Value::Value(attrs_t, BindingsBuilder & bindings) : Value(NewValueAs::attrs, bindings.finish()) {}
Value::Value(primop_t, PrimOp & primop) : raw(tag(tAuxiliary, &primop)) {}
void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
{
printValue(state, str, *this, options);
}
bool Value::isTrivial() const
{
return internalType() != tApp
&& (internalType() != tThunk
|| (thunk().expr->try_cast<ExprSet>()
&& static_cast<ExprSet *>(thunk().expr)->dynamicAttrs.empty())
|| thunk().expr->try_cast<ExprLambda>() || thunk().expr->try_cast<ExprList>());
}
Value::Value(string_t, Str * s, const NixStringContext & context)
: Value(NewValueAs::string, s, copyContext(context))
{
}
Value::Value(string_t, std::string_view s, const NixStringContext & context)
: Value(NewValueAs::string, s, copyContext(context))
{
}
#ifndef __APPLE__
[[gnu::section(".debug_gdb_scripts"), gnu::used, gnu::aligned(1)]]
static const char printer_script[] =
"\4"
R"(lix-value-printer
class ValuePrinter(gdb.ValuePrinter):
def __init__(self, val):
self._val = val
self._t_thunk = gdb.lookup_type('nix::Value::Thunk').pointer()
self._t_app = gdb.lookup_type('nix::Value::App').pointer()
self._t_int = gdb.lookup_type('intptr_t')
self._t_string = gdb.lookup_type('nix::Value::String').pointer()
self._t_attrs = gdb.lookup_type('nix::Bindings').pointer()
self._t_list = gdb.lookup_type('nix::Value::List').pointer()
self._t_aux = gdb.lookup_type('nix::Value::Acb').pointer()
def _addr(self, t = None):
addr = self._val['raw'] & ~self._val['TAG_MASK']
return addr.cast(t).referenced_value() if t else addr
def _tag(self):
return self._val['raw'] & self._val['TAG_MASK']
def _v_int(self):
return self._val['raw'].cast(self._t_int) >> self._val['TAG_BITS']
def to_string(self):
if self._tag() == 7:
return self._addr(self._t_aux)
elif not self.children():
return f'undecoded {self._val['raw'].format_string(format='x')}'
return None
def children(self):
match self._tag():
case 0:
return [('thunk', self._addr(self._t_thunk))]
case 1:
return [('app', self._addr(self._t_app))]
case 2:
return [('int', self._v_int())]
case 3:
return [('bool', (self._val['raw'] >> self._val['TAG_BITS']) != 0)]
case 4:
return [('string', self._addr(self._t_string))]
case 5:
return [('attrs', self._addr(self._t_attrs))]
case 6:
return [('list', self._addr(self._t_list))]
case _:
return []
class ThunkPrinter(gdb.ValuePrinter):
def __init__(self, val):
self._val = val
self._t_value = gdb.lookup_type('nix::Value')
self._t_env = gdb.lookup_type('nix::Env').pointer()
def children(self):
if self._val['expr'] == 0:
return [('result', self._val['_result'].cast(self._t_value))]
else:
return [
('env', self._val['_env'].cast(self._t_env).referenced_value()),
('expr', self._val['expr'].referenced_value()),
]
class AppPrinter(gdb.ValuePrinter):
def __init__(self, val):
self._val = val
self._t_value = gdb.lookup_type('nix::Value')
def children(self):
if self._val['_n'] + 1 == 0:
return [('result', self._val['_left'].cast(self._t_value))]
else:
n = int(self._val['_n'])
arr = self._t_value.array(0, n - 1)
return [
('fn', self._val['_left'].cast(self._t_value)),
('n', n),
('args', self._val['_args'][0].cast(arr)),
]
class AcbPrinter(gdb.ValuePrinter):
def __init__(self, val):
self._val = val
self._t_external = gdb.lookup_type('nix::Value::External').pointer()
self._t_float = gdb.lookup_type('nix::Value::Float').pointer()
self._t_primop = gdb.lookup_type('nix::Value::PrimOp')
self._t_primopdetails = gdb.lookup_type('nix::PrimOpDetails')
self._t_lambda = gdb.lookup_type('nix::Value::Lambda').pointer()
self._t_int = gdb.lookup_type('nix::Value::Int').pointer()
def _addr(self, t = None):
addr = self._val['raw'] & ~self._val['TAG_MASK']
return addr.cast(t).referenced_value() if t else addr
def _tag(self):
return self._val['raw'] & self._val['TAG_MASK']
def to_string(self):
match self._tag():
case 2:
return 'null'
case 3:
op = self._val.cast(self._t_primop).cast(self._t_primopdetails)
return f'primop {op['name']}'
case _:
return None
def children(self):
match self._tag():
case 0:
return [('external', self._addr(self._t_external))]
case 1:
return [('float', self._addr(self._t_float)['value'])]
case 4:
return [('lambda', self._addr(self._t_lambda))]
case 5:
return [('int', self._addr(self._t_int)['value'])]
case _:
return []
def value_lookup_function(val):
lookup_tag = val.type.tag
if lookup_tag is None:
return None
if lookup_tag == 'nix::Value':
return ValuePrinter(val)
elif lookup_tag == 'nix::Value::Thunk':
return ThunkPrinter(val)
elif lookup_tag == 'nix::Value::App':
return AppPrinter(val)
elif lookup_tag == 'nix::Value::Acb':
return AcbPrinter(val)
return None
def register_printers(objfile):
objfile.pretty_printers.append(value_lookup_function)
register_printers(gdb.current_objfile())
)";
#endif
}