1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_UNITTESTS_TEST_UTILS_H_
6 #define V8_UNITTESTS_TEST_UTILS_H_
7
8 #include <memory>
9 #include <vector>
10
11 #include "include/v8-array-buffer.h"
12 #include "include/v8-context.h"
13 #include "include/v8-local-handle.h"
14 #include "include/v8-primitive.h"
15 #include "src/api/api-inl.h"
16 #include "src/base/macros.h"
17 #include "src/base/utils/random-number-generator.h"
18 #include "src/handles/handles.h"
19 #include "src/objects/objects-inl.h"
20 #include "src/objects/objects.h"
21 #include "src/zone/accounting-allocator.h"
22 #include "src/zone/zone.h"
23 #include "testing/gtest-support.h"
24
25 namespace v8 {
26
27 class ArrayBufferAllocator;
28
29 using CounterMap = std::map<std::string, int>;
30
31 enum CountersMode { kNoCounters, kEnableCounters };
32
33 // RAII-like Isolate instance wrapper.
34 class IsolateWrapper final {
35 public:
36 explicit IsolateWrapper(CountersMode counters_mode);
37 ~IsolateWrapper();
38 IsolateWrapper(const IsolateWrapper&) = delete;
39 IsolateWrapper& operator=(const IsolateWrapper&) = delete;
40
isolate()41 v8::Isolate* isolate() const { return isolate_; }
42
43 private:
44 std::unique_ptr<v8::ArrayBuffer::Allocator> array_buffer_allocator_;
45 std::unique_ptr<CounterMap> counter_map_;
46 v8::Isolate* isolate_;
47 };
48
49 //
50 // A set of mixins from which the test fixtures will be constructed.
51 //
52 template <typename TMixin, CountersMode kCountersMode = kNoCounters>
53 class WithIsolateMixin : public TMixin {
54 public:
WithIsolateMixin()55 WithIsolateMixin() : isolate_wrapper_(kCountersMode) {}
56
v8_isolate()57 v8::Isolate* v8_isolate() const { return isolate_wrapper_.isolate(); }
58
59 private:
60 v8::IsolateWrapper isolate_wrapper_;
61 };
62
63 template <typename TMixin>
64 class WithIsolateScopeMixin : public TMixin {
65 public:
WithIsolateScopeMixin()66 WithIsolateScopeMixin()
67 : isolate_scope_(this->v8_isolate()), handle_scope_(this->v8_isolate()) {}
68 WithIsolateScopeMixin(const WithIsolateScopeMixin&) = delete;
69 WithIsolateScopeMixin& operator=(const WithIsolateScopeMixin&) = delete;
70
isolate()71 v8::Isolate* isolate() const { return this->v8_isolate(); }
72
i_isolate()73 v8::internal::Isolate* i_isolate() const {
74 return reinterpret_cast<v8::internal::Isolate*>(this->v8_isolate());
75 }
76
77 private:
78 v8::Isolate::Scope isolate_scope_;
79 v8::HandleScope handle_scope_;
80 };
81
82 template <typename TMixin>
83 class WithContextMixin : public TMixin {
84 public:
WithContextMixin()85 WithContextMixin()
86 : context_(Context::New(this->v8_isolate())), context_scope_(context_) {}
87 WithContextMixin(const WithContextMixin&) = delete;
88 WithContextMixin& operator=(const WithContextMixin&) = delete;
89
context()90 const Local<Context>& context() const { return v8_context(); }
v8_context()91 const Local<Context>& v8_context() const { return context_; }
92
RunJS(const char * source)93 Local<Value> RunJS(const char* source) {
94 return RunJS(
95 v8::String::NewFromUtf8(this->v8_isolate(), source).ToLocalChecked());
96 }
97
RunJS(v8::String::ExternalOneByteStringResource * source)98 Local<Value> RunJS(v8::String::ExternalOneByteStringResource* source) {
99 return RunJS(v8::String::NewExternalOneByte(this->v8_isolate(), source)
100 .ToLocalChecked());
101 }
102
NewString(const char * string)103 v8::Local<v8::String> NewString(const char* string) {
104 return v8::String::NewFromUtf8(this->v8_isolate(), string).ToLocalChecked();
105 }
106
SetGlobalProperty(const char * name,v8::Local<v8::Value> value)107 void SetGlobalProperty(const char* name, v8::Local<v8::Value> value) {
108 CHECK(v8_context()
109 ->Global()
110 ->Set(v8_context(), NewString(name), value)
111 .FromJust());
112 }
113
114 private:
RunJS(Local<String> source)115 Local<Value> RunJS(Local<String> source) {
116 auto context = this->v8_isolate()->GetCurrentContext();
117 Local<Script> script =
118 v8::Script::Compile(context, source).ToLocalChecked();
119 return script->Run(context).ToLocalChecked();
120 }
121
122 v8::Local<v8::Context> context_;
123 v8::Context::Scope context_scope_;
124 };
125
126 // Use v8::internal::TestWithIsolate if you are testing internals,
127 // aka. directly work with Handles.
128 using TestWithIsolate = //
129 WithIsolateScopeMixin< //
130 WithIsolateMixin< //
131 ::testing::Test>>;
132
133 // Use v8::internal::TestWithNativeContext if you are testing internals,
134 // aka. directly work with Handles.
135 using TestWithContext = //
136 WithContextMixin< //
137 WithIsolateScopeMixin< //
138 WithIsolateMixin< //
139 ::testing::Test>>>;
140
141 namespace internal {
142
143 // Forward declarations.
144 class Factory;
145
146 template <typename TMixin>
147 class WithInternalIsolateMixin : public TMixin {
148 public:
149 WithInternalIsolateMixin() = default;
150 WithInternalIsolateMixin(const WithInternalIsolateMixin&) = delete;
151 WithInternalIsolateMixin& operator=(const WithInternalIsolateMixin&) = delete;
152
factory()153 Factory* factory() const { return isolate()->factory(); }
isolate()154 Isolate* isolate() const { return TMixin::i_isolate(); }
155
native_context()156 Handle<NativeContext> native_context() const {
157 return isolate()->native_context();
158 }
159
160 template <typename T = Object>
RunJS(const char * source)161 Handle<T> RunJS(const char* source) {
162 return Handle<T>::cast(RunJSInternal(source));
163 }
164
RunJSInternal(const char * source)165 Handle<Object> RunJSInternal(const char* source) {
166 return Utils::OpenHandle(*TMixin::RunJS(source));
167 }
168
169 template <typename T = Object>
RunJS(::v8::String::ExternalOneByteStringResource * source)170 Handle<T> RunJS(::v8::String::ExternalOneByteStringResource* source) {
171 return Handle<T>::cast(RunJSInternal(source));
172 }
173
RunJSInternal(::v8::String::ExternalOneByteStringResource * source)174 Handle<Object> RunJSInternal(
175 ::v8::String::ExternalOneByteStringResource* source) {
176 return Utils::OpenHandle(*TMixin::RunJS(source));
177 }
178
random_number_generator()179 base::RandomNumberGenerator* random_number_generator() const {
180 return isolate()->random_number_generator();
181 }
182 };
183
184 template <typename TMixin>
185 class WithZoneMixin : public TMixin {
186 public:
187 explicit WithZoneMixin(bool support_zone_compression = false)
188 : zone_(&allocator_, ZONE_NAME, support_zone_compression) {}
189 WithZoneMixin(const WithZoneMixin&) = delete;
190 WithZoneMixin& operator=(const WithZoneMixin&) = delete;
191
zone()192 Zone* zone() { return &zone_; }
193
194 private:
195 v8::internal::AccountingAllocator allocator_;
196 Zone zone_;
197 };
198
199 using TestWithIsolate = //
200 WithInternalIsolateMixin< //
201 WithIsolateScopeMixin< //
202 WithIsolateMixin< //
203 ::testing::Test>>>;
204
205 using TestWithZone = WithZoneMixin<::testing::Test>;
206
207 using TestWithIsolateAndZone = //
208 WithZoneMixin< //
209 WithInternalIsolateMixin< //
210 WithIsolateScopeMixin< //
211 WithIsolateMixin< //
212 ::testing::Test>>>>;
213
214 using TestWithNativeContext = //
215 WithInternalIsolateMixin< //
216 WithContextMixin< //
217 WithIsolateScopeMixin< //
218 WithIsolateMixin< //
219 ::testing::Test>>>>;
220
221 using TestWithNativeContextAndCounters = //
222 WithInternalIsolateMixin< //
223 WithContextMixin< //
224 WithIsolateScopeMixin< //
225 WithIsolateMixin< //
226 ::testing::Test, kEnableCounters>>>>;
227
228 using TestWithNativeContextAndZone = //
229 WithZoneMixin< //
230 WithInternalIsolateMixin< //
231 WithContextMixin< //
232 WithIsolateScopeMixin< //
233 WithIsolateMixin< //
234 ::testing::Test>>>>>;
235
236 class V8_NODISCARD SaveFlags {
237 public:
238 SaveFlags();
239 ~SaveFlags();
240 SaveFlags(const SaveFlags&) = delete;
241 SaveFlags& operator=(const SaveFlags&) = delete;
242
243 private:
244 #define FLAG_MODE_APPLY(ftype, ctype, nam, def, cmt) ctype SAVED_##nam;
245 #include "src/flags/flag-definitions.h"
246 #undef FLAG_MODE_APPLY
247 };
248
249 // For GTest.
PrintTo(Object o,::std::ostream * os)250 inline void PrintTo(Object o, ::std::ostream* os) {
251 *os << reinterpret_cast<void*>(o.ptr());
252 }
PrintTo(Smi o,::std::ostream * os)253 inline void PrintTo(Smi o, ::std::ostream* os) {
254 *os << reinterpret_cast<void*>(o.ptr());
255 }
256
257 } // namespace internal
258 } // namespace v8
259
260 #endif // V8_UNITTESTS_TEST_UTILS_H_
261