Skip to content

Commit

Permalink
Use an union inside FNValue
Browse files Browse the repository at this point in the history
Summary:
The union makes it easier to debug Flow Native with a normal C debugger.

This doesn't introduce new UB because, regardless of whether union type
punning is supported or not, reading an union member different
from the one which was written to and using the value is already UB.

The only valid case is getBool() and that uses memcpy().

Reviewed By: neildhar

Differential Revision: D37266753

fbshipit-source-id: 27cef6610e59fa1b293503fc082ed6c870a7e4fa
  • Loading branch information
tmikov authored and facebook-github-bot committed Jul 29, 2022
1 parent 65df74e commit 0dc6ff3
Showing 1 changed file with 40 additions and 30 deletions.
70 changes: 40 additions & 30 deletions unsupported/juno/crates/flow_native/runtime/FNRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,17 @@ struct FNPredefined {
// purposes. It will mostly be deleted once we have real type checking.
class FNValue {
FNType tag;
uint64_t value;
union {
double num;
uint64_t u64;
FNObject *obj;
const FNString *str;
} value;

static_assert(
sizeof(value) >= sizeof(uintptr_t),
"Value must be able to fit a pointer.");

void *getPointer() const {
return reinterpret_cast<FNString *>(static_cast<uintptr_t>(value));
}

public:
bool isUndefined() const {
return tag == FNType::Undefined;
Expand Down Expand Up @@ -127,76 +128,74 @@ class FNValue {

double getNumber() const {
assert(isNumber());
double num;
memcpy(&num, &value, sizeof(double));
return num;
return value.num;
}
bool getBool() const {
assert(isBool() || isNumber() || isUndefined() || isNull());
return value;
uint64_t r;
memcpy(&r, &value, sizeof(r));
return r;
}
const FNString *getString() const {
assert(isString());
return reinterpret_cast<const FNString *>(value);
return value.str;
}
FNObject *getObject() const {
assert(isObject() || isClosure());
return reinterpret_cast<FNObject *>(value);
}
FNClosure *getClosure() const {
assert(isClosure());
return reinterpret_cast<FNClosure *>(value);
return value.obj;
}
inline FNClosure *getClosure() const;

static FNValue encodeUndefined() {
FNValue ret;
ret.tag = FNType::Undefined;
// Explicitly initialize value so we can reliably test for equality.
ret.value = 0;
ret.value.u64 = 0;
return ret;
}
static FNValue encodeNull() {
FNValue ret;
ret.tag = FNType::Null;
// Explicitly initialize value so we can reliably test for equality.
ret.value = 0;
ret.value.u64 = 0;
return ret;
}
static FNValue encodeNumber(double num) {
FNValue ret;
ret.tag = FNType::Number;
uint64_t bits;
memcpy(&bits, &num, sizeof(double));
ret.value = bits;
ret.value.num = num;
return ret;
}
static FNValue encodeBool(bool b) {
FNValue ret;
ret.tag = FNType::Bool;
ret.value = b;
ret.value.u64 = b;
return ret;
}
static FNValue encodeString(const FNString *str) {
FNValue ret;
ret.tag = FNType::String;
ret.value = reinterpret_cast<uint64_t>(str);
ret.value.str = str;
return ret;
}
static FNValue encodeObject(FNObject *obj) {
FNValue ret;
ret.tag = FNType::Object;
ret.value = reinterpret_cast<uint64_t>(obj);
return ret;
}
static FNValue encodeClosure(FNClosure *closure) {
FNValue ret;
ret.tag = FNType::Closure;
ret.value = reinterpret_cast<uint64_t>(closure);
ret.value.obj = obj;
return ret;
}
static inline FNValue encodeClosure(FNClosure *closure);

static bool isEqual(FNValue a, FNValue b) {
return a.tag == b.tag && a.value == b.value;
// return a.tag == b.tag && memcmp(a.v, b.v, sizeof(a.v)) == 0;
// NOTE: had to use this clumsier version because old GCC versions don't
// inline memcmp().
if (a.tag != b.tag)
return false;
uint64_t x, y;
memcpy(&x, &a.value, sizeof(x));
memcpy(&y, &b.value, sizeof(y));
return x == y;
}

static const FNString *typeOf(FNValue v);
Expand Down Expand Up @@ -285,6 +284,17 @@ struct FNArray : public FNObject {
fn_vector<FNValue> arr;
};

inline FNClosure *FNValue::getClosure() const {
assert(isClosure());
return static_cast<FNClosure *>(value.obj);
}
inline FNValue FNValue::encodeClosure(FNClosure *closure) {
FNValue ret;
ret.tag = FNType::Closure;
ret.value.obj = closure;
return ret;
}

FNObject *global();

int32_t truncateToInt32SlowPath(double d);
Expand Down

0 comments on commit 0dc6ff3

Please sign in to comment.