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