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