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