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