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