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 <stdlib.h>
29
30#include "src/v8.h"
31#include "src/api.h"
32#include "src/heap.h"
33#include "src/objects.h"
34
35#include "test/cctest/cctest.h"
36
37using namespace v8::internal;
38
39static Isolate* GetIsolateFrom(LocalContext* context) {
40  return reinterpret_cast<Isolate*>((*context)->GetIsolate());
41}
42
43
44static int CountArrayBuffersInWeakList(Heap* heap) {
45  int count = 0;
46  for (Object* o = heap->array_buffers_list();
47       !o->IsUndefined();
48       o = JSArrayBuffer::cast(o)->weak_next()) {
49    count++;
50  }
51  return count;
52}
53
54
55static bool HasArrayBufferInWeakList(Heap* heap, JSArrayBuffer* ab) {
56  for (Object* o = heap->array_buffers_list();
57       !o->IsUndefined();
58       o = JSArrayBuffer::cast(o)->weak_next()) {
59    if (ab == o) return true;
60  }
61  return false;
62}
63
64
65static int CountViews(JSArrayBuffer* array_buffer) {
66  int count = 0;
67  for (Object* o = array_buffer->weak_first_view();
68       !o->IsUndefined();
69       o = JSArrayBufferView::cast(o)->weak_next()) {
70    count++;
71  }
72
73  return count;
74}
75
76static bool HasViewInWeakList(JSArrayBuffer* array_buffer,
77                              JSArrayBufferView* ta) {
78  for (Object* o = array_buffer->weak_first_view();
79       !o->IsUndefined();
80       o = JSArrayBufferView::cast(o)->weak_next()) {
81    if (ta == o) return true;
82  }
83  return false;
84}
85
86
87TEST(WeakArrayBuffersFromApi) {
88  v8::V8::Initialize();
89  LocalContext context;
90  Isolate* isolate = GetIsolateFrom(&context);
91
92  int start = CountArrayBuffersInWeakList(isolate->heap());
93  {
94    v8::HandleScope s1(context->GetIsolate());
95    v8::Handle<v8::ArrayBuffer> ab1 =
96        v8::ArrayBuffer::New(context->GetIsolate(), 256);
97    {
98      v8::HandleScope s2(context->GetIsolate());
99      v8::Handle<v8::ArrayBuffer> ab2 =
100          v8::ArrayBuffer::New(context->GetIsolate(), 128);
101
102      Handle<JSArrayBuffer> iab1 = v8::Utils::OpenHandle(*ab1);
103      Handle<JSArrayBuffer> iab2 = v8::Utils::OpenHandle(*ab2);
104      CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap()) - start);
105      CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab1));
106      CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab2));
107    }
108    isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
109    CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start);
110    {
111      HandleScope scope2(isolate);
112      Handle<JSArrayBuffer> iab1 = v8::Utils::OpenHandle(*ab1);
113
114      CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab1));
115    }
116  }
117
118  isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
119  CHECK_EQ(start, CountArrayBuffersInWeakList(isolate->heap()));
120}
121
122
123TEST(WeakArrayBuffersFromScript) {
124  v8::V8::Initialize();
125  LocalContext context;
126  Isolate* isolate = GetIsolateFrom(&context);
127  int start = CountArrayBuffersInWeakList(isolate->heap());
128
129  for (int i = 1; i <= 3; i++) {
130    // Create 3 array buffers, make i-th of them garbage,
131    // validate correct state of array buffer weak list.
132    CHECK_EQ(start, CountArrayBuffersInWeakList(isolate->heap()));
133    {
134      v8::HandleScope scope(context->GetIsolate());
135
136      {
137        v8::HandleScope s1(context->GetIsolate());
138        CompileRun("var ab1 = new ArrayBuffer(256);"
139                   "var ab2 = new ArrayBuffer(256);"
140                   "var ab3 = new ArrayBuffer(256);");
141        v8::Handle<v8::ArrayBuffer> ab1 =
142            v8::Handle<v8::ArrayBuffer>::Cast(CompileRun("ab1"));
143        v8::Handle<v8::ArrayBuffer> ab2 =
144            v8::Handle<v8::ArrayBuffer>::Cast(CompileRun("ab2"));
145        v8::Handle<v8::ArrayBuffer> ab3 =
146            v8::Handle<v8::ArrayBuffer>::Cast(CompileRun("ab3"));
147
148        CHECK_EQ(3, CountArrayBuffersInWeakList(isolate->heap()) - start);
149        CHECK(HasArrayBufferInWeakList(isolate->heap(),
150              *v8::Utils::OpenHandle(*ab1)));
151        CHECK(HasArrayBufferInWeakList(isolate->heap(),
152              *v8::Utils::OpenHandle(*ab2)));
153        CHECK(HasArrayBufferInWeakList(isolate->heap(),
154              *v8::Utils::OpenHandle(*ab3)));
155      }
156
157      i::ScopedVector<char> source(1024);
158      i::SNPrintF(source, "ab%d = null;", i);
159      CompileRun(source.start());
160      isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
161
162      CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap()) - start);
163
164      {
165        v8::HandleScope s2(context->GetIsolate());
166        for (int j = 1; j <= 3; j++) {
167          if (j == i) continue;
168          i::SNPrintF(source, "ab%d", j);
169          v8::Handle<v8::ArrayBuffer> ab =
170              v8::Handle<v8::ArrayBuffer>::Cast(CompileRun(source.start()));
171          CHECK(HasArrayBufferInWeakList(isolate->heap(),
172                *v8::Utils::OpenHandle(*ab)));
173          }
174      }
175
176      CompileRun("ab1 = null; ab2 = null; ab3 = null;");
177    }
178
179    isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
180    CHECK_EQ(start, CountArrayBuffersInWeakList(isolate->heap()));
181  }
182}
183
184template <typename View>
185void TestViewFromApi() {
186  v8::V8::Initialize();
187  LocalContext context;
188  Isolate* isolate = GetIsolateFrom(&context);
189
190  v8::HandleScope s1(context->GetIsolate());
191  v8::Handle<v8::ArrayBuffer> ab =
192      v8::ArrayBuffer::New(context->GetIsolate(), 2048);
193  Handle<JSArrayBuffer> iab = v8::Utils::OpenHandle(*ab);
194  {
195    v8::HandleScope s2(context->GetIsolate());
196    v8::Handle<View> ta1 = View::New(ab, 0, 256);
197    {
198      v8::HandleScope s3(context->GetIsolate());
199      v8::Handle<View> ta2 = View::New(ab, 0, 128);
200
201      Handle<JSArrayBufferView> ita1 = v8::Utils::OpenHandle(*ta1);
202      Handle<JSArrayBufferView> ita2 = v8::Utils::OpenHandle(*ta2);
203      CHECK_EQ(2, CountViews(*iab));
204      CHECK(HasViewInWeakList(*iab, *ita1));
205      CHECK(HasViewInWeakList(*iab, *ita2));
206    }
207    isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
208    CHECK_EQ(1, CountViews(*iab));
209    Handle<JSArrayBufferView> ita1 = v8::Utils::OpenHandle(*ta1);
210    CHECK(HasViewInWeakList(*iab, *ita1));
211  }
212  isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
213
214  CHECK_EQ(0, CountViews(*iab));
215}
216
217
218TEST(Uint8ArrayFromApi) {
219  TestViewFromApi<v8::Uint8Array>();
220}
221
222
223TEST(Int8ArrayFromApi) {
224  TestViewFromApi<v8::Int8Array>();
225}
226
227
228TEST(Uint16ArrayFromApi) {
229  TestViewFromApi<v8::Uint16Array>();
230}
231
232
233TEST(Int16ArrayFromApi) {
234  TestViewFromApi<v8::Int16Array>();
235}
236
237
238TEST(Uint32ArrayFromApi) {
239  TestViewFromApi<v8::Uint32Array>();
240}
241
242
243TEST(Int32ArrayFromApi) {
244  TestViewFromApi<v8::Int32Array>();
245}
246
247
248TEST(Float32ArrayFromApi) {
249  TestViewFromApi<v8::Float32Array>();
250}
251
252
253TEST(Float64ArrayFromApi) {
254  TestViewFromApi<v8::Float64Array>();
255}
256
257
258TEST(Uint8ClampedArrayFromApi) {
259  TestViewFromApi<v8::Uint8ClampedArray>();
260}
261
262
263TEST(DataViewFromApi) {
264  TestViewFromApi<v8::DataView>();
265}
266
267template <typename TypedArray>
268static void TestTypedArrayFromScript(const char* constructor) {
269  v8::V8::Initialize();
270  LocalContext context;
271  Isolate* isolate = GetIsolateFrom(&context);
272  v8::HandleScope scope(context->GetIsolate());
273  int start = CountArrayBuffersInWeakList(isolate->heap());
274  CompileRun("var ab = new ArrayBuffer(2048);");
275  for (int i = 1; i <= 3; i++) {
276    // Create 3 typed arrays, make i-th of them garbage,
277    // validate correct state of typed array weak list.
278    v8::HandleScope s0(context->GetIsolate());
279    i::ScopedVector<char> source(2048);
280
281    CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start);
282
283    {
284      v8::HandleScope s1(context->GetIsolate());
285      i::SNPrintF(source,
286              "var ta1 = new %s(ab);"
287              "var ta2 = new %s(ab);"
288              "var ta3 = new %s(ab)",
289              constructor, constructor, constructor);
290
291      CompileRun(source.start());
292      v8::Handle<v8::ArrayBuffer> ab =
293          v8::Handle<v8::ArrayBuffer>::Cast(CompileRun("ab"));
294      v8::Handle<TypedArray> ta1 =
295          v8::Handle<TypedArray>::Cast(CompileRun("ta1"));
296      v8::Handle<TypedArray> ta2 =
297          v8::Handle<TypedArray>::Cast(CompileRun("ta2"));
298      v8::Handle<TypedArray> ta3 =
299          v8::Handle<TypedArray>::Cast(CompileRun("ta3"));
300      CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start);
301      Handle<JSArrayBuffer> iab = v8::Utils::OpenHandle(*ab);
302      CHECK_EQ(3, CountViews(*iab));
303      CHECK(HasViewInWeakList(*iab, *v8::Utils::OpenHandle(*ta1)));
304      CHECK(HasViewInWeakList(*iab, *v8::Utils::OpenHandle(*ta2)));
305      CHECK(HasViewInWeakList(*iab, *v8::Utils::OpenHandle(*ta3)));
306    }
307
308    i::SNPrintF(source, "ta%d = null;", i);
309    CompileRun(source.start());
310    isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
311
312    CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start);
313
314    {
315      v8::HandleScope s2(context->GetIsolate());
316      v8::Handle<v8::ArrayBuffer> ab =
317          v8::Handle<v8::ArrayBuffer>::Cast(CompileRun("ab"));
318      Handle<JSArrayBuffer> iab = v8::Utils::OpenHandle(*ab);
319      CHECK_EQ(2, CountViews(*iab));
320      for (int j = 1; j <= 3; j++) {
321        if (j == i) continue;
322        i::SNPrintF(source, "ta%d", j);
323        v8::Handle<TypedArray> ta =
324            v8::Handle<TypedArray>::Cast(CompileRun(source.start()));
325        CHECK(HasViewInWeakList(*iab, *v8::Utils::OpenHandle(*ta)));
326      }
327    }
328
329    CompileRun("ta1 = null; ta2 = null; ta3 = null;");
330    isolate->heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
331
332    CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap()) - start);
333
334    {
335      v8::HandleScope s3(context->GetIsolate());
336      v8::Handle<v8::ArrayBuffer> ab =
337          v8::Handle<v8::ArrayBuffer>::Cast(CompileRun("ab"));
338      Handle<JSArrayBuffer> iab = v8::Utils::OpenHandle(*ab);
339      CHECK_EQ(0, CountViews(*iab));
340    }
341  }
342}
343
344
345TEST(Uint8ArrayFromScript) {
346  TestTypedArrayFromScript<v8::Uint8Array>("Uint8Array");
347}
348
349
350TEST(Int8ArrayFromScript) {
351  TestTypedArrayFromScript<v8::Int8Array>("Int8Array");
352}
353
354
355TEST(Uint16ArrayFromScript) {
356  TestTypedArrayFromScript<v8::Uint16Array>("Uint16Array");
357}
358
359
360TEST(Int16ArrayFromScript) {
361  TestTypedArrayFromScript<v8::Int16Array>("Int16Array");
362}
363
364
365TEST(Uint32ArrayFromScript) {
366  TestTypedArrayFromScript<v8::Uint32Array>("Uint32Array");
367}
368
369
370TEST(Int32ArrayFromScript) {
371  TestTypedArrayFromScript<v8::Int32Array>("Int32Array");
372}
373
374
375TEST(Float32ArrayFromScript) {
376  TestTypedArrayFromScript<v8::Float32Array>("Float32Array");
377}
378
379
380TEST(Float64ArrayFromScript) {
381  TestTypedArrayFromScript<v8::Float64Array>("Float64Array");
382}
383
384
385TEST(Uint8ClampedArrayFromScript) {
386  TestTypedArrayFromScript<v8::Uint8ClampedArray>("Uint8ClampedArray");
387}
388
389
390TEST(DataViewFromScript) {
391  TestTypedArrayFromScript<v8::DataView>("DataView");
392}
393