src: introduce Constant class · nodejs/llnode@2afd59e · GitHub
Skip to content

Commit 2afd59e

Browse files
committed
src: introduce Constant class
Same idea as CheckedType: instead of relying on the default value for constants to determine if they were loaded correctly or not (default value usually was -1), have a class which will have information about the loaded constant. This can help us: - Check if a constant was properly loaded before using it (otherwise we'd get into undefined behavior, and there are a lot of places where this happens. - Check if a constant value was loaded or if we're looking at the default value. - Explicitly define a constant as optional (constants which are not relevant except for specific V8 versions). This will help us improve reliability and debugability of llnode. PR-URL: #303 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent ec01604 commit 2afd59e

7 files changed

Lines changed: 145 additions & 60 deletions

File tree

src/constants.cc

Lines changed: 64 additions & 42 deletions

src/constants.h

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,56 @@ using lldb::SBTarget;
1010

1111
namespace llnode {
1212

13+
enum ConstantStatus { kInvalid, kValid, kLoaded };
14+
15+
// Class representing a constant which is used to interpret memory data. Most
16+
// constants represent offset of fields on an object, bit-masks or "tags" which
17+
// are used to identify types, but there are some constants with different
18+
// meanings as well.
19+
//
20+
// By default, constants are loaded from the binary debug symbols, and usually
21+
// those debug symbols are generated by V8's gen-postmortem-metadata.py or by
22+
// Node.js' node-postmortem-metadata.cc. Some constants can also come from C++
23+
// generated debug symbols.
24+
//
25+
// When a constant is successfully loaded, Check() and Loaded() will return
26+
// true, which means we can safely use this constant and make assumptions based
27+
// on its existence. In some cases, it's safe to assume defaults for a given
28+
// constant. If that's the case, the constant will return false on Loaded() but
29+
// true on Check(). A constant returning false for both Check() and Loaded() is
30+
// not safe to use.
31+
//
32+
// Use the dereference operator (*constant) to access a constant value.
33+
template <typename T>
34+
class Constant {
35+
public:
36+
Constant() : value_(-1), status_(kInvalid) {}
37+
inline bool Check() {
38+
return (status_ == ConstantStatus::kValid ||
39+
status_ == ConstantStatus::kLoaded);
40+
}
41+
42+
inline bool Loaded() { return status_ == kLoaded; }
43+
44+
T operator*() {
45+
// TODO(mmarchini): Check()
46+
return value_;
47+
}
48+
49+
inline std::string name() { return name_; }
50+
51+
protected:
52+
friend class Constants;
53+
explicit Constant(T value) : value_(value), status_(kValid), name_("") {}
54+
Constant(T value, std::string name)
55+
: value_(value), status_(kLoaded), name_(name) {}
56+
57+
private:
58+
T value_;
59+
ConstantStatus status_;
60+
std::string name_;
61+
};
62+
1363
#define CONSTANTS_DEFAULT_METHODS(NAME) \
1464
inline NAME* operator()() { \
1565
if (loaded_) return this; \
@@ -28,15 +78,16 @@ class Constants {
2878

2979
inline virtual std::string constant_prefix() { return ""; };
3080

31-
static int64_t LookupConstant(SBTarget target, const char* name, int64_t def,
32-
Error& err);
81+
static Constant<int64_t> LookupConstant(SBTarget target, const char* name);
3382

3483
protected:
3584
int64_t LoadRawConstant(const char* name, int64_t def = -1);
36-
int64_t LoadConstant(const char* name, Error& err, int64_t def = -1);
3785
int64_t LoadConstant(const char* name, int64_t def = -1);
3886
int64_t LoadConstant(const char* name, const char* fallback,
3987
int64_t def = -1);
88+
Constant<int64_t> LoadConstant(std::initializer_list<const char*> names);
89+
Constant<int64_t> LoadOptionalConstant(
90+
std::initializer_list<const char*> names, int def);
4091

4192
lldb::SBTarget target_;
4293
bool loaded_;

src/error.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Error {
3030
inline const char* GetMessage() { return msg_.c_str(); }
3131

3232
static void SetDebugMode(bool mode) { is_debug_mode = mode; }
33+
static bool IsDebugMode() { return is_debug_mode; }
3334

3435
private:
3536
bool failed_;

src/llv8-constants.cc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ void HeapObject::Load() {
8181

8282
void Map::Load() {
8383
Error err;
84-
kInstanceAttrsOffset =
85-
LoadConstant("class_Map__instance_attributes__int", err);
86-
if (err.Fail()) {
87-
kInstanceAttrsOffset = LoadConstant("class_Map__instance_type__uint16_t");
84+
kInstanceAttrsOffset = LoadConstant({"class_Map__instance_attributes__int",
85+
"class_Map__instance_type__uint16_t"});
86+
if (kInstanceAttrsOffset.name() ==
87+
"v8dbg_class_Map__instance_type__uint16_t") {
8888
kMapTypeMask = 0xffff;
8989
} else {
9090
kMapTypeMask = 0xff;
@@ -93,8 +93,9 @@ void Map::Load() {
9393
kMaybeConstructorOffset =
9494
LoadConstant("class_Map__constructor_or_backpointer__Object",
9595
"class_Map__constructor__Object");
96-
kInstanceDescriptorsOffset =
97-
LoadConstant("class_Map__instance_descriptors__DescriptorArray");
96+
kInstanceDescriptorsOffset = LoadConstant({
97+
"class_Map__instance_descriptors__DescriptorArray",
98+
});
9899
kBitField3Offset =
99100
LoadConstant("class_Map__bit_field3__int", "class_Map__bit_field3__SMI");
100101
kInObjectPropertiesOffset = LoadConstant(

src/llv8-constants.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ class Map : public Module {
6969
CONSTANTS_DEFAULT_METHODS(Map);
7070

7171
int64_t kMapTypeMask;
72-
int64_t kInstanceAttrsOffset;
72+
Constant<int64_t> kInstanceAttrsOffset;
7373
int64_t kMaybeConstructorOffset;
74-
int64_t kInstanceDescriptorsOffset;
74+
Constant<int64_t> kInstanceDescriptorsOffset;
7575
int64_t kBitField3Offset;
7676
int64_t kInObjectPropertiesOffset;
7777
int64_t kInObjectPropertiesStartOffset;

src/llv8-inl.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,11 @@ inline bool HeapObject::IsJSErrorType(Error& err) {
165165
}
166166

167167

168+
// TODO(mmarchini): return CheckedType
168169
inline int64_t Map::GetType(Error& err) {
169-
int64_t type =
170-
v8()->LoadUnsigned(LeaField(v8()->map()->kInstanceAttrsOffset), 2, err);
170+
RETURN_IF_INVALID(v8()->map()->kInstanceAttrsOffset, -1);
171+
int64_t type = v8()->LoadUnsigned(
172+
LeaField(*(v8()->map()->kInstanceAttrsOffset)), 2, err);
171173
if (err.Fail()) return -1;
172174

173175
return type & v8()->map()->kMapTypeMask;
@@ -230,12 +232,19 @@ inline int64_t Map::NumberOfOwnDescriptors(Error& err) {
230232
return LoadFieldValue<TYPE>(v8()->OFF, err); \
231233
}
232234

235+
#define SAFE_ACCESSOR(CLASS, METHOD, OFF, TYPE) \
236+
inline TYPE CLASS::METHOD(Error& err) { \
237+
if (!Check()) return TYPE(); \
238+
if (!v8()->OFF.Check()) return TYPE(); \
239+
return LoadFieldValue<TYPE>(*(v8()->OFF), err); \
240+
}
241+
233242

234243
ACCESSOR(HeapObject, GetMap, heap_obj()->kMapOffset, HeapObject)
235244

236245
ACCESSOR(Map, MaybeConstructor, map()->kMaybeConstructorOffset, HeapObject)
237-
ACCESSOR(Map, InstanceDescriptors, map()->kInstanceDescriptorsOffset,
238-
HeapObject)
246+
SAFE_ACCESSOR(Map, InstanceDescriptors, map()->kInstanceDescriptorsOffset,
247+
HeapObject)
239248

240249
ACCESSOR(Symbol, Name, symbol()->kNameOffset, HeapObject)
241250

src/llv8.h

Lines changed: 4 additions & 3 deletions

0 commit comments

Comments
 (0)