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