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