forked from facebook/hermes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TestHelpers.h
432 lines (352 loc) · 14 KB
/
TestHelpers.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef HERMES_UNITTESTS_VMRUNTIME_TESTHELPERS_H
#define HERMES_UNITTESTS_VMRUNTIME_TESTHELPERS_H
#include "hermes/BCGen/HBC/BytecodeGenerator.h"
#include "hermes/BCGen/HBC/BytecodeProviderFromSrc.h"
#include "hermes/Public/GCConfig.h"
#include "hermes/Public/RuntimeConfig.h"
#include "hermes/VM/Callable.h"
#include "hermes/VM/CodeBlock.h"
#include "hermes/VM/Domain.h"
#include "hermes/VM/JSArray.h"
#include "hermes/VM/Operations.h"
#include "hermes/VM/PointerBase.h"
#include "hermes/VM/Runtime.h"
#include "hermes/VM/RuntimeModule-inline.h"
#include "hermes/VM/StorageProvider.h"
#include "hermes/VM/StringPrimitive.h"
#include "hermes/VM/StringRefUtils.h"
#include "gtest/gtest.h"
namespace hermes {
namespace vm {
// Initial and max heap size constants
static constexpr uint32_t kInitHeapSmall = 1 << 8;
static constexpr uint32_t kMaxHeapSmall = 1 << 11;
static constexpr uint32_t kInitHeapSize = 1 << 16;
static constexpr uint32_t kMaxHeapSize = 1 << 19;
static constexpr uint32_t kInitHeapLarge = 1 << 20;
static constexpr uint32_t kMaxHeapLarge = 1 << 24;
static const GCConfig::Builder kTestGCConfigBaseBuilder =
GCConfig::Builder()
.withSanitizeConfig(
vm::GCSanitizeConfig::Builder().withSanitizeRate(0.0).build())
.withShouldRandomizeAllocSpace(false);
static const GCConfig kTestGCConfigSmall =
GCConfig::Builder(kTestGCConfigBaseBuilder)
.withInitHeapSize(kInitHeapSmall)
.withMaxHeapSize(kMaxHeapSmall)
.build();
static const GCConfig::Builder kTestGCConfigBuilder =
GCConfig::Builder(kTestGCConfigBaseBuilder)
.withInitHeapSize(kInitHeapSize)
.withMaxHeapSize(kMaxHeapSize);
static const GCConfig kTestGCConfig =
GCConfig::Builder(kTestGCConfigBuilder).build();
static const GCConfig kTestGCConfigLarge =
GCConfig::Builder(kTestGCConfigBuilder)
.withInitHeapSize(kInitHeapLarge)
.withMaxHeapSize(kMaxHeapLarge)
.build();
static const RuntimeConfig kTestRTConfigSmallHeap =
RuntimeConfig::Builder().withGCConfig(kTestGCConfigSmall).build();
static const RuntimeConfig::Builder kTestRTConfigBuilder =
RuntimeConfig::Builder().withGCConfig(kTestGCConfig);
static const RuntimeConfig kTestRTConfig =
RuntimeConfig::Builder(kTestRTConfigBuilder).build();
static const RuntimeConfig kTestRTConfigLargeHeap =
RuntimeConfig::Builder().withGCConfig(kTestGCConfigLarge).build();
template <typename T>
::testing::AssertionResult isException(
Runtime *runtime,
const CallResult<T> &res) {
return isException(runtime, res.getStatus());
}
::testing::AssertionResult isException(
Runtime *runtime,
ExecutionStatus status);
/// A RuntimeTestFixture should be used by any test that requires a Runtime.
/// For different heap sizes, use the different subclasses.
class RuntimeTestFixtureBase : public ::testing::Test {
std::shared_ptr<Runtime> rt;
protected:
// Convenience accessor that points to rt.
Runtime *runtime;
RuntimeConfig rtConfig;
GCScope gcScope;
Handle<Domain> domain;
RuntimeTestFixtureBase(const RuntimeConfig &runtimeConfig)
: rt(Runtime::create(runtimeConfig)),
runtime(rt.get()),
rtConfig(runtimeConfig),
gcScope(runtime),
domain(runtime->makeHandle(Domain::create(runtime))) {}
/// Can't copy due to internal pointer.
RuntimeTestFixtureBase(const RuntimeTestFixtureBase &) = delete;
template <typename T>
::testing::AssertionResult isException(const CallResult<T> &res) {
return isException(res.getStatus());
}
::testing::AssertionResult isException(ExecutionStatus status) {
return ::hermes::vm::isException(runtime, status);
}
std::shared_ptr<Runtime> newRuntime() {
return Runtime::create(rtConfig);
}
};
class RuntimeTestFixture : public RuntimeTestFixtureBase {
public:
RuntimeTestFixture() : RuntimeTestFixtureBase(kTestRTConfig) {}
RuntimeTestFixture(experiments::VMExperimentFlags flags)
: RuntimeTestFixtureBase(RuntimeConfig::Builder()
.withGCConfig(kTestGCConfig)
.withVMExperimentFlags(flags)
.build()) {}
};
class SmallHeapRuntimeTestFixture : public RuntimeTestFixtureBase {
public:
SmallHeapRuntimeTestFixture()
: RuntimeTestFixtureBase(kTestRTConfigSmallHeap) {}
};
class LargeHeapRuntimeTestFixture : public RuntimeTestFixtureBase {
public:
LargeHeapRuntimeTestFixture()
: RuntimeTestFixtureBase(kTestRTConfigLargeHeap) {}
};
/// Configuration for the GC which fixes a size -- \p sz -- for the heap, and
/// does not permit any growth. Intended only for testing purposes where we
/// don't expect or want the heap to grow.
///
/// \p builder is an optional parameter representing an initial builder to set
/// size parameters upon, providing an opportunity to set other parameters.
inline const GCConfig TestGCConfigFixedSize(
gcheapsize_t sz,
GCConfig::Builder builder = kTestGCConfigBuilder) {
return builder.withInitHeapSize(sz).withMaxHeapSize(sz).build();
}
/// Assert that execution of `x' didn't throw.
#define ASSERT_RETURNED(x) ASSERT_EQ(ExecutionStatus::RETURNED, x)
/// Expect that 'x' is a string primitive with value 'str'
#define EXPECT_STRINGPRIM(str, x) \
do { \
GCScopeMarkerRAII marker{runtime}; \
Handle<> xHandle{runtime, x}; \
Handle<StringPrimitive> strHandle = \
StringPrimitive::createNoThrow(runtime, str); \
EXPECT_TRUE( \
isSameValue(strHandle.getHermesValue(), xHandle.getHermesValue())); \
} while (0)
/// Assert that execution of 'x' didn't throw and returned the expected bool.
#define EXPECT_CALLRESULT_BOOL_RAW(B, x) \
do { \
auto res = x; \
ASSERT_RETURNED(res.getStatus()); \
EXPECT_##B(*res); \
} while (0)
/// Assert that execution of 'x' didn't throw and returned the expected bool.
#define EXPECT_CALLRESULT_BOOL(B, x) \
do { \
auto res = x; \
ASSERT_RETURNED(res.getStatus()); \
EXPECT_##B((*res)->getBool()); \
} while (0)
/// Assert that execution of 'x' didn't throw and returned the expected
/// CallResult.
// Will replace "EXPECT_RETURN_STRING" after the entire refactor.
#define EXPECT_CALLRESULT_STRING(str, x) \
do { \
auto res = x; \
ASSERT_RETURNED(res.getStatus()); \
EXPECT_STRINGPRIM(str, res->get()); \
} while (0)
/// Assert that execution of 'x' didn't throw and returned undefined.
#define EXPECT_CALLRESULT_UNDEFINED(x) \
do { \
auto res = x; \
ASSERT_RETURNED(res.getStatus()); \
EXPECT_TRUE((*res)->isUndefined()); \
} while (0)
/// Assert that execution of 'x' didn't throw and returned the expected double.
#define EXPECT_CALLRESULT_DOUBLE(d, x) \
do { \
auto res = x; \
ASSERT_RETURNED(res.getStatus()); \
EXPECT_EQ(d, (*res)->getDouble()); \
} while (0)
/// Assert that execution of 'x' didn't throw and returned the expected double.
#define EXPECT_CALLRESULT_VALUE(v, x) \
do { \
auto res = x; \
ASSERT_RETURNED(res.getStatus()); \
EXPECT_EQ(v, res->get()); \
} while (0)
/// Some tests expect out of memory. This may either be fatal, or throw
/// exception; parameterize tests over this choice.
#ifdef HERMESVM_EXCEPTION_ON_OOM
#define EXPECT_OOM(exp) \
{ \
bool exThrown = false; \
try { \
exp; \
} catch (const JSOutOfMemoryError &x) { \
exThrown = true; \
} \
EXPECT_TRUE(exThrown); \
}
#else
#define EXPECT_OOM(exp) EXPECT_DEATH_IF_SUPPORTED({ exp; }, "OOM")
#endif
/// Get a named value from an object.
#define GET_VALUE(objHandle, predefinedId) \
do { \
propRes = JSObject::getNamed_RJS( \
objHandle, \
runtime, \
Predefined::getSymbolID(Predefined::predefinedId)); \
ASSERT_RETURNED(propRes.getStatus()); \
} while (0)
/// Get the global object.
#define GET_GLOBAL(predefinedId) GET_VALUE(runtime->getGlobal(), predefinedId)
inline HermesValue operator"" _hd(long double d) {
return HermesValue::encodeDoubleValue(d);
}
/// A minimal Runtime for GC tests.
class DummyRuntime final : public HandleRootOwner,
public PointerBase,
private GCBase::GCCallbacks {
private:
GCStorage gcStorage_;
public:
std::vector<WeakRoot<GCCell> *> weakRoots{};
/// Create a DummyRuntime with the default parameters.
static std::shared_ptr<DummyRuntime> create(const GCConfig &gcConfig);
/// Use a custom storage provider and/or a custom crash manager.
/// \param provider A pointer to a StorageProvider. It *must* use
/// StorageProvider::defaultProvider eventually or the test will fail.
/// \param crashMgr
static std::shared_ptr<DummyRuntime> create(
const GCConfig &gcConfig,
std::shared_ptr<StorageProvider> provider,
std::shared_ptr<CrashManager> crashMgr =
std::make_shared<NopCrashManager>());
/// Provide the correct storage provider based on build modes.
/// All decorator StorageProviders must wrap the one returned from this
/// function.
static std::unique_ptr<StorageProvider> defaultProvider();
~DummyRuntime();
template <
typename T,
HasFinalizer hasFinalizer = HasFinalizer::No,
LongLived longLived = LongLived::No,
class... Args>
T *makeAFixed(Args &&...args) {
return getHeap().makeAFixed<T, hasFinalizer, longLived>(
std::forward<Args>(args)...);
}
template <
typename T,
HasFinalizer hasFinalizer = HasFinalizer::No,
LongLived longLived = LongLived::No,
class... Args>
T *makeAVariable(uint32_t size, Args &&...args) {
return getHeap().makeAVariable<T, hasFinalizer, longLived>(
size, std::forward<Args>(args)...);
}
GC &getHeap() {
return *gcStorage_.get();
}
void collect();
void markRoots(RootAndSlotAcceptorWithNames &acceptor, bool) override;
void markWeakRoots(WeakRootAcceptor &weakAcceptor, bool) override;
void markRootsForCompleteMarking(
RootAndSlotAcceptorWithNames &acceptor) override;
unsigned int getSymbolsEnd() const override {
return 0;
}
void unmarkSymbols() override {}
void freeSymbols(const llvh::BitVector &) override {}
#ifdef HERMES_SLOW_DEBUG
bool isSymbolLive(SymbolID) override {
return true;
}
const void *getStringForSymbol(SymbolID) override {
return nullptr;
}
#endif
void printRuntimeGCStats(JSONEmitter &) const override {}
void visitIdentifiers(
const std::function<void(SymbolID, const StringPrimitive *)> &) override {
}
std::string convertSymbolToUTF8(SymbolID) override;
std::string getCallStackNoAlloc() override {
return "<dummy runtime has no call stack>";
}
void onGCEvent(GCEventKind, const std::string &) override {}
/// It's a unit test, it doesn't care about reporting how much memory it uses.
size_t mallocSize() const override {
return 0;
}
const inst::Inst *getCurrentIPSlow() const override {
return nullptr;
}
StackTracesTreeNode *getCurrentStackTracesTreeNode(
const inst::Inst *ip) override {
return nullptr;
}
StackTracesTree *getStackTracesTree() override {
return nullptr;
}
private:
DummyRuntime(
const GCConfig &gcConfig,
std::shared_ptr<StorageProvider> storageProvider,
std::shared_ptr<CrashManager> crashMgr);
};
/// A DummyRuntimeTestFixtureBase should be used by any test that requires a
/// DummyRuntime. It takes a GCConfig, which can be
/// used to specify heap size using the constants i.e kInitHeapSize.
class DummyRuntimeTestFixtureBase : public ::testing::Test {
std::shared_ptr<DummyRuntime> rt;
protected:
// Convenience accessor that points to rt.
DummyRuntime *runtime;
GCScope gcScope;
DummyRuntimeTestFixtureBase(const GCConfig &gcConfig)
: rt(DummyRuntime::create(gcConfig)),
runtime(rt.get()),
gcScope(runtime) {}
/// Can't copy due to internal pointer.
DummyRuntimeTestFixtureBase(const DummyRuntimeTestFixtureBase &) = delete;
};
// Provide HermesValue & wrappers comparison operators for convenience.
/// Compare two HermesValue for bit equality.
inline bool operator==(HermesValue a, HermesValue b) {
return a.getRaw() == b.getRaw();
}
/// Helper function to create a CodeBlock that correspond to a single function
/// generated from \p BFG. The generated code block will be part of the \p
/// runtimeModule.
inline CodeBlock *createCodeBlock(
RuntimeModule *runtimeModule,
Runtime *,
hbc::BytecodeFunctionGenerator *BFG) {
std::unique_ptr<hbc::BytecodeModule> BM(new hbc::BytecodeModule(1));
BM->setFunction(
0,
BFG->generateBytecodeFunction(
Function::DefinitionKind::ES5Function,
ValueKind::FunctionKind,
true,
0,
0));
runtimeModule->initializeWithoutCJSModulesMayAllocate(
hbc::BCProviderFromSrc::createBCProviderFromSrc(std::move(BM)));
return runtimeModule->getCodeBlockMayAllocate(0);
}
} // namespace vm
} // namespace hermes
#endif // HERMES_UNITTESTS_VMRUNTIME_TESTHELPERS_H