1// Copyright 2009 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 "v8.h"
31
32#include "api.h"
33#include "cctest.h"
34#include "frames-inl.h"
35#include "string-stream.h"
36
37using ::v8::ObjectTemplate;
38using ::v8::Value;
39using ::v8::Context;
40using ::v8::Local;
41using ::v8::String;
42using ::v8::Script;
43using ::v8::Function;
44using ::v8::AccessorInfo;
45using ::v8::Extension;
46
47namespace i = ::v8::internal;
48
49static v8::Handle<Value> handle_property(Local<String> name,
50                                         const AccessorInfo&) {
51  ApiTestFuzzer::Fuzz();
52  return v8_num(900);
53}
54
55
56THREADED_TEST(PropertyHandler) {
57  v8::HandleScope scope;
58  Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
59  fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property);
60  LocalContext env;
61  Local<Function> fun = fun_templ->GetFunction();
62  env->Global()->Set(v8_str("Fun"), fun);
63  Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;");
64  CHECK_EQ(900, getter->Run()->Int32Value());
65  Local<Script> setter = v8_compile("obj.foo = 901;");
66  CHECK_EQ(901, setter->Run()->Int32Value());
67}
68
69
70static v8::Handle<Value> GetIntValue(Local<String> property,
71                                     const AccessorInfo& info) {
72  ApiTestFuzzer::Fuzz();
73  int* value =
74      static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
75  return v8_num(*value);
76}
77
78
79static void SetIntValue(Local<String> property,
80                        Local<Value> value,
81                        const AccessorInfo& info) {
82  int* field =
83      static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
84  *field = value->Int32Value();
85}
86
87int foo, bar, baz;
88
89THREADED_TEST(GlobalVariableAccess) {
90  foo = 0;
91  bar = -4;
92  baz = 10;
93  v8::HandleScope scope;
94  v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
95  templ->InstanceTemplate()->SetAccessor(v8_str("foo"),
96                                         GetIntValue,
97                                         SetIntValue,
98                                         v8::External::New(&foo));
99  templ->InstanceTemplate()->SetAccessor(v8_str("bar"),
100                                         GetIntValue,
101                                         SetIntValue,
102                                         v8::External::New(&bar));
103  templ->InstanceTemplate()->SetAccessor(v8_str("baz"),
104                                         GetIntValue,
105                                         SetIntValue,
106                                         v8::External::New(&baz));
107  LocalContext env(0, templ->InstanceTemplate());
108  v8_compile("foo = (++bar) + baz")->Run();
109  CHECK_EQ(bar, -3);
110  CHECK_EQ(foo, 7);
111}
112
113
114static int x_register = 0;
115static v8::Handle<v8::Object> x_receiver;
116static v8::Handle<v8::Object> x_holder;
117
118
119static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) {
120  ApiTestFuzzer::Fuzz();
121  CHECK_EQ(x_receiver, info.This());
122  CHECK_EQ(x_holder, info.Holder());
123  return v8_num(x_register);
124}
125
126
127static void XSetter(Local<String> name,
128                    Local<Value> value,
129                    const AccessorInfo& info) {
130  CHECK_EQ(x_holder, info.This());
131  CHECK_EQ(x_holder, info.Holder());
132  x_register = value->Int32Value();
133}
134
135
136THREADED_TEST(AccessorIC) {
137  v8::HandleScope scope;
138  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
139  obj->SetAccessor(v8_str("x"), XGetter, XSetter);
140  LocalContext context;
141  x_holder = obj->NewInstance();
142  context->Global()->Set(v8_str("holder"), x_holder);
143  x_receiver = v8::Object::New();
144  context->Global()->Set(v8_str("obj"), x_receiver);
145  v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun(
146    "obj.__proto__ = holder;"
147    "var result = [];"
148    "for (var i = 0; i < 10; i++) {"
149    "  holder.x = i;"
150    "  result.push(obj.x);"
151    "}"
152    "result"));
153  CHECK_EQ(10, array->Length());
154  for (int i = 0; i < 10; i++) {
155    v8::Handle<Value> entry = array->Get(v8::Integer::New(i));
156    CHECK_EQ(v8::Integer::New(i), entry);
157  }
158}
159
160
161static v8::Handle<Value> AccessorProhibitsOverwritingGetter(
162    Local<String> name,
163    const AccessorInfo& info) {
164  ApiTestFuzzer::Fuzz();
165  return v8::True();
166}
167
168
169THREADED_TEST(AccessorProhibitsOverwriting) {
170  v8::HandleScope scope;
171  LocalContext context;
172  Local<ObjectTemplate> templ = ObjectTemplate::New();
173  templ->SetAccessor(v8_str("x"),
174                     AccessorProhibitsOverwritingGetter,
175                     0,
176                     v8::Handle<Value>(),
177                     v8::PROHIBITS_OVERWRITING,
178                     v8::ReadOnly);
179  Local<v8::Object> instance = templ->NewInstance();
180  context->Global()->Set(v8_str("obj"), instance);
181  Local<Value> value = CompileRun(
182      "obj.__defineGetter__('x', function() { return false; });"
183      "obj.x");
184  CHECK(value->BooleanValue());
185  value = CompileRun(
186      "var setter_called = false;"
187      "obj.__defineSetter__('x', function() { setter_called = true; });"
188      "obj.x = 42;"
189      "setter_called");
190  CHECK(!value->BooleanValue());
191  value = CompileRun(
192      "obj2 = {};"
193      "obj2.__proto__ = obj;"
194      "obj2.__defineGetter__('x', function() { return false; });"
195      "obj2.x");
196  CHECK(value->BooleanValue());
197  value = CompileRun(
198      "var setter_called = false;"
199      "obj2 = {};"
200      "obj2.__proto__ = obj;"
201      "obj2.__defineSetter__('x', function() { setter_called = true; });"
202      "obj2.x = 42;"
203      "setter_called");
204  CHECK(!value->BooleanValue());
205}
206
207
208template <int C>
209static v8::Handle<Value> HandleAllocatingGetter(Local<String> name,
210                                                const AccessorInfo& info) {
211  ApiTestFuzzer::Fuzz();
212  for (int i = 0; i < C; i++)
213    v8::String::New("foo");
214  return v8::String::New("foo");
215}
216
217
218THREADED_TEST(HandleScopePop) {
219  v8::HandleScope scope;
220  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
221  obj->SetAccessor(v8_str("one"), HandleAllocatingGetter<1>);
222  obj->SetAccessor(v8_str("many"), HandleAllocatingGetter<1024>);
223  LocalContext context;
224  v8::Handle<v8::Object> inst = obj->NewInstance();
225  context->Global()->Set(v8::String::New("obj"), inst);
226  int count_before = i::HandleScope::NumberOfHandles();
227  {
228    v8::HandleScope scope;
229    CompileRun(
230        "for (var i = 0; i < 1000; i++) {"
231        "  obj.one;"
232        "  obj.many;"
233        "}");
234  }
235  int count_after = i::HandleScope::NumberOfHandles();
236  CHECK_EQ(count_before, count_after);
237}
238
239static v8::Handle<Value> CheckAccessorArgsCorrect(Local<String> name,
240                                                  const AccessorInfo& info) {
241  CHECK(info.This() == info.Holder());
242  CHECK(info.Data()->Equals(v8::String::New("data")));
243  ApiTestFuzzer::Fuzz();
244  CHECK(info.This() == info.Holder());
245  CHECK(info.Data()->Equals(v8::String::New("data")));
246  HEAP->CollectAllGarbage(true);
247  CHECK(info.This() == info.Holder());
248  CHECK(info.Data()->Equals(v8::String::New("data")));
249  return v8::Integer::New(17);
250}
251
252THREADED_TEST(DirectCall) {
253  v8::HandleScope scope;
254  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
255  obj->SetAccessor(v8_str("xxx"),
256                   CheckAccessorArgsCorrect,
257                   NULL,
258                   v8::String::New("data"));
259  LocalContext context;
260  v8::Handle<v8::Object> inst = obj->NewInstance();
261  context->Global()->Set(v8::String::New("obj"), inst);
262  Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
263  for (int i = 0; i < 10; i++) {
264    Local<Value> result = scr->Run();
265    CHECK(!result.IsEmpty());
266    CHECK_EQ(17, result->Int32Value());
267  }
268}
269
270static v8::Handle<Value> EmptyGetter(Local<String> name,
271                                     const AccessorInfo& info) {
272  CheckAccessorArgsCorrect(name, info);
273  ApiTestFuzzer::Fuzz();
274  CheckAccessorArgsCorrect(name, info);
275  return v8::Handle<v8::Value>();
276}
277
278THREADED_TEST(EmptyResult) {
279  v8::HandleScope scope;
280  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
281  obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data"));
282  LocalContext context;
283  v8::Handle<v8::Object> inst = obj->NewInstance();
284  context->Global()->Set(v8::String::New("obj"), inst);
285  Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
286  for (int i = 0; i < 10; i++) {
287    Local<Value> result = scr->Run();
288    CHECK(result == v8::Undefined());
289  }
290}
291
292
293THREADED_TEST(NoReuseRegress) {
294  // Check that the IC generated for the one test doesn't get reused
295  // for the other.
296  v8::HandleScope scope;
297  {
298    v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
299    obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data"));
300    LocalContext context;
301    v8::Handle<v8::Object> inst = obj->NewInstance();
302    context->Global()->Set(v8::String::New("obj"), inst);
303    Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
304    for (int i = 0; i < 2; i++) {
305      Local<Value> result = scr->Run();
306      CHECK(result == v8::Undefined());
307    }
308  }
309  {
310    v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
311    obj->SetAccessor(v8_str("xxx"),
312                     CheckAccessorArgsCorrect,
313                     NULL,
314                     v8::String::New("data"));
315    LocalContext context;
316    v8::Handle<v8::Object> inst = obj->NewInstance();
317    context->Global()->Set(v8::String::New("obj"), inst);
318    Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
319    for (int i = 0; i < 10; i++) {
320      Local<Value> result = scr->Run();
321      CHECK(!result.IsEmpty());
322      CHECK_EQ(17, result->Int32Value());
323    }
324  }
325}
326
327static v8::Handle<Value> ThrowingGetAccessor(Local<String> name,
328                                             const AccessorInfo& info) {
329  ApiTestFuzzer::Fuzz();
330  return v8::ThrowException(v8_str("g"));
331}
332
333
334static void ThrowingSetAccessor(Local<String> name,
335                                Local<Value> value,
336                                const AccessorInfo& info) {
337  v8::ThrowException(value);
338}
339
340
341THREADED_TEST(Regress1054726) {
342  v8::HandleScope scope;
343  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
344  obj->SetAccessor(v8_str("x"),
345                   ThrowingGetAccessor,
346                   ThrowingSetAccessor,
347                   Local<Value>());
348
349  LocalContext env;
350  env->Global()->Set(v8_str("obj"), obj->NewInstance());
351
352  // Use the throwing property setter/getter in a loop to force
353  // the accessor ICs to be initialized.
354  v8::Handle<Value> result;
355  result = Script::Compile(v8_str(
356      "var result = '';"
357      "for (var i = 0; i < 5; i++) {"
358      "  try { obj.x; } catch (e) { result += e; }"
359      "}; result"))->Run();
360  CHECK_EQ(v8_str("ggggg"), result);
361
362  result = Script::Compile(String::New(
363      "var result = '';"
364      "for (var i = 0; i < 5; i++) {"
365      "  try { obj.x = i; } catch (e) { result += e; }"
366      "}; result"))->Run();
367  CHECK_EQ(v8_str("01234"), result);
368}
369
370
371static v8::Handle<Value> AllocGetter(Local<String> name,
372                                     const AccessorInfo& info) {
373  ApiTestFuzzer::Fuzz();
374  return v8::Array::New(1000);
375}
376
377
378THREADED_TEST(Gc) {
379  v8::HandleScope scope;
380  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
381  obj->SetAccessor(v8_str("xxx"), AllocGetter);
382  LocalContext env;
383  env->Global()->Set(v8_str("obj"), obj->NewInstance());
384  Script::Compile(String::New(
385      "var last = [];"
386      "for (var i = 0; i < 2048; i++) {"
387      "  var result = obj.xxx;"
388      "  result[0] = last;"
389      "  last = result;"
390      "}"))->Run();
391}
392
393
394static v8::Handle<Value> StackCheck(Local<String> name,
395                                    const AccessorInfo& info) {
396  i::StackFrameIterator iter;
397  for (int i = 0; !iter.done(); i++) {
398    i::StackFrame* frame = iter.frame();
399    CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT));
400    i::Code* code = frame->LookupCode();
401    CHECK(code->IsCode());
402    i::Address pc = frame->pc();
403    CHECK(code->contains(pc));
404    iter.Advance();
405  }
406  return v8::Undefined();
407}
408
409
410THREADED_TEST(StackIteration) {
411  v8::HandleScope scope;
412  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
413  i::StringStream::ClearMentionedObjectCache();
414  obj->SetAccessor(v8_str("xxx"), StackCheck);
415  LocalContext env;
416  env->Global()->Set(v8_str("obj"), obj->NewInstance());
417  Script::Compile(String::New(
418      "function foo() {"
419      "  return obj.xxx;"
420      "}"
421      "for (var i = 0; i < 100; i++) {"
422      "  foo();"
423      "}"))->Run();
424}
425
426
427static v8::Handle<Value> AllocateHandles(Local<String> name,
428                                         const AccessorInfo& info) {
429  for (int i = 0; i < i::kHandleBlockSize + 1; i++) {
430    v8::Local<v8::Value>::New(name);
431  }
432  return v8::Integer::New(100);
433}
434
435
436THREADED_TEST(HandleScopeSegment) {
437  // Check that we can return values past popping of handle scope
438  // segments.
439  v8::HandleScope scope;
440  v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
441  obj->SetAccessor(v8_str("xxx"), AllocateHandles);
442  LocalContext env;
443  env->Global()->Set(v8_str("obj"), obj->NewInstance());
444  v8::Handle<v8::Value> result = Script::Compile(String::New(
445      "var result;"
446      "for (var i = 0; i < 4; i++)"
447      "  result = obj.xxx;"
448      "result;"))->Run();
449  CHECK_EQ(100, result->Int32Value());
450}
451