1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include "src/global-handles.h"
29 
30 #include "test/cctest/cctest.h"
31 
32 using namespace v8::internal;
33 using v8::UniqueId;
34 
35 
36 static List<Object*> skippable_objects;
37 static List<Object*> can_skip_called_objects;
38 
39 
CanSkipCallback(Heap * heap,Object ** pointer)40 static bool CanSkipCallback(Heap* heap, Object** pointer) {
41   can_skip_called_objects.Add(*pointer);
42   return skippable_objects.Contains(*pointer);
43 }
44 
45 
ResetCanSkipData()46 static void ResetCanSkipData() {
47   skippable_objects.Clear();
48   can_skip_called_objects.Clear();
49 }
50 
51 
52 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
53  public:
TestRetainedObjectInfo()54   TestRetainedObjectInfo() : has_been_disposed_(false) {}
55 
has_been_disposed()56   bool has_been_disposed() { return has_been_disposed_; }
57 
Dispose()58   virtual void Dispose() {
59     CHECK(!has_been_disposed_);
60     has_been_disposed_ = true;
61   }
62 
IsEquivalent(v8::RetainedObjectInfo * other)63   virtual bool IsEquivalent(v8::RetainedObjectInfo* other) {
64     return other == this;
65   }
66 
GetHash()67   virtual intptr_t GetHash() { return 0; }
68 
GetLabel()69   virtual const char* GetLabel() { return "whatever"; }
70 
71  private:
72   bool has_been_disposed_;
73 };
74 
75 
76 class TestObjectVisitor : public ObjectVisitor {
77  public:
VisitPointers(Object ** start,Object ** end)78   void VisitPointers(Object** start, Object** end) override {
79     for (Object** o = start; o != end; ++o)
80       visited.Add(*o);
81   }
82 
83   List<Object*> visited;
84 };
85 
86 
TEST(IterateObjectGroupsOldApi)87 TEST(IterateObjectGroupsOldApi) {
88   CcTest::InitializeVM();
89   Isolate* isolate = CcTest::i_isolate();
90   GlobalHandles* global_handles = isolate->global_handles();
91   v8::HandleScope handle_scope(CcTest::isolate());
92 
93   Handle<Object> g1s1 =
94       global_handles->Create(*isolate->factory()->NewFixedArray(1));
95   Handle<Object> g1s2 =
96       global_handles->Create(*isolate->factory()->NewFixedArray(1));
97 
98   Handle<Object> g2s1 =
99       global_handles->Create(*isolate->factory()->NewFixedArray(1));
100   Handle<Object> g2s2 =
101       global_handles->Create(*isolate->factory()->NewFixedArray(1));
102 
103   TestRetainedObjectInfo info1;
104   TestRetainedObjectInfo info2;
105   {
106     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
107     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
108 
109     global_handles->AddObjectGroup(g1_objects, 2, &info1);
110     global_handles->AddObjectGroup(g2_objects, 2, &info2);
111   }
112 
113   // Iterate the object groups. First skip all.
114   {
115     ResetCanSkipData();
116     skippable_objects.Add(*g1s1.location());
117     skippable_objects.Add(*g1s2.location());
118     skippable_objects.Add(*g2s1.location());
119     skippable_objects.Add(*g2s2.location());
120     TestObjectVisitor visitor;
121     global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);
122 
123     // CanSkipCallback was called for all objects.
124     CHECK(can_skip_called_objects.length() == 4);
125     CHECK(can_skip_called_objects.Contains(*g1s1.location()));
126     CHECK(can_skip_called_objects.Contains(*g1s2.location()));
127     CHECK(can_skip_called_objects.Contains(*g2s1.location()));
128     CHECK(can_skip_called_objects.Contains(*g2s2.location()));
129 
130     // Nothing was visited.
131     CHECK(visitor.visited.length() == 0);
132     CHECK(!info1.has_been_disposed());
133     CHECK(!info2.has_been_disposed());
134   }
135 
136   // Iterate again, now only skip the second object group.
137   {
138     ResetCanSkipData();
139     // The first grough should still be visited, since only one object is
140     // skipped.
141     skippable_objects.Add(*g1s1.location());
142     skippable_objects.Add(*g2s1.location());
143     skippable_objects.Add(*g2s2.location());
144     TestObjectVisitor visitor;
145     global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);
146 
147     // CanSkipCallback was called for all objects.
148     CHECK(can_skip_called_objects.length() == 3 ||
149           can_skip_called_objects.length() == 4);
150     CHECK(can_skip_called_objects.Contains(*g1s2.location()));
151     CHECK(can_skip_called_objects.Contains(*g2s1.location()));
152     CHECK(can_skip_called_objects.Contains(*g2s2.location()));
153 
154     // The first group was visited.
155     CHECK(visitor.visited.length() == 2);
156     CHECK(visitor.visited.Contains(*g1s1.location()));
157     CHECK(visitor.visited.Contains(*g1s2.location()));
158     CHECK(info1.has_been_disposed());
159     CHECK(!info2.has_been_disposed());
160   }
161 
162   // Iterate again, don't skip anything.
163   {
164     ResetCanSkipData();
165     TestObjectVisitor visitor;
166     global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);
167 
168     // CanSkipCallback was called for all objects.
169     CHECK(can_skip_called_objects.length() == 1);
170     CHECK(can_skip_called_objects.Contains(*g2s1.location()) ||
171           can_skip_called_objects.Contains(*g2s2.location()));
172 
173     // The second group was visited.
174     CHECK(visitor.visited.length() == 2);
175     CHECK(visitor.visited.Contains(*g2s1.location()));
176     CHECK(visitor.visited.Contains(*g2s2.location()));
177     CHECK(info2.has_been_disposed());
178   }
179 }
180 
181 
TEST(IterateObjectGroups)182 TEST(IterateObjectGroups) {
183   CcTest::InitializeVM();
184   Isolate* isolate = CcTest::i_isolate();
185   GlobalHandles* global_handles = isolate->global_handles();
186 
187   v8::HandleScope handle_scope(CcTest::isolate());
188 
189   Handle<Object> g1s1 =
190       global_handles->Create(*isolate->factory()->NewFixedArray(1));
191   Handle<Object> g1s2 =
192       global_handles->Create(*isolate->factory()->NewFixedArray(1));
193 
194   Handle<Object> g2s1 =
195       global_handles->Create(*isolate->factory()->NewFixedArray(1));
196   Handle<Object> g2s2 =
197     global_handles->Create(*isolate->factory()->NewFixedArray(1));
198 
199   TestRetainedObjectInfo info1;
200   TestRetainedObjectInfo info2;
201   global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2));
202   global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2));
203   global_handles->SetRetainedObjectInfo(UniqueId(2), &info2);
204   global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1));
205   global_handles->SetObjectGroupId(g1s2.location(), UniqueId(1));
206   global_handles->SetRetainedObjectInfo(UniqueId(1), &info1);
207 
208   // Iterate the object groups. First skip all.
209   {
210     ResetCanSkipData();
211     skippable_objects.Add(*g1s1.location());
212     skippable_objects.Add(*g1s2.location());
213     skippable_objects.Add(*g2s1.location());
214     skippable_objects.Add(*g2s2.location());
215     TestObjectVisitor visitor;
216     global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);
217 
218     // CanSkipCallback was called for all objects.
219     CHECK(can_skip_called_objects.length() == 4);
220     CHECK(can_skip_called_objects.Contains(*g1s1.location()));
221     CHECK(can_skip_called_objects.Contains(*g1s2.location()));
222     CHECK(can_skip_called_objects.Contains(*g2s1.location()));
223     CHECK(can_skip_called_objects.Contains(*g2s2.location()));
224 
225     // Nothing was visited.
226     CHECK(visitor.visited.length() == 0);
227     CHECK(!info1.has_been_disposed());
228     CHECK(!info2.has_been_disposed());
229   }
230 
231   // Iterate again, now only skip the second object group.
232   {
233     ResetCanSkipData();
234     // The first grough should still be visited, since only one object is
235     // skipped.
236     skippable_objects.Add(*g1s1.location());
237     skippable_objects.Add(*g2s1.location());
238     skippable_objects.Add(*g2s2.location());
239     TestObjectVisitor visitor;
240     global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);
241 
242     // CanSkipCallback was called for all objects.
243     CHECK(can_skip_called_objects.length() == 3 ||
244           can_skip_called_objects.length() == 4);
245     CHECK(can_skip_called_objects.Contains(*g1s2.location()));
246     CHECK(can_skip_called_objects.Contains(*g2s1.location()));
247     CHECK(can_skip_called_objects.Contains(*g2s2.location()));
248 
249     // The first group was visited.
250     CHECK(visitor.visited.length() == 2);
251     CHECK(visitor.visited.Contains(*g1s1.location()));
252     CHECK(visitor.visited.Contains(*g1s2.location()));
253     CHECK(info1.has_been_disposed());
254     CHECK(!info2.has_been_disposed());
255   }
256 
257   // Iterate again, don't skip anything.
258   {
259     ResetCanSkipData();
260     TestObjectVisitor visitor;
261     global_handles->IterateObjectGroups(&visitor, &CanSkipCallback);
262 
263     // CanSkipCallback was called for all objects.
264     CHECK(can_skip_called_objects.length() == 1);
265     CHECK(can_skip_called_objects.Contains(*g2s1.location()) ||
266           can_skip_called_objects.Contains(*g2s2.location()));
267 
268     // The second group was visited.
269     CHECK(visitor.visited.length() == 2);
270     CHECK(visitor.visited.Contains(*g2s1.location()));
271     CHECK(visitor.visited.Contains(*g2s2.location()));
272     CHECK(info2.has_been_disposed());
273   }
274 }
275 
276 
TEST(ImplicitReferences)277 TEST(ImplicitReferences) {
278   CcTest::InitializeVM();
279   Isolate* isolate = CcTest::i_isolate();
280   GlobalHandles* global_handles = isolate->global_handles();
281 
282   v8::HandleScope handle_scope(CcTest::isolate());
283 
284   Handle<Object> g1s1 =
285       global_handles->Create(*isolate->factory()->NewFixedArray(1));
286   Handle<Object> g1c1 =
287       global_handles->Create(*isolate->factory()->NewFixedArray(1));
288   Handle<Object> g1c2 =
289       global_handles->Create(*isolate->factory()->NewFixedArray(1));
290 
291 
292   Handle<Object> g2s1 =
293       global_handles->Create(*isolate->factory()->NewFixedArray(1));
294   Handle<Object> g2s2 =
295     global_handles->Create(*isolate->factory()->NewFixedArray(1));
296   Handle<Object> g2c1 =
297     global_handles->Create(*isolate->factory()->NewFixedArray(1));
298 
299   global_handles->SetObjectGroupId(g1s1.location(), UniqueId(1));
300   global_handles->SetObjectGroupId(g2s1.location(), UniqueId(2));
301   global_handles->SetObjectGroupId(g2s2.location(), UniqueId(2));
302   global_handles->SetReferenceFromGroup(UniqueId(1), g1c1.location());
303   global_handles->SetReferenceFromGroup(UniqueId(1), g1c2.location());
304   global_handles->SetReferenceFromGroup(UniqueId(2), g2c1.location());
305 
306   List<ImplicitRefGroup*>* implicit_refs =
307       global_handles->implicit_ref_groups();
308   USE(implicit_refs);
309   CHECK(implicit_refs->length() == 2);
310   CHECK(implicit_refs->at(0)->parent ==
311         reinterpret_cast<HeapObject**>(g1s1.location()));
312   CHECK(implicit_refs->at(0)->length == 2);
313   CHECK(implicit_refs->at(0)->children[0] == g1c1.location());
314   CHECK(implicit_refs->at(0)->children[1] == g1c2.location());
315   CHECK(implicit_refs->at(1)->parent ==
316         reinterpret_cast<HeapObject**>(g2s1.location()));
317   CHECK(implicit_refs->at(1)->length == 1);
318   CHECK(implicit_refs->at(1)->children[0] == g2c1.location());
319   global_handles->RemoveObjectGroups();
320   global_handles->RemoveImplicitRefGroups();
321 }
322 
323 
TEST(EternalHandles)324 TEST(EternalHandles) {
325   CcTest::InitializeVM();
326   Isolate* isolate = CcTest::i_isolate();
327   v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
328   EternalHandles* eternal_handles = isolate->eternal_handles();
329 
330   // Create a number of handles that will not be on a block boundary
331   const int kArrayLength = 2048-1;
332   int indices[kArrayLength];
333   v8::Eternal<v8::Value> eternals[kArrayLength];
334 
335   CHECK_EQ(0, eternal_handles->NumberOfHandles());
336   for (int i = 0; i < kArrayLength; i++) {
337     indices[i] = -1;
338     HandleScope scope(isolate);
339     v8::Local<v8::Object> object = v8::Object::New(v8_isolate);
340     object->Set(v8_isolate->GetCurrentContext(), i,
341                 v8::Integer::New(v8_isolate, i))
342         .FromJust();
343     // Create with internal api
344     eternal_handles->Create(
345         isolate, *v8::Utils::OpenHandle(*object), &indices[i]);
346     // Create with external api
347     CHECK(eternals[i].IsEmpty());
348     eternals[i].Set(v8_isolate, object);
349     CHECK(!eternals[i].IsEmpty());
350   }
351 
352   isolate->heap()->CollectAllAvailableGarbage();
353 
354   for (int i = 0; i < kArrayLength; i++) {
355     for (int j = 0; j < 2; j++) {
356       HandleScope scope(isolate);
357       v8::Local<v8::Value> local;
358       if (j == 0) {
359         // Test internal api
360         local = v8::Utils::ToLocal(eternal_handles->Get(indices[i]));
361       } else {
362         // Test external api
363         local = eternals[i].Get(v8_isolate);
364       }
365       v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(local);
366       v8::Local<v8::Value> value =
367           object->Get(v8_isolate->GetCurrentContext(), i).ToLocalChecked();
368       CHECK(value->IsInt32());
369       CHECK_EQ(i,
370                value->Int32Value(v8_isolate->GetCurrentContext()).FromJust());
371     }
372   }
373 
374   CHECK_EQ(2*kArrayLength, eternal_handles->NumberOfHandles());
375 
376   // Create an eternal via the constructor
377   {
378     HandleScope scope(isolate);
379     v8::Local<v8::Object> object = v8::Object::New(v8_isolate);
380     v8::Eternal<v8::Object> eternal(v8_isolate, object);
381     CHECK(!eternal.IsEmpty());
382     CHECK(object == eternal.Get(v8_isolate));
383   }
384 
385   CHECK_EQ(2*kArrayLength + 1, eternal_handles->NumberOfHandles());
386 }
387 
388 
TEST(PersistentBaseGetLocal)389 TEST(PersistentBaseGetLocal) {
390   CcTest::InitializeVM();
391   v8::Isolate* isolate = CcTest::isolate();
392 
393   v8::HandleScope scope(isolate);
394   v8::Local<v8::Object> o = v8::Object::New(isolate);
395   CHECK(!o.IsEmpty());
396   v8::Persistent<v8::Object> p(isolate, o);
397   CHECK(o == p.Get(isolate));
398   CHECK(v8::Local<v8::Object>::New(isolate, p) == p.Get(isolate));
399 
400   v8::Global<v8::Object> g(isolate, o);
401   CHECK(o == g.Get(isolate));
402   CHECK(v8::Local<v8::Object>::New(isolate, g) == g.Get(isolate));
403 }
404 
405 
WeakCallback(const v8::WeakCallbackInfo<void> & data)406 void WeakCallback(const v8::WeakCallbackInfo<void>& data) {}
407 
408 
TEST(WeakPersistentSmi)409 TEST(WeakPersistentSmi) {
410   CcTest::InitializeVM();
411   v8::Isolate* isolate = CcTest::isolate();
412 
413   v8::HandleScope scope(isolate);
414   v8::Local<v8::Number> n = v8::Number::New(isolate, 0);
415   v8::Global<v8::Number> g(isolate, n);
416 
417   // Should not crash.
418   g.SetWeak<void>(nullptr, &WeakCallback, v8::WeakCallbackType::kParameter);
419 }
420