1 // Copyright 2007-2008 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 "compilation-cache.h"
34 #include "snapshot.h"
35 #include "platform.h"
36 #include "top.h"
37 #include "cctest.h"
38
IsNaN(double x)39 static bool IsNaN(double x) {
40 #ifdef WIN32
41 return _isnan(x);
42 #else
43 return isnan(x);
44 #endif
45 }
46
47 using ::v8::ObjectTemplate;
48 using ::v8::Value;
49 using ::v8::Context;
50 using ::v8::Local;
51 using ::v8::String;
52 using ::v8::Script;
53 using ::v8::Function;
54 using ::v8::AccessorInfo;
55 using ::v8::Extension;
56
57 namespace i = ::v8::internal;
58
v8_num(double x)59 static Local<Value> v8_num(double x) {
60 return v8::Number::New(x);
61 }
62
63
v8_str(const char * x)64 static Local<String> v8_str(const char* x) {
65 return String::New(x);
66 }
67
68
v8_compile(const char * x)69 static Local<Script> v8_compile(const char* x) {
70 return Script::Compile(v8_str(x));
71 }
72
73
74 // A LocalContext holds a reference to a v8::Context.
75 class LocalContext {
76 public:
LocalContext(v8::ExtensionConfiguration * extensions=0,v8::Handle<ObjectTemplate> global_template=v8::Handle<ObjectTemplate> (),v8::Handle<Value> global_object=v8::Handle<Value> ())77 LocalContext(v8::ExtensionConfiguration* extensions = 0,
78 v8::Handle<ObjectTemplate> global_template =
79 v8::Handle<ObjectTemplate>(),
80 v8::Handle<Value> global_object = v8::Handle<Value>())
81 : context_(Context::New(extensions, global_template, global_object)) {
82 context_->Enter();
83 }
84
~LocalContext()85 virtual ~LocalContext() {
86 context_->Exit();
87 context_.Dispose();
88 }
89
operator ->()90 Context* operator->() { return *context_; }
operator *()91 Context* operator*() { return *context_; }
local()92 Local<Context> local() { return Local<Context>::New(context_); }
IsReady()93 bool IsReady() { return !context_.IsEmpty(); }
94
95 private:
96 v8::Persistent<Context> context_;
97 };
98
99
100 // Switches between all the Api tests using the threading support.
101 // In order to get a surprising but repeatable pattern of thread
102 // switching it has extra semaphores to control the order in which
103 // the tests alternate, not relying solely on the big V8 lock.
104 //
105 // A test is augmented with calls to ApiTestFuzzer::Fuzz() in its
106 // callbacks. This will have no effect when we are not running the
107 // thread fuzzing test. In the thread fuzzing test it will
108 // pseudorandomly select a successor thread and switch execution
109 // to that thread, suspending the current test.
110 class ApiTestFuzzer: public v8::internal::Thread {
111 public:
112 void CallTest();
ApiTestFuzzer(int num)113 explicit ApiTestFuzzer(int num)
114 : test_number_(num),
115 gate_(v8::internal::OS::CreateSemaphore(0)),
116 active_(true) {
117 }
~ApiTestFuzzer()118 ~ApiTestFuzzer() { delete gate_; }
119
120 // The ApiTestFuzzer is also a Thread, so it has a Run method.
121 virtual void Run();
122
123 enum PartOfTest { FIRST_PART, SECOND_PART };
124
125 static void Setup(PartOfTest part);
126 static void RunAllTests();
127 static void TearDown();
128 // This method switches threads if we are running the Threading test.
129 // Otherwise it does nothing.
130 static void Fuzz();
131 private:
132 static bool fuzzing_;
133 static int tests_being_run_;
134 static int current_;
135 static int active_tests_;
136 static bool NextThread();
137 int test_number_;
138 v8::internal::Semaphore* gate_;
139 bool active_;
140 void ContextSwitch();
141 static int GetNextTestNumber();
142 static v8::internal::Semaphore* all_tests_done_;
143 };
144
145
146 #define THREADED_TEST(Name) \
147 static void Test##Name(); \
148 RegisterThreadedTest register_##Name(Test##Name); \
149 /* */ TEST(Name)
150
151
152 class RegisterThreadedTest {
153 public:
RegisterThreadedTest(CcTest::TestFunction * callback)154 explicit RegisterThreadedTest(CcTest::TestFunction* callback)
155 : fuzzer_(NULL), callback_(callback) {
156 prev_ = first_;
157 first_ = this;
158 count_++;
159 }
count()160 static int count() { return count_; }
nth(int i)161 static RegisterThreadedTest* nth(int i) {
162 CHECK(i < count());
163 RegisterThreadedTest* current = first_;
164 while (i > 0) {
165 i--;
166 current = current->prev_;
167 }
168 return current;
169 }
callback()170 CcTest::TestFunction* callback() { return callback_; }
171 ApiTestFuzzer* fuzzer_;
172
173 private:
174 static RegisterThreadedTest* first_;
175 static int count_;
176 CcTest::TestFunction* callback_;
177 RegisterThreadedTest* prev_;
178 };
179
180
181 RegisterThreadedTest *RegisterThreadedTest::first_ = NULL;
182 int RegisterThreadedTest::count_ = 0;
183
184
185 static int signature_callback_count;
IncrementingSignatureCallback(const v8::Arguments & args)186 static v8::Handle<Value> IncrementingSignatureCallback(
187 const v8::Arguments& args) {
188 ApiTestFuzzer::Fuzz();
189 signature_callback_count++;
190 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
191 for (int i = 0; i < args.Length(); i++)
192 result->Set(v8::Integer::New(i), args[i]);
193 return result;
194 }
195
196
SignatureCallback(const v8::Arguments & args)197 static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
198 ApiTestFuzzer::Fuzz();
199 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
200 for (int i = 0; i < args.Length(); i++) {
201 result->Set(v8::Integer::New(i), args[i]);
202 }
203 return result;
204 }
205
206
THREADED_TEST(Handles)207 THREADED_TEST(Handles) {
208 v8::HandleScope scope;
209 Local<Context> local_env;
210 {
211 LocalContext env;
212 local_env = env.local();
213 }
214
215 // Local context should still be live.
216 CHECK(!local_env.IsEmpty());
217 local_env->Enter();
218
219 v8::Handle<v8::Primitive> undef = v8::Undefined();
220 CHECK(!undef.IsEmpty());
221 CHECK(undef->IsUndefined());
222
223 const char* c_source = "1 + 2 + 3";
224 Local<String> source = String::New(c_source);
225 Local<Script> script = Script::Compile(source);
226 CHECK_EQ(6, script->Run()->Int32Value());
227
228 local_env->Exit();
229 }
230
231
232 // Helper function that compiles and runs the source.
CompileRun(const char * source)233 static Local<Value> CompileRun(const char* source) {
234 return Script::Compile(String::New(source))->Run();
235 }
236
THREADED_TEST(ReceiverSignature)237 THREADED_TEST(ReceiverSignature) {
238 v8::HandleScope scope;
239 LocalContext env;
240 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
241 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
242 fun->PrototypeTemplate()->Set(
243 v8_str("m"),
244 v8::FunctionTemplate::New(IncrementingSignatureCallback,
245 v8::Handle<Value>(),
246 sig));
247 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
248 signature_callback_count = 0;
249 CompileRun(
250 "var o = new Fun();"
251 "o.m();");
252 CHECK_EQ(1, signature_callback_count);
253 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
254 sub_fun->Inherit(fun);
255 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
256 CompileRun(
257 "var o = new SubFun();"
258 "o.m();");
259 CHECK_EQ(2, signature_callback_count);
260
261 v8::TryCatch try_catch;
262 CompileRun(
263 "var o = { };"
264 "o.m = Fun.prototype.m;"
265 "o.m();");
266 CHECK_EQ(2, signature_callback_count);
267 CHECK(try_catch.HasCaught());
268 try_catch.Reset();
269 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
270 sub_fun->Inherit(fun);
271 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
272 CompileRun(
273 "var o = new UnrelFun();"
274 "o.m = Fun.prototype.m;"
275 "o.m();");
276 CHECK_EQ(2, signature_callback_count);
277 CHECK(try_catch.HasCaught());
278 }
279
280
281
282
THREADED_TEST(ArgumentSignature)283 THREADED_TEST(ArgumentSignature) {
284 v8::HandleScope scope;
285 LocalContext env;
286 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
287 cons->SetClassName(v8_str("Cons"));
288 v8::Handle<v8::Signature> sig =
289 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
290 v8::Handle<v8::FunctionTemplate> fun =
291 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
292 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
293 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
294
295 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
296 CHECK(value1->IsTrue());
297
298 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
299 CHECK(value2->IsTrue());
300
301 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
302 CHECK(value3->IsTrue());
303
304 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
305 cons1->SetClassName(v8_str("Cons1"));
306 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
307 cons2->SetClassName(v8_str("Cons2"));
308 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
309 cons3->SetClassName(v8_str("Cons3"));
310
311 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
312 v8::Handle<v8::Signature> wsig =
313 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
314 v8::Handle<v8::FunctionTemplate> fun2 =
315 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
316
317 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
318 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
319 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
320 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
321 v8::Handle<Value> value4 = CompileRun(
322 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
323 "'[object Cons1],[object Cons2],[object Cons3]'");
324 CHECK(value4->IsTrue());
325
326 v8::Handle<Value> value5 = CompileRun(
327 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
328 CHECK(value5->IsTrue());
329
330 v8::Handle<Value> value6 = CompileRun(
331 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
332 CHECK(value6->IsTrue());
333
334 v8::Handle<Value> value7 = CompileRun(
335 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
336 "'[object Cons1],[object Cons2],[object Cons3],d';");
337 CHECK(value7->IsTrue());
338
339 v8::Handle<Value> value8 = CompileRun(
340 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
341 CHECK(value8->IsTrue());
342 }
343
344
THREADED_TEST(HulIgennem)345 THREADED_TEST(HulIgennem) {
346 v8::HandleScope scope;
347 LocalContext env;
348 v8::Handle<v8::Primitive> undef = v8::Undefined();
349 Local<String> undef_str = undef->ToString();
350 char* value = i::NewArray<char>(undef_str->Length() + 1);
351 undef_str->WriteAscii(value);
352 CHECK_EQ(0, strcmp(value, "undefined"));
353 i::DeleteArray(value);
354 }
355
356
THREADED_TEST(Access)357 THREADED_TEST(Access) {
358 v8::HandleScope scope;
359 LocalContext env;
360 Local<v8::Object> obj = v8::Object::New();
361 Local<Value> foo_before = obj->Get(v8_str("foo"));
362 CHECK(foo_before->IsUndefined());
363 Local<String> bar_str = v8_str("bar");
364 obj->Set(v8_str("foo"), bar_str);
365 Local<Value> foo_after = obj->Get(v8_str("foo"));
366 CHECK(!foo_after->IsUndefined());
367 CHECK(foo_after->IsString());
368 CHECK_EQ(bar_str, foo_after);
369 }
370
371
THREADED_TEST(Script)372 THREADED_TEST(Script) {
373 v8::HandleScope scope;
374 LocalContext env;
375 const char* c_source = "1 + 2 + 3";
376 Local<String> source = String::New(c_source);
377 Local<Script> script = Script::Compile(source);
378 CHECK_EQ(6, script->Run()->Int32Value());
379 }
380
381
AsciiToTwoByteString(const char * source)382 static uint16_t* AsciiToTwoByteString(const char* source) {
383 size_t array_length = strlen(source) + 1;
384 uint16_t* converted = i::NewArray<uint16_t>(array_length);
385 for (size_t i = 0; i < array_length; i++) converted[i] = source[i];
386 return converted;
387 }
388
389
390 class TestResource: public String::ExternalStringResource {
391 public:
392 static int dispose_count;
393
TestResource(uint16_t * data)394 explicit TestResource(uint16_t* data)
395 : data_(data), length_(0) {
396 while (data[length_]) ++length_;
397 }
398
~TestResource()399 ~TestResource() {
400 i::DeleteArray(data_);
401 ++dispose_count;
402 }
403
data() const404 const uint16_t* data() const {
405 return data_;
406 }
407
length() const408 size_t length() const {
409 return length_;
410 }
411 private:
412 uint16_t* data_;
413 size_t length_;
414 };
415
416
417 int TestResource::dispose_count = 0;
418
419
420 class TestAsciiResource: public String::ExternalAsciiStringResource {
421 public:
422 static int dispose_count;
423
TestAsciiResource(const char * data)424 explicit TestAsciiResource(const char* data)
425 : data_(data),
426 length_(strlen(data)) { }
427
~TestAsciiResource()428 ~TestAsciiResource() {
429 i::DeleteArray(data_);
430 ++dispose_count;
431 }
432
data() const433 const char* data() const {
434 return data_;
435 }
436
length() const437 size_t length() const {
438 return length_;
439 }
440 private:
441 const char* data_;
442 size_t length_;
443 };
444
445
446 int TestAsciiResource::dispose_count = 0;
447
448
THREADED_TEST(ScriptUsingStringResource)449 THREADED_TEST(ScriptUsingStringResource) {
450 TestResource::dispose_count = 0;
451 const char* c_source = "1 + 2 * 3";
452 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
453 {
454 v8::HandleScope scope;
455 LocalContext env;
456 TestResource* resource = new TestResource(two_byte_source);
457 Local<String> source = String::NewExternal(resource);
458 Local<Script> script = Script::Compile(source);
459 Local<Value> value = script->Run();
460 CHECK(value->IsNumber());
461 CHECK_EQ(7, value->Int32Value());
462 CHECK(source->IsExternal());
463 CHECK_EQ(resource,
464 static_cast<TestResource*>(source->GetExternalStringResource()));
465 v8::internal::Heap::CollectAllGarbage(false);
466 CHECK_EQ(0, TestResource::dispose_count);
467 }
468 v8::internal::CompilationCache::Clear();
469 v8::internal::Heap::CollectAllGarbage(false);
470 CHECK_EQ(1, TestResource::dispose_count);
471 }
472
473
THREADED_TEST(ScriptUsingAsciiStringResource)474 THREADED_TEST(ScriptUsingAsciiStringResource) {
475 TestAsciiResource::dispose_count = 0;
476 const char* c_source = "1 + 2 * 3";
477 {
478 v8::HandleScope scope;
479 LocalContext env;
480 Local<String> source =
481 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
482 Local<Script> script = Script::Compile(source);
483 Local<Value> value = script->Run();
484 CHECK(value->IsNumber());
485 CHECK_EQ(7, value->Int32Value());
486 v8::internal::Heap::CollectAllGarbage(false);
487 CHECK_EQ(0, TestAsciiResource::dispose_count);
488 }
489 v8::internal::CompilationCache::Clear();
490 v8::internal::Heap::CollectAllGarbage(false);
491 CHECK_EQ(1, TestAsciiResource::dispose_count);
492 }
493
494
THREADED_TEST(ScriptMakingExternalString)495 THREADED_TEST(ScriptMakingExternalString) {
496 TestResource::dispose_count = 0;
497 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
498 {
499 v8::HandleScope scope;
500 LocalContext env;
501 Local<String> source = String::New(two_byte_source);
502 bool success = source->MakeExternal(new TestResource(two_byte_source));
503 CHECK(success);
504 Local<Script> script = Script::Compile(source);
505 Local<Value> value = script->Run();
506 CHECK(value->IsNumber());
507 CHECK_EQ(7, value->Int32Value());
508 v8::internal::Heap::CollectAllGarbage(false);
509 CHECK_EQ(0, TestResource::dispose_count);
510 }
511 v8::internal::CompilationCache::Clear();
512 v8::internal::Heap::CollectAllGarbage(false);
513 CHECK_EQ(1, TestResource::dispose_count);
514 }
515
516
THREADED_TEST(ScriptMakingExternalAsciiString)517 THREADED_TEST(ScriptMakingExternalAsciiString) {
518 TestAsciiResource::dispose_count = 0;
519 const char* c_source = "1 + 2 * 3";
520 {
521 v8::HandleScope scope;
522 LocalContext env;
523 Local<String> source = v8_str(c_source);
524 bool success = source->MakeExternal(
525 new TestAsciiResource(i::StrDup(c_source)));
526 CHECK(success);
527 Local<Script> script = Script::Compile(source);
528 Local<Value> value = script->Run();
529 CHECK(value->IsNumber());
530 CHECK_EQ(7, value->Int32Value());
531 v8::internal::Heap::CollectAllGarbage(false);
532 CHECK_EQ(0, TestAsciiResource::dispose_count);
533 }
534 v8::internal::CompilationCache::Clear();
535 v8::internal::Heap::CollectAllGarbage(false);
536 CHECK_EQ(1, TestAsciiResource::dispose_count);
537 }
538
539
THREADED_TEST(UsingExternalString)540 THREADED_TEST(UsingExternalString) {
541 {
542 v8::HandleScope scope;
543 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
544 Local<String> string =
545 String::NewExternal(new TestResource(two_byte_string));
546 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
547 // Trigger GCs so that the newly allocated string moves to old gen.
548 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
549 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
550 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
551 CHECK(isymbol->IsSymbol());
552 }
553 i::Heap::CollectAllGarbage(false);
554 i::Heap::CollectAllGarbage(false);
555 }
556
557
THREADED_TEST(UsingExternalAsciiString)558 THREADED_TEST(UsingExternalAsciiString) {
559 {
560 v8::HandleScope scope;
561 const char* one_byte_string = "test string";
562 Local<String> string = String::NewExternal(
563 new TestAsciiResource(i::StrDup(one_byte_string)));
564 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
565 // Trigger GCs so that the newly allocated string moves to old gen.
566 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
567 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
568 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
569 CHECK(isymbol->IsSymbol());
570 }
571 i::Heap::CollectAllGarbage(false);
572 i::Heap::CollectAllGarbage(false);
573 }
574
575
THREADED_TEST(GlobalProperties)576 THREADED_TEST(GlobalProperties) {
577 v8::HandleScope scope;
578 LocalContext env;
579 v8::Handle<v8::Object> global = env->Global();
580 global->Set(v8_str("pi"), v8_num(3.1415926));
581 Local<Value> pi = global->Get(v8_str("pi"));
582 CHECK_EQ(3.1415926, pi->NumberValue());
583 }
584
585
handle_call(const v8::Arguments & args)586 static v8::Handle<Value> handle_call(const v8::Arguments& args) {
587 ApiTestFuzzer::Fuzz();
588 return v8_num(102);
589 }
590
591
construct_call(const v8::Arguments & args)592 static v8::Handle<Value> construct_call(const v8::Arguments& args) {
593 ApiTestFuzzer::Fuzz();
594 args.This()->Set(v8_str("x"), v8_num(1));
595 args.This()->Set(v8_str("y"), v8_num(2));
596 return args.This();
597 }
598
THREADED_TEST(FunctionTemplate)599 THREADED_TEST(FunctionTemplate) {
600 v8::HandleScope scope;
601 LocalContext env;
602 {
603 Local<v8::FunctionTemplate> fun_templ =
604 v8::FunctionTemplate::New(handle_call);
605 Local<Function> fun = fun_templ->GetFunction();
606 env->Global()->Set(v8_str("obj"), fun);
607 Local<Script> script = v8_compile("obj()");
608 CHECK_EQ(102, script->Run()->Int32Value());
609 }
610 // Use SetCallHandler to initialize a function template, should work like the
611 // previous one.
612 {
613 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
614 fun_templ->SetCallHandler(handle_call);
615 Local<Function> fun = fun_templ->GetFunction();
616 env->Global()->Set(v8_str("obj"), fun);
617 Local<Script> script = v8_compile("obj()");
618 CHECK_EQ(102, script->Run()->Int32Value());
619 }
620 // Test constructor calls.
621 {
622 Local<v8::FunctionTemplate> fun_templ =
623 v8::FunctionTemplate::New(construct_call);
624 fun_templ->SetClassName(v8_str("funky"));
625 Local<Function> fun = fun_templ->GetFunction();
626 env->Global()->Set(v8_str("obj"), fun);
627 Local<Script> script = v8_compile("var s = new obj(); s.x");
628 CHECK_EQ(1, script->Run()->Int32Value());
629
630 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
631 CHECK_EQ(v8_str("[object funky]"), result);
632 }
633 }
634
635
THREADED_TEST(FindInstanceInPrototypeChain)636 THREADED_TEST(FindInstanceInPrototypeChain) {
637 v8::HandleScope scope;
638 LocalContext env;
639
640 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
641 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
642 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
643 derived->Inherit(base);
644
645 Local<v8::Function> base_function = base->GetFunction();
646 Local<v8::Function> derived_function = derived->GetFunction();
647 Local<v8::Function> other_function = other->GetFunction();
648
649 Local<v8::Object> base_instance = base_function->NewInstance();
650 Local<v8::Object> derived_instance = derived_function->NewInstance();
651 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
652 Local<v8::Object> other_instance = other_function->NewInstance();
653 derived_instance2->Set(v8_str("__proto__"), derived_instance);
654 other_instance->Set(v8_str("__proto__"), derived_instance2);
655
656 // base_instance is only an instance of base.
657 CHECK_EQ(base_instance,
658 base_instance->FindInstanceInPrototypeChain(base));
659 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
660 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
661
662 // derived_instance is an instance of base and derived.
663 CHECK_EQ(derived_instance,
664 derived_instance->FindInstanceInPrototypeChain(base));
665 CHECK_EQ(derived_instance,
666 derived_instance->FindInstanceInPrototypeChain(derived));
667 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
668
669 // other_instance is an instance of other and its immediate
670 // prototype derived_instance2 is an instance of base and derived.
671 // Note, derived_instance is an instance of base and derived too,
672 // but it comes after derived_instance2 in the prototype chain of
673 // other_instance.
674 CHECK_EQ(derived_instance2,
675 other_instance->FindInstanceInPrototypeChain(base));
676 CHECK_EQ(derived_instance2,
677 other_instance->FindInstanceInPrototypeChain(derived));
678 CHECK_EQ(other_instance,
679 other_instance->FindInstanceInPrototypeChain(other));
680 }
681
682
handle_property(Local<String> name,const AccessorInfo &)683 static v8::Handle<Value> handle_property(Local<String> name,
684 const AccessorInfo&) {
685 ApiTestFuzzer::Fuzz();
686 return v8_num(900);
687 }
688
689
THREADED_TEST(PropertyHandler)690 THREADED_TEST(PropertyHandler) {
691 v8::HandleScope scope;
692 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
693 fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property);
694 LocalContext env;
695 Local<Function> fun = fun_templ->GetFunction();
696 env->Global()->Set(v8_str("Fun"), fun);
697 Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;");
698 CHECK_EQ(900, getter->Run()->Int32Value());
699 Local<Script> setter = v8_compile("obj.foo = 901;");
700 CHECK_EQ(901, setter->Run()->Int32Value());
701 }
702
703
THREADED_TEST(Number)704 THREADED_TEST(Number) {
705 v8::HandleScope scope;
706 LocalContext env;
707 double PI = 3.1415926;
708 Local<v8::Number> pi_obj = v8::Number::New(PI);
709 CHECK_EQ(PI, pi_obj->NumberValue());
710 }
711
712
THREADED_TEST(ToNumber)713 THREADED_TEST(ToNumber) {
714 v8::HandleScope scope;
715 LocalContext env;
716 Local<String> str = v8_str("3.1415926");
717 CHECK_EQ(3.1415926, str->NumberValue());
718 v8::Handle<v8::Boolean> t = v8::True();
719 CHECK_EQ(1.0, t->NumberValue());
720 v8::Handle<v8::Boolean> f = v8::False();
721 CHECK_EQ(0.0, f->NumberValue());
722 }
723
724
THREADED_TEST(Date)725 THREADED_TEST(Date) {
726 v8::HandleScope scope;
727 LocalContext env;
728 double PI = 3.1415926;
729 Local<Value> date_obj = v8::Date::New(PI);
730 CHECK_EQ(3.0, date_obj->NumberValue());
731 }
732
733
THREADED_TEST(Boolean)734 THREADED_TEST(Boolean) {
735 v8::HandleScope scope;
736 LocalContext env;
737 v8::Handle<v8::Boolean> t = v8::True();
738 CHECK(t->Value());
739 v8::Handle<v8::Boolean> f = v8::False();
740 CHECK(!f->Value());
741 v8::Handle<v8::Primitive> u = v8::Undefined();
742 CHECK(!u->BooleanValue());
743 v8::Handle<v8::Primitive> n = v8::Null();
744 CHECK(!n->BooleanValue());
745 v8::Handle<String> str1 = v8_str("");
746 CHECK(!str1->BooleanValue());
747 v8::Handle<String> str2 = v8_str("x");
748 CHECK(str2->BooleanValue());
749 CHECK(!v8::Number::New(0)->BooleanValue());
750 CHECK(v8::Number::New(-1)->BooleanValue());
751 CHECK(v8::Number::New(1)->BooleanValue());
752 CHECK(v8::Number::New(42)->BooleanValue());
753 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
754 }
755
756
DummyCallHandler(const v8::Arguments & args)757 static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
758 ApiTestFuzzer::Fuzz();
759 return v8_num(13.4);
760 }
761
762
GetM(Local<String> name,const AccessorInfo &)763 static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
764 ApiTestFuzzer::Fuzz();
765 return v8_num(876);
766 }
767
768
THREADED_TEST(GlobalPrototype)769 THREADED_TEST(GlobalPrototype) {
770 v8::HandleScope scope;
771 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
772 func_templ->PrototypeTemplate()->Set(
773 "dummy",
774 v8::FunctionTemplate::New(DummyCallHandler));
775 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
776 templ->Set("x", v8_num(200));
777 templ->SetAccessor(v8_str("m"), GetM);
778 LocalContext env(0, templ);
779 v8::Handle<v8::Object> obj = env->Global();
780 v8::Handle<Script> script = v8_compile("dummy()");
781 v8::Handle<Value> result = script->Run();
782 CHECK_EQ(13.4, result->NumberValue());
783 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
784 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
785 }
786
787
GetIntValue(Local<String> property,const AccessorInfo & info)788 static v8::Handle<Value> GetIntValue(Local<String> property,
789 const AccessorInfo& info) {
790 ApiTestFuzzer::Fuzz();
791 int* value =
792 static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
793 return v8_num(*value);
794 }
795
SetIntValue(Local<String> property,Local<Value> value,const AccessorInfo & info)796 static void SetIntValue(Local<String> property,
797 Local<Value> value,
798 const AccessorInfo& info) {
799 int* field =
800 static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
801 *field = value->Int32Value();
802 }
803
804 int foo, bar, baz;
805
THREADED_TEST(GlobalVariableAccess)806 THREADED_TEST(GlobalVariableAccess) {
807 foo = 0;
808 bar = -4;
809 baz = 10;
810 v8::HandleScope scope;
811 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
812 templ->InstanceTemplate()->SetAccessor(v8_str("foo"),
813 GetIntValue,
814 SetIntValue,
815 v8::External::New(&foo));
816 templ->InstanceTemplate()->SetAccessor(v8_str("bar"),
817 GetIntValue,
818 SetIntValue,
819 v8::External::New(&bar));
820 templ->InstanceTemplate()->SetAccessor(v8_str("baz"),
821 GetIntValue,
822 SetIntValue,
823 v8::External::New(&baz));
824 LocalContext env(0, templ->InstanceTemplate());
825 v8_compile("foo = (++bar) + baz")->Run();
826 CHECK_EQ(bar, -3);
827 CHECK_EQ(foo, 7);
828 }
829
830
THREADED_TEST(ObjectTemplate)831 THREADED_TEST(ObjectTemplate) {
832 v8::HandleScope scope;
833 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
834 templ1->Set("x", v8_num(10));
835 templ1->Set("y", v8_num(13));
836 LocalContext env;
837 Local<v8::Object> instance1 = templ1->NewInstance();
838 env->Global()->Set(v8_str("p"), instance1);
839 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
840 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
841 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
842 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
843 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
844 templ2->Set("a", v8_num(12));
845 templ2->Set("b", templ1);
846 Local<v8::Object> instance2 = templ2->NewInstance();
847 env->Global()->Set(v8_str("q"), instance2);
848 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
849 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
850 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
851 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
852 }
853
854
GetFlabby(const v8::Arguments & args)855 static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
856 ApiTestFuzzer::Fuzz();
857 return v8_num(17.2);
858 }
859
860
GetKnurd(Local<String> property,const AccessorInfo &)861 static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
862 ApiTestFuzzer::Fuzz();
863 return v8_num(15.2);
864 }
865
866
THREADED_TEST(DescriptorInheritance)867 THREADED_TEST(DescriptorInheritance) {
868 v8::HandleScope scope;
869 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
870 super->PrototypeTemplate()->Set("flabby",
871 v8::FunctionTemplate::New(GetFlabby));
872 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
873
874 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
875
876 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
877 base1->Inherit(super);
878 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
879
880 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
881 base2->Inherit(super);
882 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
883
884 LocalContext env;
885
886 env->Global()->Set(v8_str("s"), super->GetFunction());
887 env->Global()->Set(v8_str("base1"), base1->GetFunction());
888 env->Global()->Set(v8_str("base2"), base2->GetFunction());
889
890 // Checks right __proto__ chain.
891 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
892 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
893
894 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
895
896 // Instance accessor should not be visible on function object or its prototype
897 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
898 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
899 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
900
901 env->Global()->Set(v8_str("obj"),
902 base1->GetFunction()->NewInstance());
903 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
904 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
905 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
906 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
907 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
908
909 env->Global()->Set(v8_str("obj2"),
910 base2->GetFunction()->NewInstance());
911 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
912 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
913 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
914 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
915 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
916
917 // base1 and base2 cannot cross reference to each's prototype
918 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
919 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
920 }
921
922
923 int echo_named_call_count;
924
925
EchoNamedProperty(Local<String> name,const AccessorInfo & info)926 static v8::Handle<Value> EchoNamedProperty(Local<String> name,
927 const AccessorInfo& info) {
928 ApiTestFuzzer::Fuzz();
929 CHECK_EQ(v8_str("data"), info.Data());
930 echo_named_call_count++;
931 return name;
932 }
933
934
THREADED_TEST(NamedPropertyHandlerGetter)935 THREADED_TEST(NamedPropertyHandlerGetter) {
936 echo_named_call_count = 0;
937 v8::HandleScope scope;
938 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
939 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
940 0, 0, 0, 0,
941 v8_str("data"));
942 LocalContext env;
943 env->Global()->Set(v8_str("obj"),
944 templ->GetFunction()->NewInstance());
945 CHECK_EQ(echo_named_call_count, 0);
946 v8_compile("obj.x")->Run();
947 CHECK_EQ(echo_named_call_count, 1);
948 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
949 v8::Handle<Value> str = CompileRun(code);
950 String::AsciiValue value(str);
951 CHECK_EQ(*value, "oddlepoddle");
952 // Check default behavior
953 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
954 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
955 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
956 }
957
958
959 int echo_indexed_call_count = 0;
960
961
EchoIndexedProperty(uint32_t index,const AccessorInfo & info)962 static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
963 const AccessorInfo& info) {
964 ApiTestFuzzer::Fuzz();
965 CHECK_EQ(v8_num(637), info.Data());
966 echo_indexed_call_count++;
967 return v8_num(index);
968 }
969
970
THREADED_TEST(IndexedPropertyHandlerGetter)971 THREADED_TEST(IndexedPropertyHandlerGetter) {
972 v8::HandleScope scope;
973 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
974 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
975 0, 0, 0, 0,
976 v8_num(637));
977 LocalContext env;
978 env->Global()->Set(v8_str("obj"),
979 templ->GetFunction()->NewInstance());
980 Local<Script> script = v8_compile("obj[900]");
981 CHECK_EQ(script->Run()->Int32Value(), 900);
982 }
983
984
985 v8::Handle<v8::Object> bottom;
986
CheckThisIndexedPropertyHandler(uint32_t index,const AccessorInfo & info)987 static v8::Handle<Value> CheckThisIndexedPropertyHandler(
988 uint32_t index,
989 const AccessorInfo& info) {
990 ApiTestFuzzer::Fuzz();
991 CHECK(info.This()->Equals(bottom));
992 return v8::Handle<Value>();
993 }
994
CheckThisNamedPropertyHandler(Local<String> name,const AccessorInfo & info)995 static v8::Handle<Value> CheckThisNamedPropertyHandler(
996 Local<String> name,
997 const AccessorInfo& info) {
998 ApiTestFuzzer::Fuzz();
999 CHECK(info.This()->Equals(bottom));
1000 return v8::Handle<Value>();
1001 }
1002
1003
CheckThisIndexedPropertySetter(uint32_t index,Local<Value> value,const AccessorInfo & info)1004 v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1005 Local<Value> value,
1006 const AccessorInfo& info) {
1007 ApiTestFuzzer::Fuzz();
1008 CHECK(info.This()->Equals(bottom));
1009 return v8::Handle<Value>();
1010 }
1011
1012
CheckThisNamedPropertySetter(Local<String> property,Local<Value> value,const AccessorInfo & info)1013 v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1014 Local<Value> value,
1015 const AccessorInfo& info) {
1016 ApiTestFuzzer::Fuzz();
1017 CHECK(info.This()->Equals(bottom));
1018 return v8::Handle<Value>();
1019 }
1020
CheckThisIndexedPropertyQuery(uint32_t index,const AccessorInfo & info)1021 v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery(
1022 uint32_t index,
1023 const AccessorInfo& info) {
1024 ApiTestFuzzer::Fuzz();
1025 CHECK(info.This()->Equals(bottom));
1026 return v8::Handle<v8::Boolean>();
1027 }
1028
1029
CheckThisNamedPropertyQuery(Local<String> property,const AccessorInfo & info)1030 v8::Handle<v8::Boolean> CheckThisNamedPropertyQuery(Local<String> property,
1031 const AccessorInfo& info) {
1032 ApiTestFuzzer::Fuzz();
1033 CHECK(info.This()->Equals(bottom));
1034 return v8::Handle<v8::Boolean>();
1035 }
1036
1037
CheckThisIndexedPropertyDeleter(uint32_t index,const AccessorInfo & info)1038 v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1039 uint32_t index,
1040 const AccessorInfo& info) {
1041 ApiTestFuzzer::Fuzz();
1042 CHECK(info.This()->Equals(bottom));
1043 return v8::Handle<v8::Boolean>();
1044 }
1045
1046
CheckThisNamedPropertyDeleter(Local<String> property,const AccessorInfo & info)1047 v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1048 Local<String> property,
1049 const AccessorInfo& info) {
1050 ApiTestFuzzer::Fuzz();
1051 CHECK(info.This()->Equals(bottom));
1052 return v8::Handle<v8::Boolean>();
1053 }
1054
1055
CheckThisIndexedPropertyEnumerator(const AccessorInfo & info)1056 v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1057 const AccessorInfo& info) {
1058 ApiTestFuzzer::Fuzz();
1059 CHECK(info.This()->Equals(bottom));
1060 return v8::Handle<v8::Array>();
1061 }
1062
1063
CheckThisNamedPropertyEnumerator(const AccessorInfo & info)1064 v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1065 const AccessorInfo& info) {
1066 ApiTestFuzzer::Fuzz();
1067 CHECK(info.This()->Equals(bottom));
1068 return v8::Handle<v8::Array>();
1069 }
1070
1071
THREADED_TEST(PropertyHandlerInPrototype)1072 THREADED_TEST(PropertyHandlerInPrototype) {
1073 v8::HandleScope scope;
1074 LocalContext env;
1075
1076 // Set up a prototype chain with three interceptors.
1077 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1078 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1079 CheckThisIndexedPropertyHandler,
1080 CheckThisIndexedPropertySetter,
1081 CheckThisIndexedPropertyQuery,
1082 CheckThisIndexedPropertyDeleter,
1083 CheckThisIndexedPropertyEnumerator);
1084
1085 templ->InstanceTemplate()->SetNamedPropertyHandler(
1086 CheckThisNamedPropertyHandler,
1087 CheckThisNamedPropertySetter,
1088 CheckThisNamedPropertyQuery,
1089 CheckThisNamedPropertyDeleter,
1090 CheckThisNamedPropertyEnumerator);
1091
1092 bottom = templ->GetFunction()->NewInstance();
1093 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1094 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1095
1096 bottom->Set(v8_str("__proto__"), middle);
1097 middle->Set(v8_str("__proto__"), top);
1098 env->Global()->Set(v8_str("obj"), bottom);
1099
1100 // Indexed and named get.
1101 Script::Compile(v8_str("obj[0]"))->Run();
1102 Script::Compile(v8_str("obj.x"))->Run();
1103
1104 // Indexed and named set.
1105 Script::Compile(v8_str("obj[1] = 42"))->Run();
1106 Script::Compile(v8_str("obj.y = 42"))->Run();
1107
1108 // Indexed and named query.
1109 Script::Compile(v8_str("0 in obj"))->Run();
1110 Script::Compile(v8_str("'x' in obj"))->Run();
1111
1112 // Indexed and named deleter.
1113 Script::Compile(v8_str("delete obj[0]"))->Run();
1114 Script::Compile(v8_str("delete obj.x"))->Run();
1115
1116 // Enumerators.
1117 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1118 }
1119
1120
PrePropertyHandlerGet(Local<String> key,const AccessorInfo & info)1121 static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1122 const AccessorInfo& info) {
1123 ApiTestFuzzer::Fuzz();
1124 if (v8_str("pre")->Equals(key)) {
1125 return v8_str("PrePropertyHandler: pre");
1126 }
1127 return v8::Handle<String>();
1128 }
1129
1130
PrePropertyHandlerHas(Local<String> key,const AccessorInfo &)1131 static v8::Handle<v8::Boolean> PrePropertyHandlerHas(Local<String> key,
1132 const AccessorInfo&) {
1133 if (v8_str("pre")->Equals(key)) {
1134 return v8::True();
1135 }
1136
1137 return v8::Handle<v8::Boolean>(); // do not intercept the call
1138 }
1139
1140
THREADED_TEST(PrePropertyHandler)1141 THREADED_TEST(PrePropertyHandler) {
1142 v8::HandleScope scope;
1143 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1144 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1145 0,
1146 PrePropertyHandlerHas);
1147 LocalContext env(NULL, desc->InstanceTemplate());
1148 Script::Compile(v8_str(
1149 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1150 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1151 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1152 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1153 CHECK_EQ(v8_str("Object: on"), result_on);
1154 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1155 CHECK(result_post.IsEmpty());
1156 }
1157
1158
THREADED_TEST(UndefinedIsNotEnumerable)1159 THREADED_TEST(UndefinedIsNotEnumerable) {
1160 v8::HandleScope scope;
1161 LocalContext env;
1162 v8::Handle<Value> result = Script::Compile(v8_str(
1163 "this.propertyIsEnumerable(undefined)"))->Run();
1164 CHECK(result->IsFalse());
1165 }
1166
1167
1168 v8::Handle<Script> call_recursively_script;
1169 static const int kTargetRecursionDepth = 300; // near maximum
1170
1171
CallScriptRecursivelyCall(const v8::Arguments & args)1172 static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1173 ApiTestFuzzer::Fuzz();
1174 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1175 if (depth == kTargetRecursionDepth) return v8::Undefined();
1176 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1177 return call_recursively_script->Run();
1178 }
1179
1180
CallFunctionRecursivelyCall(const v8::Arguments & args)1181 static v8::Handle<Value> CallFunctionRecursivelyCall(
1182 const v8::Arguments& args) {
1183 ApiTestFuzzer::Fuzz();
1184 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1185 if (depth == kTargetRecursionDepth) {
1186 printf("[depth = %d]\n", depth);
1187 return v8::Undefined();
1188 }
1189 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1190 v8::Handle<Value> function =
1191 args.This()->Get(v8_str("callFunctionRecursively"));
1192 return v8::Handle<Function>::Cast(function)->Call(args.This(), 0, NULL);
1193 }
1194
1195
THREADED_TEST(DeepCrossLanguageRecursion)1196 THREADED_TEST(DeepCrossLanguageRecursion) {
1197 v8::HandleScope scope;
1198 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1199 global->Set(v8_str("callScriptRecursively"),
1200 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1201 global->Set(v8_str("callFunctionRecursively"),
1202 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1203 LocalContext env(NULL, global);
1204
1205 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1206 call_recursively_script = v8_compile("callScriptRecursively()");
1207 v8::Handle<Value> result = call_recursively_script->Run();
1208 call_recursively_script = v8::Handle<Script>();
1209
1210 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1211 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1212 }
1213
1214
1215 static v8::Handle<Value>
ThrowingPropertyHandlerGet(Local<String> key,const AccessorInfo &)1216 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1217 ApiTestFuzzer::Fuzz();
1218 return v8::ThrowException(key);
1219 }
1220
1221
ThrowingPropertyHandlerSet(Local<String> key,Local<Value>,const AccessorInfo &)1222 static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1223 Local<Value>,
1224 const AccessorInfo&) {
1225 v8::ThrowException(key);
1226 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1227 }
1228
1229
THREADED_TEST(CallbackExceptionRegression)1230 THREADED_TEST(CallbackExceptionRegression) {
1231 v8::HandleScope scope;
1232 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1233 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1234 ThrowingPropertyHandlerSet);
1235 LocalContext env;
1236 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1237 v8::Handle<Value> otto = Script::Compile(v8_str(
1238 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1239 CHECK_EQ(v8_str("otto"), otto);
1240 v8::Handle<Value> netto = Script::Compile(v8_str(
1241 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1242 CHECK_EQ(v8_str("netto"), netto);
1243 }
1244
1245
ThrowingGetAccessor(Local<String> name,const AccessorInfo & info)1246 static v8::Handle<Value> ThrowingGetAccessor(Local<String> name,
1247 const AccessorInfo& info) {
1248 ApiTestFuzzer::Fuzz();
1249 return v8::ThrowException(v8_str("g"));
1250 }
1251
1252
ThrowingSetAccessor(Local<String> name,Local<Value> value,const AccessorInfo & info)1253 static void ThrowingSetAccessor(Local<String> name,
1254 Local<Value> value,
1255 const AccessorInfo& info) {
1256 v8::ThrowException(value);
1257 }
1258
1259
THREADED_TEST(Regress1054726)1260 THREADED_TEST(Regress1054726) {
1261 v8::HandleScope scope;
1262 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1263 obj->SetAccessor(v8_str("x"),
1264 ThrowingGetAccessor,
1265 ThrowingSetAccessor,
1266 Local<Value>());
1267
1268 LocalContext env;
1269 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1270
1271 // Use the throwing property setter/getter in a loop to force
1272 // the accessor ICs to be initialized.
1273 v8::Handle<Value> result;
1274 result = Script::Compile(v8_str(
1275 "var result = '';"
1276 "for (var i = 0; i < 5; i++) {"
1277 " try { obj.x; } catch (e) { result += e; }"
1278 "}; result"))->Run();
1279 CHECK_EQ(v8_str("ggggg"), result);
1280
1281 result = Script::Compile(String::New(
1282 "var result = '';"
1283 "for (var i = 0; i < 5; i++) {"
1284 " try { obj.x = i; } catch (e) { result += e; }"
1285 "}; result"))->Run();
1286 CHECK_EQ(v8_str("01234"), result);
1287 }
1288
1289
THREADED_TEST(FunctionPrototype)1290 THREADED_TEST(FunctionPrototype) {
1291 v8::HandleScope scope;
1292 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1293 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1294 LocalContext env;
1295 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1296 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1297 CHECK_EQ(script->Run()->Int32Value(), 321);
1298 }
1299
1300
THREADED_TEST(InternalFields)1301 THREADED_TEST(InternalFields) {
1302 v8::HandleScope scope;
1303 LocalContext env;
1304
1305 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1306 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1307 instance_templ->SetInternalFieldCount(1);
1308 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1309 CHECK_EQ(1, obj->InternalFieldCount());
1310 CHECK(obj->GetInternalField(0)->IsUndefined());
1311 obj->SetInternalField(0, v8_num(17));
1312 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1313 }
1314
1315
THREADED_TEST(InternalFieldsNativePointers)1316 THREADED_TEST(InternalFieldsNativePointers) {
1317 v8::HandleScope scope;
1318 LocalContext env;
1319
1320 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1321 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1322 instance_templ->SetInternalFieldCount(1);
1323 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1324 CHECK_EQ(1, obj->InternalFieldCount());
1325 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1326
1327 char* data = new char[100];
1328
1329 void* aligned = data;
1330 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1331 void* unaligned = data + 1;
1332 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1333
1334 // Check reading and writing aligned pointers.
1335 obj->SetPointerInInternalField(0, aligned);
1336 i::Heap::CollectAllGarbage(false);
1337 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1338
1339 // Check reading and writing unaligned pointers.
1340 obj->SetPointerInInternalField(0, unaligned);
1341 i::Heap::CollectAllGarbage(false);
1342 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1343
1344 delete[] data;
1345 }
1346
1347
THREADED_TEST(IdentityHash)1348 THREADED_TEST(IdentityHash) {
1349 v8::HandleScope scope;
1350 LocalContext env;
1351
1352 // Ensure that the test starts with an fresh heap to test whether the hash
1353 // code is based on the address.
1354 i::Heap::CollectAllGarbage(false);
1355 Local<v8::Object> obj = v8::Object::New();
1356 int hash = obj->GetIdentityHash();
1357 int hash1 = obj->GetIdentityHash();
1358 CHECK_EQ(hash, hash1);
1359 int hash2 = v8::Object::New()->GetIdentityHash();
1360 // Since the identity hash is essentially a random number two consecutive
1361 // objects should not be assigned the same hash code. If the test below fails
1362 // the random number generator should be evaluated.
1363 CHECK_NE(hash, hash2);
1364 i::Heap::CollectAllGarbage(false);
1365 int hash3 = v8::Object::New()->GetIdentityHash();
1366 // Make sure that the identity hash is not based on the initial address of
1367 // the object alone. If the test below fails the random number generator
1368 // should be evaluated.
1369 CHECK_NE(hash, hash3);
1370 int hash4 = obj->GetIdentityHash();
1371 CHECK_EQ(hash, hash4);
1372 }
1373
1374
THREADED_TEST(HiddenProperties)1375 THREADED_TEST(HiddenProperties) {
1376 v8::HandleScope scope;
1377 LocalContext env;
1378
1379 v8::Local<v8::Object> obj = v8::Object::New();
1380 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1381 v8::Local<v8::String> empty = v8_str("");
1382 v8::Local<v8::String> prop_name = v8_str("prop_name");
1383
1384 i::Heap::CollectAllGarbage(false);
1385
1386 // Make sure delete of a non-existent hidden value works
1387 CHECK(obj->DeleteHiddenValue(key));
1388
1389 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1390 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1391 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1392 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1393
1394 i::Heap::CollectAllGarbage(false);
1395
1396 // Make sure we do not find the hidden property.
1397 CHECK(!obj->Has(empty));
1398 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1399 CHECK(obj->Get(empty)->IsUndefined());
1400 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1401 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1402 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1403 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1404
1405 i::Heap::CollectAllGarbage(false);
1406
1407 // Add another property and delete it afterwards to force the object in
1408 // slow case.
1409 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1410 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1411 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1412 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1413 CHECK(obj->Delete(prop_name));
1414 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1415
1416 i::Heap::CollectAllGarbage(false);
1417
1418 CHECK(obj->DeleteHiddenValue(key));
1419 CHECK(obj->GetHiddenValue(key).IsEmpty());
1420 }
1421
1422
InterceptorForHiddenProperties(Local<String> name,const AccessorInfo & info)1423 static v8::Handle<Value> InterceptorForHiddenProperties(
1424 Local<String> name, const AccessorInfo& info) {
1425 // Make sure objects move.
1426 bool saved_always_compact = i::FLAG_always_compact;
1427 if (!i::FLAG_never_compact) {
1428 i::FLAG_always_compact = true;
1429 }
1430 // The whole goal of this interceptor is to cause a GC during local property
1431 // lookup.
1432 i::Heap::CollectAllGarbage(false);
1433 i::FLAG_always_compact = saved_always_compact;
1434 return v8::Handle<Value>();
1435 }
1436
1437
THREADED_TEST(HiddenPropertiesWithInterceptors)1438 THREADED_TEST(HiddenPropertiesWithInterceptors) {
1439 v8::HandleScope scope;
1440 LocalContext context;
1441
1442 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1443
1444 // Associate an interceptor with an object and start setting hidden values.
1445 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1446 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1447 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1448 Local<v8::Function> function = fun_templ->GetFunction();
1449 Local<v8::Object> obj = function->NewInstance();
1450 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1451 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
1452 }
1453
1454
THREADED_TEST(External)1455 THREADED_TEST(External) {
1456 v8::HandleScope scope;
1457 int x = 3;
1458 Local<v8::External> ext = v8::External::New(&x);
1459 LocalContext env;
1460 env->Global()->Set(v8_str("ext"), ext);
1461 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
1462 v8::Handle<v8::External> reext = v8::Handle<v8::External>::Cast(reext_obj);
1463 int* ptr = static_cast<int*>(reext->Value());
1464 CHECK_EQ(x, 3);
1465 *ptr = 10;
1466 CHECK_EQ(x, 10);
1467
1468 // Make sure unaligned pointers are wrapped properly.
1469 char* data = i::StrDup("0123456789");
1470 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1471 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1472 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1473 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1474
1475 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1476 CHECK_EQ('0', *char_ptr);
1477 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1478 CHECK_EQ('1', *char_ptr);
1479 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1480 CHECK_EQ('2', *char_ptr);
1481 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1482 CHECK_EQ('3', *char_ptr);
1483 i::DeleteArray(data);
1484 }
1485
1486
THREADED_TEST(GlobalHandle)1487 THREADED_TEST(GlobalHandle) {
1488 v8::Persistent<String> global;
1489 {
1490 v8::HandleScope scope;
1491 Local<String> str = v8_str("str");
1492 global = v8::Persistent<String>::New(str);
1493 }
1494 CHECK_EQ(global->Length(), 3);
1495 global.Dispose();
1496 }
1497
1498
THREADED_TEST(ScriptException)1499 THREADED_TEST(ScriptException) {
1500 v8::HandleScope scope;
1501 LocalContext env;
1502 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1503 v8::TryCatch try_catch;
1504 Local<Value> result = script->Run();
1505 CHECK(result.IsEmpty());
1506 CHECK(try_catch.HasCaught());
1507 String::AsciiValue exception_value(try_catch.Exception());
1508 CHECK_EQ(*exception_value, "panama!");
1509 }
1510
1511
1512 bool message_received;
1513
1514
check_message(v8::Handle<v8::Message> message,v8::Handle<Value> data)1515 static void check_message(v8::Handle<v8::Message> message,
1516 v8::Handle<Value> data) {
1517 CHECK_EQ(5.76, data->NumberValue());
1518 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1519 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1520 message_received = true;
1521 }
1522
1523
THREADED_TEST(MessageHandlerData)1524 THREADED_TEST(MessageHandlerData) {
1525 message_received = false;
1526 v8::HandleScope scope;
1527 CHECK(!message_received);
1528 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1529 LocalContext context;
1530 v8::ScriptOrigin origin =
1531 v8::ScriptOrigin(v8_str("6.75"));
1532 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1533 &origin);
1534 script->SetData(v8_str("7.56"));
1535 script->Run();
1536 CHECK(message_received);
1537 // clear out the message listener
1538 v8::V8::RemoveMessageListeners(check_message);
1539 }
1540
1541
THREADED_TEST(GetSetProperty)1542 THREADED_TEST(GetSetProperty) {
1543 v8::HandleScope scope;
1544 LocalContext context;
1545 context->Global()->Set(v8_str("foo"), v8_num(14));
1546 context->Global()->Set(v8_str("12"), v8_num(92));
1547 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1548 context->Global()->Set(v8_num(13), v8_num(56));
1549 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1550 CHECK_EQ(14, foo->Int32Value());
1551 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1552 CHECK_EQ(92, twelve->Int32Value());
1553 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1554 CHECK_EQ(32, sixteen->Int32Value());
1555 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1556 CHECK_EQ(56, thirteen->Int32Value());
1557 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1558 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1559 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1560 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1561 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1562 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1563 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1564 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1565 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1566 }
1567
1568
THREADED_TEST(PropertyAttributes)1569 THREADED_TEST(PropertyAttributes) {
1570 v8::HandleScope scope;
1571 LocalContext context;
1572 // read-only
1573 Local<String> prop = v8_str("read_only");
1574 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1575 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1576 Script::Compile(v8_str("read_only = 9"))->Run();
1577 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1578 context->Global()->Set(prop, v8_num(10));
1579 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1580 // dont-delete
1581 prop = v8_str("dont_delete");
1582 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1583 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1584 Script::Compile(v8_str("delete dont_delete"))->Run();
1585 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1586 }
1587
1588
THREADED_TEST(Array)1589 THREADED_TEST(Array) {
1590 v8::HandleScope scope;
1591 LocalContext context;
1592 Local<v8::Array> array = v8::Array::New();
1593 CHECK_EQ(0, array->Length());
1594 CHECK(array->Get(v8::Integer::New(0))->IsUndefined());
1595 CHECK(!array->Has(0));
1596 CHECK(array->Get(v8::Integer::New(100))->IsUndefined());
1597 CHECK(!array->Has(100));
1598 array->Set(v8::Integer::New(2), v8_num(7));
1599 CHECK_EQ(3, array->Length());
1600 CHECK(!array->Has(0));
1601 CHECK(!array->Has(1));
1602 CHECK(array->Has(2));
1603 CHECK_EQ(7, array->Get(v8::Integer::New(2))->Int32Value());
1604 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
1605 Local<v8::Array> arr = Local<v8::Array>::Cast(obj);
1606 CHECK_EQ(3, arr->Length());
1607 CHECK_EQ(1, arr->Get(v8::Integer::New(0))->Int32Value());
1608 CHECK_EQ(2, arr->Get(v8::Integer::New(1))->Int32Value());
1609 CHECK_EQ(3, arr->Get(v8::Integer::New(2))->Int32Value());
1610 }
1611
1612
HandleF(const v8::Arguments & args)1613 v8::Handle<Value> HandleF(const v8::Arguments& args) {
1614 v8::HandleScope scope;
1615 ApiTestFuzzer::Fuzz();
1616 Local<v8::Array> result = v8::Array::New(args.Length());
1617 for (int i = 0; i < args.Length(); i++)
1618 result->Set(v8::Integer::New(i), args[i]);
1619 return scope.Close(result);
1620 }
1621
1622
THREADED_TEST(Vector)1623 THREADED_TEST(Vector) {
1624 v8::HandleScope scope;
1625 Local<ObjectTemplate> global = ObjectTemplate::New();
1626 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1627 LocalContext context(0, global);
1628
1629 const char* fun = "f()";
1630 Local<v8::Array> a0 =
1631 Local<v8::Array>::Cast(Script::Compile(String::New(fun))->Run());
1632 CHECK_EQ(0, a0->Length());
1633
1634 const char* fun2 = "f(11)";
1635 Local<v8::Array> a1 =
1636 Local<v8::Array>::Cast(Script::Compile(String::New(fun2))->Run());
1637 CHECK_EQ(1, a1->Length());
1638 CHECK_EQ(11, a1->Get(v8::Integer::New(0))->Int32Value());
1639
1640 const char* fun3 = "f(12, 13)";
1641 Local<v8::Array> a2 =
1642 Local<v8::Array>::Cast(Script::Compile(String::New(fun3))->Run());
1643 CHECK_EQ(2, a2->Length());
1644 CHECK_EQ(12, a2->Get(v8::Integer::New(0))->Int32Value());
1645 CHECK_EQ(13, a2->Get(v8::Integer::New(1))->Int32Value());
1646
1647 const char* fun4 = "f(14, 15, 16)";
1648 Local<v8::Array> a3 =
1649 Local<v8::Array>::Cast(Script::Compile(String::New(fun4))->Run());
1650 CHECK_EQ(3, a3->Length());
1651 CHECK_EQ(14, a3->Get(v8::Integer::New(0))->Int32Value());
1652 CHECK_EQ(15, a3->Get(v8::Integer::New(1))->Int32Value());
1653 CHECK_EQ(16, a3->Get(v8::Integer::New(2))->Int32Value());
1654
1655 const char* fun5 = "f(17, 18, 19, 20)";
1656 Local<v8::Array> a4 =
1657 Local<v8::Array>::Cast(Script::Compile(String::New(fun5))->Run());
1658 CHECK_EQ(4, a4->Length());
1659 CHECK_EQ(17, a4->Get(v8::Integer::New(0))->Int32Value());
1660 CHECK_EQ(18, a4->Get(v8::Integer::New(1))->Int32Value());
1661 CHECK_EQ(19, a4->Get(v8::Integer::New(2))->Int32Value());
1662 CHECK_EQ(20, a4->Get(v8::Integer::New(3))->Int32Value());
1663 }
1664
1665
THREADED_TEST(FunctionCall)1666 THREADED_TEST(FunctionCall) {
1667 v8::HandleScope scope;
1668 LocalContext context;
1669 CompileRun(
1670 "function Foo() {"
1671 " var result = [];"
1672 " for (var i = 0; i < arguments.length; i++) {"
1673 " result.push(arguments[i]);"
1674 " }"
1675 " return result;"
1676 "}");
1677 Local<Function> Foo =
1678 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1679
1680 v8::Handle<Value>* args0 = NULL;
1681 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1682 CHECK_EQ(0, a0->Length());
1683
1684 v8::Handle<Value> args1[] = { v8_num(1.1) };
1685 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1686 CHECK_EQ(1, a1->Length());
1687 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1688
1689 v8::Handle<Value> args2[] = { v8_num(2.2),
1690 v8_num(3.3) };
1691 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1692 CHECK_EQ(2, a2->Length());
1693 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1694 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1695
1696 v8::Handle<Value> args3[] = { v8_num(4.4),
1697 v8_num(5.5),
1698 v8_num(6.6) };
1699 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1700 CHECK_EQ(3, a3->Length());
1701 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1702 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1703 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1704
1705 v8::Handle<Value> args4[] = { v8_num(7.7),
1706 v8_num(8.8),
1707 v8_num(9.9),
1708 v8_num(10.11) };
1709 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1710 CHECK_EQ(4, a4->Length());
1711 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1712 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1713 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1714 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1715 }
1716
1717
1718 static const char* js_code_causing_out_of_memory =
1719 "var a = new Array(); while(true) a.push(a);";
1720
1721
1722 // These tests run for a long time and prevent us from running tests
1723 // that come after them so they cannot run in parallel.
TEST(OutOfMemory)1724 TEST(OutOfMemory) {
1725 // It's not possible to read a snapshot into a heap with different dimensions.
1726 if (v8::internal::Snapshot::IsEnabled()) return;
1727 // Set heap limits.
1728 static const int K = 1024;
1729 v8::ResourceConstraints constraints;
1730 constraints.set_max_young_space_size(256 * K);
1731 constraints.set_max_old_space_size(4 * K * K);
1732 v8::SetResourceConstraints(&constraints);
1733
1734 // Execute a script that causes out of memory.
1735 v8::HandleScope scope;
1736 LocalContext context;
1737 v8::V8::IgnoreOutOfMemoryException();
1738 Local<Script> script =
1739 Script::Compile(String::New(js_code_causing_out_of_memory));
1740 Local<Value> result = script->Run();
1741
1742 // Check for out of memory state.
1743 CHECK(result.IsEmpty());
1744 CHECK(context->HasOutOfMemoryException());
1745 }
1746
1747
ProvokeOutOfMemory(const v8::Arguments & args)1748 v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1749 ApiTestFuzzer::Fuzz();
1750
1751 v8::HandleScope scope;
1752 LocalContext context;
1753 Local<Script> script =
1754 Script::Compile(String::New(js_code_causing_out_of_memory));
1755 Local<Value> result = script->Run();
1756
1757 // Check for out of memory state.
1758 CHECK(result.IsEmpty());
1759 CHECK(context->HasOutOfMemoryException());
1760
1761 return result;
1762 }
1763
1764
TEST(OutOfMemoryNested)1765 TEST(OutOfMemoryNested) {
1766 // It's not possible to read a snapshot into a heap with different dimensions.
1767 if (v8::internal::Snapshot::IsEnabled()) return;
1768 // Set heap limits.
1769 static const int K = 1024;
1770 v8::ResourceConstraints constraints;
1771 constraints.set_max_young_space_size(256 * K);
1772 constraints.set_max_old_space_size(4 * K * K);
1773 v8::SetResourceConstraints(&constraints);
1774
1775 v8::HandleScope scope;
1776 Local<ObjectTemplate> templ = ObjectTemplate::New();
1777 templ->Set(v8_str("ProvokeOutOfMemory"),
1778 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1779 LocalContext context(0, templ);
1780 v8::V8::IgnoreOutOfMemoryException();
1781 Local<Value> result = CompileRun(
1782 "var thrown = false;"
1783 "try {"
1784 " ProvokeOutOfMemory();"
1785 "} catch (e) {"
1786 " thrown = true;"
1787 "}");
1788 // Check for out of memory state.
1789 CHECK(result.IsEmpty());
1790 CHECK(context->HasOutOfMemoryException());
1791 }
1792
1793
TEST(HugeConsStringOutOfMemory)1794 TEST(HugeConsStringOutOfMemory) {
1795 // It's not possible to read a snapshot into a heap with different dimensions.
1796 if (v8::internal::Snapshot::IsEnabled()) return;
1797 v8::HandleScope scope;
1798 LocalContext context;
1799 // Set heap limits.
1800 static const int K = 1024;
1801 v8::ResourceConstraints constraints;
1802 constraints.set_max_young_space_size(256 * K);
1803 constraints.set_max_old_space_size(2 * K * K);
1804 v8::SetResourceConstraints(&constraints);
1805
1806 // Execute a script that causes out of memory.
1807 v8::V8::IgnoreOutOfMemoryException();
1808
1809 // Build huge string. This should fail with out of memory exception.
1810 Local<Value> result = CompileRun(
1811 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
1812 "for (var i = 0; i < 21; i++) { str = str + str; }");
1813
1814 // Check for out of memory state.
1815 CHECK(result.IsEmpty());
1816 CHECK(context->HasOutOfMemoryException());
1817 }
1818
1819
THREADED_TEST(ConstructCall)1820 THREADED_TEST(ConstructCall) {
1821 v8::HandleScope scope;
1822 LocalContext context;
1823 CompileRun(
1824 "function Foo() {"
1825 " var result = [];"
1826 " for (var i = 0; i < arguments.length; i++) {"
1827 " result.push(arguments[i]);"
1828 " }"
1829 " return result;"
1830 "}");
1831 Local<Function> Foo =
1832 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1833
1834 v8::Handle<Value>* args0 = NULL;
1835 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
1836 CHECK_EQ(0, a0->Length());
1837
1838 v8::Handle<Value> args1[] = { v8_num(1.1) };
1839 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
1840 CHECK_EQ(1, a1->Length());
1841 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1842
1843 v8::Handle<Value> args2[] = { v8_num(2.2),
1844 v8_num(3.3) };
1845 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
1846 CHECK_EQ(2, a2->Length());
1847 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1848 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1849
1850 v8::Handle<Value> args3[] = { v8_num(4.4),
1851 v8_num(5.5),
1852 v8_num(6.6) };
1853 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
1854 CHECK_EQ(3, a3->Length());
1855 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1856 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1857 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1858
1859 v8::Handle<Value> args4[] = { v8_num(7.7),
1860 v8_num(8.8),
1861 v8_num(9.9),
1862 v8_num(10.11) };
1863 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
1864 CHECK_EQ(4, a4->Length());
1865 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1866 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1867 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1868 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1869 }
1870
1871
CheckUncle(v8::TryCatch * try_catch)1872 static void CheckUncle(v8::TryCatch* try_catch) {
1873 CHECK(try_catch->HasCaught());
1874 String::AsciiValue str_value(try_catch->Exception());
1875 CHECK_EQ(*str_value, "uncle?");
1876 try_catch->Reset();
1877 }
1878
1879
THREADED_TEST(ConversionException)1880 THREADED_TEST(ConversionException) {
1881 v8::HandleScope scope;
1882 LocalContext env;
1883 CompileRun(
1884 "function TestClass() { };"
1885 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
1886 "var obj = new TestClass();");
1887 Local<Value> obj = env->Global()->Get(v8_str("obj"));
1888
1889 v8::TryCatch try_catch;
1890
1891 Local<Value> to_string_result = obj->ToString();
1892 CHECK(to_string_result.IsEmpty());
1893 CheckUncle(&try_catch);
1894
1895 Local<Value> to_number_result = obj->ToNumber();
1896 CHECK(to_number_result.IsEmpty());
1897 CheckUncle(&try_catch);
1898
1899 Local<Value> to_integer_result = obj->ToInteger();
1900 CHECK(to_integer_result.IsEmpty());
1901 CheckUncle(&try_catch);
1902
1903 Local<Value> to_uint32_result = obj->ToUint32();
1904 CHECK(to_uint32_result.IsEmpty());
1905 CheckUncle(&try_catch);
1906
1907 Local<Value> to_int32_result = obj->ToInt32();
1908 CHECK(to_int32_result.IsEmpty());
1909 CheckUncle(&try_catch);
1910
1911 Local<Value> to_object_result = v8::Undefined()->ToObject();
1912 CHECK(to_object_result.IsEmpty());
1913 CHECK(try_catch.HasCaught());
1914 try_catch.Reset();
1915
1916 int32_t int32_value = obj->Int32Value();
1917 CHECK_EQ(0, int32_value);
1918 CheckUncle(&try_catch);
1919
1920 uint32_t uint32_value = obj->Uint32Value();
1921 CHECK_EQ(0, uint32_value);
1922 CheckUncle(&try_catch);
1923
1924 double number_value = obj->NumberValue();
1925 CHECK_NE(0, IsNaN(number_value));
1926 CheckUncle(&try_catch);
1927
1928 int64_t integer_value = obj->IntegerValue();
1929 CHECK_EQ(0.0, static_cast<double>(integer_value));
1930 CheckUncle(&try_catch);
1931 }
1932
1933
ThrowFromC(const v8::Arguments & args)1934 v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
1935 ApiTestFuzzer::Fuzz();
1936 return v8::ThrowException(v8_str("konto"));
1937 }
1938
1939
CCatcher(const v8::Arguments & args)1940 v8::Handle<Value> CCatcher(const v8::Arguments& args) {
1941 if (args.Length() < 1) return v8::Boolean::New(false);
1942 v8::HandleScope scope;
1943 v8::TryCatch try_catch;
1944 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
1945 CHECK(!try_catch.HasCaught() || result.IsEmpty());
1946 return v8::Boolean::New(try_catch.HasCaught());
1947 }
1948
1949
THREADED_TEST(APICatch)1950 THREADED_TEST(APICatch) {
1951 v8::HandleScope scope;
1952 Local<ObjectTemplate> templ = ObjectTemplate::New();
1953 templ->Set(v8_str("ThrowFromC"),
1954 v8::FunctionTemplate::New(ThrowFromC));
1955 LocalContext context(0, templ);
1956 CompileRun(
1957 "var thrown = false;"
1958 "try {"
1959 " ThrowFromC();"
1960 "} catch (e) {"
1961 " thrown = true;"
1962 "}");
1963 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
1964 CHECK(thrown->BooleanValue());
1965 }
1966
1967
THREADED_TEST(APIThrowTryCatch)1968 THREADED_TEST(APIThrowTryCatch) {
1969 v8::HandleScope scope;
1970 Local<ObjectTemplate> templ = ObjectTemplate::New();
1971 templ->Set(v8_str("ThrowFromC"),
1972 v8::FunctionTemplate::New(ThrowFromC));
1973 LocalContext context(0, templ);
1974 v8::TryCatch try_catch;
1975 CompileRun("ThrowFromC();");
1976 CHECK(try_catch.HasCaught());
1977 }
1978
1979
1980 // Test that a try-finally block doesn't shadow a try-catch block
1981 // when setting up an external handler.
1982 //
1983 // BUG(271): Some of the exception propagation does not work on the
1984 // ARM simulator because the simulator separates the C++ stack and the
1985 // JS stack. This test therefore fails on the simulator. The test is
1986 // not threaded to allow the threading tests to run on the simulator.
TEST(TryCatchInTryFinally)1987 TEST(TryCatchInTryFinally) {
1988 v8::HandleScope scope;
1989 Local<ObjectTemplate> templ = ObjectTemplate::New();
1990 templ->Set(v8_str("CCatcher"),
1991 v8::FunctionTemplate::New(CCatcher));
1992 LocalContext context(0, templ);
1993 Local<Value> result = CompileRun("try {"
1994 " try {"
1995 " CCatcher('throw 7;');"
1996 " } finally {"
1997 " }"
1998 "} catch (e) {"
1999 "}");
2000 CHECK(result->IsTrue());
2001 }
2002
2003
receive_message(v8::Handle<v8::Message> message,v8::Handle<v8::Value> data)2004 static void receive_message(v8::Handle<v8::Message> message,
2005 v8::Handle<v8::Value> data) {
2006 message->Get();
2007 message_received = true;
2008 }
2009
2010
TEST(APIThrowMessage)2011 TEST(APIThrowMessage) {
2012 message_received = false;
2013 v8::HandleScope scope;
2014 v8::V8::AddMessageListener(receive_message);
2015 Local<ObjectTemplate> templ = ObjectTemplate::New();
2016 templ->Set(v8_str("ThrowFromC"),
2017 v8::FunctionTemplate::New(ThrowFromC));
2018 LocalContext context(0, templ);
2019 CompileRun("ThrowFromC();");
2020 CHECK(message_received);
2021 v8::V8::RemoveMessageListeners(check_message);
2022 }
2023
2024
TEST(APIThrowMessageAndVerboseTryCatch)2025 TEST(APIThrowMessageAndVerboseTryCatch) {
2026 message_received = false;
2027 v8::HandleScope scope;
2028 v8::V8::AddMessageListener(receive_message);
2029 Local<ObjectTemplate> templ = ObjectTemplate::New();
2030 templ->Set(v8_str("ThrowFromC"),
2031 v8::FunctionTemplate::New(ThrowFromC));
2032 LocalContext context(0, templ);
2033 v8::TryCatch try_catch;
2034 try_catch.SetVerbose(true);
2035 Local<Value> result = CompileRun("ThrowFromC();");
2036 CHECK(try_catch.HasCaught());
2037 CHECK(result.IsEmpty());
2038 CHECK(message_received);
2039 v8::V8::RemoveMessageListeners(check_message);
2040 }
2041
2042
THREADED_TEST(ExternalScriptException)2043 THREADED_TEST(ExternalScriptException) {
2044 v8::HandleScope scope;
2045 Local<ObjectTemplate> templ = ObjectTemplate::New();
2046 templ->Set(v8_str("ThrowFromC"),
2047 v8::FunctionTemplate::New(ThrowFromC));
2048 LocalContext context(0, templ);
2049
2050 v8::TryCatch try_catch;
2051 Local<Script> script
2052 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2053 Local<Value> result = script->Run();
2054 CHECK(result.IsEmpty());
2055 CHECK(try_catch.HasCaught());
2056 String::AsciiValue exception_value(try_catch.Exception());
2057 CHECK_EQ("konto", *exception_value);
2058 }
2059
2060
2061
CThrowCountDown(const v8::Arguments & args)2062 v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2063 ApiTestFuzzer::Fuzz();
2064 CHECK_EQ(4, args.Length());
2065 int count = args[0]->Int32Value();
2066 int cInterval = args[2]->Int32Value();
2067 if (count == 0) {
2068 return v8::ThrowException(v8_str("FromC"));
2069 } else {
2070 Local<v8::Object> global = Context::GetCurrent()->Global();
2071 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2072 v8::Handle<Value> argv[] = { v8_num(count - 1),
2073 args[1],
2074 args[2],
2075 args[3] };
2076 if (count % cInterval == 0) {
2077 v8::TryCatch try_catch;
2078 Local<Value> result =
2079 v8::Handle<Function>::Cast(fun)->Call(global, 4, argv);
2080 int expected = args[3]->Int32Value();
2081 if (try_catch.HasCaught()) {
2082 CHECK_EQ(expected, count);
2083 CHECK(result.IsEmpty());
2084 CHECK(!i::Top::has_scheduled_exception());
2085 } else {
2086 CHECK_NE(expected, count);
2087 }
2088 return result;
2089 } else {
2090 return v8::Handle<Function>::Cast(fun)->Call(global, 4, argv);
2091 }
2092 }
2093 }
2094
2095
JSCheck(const v8::Arguments & args)2096 v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2097 ApiTestFuzzer::Fuzz();
2098 CHECK_EQ(3, args.Length());
2099 bool equality = args[0]->BooleanValue();
2100 int count = args[1]->Int32Value();
2101 int expected = args[2]->Int32Value();
2102 if (equality) {
2103 CHECK_EQ(count, expected);
2104 } else {
2105 CHECK_NE(count, expected);
2106 }
2107 return v8::Undefined();
2108 }
2109
2110
THREADED_TEST(EvalInTryFinally)2111 THREADED_TEST(EvalInTryFinally) {
2112 v8::HandleScope scope;
2113 LocalContext context;
2114 v8::TryCatch try_catch;
2115 CompileRun("(function() {"
2116 " try {"
2117 " eval('asldkf (*&^&*^');"
2118 " } finally {"
2119 " return;"
2120 " }"
2121 "})()");
2122 CHECK(!try_catch.HasCaught());
2123 }
2124
2125
2126 // This test works by making a stack of alternating JavaScript and C
2127 // activations. These activations set up exception handlers with regular
2128 // intervals, one interval for C activations and another for JavaScript
2129 // activations. When enough activations have been created an exception is
2130 // thrown and we check that the right activation catches the exception and that
2131 // no other activations do. The right activation is always the topmost one with
2132 // a handler, regardless of whether it is in JavaScript or C.
2133 //
2134 // The notation used to describe a test case looks like this:
2135 //
2136 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2137 //
2138 // Each entry is an activation, either JS or C. The index is the count at that
2139 // level. Stars identify activations with exception handlers, the @ identifies
2140 // the exception handler that should catch the exception.
2141 //
2142 // BUG(271): Some of the exception propagation does not work on the
2143 // ARM simulator because the simulator separates the C++ stack and the
2144 // JS stack. This test therefore fails on the simulator. The test is
2145 // not threaded to allow the threading tests to run on the simulator.
TEST(ExceptionOrder)2146 TEST(ExceptionOrder) {
2147 v8::HandleScope scope;
2148 Local<ObjectTemplate> templ = ObjectTemplate::New();
2149 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2150 templ->Set(v8_str("CThrowCountDown"),
2151 v8::FunctionTemplate::New(CThrowCountDown));
2152 LocalContext context(0, templ);
2153 CompileRun(
2154 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2155 " if (count == 0) throw 'FromJS';"
2156 " if (count % jsInterval == 0) {"
2157 " try {"
2158 " var value = CThrowCountDown(count - 1,"
2159 " jsInterval,"
2160 " cInterval,"
2161 " expected);"
2162 " check(false, count, expected);"
2163 " return value;"
2164 " } catch (e) {"
2165 " check(true, count, expected);"
2166 " }"
2167 " } else {"
2168 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2169 " }"
2170 "}");
2171 Local<Function> fun =
2172 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2173
2174 const int argc = 4;
2175 // count jsInterval cInterval expected
2176
2177 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2178 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2179 fun->Call(fun, argc, a0);
2180
2181 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2182 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2183 fun->Call(fun, argc, a1);
2184
2185 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2186 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2187 fun->Call(fun, argc, a2);
2188
2189 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2190 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2191 fun->Call(fun, argc, a3);
2192
2193 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2194 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2195 fun->Call(fun, argc, a4);
2196
2197 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2198 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2199 fun->Call(fun, argc, a5);
2200 }
2201
2202
ThrowValue(const v8::Arguments & args)2203 v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2204 ApiTestFuzzer::Fuzz();
2205 CHECK_EQ(1, args.Length());
2206 return v8::ThrowException(args[0]);
2207 }
2208
2209
THREADED_TEST(ThrowValues)2210 THREADED_TEST(ThrowValues) {
2211 v8::HandleScope scope;
2212 Local<ObjectTemplate> templ = ObjectTemplate::New();
2213 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2214 LocalContext context(0, templ);
2215 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2216 "function Run(obj) {"
2217 " try {"
2218 " Throw(obj);"
2219 " } catch (e) {"
2220 " return e;"
2221 " }"
2222 " return 'no exception';"
2223 "}"
2224 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2225 CHECK_EQ(5, result->Length());
2226 CHECK(result->Get(v8::Integer::New(0))->IsString());
2227 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2228 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2229 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2230 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2231 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2232 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2233 }
2234
2235
THREADED_TEST(CatchZero)2236 THREADED_TEST(CatchZero) {
2237 v8::HandleScope scope;
2238 LocalContext context;
2239 v8::TryCatch try_catch;
2240 CHECK(!try_catch.HasCaught());
2241 Script::Compile(v8_str("throw 10"))->Run();
2242 CHECK(try_catch.HasCaught());
2243 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2244 try_catch.Reset();
2245 CHECK(!try_catch.HasCaught());
2246 Script::Compile(v8_str("throw 0"))->Run();
2247 CHECK(try_catch.HasCaught());
2248 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2249 }
2250
2251
THREADED_TEST(CatchExceptionFromWith)2252 THREADED_TEST(CatchExceptionFromWith) {
2253 v8::HandleScope scope;
2254 LocalContext context;
2255 v8::TryCatch try_catch;
2256 CHECK(!try_catch.HasCaught());
2257 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2258 CHECK(try_catch.HasCaught());
2259 }
2260
2261
THREADED_TEST(Equality)2262 THREADED_TEST(Equality) {
2263 v8::HandleScope scope;
2264 LocalContext context;
2265 // Check that equality works at all before relying on CHECK_EQ
2266 CHECK(v8_str("a")->Equals(v8_str("a")));
2267 CHECK(!v8_str("a")->Equals(v8_str("b")));
2268
2269 CHECK_EQ(v8_str("a"), v8_str("a"));
2270 CHECK_NE(v8_str("a"), v8_str("b"));
2271 CHECK_EQ(v8_num(1), v8_num(1));
2272 CHECK_EQ(v8_num(1.00), v8_num(1));
2273 CHECK_NE(v8_num(1), v8_num(2));
2274
2275 // Assume String is not symbol.
2276 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2277 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2278 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2279 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2280 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2281 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2282 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2283 CHECK(!not_a_number->StrictEquals(not_a_number));
2284 CHECK(v8::False()->StrictEquals(v8::False()));
2285 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2286
2287 v8::Handle<v8::Object> obj = v8::Object::New();
2288 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2289 CHECK(alias->StrictEquals(obj));
2290 alias.Dispose();
2291 }
2292
2293
THREADED_TEST(MultiRun)2294 THREADED_TEST(MultiRun) {
2295 v8::HandleScope scope;
2296 LocalContext context;
2297 Local<Script> script = Script::Compile(v8_str("x"));
2298 for (int i = 0; i < 10; i++)
2299 script->Run();
2300 }
2301
2302
GetXValue(Local<String> name,const AccessorInfo & info)2303 static v8::Handle<Value> GetXValue(Local<String> name,
2304 const AccessorInfo& info) {
2305 ApiTestFuzzer::Fuzz();
2306 CHECK_EQ(info.Data(), v8_str("donut"));
2307 CHECK_EQ(name, v8_str("x"));
2308 return name;
2309 }
2310
2311
THREADED_TEST(SimplePropertyRead)2312 THREADED_TEST(SimplePropertyRead) {
2313 v8::HandleScope scope;
2314 Local<ObjectTemplate> templ = ObjectTemplate::New();
2315 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2316 LocalContext context;
2317 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2318 Local<Script> script = Script::Compile(v8_str("obj.x"));
2319 for (int i = 0; i < 10; i++) {
2320 Local<Value> result = script->Run();
2321 CHECK_EQ(result, v8_str("x"));
2322 }
2323 }
2324
2325
2326 v8::Persistent<Value> xValue;
2327
2328
SetXValue(Local<String> name,Local<Value> value,const AccessorInfo & info)2329 static void SetXValue(Local<String> name,
2330 Local<Value> value,
2331 const AccessorInfo& info) {
2332 CHECK_EQ(value, v8_num(4));
2333 CHECK_EQ(info.Data(), v8_str("donut"));
2334 CHECK_EQ(name, v8_str("x"));
2335 CHECK(xValue.IsEmpty());
2336 xValue = v8::Persistent<Value>::New(value);
2337 }
2338
2339
THREADED_TEST(SimplePropertyWrite)2340 THREADED_TEST(SimplePropertyWrite) {
2341 v8::HandleScope scope;
2342 Local<ObjectTemplate> templ = ObjectTemplate::New();
2343 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2344 LocalContext context;
2345 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2346 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2347 for (int i = 0; i < 10; i++) {
2348 CHECK(xValue.IsEmpty());
2349 script->Run();
2350 CHECK_EQ(v8_num(4), xValue);
2351 xValue.Dispose();
2352 xValue = v8::Persistent<Value>();
2353 }
2354 }
2355
2356
XPropertyGetter(Local<String> property,const AccessorInfo & info)2357 static v8::Handle<Value> XPropertyGetter(Local<String> property,
2358 const AccessorInfo& info) {
2359 ApiTestFuzzer::Fuzz();
2360 CHECK(info.Data()->IsUndefined());
2361 return property;
2362 }
2363
2364
THREADED_TEST(NamedInterceptorPropertyRead)2365 THREADED_TEST(NamedInterceptorPropertyRead) {
2366 v8::HandleScope scope;
2367 Local<ObjectTemplate> templ = ObjectTemplate::New();
2368 templ->SetNamedPropertyHandler(XPropertyGetter);
2369 LocalContext context;
2370 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2371 Local<Script> script = Script::Compile(v8_str("obj.x"));
2372 for (int i = 0; i < 10; i++) {
2373 Local<Value> result = script->Run();
2374 CHECK_EQ(result, v8_str("x"));
2375 }
2376 }
2377
2378
IndexedPropertyGetter(uint32_t index,const AccessorInfo & info)2379 static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2380 const AccessorInfo& info) {
2381 ApiTestFuzzer::Fuzz();
2382 if (index == 37) {
2383 return v8::Handle<Value>(v8_num(625));
2384 }
2385 return v8::Handle<Value>();
2386 }
2387
2388
IndexedPropertySetter(uint32_t index,Local<Value> value,const AccessorInfo & info)2389 static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2390 Local<Value> value,
2391 const AccessorInfo& info) {
2392 ApiTestFuzzer::Fuzz();
2393 if (index == 39) {
2394 return value;
2395 }
2396 return v8::Handle<Value>();
2397 }
2398
2399
THREADED_TEST(IndexedInterceptorWithIndexedAccessor)2400 THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2401 v8::HandleScope scope;
2402 Local<ObjectTemplate> templ = ObjectTemplate::New();
2403 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2404 IndexedPropertySetter);
2405 LocalContext context;
2406 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2407 Local<Script> getter_script = Script::Compile(v8_str(
2408 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2409 Local<Script> setter_script = Script::Compile(v8_str(
2410 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2411 "obj[17] = 23;"
2412 "obj.foo;"));
2413 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
2414 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
2415 "obj[39] = 47;"
2416 "obj.foo;")); // This setter should not run, due to the interceptor.
2417 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
2418 "obj[37];"));
2419 Local<Value> result = getter_script->Run();
2420 CHECK_EQ(v8_num(5), result);
2421 result = setter_script->Run();
2422 CHECK_EQ(v8_num(23), result);
2423 result = interceptor_setter_script->Run();
2424 CHECK_EQ(v8_num(23), result);
2425 result = interceptor_getter_script->Run();
2426 CHECK_EQ(v8_num(625), result);
2427 }
2428
2429
THREADED_TEST(MultiContexts)2430 THREADED_TEST(MultiContexts) {
2431 v8::HandleScope scope;
2432 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
2433 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
2434
2435 Local<String> password = v8_str("Password");
2436
2437 // Create an environment
2438 LocalContext context0(0, templ);
2439 context0->SetSecurityToken(password);
2440 v8::Handle<v8::Object> global0 = context0->Global();
2441 global0->Set(v8_str("custom"), v8_num(1234));
2442 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
2443
2444 // Create an independent environment
2445 LocalContext context1(0, templ);
2446 context1->SetSecurityToken(password);
2447 v8::Handle<v8::Object> global1 = context1->Global();
2448 global1->Set(v8_str("custom"), v8_num(1234));
2449 CHECK_NE(global0, global1);
2450 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
2451 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
2452
2453 // Now create a new context with the old global
2454 LocalContext context2(0, templ, global1);
2455 context2->SetSecurityToken(password);
2456 v8::Handle<v8::Object> global2 = context2->Global();
2457 CHECK_EQ(global1, global2);
2458 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
2459 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
2460 }
2461
2462
THREADED_TEST(FunctionPrototypeAcrossContexts)2463 THREADED_TEST(FunctionPrototypeAcrossContexts) {
2464 // Make sure that functions created by cloning boilerplates cannot
2465 // communicate through their __proto__ field.
2466
2467 v8::HandleScope scope;
2468
2469 LocalContext env0;
2470 v8::Handle<v8::Object> global0 =
2471 env0->Global();
2472 v8::Handle<v8::Object> object0 =
2473 v8::Handle<v8::Object>::Cast(global0->Get(v8_str("Object")));
2474 v8::Handle<v8::Object> tostring0 =
2475 v8::Handle<v8::Object>::Cast(object0->Get(v8_str("toString")));
2476 v8::Handle<v8::Object> proto0 =
2477 v8::Handle<v8::Object>::Cast(tostring0->Get(v8_str("__proto__")));
2478 proto0->Set(v8_str("custom"), v8_num(1234));
2479
2480 LocalContext env1;
2481 v8::Handle<v8::Object> global1 =
2482 env1->Global();
2483 v8::Handle<v8::Object> object1 =
2484 v8::Handle<v8::Object>::Cast(global1->Get(v8_str("Object")));
2485 v8::Handle<v8::Object> tostring1 =
2486 v8::Handle<v8::Object>::Cast(object1->Get(v8_str("toString")));
2487 v8::Handle<v8::Object> proto1 =
2488 v8::Handle<v8::Object>::Cast(tostring1->Get(v8_str("__proto__")));
2489 CHECK(!proto1->Has(v8_str("custom")));
2490 }
2491
2492
THREADED_TEST(Regress892105)2493 THREADED_TEST(Regress892105) {
2494 // Make sure that object and array literals created by cloning
2495 // boilerplates cannot communicate through their __proto__
2496 // field. This is rather difficult to check, but we try to add stuff
2497 // to Object.prototype and Array.prototype and create a new
2498 // environment. This should succeed.
2499
2500 v8::HandleScope scope;
2501
2502 Local<String> source = v8_str("Object.prototype.obj = 1234;"
2503 "Array.prototype.arr = 4567;"
2504 "8901");
2505
2506 LocalContext env0;
2507 Local<Script> script0 = Script::Compile(source);
2508 CHECK_EQ(8901.0, script0->Run()->NumberValue());
2509
2510 LocalContext env1;
2511 Local<Script> script1 = Script::Compile(source);
2512 CHECK_EQ(8901.0, script1->Run()->NumberValue());
2513 }
2514
2515
ExpectString(const char * code,const char * expected)2516 static void ExpectString(const char* code, const char* expected) {
2517 Local<Value> result = CompileRun(code);
2518 CHECK(result->IsString());
2519 String::AsciiValue ascii(result);
2520 CHECK_EQ(0, strcmp(*ascii, expected));
2521 }
2522
2523
ExpectBoolean(const char * code,bool expected)2524 static void ExpectBoolean(const char* code, bool expected) {
2525 Local<Value> result = CompileRun(code);
2526 CHECK(result->IsBoolean());
2527 CHECK_EQ(expected, result->BooleanValue());
2528 }
2529
2530
ExpectObject(const char * code,Local<Value> expected)2531 static void ExpectObject(const char* code, Local<Value> expected) {
2532 Local<Value> result = CompileRun(code);
2533 CHECK(result->Equals(expected));
2534 }
2535
2536
THREADED_TEST(UndetectableObject)2537 THREADED_TEST(UndetectableObject) {
2538 v8::HandleScope scope;
2539 LocalContext env;
2540
2541 Local<v8::FunctionTemplate> desc =
2542 v8::FunctionTemplate::New(0, v8::Handle<Value>());
2543 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
2544
2545 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
2546 env->Global()->Set(v8_str("undetectable"), obj);
2547
2548 ExpectString("undetectable.toString()", "[object Object]");
2549 ExpectString("typeof undetectable", "undefined");
2550 ExpectString("typeof(undetectable)", "undefined");
2551 ExpectBoolean("typeof undetectable == 'undefined'", true);
2552 ExpectBoolean("typeof undetectable == 'object'", false);
2553 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
2554 ExpectBoolean("!undetectable", true);
2555
2556 ExpectObject("true&&undetectable", obj);
2557 ExpectBoolean("false&&undetectable", false);
2558 ExpectBoolean("true||undetectable", true);
2559 ExpectObject("false||undetectable", obj);
2560
2561 ExpectObject("undetectable&&true", obj);
2562 ExpectObject("undetectable&&false", obj);
2563 ExpectBoolean("undetectable||true", true);
2564 ExpectBoolean("undetectable||false", false);
2565
2566 ExpectBoolean("undetectable==null", true);
2567 ExpectBoolean("null==undetectable", true);
2568 ExpectBoolean("undetectable==undefined", true);
2569 ExpectBoolean("undefined==undetectable", true);
2570 ExpectBoolean("undetectable==undetectable", true);
2571
2572
2573 ExpectBoolean("undetectable===null", false);
2574 ExpectBoolean("null===undetectable", false);
2575 ExpectBoolean("undetectable===undefined", false);
2576 ExpectBoolean("undefined===undetectable", false);
2577 ExpectBoolean("undetectable===undetectable", true);
2578 }
2579
2580
THREADED_TEST(UndetectableString)2581 THREADED_TEST(UndetectableString) {
2582 v8::HandleScope scope;
2583 LocalContext env;
2584
2585 Local<String> obj = String::NewUndetectable("foo");
2586 env->Global()->Set(v8_str("undetectable"), obj);
2587
2588 ExpectString("undetectable", "foo");
2589 ExpectString("typeof undetectable", "undefined");
2590 ExpectString("typeof(undetectable)", "undefined");
2591 ExpectBoolean("typeof undetectable == 'undefined'", true);
2592 ExpectBoolean("typeof undetectable == 'string'", false);
2593 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
2594 ExpectBoolean("!undetectable", true);
2595
2596 ExpectObject("true&&undetectable", obj);
2597 ExpectBoolean("false&&undetectable", false);
2598 ExpectBoolean("true||undetectable", true);
2599 ExpectObject("false||undetectable", obj);
2600
2601 ExpectObject("undetectable&&true", obj);
2602 ExpectObject("undetectable&&false", obj);
2603 ExpectBoolean("undetectable||true", true);
2604 ExpectBoolean("undetectable||false", false);
2605
2606 ExpectBoolean("undetectable==null", true);
2607 ExpectBoolean("null==undetectable", true);
2608 ExpectBoolean("undetectable==undefined", true);
2609 ExpectBoolean("undefined==undetectable", true);
2610 ExpectBoolean("undetectable==undetectable", true);
2611
2612
2613 ExpectBoolean("undetectable===null", false);
2614 ExpectBoolean("null===undetectable", false);
2615 ExpectBoolean("undetectable===undefined", false);
2616 ExpectBoolean("undefined===undetectable", false);
2617 ExpectBoolean("undetectable===undetectable", true);
2618 }
2619
2620
USE(T)2621 template <typename T> static void USE(T) { }
2622
2623
2624 // This test is not intended to be run, just type checked.
PersistentHandles()2625 static void PersistentHandles() {
2626 USE(PersistentHandles);
2627 Local<String> str = v8_str("foo");
2628 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
2629 USE(p_str);
2630 Local<Script> scr = Script::Compile(v8_str(""));
2631 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
2632 USE(p_scr);
2633 Local<ObjectTemplate> templ = ObjectTemplate::New();
2634 v8::Persistent<ObjectTemplate> p_templ =
2635 v8::Persistent<ObjectTemplate>::New(templ);
2636 USE(p_templ);
2637 }
2638
2639
HandleLogDelegator(const v8::Arguments & args)2640 static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
2641 ApiTestFuzzer::Fuzz();
2642 return v8::Undefined();
2643 }
2644
2645
THREADED_TEST(GlobalObjectTemplate)2646 THREADED_TEST(GlobalObjectTemplate) {
2647 v8::HandleScope handle_scope;
2648 Local<ObjectTemplate> global_template = ObjectTemplate::New();
2649 global_template->Set(v8_str("JSNI_Log"),
2650 v8::FunctionTemplate::New(HandleLogDelegator));
2651 v8::Persistent<Context> context = Context::New(0, global_template);
2652 Context::Scope context_scope(context);
2653 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
2654 context.Dispose();
2655 }
2656
2657
2658 static const char* kSimpleExtensionSource =
2659 "function Foo() {"
2660 " return 4;"
2661 "}";
2662
2663
THREADED_TEST(SimpleExtensions)2664 THREADED_TEST(SimpleExtensions) {
2665 v8::HandleScope handle_scope;
2666 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
2667 const char* extension_names[] = { "simpletest" };
2668 v8::ExtensionConfiguration extensions(1, extension_names);
2669 v8::Handle<Context> context = Context::New(&extensions);
2670 Context::Scope lock(context);
2671 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
2672 CHECK_EQ(result, v8::Integer::New(4));
2673 }
2674
2675
2676 static const char* kEvalExtensionSource1 =
2677 "function UseEval1() {"
2678 " var x = 42;"
2679 " return eval('x');"
2680 "}";
2681
2682
2683 static const char* kEvalExtensionSource2 =
2684 "(function() {"
2685 " var x = 42;"
2686 " function e() {"
2687 " return eval('x');"
2688 " }"
2689 " this.UseEval2 = e;"
2690 "})()";
2691
2692
THREADED_TEST(UseEvalFromExtension)2693 THREADED_TEST(UseEvalFromExtension) {
2694 v8::HandleScope handle_scope;
2695 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
2696 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
2697 const char* extension_names[] = { "evaltest1", "evaltest2" };
2698 v8::ExtensionConfiguration extensions(2, extension_names);
2699 v8::Handle<Context> context = Context::New(&extensions);
2700 Context::Scope lock(context);
2701 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
2702 CHECK_EQ(result, v8::Integer::New(42));
2703 result = Script::Compile(v8_str("UseEval2()"))->Run();
2704 CHECK_EQ(result, v8::Integer::New(42));
2705 }
2706
2707
2708 static const char* kWithExtensionSource1 =
2709 "function UseWith1() {"
2710 " var x = 42;"
2711 " with({x:87}) { return x; }"
2712 "}";
2713
2714
2715
2716 static const char* kWithExtensionSource2 =
2717 "(function() {"
2718 " var x = 42;"
2719 " function e() {"
2720 " with ({x:87}) { return x; }"
2721 " }"
2722 " this.UseWith2 = e;"
2723 "})()";
2724
2725
THREADED_TEST(UseWithFromExtension)2726 THREADED_TEST(UseWithFromExtension) {
2727 v8::HandleScope handle_scope;
2728 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
2729 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
2730 const char* extension_names[] = { "withtest1", "withtest2" };
2731 v8::ExtensionConfiguration extensions(2, extension_names);
2732 v8::Handle<Context> context = Context::New(&extensions);
2733 Context::Scope lock(context);
2734 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
2735 CHECK_EQ(result, v8::Integer::New(87));
2736 result = Script::Compile(v8_str("UseWith2()"))->Run();
2737 CHECK_EQ(result, v8::Integer::New(87));
2738 }
2739
2740
THREADED_TEST(AutoExtensions)2741 THREADED_TEST(AutoExtensions) {
2742 v8::HandleScope handle_scope;
2743 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
2744 extension->set_auto_enable(true);
2745 v8::RegisterExtension(extension);
2746 v8::Handle<Context> context = Context::New();
2747 Context::Scope lock(context);
2748 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
2749 CHECK_EQ(result, v8::Integer::New(4));
2750 }
2751
2752
CheckDependencies(const char * name,const char * expected)2753 static void CheckDependencies(const char* name, const char* expected) {
2754 v8::HandleScope handle_scope;
2755 v8::ExtensionConfiguration config(1, &name);
2756 LocalContext context(&config);
2757 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
2758 }
2759
2760
2761 /*
2762 * Configuration:
2763 *
2764 * /-- B <--\
2765 * A <- -- D <-- E
2766 * \-- C <--/
2767 */
THREADED_TEST(ExtensionDependency)2768 THREADED_TEST(ExtensionDependency) {
2769 static const char* kEDeps[] = { "D" };
2770 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
2771 static const char* kDDeps[] = { "B", "C" };
2772 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
2773 static const char* kBCDeps[] = { "A" };
2774 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
2775 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
2776 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
2777 CheckDependencies("A", "undefinedA");
2778 CheckDependencies("B", "undefinedAB");
2779 CheckDependencies("C", "undefinedAC");
2780 CheckDependencies("D", "undefinedABCD");
2781 CheckDependencies("E", "undefinedABCDE");
2782 v8::HandleScope handle_scope;
2783 static const char* exts[2] = { "C", "E" };
2784 v8::ExtensionConfiguration config(2, exts);
2785 LocalContext context(&config);
2786 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
2787 }
2788
2789
2790 static const char* kExtensionTestScript =
2791 "native function A();"
2792 "native function B();"
2793 "native function C();"
2794 "function Foo(i) {"
2795 " if (i == 0) return A();"
2796 " if (i == 1) return B();"
2797 " if (i == 2) return C();"
2798 "}";
2799
2800
CallFun(const v8::Arguments & args)2801 static v8::Handle<Value> CallFun(const v8::Arguments& args) {
2802 ApiTestFuzzer::Fuzz();
2803 return args.Data();
2804 }
2805
2806
2807 class FunctionExtension : public Extension {
2808 public:
FunctionExtension()2809 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
2810 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
2811 v8::Handle<String> name);
2812 };
2813
2814
2815 static int lookup_count = 0;
GetNativeFunction(v8::Handle<String> name)2816 v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
2817 v8::Handle<String> name) {
2818 lookup_count++;
2819 if (name->Equals(v8_str("A"))) {
2820 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
2821 } else if (name->Equals(v8_str("B"))) {
2822 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
2823 } else if (name->Equals(v8_str("C"))) {
2824 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
2825 } else {
2826 return v8::Handle<v8::FunctionTemplate>();
2827 }
2828 }
2829
2830
THREADED_TEST(FunctionLookup)2831 THREADED_TEST(FunctionLookup) {
2832 v8::RegisterExtension(new FunctionExtension());
2833 v8::HandleScope handle_scope;
2834 static const char* exts[1] = { "functiontest" };
2835 v8::ExtensionConfiguration config(1, exts);
2836 LocalContext context(&config);
2837 CHECK_EQ(3, lookup_count);
2838 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
2839 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
2840 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
2841 }
2842
2843
2844 static const char* last_location;
2845 static const char* last_message;
StoringErrorCallback(const char * location,const char * message)2846 void StoringErrorCallback(const char* location, const char* message) {
2847 if (last_location == NULL) {
2848 last_location = location;
2849 last_message = message;
2850 }
2851 }
2852
2853
2854 // ErrorReporting creates a circular extensions configuration and
2855 // tests that the fatal error handler gets called. This renders V8
2856 // unusable and therefore this test cannot be run in parallel.
TEST(ErrorReporting)2857 TEST(ErrorReporting) {
2858 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
2859 static const char* aDeps[] = { "B" };
2860 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
2861 static const char* bDeps[] = { "A" };
2862 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
2863 last_location = NULL;
2864 v8::ExtensionConfiguration config(1, bDeps);
2865 v8::Handle<Context> context = Context::New(&config);
2866 CHECK(context.IsEmpty());
2867 CHECK_NE(last_location, NULL);
2868 }
2869
2870
2871 static const char* js_code_causing_huge_string_flattening =
2872 "var str = 'X';"
2873 "for (var i = 0; i < 30; i++) {"
2874 " str = str + str;"
2875 "}"
2876 "str.match(/X/);";
2877
2878
OOMCallback(const char * location,const char * message)2879 void OOMCallback(const char* location, const char* message) {
2880 exit(0);
2881 }
2882
2883
TEST(RegexpOutOfMemory)2884 TEST(RegexpOutOfMemory) {
2885 // Execute a script that causes out of memory when flattening a string.
2886 v8::HandleScope scope;
2887 v8::V8::SetFatalErrorHandler(OOMCallback);
2888 LocalContext context;
2889 Local<Script> script =
2890 Script::Compile(String::New(js_code_causing_huge_string_flattening));
2891 last_location = NULL;
2892 Local<Value> result = script->Run();
2893
2894 CHECK(false); // Should not return.
2895 }
2896
2897
MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,v8::Handle<Value> data)2898 static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
2899 v8::Handle<Value> data) {
2900 CHECK_EQ(v8::Undefined(), data);
2901 CHECK(message->GetScriptResourceName()->IsUndefined());
2902 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
2903 message->GetLineNumber();
2904 message->GetSourceLine();
2905 }
2906
2907
THREADED_TEST(ErrorWithMissingScriptInfo)2908 THREADED_TEST(ErrorWithMissingScriptInfo) {
2909 v8::HandleScope scope;
2910 LocalContext context;
2911 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
2912 Script::Compile(v8_str("throw Error()"))->Run();
2913 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
2914 }
2915
2916
2917 int global_index = 0;
2918
2919 class Snorkel {
2920 public:
Snorkel()2921 Snorkel() { index_ = global_index++; }
2922 int index_;
2923 };
2924
2925 class Whammy {
2926 public:
Whammy()2927 Whammy() {
2928 cursor_ = 0;
2929 }
~Whammy()2930 ~Whammy() {
2931 script_.Dispose();
2932 }
getScript()2933 v8::Handle<Script> getScript() {
2934 if (script_.IsEmpty())
2935 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
2936 return Local<Script>(*script_);
2937 }
2938
2939 public:
2940 static const int kObjectCount = 256;
2941 int cursor_;
2942 v8::Persistent<v8::Object> objects_[kObjectCount];
2943 v8::Persistent<Script> script_;
2944 };
2945
HandleWeakReference(v8::Persistent<v8::Value> obj,void * data)2946 static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
2947 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
2948 delete snorkel;
2949 obj.ClearWeak();
2950 }
2951
WhammyPropertyGetter(Local<String> name,const AccessorInfo & info)2952 v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
2953 const AccessorInfo& info) {
2954 Whammy* whammy =
2955 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
2956
2957 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
2958
2959 v8::Handle<v8::Object> obj = v8::Object::New();
2960 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
2961 if (!prev.IsEmpty()) {
2962 prev->Set(v8_str("next"), obj);
2963 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
2964 whammy->objects_[whammy->cursor_].Clear();
2965 }
2966 whammy->objects_[whammy->cursor_] = global;
2967 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
2968 return whammy->getScript()->Run();
2969 }
2970
THREADED_TEST(WeakReference)2971 THREADED_TEST(WeakReference) {
2972 v8::HandleScope handle_scope;
2973 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
2974 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
2975 0, 0, 0, 0,
2976 v8::External::New(new Whammy()));
2977 const char* extension_list[] = { "v8/gc" };
2978 v8::ExtensionConfiguration extensions(1, extension_list);
2979 v8::Persistent<Context> context = Context::New(&extensions);
2980 Context::Scope context_scope(context);
2981
2982 v8::Handle<v8::Object> interceptor = templ->NewInstance();
2983 context->Global()->Set(v8_str("whammy"), interceptor);
2984 const char* code =
2985 "var last;"
2986 "for (var i = 0; i < 10000; i++) {"
2987 " var obj = whammy.length;"
2988 " if (last) last.next = obj;"
2989 " last = obj;"
2990 "}"
2991 "gc();"
2992 "4";
2993 v8::Handle<Value> result = CompileRun(code);
2994 CHECK_EQ(4.0, result->NumberValue());
2995
2996 context.Dispose();
2997 }
2998
2999
3000 v8::Handle<Function> args_fun;
3001
3002
ArgumentsTestCallback(const v8::Arguments & args)3003 static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3004 ApiTestFuzzer::Fuzz();
3005 CHECK_EQ(args_fun, args.Callee());
3006 CHECK_EQ(3, args.Length());
3007 CHECK_EQ(v8::Integer::New(1), args[0]);
3008 CHECK_EQ(v8::Integer::New(2), args[1]);
3009 CHECK_EQ(v8::Integer::New(3), args[2]);
3010 CHECK_EQ(v8::Undefined(), args[3]);
3011 v8::HandleScope scope;
3012 i::Heap::CollectAllGarbage(false);
3013 return v8::Undefined();
3014 }
3015
3016
THREADED_TEST(Arguments)3017 THREADED_TEST(Arguments) {
3018 v8::HandleScope scope;
3019 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3020 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3021 LocalContext context(NULL, global);
3022 args_fun = v8::Handle<Function>::Cast(context->Global()->Get(v8_str("f")));
3023 v8_compile("f(1, 2, 3)")->Run();
3024 }
3025
3026
3027 static int x_register = 0;
3028 static v8::Handle<v8::Object> x_receiver;
3029 static v8::Handle<v8::Object> x_holder;
3030
3031
XGetter(Local<String> name,const AccessorInfo & info)3032 static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) {
3033 ApiTestFuzzer::Fuzz();
3034 CHECK_EQ(x_receiver, info.This());
3035 CHECK_EQ(x_holder, info.Holder());
3036 return v8_num(x_register);
3037 }
3038
3039
XSetter(Local<String> name,Local<Value> value,const AccessorInfo & info)3040 static void XSetter(Local<String> name,
3041 Local<Value> value,
3042 const AccessorInfo& info) {
3043 CHECK_EQ(x_holder, info.This());
3044 CHECK_EQ(x_holder, info.Holder());
3045 x_register = value->Int32Value();
3046 }
3047
3048
THREADED_TEST(AccessorIC)3049 THREADED_TEST(AccessorIC) {
3050 v8::HandleScope scope;
3051 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3052 obj->SetAccessor(v8_str("x"), XGetter, XSetter);
3053 LocalContext context;
3054 x_holder = obj->NewInstance();
3055 context->Global()->Set(v8_str("holder"), x_holder);
3056 x_receiver = v8::Object::New();
3057 context->Global()->Set(v8_str("obj"), x_receiver);
3058 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun(
3059 "obj.__proto__ = holder;"
3060 "var result = [];"
3061 "for (var i = 0; i < 10; i++) {"
3062 " holder.x = i;"
3063 " result.push(obj.x);"
3064 "}"
3065 "result"));
3066 CHECK_EQ(10, array->Length());
3067 for (int i = 0; i < 10; i++) {
3068 v8::Handle<Value> entry = array->Get(v8::Integer::New(i));
3069 CHECK_EQ(v8::Integer::New(i), entry);
3070 }
3071 }
3072
3073
NoBlockGetterX(Local<String> name,const AccessorInfo &)3074 static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3075 const AccessorInfo&) {
3076 return v8::Handle<Value>();
3077 }
3078
3079
NoBlockGetterI(uint32_t index,const AccessorInfo &)3080 static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3081 const AccessorInfo&) {
3082 return v8::Handle<Value>();
3083 }
3084
3085
PDeleter(Local<String> name,const AccessorInfo &)3086 static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3087 const AccessorInfo&) {
3088 if (!name->Equals(v8_str("foo"))) {
3089 return v8::Handle<v8::Boolean>(); // not intercepted
3090 }
3091
3092 return v8::False(); // intercepted, and don't delete the property
3093 }
3094
3095
IDeleter(uint32_t index,const AccessorInfo &)3096 static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
3097 if (index != 2) {
3098 return v8::Handle<v8::Boolean>(); // not intercepted
3099 }
3100
3101 return v8::False(); // intercepted, and don't delete the property
3102 }
3103
3104
THREADED_TEST(Deleter)3105 THREADED_TEST(Deleter) {
3106 v8::HandleScope scope;
3107 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3108 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
3109 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
3110 LocalContext context;
3111 context->Global()->Set(v8_str("k"), obj->NewInstance());
3112 CompileRun(
3113 "k.foo = 'foo';"
3114 "k.bar = 'bar';"
3115 "k[2] = 2;"
3116 "k[4] = 4;");
3117 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
3118 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
3119
3120 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
3121 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
3122
3123 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
3124 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
3125
3126 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
3127 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
3128 }
3129
3130
GetK(Local<String> name,const AccessorInfo &)3131 static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
3132 ApiTestFuzzer::Fuzz();
3133 if (name->Equals(v8_str("foo")) ||
3134 name->Equals(v8_str("bar")) ||
3135 name->Equals(v8_str("baz"))) {
3136 return v8::Undefined();
3137 }
3138 return v8::Handle<Value>();
3139 }
3140
3141
IndexedGetK(uint32_t index,const AccessorInfo &)3142 static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
3143 ApiTestFuzzer::Fuzz();
3144 if (index == 0 || index == 1) return v8::Undefined();
3145 return v8::Handle<Value>();
3146 }
3147
3148
NamedEnum(const AccessorInfo &)3149 static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
3150 ApiTestFuzzer::Fuzz();
3151 v8::Handle<v8::Array> result = v8::Array::New(3);
3152 result->Set(v8::Integer::New(0), v8_str("foo"));
3153 result->Set(v8::Integer::New(1), v8_str("bar"));
3154 result->Set(v8::Integer::New(2), v8_str("baz"));
3155 return result;
3156 }
3157
3158
IndexedEnum(const AccessorInfo &)3159 static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
3160 ApiTestFuzzer::Fuzz();
3161 v8::Handle<v8::Array> result = v8::Array::New(2);
3162 result->Set(v8::Integer::New(0), v8_str("0"));
3163 result->Set(v8::Integer::New(1), v8_str("1"));
3164 return result;
3165 }
3166
3167
THREADED_TEST(Enumerators)3168 THREADED_TEST(Enumerators) {
3169 v8::HandleScope scope;
3170 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3171 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
3172 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
3173 LocalContext context;
3174 context->Global()->Set(v8_str("k"), obj->NewInstance());
3175 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
3176 "k[10] = 0;"
3177 "k.a = 0;"
3178 "k[5] = 0;"
3179 "k.b = 0;"
3180 "k[4294967295] = 0;"
3181 "k.c = 0;"
3182 "k[4294967296] = 0;"
3183 "k.d = 0;"
3184 "k[140000] = 0;"
3185 "k.e = 0;"
3186 "k[30000000000] = 0;"
3187 "k.f = 0;"
3188 "var result = [];"
3189 "for (var prop in k) {"
3190 " result.push(prop);"
3191 "}"
3192 "result"));
3193 // Check that we get all the property names returned including the
3194 // ones from the enumerators in the right order: indexed properties
3195 // in numerical order, indexed interceptor properties, named
3196 // properties in insertion order, named interceptor properties.
3197 // This order is not mandated by the spec, so this test is just
3198 // documenting our behavior.
3199 CHECK_EQ(17, result->Length());
3200 // Indexed properties in numerical order.
3201 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
3202 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
3203 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
3204 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
3205 // Indexed interceptor properties in the order they are returned
3206 // from the enumerator interceptor.
3207 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
3208 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
3209 // Named properties in insertion order.
3210 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
3211 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
3212 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
3213 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
3214 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
3215 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
3216 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
3217 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
3218 // Named interceptor properties.
3219 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
3220 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
3221 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
3222 }
3223
3224
3225 int p_getter_count;
3226 int p_getter_count2;
3227
3228
PGetter(Local<String> name,const AccessorInfo & info)3229 static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
3230 ApiTestFuzzer::Fuzz();
3231 p_getter_count++;
3232 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
3233 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
3234 if (name->Equals(v8_str("p1"))) {
3235 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
3236 } else if (name->Equals(v8_str("p2"))) {
3237 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
3238 } else if (name->Equals(v8_str("p3"))) {
3239 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
3240 } else if (name->Equals(v8_str("p4"))) {
3241 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
3242 }
3243 return v8::Undefined();
3244 }
3245
3246
RunHolderTest(v8::Handle<v8::ObjectTemplate> obj)3247 static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
3248 ApiTestFuzzer::Fuzz();
3249 LocalContext context;
3250 context->Global()->Set(v8_str("o1"), obj->NewInstance());
3251 CompileRun(
3252 "o1.__proto__ = { };"
3253 "var o2 = { __proto__: o1 };"
3254 "var o3 = { __proto__: o2 };"
3255 "var o4 = { __proto__: o3 };"
3256 "for (var i = 0; i < 10; i++) o4.p4;"
3257 "for (var i = 0; i < 10; i++) o3.p3;"
3258 "for (var i = 0; i < 10; i++) o2.p2;"
3259 "for (var i = 0; i < 10; i++) o1.p1;");
3260 }
3261
3262
PGetter2(Local<String> name,const AccessorInfo & info)3263 static v8::Handle<Value> PGetter2(Local<String> name,
3264 const AccessorInfo& info) {
3265 ApiTestFuzzer::Fuzz();
3266 p_getter_count2++;
3267 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
3268 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
3269 if (name->Equals(v8_str("p1"))) {
3270 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
3271 } else if (name->Equals(v8_str("p2"))) {
3272 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
3273 } else if (name->Equals(v8_str("p3"))) {
3274 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
3275 } else if (name->Equals(v8_str("p4"))) {
3276 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
3277 }
3278 return v8::Undefined();
3279 }
3280
3281
THREADED_TEST(GetterHolders)3282 THREADED_TEST(GetterHolders) {
3283 v8::HandleScope scope;
3284 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3285 obj->SetAccessor(v8_str("p1"), PGetter);
3286 obj->SetAccessor(v8_str("p2"), PGetter);
3287 obj->SetAccessor(v8_str("p3"), PGetter);
3288 obj->SetAccessor(v8_str("p4"), PGetter);
3289 p_getter_count = 0;
3290 RunHolderTest(obj);
3291 CHECK_EQ(40, p_getter_count);
3292 }
3293
3294
THREADED_TEST(PreInterceptorHolders)3295 THREADED_TEST(PreInterceptorHolders) {
3296 v8::HandleScope scope;
3297 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3298 obj->SetNamedPropertyHandler(PGetter2);
3299 p_getter_count2 = 0;
3300 RunHolderTest(obj);
3301 CHECK_EQ(40, p_getter_count2);
3302 }
3303
3304
THREADED_TEST(ObjectInstantiation)3305 THREADED_TEST(ObjectInstantiation) {
3306 v8::HandleScope scope;
3307 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
3308 templ->SetAccessor(v8_str("t"), PGetter2);
3309 LocalContext context;
3310 context->Global()->Set(v8_str("o"), templ->NewInstance());
3311 for (int i = 0; i < 100; i++) {
3312 v8::HandleScope inner_scope;
3313 v8::Handle<v8::Object> obj = templ->NewInstance();
3314 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
3315 context->Global()->Set(v8_str("o2"), obj);
3316 v8::Handle<Value> value =
3317 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
3318 CHECK_EQ(v8::True(), value);
3319 context->Global()->Set(v8_str("o"), obj);
3320 }
3321 }
3322
3323
THREADED_TEST(StringWrite)3324 THREADED_TEST(StringWrite) {
3325 v8::HandleScope scope;
3326 v8::Handle<String> str = v8_str("abcde");
3327
3328 char buf[100];
3329 int len;
3330
3331 memset(buf, 0x1, sizeof(buf));
3332 len = str->WriteAscii(buf);
3333 CHECK_EQ(len, 5);
3334 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
3335
3336 memset(buf, 0x1, sizeof(buf));
3337 len = str->WriteAscii(buf, 0, 4);
3338 CHECK_EQ(len, 4);
3339 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
3340
3341 memset(buf, 0x1, sizeof(buf));
3342 len = str->WriteAscii(buf, 0, 5);
3343 CHECK_EQ(len, 5);
3344 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
3345
3346 memset(buf, 0x1, sizeof(buf));
3347 len = str->WriteAscii(buf, 0, 6);
3348 CHECK_EQ(len, 5);
3349 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
3350
3351 memset(buf, 0x1, sizeof(buf));
3352 len = str->WriteAscii(buf, 4, -1);
3353 CHECK_EQ(len, 1);
3354 CHECK_EQ(strncmp("e\0", buf, 2), 0);
3355
3356 memset(buf, 0x1, sizeof(buf));
3357 len = str->WriteAscii(buf, 4, 6);
3358 CHECK_EQ(len, 1);
3359 CHECK_EQ(strncmp("e\0", buf, 2), 0);
3360
3361 memset(buf, 0x1, sizeof(buf));
3362 len = str->WriteAscii(buf, 4, 1);
3363 CHECK_EQ(len, 1);
3364 CHECK_EQ(strncmp("e\1", buf, 2), 0);
3365 }
3366
3367
THREADED_TEST(ToArrayIndex)3368 THREADED_TEST(ToArrayIndex) {
3369 v8::HandleScope scope;
3370 LocalContext context;
3371
3372 v8::Handle<String> str = v8_str("42");
3373 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
3374 CHECK(!index.IsEmpty());
3375 CHECK_EQ(42.0, index->Uint32Value());
3376 str = v8_str("42asdf");
3377 index = str->ToArrayIndex();
3378 CHECK(index.IsEmpty());
3379 str = v8_str("-42");
3380 index = str->ToArrayIndex();
3381 CHECK(index.IsEmpty());
3382 str = v8_str("4294967295");
3383 index = str->ToArrayIndex();
3384 CHECK(!index.IsEmpty());
3385 CHECK_EQ(4294967295.0, index->Uint32Value());
3386 v8::Handle<v8::Number> num = v8::Number::New(1);
3387 index = num->ToArrayIndex();
3388 CHECK(!index.IsEmpty());
3389 CHECK_EQ(1.0, index->Uint32Value());
3390 num = v8::Number::New(-1);
3391 index = num->ToArrayIndex();
3392 CHECK(index.IsEmpty());
3393 v8::Handle<v8::Object> obj = v8::Object::New();
3394 index = obj->ToArrayIndex();
3395 CHECK(index.IsEmpty());
3396 }
3397
3398
THREADED_TEST(ErrorConstruction)3399 THREADED_TEST(ErrorConstruction) {
3400 v8::HandleScope scope;
3401 LocalContext context;
3402
3403 v8::Handle<String> foo = v8_str("foo");
3404 v8::Handle<String> message = v8_str("message");
3405 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
3406 CHECK(range_error->IsObject());
3407 v8::Handle<v8::Object> range_obj(v8::Handle<v8::Object>::Cast(range_error));
3408 CHECK(v8::Handle<v8::Object>::Cast(range_error)->Get(message)->Equals(foo));
3409 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
3410 CHECK(reference_error->IsObject());
3411 CHECK(
3412 v8::Handle<v8::Object>::Cast(reference_error)->Get(message)->Equals(foo));
3413 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
3414 CHECK(syntax_error->IsObject());
3415 CHECK(v8::Handle<v8::Object>::Cast(syntax_error)->Get(message)->Equals(foo));
3416 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
3417 CHECK(type_error->IsObject());
3418 CHECK(v8::Handle<v8::Object>::Cast(type_error)->Get(message)->Equals(foo));
3419 v8::Handle<Value> error = v8::Exception::Error(foo);
3420 CHECK(error->IsObject());
3421 CHECK(v8::Handle<v8::Object>::Cast(error)->Get(message)->Equals(foo));
3422 }
3423
3424
YGetter(Local<String> name,const AccessorInfo & info)3425 static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
3426 ApiTestFuzzer::Fuzz();
3427 return v8_num(10);
3428 }
3429
3430
YSetter(Local<String> name,Local<Value> value,const AccessorInfo & info)3431 static void YSetter(Local<String> name,
3432 Local<Value> value,
3433 const AccessorInfo& info) {
3434 if (info.This()->Has(name)) {
3435 info.This()->Delete(name);
3436 }
3437 info.This()->Set(name, value);
3438 }
3439
3440
THREADED_TEST(DeleteAccessor)3441 THREADED_TEST(DeleteAccessor) {
3442 v8::HandleScope scope;
3443 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3444 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
3445 LocalContext context;
3446 v8::Handle<v8::Object> holder = obj->NewInstance();
3447 context->Global()->Set(v8_str("holder"), holder);
3448 v8::Handle<Value> result = CompileRun(
3449 "holder.y = 11; holder.y = 12; holder.y");
3450 CHECK_EQ(12, result->Uint32Value());
3451 }
3452
3453
THREADED_TEST(TypeSwitch)3454 THREADED_TEST(TypeSwitch) {
3455 v8::HandleScope scope;
3456 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
3457 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
3458 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
3459 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
3460 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
3461 LocalContext context;
3462 v8::Handle<v8::Object> obj0 = v8::Object::New();
3463 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
3464 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
3465 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
3466 for (int i = 0; i < 10; i++) {
3467 CHECK_EQ(0, type_switch->match(obj0));
3468 CHECK_EQ(1, type_switch->match(obj1));
3469 CHECK_EQ(2, type_switch->match(obj2));
3470 CHECK_EQ(3, type_switch->match(obj3));
3471 CHECK_EQ(3, type_switch->match(obj3));
3472 CHECK_EQ(2, type_switch->match(obj2));
3473 CHECK_EQ(1, type_switch->match(obj1));
3474 CHECK_EQ(0, type_switch->match(obj0));
3475 }
3476 }
3477
3478
3479 // For use within the TestSecurityHandler() test.
3480 static bool g_security_callback_result = false;
NamedSecurityTestCallback(Local<v8::Object> global,Local<Value> name,v8::AccessType type,Local<Value> data)3481 static bool NamedSecurityTestCallback(Local<v8::Object> global,
3482 Local<Value> name,
3483 v8::AccessType type,
3484 Local<Value> data) {
3485 // Always allow read access.
3486 if (type == v8::ACCESS_GET)
3487 return true;
3488
3489 // Sometimes allow other access.
3490 return g_security_callback_result;
3491 }
3492
3493
IndexedSecurityTestCallback(Local<v8::Object> global,uint32_t key,v8::AccessType type,Local<Value> data)3494 static bool IndexedSecurityTestCallback(Local<v8::Object> global,
3495 uint32_t key,
3496 v8::AccessType type,
3497 Local<Value> data) {
3498 // Always allow read access.
3499 if (type == v8::ACCESS_GET)
3500 return true;
3501
3502 // Sometimes allow other access.
3503 return g_security_callback_result;
3504 }
3505
3506
3507 static int trouble_nesting = 0;
TroubleCallback(const v8::Arguments & args)3508 static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
3509 ApiTestFuzzer::Fuzz();
3510 trouble_nesting++;
3511
3512 // Call a JS function that throws an uncaught exception.
3513 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
3514 Local<Value> trouble_callee = (trouble_nesting == 3) ?
3515 arg_this->Get(v8_str("trouble_callee")) :
3516 arg_this->Get(v8_str("trouble_caller"));
3517 CHECK(trouble_callee->IsFunction());
3518 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
3519 }
3520
3521
3522 static int report_count = 0;
ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,v8::Handle<Value>)3523 static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
3524 v8::Handle<Value>) {
3525 report_count++;
3526 }
3527
3528
3529 // Counts uncaught exceptions, but other tests running in parallel
3530 // also have uncaught exceptions.
TEST(ApiUncaughtException)3531 TEST(ApiUncaughtException) {
3532 report_count = 0;
3533 v8::HandleScope scope;
3534 LocalContext env;
3535 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
3536
3537 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
3538 v8::Local<v8::Object> global = env->Global();
3539 global->Set(v8_str("trouble"), fun->GetFunction());
3540
3541 Script::Compile(v8_str("function trouble_callee() {"
3542 " var x = null;"
3543 " return x.foo;"
3544 "};"
3545 "function trouble_caller() {"
3546 " trouble();"
3547 "};"))->Run();
3548 Local<Value> trouble = global->Get(v8_str("trouble"));
3549 CHECK(trouble->IsFunction());
3550 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
3551 CHECK(trouble_callee->IsFunction());
3552 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
3553 CHECK(trouble_caller->IsFunction());
3554 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
3555 CHECK_EQ(1, report_count);
3556 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
3557 }
3558
3559
TEST(CompilationErrorUsingTryCatchHandler)3560 TEST(CompilationErrorUsingTryCatchHandler) {
3561 v8::HandleScope scope;
3562 LocalContext env;
3563 v8::TryCatch try_catch;
3564 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
3565 CHECK_NE(NULL, *try_catch.Exception());
3566 CHECK(try_catch.HasCaught());
3567 }
3568
3569
TEST(TryCatchFinallyUsingTryCatchHandler)3570 TEST(TryCatchFinallyUsingTryCatchHandler) {
3571 v8::HandleScope scope;
3572 LocalContext env;
3573 v8::TryCatch try_catch;
3574 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
3575 CHECK(!try_catch.HasCaught());
3576 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
3577 CHECK(try_catch.HasCaught());
3578 try_catch.Reset();
3579 Script::Compile(v8_str("(function() {"
3580 "try { throw ''; } finally { return; }"
3581 "})()"))->Run();
3582 CHECK(!try_catch.HasCaught());
3583 Script::Compile(v8_str("(function()"
3584 " { try { throw ''; } finally { throw 0; }"
3585 "})()"))->Run();
3586 CHECK(try_catch.HasCaught());
3587 }
3588
3589
3590 // SecurityHandler can't be run twice
TEST(SecurityHandler)3591 TEST(SecurityHandler) {
3592 v8::HandleScope scope0;
3593 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
3594 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
3595 IndexedSecurityTestCallback);
3596 // Create an environment
3597 v8::Persistent<Context> context0 =
3598 Context::New(NULL, global_template);
3599 context0->Enter();
3600
3601 v8::Handle<v8::Object> global0 = context0->Global();
3602 v8::Handle<Script> script0 = v8_compile("foo = 111");
3603 script0->Run();
3604 global0->Set(v8_str("0"), v8_num(999));
3605 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
3606 CHECK_EQ(111, foo0->Int32Value());
3607 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
3608 CHECK_EQ(999, z0->Int32Value());
3609
3610 // Create another environment, should fail security checks.
3611 v8::HandleScope scope1;
3612
3613 v8::Persistent<Context> context1 =
3614 Context::New(NULL, global_template);
3615 context1->Enter();
3616
3617 v8::Handle<v8::Object> global1 = context1->Global();
3618 global1->Set(v8_str("othercontext"), global0);
3619 // This set will fail the security check.
3620 v8::Handle<Script> script1 =
3621 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
3622 script1->Run();
3623 // This read will pass the security check.
3624 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
3625 CHECK_EQ(111, foo1->Int32Value());
3626 // This read will pass the security check.
3627 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
3628 CHECK_EQ(999, z1->Int32Value());
3629
3630 // Create another environment, should pass security checks.
3631 { g_security_callback_result = true; // allow security handler to pass.
3632 v8::HandleScope scope2;
3633 LocalContext context2;
3634 v8::Handle<v8::Object> global2 = context2->Global();
3635 global2->Set(v8_str("othercontext"), global0);
3636 v8::Handle<Script> script2 =
3637 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
3638 script2->Run();
3639 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
3640 CHECK_EQ(333, foo2->Int32Value());
3641 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
3642 CHECK_EQ(888, z2->Int32Value());
3643 }
3644
3645 context1->Exit();
3646 context1.Dispose();
3647
3648 context0->Exit();
3649 context0.Dispose();
3650 }
3651
3652
THREADED_TEST(SecurityChecks)3653 THREADED_TEST(SecurityChecks) {
3654 v8::HandleScope handle_scope;
3655 LocalContext env1;
3656 v8::Persistent<Context> env2 = Context::New();
3657
3658 Local<Value> foo = v8_str("foo");
3659 Local<Value> bar = v8_str("bar");
3660
3661 // Set to the same domain.
3662 env1->SetSecurityToken(foo);
3663
3664 // Create a function in env1.
3665 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
3666 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
3667 CHECK(spy->IsFunction());
3668
3669 // Create another function accessing global objects.
3670 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
3671 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
3672 CHECK(spy2->IsFunction());
3673
3674 // Switch to env2 in the same domain and invoke spy on env2.
3675 {
3676 env2->SetSecurityToken(foo);
3677 // Enter env2
3678 Context::Scope scope_env2(env2);
3679 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
3680 CHECK(result->IsFunction());
3681 }
3682
3683 {
3684 env2->SetSecurityToken(bar);
3685 Context::Scope scope_env2(env2);
3686
3687 // Call cross_domain_call, it should throw an exception
3688 v8::TryCatch try_catch;
3689 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
3690 CHECK(try_catch.HasCaught());
3691 }
3692
3693 env2.Dispose();
3694 }
3695
3696
3697 // Regression test case for issue 1183439.
THREADED_TEST(SecurityChecksForPrototypeChain)3698 THREADED_TEST(SecurityChecksForPrototypeChain) {
3699 v8::HandleScope scope;
3700 LocalContext current;
3701 v8::Persistent<Context> other = Context::New();
3702
3703 // Change context to be able to get to the Object function in the
3704 // other context without hitting the security checks.
3705 v8::Local<Value> other_object;
3706 { Context::Scope scope(other);
3707 other_object = other->Global()->Get(v8_str("Object"));
3708 other->Global()->Set(v8_num(42), v8_num(87));
3709 }
3710
3711 current->Global()->Set(v8_str("other"), other->Global());
3712 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
3713
3714 // Make sure the security check fails here and we get an undefined
3715 // result instead of getting the Object function. Repeat in a loop
3716 // to make sure to exercise the IC code.
3717 v8::Local<Script> access_other0 = v8_compile("other.Object");
3718 v8::Local<Script> access_other1 = v8_compile("other[42]");
3719 for (int i = 0; i < 5; i++) {
3720 CHECK(!access_other0->Run()->Equals(other_object));
3721 CHECK(access_other0->Run()->IsUndefined());
3722 CHECK(!access_other1->Run()->Equals(v8_num(87)));
3723 CHECK(access_other1->Run()->IsUndefined());
3724 }
3725
3726 // Create an object that has 'other' in its prototype chain and make
3727 // sure we cannot access the Object function indirectly through
3728 // that. Repeat in a loop to make sure to exercise the IC code.
3729 v8_compile("function F() { };"
3730 "F.prototype = other;"
3731 "var f = new F();")->Run();
3732 v8::Local<Script> access_f0 = v8_compile("f.Object");
3733 v8::Local<Script> access_f1 = v8_compile("f[42]");
3734 for (int j = 0; j < 5; j++) {
3735 CHECK(!access_f0->Run()->Equals(other_object));
3736 CHECK(access_f0->Run()->IsUndefined());
3737 CHECK(!access_f1->Run()->Equals(v8_num(87)));
3738 CHECK(access_f1->Run()->IsUndefined());
3739 }
3740
3741 // Now it gets hairy: Set the prototype for the other global object
3742 // to be the current global object. The prototype chain for 'f' now
3743 // goes through 'other' but ends up in the current global object.
3744 { Context::Scope scope(other);
3745 other->Global()->Set(v8_str("__proto__"), current->Global());
3746 }
3747 // Set a named and an index property on the current global
3748 // object. To force the lookup to go through the other global object,
3749 // the properties must not exist in the other global object.
3750 current->Global()->Set(v8_str("foo"), v8_num(100));
3751 current->Global()->Set(v8_num(99), v8_num(101));
3752 // Try to read the properties from f and make sure that the access
3753 // gets stopped by the security checks on the other global object.
3754 Local<Script> access_f2 = v8_compile("f.foo");
3755 Local<Script> access_f3 = v8_compile("f[99]");
3756 for (int k = 0; k < 5; k++) {
3757 CHECK(!access_f2->Run()->Equals(v8_num(100)));
3758 CHECK(access_f2->Run()->IsUndefined());
3759 CHECK(!access_f3->Run()->Equals(v8_num(101)));
3760 CHECK(access_f3->Run()->IsUndefined());
3761 }
3762 other.Dispose();
3763 }
3764
3765
THREADED_TEST(CrossDomainDelete)3766 THREADED_TEST(CrossDomainDelete) {
3767 v8::HandleScope handle_scope;
3768 LocalContext env1;
3769 v8::Persistent<Context> env2 = Context::New();
3770
3771 Local<Value> foo = v8_str("foo");
3772 Local<Value> bar = v8_str("bar");
3773
3774 // Set to the same domain.
3775 env1->SetSecurityToken(foo);
3776 env2->SetSecurityToken(foo);
3777
3778 env1->Global()->Set(v8_str("prop"), v8_num(3));
3779 env2->Global()->Set(v8_str("env1"), env1->Global());
3780
3781 // Change env2 to a different domain and delete env1.prop.
3782 env2->SetSecurityToken(bar);
3783 {
3784 Context::Scope scope_env2(env2);
3785 Local<Value> result =
3786 Script::Compile(v8_str("delete env1.prop"))->Run();
3787 CHECK(result->IsFalse());
3788 }
3789
3790 // Check that env1.prop still exists.
3791 Local<Value> v = env1->Global()->Get(v8_str("prop"));
3792 CHECK(v->IsNumber());
3793 CHECK_EQ(3, v->Int32Value());
3794
3795 env2.Dispose();
3796 }
3797
3798
THREADED_TEST(CrossDomainIsPropertyEnumerable)3799 THREADED_TEST(CrossDomainIsPropertyEnumerable) {
3800 v8::HandleScope handle_scope;
3801 LocalContext env1;
3802 v8::Persistent<Context> env2 = Context::New();
3803
3804 Local<Value> foo = v8_str("foo");
3805 Local<Value> bar = v8_str("bar");
3806
3807 // Set to the same domain.
3808 env1->SetSecurityToken(foo);
3809 env2->SetSecurityToken(foo);
3810
3811 env1->Global()->Set(v8_str("prop"), v8_num(3));
3812 env2->Global()->Set(v8_str("env1"), env1->Global());
3813
3814 // env1.prop is enumerable in env2.
3815 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
3816 {
3817 Context::Scope scope_env2(env2);
3818 Local<Value> result = Script::Compile(test)->Run();
3819 CHECK(result->IsTrue());
3820 }
3821
3822 // Change env2 to a different domain and test again.
3823 env2->SetSecurityToken(bar);
3824 {
3825 Context::Scope scope_env2(env2);
3826 Local<Value> result = Script::Compile(test)->Run();
3827 CHECK(result->IsFalse());
3828 }
3829
3830 env2.Dispose();
3831 }
3832
3833
THREADED_TEST(CrossDomainForIn)3834 THREADED_TEST(CrossDomainForIn) {
3835 v8::HandleScope handle_scope;
3836 LocalContext env1;
3837 v8::Persistent<Context> env2 = Context::New();
3838
3839 Local<Value> foo = v8_str("foo");
3840 Local<Value> bar = v8_str("bar");
3841
3842 // Set to the same domain.
3843 env1->SetSecurityToken(foo);
3844 env2->SetSecurityToken(foo);
3845
3846 env1->Global()->Set(v8_str("prop"), v8_num(3));
3847 env2->Global()->Set(v8_str("env1"), env1->Global());
3848
3849 // Change env2 to a different domain and set env1's global object
3850 // as the __proto__ of an object in env2 and enumerate properties
3851 // in for-in. It shouldn't enumerate properties on env1's global
3852 // object.
3853 env2->SetSecurityToken(bar);
3854 {
3855 Context::Scope scope_env2(env2);
3856 Local<Value> result =
3857 CompileRun("(function(){var obj = {'__proto__':env1};"
3858 "for (var p in obj)"
3859 " if (p == 'prop') return false;"
3860 "return true;})()");
3861 CHECK(result->IsTrue());
3862 }
3863 env2.Dispose();
3864 }
3865
3866
TEST(ContextDetachGlobal)3867 TEST(ContextDetachGlobal) {
3868 v8::HandleScope handle_scope;
3869 LocalContext env1;
3870 v8::Persistent<Context> env2 = Context::New();
3871
3872 Local<v8::Object> global1 = env1->Global();
3873
3874 Local<Value> foo = v8_str("foo");
3875
3876 // Set to the same domain.
3877 env1->SetSecurityToken(foo);
3878 env2->SetSecurityToken(foo);
3879
3880 // Enter env2
3881 env2->Enter();
3882
3883 // Create a function in env1
3884 Local<v8::Object> global2 = env2->Global();
3885 global2->Set(v8_str("prop"), v8::Integer::New(1));
3886 CompileRun("function getProp() {return prop;}");
3887
3888 env1->Global()->Set(v8_str("getProp"),
3889 global2->Get(v8_str("getProp")));
3890
3891 // Detach env1's global, and reuse the global object of env1
3892 env2->Exit();
3893 env2->DetachGlobal();
3894 // env2 has a new global object.
3895 CHECK(!env2->Global()->Equals(global2));
3896
3897 v8::Persistent<Context> env3 =
3898 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
3899 env3->SetSecurityToken(v8_str("bar"));
3900 env3->Enter();
3901
3902 Local<v8::Object> global3 = env3->Global();
3903 CHECK_EQ(global2, global3);
3904 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
3905 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
3906 global3->Set(v8_str("prop"), v8::Integer::New(-1));
3907 global3->Set(v8_str("prop2"), v8::Integer::New(2));
3908 env3->Exit();
3909
3910 // Call getProp in env1, and it should return the value 1
3911 {
3912 Local<Value> get_prop = global1->Get(v8_str("getProp"));
3913 CHECK(get_prop->IsFunction());
3914 v8::TryCatch try_catch;
3915 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
3916 CHECK(!try_catch.HasCaught());
3917 CHECK_EQ(1, r->Int32Value());
3918 }
3919
3920 // Check that env3 is not accessible from env1
3921 {
3922 Local<Value> r = global3->Get(v8_str("prop2"));
3923 CHECK(r->IsUndefined());
3924 }
3925
3926 env2.Dispose();
3927 env3.Dispose();
3928 }
3929
3930
NamedAccessBlocker(Local<v8::Object> global,Local<Value> name,v8::AccessType type,Local<Value> data)3931 static bool NamedAccessBlocker(Local<v8::Object> global,
3932 Local<Value> name,
3933 v8::AccessType type,
3934 Local<Value> data) {
3935 return Context::GetCurrent()->Global()->Equals(global);
3936 }
3937
3938
IndexedAccessBlocker(Local<v8::Object> global,uint32_t key,v8::AccessType type,Local<Value> data)3939 static bool IndexedAccessBlocker(Local<v8::Object> global,
3940 uint32_t key,
3941 v8::AccessType type,
3942 Local<Value> data) {
3943 return Context::GetCurrent()->Global()->Equals(global);
3944 }
3945
3946
3947 static int g_echo_value = -1;
EchoGetter(Local<String> name,const AccessorInfo & info)3948 static v8::Handle<Value> EchoGetter(Local<String> name,
3949 const AccessorInfo& info) {
3950 return v8_num(g_echo_value);
3951 }
3952
3953
EchoSetter(Local<String> name,Local<Value> value,const AccessorInfo &)3954 static void EchoSetter(Local<String> name,
3955 Local<Value> value,
3956 const AccessorInfo&) {
3957 if (value->IsNumber())
3958 g_echo_value = value->Int32Value();
3959 }
3960
3961
UnreachableGetter(Local<String> name,const AccessorInfo & info)3962 static v8::Handle<Value> UnreachableGetter(Local<String> name,
3963 const AccessorInfo& info) {
3964 CHECK(false); // This function should not be called..
3965 return v8::Undefined();
3966 }
3967
3968
UnreachableSetter(Local<String>,Local<Value>,const AccessorInfo &)3969 static void UnreachableSetter(Local<String>, Local<Value>,
3970 const AccessorInfo&) {
3971 CHECK(false); // This function should nto be called.
3972 }
3973
3974
THREADED_TEST(AccessControl)3975 THREADED_TEST(AccessControl) {
3976 v8::HandleScope handle_scope;
3977 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
3978
3979 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
3980 IndexedAccessBlocker);
3981
3982 // Add an accessor accessible by cross-domain JS code.
3983 global_template->SetAccessor(
3984 v8_str("accessible_prop"),
3985 EchoGetter, EchoSetter,
3986 v8::Handle<Value>(),
3987 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
3988
3989 // Add an accessor that is not accessible by cross-domain JS code.
3990 global_template->SetAccessor(v8_str("blocked_prop"),
3991 UnreachableGetter, UnreachableSetter,
3992 v8::Handle<Value>(),
3993 v8::DEFAULT);
3994
3995 // Create an environment
3996 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
3997 context0->Enter();
3998
3999 v8::Handle<v8::Object> global0 = context0->Global();
4000
4001 v8::HandleScope scope1;
4002
4003 v8::Persistent<Context> context1 = Context::New();
4004 context1->Enter();
4005
4006 v8::Handle<v8::Object> global1 = context1->Global();
4007 global1->Set(v8_str("other"), global0);
4008
4009 v8::Handle<Value> value;
4010
4011 // Access blocked property
4012 value = v8_compile("other.blocked_prop = 1")->Run();
4013 value = v8_compile("other.blocked_prop")->Run();
4014 CHECK(value->IsUndefined());
4015
4016 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4017 CHECK(value->IsFalse());
4018
4019 // Access accessible property
4020 value = v8_compile("other.accessible_prop = 3")->Run();
4021 CHECK(value->IsNumber());
4022 CHECK_EQ(3, value->Int32Value());
4023
4024 value = v8_compile("other.accessible_prop")->Run();
4025 CHECK(value->IsNumber());
4026 CHECK_EQ(3, value->Int32Value());
4027
4028 value =
4029 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
4030 CHECK(value->IsTrue());
4031
4032 // Enumeration doesn't enumerate accessors from inaccessible objects in
4033 // the prototype chain even if the accessors are in themselves accessible.
4034 Local<Value> result =
4035 CompileRun("(function(){var obj = {'__proto__':other};"
4036 "for (var p in obj)"
4037 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
4038 " return false;"
4039 " }"
4040 "return true;})()");
4041 CHECK(result->IsTrue());
4042
4043 context1->Exit();
4044 context0->Exit();
4045 context1.Dispose();
4046 context0.Dispose();
4047 }
4048
4049
ConstTenGetter(Local<String> name,const AccessorInfo & info)4050 static v8::Handle<Value> ConstTenGetter(Local<String> name,
4051 const AccessorInfo& info) {
4052 return v8_num(10);
4053 }
4054
4055
THREADED_TEST(CrossDomainAccessors)4056 THREADED_TEST(CrossDomainAccessors) {
4057 v8::HandleScope handle_scope;
4058
4059 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
4060
4061 v8::Handle<v8::ObjectTemplate> global_template =
4062 func_template->InstanceTemplate();
4063
4064 v8::Handle<v8::ObjectTemplate> proto_template =
4065 func_template->PrototypeTemplate();
4066
4067 // Add an accessor to proto that's accessible by cross-domain JS code.
4068 proto_template->SetAccessor(v8_str("accessible"),
4069 ConstTenGetter, 0,
4070 v8::Handle<Value>(),
4071 v8::ALL_CAN_READ);
4072
4073 // Add an accessor that is not accessible by cross-domain JS code.
4074 global_template->SetAccessor(v8_str("unreachable"),
4075 UnreachableGetter, 0,
4076 v8::Handle<Value>(),
4077 v8::DEFAULT);
4078
4079 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4080 context0->Enter();
4081
4082 Local<v8::Object> global = context0->Global();
4083 // Add a normal property that shadows 'accessible'
4084 global->Set(v8_str("accessible"), v8_num(11));
4085
4086 // Enter a new context.
4087 v8::HandleScope scope1;
4088 v8::Persistent<Context> context1 = Context::New();
4089 context1->Enter();
4090
4091 v8::Handle<v8::Object> global1 = context1->Global();
4092 global1->Set(v8_str("other"), global);
4093
4094 // Should return 10, instead of 11
4095 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
4096 CHECK(value->IsNumber());
4097 CHECK_EQ(10, value->Int32Value());
4098
4099 value = v8_compile("other.unreachable")->Run();
4100 CHECK(value->IsUndefined());
4101
4102 context1->Exit();
4103 context0->Exit();
4104 context1.Dispose();
4105 context0.Dispose();
4106 }
4107
4108
4109 static int named_access_count = 0;
4110 static int indexed_access_count = 0;
4111
NamedAccessCounter(Local<v8::Object> global,Local<Value> name,v8::AccessType type,Local<Value> data)4112 static bool NamedAccessCounter(Local<v8::Object> global,
4113 Local<Value> name,
4114 v8::AccessType type,
4115 Local<Value> data) {
4116 named_access_count++;
4117 return true;
4118 }
4119
4120
IndexedAccessCounter(Local<v8::Object> global,uint32_t key,v8::AccessType type,Local<Value> data)4121 static bool IndexedAccessCounter(Local<v8::Object> global,
4122 uint32_t key,
4123 v8::AccessType type,
4124 Local<Value> data) {
4125 indexed_access_count++;
4126 return true;
4127 }
4128
4129
4130 // This one is too easily disturbed by other tests.
TEST(AccessControlIC)4131 TEST(AccessControlIC) {
4132 named_access_count = 0;
4133 indexed_access_count = 0;
4134
4135 v8::HandleScope handle_scope;
4136
4137 // Create an environment.
4138 v8::Persistent<Context> context0 = Context::New();
4139 context0->Enter();
4140
4141 // Create an object that requires access-check functions to be
4142 // called for cross-domain access.
4143 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4144 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
4145 IndexedAccessCounter);
4146 Local<v8::Object> object = object_template->NewInstance();
4147
4148 v8::HandleScope scope1;
4149
4150 // Create another environment.
4151 v8::Persistent<Context> context1 = Context::New();
4152 context1->Enter();
4153
4154 // Make easy access to the object from the other environment.
4155 v8::Handle<v8::Object> global1 = context1->Global();
4156 global1->Set(v8_str("obj"), object);
4157
4158 v8::Handle<Value> value;
4159
4160 // Check that the named access-control function is called every time.
4161 CompileRun("function testProp(obj) {"
4162 " for (var i = 0; i < 10; i++) obj.prop = 1;"
4163 " for (var j = 0; j < 10; j++) obj.prop;"
4164 " return obj.prop"
4165 "}");
4166 value = CompileRun("testProp(obj)");
4167 CHECK(value->IsNumber());
4168 CHECK_EQ(1, value->Int32Value());
4169 CHECK_EQ(21, named_access_count);
4170
4171 // Check that the named access-control function is called every time.
4172 CompileRun("var p = 'prop';"
4173 "function testKeyed(obj) {"
4174 " for (var i = 0; i < 10; i++) obj[p] = 1;"
4175 " for (var j = 0; j < 10; j++) obj[p];"
4176 " return obj[p];"
4177 "}");
4178 // Use obj which requires access checks. No inline caching is used
4179 // in that case.
4180 value = CompileRun("testKeyed(obj)");
4181 CHECK(value->IsNumber());
4182 CHECK_EQ(1, value->Int32Value());
4183 CHECK_EQ(42, named_access_count);
4184 // Force the inline caches into generic state and try again.
4185 CompileRun("testKeyed({ a: 0 })");
4186 CompileRun("testKeyed({ b: 0 })");
4187 value = CompileRun("testKeyed(obj)");
4188 CHECK(value->IsNumber());
4189 CHECK_EQ(1, value->Int32Value());
4190 CHECK_EQ(63, named_access_count);
4191
4192 // Check that the indexed access-control function is called every time.
4193 CompileRun("function testIndexed(obj) {"
4194 " for (var i = 0; i < 10; i++) obj[0] = 1;"
4195 " for (var j = 0; j < 10; j++) obj[0];"
4196 " return obj[0]"
4197 "}");
4198 value = CompileRun("testIndexed(obj)");
4199 CHECK(value->IsNumber());
4200 CHECK_EQ(1, value->Int32Value());
4201 CHECK_EQ(21, indexed_access_count);
4202 // Force the inline caches into generic state.
4203 CompileRun("testIndexed(new Array(1))");
4204 // Test that the indexed access check is called.
4205 value = CompileRun("testIndexed(obj)");
4206 CHECK(value->IsNumber());
4207 CHECK_EQ(1, value->Int32Value());
4208 CHECK_EQ(42, indexed_access_count);
4209
4210 // Check that the named access check is called when invoking
4211 // functions on an object that requires access checks.
4212 CompileRun("obj.f = function() {}");
4213 CompileRun("function testCallNormal(obj) {"
4214 " for (var i = 0; i < 10; i++) obj.f();"
4215 "}");
4216 CompileRun("testCallNormal(obj)");
4217 CHECK_EQ(74, named_access_count);
4218
4219 // Force obj into slow case.
4220 value = CompileRun("delete obj.prop");
4221 CHECK(value->BooleanValue());
4222 // Force inline caches into dictionary probing mode.
4223 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
4224 // Test that the named access check is called.
4225 value = CompileRun("testProp(obj);");
4226 CHECK(value->IsNumber());
4227 CHECK_EQ(1, value->Int32Value());
4228 CHECK_EQ(96, named_access_count);
4229
4230 // Force the call inline cache into dictionary probing mode.
4231 CompileRun("o.f = function() {}; testCallNormal(o)");
4232 // Test that the named access check is still called for each
4233 // invocation of the function.
4234 value = CompileRun("testCallNormal(obj)");
4235 CHECK_EQ(106, named_access_count);
4236
4237 context1->Exit();
4238 context0->Exit();
4239 context1.Dispose();
4240 context0.Dispose();
4241 }
4242
4243
NamedAccessFlatten(Local<v8::Object> global,Local<Value> name,v8::AccessType type,Local<Value> data)4244 static bool NamedAccessFlatten(Local<v8::Object> global,
4245 Local<Value> name,
4246 v8::AccessType type,
4247 Local<Value> data) {
4248 char buf[100];
4249 int len;
4250
4251 CHECK(name->IsString());
4252
4253 memset(buf, 0x1, sizeof(buf));
4254 len = Local<String>::Cast(name)->WriteAscii(buf);
4255 CHECK_EQ(4, len);
4256
4257 uint16_t buf2[100];
4258
4259 memset(buf, 0x1, sizeof(buf));
4260 len = Local<String>::Cast(name)->Write(buf2);
4261 CHECK_EQ(4, len);
4262
4263 return true;
4264 }
4265
4266
IndexedAccessFlatten(Local<v8::Object> global,uint32_t key,v8::AccessType type,Local<Value> data)4267 static bool IndexedAccessFlatten(Local<v8::Object> global,
4268 uint32_t key,
4269 v8::AccessType type,
4270 Local<Value> data) {
4271 return true;
4272 }
4273
4274
4275 // Regression test. In access checks, operations that may cause
4276 // garbage collection are not allowed. It used to be the case that
4277 // using the Write operation on a string could cause a garbage
4278 // collection due to flattening of the string. This is no longer the
4279 // case.
THREADED_TEST(AccessControlFlatten)4280 THREADED_TEST(AccessControlFlatten) {
4281 named_access_count = 0;
4282 indexed_access_count = 0;
4283
4284 v8::HandleScope handle_scope;
4285
4286 // Create an environment.
4287 v8::Persistent<Context> context0 = Context::New();
4288 context0->Enter();
4289
4290 // Create an object that requires access-check functions to be
4291 // called for cross-domain access.
4292 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4293 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
4294 IndexedAccessFlatten);
4295 Local<v8::Object> object = object_template->NewInstance();
4296
4297 v8::HandleScope scope1;
4298
4299 // Create another environment.
4300 v8::Persistent<Context> context1 = Context::New();
4301 context1->Enter();
4302
4303 // Make easy access to the object from the other environment.
4304 v8::Handle<v8::Object> global1 = context1->Global();
4305 global1->Set(v8_str("obj"), object);
4306
4307 v8::Handle<Value> value;
4308
4309 value = v8_compile("var p = 'as' + 'df';")->Run();
4310 value = v8_compile("obj[p];")->Run();
4311
4312 context1->Exit();
4313 context0->Exit();
4314 context1.Dispose();
4315 context0.Dispose();
4316 }
4317
4318
AccessControlNamedGetter(Local<String>,const AccessorInfo &)4319 static v8::Handle<Value> AccessControlNamedGetter(
4320 Local<String>, const AccessorInfo&) {
4321 return v8::Integer::New(42);
4322 }
4323
4324
AccessControlNamedSetter(Local<String>,Local<Value> value,const AccessorInfo &)4325 static v8::Handle<Value> AccessControlNamedSetter(
4326 Local<String>, Local<Value> value, const AccessorInfo&) {
4327 return value;
4328 }
4329
4330
AccessControlIndexedGetter(uint32_t index,const AccessorInfo & info)4331 static v8::Handle<Value> AccessControlIndexedGetter(
4332 uint32_t index,
4333 const AccessorInfo& info) {
4334 return v8_num(42);
4335 }
4336
4337
AccessControlIndexedSetter(uint32_t,Local<Value> value,const AccessorInfo &)4338 static v8::Handle<Value> AccessControlIndexedSetter(
4339 uint32_t, Local<Value> value, const AccessorInfo&) {
4340 return value;
4341 }
4342
4343
THREADED_TEST(AccessControlInterceptorIC)4344 THREADED_TEST(AccessControlInterceptorIC) {
4345 named_access_count = 0;
4346 indexed_access_count = 0;
4347
4348 v8::HandleScope handle_scope;
4349
4350 // Create an environment.
4351 v8::Persistent<Context> context0 = Context::New();
4352 context0->Enter();
4353
4354 // Create an object that requires access-check functions to be
4355 // called for cross-domain access. The object also has interceptors
4356 // interceptor.
4357 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4358 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
4359 IndexedAccessCounter);
4360 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
4361 AccessControlNamedSetter);
4362 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
4363 AccessControlIndexedSetter);
4364 Local<v8::Object> object = object_template->NewInstance();
4365
4366 v8::HandleScope scope1;
4367
4368 // Create another environment.
4369 v8::Persistent<Context> context1 = Context::New();
4370 context1->Enter();
4371
4372 // Make easy access to the object from the other environment.
4373 v8::Handle<v8::Object> global1 = context1->Global();
4374 global1->Set(v8_str("obj"), object);
4375
4376 v8::Handle<Value> value;
4377
4378 // Check that the named access-control function is called every time
4379 // eventhough there is an interceptor on the object.
4380 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
4381 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
4382 "obj.x")->Run();
4383 CHECK(value->IsNumber());
4384 CHECK_EQ(42, value->Int32Value());
4385 CHECK_EQ(21, named_access_count);
4386
4387 value = v8_compile("var p = 'x';")->Run();
4388 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
4389 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
4390 "obj[p]")->Run();
4391 CHECK(value->IsNumber());
4392 CHECK_EQ(42, value->Int32Value());
4393 CHECK_EQ(42, named_access_count);
4394
4395 // Check that the indexed access-control function is called every
4396 // time eventhough there is an interceptor on the object.
4397 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
4398 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
4399 "obj[0]")->Run();
4400 CHECK(value->IsNumber());
4401 CHECK_EQ(42, value->Int32Value());
4402 CHECK_EQ(21, indexed_access_count);
4403
4404 context1->Exit();
4405 context0->Exit();
4406 context1.Dispose();
4407 context0.Dispose();
4408 }
4409
4410
THREADED_TEST(Version)4411 THREADED_TEST(Version) {
4412 v8::V8::GetVersion();
4413 }
4414
4415
InstanceFunctionCallback(const v8::Arguments & args)4416 static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
4417 ApiTestFuzzer::Fuzz();
4418 return v8_num(12);
4419 }
4420
4421
THREADED_TEST(InstanceProperties)4422 THREADED_TEST(InstanceProperties) {
4423 v8::HandleScope handle_scope;
4424 LocalContext context;
4425
4426 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4427 Local<ObjectTemplate> instance = t->InstanceTemplate();
4428
4429 instance->Set(v8_str("x"), v8_num(42));
4430 instance->Set(v8_str("f"),
4431 v8::FunctionTemplate::New(InstanceFunctionCallback));
4432
4433 Local<Value> o = t->GetFunction()->NewInstance();
4434
4435 context->Global()->Set(v8_str("i"), o);
4436 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
4437 CHECK_EQ(42, value->Int32Value());
4438
4439 value = Script::Compile(v8_str("i.f()"))->Run();
4440 CHECK_EQ(12, value->Int32Value());
4441 }
4442
4443
4444 static v8::Handle<Value>
GlobalObjectInstancePropertiesGet(Local<String> key,const AccessorInfo &)4445 GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
4446 ApiTestFuzzer::Fuzz();
4447 return v8::Handle<Value>();
4448 }
4449
4450
THREADED_TEST(GlobalObjectInstanceProperties)4451 THREADED_TEST(GlobalObjectInstanceProperties) {
4452 v8::HandleScope handle_scope;
4453
4454 Local<Value> global_object;
4455
4456 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4457 t->InstanceTemplate()->SetNamedPropertyHandler(
4458 GlobalObjectInstancePropertiesGet);
4459 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
4460 instance_template->Set(v8_str("x"), v8_num(42));
4461 instance_template->Set(v8_str("f"),
4462 v8::FunctionTemplate::New(InstanceFunctionCallback));
4463
4464 {
4465 LocalContext env(NULL, instance_template);
4466 // Hold on to the global object so it can be used again in another
4467 // environment initialization.
4468 global_object = env->Global();
4469
4470 Local<Value> value = Script::Compile(v8_str("x"))->Run();
4471 CHECK_EQ(42, value->Int32Value());
4472 value = Script::Compile(v8_str("f()"))->Run();
4473 CHECK_EQ(12, value->Int32Value());
4474 }
4475
4476 {
4477 // Create new environment reusing the global object.
4478 LocalContext env(NULL, instance_template, global_object);
4479 Local<Value> value = Script::Compile(v8_str("x"))->Run();
4480 CHECK_EQ(42, value->Int32Value());
4481 value = Script::Compile(v8_str("f()"))->Run();
4482 CHECK_EQ(12, value->Int32Value());
4483 }
4484 }
4485
4486
ShadowFunctionCallback(const v8::Arguments & args)4487 static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
4488 ApiTestFuzzer::Fuzz();
4489 return v8_num(42);
4490 }
4491
4492
4493 static int shadow_y;
4494 static int shadow_y_setter_call_count;
4495 static int shadow_y_getter_call_count;
4496
4497
ShadowYSetter(Local<String>,Local<Value>,const AccessorInfo &)4498 static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
4499 shadow_y_setter_call_count++;
4500 shadow_y = 42;
4501 }
4502
4503
ShadowYGetter(Local<String> name,const AccessorInfo & info)4504 static v8::Handle<Value> ShadowYGetter(Local<String> name,
4505 const AccessorInfo& info) {
4506 ApiTestFuzzer::Fuzz();
4507 shadow_y_getter_call_count++;
4508 return v8_num(shadow_y);
4509 }
4510
4511
ShadowIndexedGet(uint32_t index,const AccessorInfo & info)4512 static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
4513 const AccessorInfo& info) {
4514 return v8::Handle<Value>();
4515 }
4516
4517
ShadowNamedGet(Local<String> key,const AccessorInfo &)4518 static v8::Handle<Value> ShadowNamedGet(Local<String> key,
4519 const AccessorInfo&) {
4520 return v8::Handle<Value>();
4521 }
4522
4523
THREADED_TEST(ShadowObject)4524 THREADED_TEST(ShadowObject) {
4525 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
4526 v8::HandleScope handle_scope;
4527
4528 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
4529 LocalContext context(NULL, global_template);
4530
4531 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4532 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
4533 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
4534 Local<ObjectTemplate> proto = t->PrototypeTemplate();
4535 Local<ObjectTemplate> instance = t->InstanceTemplate();
4536
4537 // Only allow calls of f on instances of t.
4538 Local<v8::Signature> signature = v8::Signature::New(t);
4539 proto->Set(v8_str("f"),
4540 v8::FunctionTemplate::New(ShadowFunctionCallback,
4541 Local<Value>(),
4542 signature));
4543 proto->Set(v8_str("x"), v8_num(12));
4544
4545 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
4546
4547 Local<Value> o = t->GetFunction()->NewInstance();
4548 context->Global()->Set(v8_str("__proto__"), o);
4549
4550 Local<Value> value =
4551 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
4552 CHECK(value->IsBoolean());
4553 CHECK(!value->BooleanValue());
4554
4555 value = Script::Compile(v8_str("x"))->Run();
4556 CHECK_EQ(12, value->Int32Value());
4557
4558 value = Script::Compile(v8_str("f()"))->Run();
4559 CHECK_EQ(42, value->Int32Value());
4560
4561 Script::Compile(v8_str("y = 42"))->Run();
4562 CHECK_EQ(1, shadow_y_setter_call_count);
4563 value = Script::Compile(v8_str("y"))->Run();
4564 CHECK_EQ(1, shadow_y_getter_call_count);
4565 CHECK_EQ(42, value->Int32Value());
4566 }
4567
4568
THREADED_TEST(HiddenPrototype)4569 THREADED_TEST(HiddenPrototype) {
4570 v8::HandleScope handle_scope;
4571 LocalContext context;
4572
4573 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
4574 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
4575 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
4576 t1->SetHiddenPrototype(true);
4577 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
4578 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
4579 t2->SetHiddenPrototype(true);
4580 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
4581 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
4582 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
4583
4584 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
4585 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
4586 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
4587 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
4588
4589 // Setting the prototype on an object skips hidden prototypes.
4590 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4591 o0->Set(v8_str("__proto__"), o1);
4592 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4593 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4594 o0->Set(v8_str("__proto__"), o2);
4595 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4596 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4597 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
4598 o0->Set(v8_str("__proto__"), o3);
4599 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4600 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4601 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
4602 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
4603
4604 // Getting the prototype of o0 should get the first visible one
4605 // which is o3. Therefore, z should not be defined on the prototype
4606 // object.
4607 Local<Value> proto = o0->Get(v8_str("__proto__"));
4608 CHECK(proto->IsObject());
4609 CHECK(Local<v8::Object>::Cast(proto)->Get(v8_str("z"))->IsUndefined());
4610 }
4611
4612
THREADED_TEST(GetterSetterExceptions)4613 THREADED_TEST(GetterSetterExceptions) {
4614 v8::HandleScope handle_scope;
4615 LocalContext context;
4616 CompileRun(
4617 "function Foo() { };"
4618 "function Throw() { throw 5; };"
4619 "var x = { };"
4620 "x.__defineSetter__('set', Throw);"
4621 "x.__defineGetter__('get', Throw);");
4622 Local<v8::Object> x =
4623 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
4624 v8::TryCatch try_catch;
4625 x->Set(v8_str("set"), v8::Integer::New(8));
4626 x->Get(v8_str("get"));
4627 x->Set(v8_str("set"), v8::Integer::New(8));
4628 x->Get(v8_str("get"));
4629 x->Set(v8_str("set"), v8::Integer::New(8));
4630 x->Get(v8_str("get"));
4631 x->Set(v8_str("set"), v8::Integer::New(8));
4632 x->Get(v8_str("get"));
4633 }
4634
4635
THREADED_TEST(Constructor)4636 THREADED_TEST(Constructor) {
4637 v8::HandleScope handle_scope;
4638 LocalContext context;
4639 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
4640 templ->SetClassName(v8_str("Fun"));
4641 Local<Function> cons = templ->GetFunction();
4642 context->Global()->Set(v8_str("Fun"), cons);
4643 Local<v8::Object> inst = cons->NewInstance();
4644 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
4645 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
4646 CHECK(value->BooleanValue());
4647 }
4648
THREADED_TEST(FunctionDescriptorException)4649 THREADED_TEST(FunctionDescriptorException) {
4650 v8::HandleScope handle_scope;
4651 LocalContext context;
4652 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
4653 templ->SetClassName(v8_str("Fun"));
4654 Local<Function> cons = templ->GetFunction();
4655 context->Global()->Set(v8_str("Fun"), cons);
4656 Local<Value> value = CompileRun(
4657 "function test() {"
4658 " try {"
4659 " (new Fun()).blah()"
4660 " } catch (e) {"
4661 " var str = String(e);"
4662 " if (str.indexOf('TypeError') == -1) return 1;"
4663 " if (str.indexOf('[object Fun]') != -1) return 2;"
4664 " if (str.indexOf('#<a Fun>') == -1) return 3;"
4665 " return 0;"
4666 " }"
4667 " return 4;"
4668 "}"
4669 "test();");
4670 CHECK_EQ(0, value->Int32Value());
4671 }
4672
4673
THREADED_TEST(EvalAliasedDynamic)4674 THREADED_TEST(EvalAliasedDynamic) {
4675 v8::HandleScope scope;
4676 LocalContext current;
4677
4678 // Tests where aliased eval can only be resolved dynamically.
4679 Local<Script> script =
4680 Script::Compile(v8_str("function f(x) { "
4681 " var foo = 2;"
4682 " with (x) { return eval('foo'); }"
4683 "}"
4684 "foo = 0;"
4685 "result1 = f(new Object());"
4686 "result2 = f(this);"
4687 "var x = new Object();"
4688 "x.eval = function(x) { return 1; };"
4689 "result3 = f(x);"));
4690 script->Run();
4691 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
4692 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
4693 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
4694
4695 v8::TryCatch try_catch;
4696 script =
4697 Script::Compile(v8_str("function f(x) { "
4698 " var bar = 2;"
4699 " with (x) { return eval('bar'); }"
4700 "}"
4701 "f(this)"));
4702 script->Run();
4703 CHECK(try_catch.HasCaught());
4704 try_catch.Reset();
4705 }
4706
4707
THREADED_TEST(CrossEval)4708 THREADED_TEST(CrossEval) {
4709 v8::HandleScope scope;
4710 LocalContext other;
4711 LocalContext current;
4712
4713 Local<String> token = v8_str("<security token>");
4714 other->SetSecurityToken(token);
4715 current->SetSecurityToken(token);
4716
4717 // Setup reference from current to other.
4718 current->Global()->Set(v8_str("other"), other->Global());
4719
4720 // Check that new variables are introduced in other context.
4721 Local<Script> script =
4722 Script::Compile(v8_str("other.eval('var foo = 1234')"));
4723 script->Run();
4724 Local<Value> foo = other->Global()->Get(v8_str("foo"));
4725 CHECK_EQ(1234, foo->Int32Value());
4726 CHECK(!current->Global()->Has(v8_str("foo")));
4727
4728 // Check that writing to non-existing properties introduces them in
4729 // the other context.
4730 script =
4731 Script::Compile(v8_str("other.eval('na = 1234')"));
4732 script->Run();
4733 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
4734 CHECK(!current->Global()->Has(v8_str("na")));
4735
4736 // Check that global variables in current context are not visible in other
4737 // context.
4738 v8::TryCatch try_catch;
4739 script =
4740 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
4741 Local<Value> result = script->Run();
4742 CHECK(try_catch.HasCaught());
4743 try_catch.Reset();
4744
4745 // Check that local variables in current context are not visible in other
4746 // context.
4747 script =
4748 Script::Compile(v8_str("(function() { "
4749 " var baz = 87;"
4750 " return other.eval('baz');"
4751 "})();"));
4752 result = script->Run();
4753 CHECK(try_catch.HasCaught());
4754 try_catch.Reset();
4755
4756 // Check that global variables in the other environment are visible
4757 // when evaluting code.
4758 other->Global()->Set(v8_str("bis"), v8_num(1234));
4759 script = Script::Compile(v8_str("other.eval('bis')"));
4760 CHECK_EQ(1234, script->Run()->Int32Value());
4761 CHECK(!try_catch.HasCaught());
4762
4763 // Check that the 'this' pointer points to the global object evaluating
4764 // code.
4765 other->Global()->Set(v8_str("t"), other->Global());
4766 script = Script::Compile(v8_str("other.eval('this == t')"));
4767 result = script->Run();
4768 CHECK(result->IsTrue());
4769 CHECK(!try_catch.HasCaught());
4770
4771 // Check that variables introduced in with-statement are not visible in
4772 // other context.
4773 script =
4774 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
4775 result = script->Run();
4776 CHECK(try_catch.HasCaught());
4777 try_catch.Reset();
4778
4779 // Check that you cannot use 'eval.call' with another object than the
4780 // current global object.
4781 script =
4782 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
4783 result = script->Run();
4784 CHECK(try_catch.HasCaught());
4785 }
4786
4787
4788 // Test that calling eval in a context which has been detached from
4789 // its global throws an exception. This behavior is consistent with
4790 // other JavaScript implementations.
THREADED_TEST(EvalInDetachedGlobal)4791 THREADED_TEST(EvalInDetachedGlobal) {
4792 v8::HandleScope scope;
4793
4794 v8::Persistent<Context> context0 = Context::New();
4795 v8::Persistent<Context> context1 = Context::New();
4796
4797 // Setup function in context0 that uses eval from context0.
4798 context0->Enter();
4799 v8::Handle<v8::Value> fun =
4800 CompileRun("var x = 42;"
4801 "(function() {"
4802 " var e = eval;"
4803 " return function(s) { return e(s); }"
4804 "})()");
4805 context0->Exit();
4806
4807 // Put the function into context1 and call it before and after
4808 // detaching the global. Before detaching, the call succeeds and
4809 // after detaching and exception is thrown.
4810 context1->Enter();
4811 context1->Global()->Set(v8_str("fun"), fun);
4812 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
4813 CHECK_EQ(42, x_value->Int32Value());
4814 context0->DetachGlobal();
4815 v8::TryCatch catcher;
4816 x_value = CompileRun("fun('x')");
4817 CHECK(x_value.IsEmpty());
4818 CHECK(catcher.HasCaught());
4819 context1->Exit();
4820
4821 context1.Dispose();
4822 context0.Dispose();
4823 }
4824
4825
THREADED_TEST(CrossLazyLoad)4826 THREADED_TEST(CrossLazyLoad) {
4827 v8::HandleScope scope;
4828 LocalContext other;
4829 LocalContext current;
4830
4831 Local<String> token = v8_str("<security token>");
4832 other->SetSecurityToken(token);
4833 current->SetSecurityToken(token);
4834
4835 // Setup reference from current to other.
4836 current->Global()->Set(v8_str("other"), other->Global());
4837
4838 // Trigger lazy loading in other context.
4839 Local<Script> script =
4840 Script::Compile(v8_str("other.eval('new Date(42)')"));
4841 Local<Value> value = script->Run();
4842 CHECK_EQ(42.0, value->NumberValue());
4843 }
4844
4845
call_as_function(const v8::Arguments & args)4846 static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
4847 ApiTestFuzzer::Fuzz();
4848 if (args.IsConstructCall()) {
4849 if (args[0]->IsInt32()) {
4850 return v8_num(-args[0]->Int32Value());
4851 }
4852 }
4853
4854 return args[0];
4855 }
4856
4857
4858 // Test that a call handler can be set for objects which will allow
4859 // non-function objects created through the API to be called as
4860 // functions.
THREADED_TEST(CallAsFunction)4861 THREADED_TEST(CallAsFunction) {
4862 v8::HandleScope scope;
4863 LocalContext context;
4864
4865 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4866 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
4867 instance_template->SetCallAsFunctionHandler(call_as_function);
4868 Local<v8::Object> instance = t->GetFunction()->NewInstance();
4869 context->Global()->Set(v8_str("obj"), instance);
4870 v8::TryCatch try_catch;
4871 Local<Value> value;
4872 CHECK(!try_catch.HasCaught());
4873
4874 value = CompileRun("obj(42)");
4875 CHECK(!try_catch.HasCaught());
4876 CHECK_EQ(42, value->Int32Value());
4877
4878 value = CompileRun("(function(o){return o(49)})(obj)");
4879 CHECK(!try_catch.HasCaught());
4880 CHECK_EQ(49, value->Int32Value());
4881
4882 // test special case of call as function
4883 value = CompileRun("[obj]['0'](45)");
4884 CHECK(!try_catch.HasCaught());
4885 CHECK_EQ(45, value->Int32Value());
4886
4887 value = CompileRun("obj.call = Function.prototype.call;"
4888 "obj.call(null, 87)");
4889 CHECK(!try_catch.HasCaught());
4890 CHECK_EQ(87, value->Int32Value());
4891
4892 // Regression tests for bug #1116356: Calling call through call/apply
4893 // must work for non-function receivers.
4894 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
4895 value = CompileRun(apply_99);
4896 CHECK(!try_catch.HasCaught());
4897 CHECK_EQ(99, value->Int32Value());
4898
4899 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
4900 value = CompileRun(call_17);
4901 CHECK(!try_catch.HasCaught());
4902 CHECK_EQ(17, value->Int32Value());
4903
4904 // Check that the call-as-function handler can be called through
4905 // new. Currently, there is no way to check in the call-as-function
4906 // handler if it has been called through new or not.
4907 value = CompileRun("new obj(43)");
4908 CHECK(!try_catch.HasCaught());
4909 CHECK_EQ(-43, value->Int32Value());
4910 }
4911
4912
CountHandles()4913 static int CountHandles() {
4914 return v8::HandleScope::NumberOfHandles();
4915 }
4916
4917
Recurse(int depth,int iterations)4918 static int Recurse(int depth, int iterations) {
4919 v8::HandleScope scope;
4920 if (depth == 0) return CountHandles();
4921 for (int i = 0; i < iterations; i++) {
4922 Local<v8::Number> n = v8::Integer::New(42);
4923 }
4924 return Recurse(depth - 1, iterations);
4925 }
4926
4927
THREADED_TEST(HandleIteration)4928 THREADED_TEST(HandleIteration) {
4929 static const int kIterations = 500;
4930 static const int kNesting = 200;
4931 CHECK_EQ(0, CountHandles());
4932 {
4933 v8::HandleScope scope1;
4934 CHECK_EQ(0, CountHandles());
4935 for (int i = 0; i < kIterations; i++) {
4936 Local<v8::Number> n = v8::Integer::New(42);
4937 CHECK_EQ(i + 1, CountHandles());
4938 }
4939
4940 CHECK_EQ(kIterations, CountHandles());
4941 {
4942 v8::HandleScope scope2;
4943 for (int j = 0; j < kIterations; j++) {
4944 Local<v8::Number> n = v8::Integer::New(42);
4945 CHECK_EQ(j + 1 + kIterations, CountHandles());
4946 }
4947 }
4948 CHECK_EQ(kIterations, CountHandles());
4949 }
4950 CHECK_EQ(0, CountHandles());
4951 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
4952 }
4953
4954
InterceptorHasOwnPropertyGetter(Local<String> name,const AccessorInfo & info)4955 static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
4956 Local<String> name,
4957 const AccessorInfo& info) {
4958 ApiTestFuzzer::Fuzz();
4959 return v8::Handle<Value>();
4960 }
4961
4962
THREADED_TEST(InterceptorHasOwnProperty)4963 THREADED_TEST(InterceptorHasOwnProperty) {
4964 v8::HandleScope scope;
4965 LocalContext context;
4966 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
4967 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
4968 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
4969 Local<Function> function = fun_templ->GetFunction();
4970 context->Global()->Set(v8_str("constructor"), function);
4971 v8::Handle<Value> value = CompileRun(
4972 "var o = new constructor();"
4973 "o.hasOwnProperty('ostehaps');");
4974 CHECK_EQ(false, value->BooleanValue());
4975 value = CompileRun(
4976 "o.ostehaps = 42;"
4977 "o.hasOwnProperty('ostehaps');");
4978 CHECK_EQ(true, value->BooleanValue());
4979 value = CompileRun(
4980 "var p = new constructor();"
4981 "p.hasOwnProperty('ostehaps');");
4982 CHECK_EQ(false, value->BooleanValue());
4983 }
4984
4985
InterceptorHasOwnPropertyGetterGC(Local<String> name,const AccessorInfo & info)4986 static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
4987 Local<String> name,
4988 const AccessorInfo& info) {
4989 ApiTestFuzzer::Fuzz();
4990 i::Heap::CollectAllGarbage(false);
4991 return v8::Handle<Value>();
4992 }
4993
4994
THREADED_TEST(InterceptorHasOwnPropertyCausingGC)4995 THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
4996 v8::HandleScope scope;
4997 LocalContext context;
4998 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
4999 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5000 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
5001 Local<Function> function = fun_templ->GetFunction();
5002 context->Global()->Set(v8_str("constructor"), function);
5003 // Let's first make some stuff so we can be sure to get a good GC.
5004 CompileRun(
5005 "function makestr(size) {"
5006 " switch (size) {"
5007 " case 1: return 'f';"
5008 " case 2: return 'fo';"
5009 " case 3: return 'foo';"
5010 " }"
5011 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
5012 "}"
5013 "var x = makestr(12345);"
5014 "x = makestr(31415);"
5015 "x = makestr(23456);");
5016 v8::Handle<Value> value = CompileRun(
5017 "var o = new constructor();"
5018 "o.__proto__ = new String(x);"
5019 "o.hasOwnProperty('ostehaps');");
5020 CHECK_EQ(false, value->BooleanValue());
5021 }
5022
5023
5024 typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
5025 const AccessorInfo& info);
5026
5027
CheckInterceptorLoadIC(NamedPropertyGetter getter,const char * source,int expected)5028 static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
5029 const char* source,
5030 int expected) {
5031 v8::HandleScope scope;
5032 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5033 templ->SetNamedPropertyHandler(getter);
5034 LocalContext context;
5035 context->Global()->Set(v8_str("o"), templ->NewInstance());
5036 v8::Handle<Value> value = CompileRun(source);
5037 CHECK_EQ(expected, value->Int32Value());
5038 }
5039
5040
InterceptorLoadICGetter(Local<String> name,const AccessorInfo & info)5041 static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
5042 const AccessorInfo& info) {
5043 ApiTestFuzzer::Fuzz();
5044 CHECK(v8_str("x")->Equals(name));
5045 return v8::Integer::New(42);
5046 }
5047
5048
5049 // This test should hit the load IC for the interceptor case.
THREADED_TEST(InterceptorLoadIC)5050 THREADED_TEST(InterceptorLoadIC) {
5051 CheckInterceptorLoadIC(InterceptorLoadICGetter,
5052 "var result = 0;"
5053 "for (var i = 0; i < 1000; i++) {"
5054 " result = o.x;"
5055 "}",
5056 42);
5057 }
5058
5059
5060 // Below go several tests which verify that JITing for various
5061 // configurations of interceptor and explicit fields works fine
5062 // (those cases are special cased to get better performance).
5063
InterceptorLoadXICGetter(Local<String> name,const AccessorInfo & info)5064 static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
5065 const AccessorInfo& info) {
5066 ApiTestFuzzer::Fuzz();
5067 return v8_str("x")->Equals(name)
5068 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
5069 }
5070
5071
THREADED_TEST(InterceptorLoadICWithFieldOnHolder)5072 THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
5073 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5074 "var result = 0;"
5075 "o.y = 239;"
5076 "for (var i = 0; i < 1000; i++) {"
5077 " result = o.y;"
5078 "}",
5079 239);
5080 }
5081
5082
THREADED_TEST(InterceptorLoadICWithSubstitutedProto)5083 THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
5084 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5085 "var result = 0;"
5086 "o.__proto__ = { 'y': 239 };"
5087 "for (var i = 0; i < 1000; i++) {"
5088 " result = o.y + o.x;"
5089 "}",
5090 239 + 42);
5091 }
5092
5093
THREADED_TEST(InterceptorLoadICWithPropertyOnProto)5094 THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
5095 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5096 "var result = 0;"
5097 "o.__proto__.y = 239;"
5098 "for (var i = 0; i < 1000; i++) {"
5099 " result = o.y + o.x;"
5100 "}",
5101 239 + 42);
5102 }
5103
5104
THREADED_TEST(InterceptorLoadICUndefined)5105 THREADED_TEST(InterceptorLoadICUndefined) {
5106 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5107 "var result = 0;"
5108 "for (var i = 0; i < 1000; i++) {"
5109 " result = (o.y == undefined) ? 239 : 42;"
5110 "}",
5111 239);
5112 }
5113
5114
THREADED_TEST(InterceptorLoadICWithOverride)5115 THREADED_TEST(InterceptorLoadICWithOverride) {
5116 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5117 "fst = new Object(); fst.__proto__ = o;"
5118 "snd = new Object(); snd.__proto__ = fst;"
5119 "var result1 = 0;"
5120 "for (var i = 0; i < 1000; i++) {"
5121 " result1 = snd.x;"
5122 "}"
5123 "fst.x = 239;"
5124 "var result = 0;"
5125 "for (var i = 0; i < 1000; i++) {"
5126 " result = snd.x;"
5127 "}"
5128 "result + result1",
5129 239 + 42);
5130 }
5131
5132
5133 // Test the case when we stored field into
5134 // a stub, but interceptor produced value on its own.
THREADED_TEST(InterceptorLoadICFieldNotNeeded)5135 THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
5136 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5137 "proto = new Object();"
5138 "o.__proto__ = proto;"
5139 "proto.x = 239;"
5140 "for (var i = 0; i < 1000; i++) {"
5141 " o.x;"
5142 // Now it should be ICed and keep a reference to x defined on proto
5143 "}"
5144 "var result = 0;"
5145 "for (var i = 0; i < 1000; i++) {"
5146 " result += o.x;"
5147 "}"
5148 "result;",
5149 42 * 1000);
5150 }
5151
5152
5153 // Test the case when we stored field into
5154 // a stub, but it got invalidated later on.
THREADED_TEST(InterceptorLoadICInvalidatedField)5155 THREADED_TEST(InterceptorLoadICInvalidatedField) {
5156 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5157 "proto1 = new Object();"
5158 "proto2 = new Object();"
5159 "o.__proto__ = proto1;"
5160 "proto1.__proto__ = proto2;"
5161 "proto2.y = 239;"
5162 "for (var i = 0; i < 1000; i++) {"
5163 " o.y;"
5164 // Now it should be ICed and keep a reference to y defined on proto2
5165 "}"
5166 "proto1.y = 42;"
5167 "var result = 0;"
5168 "for (var i = 0; i < 1000; i++) {"
5169 " result += o.y;"
5170 "}"
5171 "result;",
5172 42 * 1000);
5173 }
5174
5175
5176 // Test the case when we stored field into
5177 // a stub, but it got invalidated later on due to override on
5178 // global object which is between interceptor and fields' holders.
THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal)5179 THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
5180 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5181 "o.__proto__ = this;" // set a global to be a proto of o.
5182 "this.__proto__.y = 239;"
5183 "for (var i = 0; i < 10; i++) {"
5184 " if (o.y != 239) throw 'oops: ' + o.y;"
5185 // Now it should be ICed and keep a reference to y defined on field_holder.
5186 "}"
5187 "this.y = 42;" // Assign on a global.
5188 "var result = 0;"
5189 "for (var i = 0; i < 10; i++) {"
5190 " result += o.y;"
5191 "}"
5192 "result;",
5193 42 * 10);
5194 }
5195
5196
Return239(Local<String> name,const AccessorInfo &)5197 static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
5198 ApiTestFuzzer::Fuzz();
5199 return v8_num(239);
5200 }
5201
5202
SetOnThis(Local<String> name,Local<Value> value,const AccessorInfo & info)5203 static void SetOnThis(Local<String> name,
5204 Local<Value> value,
5205 const AccessorInfo& info) {
5206 info.This()->ForceSet(name, value);
5207 }
5208
5209
THREADED_TEST(InterceptorLoadICWithCallbackOnHolder)5210 THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
5211 v8::HandleScope scope;
5212 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5213 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5214 templ->SetAccessor(v8_str("y"), Return239);
5215 LocalContext context;
5216 context->Global()->Set(v8_str("o"), templ->NewInstance());
5217 v8::Handle<Value> value = CompileRun(
5218 "var result = 0;"
5219 "for (var i = 0; i < 7; i++) {"
5220 " result = o.y;"
5221 "}");
5222 CHECK_EQ(239, value->Int32Value());
5223 }
5224
5225
THREADED_TEST(InterceptorLoadICWithCallbackOnProto)5226 THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
5227 v8::HandleScope scope;
5228 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5229 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5230 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5231 templ_p->SetAccessor(v8_str("y"), Return239);
5232
5233 LocalContext context;
5234 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5235 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5236
5237 v8::Handle<Value> value = CompileRun(
5238 "o.__proto__ = p;"
5239 "var result = 0;"
5240 "for (var i = 0; i < 7; i++) {"
5241 " result = o.x + o.y;"
5242 "}");
5243 CHECK_EQ(239 + 42, value->Int32Value());
5244 }
5245
5246
THREADED_TEST(InterceptorLoadICForCallbackWithOverride)5247 THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
5248 v8::HandleScope scope;
5249 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5250 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5251 templ->SetAccessor(v8_str("y"), Return239);
5252
5253 LocalContext context;
5254 context->Global()->Set(v8_str("o"), templ->NewInstance());
5255
5256 v8::Handle<Value> value = CompileRun(
5257 "fst = new Object(); fst.__proto__ = o;"
5258 "snd = new Object(); snd.__proto__ = fst;"
5259 "var result1 = 0;"
5260 "for (var i = 0; i < 7; i++) {"
5261 " result1 = snd.x;"
5262 "}"
5263 "fst.x = 239;"
5264 "var result = 0;"
5265 "for (var i = 0; i < 7; i++) {"
5266 " result = snd.x;"
5267 "}"
5268 "result + result1");
5269 CHECK_EQ(239 + 42, value->Int32Value());
5270 }
5271
5272
5273 // Test the case when we stored callback into
5274 // a stub, but interceptor produced value on its own.
THREADED_TEST(InterceptorLoadICCallbackNotNeeded)5275 THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
5276 v8::HandleScope scope;
5277 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5278 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5279 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5280 templ_p->SetAccessor(v8_str("y"), Return239);
5281
5282 LocalContext context;
5283 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5284 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5285
5286 v8::Handle<Value> value = CompileRun(
5287 "o.__proto__ = p;"
5288 "for (var i = 0; i < 7; i++) {"
5289 " o.x;"
5290 // Now it should be ICed and keep a reference to x defined on p
5291 "}"
5292 "var result = 0;"
5293 "for (var i = 0; i < 7; i++) {"
5294 " result += o.x;"
5295 "}"
5296 "result");
5297 CHECK_EQ(42 * 7, value->Int32Value());
5298 }
5299
5300
5301 // Test the case when we stored callback into
5302 // a stub, but it got invalidated later on.
THREADED_TEST(InterceptorLoadICInvalidatedCallback)5303 THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
5304 v8::HandleScope scope;
5305 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5306 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5307 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5308 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
5309
5310 LocalContext context;
5311 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5312 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5313
5314 v8::Handle<Value> value = CompileRun(
5315 "inbetween = new Object();"
5316 "o.__proto__ = inbetween;"
5317 "inbetween.__proto__ = p;"
5318 "for (var i = 0; i < 10; i++) {"
5319 " o.y;"
5320 // Now it should be ICed and keep a reference to y defined on p
5321 "}"
5322 "inbetween.y = 42;"
5323 "var result = 0;"
5324 "for (var i = 0; i < 10; i++) {"
5325 " result += o.y;"
5326 "}"
5327 "result");
5328 CHECK_EQ(42 * 10, value->Int32Value());
5329 }
5330
5331
5332 // Test the case when we stored callback into
5333 // a stub, but it got invalidated later on due to override on
5334 // global object which is between interceptor and callbacks' holders.
THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal)5335 THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
5336 v8::HandleScope scope;
5337 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5338 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5339 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5340 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
5341
5342 LocalContext context;
5343 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5344 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5345
5346 v8::Handle<Value> value = CompileRun(
5347 "o.__proto__ = this;"
5348 "this.__proto__ = p;"
5349 "for (var i = 0; i < 10; i++) {"
5350 " if (o.y != 239) throw 'oops: ' + o.y;"
5351 // Now it should be ICed and keep a reference to y defined on p
5352 "}"
5353 "this.y = 42;"
5354 "var result = 0;"
5355 "for (var i = 0; i < 10; i++) {"
5356 " result += o.y;"
5357 "}"
5358 "result");
5359 CHECK_EQ(42 * 10, value->Int32Value());
5360 }
5361
5362
InterceptorLoadICGetter0(Local<String> name,const AccessorInfo & info)5363 static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
5364 const AccessorInfo& info) {
5365 ApiTestFuzzer::Fuzz();
5366 CHECK(v8_str("x")->Equals(name));
5367 return v8::Integer::New(0);
5368 }
5369
5370
THREADED_TEST(InterceptorReturningZero)5371 THREADED_TEST(InterceptorReturningZero) {
5372 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
5373 "o.x == undefined ? 1 : 0",
5374 0);
5375 }
5376
5377
InterceptorStoreICSetter(Local<String> key,Local<Value> value,const AccessorInfo &)5378 static v8::Handle<Value> InterceptorStoreICSetter(
5379 Local<String> key, Local<Value> value, const AccessorInfo&) {
5380 CHECK(v8_str("x")->Equals(key));
5381 CHECK_EQ(42, value->Int32Value());
5382 return value;
5383 }
5384
5385
5386 // This test should hit the store IC for the interceptor case.
THREADED_TEST(InterceptorStoreIC)5387 THREADED_TEST(InterceptorStoreIC) {
5388 v8::HandleScope scope;
5389 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5390 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
5391 InterceptorStoreICSetter);
5392 LocalContext context;
5393 context->Global()->Set(v8_str("o"), templ->NewInstance());
5394 v8::Handle<Value> value = CompileRun(
5395 "for (var i = 0; i < 1000; i++) {"
5396 " o.x = 42;"
5397 "}");
5398 }
5399
5400
THREADED_TEST(InterceptorStoreICWithNoSetter)5401 THREADED_TEST(InterceptorStoreICWithNoSetter) {
5402 v8::HandleScope scope;
5403 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5404 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5405 LocalContext context;
5406 context->Global()->Set(v8_str("o"), templ->NewInstance());
5407 v8::Handle<Value> value = CompileRun(
5408 "for (var i = 0; i < 1000; i++) {"
5409 " o.y = 239;"
5410 "}"
5411 "42 + o.y");
5412 CHECK_EQ(239 + 42, value->Int32Value());
5413 }
5414
5415
5416
5417
5418 v8::Handle<Value> call_ic_function;
5419 v8::Handle<Value> call_ic_function2;
5420 v8::Handle<Value> call_ic_function3;
5421
InterceptorCallICGetter(Local<String> name,const AccessorInfo & info)5422 static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
5423 const AccessorInfo& info) {
5424 ApiTestFuzzer::Fuzz();
5425 CHECK(v8_str("x")->Equals(name));
5426 return call_ic_function;
5427 }
5428
5429
5430 // This test should hit the call IC for the interceptor case.
THREADED_TEST(InterceptorCallIC)5431 THREADED_TEST(InterceptorCallIC) {
5432 v8::HandleScope scope;
5433 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5434 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
5435 LocalContext context;
5436 context->Global()->Set(v8_str("o"), templ->NewInstance());
5437 call_ic_function =
5438 v8_compile("function f(x) { return x + 1; }; f")->Run();
5439 v8::Handle<Value> value = CompileRun(
5440 "var result = 0;"
5441 "for (var i = 0; i < 1000; i++) {"
5442 " result = o.x(41);"
5443 "}");
5444 CHECK_EQ(42, value->Int32Value());
5445 }
5446
5447
5448 // This test checks that if interceptor doesn't provide
5449 // a value, we can fetch regular value.
THREADED_TEST(InterceptorCallICSeesOthers)5450 THREADED_TEST(InterceptorCallICSeesOthers) {
5451 v8::HandleScope scope;
5452 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5453 templ->SetNamedPropertyHandler(NoBlockGetterX);
5454 LocalContext context;
5455 context->Global()->Set(v8_str("o"), templ->NewInstance());
5456 v8::Handle<Value> value = CompileRun(
5457 "o.x = function f(x) { return x + 1; };"
5458 "var result = 0;"
5459 "for (var i = 0; i < 7; i++) {"
5460 " result = o.x(41);"
5461 "}");
5462 CHECK_EQ(42, value->Int32Value());
5463 }
5464
5465
5466 static v8::Handle<Value> call_ic_function4;
InterceptorCallICGetter4(Local<String> name,const AccessorInfo & info)5467 static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
5468 const AccessorInfo& info) {
5469 ApiTestFuzzer::Fuzz();
5470 CHECK(v8_str("x")->Equals(name));
5471 return call_ic_function4;
5472 }
5473
5474
5475 // This test checks that if interceptor provides a function,
5476 // even if we cached shadowed variant, interceptor's function
5477 // is invoked
THREADED_TEST(InterceptorCallICCacheableNotNeeded)5478 THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
5479 v8::HandleScope scope;
5480 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5481 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
5482 LocalContext context;
5483 context->Global()->Set(v8_str("o"), templ->NewInstance());
5484 call_ic_function4 =
5485 v8_compile("function f(x) { return x - 1; }; f")->Run();
5486 v8::Handle<Value> value = CompileRun(
5487 "o.__proto__.x = function(x) { return x + 1; };"
5488 "var result = 0;"
5489 "for (var i = 0; i < 1000; i++) {"
5490 " result = o.x(42);"
5491 "}");
5492 CHECK_EQ(41, value->Int32Value());
5493 }
5494
5495
5496 // Test the case when we stored cacheable lookup into
5497 // a stub, but it got invalidated later on
THREADED_TEST(InterceptorCallICInvalidatedCacheable)5498 THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
5499 v8::HandleScope scope;
5500 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5501 templ->SetNamedPropertyHandler(NoBlockGetterX);
5502 LocalContext context;
5503 context->Global()->Set(v8_str("o"), templ->NewInstance());
5504 v8::Handle<Value> value = CompileRun(
5505 "proto1 = new Object();"
5506 "proto2 = new Object();"
5507 "o.__proto__ = proto1;"
5508 "proto1.__proto__ = proto2;"
5509 "proto2.y = function(x) { return x + 1; };"
5510 // Invoke it many times to compile a stub
5511 "for (var i = 0; i < 7; i++) {"
5512 " o.y(42);"
5513 "}"
5514 "proto1.y = function(x) { return x - 1; };"
5515 "var result = 0;"
5516 "for (var i = 0; i < 7; i++) {"
5517 " result += o.y(42);"
5518 "}");
5519 CHECK_EQ(41 * 7, value->Int32Value());
5520 }
5521
5522
5523 static v8::Handle<Value> call_ic_function5;
InterceptorCallICGetter5(Local<String> name,const AccessorInfo & info)5524 static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
5525 const AccessorInfo& info) {
5526 ApiTestFuzzer::Fuzz();
5527 if (v8_str("x")->Equals(name))
5528 return call_ic_function5;
5529 else
5530 return Local<Value>();
5531 }
5532
5533
5534 // This test checks that if interceptor doesn't provide a function,
5535 // cached constant function is used
THREADED_TEST(InterceptorCallICConstantFunctionUsed)5536 THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
5537 v8::HandleScope scope;
5538 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5539 templ->SetNamedPropertyHandler(NoBlockGetterX);
5540 LocalContext context;
5541 context->Global()->Set(v8_str("o"), templ->NewInstance());
5542 v8::Handle<Value> value = CompileRun(
5543 "function inc(x) { return x + 1; };"
5544 "inc(1);"
5545 "o.x = inc;"
5546 "var result = 0;"
5547 "for (var i = 0; i < 1000; i++) {"
5548 " result = o.x(42);"
5549 "}");
5550 CHECK_EQ(43, value->Int32Value());
5551 }
5552
5553
5554 // This test checks that if interceptor provides a function,
5555 // even if we cached constant function, interceptor's function
5556 // is invoked
THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded)5557 THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
5558 v8::HandleScope scope;
5559 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5560 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
5561 LocalContext context;
5562 context->Global()->Set(v8_str("o"), templ->NewInstance());
5563 call_ic_function5 =
5564 v8_compile("function f(x) { return x - 1; }; f")->Run();
5565 v8::Handle<Value> value = CompileRun(
5566 "function inc(x) { return x + 1; };"
5567 "inc(1);"
5568 "o.x = inc;"
5569 "var result = 0;"
5570 "for (var i = 0; i < 1000; i++) {"
5571 " result = o.x(42);"
5572 "}");
5573 CHECK_EQ(41, value->Int32Value());
5574 }
5575
5576
5577 // Test the case when we stored constant function into
5578 // a stub, but it got invalidated later on
THREADED_TEST(InterceptorCallICInvalidatedConstantFunction)5579 THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
5580 v8::HandleScope scope;
5581 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5582 templ->SetNamedPropertyHandler(NoBlockGetterX);
5583 LocalContext context;
5584 context->Global()->Set(v8_str("o"), templ->NewInstance());
5585 v8::Handle<Value> value = CompileRun(
5586 "function inc(x) { return x + 1; };"
5587 "inc(1);"
5588 "proto1 = new Object();"
5589 "proto2 = new Object();"
5590 "o.__proto__ = proto1;"
5591 "proto1.__proto__ = proto2;"
5592 "proto2.y = inc;"
5593 // Invoke it many times to compile a stub
5594 "for (var i = 0; i < 7; i++) {"
5595 " o.y(42);"
5596 "}"
5597 "proto1.y = function(x) { return x - 1; };"
5598 "var result = 0;"
5599 "for (var i = 0; i < 7; i++) {"
5600 " result += o.y(42);"
5601 "}");
5602 CHECK_EQ(41 * 7, value->Int32Value());
5603 }
5604
5605
5606 // Test the case when we stored constant function into
5607 // a stub, but it got invalidated later on due to override on
5608 // global object which is between interceptor and constant function' holders.
THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal)5609 THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
5610 v8::HandleScope scope;
5611 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5612 templ->SetNamedPropertyHandler(NoBlockGetterX);
5613 LocalContext context;
5614 context->Global()->Set(v8_str("o"), templ->NewInstance());
5615 v8::Handle<Value> value = CompileRun(
5616 "function inc(x) { return x + 1; };"
5617 "inc(1);"
5618 "o.__proto__ = this;"
5619 "this.__proto__.y = inc;"
5620 // Invoke it many times to compile a stub
5621 "for (var i = 0; i < 7; i++) {"
5622 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
5623 "}"
5624 "this.y = function(x) { return x - 1; };"
5625 "var result = 0;"
5626 "for (var i = 0; i < 7; i++) {"
5627 " result += o.y(42);"
5628 "}");
5629 CHECK_EQ(41 * 7, value->Int32Value());
5630 }
5631
5632
5633 static int interceptor_call_count = 0;
5634
InterceptorICRefErrorGetter(Local<String> name,const AccessorInfo & info)5635 static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
5636 const AccessorInfo& info) {
5637 ApiTestFuzzer::Fuzz();
5638 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
5639 return call_ic_function2;
5640 }
5641 return v8::Handle<Value>();
5642 }
5643
5644
5645 // This test should hit load and call ICs for the interceptor case.
5646 // Once in a while, the interceptor will reply that a property was not
5647 // found in which case we should get a reference error.
THREADED_TEST(InterceptorICReferenceErrors)5648 THREADED_TEST(InterceptorICReferenceErrors) {
5649 v8::HandleScope scope;
5650 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5651 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
5652 LocalContext context(0, templ, v8::Handle<Value>());
5653 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
5654 v8::Handle<Value> value = CompileRun(
5655 "function f() {"
5656 " for (var i = 0; i < 1000; i++) {"
5657 " try { x; } catch(e) { return true; }"
5658 " }"
5659 " return false;"
5660 "};"
5661 "f();");
5662 CHECK_EQ(true, value->BooleanValue());
5663 interceptor_call_count = 0;
5664 value = CompileRun(
5665 "function g() {"
5666 " for (var i = 0; i < 1000; i++) {"
5667 " try { x(42); } catch(e) { return true; }"
5668 " }"
5669 " return false;"
5670 "};"
5671 "g();");
5672 CHECK_EQ(true, value->BooleanValue());
5673 }
5674
5675
5676 static int interceptor_ic_exception_get_count = 0;
5677
InterceptorICExceptionGetter(Local<String> name,const AccessorInfo & info)5678 static v8::Handle<Value> InterceptorICExceptionGetter(
5679 Local<String> name,
5680 const AccessorInfo& info) {
5681 ApiTestFuzzer::Fuzz();
5682 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
5683 return call_ic_function3;
5684 }
5685 if (interceptor_ic_exception_get_count == 20) {
5686 return v8::ThrowException(v8_num(42));
5687 }
5688 // Do not handle get for properties other than x.
5689 return v8::Handle<Value>();
5690 }
5691
5692 // Test interceptor load/call IC where the interceptor throws an
5693 // exception once in a while.
THREADED_TEST(InterceptorICGetterExceptions)5694 THREADED_TEST(InterceptorICGetterExceptions) {
5695 interceptor_ic_exception_get_count = 0;
5696 v8::HandleScope scope;
5697 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5698 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
5699 LocalContext context(0, templ, v8::Handle<Value>());
5700 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
5701 v8::Handle<Value> value = CompileRun(
5702 "function f() {"
5703 " for (var i = 0; i < 100; i++) {"
5704 " try { x; } catch(e) { return true; }"
5705 " }"
5706 " return false;"
5707 "};"
5708 "f();");
5709 CHECK_EQ(true, value->BooleanValue());
5710 interceptor_ic_exception_get_count = 0;
5711 value = CompileRun(
5712 "function f() {"
5713 " for (var i = 0; i < 100; i++) {"
5714 " try { x(42); } catch(e) { return true; }"
5715 " }"
5716 " return false;"
5717 "};"
5718 "f();");
5719 CHECK_EQ(true, value->BooleanValue());
5720 }
5721
5722
5723 static int interceptor_ic_exception_set_count = 0;
5724
InterceptorICExceptionSetter(Local<String> key,Local<Value> value,const AccessorInfo &)5725 static v8::Handle<Value> InterceptorICExceptionSetter(
5726 Local<String> key, Local<Value> value, const AccessorInfo&) {
5727 ApiTestFuzzer::Fuzz();
5728 if (++interceptor_ic_exception_set_count > 20) {
5729 return v8::ThrowException(v8_num(42));
5730 }
5731 // Do not actually handle setting.
5732 return v8::Handle<Value>();
5733 }
5734
5735 // Test interceptor store IC where the interceptor throws an exception
5736 // once in a while.
THREADED_TEST(InterceptorICSetterExceptions)5737 THREADED_TEST(InterceptorICSetterExceptions) {
5738 interceptor_ic_exception_set_count = 0;
5739 v8::HandleScope scope;
5740 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5741 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
5742 LocalContext context(0, templ, v8::Handle<Value>());
5743 v8::Handle<Value> value = CompileRun(
5744 "function f() {"
5745 " for (var i = 0; i < 100; i++) {"
5746 " try { x = 42; } catch(e) { return true; }"
5747 " }"
5748 " return false;"
5749 "};"
5750 "f();");
5751 CHECK_EQ(true, value->BooleanValue());
5752 }
5753
5754
5755 // Test that we ignore null interceptors.
THREADED_TEST(NullNamedInterceptor)5756 THREADED_TEST(NullNamedInterceptor) {
5757 v8::HandleScope scope;
5758 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5759 templ->SetNamedPropertyHandler(0);
5760 LocalContext context;
5761 templ->Set("x", v8_num(42));
5762 v8::Handle<v8::Object> obj = templ->NewInstance();
5763 context->Global()->Set(v8_str("obj"), obj);
5764 v8::Handle<Value> value = CompileRun("obj.x");
5765 CHECK(value->IsInt32());
5766 CHECK_EQ(42, value->Int32Value());
5767 }
5768
5769
5770 // Test that we ignore null interceptors.
THREADED_TEST(NullIndexedInterceptor)5771 THREADED_TEST(NullIndexedInterceptor) {
5772 v8::HandleScope scope;
5773 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5774 templ->SetIndexedPropertyHandler(0);
5775 LocalContext context;
5776 templ->Set("42", v8_num(42));
5777 v8::Handle<v8::Object> obj = templ->NewInstance();
5778 context->Global()->Set(v8_str("obj"), obj);
5779 v8::Handle<Value> value = CompileRun("obj[42]");
5780 CHECK(value->IsInt32());
5781 CHECK_EQ(42, value->Int32Value());
5782 }
5783
5784
ParentGetter(Local<String> name,const AccessorInfo & info)5785 static v8::Handle<Value> ParentGetter(Local<String> name,
5786 const AccessorInfo& info) {
5787 ApiTestFuzzer::Fuzz();
5788 return v8_num(1);
5789 }
5790
5791
ChildGetter(Local<String> name,const AccessorInfo & info)5792 static v8::Handle<Value> ChildGetter(Local<String> name,
5793 const AccessorInfo& info) {
5794 ApiTestFuzzer::Fuzz();
5795 return v8_num(42);
5796 }
5797
5798
THREADED_TEST(Overriding)5799 THREADED_TEST(Overriding) {
5800 v8::HandleScope scope;
5801 LocalContext context;
5802
5803 // Parent template.
5804 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
5805 Local<ObjectTemplate> parent_instance_templ =
5806 parent_templ->InstanceTemplate();
5807 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
5808
5809 // Template that inherits from the parent template.
5810 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
5811 Local<ObjectTemplate> child_instance_templ =
5812 child_templ->InstanceTemplate();
5813 child_templ->Inherit(parent_templ);
5814 // Override 'f'. The child version of 'f' should get called for child
5815 // instances.
5816 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
5817 // Add 'g' twice. The 'g' added last should get called for instances.
5818 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
5819 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
5820
5821 // Add 'h' as an accessor to the proto template with ReadOnly attributes
5822 // so 'h' can be shadowed on the instance object.
5823 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
5824 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
5825 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
5826
5827 // Add 'i' as an accessor to the instance template with ReadOnly attributes
5828 // but the attribute does not have effect because it is duplicated with
5829 // NULL setter.
5830 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
5831 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
5832
5833
5834
5835 // Instantiate the child template.
5836 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
5837
5838 // Check that the child function overrides the parent one.
5839 context->Global()->Set(v8_str("o"), instance);
5840 Local<Value> value = v8_compile("o.f")->Run();
5841 // Check that the 'g' that was added last is hit.
5842 CHECK_EQ(42, value->Int32Value());
5843 value = v8_compile("o.g")->Run();
5844 CHECK_EQ(42, value->Int32Value());
5845
5846 // Check 'h' can be shadowed.
5847 value = v8_compile("o.h = 3; o.h")->Run();
5848 CHECK_EQ(3, value->Int32Value());
5849
5850 // Check 'i' is cannot be shadowed or changed.
5851 value = v8_compile("o.i = 3; o.i")->Run();
5852 CHECK_EQ(42, value->Int32Value());
5853 }
5854
5855
IsConstructHandler(const v8::Arguments & args)5856 static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
5857 ApiTestFuzzer::Fuzz();
5858 if (args.IsConstructCall()) {
5859 return v8::Boolean::New(true);
5860 }
5861 return v8::Boolean::New(false);
5862 }
5863
5864
THREADED_TEST(IsConstructCall)5865 THREADED_TEST(IsConstructCall) {
5866 v8::HandleScope scope;
5867
5868 // Function template with call handler.
5869 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5870 templ->SetCallHandler(IsConstructHandler);
5871
5872 LocalContext context;
5873
5874 context->Global()->Set(v8_str("f"), templ->GetFunction());
5875 Local<Value> value = v8_compile("f()")->Run();
5876 CHECK(!value->BooleanValue());
5877 value = v8_compile("new f()")->Run();
5878 CHECK(value->BooleanValue());
5879 }
5880
5881
THREADED_TEST(ObjectProtoToString)5882 THREADED_TEST(ObjectProtoToString) {
5883 v8::HandleScope scope;
5884 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5885 templ->SetClassName(v8_str("MyClass"));
5886
5887 LocalContext context;
5888
5889 Local<String> customized_tostring = v8_str("customized toString");
5890
5891 // Replace Object.prototype.toString
5892 v8_compile("Object.prototype.toString = function() {"
5893 " return 'customized toString';"
5894 "}")->Run();
5895
5896 // Normal ToString call should call replaced Object.prototype.toString
5897 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
5898 Local<String> value = instance->ToString();
5899 CHECK(value->IsString() && value->Equals(customized_tostring));
5900
5901 // ObjectProtoToString should not call replace toString function.
5902 value = instance->ObjectProtoToString();
5903 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
5904
5905 // Check global
5906 value = context->Global()->ObjectProtoToString();
5907 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
5908
5909 // Check ordinary object
5910 Local<Value> object = v8_compile("new Object()")->Run();
5911 value = Local<v8::Object>::Cast(object)->ObjectProtoToString();
5912 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
5913 }
5914
5915
5916 bool ApiTestFuzzer::fuzzing_ = false;
5917 v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_=
5918 v8::internal::OS::CreateSemaphore(0);
5919 int ApiTestFuzzer::active_tests_;
5920 int ApiTestFuzzer::tests_being_run_;
5921 int ApiTestFuzzer::current_;
5922
5923
5924 // We are in a callback and want to switch to another thread (if we
5925 // are currently running the thread fuzzing test).
Fuzz()5926 void ApiTestFuzzer::Fuzz() {
5927 if (!fuzzing_) return;
5928 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
5929 test->ContextSwitch();
5930 }
5931
5932
5933 // Let the next thread go. Since it is also waiting on the V8 lock it may
5934 // not start immediately.
NextThread()5935 bool ApiTestFuzzer::NextThread() {
5936 int test_position = GetNextTestNumber();
5937 int test_number = RegisterThreadedTest::nth(current_)->fuzzer_->test_number_;
5938 if (test_position == current_) {
5939 printf("Stay with %d\n", test_number);
5940 return false;
5941 }
5942 printf("Switch from %d to %d\n",
5943 current_ < 0 ? 0 : test_number, test_position < 0 ? 0 : test_number);
5944 current_ = test_position;
5945 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
5946 return true;
5947 }
5948
5949
Run()5950 void ApiTestFuzzer::Run() {
5951 // When it is our turn...
5952 gate_->Wait();
5953 {
5954 // ... get the V8 lock and start running the test.
5955 v8::Locker locker;
5956 CallTest();
5957 }
5958 // This test finished.
5959 active_ = false;
5960 active_tests_--;
5961 // If it was the last then signal that fact.
5962 if (active_tests_ == 0) {
5963 all_tests_done_->Signal();
5964 } else {
5965 // Otherwise select a new test and start that.
5966 NextThread();
5967 }
5968 }
5969
5970
5971 static unsigned linear_congruential_generator;
5972
5973
Setup(PartOfTest part)5974 void ApiTestFuzzer::Setup(PartOfTest part) {
5975 linear_congruential_generator = i::FLAG_testing_prng_seed;
5976 fuzzing_ = true;
5977 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
5978 int end = (part == FIRST_PART)
5979 ? (RegisterThreadedTest::count() >> 1)
5980 : RegisterThreadedTest::count();
5981 active_tests_ = tests_being_run_ = end - start;
5982 for (int i = 0; i < tests_being_run_; i++) {
5983 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
5984 }
5985 for (int i = 0; i < active_tests_; i++) {
5986 RegisterThreadedTest::nth(i)->fuzzer_->Start();
5987 }
5988 }
5989
5990
CallTestNumber(int test_number)5991 static void CallTestNumber(int test_number) {
5992 (RegisterThreadedTest::nth(test_number)->callback())();
5993 }
5994
5995
RunAllTests()5996 void ApiTestFuzzer::RunAllTests() {
5997 // Set off the first test.
5998 current_ = -1;
5999 NextThread();
6000 // Wait till they are all done.
6001 all_tests_done_->Wait();
6002 }
6003
6004
GetNextTestNumber()6005 int ApiTestFuzzer::GetNextTestNumber() {
6006 int next_test;
6007 do {
6008 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
6009 linear_congruential_generator *= 1664525u;
6010 linear_congruential_generator += 1013904223u;
6011 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
6012 return next_test;
6013 }
6014
6015
ContextSwitch()6016 void ApiTestFuzzer::ContextSwitch() {
6017 // If the new thread is the same as the current thread there is nothing to do.
6018 if (NextThread()) {
6019 // Now it can start.
6020 v8::Unlocker unlocker;
6021 // Wait till someone starts us again.
6022 gate_->Wait();
6023 // And we're off.
6024 }
6025 }
6026
6027
TearDown()6028 void ApiTestFuzzer::TearDown() {
6029 fuzzing_ = false;
6030 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
6031 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
6032 if (fuzzer != NULL) fuzzer->Join();
6033 }
6034 }
6035
6036
6037 // Lets not be needlessly self-referential.
TEST(Threading)6038 TEST(Threading) {
6039 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
6040 ApiTestFuzzer::RunAllTests();
6041 ApiTestFuzzer::TearDown();
6042 }
6043
TEST(Threading2)6044 TEST(Threading2) {
6045 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
6046 ApiTestFuzzer::RunAllTests();
6047 ApiTestFuzzer::TearDown();
6048 }
6049
6050
CallTest()6051 void ApiTestFuzzer::CallTest() {
6052 printf("Start test %d\n", test_number_);
6053 CallTestNumber(test_number_);
6054 printf("End test %d\n", test_number_);
6055 }
6056
6057
ThrowInJS(const v8::Arguments & args)6058 static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
6059 CHECK(v8::Locker::IsLocked());
6060 ApiTestFuzzer::Fuzz();
6061 v8::Unlocker unlocker;
6062 const char* code = "throw 7;";
6063 {
6064 v8::Locker nested_locker;
6065 v8::HandleScope scope;
6066 v8::Handle<Value> exception;
6067 { v8::TryCatch try_catch;
6068 v8::Handle<Value> value = CompileRun(code);
6069 CHECK(value.IsEmpty());
6070 CHECK(try_catch.HasCaught());
6071 // Make sure to wrap the exception in a new handle because
6072 // the handle returned from the TryCatch is destroyed
6073 // when the TryCatch is destroyed.
6074 exception = Local<Value>::New(try_catch.Exception());
6075 }
6076 return v8::ThrowException(exception);
6077 }
6078 }
6079
6080
ThrowInJSNoCatch(const v8::Arguments & args)6081 static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
6082 CHECK(v8::Locker::IsLocked());
6083 ApiTestFuzzer::Fuzz();
6084 v8::Unlocker unlocker;
6085 const char* code = "throw 7;";
6086 {
6087 v8::Locker nested_locker;
6088 v8::HandleScope scope;
6089 v8::Handle<Value> value = CompileRun(code);
6090 CHECK(value.IsEmpty());
6091 return v8_str("foo");
6092 }
6093 }
6094
6095
6096 // These are locking tests that don't need to be run again
6097 // as part of the locking aggregation tests.
TEST(NestedLockers)6098 TEST(NestedLockers) {
6099 v8::Locker locker;
6100 CHECK(v8::Locker::IsLocked());
6101 v8::HandleScope scope;
6102 LocalContext env;
6103 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
6104 Local<Function> fun = fun_templ->GetFunction();
6105 env->Global()->Set(v8_str("throw_in_js"), fun);
6106 Local<Script> script = v8_compile("(function () {"
6107 " try {"
6108 " throw_in_js();"
6109 " return 42;"
6110 " } catch (e) {"
6111 " return e * 13;"
6112 " }"
6113 "})();");
6114 CHECK_EQ(91, script->Run()->Int32Value());
6115 }
6116
6117
6118 // These are locking tests that don't need to be run again
6119 // as part of the locking aggregation tests.
TEST(NestedLockersNoTryCatch)6120 TEST(NestedLockersNoTryCatch) {
6121 v8::Locker locker;
6122 v8::HandleScope scope;
6123 LocalContext env;
6124 Local<v8::FunctionTemplate> fun_templ =
6125 v8::FunctionTemplate::New(ThrowInJSNoCatch);
6126 Local<Function> fun = fun_templ->GetFunction();
6127 env->Global()->Set(v8_str("throw_in_js"), fun);
6128 Local<Script> script = v8_compile("(function () {"
6129 " try {"
6130 " throw_in_js();"
6131 " return 42;"
6132 " } catch (e) {"
6133 " return e * 13;"
6134 " }"
6135 "})();");
6136 CHECK_EQ(91, script->Run()->Int32Value());
6137 }
6138
6139
THREADED_TEST(RecursiveLocking)6140 THREADED_TEST(RecursiveLocking) {
6141 v8::Locker locker;
6142 {
6143 v8::Locker locker2;
6144 CHECK(v8::Locker::IsLocked());
6145 }
6146 }
6147
6148
UnlockForAMoment(const v8::Arguments & args)6149 static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
6150 ApiTestFuzzer::Fuzz();
6151 v8::Unlocker unlocker;
6152 return v8::Undefined();
6153 }
6154
6155
THREADED_TEST(LockUnlockLock)6156 THREADED_TEST(LockUnlockLock) {
6157 {
6158 v8::Locker locker;
6159 v8::HandleScope scope;
6160 LocalContext env;
6161 Local<v8::FunctionTemplate> fun_templ =
6162 v8::FunctionTemplate::New(UnlockForAMoment);
6163 Local<Function> fun = fun_templ->GetFunction();
6164 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
6165 Local<Script> script = v8_compile("(function () {"
6166 " unlock_for_a_moment();"
6167 " return 42;"
6168 "})();");
6169 CHECK_EQ(42, script->Run()->Int32Value());
6170 }
6171 {
6172 v8::Locker locker;
6173 v8::HandleScope scope;
6174 LocalContext env;
6175 Local<v8::FunctionTemplate> fun_templ =
6176 v8::FunctionTemplate::New(UnlockForAMoment);
6177 Local<Function> fun = fun_templ->GetFunction();
6178 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
6179 Local<Script> script = v8_compile("(function () {"
6180 " unlock_for_a_moment();"
6181 " return 42;"
6182 "})();");
6183 CHECK_EQ(42, script->Run()->Int32Value());
6184 }
6185 }
6186
6187
GetSurvivingGlobalObjectsCount()6188 static int GetSurvivingGlobalObjectsCount() {
6189 int count = 0;
6190 // We need to collect all garbage twice to be sure that everything
6191 // has been collected. This is because inline caches are cleared in
6192 // the first garbage collection but some of the maps have already
6193 // been marked at that point. Therefore some of the maps are not
6194 // collected until the second garbage collection.
6195 v8::internal::Heap::CollectAllGarbage(false);
6196 v8::internal::Heap::CollectAllGarbage(false);
6197 v8::internal::HeapIterator it;
6198 while (it.has_next()) {
6199 v8::internal::HeapObject* object = it.next();
6200 if (object->IsJSGlobalObject()) {
6201 count++;
6202 }
6203 }
6204 #ifdef DEBUG
6205 if (count > 0) v8::internal::Heap::TracePathToGlobal();
6206 #endif
6207 return count;
6208 }
6209
6210
TEST(DontLeakGlobalObjects)6211 TEST(DontLeakGlobalObjects) {
6212 // Regression test for issues 1139850 and 1174891.
6213
6214 v8::V8::Initialize();
6215
6216 int count = GetSurvivingGlobalObjectsCount();
6217
6218 for (int i = 0; i < 5; i++) {
6219 { v8::HandleScope scope;
6220 LocalContext context;
6221 }
6222 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6223
6224 { v8::HandleScope scope;
6225 LocalContext context;
6226 v8_compile("Date")->Run();
6227 }
6228 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6229
6230 { v8::HandleScope scope;
6231 LocalContext context;
6232 v8_compile("/aaa/")->Run();
6233 }
6234 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6235
6236 { v8::HandleScope scope;
6237 const char* extension_list[] = { "v8/gc" };
6238 v8::ExtensionConfiguration extensions(1, extension_list);
6239 LocalContext context(&extensions);
6240 v8_compile("gc();")->Run();
6241 }
6242 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6243 }
6244 }
6245
6246
6247 v8::Persistent<v8::Object> some_object;
6248 v8::Persistent<v8::Object> bad_handle;
6249
NewPersistentHandleCallback(v8::Persistent<v8::Value>,void *)6250 void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
6251 v8::HandleScope scope;
6252 bad_handle = v8::Persistent<v8::Object>::New(some_object);
6253 }
6254
6255
THREADED_TEST(NewPersistentHandleFromWeakCallback)6256 THREADED_TEST(NewPersistentHandleFromWeakCallback) {
6257 LocalContext context;
6258
6259 v8::Persistent<v8::Object> handle1, handle2;
6260 {
6261 v8::HandleScope scope;
6262 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
6263 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
6264 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
6265 }
6266 // Note: order is implementation dependent alas: currently
6267 // global handle nodes are processed by PostGarbageCollectionProcessing
6268 // in reverse allocation order, so if second allocated handle is deleted,
6269 // weak callback of the first handle would be able to 'reallocate' it.
6270 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
6271 handle2.Dispose();
6272 i::Heap::CollectAllGarbage(false);
6273 }
6274
6275
6276 v8::Persistent<v8::Object> to_be_disposed;
6277
DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle,void *)6278 void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
6279 to_be_disposed.Dispose();
6280 i::Heap::CollectAllGarbage(false);
6281 }
6282
6283
THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc)6284 THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
6285 LocalContext context;
6286
6287 v8::Persistent<v8::Object> handle1, handle2;
6288 {
6289 v8::HandleScope scope;
6290 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
6291 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
6292 }
6293 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
6294 to_be_disposed = handle2;
6295 i::Heap::CollectAllGarbage(false);
6296 }
6297
6298
THREADED_TEST(CheckForCrossContextObjectLiterals)6299 THREADED_TEST(CheckForCrossContextObjectLiterals) {
6300 v8::V8::Initialize();
6301
6302 const int nof = 2;
6303 const char* sources[nof] = {
6304 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
6305 "Object()"
6306 };
6307
6308 for (int i = 0; i < nof; i++) {
6309 const char* source = sources[i];
6310 { v8::HandleScope scope;
6311 LocalContext context;
6312 CompileRun(source);
6313 }
6314 { v8::HandleScope scope;
6315 LocalContext context;
6316 CompileRun(source);
6317 }
6318 }
6319 }
6320
6321
NestedScope(v8::Persistent<Context> env)6322 static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
6323 v8::HandleScope inner;
6324 env->Enter();
6325 v8::Handle<Value> three = v8_num(3);
6326 v8::Handle<Value> value = inner.Close(three);
6327 env->Exit();
6328 return value;
6329 }
6330
6331
THREADED_TEST(NestedHandleScopeAndContexts)6332 THREADED_TEST(NestedHandleScopeAndContexts) {
6333 v8::HandleScope outer;
6334 v8::Persistent<Context> env = Context::New();
6335 env->Enter();
6336 v8::Handle<Value> value = NestedScope(env);
6337 v8::Handle<String> str = value->ToString();
6338 env->Exit();
6339 env.Dispose();
6340 }
6341
6342
THREADED_TEST(ExternalAllocatedMemory)6343 THREADED_TEST(ExternalAllocatedMemory) {
6344 v8::HandleScope outer;
6345 v8::Persistent<Context> env = Context::New();
6346 const int kSize = 1024*1024;
6347 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
6348 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
6349 }
6350
6351
THREADED_TEST(DisposeEnteredContext)6352 THREADED_TEST(DisposeEnteredContext) {
6353 v8::HandleScope scope;
6354 LocalContext outer;
6355 { v8::Persistent<v8::Context> inner = v8::Context::New();
6356 inner->Enter();
6357 inner.Dispose();
6358 inner.Clear();
6359 inner->Exit();
6360 }
6361 }
6362
6363
6364 // Regression test for issue 54, object templates with internal fields
6365 // but no accessors or interceptors did not get their internal field
6366 // count set on instances.
THREADED_TEST(Regress54)6367 THREADED_TEST(Regress54) {
6368 v8::HandleScope outer;
6369 LocalContext context;
6370 static v8::Persistent<v8::ObjectTemplate> templ;
6371 if (templ.IsEmpty()) {
6372 v8::HandleScope inner;
6373 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
6374 local->SetInternalFieldCount(1);
6375 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
6376 }
6377 v8::Handle<v8::Object> result = templ->NewInstance();
6378 CHECK_EQ(1, result->InternalFieldCount());
6379 }
6380
6381
6382 // If part of the threaded tests, this test makes ThreadingTest fail
6383 // on mac.
TEST(CatchStackOverflow)6384 TEST(CatchStackOverflow) {
6385 v8::HandleScope scope;
6386 LocalContext context;
6387 v8::TryCatch try_catch;
6388 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
6389 "function f() {"
6390 " return f();"
6391 "}"
6392 ""
6393 "f();"));
6394 v8::Handle<v8::Value> result = script->Run();
6395 CHECK(result.IsEmpty());
6396 }
6397
6398
CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,const char * resource_name,int line_offset)6399 static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
6400 const char* resource_name,
6401 int line_offset) {
6402 v8::HandleScope scope;
6403 v8::TryCatch try_catch;
6404 v8::Handle<v8::Value> result = script->Run();
6405 CHECK(result.IsEmpty());
6406 CHECK(try_catch.HasCaught());
6407 v8::Handle<v8::Message> message = try_catch.Message();
6408 CHECK(!message.IsEmpty());
6409 CHECK_EQ(10 + line_offset, message->GetLineNumber());
6410 CHECK_EQ(91, message->GetStartPosition());
6411 CHECK_EQ(92, message->GetEndPosition());
6412 CHECK_EQ(2, message->GetStartColumn());
6413 CHECK_EQ(3, message->GetEndColumn());
6414 v8::String::AsciiValue line(message->GetSourceLine());
6415 CHECK_EQ(" throw 'nirk';", *line);
6416 v8::String::AsciiValue name(message->GetScriptResourceName());
6417 CHECK_EQ(resource_name, *name);
6418 }
6419
6420
THREADED_TEST(TryCatchSourceInfo)6421 THREADED_TEST(TryCatchSourceInfo) {
6422 v8::HandleScope scope;
6423 LocalContext context;
6424 v8::Handle<v8::String> source = v8::String::New(
6425 "function Foo() {\n"
6426 " return Bar();\n"
6427 "}\n"
6428 "\n"
6429 "function Bar() {\n"
6430 " return Baz();\n"
6431 "}\n"
6432 "\n"
6433 "function Baz() {\n"
6434 " throw 'nirk';\n"
6435 "}\n"
6436 "\n"
6437 "Foo();\n");
6438
6439 const char* resource_name;
6440 v8::Handle<v8::Script> script;
6441 resource_name = "test.js";
6442 script = v8::Script::Compile(source, v8::String::New(resource_name));
6443 CheckTryCatchSourceInfo(script, resource_name, 0);
6444
6445 resource_name = "test1.js";
6446 v8::ScriptOrigin origin1(v8::String::New(resource_name));
6447 script = v8::Script::Compile(source, &origin1);
6448 CheckTryCatchSourceInfo(script, resource_name, 0);
6449
6450 resource_name = "test2.js";
6451 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
6452 script = v8::Script::Compile(source, &origin2);
6453 CheckTryCatchSourceInfo(script, resource_name, 7);
6454 }
6455
6456
THREADED_TEST(CompilationCache)6457 THREADED_TEST(CompilationCache) {
6458 v8::HandleScope scope;
6459 LocalContext context;
6460 v8::Handle<v8::String> source0 = v8::String::New("1234");
6461 v8::Handle<v8::String> source1 = v8::String::New("1234");
6462 v8::Handle<v8::Script> script0 =
6463 v8::Script::Compile(source0, v8::String::New("test.js"));
6464 v8::Handle<v8::Script> script1 =
6465 v8::Script::Compile(source1, v8::String::New("test.js"));
6466 v8::Handle<v8::Script> script2 =
6467 v8::Script::Compile(source0); // different origin
6468 CHECK_EQ(1234, script0->Run()->Int32Value());
6469 CHECK_EQ(1234, script1->Run()->Int32Value());
6470 CHECK_EQ(1234, script2->Run()->Int32Value());
6471 }
6472
6473
FunctionNameCallback(const v8::Arguments & args)6474 static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
6475 ApiTestFuzzer::Fuzz();
6476 return v8_num(42);
6477 }
6478
6479
THREADED_TEST(CallbackFunctionName)6480 THREADED_TEST(CallbackFunctionName) {
6481 v8::HandleScope scope;
6482 LocalContext context;
6483 Local<ObjectTemplate> t = ObjectTemplate::New();
6484 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
6485 context->Global()->Set(v8_str("obj"), t->NewInstance());
6486 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
6487 CHECK(value->IsString());
6488 v8::String::AsciiValue name(value);
6489 CHECK_EQ("asdf", *name);
6490 }
6491
6492
THREADED_TEST(DateAccess)6493 THREADED_TEST(DateAccess) {
6494 v8::HandleScope scope;
6495 LocalContext context;
6496 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
6497 CHECK(date->IsDate());
6498 CHECK_EQ(1224744689038.0, v8::Handle<v8::Date>::Cast(date)->NumberValue());
6499 }
6500
6501
CheckProperties(v8::Handle<v8::Value> val,int elmc,const char * elmv[])6502 void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
6503 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
6504 v8::Handle<v8::Array> props = obj->GetPropertyNames();
6505 CHECK_EQ(elmc, props->Length());
6506 for (int i = 0; i < elmc; i++) {
6507 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
6508 CHECK_EQ(elmv[i], *elm);
6509 }
6510 }
6511
6512
THREADED_TEST(PropertyEnumeration)6513 THREADED_TEST(PropertyEnumeration) {
6514 v8::HandleScope scope;
6515 LocalContext context;
6516 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
6517 "var result = [];"
6518 "result[0] = {};"
6519 "result[1] = {a: 1, b: 2};"
6520 "result[2] = [1, 2, 3];"
6521 "var proto = {x: 1, y: 2, z: 3};"
6522 "var x = { __proto__: proto, w: 0, z: 1 };"
6523 "result[3] = x;"
6524 "result;"))->Run();
6525 v8::Handle<v8::Array> elms = v8::Handle<v8::Array>::Cast(obj);
6526 CHECK_EQ(4, elms->Length());
6527 int elmc0 = 0;
6528 const char** elmv0 = NULL;
6529 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
6530 int elmc1 = 2;
6531 const char* elmv1[] = {"a", "b"};
6532 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
6533 int elmc2 = 3;
6534 const char* elmv2[] = {"0", "1", "2"};
6535 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
6536 int elmc3 = 4;
6537 const char* elmv3[] = {"w", "z", "x", "y"};
6538 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
6539 }
6540
6541
AccessorProhibitsOverwritingGetter(Local<String> name,const AccessorInfo & info)6542 static v8::Handle<Value> AccessorProhibitsOverwritingGetter(
6543 Local<String> name,
6544 const AccessorInfo& info) {
6545 ApiTestFuzzer::Fuzz();
6546 return v8::True();
6547 }
6548
6549
THREADED_TEST(AccessorProhibitsOverwriting)6550 THREADED_TEST(AccessorProhibitsOverwriting) {
6551 v8::HandleScope scope;
6552 LocalContext context;
6553 Local<ObjectTemplate> templ = ObjectTemplate::New();
6554 templ->SetAccessor(v8_str("x"),
6555 AccessorProhibitsOverwritingGetter,
6556 0,
6557 v8::Handle<Value>(),
6558 v8::PROHIBITS_OVERWRITING,
6559 v8::ReadOnly);
6560 Local<v8::Object> instance = templ->NewInstance();
6561 context->Global()->Set(v8_str("obj"), instance);
6562 Local<Value> value = CompileRun(
6563 "obj.__defineGetter__('x', function() { return false; });"
6564 "obj.x");
6565 CHECK(value->BooleanValue());
6566 value = CompileRun(
6567 "var setter_called = false;"
6568 "obj.__defineSetter__('x', function() { setter_called = true; });"
6569 "obj.x = 42;"
6570 "setter_called");
6571 CHECK(!value->BooleanValue());
6572 value = CompileRun(
6573 "obj2 = {};"
6574 "obj2.__proto__ = obj;"
6575 "obj2.__defineGetter__('x', function() { return false; });"
6576 "obj2.x");
6577 CHECK(value->BooleanValue());
6578 value = CompileRun(
6579 "var setter_called = false;"
6580 "obj2 = {};"
6581 "obj2.__proto__ = obj;"
6582 "obj2.__defineSetter__('x', function() { setter_called = true; });"
6583 "obj2.x = 42;"
6584 "setter_called");
6585 CHECK(!value->BooleanValue());
6586 }
6587
6588
NamedSetAccessBlocker(Local<v8::Object> obj,Local<Value> name,v8::AccessType type,Local<Value> data)6589 static bool NamedSetAccessBlocker(Local<v8::Object> obj,
6590 Local<Value> name,
6591 v8::AccessType type,
6592 Local<Value> data) {
6593 return type != v8::ACCESS_SET;
6594 }
6595
6596
IndexedSetAccessBlocker(Local<v8::Object> obj,uint32_t key,v8::AccessType type,Local<Value> data)6597 static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
6598 uint32_t key,
6599 v8::AccessType type,
6600 Local<Value> data) {
6601 return type != v8::ACCESS_SET;
6602 }
6603
6604
THREADED_TEST(DisableAccessChecksWhileConfiguring)6605 THREADED_TEST(DisableAccessChecksWhileConfiguring) {
6606 v8::HandleScope scope;
6607 LocalContext context;
6608 Local<ObjectTemplate> templ = ObjectTemplate::New();
6609 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
6610 IndexedSetAccessBlocker);
6611 templ->Set(v8_str("x"), v8::True());
6612 Local<v8::Object> instance = templ->NewInstance();
6613 context->Global()->Set(v8_str("obj"), instance);
6614 Local<Value> value = CompileRun("obj.x");
6615 CHECK(value->BooleanValue());
6616 }
6617
6618
NamedGetAccessBlocker(Local<v8::Object> obj,Local<Value> name,v8::AccessType type,Local<Value> data)6619 static bool NamedGetAccessBlocker(Local<v8::Object> obj,
6620 Local<Value> name,
6621 v8::AccessType type,
6622 Local<Value> data) {
6623 return false;
6624 }
6625
6626
IndexedGetAccessBlocker(Local<v8::Object> obj,uint32_t key,v8::AccessType type,Local<Value> data)6627 static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
6628 uint32_t key,
6629 v8::AccessType type,
6630 Local<Value> data) {
6631 return false;
6632 }
6633
6634
6635
THREADED_TEST(AccessChecksReenabledCorrectly)6636 THREADED_TEST(AccessChecksReenabledCorrectly) {
6637 v8::HandleScope scope;
6638 LocalContext context;
6639 Local<ObjectTemplate> templ = ObjectTemplate::New();
6640 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
6641 IndexedGetAccessBlocker);
6642 templ->Set(v8_str("a"), v8_str("a"));
6643 // Add more than 8 (see kMaxFastProperties) properties
6644 // so that the constructor will force copying map.
6645 // Cannot sprintf, gcc complains unsafety.
6646 char buf[4];
6647 for (char i = '0'; i <= '9' ; i++) {
6648 buf[0] = i;
6649 for (char j = '0'; j <= '9'; j++) {
6650 buf[1] = j;
6651 for (char k = '0'; k <= '9'; k++) {
6652 buf[2] = k;
6653 buf[3] = 0;
6654 templ->Set(v8_str(buf), v8::Number::New(k));
6655 }
6656 }
6657 }
6658
6659 Local<v8::Object> instance_1 = templ->NewInstance();
6660 context->Global()->Set(v8_str("obj_1"), instance_1);
6661
6662 Local<Value> value_1 = CompileRun("obj_1.a");
6663 CHECK(value_1->IsUndefined());
6664
6665 Local<v8::Object> instance_2 = templ->NewInstance();
6666 context->Global()->Set(v8_str("obj_2"), instance_2);
6667
6668 Local<Value> value_2 = CompileRun("obj_2.a");
6669 CHECK(value_2->IsUndefined());
6670 }
6671
6672
6673 // This tests that access check information remains on the global
6674 // object template when creating contexts.
THREADED_TEST(AccessControlRepeatedContextCreation)6675 THREADED_TEST(AccessControlRepeatedContextCreation) {
6676 v8::HandleScope handle_scope;
6677 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
6678 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
6679 IndexedSetAccessBlocker);
6680 i::Handle<i::ObjectTemplateInfo> internal_template =
6681 v8::Utils::OpenHandle(*global_template);
6682 CHECK(!internal_template->constructor()->IsUndefined());
6683 i::Handle<i::FunctionTemplateInfo> constructor(
6684 i::FunctionTemplateInfo::cast(internal_template->constructor()));
6685 CHECK(!constructor->access_check_info()->IsUndefined());
6686 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
6687 CHECK(!constructor->access_check_info()->IsUndefined());
6688 }
6689
6690
THREADED_TEST(TurnOnAccessCheck)6691 THREADED_TEST(TurnOnAccessCheck) {
6692 v8::HandleScope handle_scope;
6693
6694 // Create an environment with access check to the global object disabled by
6695 // default.
6696 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
6697 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
6698 IndexedGetAccessBlocker,
6699 v8::Handle<v8::Value>(),
6700 false);
6701 v8::Persistent<Context> context = Context::New(NULL, global_template);
6702 Context::Scope context_scope(context);
6703
6704 // Set up a property and a number of functions.
6705 context->Global()->Set(v8_str("a"), v8_num(1));
6706 CompileRun("function f1() {return a;}"
6707 "function f2() {return a;}"
6708 "function g1() {return h();}"
6709 "function g2() {return h();}"
6710 "function h() {return 1;}");
6711 Local<Function> f1 =
6712 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
6713 Local<Function> f2 =
6714 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
6715 Local<Function> g1 =
6716 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
6717 Local<Function> g2 =
6718 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
6719 Local<Function> h =
6720 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
6721
6722 // Get the global object.
6723 v8::Handle<v8::Object> global = context->Global();
6724
6725 // Call f1 one time and f2 a number of times. This will ensure that f1 still
6726 // uses the runtime system to retreive property a whereas f2 uses global load
6727 // inline cache.
6728 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
6729 for (int i = 0; i < 4; i++) {
6730 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
6731 }
6732
6733 // Same for g1 and g2.
6734 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
6735 for (int i = 0; i < 4; i++) {
6736 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
6737 }
6738
6739 // Detach the global and turn on access check.
6740 context->DetachGlobal();
6741 context->Global()->TurnOnAccessCheck();
6742
6743 // Failing access check to property get results in undefined.
6744 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
6745 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
6746
6747 // Failing access check to function call results in exception.
6748 CHECK(g1->Call(global, 0, NULL).IsEmpty());
6749 CHECK(g2->Call(global, 0, NULL).IsEmpty());
6750
6751 // No failing access check when just returning a constant.
6752 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
6753 }
6754
6755
6756 // This test verifies that pre-compilation (aka preparsing) can be called
6757 // without initializing the whole VM. Thus we cannot run this test in a
6758 // multi-threaded setup.
TEST(PreCompile)6759 TEST(PreCompile) {
6760 // TODO(155): This test would break without the initialization of V8. This is
6761 // a workaround for now to make this test not fail.
6762 v8::V8::Initialize();
6763 const char *script = "function foo(a) { return a+1; }";
6764 v8::ScriptData *sd = v8::ScriptData::PreCompile(script, strlen(script));
6765 CHECK_NE(sd->Length(), 0);
6766 CHECK_NE(sd->Data(), NULL);
6767 delete sd;
6768 }
6769
6770
6771 // This tests that we do not allow dictionary load/call inline caches
6772 // to use functions that have not yet been compiled. The potential
6773 // problem of loading a function that has not yet been compiled can
6774 // arise because we share code between contexts via the compilation
6775 // cache.
THREADED_TEST(DictionaryICLoadedFunction)6776 THREADED_TEST(DictionaryICLoadedFunction) {
6777 v8::HandleScope scope;
6778 // Test LoadIC.
6779 for (int i = 0; i < 2; i++) {
6780 LocalContext context;
6781 context->Global()->Set(v8_str("tmp"), v8::True());
6782 context->Global()->Delete(v8_str("tmp"));
6783 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
6784 }
6785 // Test CallIC.
6786 for (int i = 0; i < 2; i++) {
6787 LocalContext context;
6788 context->Global()->Set(v8_str("tmp"), v8::True());
6789 context->Global()->Delete(v8_str("tmp"));
6790 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
6791 }
6792 }
6793
6794
6795 // Test that cross-context new calls use the context of the callee to
6796 // create the new JavaScript object.
THREADED_TEST(CrossContextNew)6797 THREADED_TEST(CrossContextNew) {
6798 v8::HandleScope scope;
6799 v8::Persistent<Context> context0 = Context::New();
6800 v8::Persistent<Context> context1 = Context::New();
6801
6802 // Allow cross-domain access.
6803 Local<String> token = v8_str("<security token>");
6804 context0->SetSecurityToken(token);
6805 context1->SetSecurityToken(token);
6806
6807 // Set an 'x' property on the Object prototype and define a
6808 // constructor function in context0.
6809 context0->Enter();
6810 CompileRun("Object.prototype.x = 42; function C() {};");
6811 context0->Exit();
6812
6813 // Call the constructor function from context0 and check that the
6814 // result has the 'x' property.
6815 context1->Enter();
6816 context1->Global()->Set(v8_str("other"), context0->Global());
6817 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
6818 CHECK(value->IsInt32());
6819 CHECK_EQ(42, value->Int32Value());
6820 context1->Exit();
6821
6822 // Dispose the contexts to allow them to be garbage collected.
6823 context0.Dispose();
6824 context1.Dispose();
6825 }
6826
6827
6828 class RegExpInterruptTest {
6829 public:
RegExpInterruptTest()6830 RegExpInterruptTest() : block_(NULL) {}
~RegExpInterruptTest()6831 ~RegExpInterruptTest() { delete block_; }
RunTest()6832 void RunTest() {
6833 block_ = i::OS::CreateSemaphore(0);
6834 gc_count_ = 0;
6835 gc_during_regexp_ = 0;
6836 regexp_success_ = false;
6837 gc_success_ = false;
6838 GCThread gc_thread(this);
6839 gc_thread.Start();
6840 v8::Locker::StartPreemption(1);
6841
6842 LongRunningRegExp();
6843 {
6844 v8::Unlocker unlock;
6845 gc_thread.Join();
6846 }
6847 v8::Locker::StopPreemption();
6848 CHECK(regexp_success_);
6849 CHECK(gc_success_);
6850 }
6851 private:
6852 // Number of garbage collections required.
6853 static const int kRequiredGCs = 5;
6854
6855 class GCThread : public i::Thread {
6856 public:
GCThread(RegExpInterruptTest * test)6857 explicit GCThread(RegExpInterruptTest* test)
6858 : test_(test) {}
Run()6859 virtual void Run() {
6860 test_->CollectGarbage();
6861 }
6862 private:
6863 RegExpInterruptTest* test_;
6864 };
6865
CollectGarbage()6866 void CollectGarbage() {
6867 block_->Wait();
6868 while (gc_during_regexp_ < kRequiredGCs) {
6869 {
6870 v8::Locker lock;
6871 // TODO(lrn): Perhaps create some garbage before collecting.
6872 i::Heap::CollectAllGarbage(false);
6873 gc_count_++;
6874 }
6875 i::OS::Sleep(1);
6876 }
6877 gc_success_ = true;
6878 }
6879
LongRunningRegExp()6880 void LongRunningRegExp() {
6881 block_->Signal(); // Enable garbage collection thread on next preemption.
6882 int rounds = 0;
6883 while (gc_during_regexp_ < kRequiredGCs) {
6884 int gc_before = gc_count_;
6885 {
6886 // Match 15-30 "a"'s against 14 and a "b".
6887 const char* c_source =
6888 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
6889 ".exec('aaaaaaaaaaaaaaab') === null";
6890 Local<String> source = String::New(c_source);
6891 Local<Script> script = Script::Compile(source);
6892 Local<Value> result = script->Run();
6893 if (!result->BooleanValue()) {
6894 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
6895 return;
6896 }
6897 }
6898 {
6899 // Match 15-30 "a"'s against 15 and a "b".
6900 const char* c_source =
6901 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
6902 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
6903 Local<String> source = String::New(c_source);
6904 Local<Script> script = Script::Compile(source);
6905 Local<Value> result = script->Run();
6906 if (!result->BooleanValue()) {
6907 gc_during_regexp_ = kRequiredGCs;
6908 return;
6909 }
6910 }
6911 int gc_after = gc_count_;
6912 gc_during_regexp_ += gc_after - gc_before;
6913 rounds++;
6914 i::OS::Sleep(1);
6915 }
6916 regexp_success_ = true;
6917 }
6918
6919 i::Semaphore* block_;
6920 int gc_count_;
6921 int gc_during_regexp_;
6922 bool regexp_success_;
6923 bool gc_success_;
6924 };
6925
6926
6927 // Test that a regular expression execution can be interrupted and
6928 // survive a garbage collection.
TEST(RegExpInterruption)6929 TEST(RegExpInterruption) {
6930 v8::Locker lock;
6931 v8::V8::Initialize();
6932 v8::HandleScope scope;
6933 Local<Context> local_env;
6934 {
6935 LocalContext env;
6936 local_env = env.local();
6937 }
6938
6939 // Local context should still be live.
6940 CHECK(!local_env.IsEmpty());
6941 local_env->Enter();
6942
6943 // Should complete without problems.
6944 RegExpInterruptTest().RunTest();
6945
6946 local_env->Exit();
6947 }
6948
6949
6950 class ApplyInterruptTest {
6951 public:
ApplyInterruptTest()6952 ApplyInterruptTest() : block_(NULL) {}
~ApplyInterruptTest()6953 ~ApplyInterruptTest() { delete block_; }
RunTest()6954 void RunTest() {
6955 block_ = i::OS::CreateSemaphore(0);
6956 gc_count_ = 0;
6957 gc_during_apply_ = 0;
6958 apply_success_ = false;
6959 gc_success_ = false;
6960 GCThread gc_thread(this);
6961 gc_thread.Start();
6962 v8::Locker::StartPreemption(1);
6963
6964 LongRunningApply();
6965 {
6966 v8::Unlocker unlock;
6967 gc_thread.Join();
6968 }
6969 v8::Locker::StopPreemption();
6970 CHECK(apply_success_);
6971 CHECK(gc_success_);
6972 }
6973 private:
6974 // Number of garbage collections required.
6975 static const int kRequiredGCs = 2;
6976
6977 class GCThread : public i::Thread {
6978 public:
GCThread(ApplyInterruptTest * test)6979 explicit GCThread(ApplyInterruptTest* test)
6980 : test_(test) {}
Run()6981 virtual void Run() {
6982 test_->CollectGarbage();
6983 }
6984 private:
6985 ApplyInterruptTest* test_;
6986 };
6987
CollectGarbage()6988 void CollectGarbage() {
6989 block_->Wait();
6990 while (gc_during_apply_ < kRequiredGCs) {
6991 {
6992 v8::Locker lock;
6993 i::Heap::CollectAllGarbage(false);
6994 gc_count_++;
6995 }
6996 i::OS::Sleep(1);
6997 }
6998 gc_success_ = true;
6999 }
7000
LongRunningApply()7001 void LongRunningApply() {
7002 block_->Signal();
7003 int rounds = 0;
7004 while (gc_during_apply_ < kRequiredGCs) {
7005 int gc_before = gc_count_;
7006 {
7007 const char* c_source =
7008 "function do_very_little(bar) {"
7009 " this.foo = bar;"
7010 "}"
7011 "for (var i = 0; i < 100000; i++) {"
7012 " do_very_little.apply(this, ['bar']);"
7013 "}";
7014 Local<String> source = String::New(c_source);
7015 Local<Script> script = Script::Compile(source);
7016 Local<Value> result = script->Run();
7017 // Check that no exception was thrown.
7018 CHECK(!result.IsEmpty());
7019 }
7020 int gc_after = gc_count_;
7021 gc_during_apply_ += gc_after - gc_before;
7022 rounds++;
7023 }
7024 apply_success_ = true;
7025 }
7026
7027 i::Semaphore* block_;
7028 int gc_count_;
7029 int gc_during_apply_;
7030 bool apply_success_;
7031 bool gc_success_;
7032 };
7033
7034
7035 // Test that nothing bad happens if we get a preemption just when we were
7036 // about to do an apply().
TEST(ApplyInterruption)7037 TEST(ApplyInterruption) {
7038 v8::Locker lock;
7039 v8::V8::Initialize();
7040 v8::HandleScope scope;
7041 Local<Context> local_env;
7042 {
7043 LocalContext env;
7044 local_env = env.local();
7045 }
7046
7047 // Local context should still be live.
7048 CHECK(!local_env.IsEmpty());
7049 local_env->Enter();
7050
7051 // Should complete without problems.
7052 ApplyInterruptTest().RunTest();
7053
7054 local_env->Exit();
7055 }
7056
7057
7058 // Verify that we can clone an object
TEST(ObjectClone)7059 TEST(ObjectClone) {
7060 v8::HandleScope scope;
7061 LocalContext env;
7062
7063 const char* sample =
7064 "var rv = {};" \
7065 "rv.alpha = 'hello';" \
7066 "rv.beta = 123;" \
7067 "rv;";
7068
7069 // Create an object, verify basics.
7070 Local<Value> val = CompileRun(sample);
7071 CHECK(val->IsObject());
7072 Local<v8::Object> obj = Local<v8::Object>::Cast(val);
7073 obj->Set(v8_str("gamma"), v8_str("cloneme"));
7074
7075 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
7076 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
7077 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
7078
7079 // Clone it.
7080 Local<v8::Object> clone = obj->Clone();
7081 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
7082 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
7083 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
7084
7085 // Set a property on the clone, verify each object.
7086 clone->Set(v8_str("beta"), v8::Integer::New(456));
7087 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
7088 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
7089 }
7090
7091
7092 class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
7093 public:
AsciiVectorResource(i::Vector<const char> vector)7094 explicit AsciiVectorResource(i::Vector<const char> vector)
7095 : data_(vector) {}
~AsciiVectorResource()7096 virtual ~AsciiVectorResource() {}
length() const7097 virtual size_t length() const { return data_.length(); }
data() const7098 virtual const char* data() const { return data_.start(); }
7099 private:
7100 i::Vector<const char> data_;
7101 };
7102
7103
7104 class UC16VectorResource : public v8::String::ExternalStringResource {
7105 public:
UC16VectorResource(i::Vector<const i::uc16> vector)7106 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
7107 : data_(vector) {}
~UC16VectorResource()7108 virtual ~UC16VectorResource() {}
length() const7109 virtual size_t length() const { return data_.length(); }
data() const7110 virtual const i::uc16* data() const { return data_.start(); }
7111 private:
7112 i::Vector<const i::uc16> data_;
7113 };
7114
7115
MorphAString(i::String * string,AsciiVectorResource * ascii_resource,UC16VectorResource * uc16_resource)7116 static void MorphAString(i::String* string,
7117 AsciiVectorResource* ascii_resource,
7118 UC16VectorResource* uc16_resource) {
7119 CHECK(i::StringShape(string).IsExternal());
7120 if (string->IsAsciiRepresentation()) {
7121 // Check old map is not symbol or long.
7122 CHECK(string->map() == i::Heap::short_external_ascii_string_map() ||
7123 string->map() == i::Heap::medium_external_ascii_string_map());
7124 // Morph external string to be TwoByte string.
7125 if (string->length() <= i::String::kMaxShortStringSize) {
7126 string->set_map(i::Heap::short_external_string_map());
7127 } else {
7128 string->set_map(i::Heap::medium_external_string_map());
7129 }
7130 i::ExternalTwoByteString* morphed =
7131 i::ExternalTwoByteString::cast(string);
7132 morphed->set_resource(uc16_resource);
7133 } else {
7134 // Check old map is not symbol or long.
7135 CHECK(string->map() == i::Heap::short_external_string_map() ||
7136 string->map() == i::Heap::medium_external_string_map());
7137 // Morph external string to be ASCII string.
7138 if (string->length() <= i::String::kMaxShortStringSize) {
7139 string->set_map(i::Heap::short_external_ascii_string_map());
7140 } else {
7141 string->set_map(i::Heap::medium_external_ascii_string_map());
7142 }
7143 i::ExternalAsciiString* morphed =
7144 i::ExternalAsciiString::cast(string);
7145 morphed->set_resource(ascii_resource);
7146 }
7147 }
7148
7149
7150 // Test that we can still flatten a string if the components it is built up
7151 // from have been turned into 16 bit strings in the mean time.
THREADED_TEST(MorphCompositeStringTest)7152 THREADED_TEST(MorphCompositeStringTest) {
7153 const char* c_string = "Now is the time for all good men"
7154 " to come to the aid of the party";
7155 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
7156 {
7157 v8::HandleScope scope;
7158 LocalContext env;
7159 AsciiVectorResource ascii_resource(
7160 i::Vector<const char>(c_string, strlen(c_string)));
7161 UC16VectorResource uc16_resource(
7162 i::Vector<const uint16_t>(two_byte_string, strlen(c_string)));
7163
7164 Local<String> lhs(v8::Utils::ToLocal(
7165 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
7166 Local<String> rhs(v8::Utils::ToLocal(
7167 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
7168
7169 env->Global()->Set(v8_str("lhs"), lhs);
7170 env->Global()->Set(v8_str("rhs"), rhs);
7171
7172 CompileRun(
7173 "var cons = lhs + rhs;"
7174 "var slice = lhs.substring(1, lhs.length - 1);"
7175 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
7176
7177 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
7178 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
7179
7180 // Now do some stuff to make sure the strings are flattened, etc.
7181 CompileRun(
7182 "/[^a-z]/.test(cons);"
7183 "/[^a-z]/.test(slice);"
7184 "/[^a-z]/.test(slice_on_cons);");
7185 const char* expected_cons =
7186 "Now is the time for all good men to come to the aid of the party"
7187 "Now is the time for all good men to come to the aid of the party";
7188 const char* expected_slice =
7189 "ow is the time for all good men to come to the aid of the part";
7190 const char* expected_slice_on_cons =
7191 "ow is the time for all good men to come to the aid of the party"
7192 "Now is the time for all good men to come to the aid of the part";
7193 CHECK_EQ(String::New(expected_cons),
7194 env->Global()->Get(v8_str("cons")));
7195 CHECK_EQ(String::New(expected_slice),
7196 env->Global()->Get(v8_str("slice")));
7197 CHECK_EQ(String::New(expected_slice_on_cons),
7198 env->Global()->Get(v8_str("slice_on_cons")));
7199 }
7200 }
7201
7202
TEST(CompileExternalTwoByteSource)7203 TEST(CompileExternalTwoByteSource) {
7204 v8::HandleScope scope;
7205 LocalContext context;
7206
7207 // This is a very short list of sources, which currently is to check for a
7208 // regression caused by r2703.
7209 const char* ascii_sources[] = {
7210 "0.5",
7211 "-0.5", // This mainly testes PushBack in the Scanner.
7212 "--0.5", // This mainly testes PushBack in the Scanner.
7213 NULL
7214 };
7215
7216 // Compile the sources as external two byte strings.
7217 for (int i = 0; ascii_sources[i] != NULL; i++) {
7218 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
7219 UC16VectorResource uc16_resource(
7220 i::Vector<const uint16_t>(two_byte_string, strlen(ascii_sources[i])));
7221 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
7222 v8::Script::Compile(source);
7223 }
7224 }
7225
7226
7227 class RegExpStringModificationTest {
7228 public:
RegExpStringModificationTest()7229 RegExpStringModificationTest()
7230 : block_(i::OS::CreateSemaphore(0)),
7231 morphs_(0),
7232 morphs_during_regexp_(0),
7233 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
7234 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
~RegExpStringModificationTest()7235 ~RegExpStringModificationTest() { delete block_; }
RunTest()7236 void RunTest() {
7237 regexp_success_ = false;
7238 morph_success_ = false;
7239
7240 // Initialize the contents of two_byte_content_ to be a uc16 representation
7241 // of "aaaaaaaaaaaaaab".
7242 for (int i = 0; i < 14; i++) {
7243 two_byte_content_[i] = 'a';
7244 }
7245 two_byte_content_[14] = 'b';
7246
7247 // Create the input string for the regexp - the one we are going to change
7248 // properties of.
7249 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
7250
7251 // Inject the input as a global variable.
7252 i::Handle<i::String> input_name =
7253 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
7254 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
7255
7256
7257 MorphThread morph_thread(this);
7258 morph_thread.Start();
7259 v8::Locker::StartPreemption(1);
7260 LongRunningRegExp();
7261 {
7262 v8::Unlocker unlock;
7263 morph_thread.Join();
7264 }
7265 v8::Locker::StopPreemption();
7266 CHECK(regexp_success_);
7267 CHECK(morph_success_);
7268 }
7269 private:
7270
7271 // Number of string modifications required.
7272 static const int kRequiredModifications = 5;
7273 static const int kMaxModifications = 100;
7274
7275 class MorphThread : public i::Thread {
7276 public:
MorphThread(RegExpStringModificationTest * test)7277 explicit MorphThread(RegExpStringModificationTest* test)
7278 : test_(test) {}
Run()7279 virtual void Run() {
7280 test_->MorphString();
7281 }
7282 private:
7283 RegExpStringModificationTest* test_;
7284 };
7285
MorphString()7286 void MorphString() {
7287 block_->Wait();
7288 while (morphs_during_regexp_ < kRequiredModifications &&
7289 morphs_ < kMaxModifications) {
7290 {
7291 v8::Locker lock;
7292 // Swap string between ascii and two-byte representation.
7293 i::String* string = *input_;
7294 MorphAString(string, &ascii_resource_, &uc16_resource_);
7295 morphs_++;
7296 }
7297 i::OS::Sleep(1);
7298 }
7299 morph_success_ = true;
7300 }
7301
LongRunningRegExp()7302 void LongRunningRegExp() {
7303 block_->Signal(); // Enable morphing thread on next preemption.
7304 while (morphs_during_regexp_ < kRequiredModifications &&
7305 morphs_ < kMaxModifications) {
7306 int morphs_before = morphs_;
7307 {
7308 // Match 15-30 "a"'s against 14 and a "b".
7309 const char* c_source =
7310 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
7311 ".exec(input) === null";
7312 Local<String> source = String::New(c_source);
7313 Local<Script> script = Script::Compile(source);
7314 Local<Value> result = script->Run();
7315 CHECK(result->IsTrue());
7316 }
7317 int morphs_after = morphs_;
7318 morphs_during_regexp_ += morphs_after - morphs_before;
7319 }
7320 regexp_success_ = true;
7321 }
7322
7323 i::uc16 two_byte_content_[15];
7324 i::Semaphore* block_;
7325 int morphs_;
7326 int morphs_during_regexp_;
7327 bool regexp_success_;
7328 bool morph_success_;
7329 i::Handle<i::String> input_;
7330 AsciiVectorResource ascii_resource_;
7331 UC16VectorResource uc16_resource_;
7332 };
7333
7334
7335 // Test that a regular expression execution can be interrupted and
7336 // the string changed without failing.
TEST(RegExpStringModification)7337 TEST(RegExpStringModification) {
7338 v8::Locker lock;
7339 v8::V8::Initialize();
7340 v8::HandleScope scope;
7341 Local<Context> local_env;
7342 {
7343 LocalContext env;
7344 local_env = env.local();
7345 }
7346
7347 // Local context should still be live.
7348 CHECK(!local_env.IsEmpty());
7349 local_env->Enter();
7350
7351 // Should complete without problems.
7352 RegExpStringModificationTest().RunTest();
7353
7354 local_env->Exit();
7355 }
7356
7357
7358 // Test that we can set a property on the global object even if there
7359 // is a read-only property in the prototype chain.
TEST(ReadOnlyPropertyInGlobalProto)7360 TEST(ReadOnlyPropertyInGlobalProto) {
7361 v8::HandleScope scope;
7362 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7363 LocalContext context(0, templ);
7364 v8::Handle<v8::Object> global = context->Global();
7365 v8::Handle<v8::Object> global_proto =
7366 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
7367 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
7368 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
7369 // Check without 'eval' or 'with'.
7370 v8::Handle<v8::Value> res =
7371 CompileRun("function f() { x = 42; return x; }; f()");
7372 // Check with 'eval'.
7373 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
7374 CHECK_EQ(v8::Integer::New(42), res);
7375 // Check with 'with'.
7376 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
7377 CHECK_EQ(v8::Integer::New(42), res);
7378 }
7379
7380 static int force_set_set_count = 0;
7381 static int force_set_get_count = 0;
7382 bool pass_on_get = false;
7383
ForceSetGetter(v8::Local<v8::String> name,const v8::AccessorInfo & info)7384 static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
7385 const v8::AccessorInfo& info) {
7386 force_set_get_count++;
7387 if (pass_on_get) {
7388 return v8::Handle<v8::Value>();
7389 } else {
7390 return v8::Int32::New(3);
7391 }
7392 }
7393
ForceSetSetter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::AccessorInfo & info)7394 static void ForceSetSetter(v8::Local<v8::String> name,
7395 v8::Local<v8::Value> value,
7396 const v8::AccessorInfo& info) {
7397 force_set_set_count++;
7398 }
7399
ForceSetInterceptSetter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::AccessorInfo & info)7400 static v8::Handle<v8::Value> ForceSetInterceptSetter(
7401 v8::Local<v8::String> name,
7402 v8::Local<v8::Value> value,
7403 const v8::AccessorInfo& info) {
7404 force_set_set_count++;
7405 return v8::Undefined();
7406 }
7407
TEST(ForceSet)7408 TEST(ForceSet) {
7409 force_set_get_count = 0;
7410 force_set_set_count = 0;
7411 pass_on_get = false;
7412
7413 v8::HandleScope scope;
7414 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7415 v8::Handle<v8::String> access_property = v8::String::New("a");
7416 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
7417 LocalContext context(NULL, templ);
7418 v8::Handle<v8::Object> global = context->Global();
7419
7420 // Ordinary properties
7421 v8::Handle<v8::String> simple_property = v8::String::New("p");
7422 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
7423 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7424 // This should fail because the property is read-only
7425 global->Set(simple_property, v8::Int32::New(5));
7426 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7427 // This should succeed even though the property is read-only
7428 global->ForceSet(simple_property, v8::Int32::New(6));
7429 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
7430
7431 // Accessors
7432 CHECK_EQ(0, force_set_set_count);
7433 CHECK_EQ(0, force_set_get_count);
7434 CHECK_EQ(3, global->Get(access_property)->Int32Value());
7435 // CHECK_EQ the property shouldn't override it, just call the setter
7436 // which in this case does nothing.
7437 global->Set(access_property, v8::Int32::New(7));
7438 CHECK_EQ(3, global->Get(access_property)->Int32Value());
7439 CHECK_EQ(1, force_set_set_count);
7440 CHECK_EQ(2, force_set_get_count);
7441 // Forcing the property to be set should override the accessor without
7442 // calling it
7443 global->ForceSet(access_property, v8::Int32::New(8));
7444 CHECK_EQ(8, global->Get(access_property)->Int32Value());
7445 CHECK_EQ(1, force_set_set_count);
7446 CHECK_EQ(2, force_set_get_count);
7447 }
7448
TEST(ForceSetWithInterceptor)7449 TEST(ForceSetWithInterceptor) {
7450 force_set_get_count = 0;
7451 force_set_set_count = 0;
7452 pass_on_get = false;
7453
7454 v8::HandleScope scope;
7455 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7456 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
7457 LocalContext context(NULL, templ);
7458 v8::Handle<v8::Object> global = context->Global();
7459
7460 v8::Handle<v8::String> some_property = v8::String::New("a");
7461 CHECK_EQ(0, force_set_set_count);
7462 CHECK_EQ(0, force_set_get_count);
7463 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7464 // Setting the property shouldn't override it, just call the setter
7465 // which in this case does nothing.
7466 global->Set(some_property, v8::Int32::New(7));
7467 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7468 CHECK_EQ(1, force_set_set_count);
7469 CHECK_EQ(2, force_set_get_count);
7470 // Getting the property when the interceptor returns an empty handle
7471 // should yield undefined, since the property isn't present on the
7472 // object itself yet.
7473 pass_on_get = true;
7474 CHECK(global->Get(some_property)->IsUndefined());
7475 CHECK_EQ(1, force_set_set_count);
7476 CHECK_EQ(3, force_set_get_count);
7477 // Forcing the property to be set should cause the value to be
7478 // set locally without calling the interceptor.
7479 global->ForceSet(some_property, v8::Int32::New(8));
7480 CHECK_EQ(8, global->Get(some_property)->Int32Value());
7481 CHECK_EQ(1, force_set_set_count);
7482 CHECK_EQ(4, force_set_get_count);
7483 // Reenabling the interceptor should cause it to take precedence over
7484 // the property
7485 pass_on_get = false;
7486 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7487 CHECK_EQ(1, force_set_set_count);
7488 CHECK_EQ(5, force_set_get_count);
7489 // The interceptor should also work for other properties
7490 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
7491 CHECK_EQ(1, force_set_set_count);
7492 CHECK_EQ(6, force_set_get_count);
7493 }
7494
7495
THREADED_TEST(ForceDelete)7496 THREADED_TEST(ForceDelete) {
7497 v8::HandleScope scope;
7498 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7499 LocalContext context(NULL, templ);
7500 v8::Handle<v8::Object> global = context->Global();
7501
7502 // Ordinary properties
7503 v8::Handle<v8::String> simple_property = v8::String::New("p");
7504 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
7505 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7506 // This should fail because the property is dont-delete.
7507 CHECK(!global->Delete(simple_property));
7508 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7509 // This should succeed even though the property is dont-delete.
7510 CHECK(global->ForceDelete(simple_property));
7511 CHECK(global->Get(simple_property)->IsUndefined());
7512 }
7513
7514
7515 static int force_delete_interceptor_count = 0;
7516 static bool pass_on_delete = false;
7517
7518
ForceDeleteDeleter(v8::Local<v8::String> name,const v8::AccessorInfo & info)7519 static v8::Handle<v8::Boolean> ForceDeleteDeleter(
7520 v8::Local<v8::String> name,
7521 const v8::AccessorInfo& info) {
7522 force_delete_interceptor_count++;
7523 if (pass_on_delete) {
7524 return v8::Handle<v8::Boolean>();
7525 } else {
7526 return v8::True();
7527 }
7528 }
7529
7530
THREADED_TEST(ForceDeleteWithInterceptor)7531 THREADED_TEST(ForceDeleteWithInterceptor) {
7532 force_delete_interceptor_count = 0;
7533 pass_on_delete = false;
7534
7535 v8::HandleScope scope;
7536 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7537 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
7538 LocalContext context(NULL, templ);
7539 v8::Handle<v8::Object> global = context->Global();
7540
7541 v8::Handle<v8::String> some_property = v8::String::New("a");
7542 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
7543
7544 // Deleting a property should get intercepted and nothing should
7545 // happen.
7546 CHECK_EQ(0, force_delete_interceptor_count);
7547 CHECK(global->Delete(some_property));
7548 CHECK_EQ(1, force_delete_interceptor_count);
7549 CHECK_EQ(42, global->Get(some_property)->Int32Value());
7550 // Deleting the property when the interceptor returns an empty
7551 // handle should not delete the property since it is DontDelete.
7552 pass_on_delete = true;
7553 CHECK(!global->Delete(some_property));
7554 CHECK_EQ(2, force_delete_interceptor_count);
7555 CHECK_EQ(42, global->Get(some_property)->Int32Value());
7556 // Forcing the property to be deleted should delete the value
7557 // without calling the interceptor.
7558 CHECK(global->ForceDelete(some_property));
7559 CHECK(global->Get(some_property)->IsUndefined());
7560 CHECK_EQ(2, force_delete_interceptor_count);
7561 }
7562
7563
7564 // Make sure that forcing a delete invalidates any IC stubs, so we
7565 // don't read the hole value.
THREADED_TEST(ForceDeleteIC)7566 THREADED_TEST(ForceDeleteIC) {
7567 v8::HandleScope scope;
7568 LocalContext context;
7569 // Create a DontDelete variable on the global object.
7570 CompileRun("this.__proto__ = { foo: 'horse' };"
7571 "var foo = 'fish';"
7572 "function f() { return foo.length; }");
7573 // Initialize the IC for foo in f.
7574 CompileRun("for (var i = 0; i < 4; i++) f();");
7575 // Make sure the value of foo is correct before the deletion.
7576 CHECK_EQ(4, CompileRun("f()")->Int32Value());
7577 // Force the deletion of foo.
7578 CHECK(context->Global()->ForceDelete(v8_str("foo")));
7579 // Make sure the value for foo is read from the prototype, and that
7580 // we don't get in trouble with reading the deleted cell value
7581 // sentinel.
7582 CHECK_EQ(5, CompileRun("f()")->Int32Value());
7583 }
7584
7585
7586 v8::Persistent<Context> calling_context0;
7587 v8::Persistent<Context> calling_context1;
7588 v8::Persistent<Context> calling_context2;
7589
7590
7591 // Check that the call to the callback is initiated in
7592 // calling_context2, the directly calling context is calling_context1
7593 // and the callback itself is in calling_context0.
GetCallingContextCallback(const v8::Arguments & args)7594 static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
7595 ApiTestFuzzer::Fuzz();
7596 CHECK(Context::GetCurrent() == calling_context0);
7597 CHECK(Context::GetCalling() == calling_context1);
7598 CHECK(Context::GetEntered() == calling_context2);
7599 return v8::Integer::New(42);
7600 }
7601
7602
THREADED_TEST(GetCallingContext)7603 THREADED_TEST(GetCallingContext) {
7604 v8::HandleScope scope;
7605
7606 calling_context0 = Context::New();
7607 calling_context1 = Context::New();
7608 calling_context2 = Context::New();
7609
7610 // Allow cross-domain access.
7611 Local<String> token = v8_str("<security token>");
7612 calling_context0->SetSecurityToken(token);
7613 calling_context1->SetSecurityToken(token);
7614 calling_context2->SetSecurityToken(token);
7615
7616 // Create an object with a C++ callback in context0.
7617 calling_context0->Enter();
7618 Local<v8::FunctionTemplate> callback_templ =
7619 v8::FunctionTemplate::New(GetCallingContextCallback);
7620 calling_context0->Global()->Set(v8_str("callback"),
7621 callback_templ->GetFunction());
7622 calling_context0->Exit();
7623
7624 // Expose context0 in context1 and setup a function that calls the
7625 // callback function.
7626 calling_context1->Enter();
7627 calling_context1->Global()->Set(v8_str("context0"),
7628 calling_context0->Global());
7629 CompileRun("function f() { context0.callback() }");
7630 calling_context1->Exit();
7631
7632 // Expose context1 in context2 and call the callback function in
7633 // context0 indirectly through f in context1.
7634 calling_context2->Enter();
7635 calling_context2->Global()->Set(v8_str("context1"),
7636 calling_context1->Global());
7637 CompileRun("context1.f()");
7638 calling_context2->Exit();
7639
7640 // Dispose the contexts to allow them to be garbage collected.
7641 calling_context0.Dispose();
7642 calling_context1.Dispose();
7643 calling_context2.Dispose();
7644 calling_context0.Clear();
7645 calling_context1.Clear();
7646 calling_context2.Clear();
7647 }
7648
7649
7650 // Check that a variable declaration with no explicit initialization
7651 // value does not shadow an existing property in the prototype chain.
7652 //
7653 // This is consistent with Firefox and Safari.
7654 //
7655 // See http://crbug.com/12548.
THREADED_TEST(InitGlobalVarInProtoChain)7656 THREADED_TEST(InitGlobalVarInProtoChain) {
7657 v8::HandleScope scope;
7658 LocalContext context;
7659 // Introduce a variable in the prototype chain.
7660 CompileRun("__proto__.x = 42");
7661 v8::Handle<v8::Value> result = CompileRun("var x; x");
7662 CHECK(!result->IsUndefined());
7663 CHECK_EQ(42, result->Int32Value());
7664 }
7665
7666
7667 // Regression test for issue 398.
7668 // If a function is added to an object, creating a constant function
7669 // field, and the result is cloned, replacing the constant function on the
7670 // original should not affect the clone.
7671 // See http://code.google.com/p/v8/issues/detail?id=398
THREADED_TEST(ReplaceConstantFunction)7672 THREADED_TEST(ReplaceConstantFunction) {
7673 v8::HandleScope scope;
7674 LocalContext context;
7675 v8::Handle<v8::Object> obj = v8::Object::New();
7676 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
7677 v8::Handle<v8::String> foo_string = v8::String::New("foo");
7678 obj->Set(foo_string, func_templ->GetFunction());
7679 v8::Handle<v8::Object> obj_clone = obj->Clone();
7680 obj_clone->Set(foo_string, v8::String::New("Hello"));
7681 CHECK(!obj->Get(foo_string)->IsUndefined());
7682 }
7683
7684
7685 // Regression test for http://crbug.com/16276.
THREADED_TEST(Regress16276)7686 THREADED_TEST(Regress16276) {
7687 v8::HandleScope scope;
7688 LocalContext context;
7689 // Force the IC in f to be a dictionary load IC.
7690 CompileRun("function f(obj) { return obj.x; }\n"
7691 "var obj = { x: { foo: 42 }, y: 87 };\n"
7692 "var x = obj.x;\n"
7693 "delete obj.y;\n"
7694 "for (var i = 0; i < 5; i++) f(obj);");
7695 // Detach the global object to make 'this' refer directly to the
7696 // global object (not the proxy), and make sure that the dictionary
7697 // load IC doesn't mess up loading directly from the global object.
7698 context->DetachGlobal();
7699 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
7700 }
7701
7702
THREADED_TEST(PixelArray)7703 THREADED_TEST(PixelArray) {
7704 v8::HandleScope scope;
7705 LocalContext context;
7706 const int kElementCount = 40;
7707 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
7708 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
7709 pixel_data);
7710 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
7711 for (int i = 0; i < kElementCount; i++) {
7712 pixels->set(i, i);
7713 }
7714 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
7715 for (int i = 0; i < kElementCount; i++) {
7716 CHECK_EQ(i, pixels->get(i));
7717 CHECK_EQ(i, pixel_data[i]);
7718 }
7719
7720 v8::Handle<v8::Object> obj = v8::Object::New();
7721 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
7722 // Set the elements to be the pixels.
7723 // jsobj->set_elements(*pixels);
7724 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
7725 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
7726 obj->Set(v8_str("field"), v8::Int32::New(1503));
7727 context->Global()->Set(v8_str("pixels"), obj);
7728 v8::Handle<v8::Value> result = CompileRun("pixels.field");
7729 CHECK_EQ(1503, result->Int32Value());
7730 result = CompileRun("pixels[1]");
7731 CHECK_EQ(1, result->Int32Value());
7732 result = CompileRun("var sum = 0;"
7733 "for (var i = 0; i < 8; i++) {"
7734 " sum += pixels[i];"
7735 "}"
7736 "sum;");
7737 CHECK_EQ(28, result->Int32Value());
7738
7739 i::Handle<i::Smi> value(i::Smi::FromInt(2));
7740 i::SetElement(jsobj, 1, value);
7741 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
7742 *value.location() = i::Smi::FromInt(256);
7743 i::SetElement(jsobj, 1, value);
7744 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
7745 *value.location() = i::Smi::FromInt(-1);
7746 i::SetElement(jsobj, 1, value);
7747 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
7748
7749 result = CompileRun("for (var i = 0; i < 8; i++) {"
7750 " pixels[i] = (i * 65) - 109;"
7751 "}"
7752 "pixels[1] + pixels[6];");
7753 CHECK_EQ(255, result->Int32Value());
7754 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
7755 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
7756 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
7757 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
7758 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
7759 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
7760 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
7761 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
7762 result = CompileRun("var sum = 0;"
7763 "for (var i = 0; i < 8; i++) {"
7764 " sum += pixels[i];"
7765 "}"
7766 "sum;");
7767 CHECK_EQ(984, result->Int32Value());
7768
7769 result = CompileRun("for (var i = 0; i < 8; i++) {"
7770 " pixels[i] = (i * 1.1);"
7771 "}"
7772 "pixels[1] + pixels[6];");
7773 CHECK_EQ(8, result->Int32Value());
7774 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
7775 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
7776 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
7777 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
7778 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
7779 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
7780 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
7781 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
7782
7783 result = CompileRun("for (var i = 0; i < 8; i++) {"
7784 " pixels[7] = undefined;"
7785 "}"
7786 "pixels[7];");
7787 CHECK_EQ(0, result->Int32Value());
7788 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
7789
7790 result = CompileRun("for (var i = 0; i < 8; i++) {"
7791 " pixels[6] = '2.3';"
7792 "}"
7793 "pixels[6];");
7794 CHECK_EQ(2, result->Int32Value());
7795 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
7796
7797 result = CompileRun("for (var i = 0; i < 8; i++) {"
7798 " pixels[5] = NaN;"
7799 "}"
7800 "pixels[5];");
7801 CHECK_EQ(0, result->Int32Value());
7802 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
7803
7804 result = CompileRun("for (var i = 0; i < 8; i++) {"
7805 " pixels[8] = Infinity;"
7806 "}"
7807 "pixels[8];");
7808 CHECK_EQ(255, result->Int32Value());
7809 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
7810
7811 result = CompileRun("for (var i = 0; i < 8; i++) {"
7812 " pixels[9] = -Infinity;"
7813 "}"
7814 "pixels[9];");
7815 CHECK_EQ(0, result->Int32Value());
7816 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
7817
7818 result = CompileRun("pixels[3] = 33;"
7819 "delete pixels[3];"
7820 "pixels[3];");
7821 CHECK_EQ(33, result->Int32Value());
7822
7823 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
7824 "pixels[2] = 12; pixels[3] = 13;"
7825 "pixels.__defineGetter__('2',"
7826 "function() { return 120; });"
7827 "pixels[2];");
7828 CHECK_EQ(12, result->Int32Value());
7829
7830 result = CompileRun("var js_array = new Array(40);"
7831 "js_array[0] = 77;"
7832 "js_array;");
7833 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
7834
7835 result = CompileRun("pixels[1] = 23;"
7836 "pixels.__proto__ = [];"
7837 "js_array.__proto__ = pixels;"
7838 "js_array.concat(pixels);");
7839 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
7840 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
7841
7842 free(pixel_data);
7843 }
7844
7845
THREADED_TEST(ScriptContextDependence)7846 THREADED_TEST(ScriptContextDependence) {
7847 v8::HandleScope scope;
7848 LocalContext c1;
7849 const char *source = "foo";
7850 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
7851 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
7852 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
7853 CHECK_EQ(dep->Run()->Int32Value(), 100);
7854 CHECK_EQ(indep->Run()->Int32Value(), 100);
7855 LocalContext c2;
7856 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
7857 CHECK_EQ(dep->Run()->Int32Value(), 100);
7858 CHECK_EQ(indep->Run()->Int32Value(), 101);
7859 }
7860
7861
THREADED_TEST(StackTrace)7862 THREADED_TEST(StackTrace) {
7863 v8::HandleScope scope;
7864 LocalContext context;
7865 v8::TryCatch try_catch;
7866 const char *source = "function foo() { FAIL.FAIL; }; foo();";
7867 v8::Handle<v8::String> src = v8::String::New(source);
7868 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
7869 v8::Script::New(src, origin)->Run();
7870 CHECK(try_catch.HasCaught());
7871 v8::String::Utf8Value stack(try_catch.StackTrace());
7872 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
7873 }
7874
7875
7876 // Test that idle notification can be handled when V8 has not yet been
7877 // set up.
THREADED_TEST(IdleNotification)7878 THREADED_TEST(IdleNotification) {
7879 for (int i = 0; i < 100; i++) v8::V8::IdleNotification(true);
7880 for (int i = 0; i < 100; i++) v8::V8::IdleNotification(false);
7881 }
7882