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