2012-07-18 14:59:03 -04:00
|
|
|
#pragma once
|
2012-01-07 17:26:33 +00:00
|
|
|
|
2020-07-18 16:40:15 +01:00
|
|
|
#include <tuple>
|
|
|
|
#include <vector>
|
|
|
|
|
2016-08-30 13:12:12 +02:00
|
|
|
#include <gc/gc_allocator.h>
|
2020-07-18 16:40:15 +01:00
|
|
|
#include <gc/gc_cpp.h>
|
2020-05-27 21:56:34 +01:00
|
|
|
|
|
|
|
#include "libexpr/symbol-table.hh"
|
|
|
|
#include "libutil/types.hh"
|
2016-08-30 13:12:12 +02:00
|
|
|
|
2012-01-07 17:26:33 +00:00
|
|
|
namespace nix {
|
|
|
|
|
2020-08-12 01:35:08 +01:00
|
|
|
using ValueType = enum {
|
2012-01-07 17:26:33 +00:00
|
|
|
tInt = 1,
|
|
|
|
tBool,
|
|
|
|
tString,
|
|
|
|
tPath,
|
|
|
|
tNull,
|
|
|
|
tAttrs,
|
2020-07-18 16:40:15 +01:00
|
|
|
tList,
|
2012-01-07 17:26:33 +00:00
|
|
|
tThunk,
|
|
|
|
tApp,
|
|
|
|
tLambda,
|
|
|
|
tBlackhole,
|
|
|
|
tPrimOp,
|
|
|
|
tPrimOpApp,
|
2020-07-31 15:27:39 -07:00
|
|
|
_reserved1, // formerly tExternal
|
2016-01-05 00:40:40 +01:00
|
|
|
tFloat
|
2020-08-12 01:35:08 +01:00
|
|
|
};
|
2012-01-07 17:26:33 +00:00
|
|
|
|
2014-01-21 18:29:55 +01:00
|
|
|
class Bindings;
|
2012-01-07 17:26:33 +00:00
|
|
|
struct Env;
|
|
|
|
struct Expr;
|
|
|
|
struct ExprLambda;
|
|
|
|
struct PrimOp;
|
|
|
|
struct PrimOp;
|
2014-01-21 18:29:55 +01:00
|
|
|
class Symbol;
|
2012-01-07 17:26:33 +00:00
|
|
|
|
libexpr: Use int64_t for NixInt
Using a 64bit integer on 32bit systems will come with a bit of a
performance overhead, but given that Nix doesn't use a lot of integers
compared to other types, I think the overhead is negligible also
considering that 32bit systems are in decline.
The biggest advantage however is that when we use a consistent integer
size across all platforms it's less likely that we miss things that we
break due to that. One example would be:
https://github.com/NixOS/nixpkgs/pull/44233
On Hydra it will evaluate, because the evaluator runs on a 64bit
machine, but when evaluating the same on a 32bit machine it will fail,
so using 64bit integers should make that consistent.
While the change of the type in value.hh is rather easy to do, we have a
few more options available for doing the conversion in the lexer:
* Via an #ifdef on the architecture and using strtol() or strtoll()
accordingly depending on which architecture we are. For the #ifdef
we would need another AX_COMPILE_CHECK_SIZEOF in configure.ac.
* Using istringstream, which would involve copying the value.
* As we're already using boost, lexical_cast might be a good idea.
Spoiler: I went for the latter, first of all because lexical_cast does
have an overload for const char* and second of all, because it doesn't
involve copying around the input string. Also, because istringstream
seems to come with a bigger overhead than boost::lexical_cast:
https://www.boost.org/doc/libs/release/doc/html/boost_lexical_cast/performance.html
The first method (still using strtol/strtoll) also wasn't something I
pursued further, because it is also locale-aware which I doubt is what
we want, given that the regex for int is [0-9]+.
Signed-off-by: aszlig <aszlig@nix.build>
Fixes: #2339
2018-08-29 00:23:51 +02:00
|
|
|
typedef int64_t NixInt;
|
2018-07-03 18:04:51 +02:00
|
|
|
typedef double NixFloat;
|
2013-08-19 12:35:03 +02:00
|
|
|
|
2020-05-19 18:19:35 +01:00
|
|
|
// Forward declaration of Value is required because the following
|
|
|
|
// types are mutually recursive.
|
|
|
|
//
|
|
|
|
// TODO(tazjin): Really, these types need some serious refactoring.
|
|
|
|
struct Value;
|
|
|
|
|
|
|
|
/* Strings in the evaluator carry a so-called `context' which
|
|
|
|
is a list of strings representing store paths. This is to
|
|
|
|
allow users to write things like
|
|
|
|
|
|
|
|
"--with-freetype2-library=" + freetype + "/lib"
|
|
|
|
|
|
|
|
where `freetype' is a derivation (or a source to be copied
|
|
|
|
to the store). If we just concatenated the strings without
|
|
|
|
keeping track of the referenced store paths, then if the
|
|
|
|
string is used as a derivation attribute, the derivation
|
|
|
|
will not have the correct dependencies in its inputDrvs and
|
|
|
|
inputSrcs.
|
|
|
|
|
|
|
|
The semantics of the context is as follows: when a string
|
|
|
|
with context C is used as a derivation attribute, then the
|
|
|
|
derivations in C will be added to the inputDrvs of the
|
|
|
|
derivation, and the other store paths in C will be added to
|
|
|
|
the inputSrcs of the derivations.
|
|
|
|
|
|
|
|
For canonicity, the store paths should be in sorted order. */
|
2020-08-08 21:49:38 -04:00
|
|
|
struct NixString : public gc {
|
2020-05-19 18:19:35 +01:00
|
|
|
const char* s;
|
|
|
|
const char** context; // must be in sorted order
|
|
|
|
};
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-08-08 21:49:38 -04:00
|
|
|
struct NixThunk : public gc {
|
2020-05-19 18:19:35 +01:00
|
|
|
Env* env;
|
|
|
|
Expr* expr;
|
|
|
|
};
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-08-08 21:49:38 -04:00
|
|
|
struct NixApp : public gc {
|
2020-05-19 18:19:35 +01:00
|
|
|
Value *left, *right;
|
|
|
|
};
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-08-08 21:49:38 -04:00
|
|
|
struct NixLambda : public gc {
|
2020-05-19 18:19:35 +01:00
|
|
|
Env* env;
|
|
|
|
ExprLambda* fun;
|
|
|
|
};
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-08-08 21:49:38 -04:00
|
|
|
struct NixPrimOpApp : public gc {
|
2020-05-19 18:19:35 +01:00
|
|
|
Value *left, *right;
|
|
|
|
};
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-07-18 16:40:15 +01:00
|
|
|
using NixList = std::vector<Value*, traceable_allocator<Value*>>;
|
|
|
|
|
|
|
|
struct Value : public gc {
|
2020-05-19 18:19:35 +01:00
|
|
|
ValueType type;
|
|
|
|
union { // TODO(tazjin): std::variant
|
|
|
|
NixInt integer;
|
|
|
|
bool boolean;
|
|
|
|
NixString string;
|
2012-01-07 17:26:33 +00:00
|
|
|
const char* path;
|
|
|
|
Bindings* attrs;
|
2020-07-18 16:40:15 +01:00
|
|
|
NixList* list;
|
2020-05-19 18:19:35 +01:00
|
|
|
NixThunk thunk;
|
|
|
|
NixApp app; // TODO(tazjin): "app"?
|
|
|
|
NixLambda lambda;
|
2012-01-07 17:26:33 +00:00
|
|
|
PrimOp* primOp;
|
2020-05-19 18:19:35 +01:00
|
|
|
NixPrimOpApp primOpApp;
|
2016-01-05 00:40:40 +01:00
|
|
|
NixFloat fpoint;
|
2020-05-17 16:31:57 +01:00
|
|
|
};
|
|
|
|
|
2020-07-18 16:40:15 +01:00
|
|
|
bool isList() const { return type == tList; }
|
2020-05-17 16:31:57 +01:00
|
|
|
|
2020-07-18 16:40:15 +01:00
|
|
|
size_t listSize() const { return list->size(); }
|
2020-05-17 16:31:57 +01:00
|
|
|
};
|
2013-11-18 22:22:35 +01:00
|
|
|
|
2012-01-07 17:26:33 +00:00
|
|
|
/* After overwriting an app node, be sure to clear pointers in the
|
|
|
|
Value to ensure that the target isn't kept alive unnecessarily. */
|
|
|
|
static inline void clearValue(Value& v) { v.app.left = v.app.right = 0; }
|
2013-11-18 22:22:35 +01:00
|
|
|
|
2012-01-07 17:26:33 +00:00
|
|
|
static inline void mkInt(Value& v, NixInt n) {
|
|
|
|
clearValue(v);
|
|
|
|
v.type = tInt;
|
|
|
|
v.integer = n;
|
|
|
|
}
|
|
|
|
|
2017-04-25 11:20:37 +02:00
|
|
|
static inline void mkFloat(Value& v, NixFloat n) {
|
|
|
|
clearValue(v);
|
|
|
|
v.type = tFloat;
|
|
|
|
v.fpoint = n;
|
|
|
|
}
|
|
|
|
|
2012-01-07 17:26:33 +00:00
|
|
|
static inline void mkBool(Value& v, bool b) {
|
|
|
|
clearValue(v);
|
|
|
|
v.type = tBool;
|
|
|
|
v.boolean = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void mkNull(Value& v) {
|
2012-12-12 21:13:26 -08:00
|
|
|
clearValue(v);
|
2012-01-07 17:26:33 +00:00
|
|
|
v.type = tNull;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void mkApp(Value& v, Value& left, Value& right) {
|
2016-01-05 00:40:40 +01:00
|
|
|
v.type = tApp;
|
2012-01-07 17:26:33 +00:00
|
|
|
v.app.left = &left;
|
|
|
|
v.app.right = &right;
|
2020-05-17 16:31:57 +01:00
|
|
|
}
|
2012-01-07 17:26:33 +00:00
|
|
|
|
|
|
|
static inline void mkPrimOpApp(Value& v, Value& left, Value& right) {
|
|
|
|
v.type = tPrimOpApp;
|
|
|
|
v.app.left = &left;
|
|
|
|
v.app.right = &right;
|
2020-05-17 16:31:57 +01:00
|
|
|
}
|
2012-01-07 17:26:33 +00:00
|
|
|
|
|
|
|
static inline void mkStringNoCopy(Value& v, const char* s) {
|
2014-12-02 10:02:03 -05:00
|
|
|
v.type = tString;
|
2012-01-07 17:26:33 +00:00
|
|
|
v.string.s = s;
|
|
|
|
v.string.context = 0;
|
2020-05-17 16:31:57 +01:00
|
|
|
}
|
2012-01-07 17:26:33 +00:00
|
|
|
|
|
|
|
static inline void mkString(Value& v, const Symbol& s) {
|
2020-05-21 05:43:22 +01:00
|
|
|
mkStringNoCopy(v, ((const std::string&)s).c_str());
|
2012-01-07 17:26:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void mkString(Value& v, const char* s);
|
|
|
|
|
|
|
|
static inline void mkPathNoCopy(Value& v, const char* s) {
|
|
|
|
clearValue(v);
|
|
|
|
v.type = tPath;
|
|
|
|
v.path = s;
|
2020-05-17 16:31:57 +01:00
|
|
|
}
|
2012-01-07 17:26:33 +00:00
|
|
|
|
2014-12-02 10:02:03 -05:00
|
|
|
void mkPath(Value& v, const char* s);
|
2012-01-07 17:26:33 +00:00
|
|
|
|
2014-09-22 14:46:42 +02:00
|
|
|
/* Compute the size in bytes of the given value, including all values
|
|
|
|
and environments reachable from it. Static expressions (Exprs) are
|
|
|
|
not included. */
|
2020-07-31 15:40:49 -07:00
|
|
|
size_t valueSize(const Value& v);
|
2014-09-22 14:46:42 +02:00
|
|
|
|
2017-03-24 23:05:49 +01:00
|
|
|
typedef std::map<Symbol, Value*, std::less<Symbol>,
|
2020-04-16 17:24:28 +02:00
|
|
|
traceable_allocator<std::pair<const Symbol, Value*>>>
|
2017-03-24 23:05:49 +01:00
|
|
|
ValueMap;
|
2016-08-29 17:28:20 +02:00
|
|
|
|
2020-04-16 16:28:07 +02:00
|
|
|
std::shared_ptr<Value*> allocRootValue(Value* v);
|
|
|
|
|
2012-01-07 17:26:33 +00:00
|
|
|
} // namespace nix
|