blob: f71b3258a8a34afba89f1576b45870c3f7e2ddc5 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2007-2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
Steve Block3ce2e202009-11-05 08:53:23 +000028#include <limits.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000029
30#include "v8.h"
31
32#include "api.h"
33#include "compilation-cache.h"
34#include "execution.h"
35#include "snapshot.h"
36#include "platform.h"
37#include "top.h"
Steve Block3ce2e202009-11-05 08:53:23 +000038#include "utils.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000039#include "cctest.h"
40
Steve Blockd0582a62009-12-15 09:54:21 +000041static const bool kLogThreading = false;
42
Steve Blocka7e24c12009-10-30 11:49:00 +000043static bool IsNaN(double x) {
44#ifdef WIN32
45 return _isnan(x);
46#else
47 return isnan(x);
48#endif
49}
50
51using ::v8::ObjectTemplate;
52using ::v8::Value;
53using ::v8::Context;
54using ::v8::Local;
55using ::v8::String;
56using ::v8::Script;
57using ::v8::Function;
58using ::v8::AccessorInfo;
59using ::v8::Extension;
60
61namespace i = ::v8::internal;
62
Steve Blocka7e24c12009-10-30 11:49:00 +000063
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
2300
2301v8::Persistent<Value> xValue;
2302
2303
2304static void SetXValue(Local<String> name,
2305 Local<Value> value,
2306 const AccessorInfo& info) {
2307 CHECK_EQ(value, v8_num(4));
2308 CHECK_EQ(info.Data(), v8_str("donut"));
2309 CHECK_EQ(name, v8_str("x"));
2310 CHECK(xValue.IsEmpty());
2311 xValue = v8::Persistent<Value>::New(value);
2312}
2313
2314
2315THREADED_TEST(SimplePropertyWrite) {
2316 v8::HandleScope scope;
2317 Local<ObjectTemplate> templ = ObjectTemplate::New();
2318 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2319 LocalContext context;
2320 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2321 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2322 for (int i = 0; i < 10; i++) {
2323 CHECK(xValue.IsEmpty());
2324 script->Run();
2325 CHECK_EQ(v8_num(4), xValue);
2326 xValue.Dispose();
2327 xValue = v8::Persistent<Value>();
2328 }
2329}
2330
2331
2332static v8::Handle<Value> XPropertyGetter(Local<String> property,
2333 const AccessorInfo& info) {
2334 ApiTestFuzzer::Fuzz();
2335 CHECK(info.Data()->IsUndefined());
2336 return property;
2337}
2338
2339
2340THREADED_TEST(NamedInterceptorPropertyRead) {
2341 v8::HandleScope scope;
2342 Local<ObjectTemplate> templ = ObjectTemplate::New();
2343 templ->SetNamedPropertyHandler(XPropertyGetter);
2344 LocalContext context;
2345 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2346 Local<Script> script = Script::Compile(v8_str("obj.x"));
2347 for (int i = 0; i < 10; i++) {
2348 Local<Value> result = script->Run();
2349 CHECK_EQ(result, v8_str("x"));
2350 }
2351}
2352
2353
2354static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2355 const AccessorInfo& info) {
2356 ApiTestFuzzer::Fuzz();
2357 if (index == 37) {
2358 return v8::Handle<Value>(v8_num(625));
2359 }
2360 return v8::Handle<Value>();
2361}
2362
2363
2364static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2365 Local<Value> value,
2366 const AccessorInfo& info) {
2367 ApiTestFuzzer::Fuzz();
2368 if (index == 39) {
2369 return value;
2370 }
2371 return v8::Handle<Value>();
2372}
2373
2374
2375THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2376 v8::HandleScope scope;
2377 Local<ObjectTemplate> templ = ObjectTemplate::New();
2378 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2379 IndexedPropertySetter);
2380 LocalContext context;
2381 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2382 Local<Script> getter_script = Script::Compile(v8_str(
2383 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2384 Local<Script> setter_script = Script::Compile(v8_str(
2385 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2386 "obj[17] = 23;"
2387 "obj.foo;"));
2388 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
2389 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
2390 "obj[39] = 47;"
2391 "obj.foo;")); // This setter should not run, due to the interceptor.
2392 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
2393 "obj[37];"));
2394 Local<Value> result = getter_script->Run();
2395 CHECK_EQ(v8_num(5), result);
2396 result = setter_script->Run();
2397 CHECK_EQ(v8_num(23), result);
2398 result = interceptor_setter_script->Run();
2399 CHECK_EQ(v8_num(23), result);
2400 result = interceptor_getter_script->Run();
2401 CHECK_EQ(v8_num(625), result);
2402}
2403
2404
Leon Clarked91b9f72010-01-27 17:25:45 +00002405static v8::Handle<Value> IdentityIndexedPropertyGetter(
2406 uint32_t index,
2407 const AccessorInfo& info) {
2408 return v8::Integer::New(index);
2409}
2410
2411
2412THREADED_TEST(IndexedInterceptorWithNoSetter) {
2413 v8::HandleScope scope;
2414 Local<ObjectTemplate> templ = ObjectTemplate::New();
2415 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
2416
2417 LocalContext context;
2418 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2419
2420 const char* code =
2421 "try {"
2422 " obj[0] = 239;"
2423 " for (var i = 0; i < 100; i++) {"
2424 " var v = obj[0];"
2425 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
2426 " }"
2427 " 'PASSED'"
2428 "} catch(e) {"
2429 " e"
2430 "}";
2431 ExpectString(code, "PASSED");
2432}
2433
2434
Steve Blocka7e24c12009-10-30 11:49:00 +00002435THREADED_TEST(MultiContexts) {
2436 v8::HandleScope scope;
2437 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
2438 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
2439
2440 Local<String> password = v8_str("Password");
2441
2442 // Create an environment
2443 LocalContext context0(0, templ);
2444 context0->SetSecurityToken(password);
2445 v8::Handle<v8::Object> global0 = context0->Global();
2446 global0->Set(v8_str("custom"), v8_num(1234));
2447 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
2448
2449 // Create an independent environment
2450 LocalContext context1(0, templ);
2451 context1->SetSecurityToken(password);
2452 v8::Handle<v8::Object> global1 = context1->Global();
2453 global1->Set(v8_str("custom"), v8_num(1234));
2454 CHECK_NE(global0, global1);
2455 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
2456 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
2457
2458 // Now create a new context with the old global
2459 LocalContext context2(0, templ, global1);
2460 context2->SetSecurityToken(password);
2461 v8::Handle<v8::Object> global2 = context2->Global();
2462 CHECK_EQ(global1, global2);
2463 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
2464 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
2465}
2466
2467
2468THREADED_TEST(FunctionPrototypeAcrossContexts) {
2469 // Make sure that functions created by cloning boilerplates cannot
2470 // communicate through their __proto__ field.
2471
2472 v8::HandleScope scope;
2473
2474 LocalContext env0;
2475 v8::Handle<v8::Object> global0 =
2476 env0->Global();
2477 v8::Handle<v8::Object> object0 =
2478 v8::Handle<v8::Object>::Cast(global0->Get(v8_str("Object")));
2479 v8::Handle<v8::Object> tostring0 =
2480 v8::Handle<v8::Object>::Cast(object0->Get(v8_str("toString")));
2481 v8::Handle<v8::Object> proto0 =
2482 v8::Handle<v8::Object>::Cast(tostring0->Get(v8_str("__proto__")));
2483 proto0->Set(v8_str("custom"), v8_num(1234));
2484
2485 LocalContext env1;
2486 v8::Handle<v8::Object> global1 =
2487 env1->Global();
2488 v8::Handle<v8::Object> object1 =
2489 v8::Handle<v8::Object>::Cast(global1->Get(v8_str("Object")));
2490 v8::Handle<v8::Object> tostring1 =
2491 v8::Handle<v8::Object>::Cast(object1->Get(v8_str("toString")));
2492 v8::Handle<v8::Object> proto1 =
2493 v8::Handle<v8::Object>::Cast(tostring1->Get(v8_str("__proto__")));
2494 CHECK(!proto1->Has(v8_str("custom")));
2495}
2496
2497
2498THREADED_TEST(Regress892105) {
2499 // Make sure that object and array literals created by cloning
2500 // boilerplates cannot communicate through their __proto__
2501 // field. This is rather difficult to check, but we try to add stuff
2502 // to Object.prototype and Array.prototype and create a new
2503 // environment. This should succeed.
2504
2505 v8::HandleScope scope;
2506
2507 Local<String> source = v8_str("Object.prototype.obj = 1234;"
2508 "Array.prototype.arr = 4567;"
2509 "8901");
2510
2511 LocalContext env0;
2512 Local<Script> script0 = Script::Compile(source);
2513 CHECK_EQ(8901.0, script0->Run()->NumberValue());
2514
2515 LocalContext env1;
2516 Local<Script> script1 = Script::Compile(source);
2517 CHECK_EQ(8901.0, script1->Run()->NumberValue());
2518}
2519
2520
Steve Blocka7e24c12009-10-30 11:49:00 +00002521THREADED_TEST(UndetectableObject) {
2522 v8::HandleScope scope;
2523 LocalContext env;
2524
2525 Local<v8::FunctionTemplate> desc =
2526 v8::FunctionTemplate::New(0, v8::Handle<Value>());
2527 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
2528
2529 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
2530 env->Global()->Set(v8_str("undetectable"), obj);
2531
2532 ExpectString("undetectable.toString()", "[object Object]");
2533 ExpectString("typeof undetectable", "undefined");
2534 ExpectString("typeof(undetectable)", "undefined");
2535 ExpectBoolean("typeof undetectable == 'undefined'", true);
2536 ExpectBoolean("typeof undetectable == 'object'", false);
2537 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
2538 ExpectBoolean("!undetectable", true);
2539
2540 ExpectObject("true&&undetectable", obj);
2541 ExpectBoolean("false&&undetectable", false);
2542 ExpectBoolean("true||undetectable", true);
2543 ExpectObject("false||undetectable", obj);
2544
2545 ExpectObject("undetectable&&true", obj);
2546 ExpectObject("undetectable&&false", obj);
2547 ExpectBoolean("undetectable||true", true);
2548 ExpectBoolean("undetectable||false", false);
2549
2550 ExpectBoolean("undetectable==null", true);
2551 ExpectBoolean("null==undetectable", true);
2552 ExpectBoolean("undetectable==undefined", true);
2553 ExpectBoolean("undefined==undetectable", true);
2554 ExpectBoolean("undetectable==undetectable", true);
2555
2556
2557 ExpectBoolean("undetectable===null", false);
2558 ExpectBoolean("null===undetectable", false);
2559 ExpectBoolean("undetectable===undefined", false);
2560 ExpectBoolean("undefined===undetectable", false);
2561 ExpectBoolean("undetectable===undetectable", true);
2562}
2563
2564
2565THREADED_TEST(UndetectableString) {
2566 v8::HandleScope scope;
2567 LocalContext env;
2568
2569 Local<String> obj = String::NewUndetectable("foo");
2570 env->Global()->Set(v8_str("undetectable"), obj);
2571
2572 ExpectString("undetectable", "foo");
2573 ExpectString("typeof undetectable", "undefined");
2574 ExpectString("typeof(undetectable)", "undefined");
2575 ExpectBoolean("typeof undetectable == 'undefined'", true);
2576 ExpectBoolean("typeof undetectable == 'string'", false);
2577 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
2578 ExpectBoolean("!undetectable", true);
2579
2580 ExpectObject("true&&undetectable", obj);
2581 ExpectBoolean("false&&undetectable", false);
2582 ExpectBoolean("true||undetectable", true);
2583 ExpectObject("false||undetectable", obj);
2584
2585 ExpectObject("undetectable&&true", obj);
2586 ExpectObject("undetectable&&false", obj);
2587 ExpectBoolean("undetectable||true", true);
2588 ExpectBoolean("undetectable||false", false);
2589
2590 ExpectBoolean("undetectable==null", true);
2591 ExpectBoolean("null==undetectable", true);
2592 ExpectBoolean("undetectable==undefined", true);
2593 ExpectBoolean("undefined==undetectable", true);
2594 ExpectBoolean("undetectable==undetectable", true);
2595
2596
2597 ExpectBoolean("undetectable===null", false);
2598 ExpectBoolean("null===undetectable", false);
2599 ExpectBoolean("undetectable===undefined", false);
2600 ExpectBoolean("undefined===undetectable", false);
2601 ExpectBoolean("undetectable===undetectable", true);
2602}
2603
2604
2605template <typename T> static void USE(T) { }
2606
2607
2608// This test is not intended to be run, just type checked.
2609static void PersistentHandles() {
2610 USE(PersistentHandles);
2611 Local<String> str = v8_str("foo");
2612 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
2613 USE(p_str);
2614 Local<Script> scr = Script::Compile(v8_str(""));
2615 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
2616 USE(p_scr);
2617 Local<ObjectTemplate> templ = ObjectTemplate::New();
2618 v8::Persistent<ObjectTemplate> p_templ =
2619 v8::Persistent<ObjectTemplate>::New(templ);
2620 USE(p_templ);
2621}
2622
2623
2624static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
2625 ApiTestFuzzer::Fuzz();
2626 return v8::Undefined();
2627}
2628
2629
2630THREADED_TEST(GlobalObjectTemplate) {
2631 v8::HandleScope handle_scope;
2632 Local<ObjectTemplate> global_template = ObjectTemplate::New();
2633 global_template->Set(v8_str("JSNI_Log"),
2634 v8::FunctionTemplate::New(HandleLogDelegator));
2635 v8::Persistent<Context> context = Context::New(0, global_template);
2636 Context::Scope context_scope(context);
2637 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
2638 context.Dispose();
2639}
2640
2641
2642static const char* kSimpleExtensionSource =
2643 "function Foo() {"
2644 " return 4;"
2645 "}";
2646
2647
2648THREADED_TEST(SimpleExtensions) {
2649 v8::HandleScope handle_scope;
2650 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
2651 const char* extension_names[] = { "simpletest" };
2652 v8::ExtensionConfiguration extensions(1, extension_names);
2653 v8::Handle<Context> context = Context::New(&extensions);
2654 Context::Scope lock(context);
2655 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
2656 CHECK_EQ(result, v8::Integer::New(4));
2657}
2658
2659
2660static const char* kEvalExtensionSource1 =
2661 "function UseEval1() {"
2662 " var x = 42;"
2663 " return eval('x');"
2664 "}";
2665
2666
2667static const char* kEvalExtensionSource2 =
2668 "(function() {"
2669 " var x = 42;"
2670 " function e() {"
2671 " return eval('x');"
2672 " }"
2673 " this.UseEval2 = e;"
2674 "})()";
2675
2676
2677THREADED_TEST(UseEvalFromExtension) {
2678 v8::HandleScope handle_scope;
2679 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
2680 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
2681 const char* extension_names[] = { "evaltest1", "evaltest2" };
2682 v8::ExtensionConfiguration extensions(2, extension_names);
2683 v8::Handle<Context> context = Context::New(&extensions);
2684 Context::Scope lock(context);
2685 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
2686 CHECK_EQ(result, v8::Integer::New(42));
2687 result = Script::Compile(v8_str("UseEval2()"))->Run();
2688 CHECK_EQ(result, v8::Integer::New(42));
2689}
2690
2691
2692static const char* kWithExtensionSource1 =
2693 "function UseWith1() {"
2694 " var x = 42;"
2695 " with({x:87}) { return x; }"
2696 "}";
2697
2698
2699
2700static const char* kWithExtensionSource2 =
2701 "(function() {"
2702 " var x = 42;"
2703 " function e() {"
2704 " with ({x:87}) { return x; }"
2705 " }"
2706 " this.UseWith2 = e;"
2707 "})()";
2708
2709
2710THREADED_TEST(UseWithFromExtension) {
2711 v8::HandleScope handle_scope;
2712 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
2713 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
2714 const char* extension_names[] = { "withtest1", "withtest2" };
2715 v8::ExtensionConfiguration extensions(2, extension_names);
2716 v8::Handle<Context> context = Context::New(&extensions);
2717 Context::Scope lock(context);
2718 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
2719 CHECK_EQ(result, v8::Integer::New(87));
2720 result = Script::Compile(v8_str("UseWith2()"))->Run();
2721 CHECK_EQ(result, v8::Integer::New(87));
2722}
2723
2724
2725THREADED_TEST(AutoExtensions) {
2726 v8::HandleScope handle_scope;
2727 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
2728 extension->set_auto_enable(true);
2729 v8::RegisterExtension(extension);
2730 v8::Handle<Context> context = Context::New();
2731 Context::Scope lock(context);
2732 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
2733 CHECK_EQ(result, v8::Integer::New(4));
2734}
2735
2736
Steve Blockd0582a62009-12-15 09:54:21 +00002737static const char* kSyntaxErrorInExtensionSource =
2738 "[";
2739
2740
2741// Test that a syntax error in an extension does not cause a fatal
2742// error but results in an empty context.
2743THREADED_TEST(SyntaxErrorExtensions) {
2744 v8::HandleScope handle_scope;
2745 v8::RegisterExtension(new Extension("syntaxerror",
2746 kSyntaxErrorInExtensionSource));
2747 const char* extension_names[] = { "syntaxerror" };
2748 v8::ExtensionConfiguration extensions(1, extension_names);
2749 v8::Handle<Context> context = Context::New(&extensions);
2750 CHECK(context.IsEmpty());
2751}
2752
2753
2754static const char* kExceptionInExtensionSource =
2755 "throw 42";
2756
2757
2758// Test that an exception when installing an extension does not cause
2759// a fatal error but results in an empty context.
2760THREADED_TEST(ExceptionExtensions) {
2761 v8::HandleScope handle_scope;
2762 v8::RegisterExtension(new Extension("exception",
2763 kExceptionInExtensionSource));
2764 const char* extension_names[] = { "exception" };
2765 v8::ExtensionConfiguration extensions(1, extension_names);
2766 v8::Handle<Context> context = Context::New(&extensions);
2767 CHECK(context.IsEmpty());
2768}
2769
2770
Steve Blocka7e24c12009-10-30 11:49:00 +00002771static void CheckDependencies(const char* name, const char* expected) {
2772 v8::HandleScope handle_scope;
2773 v8::ExtensionConfiguration config(1, &name);
2774 LocalContext context(&config);
2775 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
2776}
2777
2778
2779/*
2780 * Configuration:
2781 *
2782 * /-- B <--\
2783 * A <- -- D <-- E
2784 * \-- C <--/
2785 */
2786THREADED_TEST(ExtensionDependency) {
2787 static const char* kEDeps[] = { "D" };
2788 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
2789 static const char* kDDeps[] = { "B", "C" };
2790 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
2791 static const char* kBCDeps[] = { "A" };
2792 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
2793 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
2794 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
2795 CheckDependencies("A", "undefinedA");
2796 CheckDependencies("B", "undefinedAB");
2797 CheckDependencies("C", "undefinedAC");
2798 CheckDependencies("D", "undefinedABCD");
2799 CheckDependencies("E", "undefinedABCDE");
2800 v8::HandleScope handle_scope;
2801 static const char* exts[2] = { "C", "E" };
2802 v8::ExtensionConfiguration config(2, exts);
2803 LocalContext context(&config);
2804 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
2805}
2806
2807
2808static const char* kExtensionTestScript =
2809 "native function A();"
2810 "native function B();"
2811 "native function C();"
2812 "function Foo(i) {"
2813 " if (i == 0) return A();"
2814 " if (i == 1) return B();"
2815 " if (i == 2) return C();"
2816 "}";
2817
2818
2819static v8::Handle<Value> CallFun(const v8::Arguments& args) {
2820 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00002821 if (args.IsConstructCall()) {
2822 args.This()->Set(v8_str("data"), args.Data());
2823 return v8::Null();
2824 }
Steve Blocka7e24c12009-10-30 11:49:00 +00002825 return args.Data();
2826}
2827
2828
2829class FunctionExtension : public Extension {
2830 public:
2831 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
2832 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
2833 v8::Handle<String> name);
2834};
2835
2836
2837static int lookup_count = 0;
2838v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
2839 v8::Handle<String> name) {
2840 lookup_count++;
2841 if (name->Equals(v8_str("A"))) {
2842 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
2843 } else if (name->Equals(v8_str("B"))) {
2844 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
2845 } else if (name->Equals(v8_str("C"))) {
2846 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
2847 } else {
2848 return v8::Handle<v8::FunctionTemplate>();
2849 }
2850}
2851
2852
2853THREADED_TEST(FunctionLookup) {
2854 v8::RegisterExtension(new FunctionExtension());
2855 v8::HandleScope handle_scope;
2856 static const char* exts[1] = { "functiontest" };
2857 v8::ExtensionConfiguration config(1, exts);
2858 LocalContext context(&config);
2859 CHECK_EQ(3, lookup_count);
2860 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
2861 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
2862 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
2863}
2864
2865
Leon Clarkee46be812010-01-19 14:06:41 +00002866THREADED_TEST(NativeFunctionConstructCall) {
2867 v8::RegisterExtension(new FunctionExtension());
2868 v8::HandleScope handle_scope;
2869 static const char* exts[1] = { "functiontest" };
2870 v8::ExtensionConfiguration config(1, exts);
2871 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00002872 for (int i = 0; i < 10; i++) {
2873 // Run a few times to ensure that allocation of objects doesn't
2874 // change behavior of a constructor function.
2875 CHECK_EQ(v8::Integer::New(8),
2876 Script::Compile(v8_str("(new A()).data"))->Run());
2877 CHECK_EQ(v8::Integer::New(7),
2878 Script::Compile(v8_str("(new B()).data"))->Run());
2879 CHECK_EQ(v8::Integer::New(6),
2880 Script::Compile(v8_str("(new C()).data"))->Run());
2881 }
Leon Clarkee46be812010-01-19 14:06:41 +00002882}
2883
2884
Steve Blocka7e24c12009-10-30 11:49:00 +00002885static const char* last_location;
2886static const char* last_message;
2887void StoringErrorCallback(const char* location, const char* message) {
2888 if (last_location == NULL) {
2889 last_location = location;
2890 last_message = message;
2891 }
2892}
2893
2894
2895// ErrorReporting creates a circular extensions configuration and
2896// tests that the fatal error handler gets called. This renders V8
2897// unusable and therefore this test cannot be run in parallel.
2898TEST(ErrorReporting) {
2899 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
2900 static const char* aDeps[] = { "B" };
2901 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
2902 static const char* bDeps[] = { "A" };
2903 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
2904 last_location = NULL;
2905 v8::ExtensionConfiguration config(1, bDeps);
2906 v8::Handle<Context> context = Context::New(&config);
2907 CHECK(context.IsEmpty());
2908 CHECK_NE(last_location, NULL);
2909}
2910
2911
2912static const char* js_code_causing_huge_string_flattening =
2913 "var str = 'X';"
2914 "for (var i = 0; i < 30; i++) {"
2915 " str = str + str;"
2916 "}"
2917 "str.match(/X/);";
2918
2919
2920void OOMCallback(const char* location, const char* message) {
2921 exit(0);
2922}
2923
2924
2925TEST(RegexpOutOfMemory) {
2926 // Execute a script that causes out of memory when flattening a string.
2927 v8::HandleScope scope;
2928 v8::V8::SetFatalErrorHandler(OOMCallback);
2929 LocalContext context;
2930 Local<Script> script =
2931 Script::Compile(String::New(js_code_causing_huge_string_flattening));
2932 last_location = NULL;
2933 Local<Value> result = script->Run();
2934
2935 CHECK(false); // Should not return.
2936}
2937
2938
2939static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
2940 v8::Handle<Value> data) {
2941 CHECK_EQ(v8::Undefined(), data);
2942 CHECK(message->GetScriptResourceName()->IsUndefined());
2943 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
2944 message->GetLineNumber();
2945 message->GetSourceLine();
2946}
2947
2948
2949THREADED_TEST(ErrorWithMissingScriptInfo) {
2950 v8::HandleScope scope;
2951 LocalContext context;
2952 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
2953 Script::Compile(v8_str("throw Error()"))->Run();
2954 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
2955}
2956
2957
2958int global_index = 0;
2959
2960class Snorkel {
2961 public:
2962 Snorkel() { index_ = global_index++; }
2963 int index_;
2964};
2965
2966class Whammy {
2967 public:
2968 Whammy() {
2969 cursor_ = 0;
2970 }
2971 ~Whammy() {
2972 script_.Dispose();
2973 }
2974 v8::Handle<Script> getScript() {
2975 if (script_.IsEmpty())
2976 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
2977 return Local<Script>(*script_);
2978 }
2979
2980 public:
2981 static const int kObjectCount = 256;
2982 int cursor_;
2983 v8::Persistent<v8::Object> objects_[kObjectCount];
2984 v8::Persistent<Script> script_;
2985};
2986
2987static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
2988 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
2989 delete snorkel;
2990 obj.ClearWeak();
2991}
2992
2993v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
2994 const AccessorInfo& info) {
2995 Whammy* whammy =
2996 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
2997
2998 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
2999
3000 v8::Handle<v8::Object> obj = v8::Object::New();
3001 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3002 if (!prev.IsEmpty()) {
3003 prev->Set(v8_str("next"), obj);
3004 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3005 whammy->objects_[whammy->cursor_].Clear();
3006 }
3007 whammy->objects_[whammy->cursor_] = global;
3008 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3009 return whammy->getScript()->Run();
3010}
3011
3012THREADED_TEST(WeakReference) {
3013 v8::HandleScope handle_scope;
3014 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
3015 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3016 0, 0, 0, 0,
3017 v8::External::New(new Whammy()));
3018 const char* extension_list[] = { "v8/gc" };
3019 v8::ExtensionConfiguration extensions(1, extension_list);
3020 v8::Persistent<Context> context = Context::New(&extensions);
3021 Context::Scope context_scope(context);
3022
3023 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3024 context->Global()->Set(v8_str("whammy"), interceptor);
3025 const char* code =
3026 "var last;"
3027 "for (var i = 0; i < 10000; i++) {"
3028 " var obj = whammy.length;"
3029 " if (last) last.next = obj;"
3030 " last = obj;"
3031 "}"
3032 "gc();"
3033 "4";
3034 v8::Handle<Value> result = CompileRun(code);
3035 CHECK_EQ(4.0, result->NumberValue());
3036
3037 context.Dispose();
3038}
3039
3040
Steve Blockd0582a62009-12-15 09:54:21 +00003041static bool in_scavenge = false;
3042static int last = -1;
3043
3044static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3045 CHECK_EQ(-1, last);
3046 last = 0;
3047 obj.Dispose();
3048 obj.Clear();
3049 in_scavenge = true;
3050 i::Heap::PerformScavenge();
3051 in_scavenge = false;
3052 *(reinterpret_cast<bool*>(data)) = true;
3053}
3054
3055static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3056 void* data) {
3057 CHECK_EQ(0, last);
3058 last = 1;
3059 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3060 obj.Dispose();
3061 obj.Clear();
3062}
3063
3064THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3065 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3066 // Calling callbacks from scavenges is unsafe as objects held by those
3067 // handlers might have become strongly reachable, but scavenge doesn't
3068 // check that.
3069 v8::Persistent<Context> context = Context::New();
3070 Context::Scope context_scope(context);
3071
3072 v8::Persistent<v8::Object> object_a;
3073 v8::Persistent<v8::Object> object_b;
3074
3075 {
3076 v8::HandleScope handle_scope;
3077 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
3078 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
3079 }
3080
3081 bool object_a_disposed = false;
3082 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
3083 bool released_in_scavenge = false;
3084 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
3085
3086 while (!object_a_disposed) {
3087 i::Heap::CollectAllGarbage(false);
3088 }
3089 CHECK(!released_in_scavenge);
3090}
3091
3092
Steve Blocka7e24c12009-10-30 11:49:00 +00003093v8::Handle<Function> args_fun;
3094
3095
3096static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3097 ApiTestFuzzer::Fuzz();
3098 CHECK_EQ(args_fun, args.Callee());
3099 CHECK_EQ(3, args.Length());
3100 CHECK_EQ(v8::Integer::New(1), args[0]);
3101 CHECK_EQ(v8::Integer::New(2), args[1]);
3102 CHECK_EQ(v8::Integer::New(3), args[2]);
3103 CHECK_EQ(v8::Undefined(), args[3]);
3104 v8::HandleScope scope;
3105 i::Heap::CollectAllGarbage(false);
3106 return v8::Undefined();
3107}
3108
3109
3110THREADED_TEST(Arguments) {
3111 v8::HandleScope scope;
3112 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3113 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3114 LocalContext context(NULL, global);
3115 args_fun = v8::Handle<Function>::Cast(context->Global()->Get(v8_str("f")));
3116 v8_compile("f(1, 2, 3)")->Run();
3117}
3118
3119
Steve Blocka7e24c12009-10-30 11:49:00 +00003120static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3121 const AccessorInfo&) {
3122 return v8::Handle<Value>();
3123}
3124
3125
3126static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3127 const AccessorInfo&) {
3128 return v8::Handle<Value>();
3129}
3130
3131
3132static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3133 const AccessorInfo&) {
3134 if (!name->Equals(v8_str("foo"))) {
3135 return v8::Handle<v8::Boolean>(); // not intercepted
3136 }
3137
3138 return v8::False(); // intercepted, and don't delete the property
3139}
3140
3141
3142static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
3143 if (index != 2) {
3144 return v8::Handle<v8::Boolean>(); // not intercepted
3145 }
3146
3147 return v8::False(); // intercepted, and don't delete the property
3148}
3149
3150
3151THREADED_TEST(Deleter) {
3152 v8::HandleScope scope;
3153 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3154 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
3155 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
3156 LocalContext context;
3157 context->Global()->Set(v8_str("k"), obj->NewInstance());
3158 CompileRun(
3159 "k.foo = 'foo';"
3160 "k.bar = 'bar';"
3161 "k[2] = 2;"
3162 "k[4] = 4;");
3163 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
3164 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
3165
3166 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
3167 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
3168
3169 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
3170 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
3171
3172 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
3173 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
3174}
3175
3176
3177static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
3178 ApiTestFuzzer::Fuzz();
3179 if (name->Equals(v8_str("foo")) ||
3180 name->Equals(v8_str("bar")) ||
3181 name->Equals(v8_str("baz"))) {
3182 return v8::Undefined();
3183 }
3184 return v8::Handle<Value>();
3185}
3186
3187
3188static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
3189 ApiTestFuzzer::Fuzz();
3190 if (index == 0 || index == 1) return v8::Undefined();
3191 return v8::Handle<Value>();
3192}
3193
3194
3195static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
3196 ApiTestFuzzer::Fuzz();
3197 v8::Handle<v8::Array> result = v8::Array::New(3);
3198 result->Set(v8::Integer::New(0), v8_str("foo"));
3199 result->Set(v8::Integer::New(1), v8_str("bar"));
3200 result->Set(v8::Integer::New(2), v8_str("baz"));
3201 return result;
3202}
3203
3204
3205static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
3206 ApiTestFuzzer::Fuzz();
3207 v8::Handle<v8::Array> result = v8::Array::New(2);
3208 result->Set(v8::Integer::New(0), v8_str("0"));
3209 result->Set(v8::Integer::New(1), v8_str("1"));
3210 return result;
3211}
3212
3213
3214THREADED_TEST(Enumerators) {
3215 v8::HandleScope scope;
3216 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3217 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
3218 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
3219 LocalContext context;
3220 context->Global()->Set(v8_str("k"), obj->NewInstance());
3221 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
3222 "k[10] = 0;"
3223 "k.a = 0;"
3224 "k[5] = 0;"
3225 "k.b = 0;"
3226 "k[4294967295] = 0;"
3227 "k.c = 0;"
3228 "k[4294967296] = 0;"
3229 "k.d = 0;"
3230 "k[140000] = 0;"
3231 "k.e = 0;"
3232 "k[30000000000] = 0;"
3233 "k.f = 0;"
3234 "var result = [];"
3235 "for (var prop in k) {"
3236 " result.push(prop);"
3237 "}"
3238 "result"));
3239 // Check that we get all the property names returned including the
3240 // ones from the enumerators in the right order: indexed properties
3241 // in numerical order, indexed interceptor properties, named
3242 // properties in insertion order, named interceptor properties.
3243 // This order is not mandated by the spec, so this test is just
3244 // documenting our behavior.
3245 CHECK_EQ(17, result->Length());
3246 // Indexed properties in numerical order.
3247 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
3248 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
3249 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
3250 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
3251 // Indexed interceptor properties in the order they are returned
3252 // from the enumerator interceptor.
3253 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
3254 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
3255 // Named properties in insertion order.
3256 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
3257 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
3258 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
3259 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
3260 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
3261 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
3262 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
3263 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
3264 // Named interceptor properties.
3265 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
3266 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
3267 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
3268}
3269
3270
3271int p_getter_count;
3272int p_getter_count2;
3273
3274
3275static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
3276 ApiTestFuzzer::Fuzz();
3277 p_getter_count++;
3278 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
3279 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
3280 if (name->Equals(v8_str("p1"))) {
3281 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
3282 } else if (name->Equals(v8_str("p2"))) {
3283 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
3284 } else if (name->Equals(v8_str("p3"))) {
3285 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
3286 } else if (name->Equals(v8_str("p4"))) {
3287 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
3288 }
3289 return v8::Undefined();
3290}
3291
3292
3293static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
3294 ApiTestFuzzer::Fuzz();
3295 LocalContext context;
3296 context->Global()->Set(v8_str("o1"), obj->NewInstance());
3297 CompileRun(
3298 "o1.__proto__ = { };"
3299 "var o2 = { __proto__: o1 };"
3300 "var o3 = { __proto__: o2 };"
3301 "var o4 = { __proto__: o3 };"
3302 "for (var i = 0; i < 10; i++) o4.p4;"
3303 "for (var i = 0; i < 10; i++) o3.p3;"
3304 "for (var i = 0; i < 10; i++) o2.p2;"
3305 "for (var i = 0; i < 10; i++) o1.p1;");
3306}
3307
3308
3309static v8::Handle<Value> PGetter2(Local<String> name,
3310 const AccessorInfo& info) {
3311 ApiTestFuzzer::Fuzz();
3312 p_getter_count2++;
3313 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
3314 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
3315 if (name->Equals(v8_str("p1"))) {
3316 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
3317 } else if (name->Equals(v8_str("p2"))) {
3318 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
3319 } else if (name->Equals(v8_str("p3"))) {
3320 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
3321 } else if (name->Equals(v8_str("p4"))) {
3322 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
3323 }
3324 return v8::Undefined();
3325}
3326
3327
3328THREADED_TEST(GetterHolders) {
3329 v8::HandleScope scope;
3330 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3331 obj->SetAccessor(v8_str("p1"), PGetter);
3332 obj->SetAccessor(v8_str("p2"), PGetter);
3333 obj->SetAccessor(v8_str("p3"), PGetter);
3334 obj->SetAccessor(v8_str("p4"), PGetter);
3335 p_getter_count = 0;
3336 RunHolderTest(obj);
3337 CHECK_EQ(40, p_getter_count);
3338}
3339
3340
3341THREADED_TEST(PreInterceptorHolders) {
3342 v8::HandleScope scope;
3343 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3344 obj->SetNamedPropertyHandler(PGetter2);
3345 p_getter_count2 = 0;
3346 RunHolderTest(obj);
3347 CHECK_EQ(40, p_getter_count2);
3348}
3349
3350
3351THREADED_TEST(ObjectInstantiation) {
3352 v8::HandleScope scope;
3353 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
3354 templ->SetAccessor(v8_str("t"), PGetter2);
3355 LocalContext context;
3356 context->Global()->Set(v8_str("o"), templ->NewInstance());
3357 for (int i = 0; i < 100; i++) {
3358 v8::HandleScope inner_scope;
3359 v8::Handle<v8::Object> obj = templ->NewInstance();
3360 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
3361 context->Global()->Set(v8_str("o2"), obj);
3362 v8::Handle<Value> value =
3363 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
3364 CHECK_EQ(v8::True(), value);
3365 context->Global()->Set(v8_str("o"), obj);
3366 }
3367}
3368
3369
3370THREADED_TEST(StringWrite) {
3371 v8::HandleScope scope;
3372 v8::Handle<String> str = v8_str("abcde");
3373
3374 char buf[100];
3375 int len;
3376
3377 memset(buf, 0x1, sizeof(buf));
3378 len = str->WriteAscii(buf);
3379 CHECK_EQ(len, 5);
3380 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
3381
3382 memset(buf, 0x1, sizeof(buf));
3383 len = str->WriteAscii(buf, 0, 4);
3384 CHECK_EQ(len, 4);
3385 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
3386
3387 memset(buf, 0x1, sizeof(buf));
3388 len = str->WriteAscii(buf, 0, 5);
3389 CHECK_EQ(len, 5);
3390 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
3391
3392 memset(buf, 0x1, sizeof(buf));
3393 len = str->WriteAscii(buf, 0, 6);
3394 CHECK_EQ(len, 5);
3395 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
3396
3397 memset(buf, 0x1, sizeof(buf));
3398 len = str->WriteAscii(buf, 4, -1);
3399 CHECK_EQ(len, 1);
3400 CHECK_EQ(strncmp("e\0", buf, 2), 0);
3401
3402 memset(buf, 0x1, sizeof(buf));
3403 len = str->WriteAscii(buf, 4, 6);
3404 CHECK_EQ(len, 1);
3405 CHECK_EQ(strncmp("e\0", buf, 2), 0);
3406
3407 memset(buf, 0x1, sizeof(buf));
3408 len = str->WriteAscii(buf, 4, 1);
3409 CHECK_EQ(len, 1);
3410 CHECK_EQ(strncmp("e\1", buf, 2), 0);
3411}
3412
3413
3414THREADED_TEST(ToArrayIndex) {
3415 v8::HandleScope scope;
3416 LocalContext context;
3417
3418 v8::Handle<String> str = v8_str("42");
3419 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
3420 CHECK(!index.IsEmpty());
3421 CHECK_EQ(42.0, index->Uint32Value());
3422 str = v8_str("42asdf");
3423 index = str->ToArrayIndex();
3424 CHECK(index.IsEmpty());
3425 str = v8_str("-42");
3426 index = str->ToArrayIndex();
3427 CHECK(index.IsEmpty());
3428 str = v8_str("4294967295");
3429 index = str->ToArrayIndex();
3430 CHECK(!index.IsEmpty());
3431 CHECK_EQ(4294967295.0, index->Uint32Value());
3432 v8::Handle<v8::Number> num = v8::Number::New(1);
3433 index = num->ToArrayIndex();
3434 CHECK(!index.IsEmpty());
3435 CHECK_EQ(1.0, index->Uint32Value());
3436 num = v8::Number::New(-1);
3437 index = num->ToArrayIndex();
3438 CHECK(index.IsEmpty());
3439 v8::Handle<v8::Object> obj = v8::Object::New();
3440 index = obj->ToArrayIndex();
3441 CHECK(index.IsEmpty());
3442}
3443
3444
3445THREADED_TEST(ErrorConstruction) {
3446 v8::HandleScope scope;
3447 LocalContext context;
3448
3449 v8::Handle<String> foo = v8_str("foo");
3450 v8::Handle<String> message = v8_str("message");
3451 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
3452 CHECK(range_error->IsObject());
3453 v8::Handle<v8::Object> range_obj(v8::Handle<v8::Object>::Cast(range_error));
3454 CHECK(v8::Handle<v8::Object>::Cast(range_error)->Get(message)->Equals(foo));
3455 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
3456 CHECK(reference_error->IsObject());
3457 CHECK(
3458 v8::Handle<v8::Object>::Cast(reference_error)->Get(message)->Equals(foo));
3459 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
3460 CHECK(syntax_error->IsObject());
3461 CHECK(v8::Handle<v8::Object>::Cast(syntax_error)->Get(message)->Equals(foo));
3462 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
3463 CHECK(type_error->IsObject());
3464 CHECK(v8::Handle<v8::Object>::Cast(type_error)->Get(message)->Equals(foo));
3465 v8::Handle<Value> error = v8::Exception::Error(foo);
3466 CHECK(error->IsObject());
3467 CHECK(v8::Handle<v8::Object>::Cast(error)->Get(message)->Equals(foo));
3468}
3469
3470
3471static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
3472 ApiTestFuzzer::Fuzz();
3473 return v8_num(10);
3474}
3475
3476
3477static void YSetter(Local<String> name,
3478 Local<Value> value,
3479 const AccessorInfo& info) {
3480 if (info.This()->Has(name)) {
3481 info.This()->Delete(name);
3482 }
3483 info.This()->Set(name, value);
3484}
3485
3486
3487THREADED_TEST(DeleteAccessor) {
3488 v8::HandleScope scope;
3489 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3490 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
3491 LocalContext context;
3492 v8::Handle<v8::Object> holder = obj->NewInstance();
3493 context->Global()->Set(v8_str("holder"), holder);
3494 v8::Handle<Value> result = CompileRun(
3495 "holder.y = 11; holder.y = 12; holder.y");
3496 CHECK_EQ(12, result->Uint32Value());
3497}
3498
3499
3500THREADED_TEST(TypeSwitch) {
3501 v8::HandleScope scope;
3502 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
3503 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
3504 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
3505 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
3506 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
3507 LocalContext context;
3508 v8::Handle<v8::Object> obj0 = v8::Object::New();
3509 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
3510 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
3511 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
3512 for (int i = 0; i < 10; i++) {
3513 CHECK_EQ(0, type_switch->match(obj0));
3514 CHECK_EQ(1, type_switch->match(obj1));
3515 CHECK_EQ(2, type_switch->match(obj2));
3516 CHECK_EQ(3, type_switch->match(obj3));
3517 CHECK_EQ(3, type_switch->match(obj3));
3518 CHECK_EQ(2, type_switch->match(obj2));
3519 CHECK_EQ(1, type_switch->match(obj1));
3520 CHECK_EQ(0, type_switch->match(obj0));
3521 }
3522}
3523
3524
3525// For use within the TestSecurityHandler() test.
3526static bool g_security_callback_result = false;
3527static bool NamedSecurityTestCallback(Local<v8::Object> global,
3528 Local<Value> name,
3529 v8::AccessType type,
3530 Local<Value> data) {
3531 // Always allow read access.
3532 if (type == v8::ACCESS_GET)
3533 return true;
3534
3535 // Sometimes allow other access.
3536 return g_security_callback_result;
3537}
3538
3539
3540static bool IndexedSecurityTestCallback(Local<v8::Object> global,
3541 uint32_t key,
3542 v8::AccessType type,
3543 Local<Value> data) {
3544 // Always allow read access.
3545 if (type == v8::ACCESS_GET)
3546 return true;
3547
3548 // Sometimes allow other access.
3549 return g_security_callback_result;
3550}
3551
3552
3553static int trouble_nesting = 0;
3554static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
3555 ApiTestFuzzer::Fuzz();
3556 trouble_nesting++;
3557
3558 // Call a JS function that throws an uncaught exception.
3559 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
3560 Local<Value> trouble_callee = (trouble_nesting == 3) ?
3561 arg_this->Get(v8_str("trouble_callee")) :
3562 arg_this->Get(v8_str("trouble_caller"));
3563 CHECK(trouble_callee->IsFunction());
3564 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
3565}
3566
3567
3568static int report_count = 0;
3569static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
3570 v8::Handle<Value>) {
3571 report_count++;
3572}
3573
3574
3575// Counts uncaught exceptions, but other tests running in parallel
3576// also have uncaught exceptions.
3577TEST(ApiUncaughtException) {
3578 report_count = 0;
3579 v8::HandleScope scope;
3580 LocalContext env;
3581 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
3582
3583 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
3584 v8::Local<v8::Object> global = env->Global();
3585 global->Set(v8_str("trouble"), fun->GetFunction());
3586
3587 Script::Compile(v8_str("function trouble_callee() {"
3588 " var x = null;"
3589 " return x.foo;"
3590 "};"
3591 "function trouble_caller() {"
3592 " trouble();"
3593 "};"))->Run();
3594 Local<Value> trouble = global->Get(v8_str("trouble"));
3595 CHECK(trouble->IsFunction());
3596 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
3597 CHECK(trouble_callee->IsFunction());
3598 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
3599 CHECK(trouble_caller->IsFunction());
3600 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
3601 CHECK_EQ(1, report_count);
3602 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
3603}
3604
Leon Clarke4515c472010-02-03 11:58:03 +00003605static const char* script_resource_name = "ExceptionInNativeScript.js";
3606static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
3607 v8::Handle<Value>) {
3608 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
3609 CHECK(!name_val.IsEmpty() && name_val->IsString());
3610 v8::String::AsciiValue name(message->GetScriptResourceName());
3611 CHECK_EQ(script_resource_name, *name);
3612 CHECK_EQ(3, message->GetLineNumber());
3613 v8::String::AsciiValue source_line(message->GetSourceLine());
3614 CHECK_EQ(" new o.foo();", *source_line);
3615}
3616
3617TEST(ExceptionInNativeScript) {
3618 v8::HandleScope scope;
3619 LocalContext env;
3620 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
3621
3622 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
3623 v8::Local<v8::Object> global = env->Global();
3624 global->Set(v8_str("trouble"), fun->GetFunction());
3625
3626 Script::Compile(v8_str("function trouble() {\n"
3627 " var o = {};\n"
3628 " new o.foo();\n"
3629 "};"), v8::String::New(script_resource_name))->Run();
3630 Local<Value> trouble = global->Get(v8_str("trouble"));
3631 CHECK(trouble->IsFunction());
3632 Function::Cast(*trouble)->Call(global, 0, NULL);
3633 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
3634}
3635
Steve Blocka7e24c12009-10-30 11:49:00 +00003636
3637TEST(CompilationErrorUsingTryCatchHandler) {
3638 v8::HandleScope scope;
3639 LocalContext env;
3640 v8::TryCatch try_catch;
3641 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
3642 CHECK_NE(NULL, *try_catch.Exception());
3643 CHECK(try_catch.HasCaught());
3644}
3645
3646
3647TEST(TryCatchFinallyUsingTryCatchHandler) {
3648 v8::HandleScope scope;
3649 LocalContext env;
3650 v8::TryCatch try_catch;
3651 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
3652 CHECK(!try_catch.HasCaught());
3653 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
3654 CHECK(try_catch.HasCaught());
3655 try_catch.Reset();
3656 Script::Compile(v8_str("(function() {"
3657 "try { throw ''; } finally { return; }"
3658 "})()"))->Run();
3659 CHECK(!try_catch.HasCaught());
3660 Script::Compile(v8_str("(function()"
3661 " { try { throw ''; } finally { throw 0; }"
3662 "})()"))->Run();
3663 CHECK(try_catch.HasCaught());
3664}
3665
3666
3667// SecurityHandler can't be run twice
3668TEST(SecurityHandler) {
3669 v8::HandleScope scope0;
3670 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
3671 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
3672 IndexedSecurityTestCallback);
3673 // Create an environment
3674 v8::Persistent<Context> context0 =
3675 Context::New(NULL, global_template);
3676 context0->Enter();
3677
3678 v8::Handle<v8::Object> global0 = context0->Global();
3679 v8::Handle<Script> script0 = v8_compile("foo = 111");
3680 script0->Run();
3681 global0->Set(v8_str("0"), v8_num(999));
3682 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
3683 CHECK_EQ(111, foo0->Int32Value());
3684 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
3685 CHECK_EQ(999, z0->Int32Value());
3686
3687 // Create another environment, should fail security checks.
3688 v8::HandleScope scope1;
3689
3690 v8::Persistent<Context> context1 =
3691 Context::New(NULL, global_template);
3692 context1->Enter();
3693
3694 v8::Handle<v8::Object> global1 = context1->Global();
3695 global1->Set(v8_str("othercontext"), global0);
3696 // This set will fail the security check.
3697 v8::Handle<Script> script1 =
3698 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
3699 script1->Run();
3700 // This read will pass the security check.
3701 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
3702 CHECK_EQ(111, foo1->Int32Value());
3703 // This read will pass the security check.
3704 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
3705 CHECK_EQ(999, z1->Int32Value());
3706
3707 // Create another environment, should pass security checks.
3708 { g_security_callback_result = true; // allow security handler to pass.
3709 v8::HandleScope scope2;
3710 LocalContext context2;
3711 v8::Handle<v8::Object> global2 = context2->Global();
3712 global2->Set(v8_str("othercontext"), global0);
3713 v8::Handle<Script> script2 =
3714 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
3715 script2->Run();
3716 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
3717 CHECK_EQ(333, foo2->Int32Value());
3718 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
3719 CHECK_EQ(888, z2->Int32Value());
3720 }
3721
3722 context1->Exit();
3723 context1.Dispose();
3724
3725 context0->Exit();
3726 context0.Dispose();
3727}
3728
3729
3730THREADED_TEST(SecurityChecks) {
3731 v8::HandleScope handle_scope;
3732 LocalContext env1;
3733 v8::Persistent<Context> env2 = Context::New();
3734
3735 Local<Value> foo = v8_str("foo");
3736 Local<Value> bar = v8_str("bar");
3737
3738 // Set to the same domain.
3739 env1->SetSecurityToken(foo);
3740
3741 // Create a function in env1.
3742 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
3743 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
3744 CHECK(spy->IsFunction());
3745
3746 // Create another function accessing global objects.
3747 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
3748 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
3749 CHECK(spy2->IsFunction());
3750
3751 // Switch to env2 in the same domain and invoke spy on env2.
3752 {
3753 env2->SetSecurityToken(foo);
3754 // Enter env2
3755 Context::Scope scope_env2(env2);
3756 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
3757 CHECK(result->IsFunction());
3758 }
3759
3760 {
3761 env2->SetSecurityToken(bar);
3762 Context::Scope scope_env2(env2);
3763
3764 // Call cross_domain_call, it should throw an exception
3765 v8::TryCatch try_catch;
3766 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
3767 CHECK(try_catch.HasCaught());
3768 }
3769
3770 env2.Dispose();
3771}
3772
3773
3774// Regression test case for issue 1183439.
3775THREADED_TEST(SecurityChecksForPrototypeChain) {
3776 v8::HandleScope scope;
3777 LocalContext current;
3778 v8::Persistent<Context> other = Context::New();
3779
3780 // Change context to be able to get to the Object function in the
3781 // other context without hitting the security checks.
3782 v8::Local<Value> other_object;
3783 { Context::Scope scope(other);
3784 other_object = other->Global()->Get(v8_str("Object"));
3785 other->Global()->Set(v8_num(42), v8_num(87));
3786 }
3787
3788 current->Global()->Set(v8_str("other"), other->Global());
3789 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
3790
3791 // Make sure the security check fails here and we get an undefined
3792 // result instead of getting the Object function. Repeat in a loop
3793 // to make sure to exercise the IC code.
3794 v8::Local<Script> access_other0 = v8_compile("other.Object");
3795 v8::Local<Script> access_other1 = v8_compile("other[42]");
3796 for (int i = 0; i < 5; i++) {
3797 CHECK(!access_other0->Run()->Equals(other_object));
3798 CHECK(access_other0->Run()->IsUndefined());
3799 CHECK(!access_other1->Run()->Equals(v8_num(87)));
3800 CHECK(access_other1->Run()->IsUndefined());
3801 }
3802
3803 // Create an object that has 'other' in its prototype chain and make
3804 // sure we cannot access the Object function indirectly through
3805 // that. Repeat in a loop to make sure to exercise the IC code.
3806 v8_compile("function F() { };"
3807 "F.prototype = other;"
3808 "var f = new F();")->Run();
3809 v8::Local<Script> access_f0 = v8_compile("f.Object");
3810 v8::Local<Script> access_f1 = v8_compile("f[42]");
3811 for (int j = 0; j < 5; j++) {
3812 CHECK(!access_f0->Run()->Equals(other_object));
3813 CHECK(access_f0->Run()->IsUndefined());
3814 CHECK(!access_f1->Run()->Equals(v8_num(87)));
3815 CHECK(access_f1->Run()->IsUndefined());
3816 }
3817
3818 // Now it gets hairy: Set the prototype for the other global object
3819 // to be the current global object. The prototype chain for 'f' now
3820 // goes through 'other' but ends up in the current global object.
3821 { Context::Scope scope(other);
3822 other->Global()->Set(v8_str("__proto__"), current->Global());
3823 }
3824 // Set a named and an index property on the current global
3825 // object. To force the lookup to go through the other global object,
3826 // the properties must not exist in the other global object.
3827 current->Global()->Set(v8_str("foo"), v8_num(100));
3828 current->Global()->Set(v8_num(99), v8_num(101));
3829 // Try to read the properties from f and make sure that the access
3830 // gets stopped by the security checks on the other global object.
3831 Local<Script> access_f2 = v8_compile("f.foo");
3832 Local<Script> access_f3 = v8_compile("f[99]");
3833 for (int k = 0; k < 5; k++) {
3834 CHECK(!access_f2->Run()->Equals(v8_num(100)));
3835 CHECK(access_f2->Run()->IsUndefined());
3836 CHECK(!access_f3->Run()->Equals(v8_num(101)));
3837 CHECK(access_f3->Run()->IsUndefined());
3838 }
3839 other.Dispose();
3840}
3841
3842
3843THREADED_TEST(CrossDomainDelete) {
3844 v8::HandleScope handle_scope;
3845 LocalContext env1;
3846 v8::Persistent<Context> env2 = Context::New();
3847
3848 Local<Value> foo = v8_str("foo");
3849 Local<Value> bar = v8_str("bar");
3850
3851 // Set to the same domain.
3852 env1->SetSecurityToken(foo);
3853 env2->SetSecurityToken(foo);
3854
3855 env1->Global()->Set(v8_str("prop"), v8_num(3));
3856 env2->Global()->Set(v8_str("env1"), env1->Global());
3857
3858 // Change env2 to a different domain and delete env1.prop.
3859 env2->SetSecurityToken(bar);
3860 {
3861 Context::Scope scope_env2(env2);
3862 Local<Value> result =
3863 Script::Compile(v8_str("delete env1.prop"))->Run();
3864 CHECK(result->IsFalse());
3865 }
3866
3867 // Check that env1.prop still exists.
3868 Local<Value> v = env1->Global()->Get(v8_str("prop"));
3869 CHECK(v->IsNumber());
3870 CHECK_EQ(3, v->Int32Value());
3871
3872 env2.Dispose();
3873}
3874
3875
3876THREADED_TEST(CrossDomainIsPropertyEnumerable) {
3877 v8::HandleScope handle_scope;
3878 LocalContext env1;
3879 v8::Persistent<Context> env2 = Context::New();
3880
3881 Local<Value> foo = v8_str("foo");
3882 Local<Value> bar = v8_str("bar");
3883
3884 // Set to the same domain.
3885 env1->SetSecurityToken(foo);
3886 env2->SetSecurityToken(foo);
3887
3888 env1->Global()->Set(v8_str("prop"), v8_num(3));
3889 env2->Global()->Set(v8_str("env1"), env1->Global());
3890
3891 // env1.prop is enumerable in env2.
3892 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
3893 {
3894 Context::Scope scope_env2(env2);
3895 Local<Value> result = Script::Compile(test)->Run();
3896 CHECK(result->IsTrue());
3897 }
3898
3899 // Change env2 to a different domain and test again.
3900 env2->SetSecurityToken(bar);
3901 {
3902 Context::Scope scope_env2(env2);
3903 Local<Value> result = Script::Compile(test)->Run();
3904 CHECK(result->IsFalse());
3905 }
3906
3907 env2.Dispose();
3908}
3909
3910
3911THREADED_TEST(CrossDomainForIn) {
3912 v8::HandleScope handle_scope;
3913 LocalContext env1;
3914 v8::Persistent<Context> env2 = Context::New();
3915
3916 Local<Value> foo = v8_str("foo");
3917 Local<Value> bar = v8_str("bar");
3918
3919 // Set to the same domain.
3920 env1->SetSecurityToken(foo);
3921 env2->SetSecurityToken(foo);
3922
3923 env1->Global()->Set(v8_str("prop"), v8_num(3));
3924 env2->Global()->Set(v8_str("env1"), env1->Global());
3925
3926 // Change env2 to a different domain and set env1's global object
3927 // as the __proto__ of an object in env2 and enumerate properties
3928 // in for-in. It shouldn't enumerate properties on env1's global
3929 // object.
3930 env2->SetSecurityToken(bar);
3931 {
3932 Context::Scope scope_env2(env2);
3933 Local<Value> result =
3934 CompileRun("(function(){var obj = {'__proto__':env1};"
3935 "for (var p in obj)"
3936 " if (p == 'prop') return false;"
3937 "return true;})()");
3938 CHECK(result->IsTrue());
3939 }
3940 env2.Dispose();
3941}
3942
3943
3944TEST(ContextDetachGlobal) {
3945 v8::HandleScope handle_scope;
3946 LocalContext env1;
3947 v8::Persistent<Context> env2 = Context::New();
3948
3949 Local<v8::Object> global1 = env1->Global();
3950
3951 Local<Value> foo = v8_str("foo");
3952
3953 // Set to the same domain.
3954 env1->SetSecurityToken(foo);
3955 env2->SetSecurityToken(foo);
3956
3957 // Enter env2
3958 env2->Enter();
3959
3960 // Create a function in env1
3961 Local<v8::Object> global2 = env2->Global();
3962 global2->Set(v8_str("prop"), v8::Integer::New(1));
3963 CompileRun("function getProp() {return prop;}");
3964
3965 env1->Global()->Set(v8_str("getProp"),
3966 global2->Get(v8_str("getProp")));
3967
3968 // Detach env1's global, and reuse the global object of env1
3969 env2->Exit();
3970 env2->DetachGlobal();
3971 // env2 has a new global object.
3972 CHECK(!env2->Global()->Equals(global2));
3973
3974 v8::Persistent<Context> env3 =
3975 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
3976 env3->SetSecurityToken(v8_str("bar"));
3977 env3->Enter();
3978
3979 Local<v8::Object> global3 = env3->Global();
3980 CHECK_EQ(global2, global3);
3981 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
3982 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
3983 global3->Set(v8_str("prop"), v8::Integer::New(-1));
3984 global3->Set(v8_str("prop2"), v8::Integer::New(2));
3985 env3->Exit();
3986
3987 // Call getProp in env1, and it should return the value 1
3988 {
3989 Local<Value> get_prop = global1->Get(v8_str("getProp"));
3990 CHECK(get_prop->IsFunction());
3991 v8::TryCatch try_catch;
3992 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
3993 CHECK(!try_catch.HasCaught());
3994 CHECK_EQ(1, r->Int32Value());
3995 }
3996
3997 // Check that env3 is not accessible from env1
3998 {
3999 Local<Value> r = global3->Get(v8_str("prop2"));
4000 CHECK(r->IsUndefined());
4001 }
4002
4003 env2.Dispose();
4004 env3.Dispose();
4005}
4006
4007
4008static bool NamedAccessBlocker(Local<v8::Object> global,
4009 Local<Value> name,
4010 v8::AccessType type,
4011 Local<Value> data) {
4012 return Context::GetCurrent()->Global()->Equals(global);
4013}
4014
4015
4016static bool IndexedAccessBlocker(Local<v8::Object> global,
4017 uint32_t key,
4018 v8::AccessType type,
4019 Local<Value> data) {
4020 return Context::GetCurrent()->Global()->Equals(global);
4021}
4022
4023
4024static int g_echo_value = -1;
4025static v8::Handle<Value> EchoGetter(Local<String> name,
4026 const AccessorInfo& info) {
4027 return v8_num(g_echo_value);
4028}
4029
4030
4031static void EchoSetter(Local<String> name,
4032 Local<Value> value,
4033 const AccessorInfo&) {
4034 if (value->IsNumber())
4035 g_echo_value = value->Int32Value();
4036}
4037
4038
4039static v8::Handle<Value> UnreachableGetter(Local<String> name,
4040 const AccessorInfo& info) {
4041 CHECK(false); // This function should not be called..
4042 return v8::Undefined();
4043}
4044
4045
4046static void UnreachableSetter(Local<String>, Local<Value>,
4047 const AccessorInfo&) {
4048 CHECK(false); // This function should nto be called.
4049}
4050
4051
4052THREADED_TEST(AccessControl) {
4053 v8::HandleScope handle_scope;
4054 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4055
4056 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4057 IndexedAccessBlocker);
4058
4059 // Add an accessor accessible by cross-domain JS code.
4060 global_template->SetAccessor(
4061 v8_str("accessible_prop"),
4062 EchoGetter, EchoSetter,
4063 v8::Handle<Value>(),
4064 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4065
4066 // Add an accessor that is not accessible by cross-domain JS code.
4067 global_template->SetAccessor(v8_str("blocked_prop"),
4068 UnreachableGetter, UnreachableSetter,
4069 v8::Handle<Value>(),
4070 v8::DEFAULT);
4071
4072 // Create an environment
4073 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4074 context0->Enter();
4075
4076 v8::Handle<v8::Object> global0 = context0->Global();
4077
4078 v8::HandleScope scope1;
4079
4080 v8::Persistent<Context> context1 = Context::New();
4081 context1->Enter();
4082
4083 v8::Handle<v8::Object> global1 = context1->Global();
4084 global1->Set(v8_str("other"), global0);
4085
4086 v8::Handle<Value> value;
4087
4088 // Access blocked property
4089 value = v8_compile("other.blocked_prop = 1")->Run();
4090 value = v8_compile("other.blocked_prop")->Run();
4091 CHECK(value->IsUndefined());
4092
4093 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4094 CHECK(value->IsFalse());
4095
4096 // Access accessible property
4097 value = v8_compile("other.accessible_prop = 3")->Run();
4098 CHECK(value->IsNumber());
4099 CHECK_EQ(3, value->Int32Value());
4100
4101 value = v8_compile("other.accessible_prop")->Run();
4102 CHECK(value->IsNumber());
4103 CHECK_EQ(3, value->Int32Value());
4104
4105 value =
4106 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
4107 CHECK(value->IsTrue());
4108
4109 // Enumeration doesn't enumerate accessors from inaccessible objects in
4110 // the prototype chain even if the accessors are in themselves accessible.
4111 Local<Value> result =
4112 CompileRun("(function(){var obj = {'__proto__':other};"
4113 "for (var p in obj)"
4114 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
4115 " return false;"
4116 " }"
4117 "return true;})()");
4118 CHECK(result->IsTrue());
4119
4120 context1->Exit();
4121 context0->Exit();
4122 context1.Dispose();
4123 context0.Dispose();
4124}
4125
4126
Leon Clarke4515c472010-02-03 11:58:03 +00004127static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
4128 Local<Value> name,
4129 v8::AccessType type,
4130 Local<Value> data) {
4131 return false;
4132}
4133
4134
4135static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
4136 uint32_t key,
4137 v8::AccessType type,
4138 Local<Value> data) {
4139 return false;
4140}
4141
4142
4143THREADED_TEST(AccessControlGetOwnPropertyNames) {
4144 v8::HandleScope handle_scope;
4145 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
4146
4147 obj_template->Set(v8_str("x"), v8::Integer::New(42));
4148 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
4149 GetOwnPropertyNamesIndexedBlocker);
4150
4151 // Create an environment
4152 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
4153 context0->Enter();
4154
4155 v8::Handle<v8::Object> global0 = context0->Global();
4156
4157 v8::HandleScope scope1;
4158
4159 v8::Persistent<Context> context1 = Context::New();
4160 context1->Enter();
4161
4162 v8::Handle<v8::Object> global1 = context1->Global();
4163 global1->Set(v8_str("other"), global0);
4164 global1->Set(v8_str("object"), obj_template->NewInstance());
4165
4166 v8::Handle<Value> value;
4167
4168 // Attempt to get the property names of the other global object and
4169 // of an object that requires access checks. Accessing the other
4170 // global object should be blocked by access checks on the global
4171 // proxy object. Accessing the object that requires access checks
4172 // is blocked by the access checks on the object itself.
4173 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
4174 CHECK(value->IsTrue());
4175
4176 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
4177 CHECK(value->IsTrue());
4178
4179 context1->Exit();
4180 context0->Exit();
4181 context1.Dispose();
4182 context0.Dispose();
4183}
4184
4185
Steve Blocka7e24c12009-10-30 11:49:00 +00004186static v8::Handle<Value> ConstTenGetter(Local<String> name,
4187 const AccessorInfo& info) {
4188 return v8_num(10);
4189}
4190
4191
4192THREADED_TEST(CrossDomainAccessors) {
4193 v8::HandleScope handle_scope;
4194
4195 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
4196
4197 v8::Handle<v8::ObjectTemplate> global_template =
4198 func_template->InstanceTemplate();
4199
4200 v8::Handle<v8::ObjectTemplate> proto_template =
4201 func_template->PrototypeTemplate();
4202
4203 // Add an accessor to proto that's accessible by cross-domain JS code.
4204 proto_template->SetAccessor(v8_str("accessible"),
4205 ConstTenGetter, 0,
4206 v8::Handle<Value>(),
4207 v8::ALL_CAN_READ);
4208
4209 // Add an accessor that is not accessible by cross-domain JS code.
4210 global_template->SetAccessor(v8_str("unreachable"),
4211 UnreachableGetter, 0,
4212 v8::Handle<Value>(),
4213 v8::DEFAULT);
4214
4215 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4216 context0->Enter();
4217
4218 Local<v8::Object> global = context0->Global();
4219 // Add a normal property that shadows 'accessible'
4220 global->Set(v8_str("accessible"), v8_num(11));
4221
4222 // Enter a new context.
4223 v8::HandleScope scope1;
4224 v8::Persistent<Context> context1 = Context::New();
4225 context1->Enter();
4226
4227 v8::Handle<v8::Object> global1 = context1->Global();
4228 global1->Set(v8_str("other"), global);
4229
4230 // Should return 10, instead of 11
4231 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
4232 CHECK(value->IsNumber());
4233 CHECK_EQ(10, value->Int32Value());
4234
4235 value = v8_compile("other.unreachable")->Run();
4236 CHECK(value->IsUndefined());
4237
4238 context1->Exit();
4239 context0->Exit();
4240 context1.Dispose();
4241 context0.Dispose();
4242}
4243
4244
4245static int named_access_count = 0;
4246static int indexed_access_count = 0;
4247
4248static bool NamedAccessCounter(Local<v8::Object> global,
4249 Local<Value> name,
4250 v8::AccessType type,
4251 Local<Value> data) {
4252 named_access_count++;
4253 return true;
4254}
4255
4256
4257static bool IndexedAccessCounter(Local<v8::Object> global,
4258 uint32_t key,
4259 v8::AccessType type,
4260 Local<Value> data) {
4261 indexed_access_count++;
4262 return true;
4263}
4264
4265
4266// This one is too easily disturbed by other tests.
4267TEST(AccessControlIC) {
4268 named_access_count = 0;
4269 indexed_access_count = 0;
4270
4271 v8::HandleScope handle_scope;
4272
4273 // Create an environment.
4274 v8::Persistent<Context> context0 = Context::New();
4275 context0->Enter();
4276
4277 // Create an object that requires access-check functions to be
4278 // called for cross-domain access.
4279 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4280 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
4281 IndexedAccessCounter);
4282 Local<v8::Object> object = object_template->NewInstance();
4283
4284 v8::HandleScope scope1;
4285
4286 // Create another environment.
4287 v8::Persistent<Context> context1 = Context::New();
4288 context1->Enter();
4289
4290 // Make easy access to the object from the other environment.
4291 v8::Handle<v8::Object> global1 = context1->Global();
4292 global1->Set(v8_str("obj"), object);
4293
4294 v8::Handle<Value> value;
4295
4296 // Check that the named access-control function is called every time.
4297 CompileRun("function testProp(obj) {"
4298 " for (var i = 0; i < 10; i++) obj.prop = 1;"
4299 " for (var j = 0; j < 10; j++) obj.prop;"
4300 " return obj.prop"
4301 "}");
4302 value = CompileRun("testProp(obj)");
4303 CHECK(value->IsNumber());
4304 CHECK_EQ(1, value->Int32Value());
4305 CHECK_EQ(21, named_access_count);
4306
4307 // Check that the named access-control function is called every time.
4308 CompileRun("var p = 'prop';"
4309 "function testKeyed(obj) {"
4310 " for (var i = 0; i < 10; i++) obj[p] = 1;"
4311 " for (var j = 0; j < 10; j++) obj[p];"
4312 " return obj[p];"
4313 "}");
4314 // Use obj which requires access checks. No inline caching is used
4315 // in that case.
4316 value = CompileRun("testKeyed(obj)");
4317 CHECK(value->IsNumber());
4318 CHECK_EQ(1, value->Int32Value());
4319 CHECK_EQ(42, named_access_count);
4320 // Force the inline caches into generic state and try again.
4321 CompileRun("testKeyed({ a: 0 })");
4322 CompileRun("testKeyed({ b: 0 })");
4323 value = CompileRun("testKeyed(obj)");
4324 CHECK(value->IsNumber());
4325 CHECK_EQ(1, value->Int32Value());
4326 CHECK_EQ(63, named_access_count);
4327
4328 // Check that the indexed access-control function is called every time.
4329 CompileRun("function testIndexed(obj) {"
4330 " for (var i = 0; i < 10; i++) obj[0] = 1;"
4331 " for (var j = 0; j < 10; j++) obj[0];"
4332 " return obj[0]"
4333 "}");
4334 value = CompileRun("testIndexed(obj)");
4335 CHECK(value->IsNumber());
4336 CHECK_EQ(1, value->Int32Value());
4337 CHECK_EQ(21, indexed_access_count);
4338 // Force the inline caches into generic state.
4339 CompileRun("testIndexed(new Array(1))");
4340 // Test that the indexed access check is called.
4341 value = CompileRun("testIndexed(obj)");
4342 CHECK(value->IsNumber());
4343 CHECK_EQ(1, value->Int32Value());
4344 CHECK_EQ(42, indexed_access_count);
4345
4346 // Check that the named access check is called when invoking
4347 // functions on an object that requires access checks.
4348 CompileRun("obj.f = function() {}");
4349 CompileRun("function testCallNormal(obj) {"
4350 " for (var i = 0; i < 10; i++) obj.f();"
4351 "}");
4352 CompileRun("testCallNormal(obj)");
4353 CHECK_EQ(74, named_access_count);
4354
4355 // Force obj into slow case.
4356 value = CompileRun("delete obj.prop");
4357 CHECK(value->BooleanValue());
4358 // Force inline caches into dictionary probing mode.
4359 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
4360 // Test that the named access check is called.
4361 value = CompileRun("testProp(obj);");
4362 CHECK(value->IsNumber());
4363 CHECK_EQ(1, value->Int32Value());
4364 CHECK_EQ(96, named_access_count);
4365
4366 // Force the call inline cache into dictionary probing mode.
4367 CompileRun("o.f = function() {}; testCallNormal(o)");
4368 // Test that the named access check is still called for each
4369 // invocation of the function.
4370 value = CompileRun("testCallNormal(obj)");
4371 CHECK_EQ(106, named_access_count);
4372
4373 context1->Exit();
4374 context0->Exit();
4375 context1.Dispose();
4376 context0.Dispose();
4377}
4378
4379
4380static bool NamedAccessFlatten(Local<v8::Object> global,
4381 Local<Value> name,
4382 v8::AccessType type,
4383 Local<Value> data) {
4384 char buf[100];
4385 int len;
4386
4387 CHECK(name->IsString());
4388
4389 memset(buf, 0x1, sizeof(buf));
4390 len = Local<String>::Cast(name)->WriteAscii(buf);
4391 CHECK_EQ(4, len);
4392
4393 uint16_t buf2[100];
4394
4395 memset(buf, 0x1, sizeof(buf));
4396 len = Local<String>::Cast(name)->Write(buf2);
4397 CHECK_EQ(4, len);
4398
4399 return true;
4400}
4401
4402
4403static bool IndexedAccessFlatten(Local<v8::Object> global,
4404 uint32_t key,
4405 v8::AccessType type,
4406 Local<Value> data) {
4407 return true;
4408}
4409
4410
4411// Regression test. In access checks, operations that may cause
4412// garbage collection are not allowed. It used to be the case that
4413// using the Write operation on a string could cause a garbage
4414// collection due to flattening of the string. This is no longer the
4415// case.
4416THREADED_TEST(AccessControlFlatten) {
4417 named_access_count = 0;
4418 indexed_access_count = 0;
4419
4420 v8::HandleScope handle_scope;
4421
4422 // Create an environment.
4423 v8::Persistent<Context> context0 = Context::New();
4424 context0->Enter();
4425
4426 // Create an object that requires access-check functions to be
4427 // called for cross-domain access.
4428 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4429 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
4430 IndexedAccessFlatten);
4431 Local<v8::Object> object = object_template->NewInstance();
4432
4433 v8::HandleScope scope1;
4434
4435 // Create another environment.
4436 v8::Persistent<Context> context1 = Context::New();
4437 context1->Enter();
4438
4439 // Make easy access to the object from the other environment.
4440 v8::Handle<v8::Object> global1 = context1->Global();
4441 global1->Set(v8_str("obj"), object);
4442
4443 v8::Handle<Value> value;
4444
4445 value = v8_compile("var p = 'as' + 'df';")->Run();
4446 value = v8_compile("obj[p];")->Run();
4447
4448 context1->Exit();
4449 context0->Exit();
4450 context1.Dispose();
4451 context0.Dispose();
4452}
4453
4454
4455static v8::Handle<Value> AccessControlNamedGetter(
4456 Local<String>, const AccessorInfo&) {
4457 return v8::Integer::New(42);
4458}
4459
4460
4461static v8::Handle<Value> AccessControlNamedSetter(
4462 Local<String>, Local<Value> value, const AccessorInfo&) {
4463 return value;
4464}
4465
4466
4467static v8::Handle<Value> AccessControlIndexedGetter(
4468 uint32_t index,
4469 const AccessorInfo& info) {
4470 return v8_num(42);
4471}
4472
4473
4474static v8::Handle<Value> AccessControlIndexedSetter(
4475 uint32_t, Local<Value> value, const AccessorInfo&) {
4476 return value;
4477}
4478
4479
4480THREADED_TEST(AccessControlInterceptorIC) {
4481 named_access_count = 0;
4482 indexed_access_count = 0;
4483
4484 v8::HandleScope handle_scope;
4485
4486 // Create an environment.
4487 v8::Persistent<Context> context0 = Context::New();
4488 context0->Enter();
4489
4490 // Create an object that requires access-check functions to be
4491 // called for cross-domain access. The object also has interceptors
4492 // interceptor.
4493 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4494 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
4495 IndexedAccessCounter);
4496 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
4497 AccessControlNamedSetter);
4498 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
4499 AccessControlIndexedSetter);
4500 Local<v8::Object> object = object_template->NewInstance();
4501
4502 v8::HandleScope scope1;
4503
4504 // Create another environment.
4505 v8::Persistent<Context> context1 = Context::New();
4506 context1->Enter();
4507
4508 // Make easy access to the object from the other environment.
4509 v8::Handle<v8::Object> global1 = context1->Global();
4510 global1->Set(v8_str("obj"), object);
4511
4512 v8::Handle<Value> value;
4513
4514 // Check that the named access-control function is called every time
4515 // eventhough there is an interceptor on the object.
4516 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
4517 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
4518 "obj.x")->Run();
4519 CHECK(value->IsNumber());
4520 CHECK_EQ(42, value->Int32Value());
4521 CHECK_EQ(21, named_access_count);
4522
4523 value = v8_compile("var p = 'x';")->Run();
4524 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
4525 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
4526 "obj[p]")->Run();
4527 CHECK(value->IsNumber());
4528 CHECK_EQ(42, value->Int32Value());
4529 CHECK_EQ(42, named_access_count);
4530
4531 // Check that the indexed access-control function is called every
4532 // time eventhough there is an interceptor on the object.
4533 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
4534 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
4535 "obj[0]")->Run();
4536 CHECK(value->IsNumber());
4537 CHECK_EQ(42, value->Int32Value());
4538 CHECK_EQ(21, indexed_access_count);
4539
4540 context1->Exit();
4541 context0->Exit();
4542 context1.Dispose();
4543 context0.Dispose();
4544}
4545
4546
4547THREADED_TEST(Version) {
4548 v8::V8::GetVersion();
4549}
4550
4551
4552static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
4553 ApiTestFuzzer::Fuzz();
4554 return v8_num(12);
4555}
4556
4557
4558THREADED_TEST(InstanceProperties) {
4559 v8::HandleScope handle_scope;
4560 LocalContext context;
4561
4562 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4563 Local<ObjectTemplate> instance = t->InstanceTemplate();
4564
4565 instance->Set(v8_str("x"), v8_num(42));
4566 instance->Set(v8_str("f"),
4567 v8::FunctionTemplate::New(InstanceFunctionCallback));
4568
4569 Local<Value> o = t->GetFunction()->NewInstance();
4570
4571 context->Global()->Set(v8_str("i"), o);
4572 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
4573 CHECK_EQ(42, value->Int32Value());
4574
4575 value = Script::Compile(v8_str("i.f()"))->Run();
4576 CHECK_EQ(12, value->Int32Value());
4577}
4578
4579
4580static v8::Handle<Value>
4581GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
4582 ApiTestFuzzer::Fuzz();
4583 return v8::Handle<Value>();
4584}
4585
4586
4587THREADED_TEST(GlobalObjectInstanceProperties) {
4588 v8::HandleScope handle_scope;
4589
4590 Local<Value> global_object;
4591
4592 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4593 t->InstanceTemplate()->SetNamedPropertyHandler(
4594 GlobalObjectInstancePropertiesGet);
4595 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
4596 instance_template->Set(v8_str("x"), v8_num(42));
4597 instance_template->Set(v8_str("f"),
4598 v8::FunctionTemplate::New(InstanceFunctionCallback));
4599
4600 {
4601 LocalContext env(NULL, instance_template);
4602 // Hold on to the global object so it can be used again in another
4603 // environment initialization.
4604 global_object = env->Global();
4605
4606 Local<Value> value = Script::Compile(v8_str("x"))->Run();
4607 CHECK_EQ(42, value->Int32Value());
4608 value = Script::Compile(v8_str("f()"))->Run();
4609 CHECK_EQ(12, value->Int32Value());
4610 }
4611
4612 {
4613 // Create new environment reusing the global object.
4614 LocalContext env(NULL, instance_template, global_object);
4615 Local<Value> value = Script::Compile(v8_str("x"))->Run();
4616 CHECK_EQ(42, value->Int32Value());
4617 value = Script::Compile(v8_str("f()"))->Run();
4618 CHECK_EQ(12, value->Int32Value());
4619 }
4620}
4621
4622
4623static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
4624 ApiTestFuzzer::Fuzz();
4625 return v8_num(42);
4626}
4627
4628
4629static int shadow_y;
4630static int shadow_y_setter_call_count;
4631static int shadow_y_getter_call_count;
4632
4633
4634static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
4635 shadow_y_setter_call_count++;
4636 shadow_y = 42;
4637}
4638
4639
4640static v8::Handle<Value> ShadowYGetter(Local<String> name,
4641 const AccessorInfo& info) {
4642 ApiTestFuzzer::Fuzz();
4643 shadow_y_getter_call_count++;
4644 return v8_num(shadow_y);
4645}
4646
4647
4648static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
4649 const AccessorInfo& info) {
4650 return v8::Handle<Value>();
4651}
4652
4653
4654static v8::Handle<Value> ShadowNamedGet(Local<String> key,
4655 const AccessorInfo&) {
4656 return v8::Handle<Value>();
4657}
4658
4659
4660THREADED_TEST(ShadowObject) {
4661 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
4662 v8::HandleScope handle_scope;
4663
4664 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
4665 LocalContext context(NULL, global_template);
4666
4667 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4668 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
4669 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
4670 Local<ObjectTemplate> proto = t->PrototypeTemplate();
4671 Local<ObjectTemplate> instance = t->InstanceTemplate();
4672
4673 // Only allow calls of f on instances of t.
4674 Local<v8::Signature> signature = v8::Signature::New(t);
4675 proto->Set(v8_str("f"),
4676 v8::FunctionTemplate::New(ShadowFunctionCallback,
4677 Local<Value>(),
4678 signature));
4679 proto->Set(v8_str("x"), v8_num(12));
4680
4681 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
4682
4683 Local<Value> o = t->GetFunction()->NewInstance();
4684 context->Global()->Set(v8_str("__proto__"), o);
4685
4686 Local<Value> value =
4687 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
4688 CHECK(value->IsBoolean());
4689 CHECK(!value->BooleanValue());
4690
4691 value = Script::Compile(v8_str("x"))->Run();
4692 CHECK_EQ(12, value->Int32Value());
4693
4694 value = Script::Compile(v8_str("f()"))->Run();
4695 CHECK_EQ(42, value->Int32Value());
4696
4697 Script::Compile(v8_str("y = 42"))->Run();
4698 CHECK_EQ(1, shadow_y_setter_call_count);
4699 value = Script::Compile(v8_str("y"))->Run();
4700 CHECK_EQ(1, shadow_y_getter_call_count);
4701 CHECK_EQ(42, value->Int32Value());
4702}
4703
4704
4705THREADED_TEST(HiddenPrototype) {
4706 v8::HandleScope handle_scope;
4707 LocalContext context;
4708
4709 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
4710 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
4711 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
4712 t1->SetHiddenPrototype(true);
4713 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
4714 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
4715 t2->SetHiddenPrototype(true);
4716 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
4717 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
4718 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
4719
4720 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
4721 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
4722 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
4723 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
4724
4725 // Setting the prototype on an object skips hidden prototypes.
4726 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4727 o0->Set(v8_str("__proto__"), o1);
4728 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4729 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4730 o0->Set(v8_str("__proto__"), o2);
4731 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4732 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4733 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
4734 o0->Set(v8_str("__proto__"), o3);
4735 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4736 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4737 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
4738 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
4739
4740 // Getting the prototype of o0 should get the first visible one
4741 // which is o3. Therefore, z should not be defined on the prototype
4742 // object.
4743 Local<Value> proto = o0->Get(v8_str("__proto__"));
4744 CHECK(proto->IsObject());
4745 CHECK(Local<v8::Object>::Cast(proto)->Get(v8_str("z"))->IsUndefined());
4746}
4747
4748
4749THREADED_TEST(GetterSetterExceptions) {
4750 v8::HandleScope handle_scope;
4751 LocalContext context;
4752 CompileRun(
4753 "function Foo() { };"
4754 "function Throw() { throw 5; };"
4755 "var x = { };"
4756 "x.__defineSetter__('set', Throw);"
4757 "x.__defineGetter__('get', Throw);");
4758 Local<v8::Object> x =
4759 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
4760 v8::TryCatch try_catch;
4761 x->Set(v8_str("set"), v8::Integer::New(8));
4762 x->Get(v8_str("get"));
4763 x->Set(v8_str("set"), v8::Integer::New(8));
4764 x->Get(v8_str("get"));
4765 x->Set(v8_str("set"), v8::Integer::New(8));
4766 x->Get(v8_str("get"));
4767 x->Set(v8_str("set"), v8::Integer::New(8));
4768 x->Get(v8_str("get"));
4769}
4770
4771
4772THREADED_TEST(Constructor) {
4773 v8::HandleScope handle_scope;
4774 LocalContext context;
4775 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
4776 templ->SetClassName(v8_str("Fun"));
4777 Local<Function> cons = templ->GetFunction();
4778 context->Global()->Set(v8_str("Fun"), cons);
4779 Local<v8::Object> inst = cons->NewInstance();
4780 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
4781 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
4782 CHECK(value->BooleanValue());
4783}
4784
4785THREADED_TEST(FunctionDescriptorException) {
4786 v8::HandleScope handle_scope;
4787 LocalContext context;
4788 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
4789 templ->SetClassName(v8_str("Fun"));
4790 Local<Function> cons = templ->GetFunction();
4791 context->Global()->Set(v8_str("Fun"), cons);
4792 Local<Value> value = CompileRun(
4793 "function test() {"
4794 " try {"
4795 " (new Fun()).blah()"
4796 " } catch (e) {"
4797 " var str = String(e);"
4798 " if (str.indexOf('TypeError') == -1) return 1;"
4799 " if (str.indexOf('[object Fun]') != -1) return 2;"
4800 " if (str.indexOf('#<a Fun>') == -1) return 3;"
4801 " return 0;"
4802 " }"
4803 " return 4;"
4804 "}"
4805 "test();");
4806 CHECK_EQ(0, value->Int32Value());
4807}
4808
4809
4810THREADED_TEST(EvalAliasedDynamic) {
4811 v8::HandleScope scope;
4812 LocalContext current;
4813
4814 // Tests where aliased eval can only be resolved dynamically.
4815 Local<Script> script =
4816 Script::Compile(v8_str("function f(x) { "
4817 " var foo = 2;"
4818 " with (x) { return eval('foo'); }"
4819 "}"
4820 "foo = 0;"
4821 "result1 = f(new Object());"
4822 "result2 = f(this);"
4823 "var x = new Object();"
4824 "x.eval = function(x) { return 1; };"
4825 "result3 = f(x);"));
4826 script->Run();
4827 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
4828 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
4829 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
4830
4831 v8::TryCatch try_catch;
4832 script =
4833 Script::Compile(v8_str("function f(x) { "
4834 " var bar = 2;"
4835 " with (x) { return eval('bar'); }"
4836 "}"
4837 "f(this)"));
4838 script->Run();
4839 CHECK(try_catch.HasCaught());
4840 try_catch.Reset();
4841}
4842
4843
4844THREADED_TEST(CrossEval) {
4845 v8::HandleScope scope;
4846 LocalContext other;
4847 LocalContext current;
4848
4849 Local<String> token = v8_str("<security token>");
4850 other->SetSecurityToken(token);
4851 current->SetSecurityToken(token);
4852
4853 // Setup reference from current to other.
4854 current->Global()->Set(v8_str("other"), other->Global());
4855
4856 // Check that new variables are introduced in other context.
4857 Local<Script> script =
4858 Script::Compile(v8_str("other.eval('var foo = 1234')"));
4859 script->Run();
4860 Local<Value> foo = other->Global()->Get(v8_str("foo"));
4861 CHECK_EQ(1234, foo->Int32Value());
4862 CHECK(!current->Global()->Has(v8_str("foo")));
4863
4864 // Check that writing to non-existing properties introduces them in
4865 // the other context.
4866 script =
4867 Script::Compile(v8_str("other.eval('na = 1234')"));
4868 script->Run();
4869 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
4870 CHECK(!current->Global()->Has(v8_str("na")));
4871
4872 // Check that global variables in current context are not visible in other
4873 // context.
4874 v8::TryCatch try_catch;
4875 script =
4876 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
4877 Local<Value> result = script->Run();
4878 CHECK(try_catch.HasCaught());
4879 try_catch.Reset();
4880
4881 // Check that local variables in current context are not visible in other
4882 // context.
4883 script =
4884 Script::Compile(v8_str("(function() { "
4885 " var baz = 87;"
4886 " return other.eval('baz');"
4887 "})();"));
4888 result = script->Run();
4889 CHECK(try_catch.HasCaught());
4890 try_catch.Reset();
4891
4892 // Check that global variables in the other environment are visible
4893 // when evaluting code.
4894 other->Global()->Set(v8_str("bis"), v8_num(1234));
4895 script = Script::Compile(v8_str("other.eval('bis')"));
4896 CHECK_EQ(1234, script->Run()->Int32Value());
4897 CHECK(!try_catch.HasCaught());
4898
4899 // Check that the 'this' pointer points to the global object evaluating
4900 // code.
4901 other->Global()->Set(v8_str("t"), other->Global());
4902 script = Script::Compile(v8_str("other.eval('this == t')"));
4903 result = script->Run();
4904 CHECK(result->IsTrue());
4905 CHECK(!try_catch.HasCaught());
4906
4907 // Check that variables introduced in with-statement are not visible in
4908 // other context.
4909 script =
4910 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
4911 result = script->Run();
4912 CHECK(try_catch.HasCaught());
4913 try_catch.Reset();
4914
4915 // Check that you cannot use 'eval.call' with another object than the
4916 // current global object.
4917 script =
4918 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
4919 result = script->Run();
4920 CHECK(try_catch.HasCaught());
4921}
4922
4923
4924// Test that calling eval in a context which has been detached from
4925// its global throws an exception. This behavior is consistent with
4926// other JavaScript implementations.
4927THREADED_TEST(EvalInDetachedGlobal) {
4928 v8::HandleScope scope;
4929
4930 v8::Persistent<Context> context0 = Context::New();
4931 v8::Persistent<Context> context1 = Context::New();
4932
4933 // Setup function in context0 that uses eval from context0.
4934 context0->Enter();
4935 v8::Handle<v8::Value> fun =
4936 CompileRun("var x = 42;"
4937 "(function() {"
4938 " var e = eval;"
4939 " return function(s) { return e(s); }"
4940 "})()");
4941 context0->Exit();
4942
4943 // Put the function into context1 and call it before and after
4944 // detaching the global. Before detaching, the call succeeds and
4945 // after detaching and exception is thrown.
4946 context1->Enter();
4947 context1->Global()->Set(v8_str("fun"), fun);
4948 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
4949 CHECK_EQ(42, x_value->Int32Value());
4950 context0->DetachGlobal();
4951 v8::TryCatch catcher;
4952 x_value = CompileRun("fun('x')");
4953 CHECK(x_value.IsEmpty());
4954 CHECK(catcher.HasCaught());
4955 context1->Exit();
4956
4957 context1.Dispose();
4958 context0.Dispose();
4959}
4960
4961
4962THREADED_TEST(CrossLazyLoad) {
4963 v8::HandleScope scope;
4964 LocalContext other;
4965 LocalContext current;
4966
4967 Local<String> token = v8_str("<security token>");
4968 other->SetSecurityToken(token);
4969 current->SetSecurityToken(token);
4970
4971 // Setup reference from current to other.
4972 current->Global()->Set(v8_str("other"), other->Global());
4973
4974 // Trigger lazy loading in other context.
4975 Local<Script> script =
4976 Script::Compile(v8_str("other.eval('new Date(42)')"));
4977 Local<Value> value = script->Run();
4978 CHECK_EQ(42.0, value->NumberValue());
4979}
4980
4981
4982static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
4983 ApiTestFuzzer::Fuzz();
4984 if (args.IsConstructCall()) {
4985 if (args[0]->IsInt32()) {
4986 return v8_num(-args[0]->Int32Value());
4987 }
4988 }
4989
4990 return args[0];
4991}
4992
4993
4994// Test that a call handler can be set for objects which will allow
4995// non-function objects created through the API to be called as
4996// functions.
4997THREADED_TEST(CallAsFunction) {
4998 v8::HandleScope scope;
4999 LocalContext context;
5000
5001 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5002 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5003 instance_template->SetCallAsFunctionHandler(call_as_function);
5004 Local<v8::Object> instance = t->GetFunction()->NewInstance();
5005 context->Global()->Set(v8_str("obj"), instance);
5006 v8::TryCatch try_catch;
5007 Local<Value> value;
5008 CHECK(!try_catch.HasCaught());
5009
5010 value = CompileRun("obj(42)");
5011 CHECK(!try_catch.HasCaught());
5012 CHECK_EQ(42, value->Int32Value());
5013
5014 value = CompileRun("(function(o){return o(49)})(obj)");
5015 CHECK(!try_catch.HasCaught());
5016 CHECK_EQ(49, value->Int32Value());
5017
5018 // test special case of call as function
5019 value = CompileRun("[obj]['0'](45)");
5020 CHECK(!try_catch.HasCaught());
5021 CHECK_EQ(45, value->Int32Value());
5022
5023 value = CompileRun("obj.call = Function.prototype.call;"
5024 "obj.call(null, 87)");
5025 CHECK(!try_catch.HasCaught());
5026 CHECK_EQ(87, value->Int32Value());
5027
5028 // Regression tests for bug #1116356: Calling call through call/apply
5029 // must work for non-function receivers.
5030 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
5031 value = CompileRun(apply_99);
5032 CHECK(!try_catch.HasCaught());
5033 CHECK_EQ(99, value->Int32Value());
5034
5035 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
5036 value = CompileRun(call_17);
5037 CHECK(!try_catch.HasCaught());
5038 CHECK_EQ(17, value->Int32Value());
5039
5040 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00005041 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00005042 value = CompileRun("new obj(43)");
5043 CHECK(!try_catch.HasCaught());
5044 CHECK_EQ(-43, value->Int32Value());
5045}
5046
5047
5048static int CountHandles() {
5049 return v8::HandleScope::NumberOfHandles();
5050}
5051
5052
5053static int Recurse(int depth, int iterations) {
5054 v8::HandleScope scope;
5055 if (depth == 0) return CountHandles();
5056 for (int i = 0; i < iterations; i++) {
5057 Local<v8::Number> n = v8::Integer::New(42);
5058 }
5059 return Recurse(depth - 1, iterations);
5060}
5061
5062
5063THREADED_TEST(HandleIteration) {
5064 static const int kIterations = 500;
5065 static const int kNesting = 200;
5066 CHECK_EQ(0, CountHandles());
5067 {
5068 v8::HandleScope scope1;
5069 CHECK_EQ(0, CountHandles());
5070 for (int i = 0; i < kIterations; i++) {
5071 Local<v8::Number> n = v8::Integer::New(42);
5072 CHECK_EQ(i + 1, CountHandles());
5073 }
5074
5075 CHECK_EQ(kIterations, CountHandles());
5076 {
5077 v8::HandleScope scope2;
5078 for (int j = 0; j < kIterations; j++) {
5079 Local<v8::Number> n = v8::Integer::New(42);
5080 CHECK_EQ(j + 1 + kIterations, CountHandles());
5081 }
5082 }
5083 CHECK_EQ(kIterations, CountHandles());
5084 }
5085 CHECK_EQ(0, CountHandles());
5086 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
5087}
5088
5089
5090static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
5091 Local<String> name,
5092 const AccessorInfo& info) {
5093 ApiTestFuzzer::Fuzz();
5094 return v8::Handle<Value>();
5095}
5096
5097
5098THREADED_TEST(InterceptorHasOwnProperty) {
5099 v8::HandleScope scope;
5100 LocalContext context;
5101 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
5102 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5103 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
5104 Local<Function> function = fun_templ->GetFunction();
5105 context->Global()->Set(v8_str("constructor"), function);
5106 v8::Handle<Value> value = CompileRun(
5107 "var o = new constructor();"
5108 "o.hasOwnProperty('ostehaps');");
5109 CHECK_EQ(false, value->BooleanValue());
5110 value = CompileRun(
5111 "o.ostehaps = 42;"
5112 "o.hasOwnProperty('ostehaps');");
5113 CHECK_EQ(true, value->BooleanValue());
5114 value = CompileRun(
5115 "var p = new constructor();"
5116 "p.hasOwnProperty('ostehaps');");
5117 CHECK_EQ(false, value->BooleanValue());
5118}
5119
5120
5121static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
5122 Local<String> name,
5123 const AccessorInfo& info) {
5124 ApiTestFuzzer::Fuzz();
5125 i::Heap::CollectAllGarbage(false);
5126 return v8::Handle<Value>();
5127}
5128
5129
5130THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
5131 v8::HandleScope scope;
5132 LocalContext context;
5133 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
5134 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5135 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
5136 Local<Function> function = fun_templ->GetFunction();
5137 context->Global()->Set(v8_str("constructor"), function);
5138 // Let's first make some stuff so we can be sure to get a good GC.
5139 CompileRun(
5140 "function makestr(size) {"
5141 " switch (size) {"
5142 " case 1: return 'f';"
5143 " case 2: return 'fo';"
5144 " case 3: return 'foo';"
5145 " }"
5146 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
5147 "}"
5148 "var x = makestr(12345);"
5149 "x = makestr(31415);"
5150 "x = makestr(23456);");
5151 v8::Handle<Value> value = CompileRun(
5152 "var o = new constructor();"
5153 "o.__proto__ = new String(x);"
5154 "o.hasOwnProperty('ostehaps');");
5155 CHECK_EQ(false, value->BooleanValue());
5156}
5157
5158
5159typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
5160 const AccessorInfo& info);
5161
5162
5163static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
5164 const char* source,
5165 int expected) {
5166 v8::HandleScope scope;
5167 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5168 templ->SetNamedPropertyHandler(getter);
5169 LocalContext context;
5170 context->Global()->Set(v8_str("o"), templ->NewInstance());
5171 v8::Handle<Value> value = CompileRun(source);
5172 CHECK_EQ(expected, value->Int32Value());
5173}
5174
5175
5176static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
5177 const AccessorInfo& info) {
5178 ApiTestFuzzer::Fuzz();
5179 CHECK(v8_str("x")->Equals(name));
5180 return v8::Integer::New(42);
5181}
5182
5183
5184// This test should hit the load IC for the interceptor case.
5185THREADED_TEST(InterceptorLoadIC) {
5186 CheckInterceptorLoadIC(InterceptorLoadICGetter,
5187 "var result = 0;"
5188 "for (var i = 0; i < 1000; i++) {"
5189 " result = o.x;"
5190 "}",
5191 42);
5192}
5193
5194
5195// Below go several tests which verify that JITing for various
5196// configurations of interceptor and explicit fields works fine
5197// (those cases are special cased to get better performance).
5198
5199static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
5200 const AccessorInfo& info) {
5201 ApiTestFuzzer::Fuzz();
5202 return v8_str("x")->Equals(name)
5203 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
5204}
5205
5206
5207THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
5208 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5209 "var result = 0;"
5210 "o.y = 239;"
5211 "for (var i = 0; i < 1000; i++) {"
5212 " result = o.y;"
5213 "}",
5214 239);
5215}
5216
5217
5218THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
5219 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5220 "var result = 0;"
5221 "o.__proto__ = { 'y': 239 };"
5222 "for (var i = 0; i < 1000; i++) {"
5223 " result = o.y + o.x;"
5224 "}",
5225 239 + 42);
5226}
5227
5228
5229THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
5230 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5231 "var result = 0;"
5232 "o.__proto__.y = 239;"
5233 "for (var i = 0; i < 1000; i++) {"
5234 " result = o.y + o.x;"
5235 "}",
5236 239 + 42);
5237}
5238
5239
5240THREADED_TEST(InterceptorLoadICUndefined) {
5241 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5242 "var result = 0;"
5243 "for (var i = 0; i < 1000; i++) {"
5244 " result = (o.y == undefined) ? 239 : 42;"
5245 "}",
5246 239);
5247}
5248
5249
5250THREADED_TEST(InterceptorLoadICWithOverride) {
5251 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5252 "fst = new Object(); fst.__proto__ = o;"
5253 "snd = new Object(); snd.__proto__ = fst;"
5254 "var result1 = 0;"
5255 "for (var i = 0; i < 1000; i++) {"
5256 " result1 = snd.x;"
5257 "}"
5258 "fst.x = 239;"
5259 "var result = 0;"
5260 "for (var i = 0; i < 1000; i++) {"
5261 " result = snd.x;"
5262 "}"
5263 "result + result1",
5264 239 + 42);
5265}
5266
5267
5268// Test the case when we stored field into
5269// a stub, but interceptor produced value on its own.
5270THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
5271 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5272 "proto = new Object();"
5273 "o.__proto__ = proto;"
5274 "proto.x = 239;"
5275 "for (var i = 0; i < 1000; i++) {"
5276 " o.x;"
5277 // Now it should be ICed and keep a reference to x defined on proto
5278 "}"
5279 "var result = 0;"
5280 "for (var i = 0; i < 1000; i++) {"
5281 " result += o.x;"
5282 "}"
5283 "result;",
5284 42 * 1000);
5285}
5286
5287
5288// Test the case when we stored field into
5289// a stub, but it got invalidated later on.
5290THREADED_TEST(InterceptorLoadICInvalidatedField) {
5291 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5292 "proto1 = new Object();"
5293 "proto2 = new Object();"
5294 "o.__proto__ = proto1;"
5295 "proto1.__proto__ = proto2;"
5296 "proto2.y = 239;"
5297 "for (var i = 0; i < 1000; i++) {"
5298 " o.y;"
5299 // Now it should be ICed and keep a reference to y defined on proto2
5300 "}"
5301 "proto1.y = 42;"
5302 "var result = 0;"
5303 "for (var i = 0; i < 1000; i++) {"
5304 " result += o.y;"
5305 "}"
5306 "result;",
5307 42 * 1000);
5308}
5309
5310
5311// Test the case when we stored field into
5312// a stub, but it got invalidated later on due to override on
5313// global object which is between interceptor and fields' holders.
5314THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
5315 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5316 "o.__proto__ = this;" // set a global to be a proto of o.
5317 "this.__proto__.y = 239;"
5318 "for (var i = 0; i < 10; i++) {"
5319 " if (o.y != 239) throw 'oops: ' + o.y;"
5320 // Now it should be ICed and keep a reference to y defined on field_holder.
5321 "}"
5322 "this.y = 42;" // Assign on a global.
5323 "var result = 0;"
5324 "for (var i = 0; i < 10; i++) {"
5325 " result += o.y;"
5326 "}"
5327 "result;",
5328 42 * 10);
5329}
5330
5331
5332static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
5333 ApiTestFuzzer::Fuzz();
5334 return v8_num(239);
5335}
5336
5337
5338static void SetOnThis(Local<String> name,
5339 Local<Value> value,
5340 const AccessorInfo& info) {
5341 info.This()->ForceSet(name, value);
5342}
5343
5344
5345THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
5346 v8::HandleScope scope;
5347 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5348 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5349 templ->SetAccessor(v8_str("y"), Return239);
5350 LocalContext context;
5351 context->Global()->Set(v8_str("o"), templ->NewInstance());
5352 v8::Handle<Value> value = CompileRun(
5353 "var result = 0;"
5354 "for (var i = 0; i < 7; i++) {"
5355 " result = o.y;"
5356 "}");
5357 CHECK_EQ(239, value->Int32Value());
5358}
5359
5360
5361THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
5362 v8::HandleScope scope;
5363 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5364 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5365 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5366 templ_p->SetAccessor(v8_str("y"), Return239);
5367
5368 LocalContext context;
5369 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5370 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5371
5372 v8::Handle<Value> value = CompileRun(
5373 "o.__proto__ = p;"
5374 "var result = 0;"
5375 "for (var i = 0; i < 7; i++) {"
5376 " result = o.x + o.y;"
5377 "}");
5378 CHECK_EQ(239 + 42, value->Int32Value());
5379}
5380
5381
5382THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
5383 v8::HandleScope scope;
5384 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5385 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5386 templ->SetAccessor(v8_str("y"), Return239);
5387
5388 LocalContext context;
5389 context->Global()->Set(v8_str("o"), templ->NewInstance());
5390
5391 v8::Handle<Value> value = CompileRun(
5392 "fst = new Object(); fst.__proto__ = o;"
5393 "snd = new Object(); snd.__proto__ = fst;"
5394 "var result1 = 0;"
5395 "for (var i = 0; i < 7; i++) {"
5396 " result1 = snd.x;"
5397 "}"
5398 "fst.x = 239;"
5399 "var result = 0;"
5400 "for (var i = 0; i < 7; i++) {"
5401 " result = snd.x;"
5402 "}"
5403 "result + result1");
5404 CHECK_EQ(239 + 42, value->Int32Value());
5405}
5406
5407
5408// Test the case when we stored callback into
5409// a stub, but interceptor produced value on its own.
5410THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
5411 v8::HandleScope scope;
5412 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5413 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5414 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5415 templ_p->SetAccessor(v8_str("y"), Return239);
5416
5417 LocalContext context;
5418 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5419 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5420
5421 v8::Handle<Value> value = CompileRun(
5422 "o.__proto__ = p;"
5423 "for (var i = 0; i < 7; i++) {"
5424 " o.x;"
5425 // Now it should be ICed and keep a reference to x defined on p
5426 "}"
5427 "var result = 0;"
5428 "for (var i = 0; i < 7; i++) {"
5429 " result += o.x;"
5430 "}"
5431 "result");
5432 CHECK_EQ(42 * 7, value->Int32Value());
5433}
5434
5435
5436// Test the case when we stored callback into
5437// a stub, but it got invalidated later on.
5438THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
5439 v8::HandleScope scope;
5440 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5441 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5442 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5443 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
5444
5445 LocalContext context;
5446 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5447 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5448
5449 v8::Handle<Value> value = CompileRun(
5450 "inbetween = new Object();"
5451 "o.__proto__ = inbetween;"
5452 "inbetween.__proto__ = p;"
5453 "for (var i = 0; i < 10; i++) {"
5454 " o.y;"
5455 // Now it should be ICed and keep a reference to y defined on p
5456 "}"
5457 "inbetween.y = 42;"
5458 "var result = 0;"
5459 "for (var i = 0; i < 10; i++) {"
5460 " result += o.y;"
5461 "}"
5462 "result");
5463 CHECK_EQ(42 * 10, value->Int32Value());
5464}
5465
5466
5467// Test the case when we stored callback into
5468// a stub, but it got invalidated later on due to override on
5469// global object which is between interceptor and callbacks' holders.
5470THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
5471 v8::HandleScope scope;
5472 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5473 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5474 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5475 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
5476
5477 LocalContext context;
5478 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5479 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5480
5481 v8::Handle<Value> value = CompileRun(
5482 "o.__proto__ = this;"
5483 "this.__proto__ = p;"
5484 "for (var i = 0; i < 10; i++) {"
5485 " if (o.y != 239) throw 'oops: ' + o.y;"
5486 // Now it should be ICed and keep a reference to y defined on p
5487 "}"
5488 "this.y = 42;"
5489 "var result = 0;"
5490 "for (var i = 0; i < 10; i++) {"
5491 " result += o.y;"
5492 "}"
5493 "result");
5494 CHECK_EQ(42 * 10, value->Int32Value());
5495}
5496
5497
5498static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
5499 const AccessorInfo& info) {
5500 ApiTestFuzzer::Fuzz();
5501 CHECK(v8_str("x")->Equals(name));
5502 return v8::Integer::New(0);
5503}
5504
5505
5506THREADED_TEST(InterceptorReturningZero) {
5507 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
5508 "o.x == undefined ? 1 : 0",
5509 0);
5510}
5511
5512
5513static v8::Handle<Value> InterceptorStoreICSetter(
5514 Local<String> key, Local<Value> value, const AccessorInfo&) {
5515 CHECK(v8_str("x")->Equals(key));
5516 CHECK_EQ(42, value->Int32Value());
5517 return value;
5518}
5519
5520
5521// This test should hit the store IC for the interceptor case.
5522THREADED_TEST(InterceptorStoreIC) {
5523 v8::HandleScope scope;
5524 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5525 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
5526 InterceptorStoreICSetter);
5527 LocalContext context;
5528 context->Global()->Set(v8_str("o"), templ->NewInstance());
5529 v8::Handle<Value> value = CompileRun(
5530 "for (var i = 0; i < 1000; i++) {"
5531 " o.x = 42;"
5532 "}");
5533}
5534
5535
5536THREADED_TEST(InterceptorStoreICWithNoSetter) {
5537 v8::HandleScope scope;
5538 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5539 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5540 LocalContext context;
5541 context->Global()->Set(v8_str("o"), templ->NewInstance());
5542 v8::Handle<Value> value = CompileRun(
5543 "for (var i = 0; i < 1000; i++) {"
5544 " o.y = 239;"
5545 "}"
5546 "42 + o.y");
5547 CHECK_EQ(239 + 42, value->Int32Value());
5548}
5549
5550
5551
5552
5553v8::Handle<Value> call_ic_function;
5554v8::Handle<Value> call_ic_function2;
5555v8::Handle<Value> call_ic_function3;
5556
5557static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
5558 const AccessorInfo& info) {
5559 ApiTestFuzzer::Fuzz();
5560 CHECK(v8_str("x")->Equals(name));
5561 return call_ic_function;
5562}
5563
5564
5565// This test should hit the call IC for the interceptor case.
5566THREADED_TEST(InterceptorCallIC) {
5567 v8::HandleScope scope;
5568 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5569 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
5570 LocalContext context;
5571 context->Global()->Set(v8_str("o"), templ->NewInstance());
5572 call_ic_function =
5573 v8_compile("function f(x) { return x + 1; }; f")->Run();
5574 v8::Handle<Value> value = CompileRun(
5575 "var result = 0;"
5576 "for (var i = 0; i < 1000; i++) {"
5577 " result = o.x(41);"
5578 "}");
5579 CHECK_EQ(42, value->Int32Value());
5580}
5581
5582
5583// This test checks that if interceptor doesn't provide
5584// a value, we can fetch regular value.
5585THREADED_TEST(InterceptorCallICSeesOthers) {
5586 v8::HandleScope scope;
5587 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5588 templ->SetNamedPropertyHandler(NoBlockGetterX);
5589 LocalContext context;
5590 context->Global()->Set(v8_str("o"), templ->NewInstance());
5591 v8::Handle<Value> value = CompileRun(
5592 "o.x = function f(x) { return x + 1; };"
5593 "var result = 0;"
5594 "for (var i = 0; i < 7; i++) {"
5595 " result = o.x(41);"
5596 "}");
5597 CHECK_EQ(42, value->Int32Value());
5598}
5599
5600
5601static v8::Handle<Value> call_ic_function4;
5602static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
5603 const AccessorInfo& info) {
5604 ApiTestFuzzer::Fuzz();
5605 CHECK(v8_str("x")->Equals(name));
5606 return call_ic_function4;
5607}
5608
5609
5610// This test checks that if interceptor provides a function,
5611// even if we cached shadowed variant, interceptor's function
5612// is invoked
5613THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
5614 v8::HandleScope scope;
5615 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5616 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
5617 LocalContext context;
5618 context->Global()->Set(v8_str("o"), templ->NewInstance());
5619 call_ic_function4 =
5620 v8_compile("function f(x) { return x - 1; }; f")->Run();
5621 v8::Handle<Value> value = CompileRun(
5622 "o.__proto__.x = function(x) { return x + 1; };"
5623 "var result = 0;"
5624 "for (var i = 0; i < 1000; i++) {"
5625 " result = o.x(42);"
5626 "}");
5627 CHECK_EQ(41, value->Int32Value());
5628}
5629
5630
5631// Test the case when we stored cacheable lookup into
5632// a stub, but it got invalidated later on
5633THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
5634 v8::HandleScope scope;
5635 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5636 templ->SetNamedPropertyHandler(NoBlockGetterX);
5637 LocalContext context;
5638 context->Global()->Set(v8_str("o"), templ->NewInstance());
5639 v8::Handle<Value> value = CompileRun(
5640 "proto1 = new Object();"
5641 "proto2 = new Object();"
5642 "o.__proto__ = proto1;"
5643 "proto1.__proto__ = proto2;"
5644 "proto2.y = function(x) { return x + 1; };"
5645 // Invoke it many times to compile a stub
5646 "for (var i = 0; i < 7; i++) {"
5647 " o.y(42);"
5648 "}"
5649 "proto1.y = function(x) { return x - 1; };"
5650 "var result = 0;"
5651 "for (var i = 0; i < 7; i++) {"
5652 " result += o.y(42);"
5653 "}");
5654 CHECK_EQ(41 * 7, value->Int32Value());
5655}
5656
5657
5658static v8::Handle<Value> call_ic_function5;
5659static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
5660 const AccessorInfo& info) {
5661 ApiTestFuzzer::Fuzz();
5662 if (v8_str("x")->Equals(name))
5663 return call_ic_function5;
5664 else
5665 return Local<Value>();
5666}
5667
5668
5669// This test checks that if interceptor doesn't provide a function,
5670// cached constant function is used
5671THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
5672 v8::HandleScope scope;
5673 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5674 templ->SetNamedPropertyHandler(NoBlockGetterX);
5675 LocalContext context;
5676 context->Global()->Set(v8_str("o"), templ->NewInstance());
5677 v8::Handle<Value> value = CompileRun(
5678 "function inc(x) { return x + 1; };"
5679 "inc(1);"
5680 "o.x = inc;"
5681 "var result = 0;"
5682 "for (var i = 0; i < 1000; i++) {"
5683 " result = o.x(42);"
5684 "}");
5685 CHECK_EQ(43, value->Int32Value());
5686}
5687
5688
5689// This test checks that if interceptor provides a function,
5690// even if we cached constant function, interceptor's function
5691// is invoked
5692THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
5693 v8::HandleScope scope;
5694 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5695 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
5696 LocalContext context;
5697 context->Global()->Set(v8_str("o"), templ->NewInstance());
5698 call_ic_function5 =
5699 v8_compile("function f(x) { return x - 1; }; f")->Run();
5700 v8::Handle<Value> value = CompileRun(
5701 "function inc(x) { return x + 1; };"
5702 "inc(1);"
5703 "o.x = inc;"
5704 "var result = 0;"
5705 "for (var i = 0; i < 1000; i++) {"
5706 " result = o.x(42);"
5707 "}");
5708 CHECK_EQ(41, value->Int32Value());
5709}
5710
5711
5712// Test the case when we stored constant function into
5713// a stub, but it got invalidated later on
5714THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
5715 v8::HandleScope scope;
5716 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5717 templ->SetNamedPropertyHandler(NoBlockGetterX);
5718 LocalContext context;
5719 context->Global()->Set(v8_str("o"), templ->NewInstance());
5720 v8::Handle<Value> value = CompileRun(
5721 "function inc(x) { return x + 1; };"
5722 "inc(1);"
5723 "proto1 = new Object();"
5724 "proto2 = new Object();"
5725 "o.__proto__ = proto1;"
5726 "proto1.__proto__ = proto2;"
5727 "proto2.y = inc;"
5728 // Invoke it many times to compile a stub
5729 "for (var i = 0; i < 7; i++) {"
5730 " o.y(42);"
5731 "}"
5732 "proto1.y = function(x) { return x - 1; };"
5733 "var result = 0;"
5734 "for (var i = 0; i < 7; i++) {"
5735 " result += o.y(42);"
5736 "}");
5737 CHECK_EQ(41 * 7, value->Int32Value());
5738}
5739
5740
5741// Test the case when we stored constant function into
5742// a stub, but it got invalidated later on due to override on
5743// global object which is between interceptor and constant function' holders.
5744THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
5745 v8::HandleScope scope;
5746 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5747 templ->SetNamedPropertyHandler(NoBlockGetterX);
5748 LocalContext context;
5749 context->Global()->Set(v8_str("o"), templ->NewInstance());
5750 v8::Handle<Value> value = CompileRun(
5751 "function inc(x) { return x + 1; };"
5752 "inc(1);"
5753 "o.__proto__ = this;"
5754 "this.__proto__.y = inc;"
5755 // Invoke it many times to compile a stub
5756 "for (var i = 0; i < 7; i++) {"
5757 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
5758 "}"
5759 "this.y = function(x) { return x - 1; };"
5760 "var result = 0;"
5761 "for (var i = 0; i < 7; i++) {"
5762 " result += o.y(42);"
5763 "}");
5764 CHECK_EQ(41 * 7, value->Int32Value());
5765}
5766
5767
Leon Clarke4515c472010-02-03 11:58:03 +00005768// Test the case when actual function to call sits on global object.
5769THREADED_TEST(InterceptorCallICCachedFromGlobal) {
5770 v8::HandleScope scope;
5771 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5772 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
5773
5774 LocalContext context;
5775 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5776
5777 v8::Handle<Value> value = CompileRun(
5778 "try {"
5779 " o.__proto__ = this;"
5780 " for (var i = 0; i < 10; i++) {"
5781 " var v = o.parseFloat('239');"
5782 " if (v != 239) throw v;"
5783 // Now it should be ICed and keep a reference to parseFloat.
5784 " }"
5785 " var result = 0;"
5786 " for (var i = 0; i < 10; i++) {"
5787 " result += o.parseFloat('239');"
5788 " }"
5789 " result"
5790 "} catch(e) {"
5791 " e"
5792 "};");
5793 CHECK_EQ(239 * 10, value->Int32Value());
5794}
5795
5796
Steve Blocka7e24c12009-10-30 11:49:00 +00005797static int interceptor_call_count = 0;
5798
5799static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
5800 const AccessorInfo& info) {
5801 ApiTestFuzzer::Fuzz();
5802 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
5803 return call_ic_function2;
5804 }
5805 return v8::Handle<Value>();
5806}
5807
5808
5809// This test should hit load and call ICs for the interceptor case.
5810// Once in a while, the interceptor will reply that a property was not
5811// found in which case we should get a reference error.
5812THREADED_TEST(InterceptorICReferenceErrors) {
5813 v8::HandleScope scope;
5814 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5815 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
5816 LocalContext context(0, templ, v8::Handle<Value>());
5817 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
5818 v8::Handle<Value> value = CompileRun(
5819 "function f() {"
5820 " for (var i = 0; i < 1000; i++) {"
5821 " try { x; } catch(e) { return true; }"
5822 " }"
5823 " return false;"
5824 "};"
5825 "f();");
5826 CHECK_EQ(true, value->BooleanValue());
5827 interceptor_call_count = 0;
5828 value = CompileRun(
5829 "function g() {"
5830 " for (var i = 0; i < 1000; i++) {"
5831 " try { x(42); } catch(e) { return true; }"
5832 " }"
5833 " return false;"
5834 "};"
5835 "g();");
5836 CHECK_EQ(true, value->BooleanValue());
5837}
5838
5839
5840static int interceptor_ic_exception_get_count = 0;
5841
5842static v8::Handle<Value> InterceptorICExceptionGetter(
5843 Local<String> name,
5844 const AccessorInfo& info) {
5845 ApiTestFuzzer::Fuzz();
5846 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
5847 return call_ic_function3;
5848 }
5849 if (interceptor_ic_exception_get_count == 20) {
5850 return v8::ThrowException(v8_num(42));
5851 }
5852 // Do not handle get for properties other than x.
5853 return v8::Handle<Value>();
5854}
5855
5856// Test interceptor load/call IC where the interceptor throws an
5857// exception once in a while.
5858THREADED_TEST(InterceptorICGetterExceptions) {
5859 interceptor_ic_exception_get_count = 0;
5860 v8::HandleScope scope;
5861 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5862 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
5863 LocalContext context(0, templ, v8::Handle<Value>());
5864 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
5865 v8::Handle<Value> value = CompileRun(
5866 "function f() {"
5867 " for (var i = 0; i < 100; i++) {"
5868 " try { x; } catch(e) { return true; }"
5869 " }"
5870 " return false;"
5871 "};"
5872 "f();");
5873 CHECK_EQ(true, value->BooleanValue());
5874 interceptor_ic_exception_get_count = 0;
5875 value = CompileRun(
5876 "function f() {"
5877 " for (var i = 0; i < 100; i++) {"
5878 " try { x(42); } catch(e) { return true; }"
5879 " }"
5880 " return false;"
5881 "};"
5882 "f();");
5883 CHECK_EQ(true, value->BooleanValue());
5884}
5885
5886
5887static int interceptor_ic_exception_set_count = 0;
5888
5889static v8::Handle<Value> InterceptorICExceptionSetter(
5890 Local<String> key, Local<Value> value, const AccessorInfo&) {
5891 ApiTestFuzzer::Fuzz();
5892 if (++interceptor_ic_exception_set_count > 20) {
5893 return v8::ThrowException(v8_num(42));
5894 }
5895 // Do not actually handle setting.
5896 return v8::Handle<Value>();
5897}
5898
5899// Test interceptor store IC where the interceptor throws an exception
5900// once in a while.
5901THREADED_TEST(InterceptorICSetterExceptions) {
5902 interceptor_ic_exception_set_count = 0;
5903 v8::HandleScope scope;
5904 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5905 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
5906 LocalContext context(0, templ, v8::Handle<Value>());
5907 v8::Handle<Value> value = CompileRun(
5908 "function f() {"
5909 " for (var i = 0; i < 100; i++) {"
5910 " try { x = 42; } catch(e) { return true; }"
5911 " }"
5912 " return false;"
5913 "};"
5914 "f();");
5915 CHECK_EQ(true, value->BooleanValue());
5916}
5917
5918
5919// Test that we ignore null interceptors.
5920THREADED_TEST(NullNamedInterceptor) {
5921 v8::HandleScope scope;
5922 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5923 templ->SetNamedPropertyHandler(0);
5924 LocalContext context;
5925 templ->Set("x", v8_num(42));
5926 v8::Handle<v8::Object> obj = templ->NewInstance();
5927 context->Global()->Set(v8_str("obj"), obj);
5928 v8::Handle<Value> value = CompileRun("obj.x");
5929 CHECK(value->IsInt32());
5930 CHECK_EQ(42, value->Int32Value());
5931}
5932
5933
5934// Test that we ignore null interceptors.
5935THREADED_TEST(NullIndexedInterceptor) {
5936 v8::HandleScope scope;
5937 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5938 templ->SetIndexedPropertyHandler(0);
5939 LocalContext context;
5940 templ->Set("42", v8_num(42));
5941 v8::Handle<v8::Object> obj = templ->NewInstance();
5942 context->Global()->Set(v8_str("obj"), obj);
5943 v8::Handle<Value> value = CompileRun("obj[42]");
5944 CHECK(value->IsInt32());
5945 CHECK_EQ(42, value->Int32Value());
5946}
5947
5948
5949static v8::Handle<Value> ParentGetter(Local<String> name,
5950 const AccessorInfo& info) {
5951 ApiTestFuzzer::Fuzz();
5952 return v8_num(1);
5953}
5954
5955
5956static v8::Handle<Value> ChildGetter(Local<String> name,
5957 const AccessorInfo& info) {
5958 ApiTestFuzzer::Fuzz();
5959 return v8_num(42);
5960}
5961
5962
5963THREADED_TEST(Overriding) {
5964 v8::HandleScope scope;
5965 LocalContext context;
5966
5967 // Parent template.
5968 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
5969 Local<ObjectTemplate> parent_instance_templ =
5970 parent_templ->InstanceTemplate();
5971 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
5972
5973 // Template that inherits from the parent template.
5974 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
5975 Local<ObjectTemplate> child_instance_templ =
5976 child_templ->InstanceTemplate();
5977 child_templ->Inherit(parent_templ);
5978 // Override 'f'. The child version of 'f' should get called for child
5979 // instances.
5980 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
5981 // Add 'g' twice. The 'g' added last should get called for instances.
5982 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
5983 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
5984
5985 // Add 'h' as an accessor to the proto template with ReadOnly attributes
5986 // so 'h' can be shadowed on the instance object.
5987 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
5988 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
5989 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
5990
5991 // Add 'i' as an accessor to the instance template with ReadOnly attributes
5992 // but the attribute does not have effect because it is duplicated with
5993 // NULL setter.
5994 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
5995 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
5996
5997
5998
5999 // Instantiate the child template.
6000 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
6001
6002 // Check that the child function overrides the parent one.
6003 context->Global()->Set(v8_str("o"), instance);
6004 Local<Value> value = v8_compile("o.f")->Run();
6005 // Check that the 'g' that was added last is hit.
6006 CHECK_EQ(42, value->Int32Value());
6007 value = v8_compile("o.g")->Run();
6008 CHECK_EQ(42, value->Int32Value());
6009
6010 // Check 'h' can be shadowed.
6011 value = v8_compile("o.h = 3; o.h")->Run();
6012 CHECK_EQ(3, value->Int32Value());
6013
6014 // Check 'i' is cannot be shadowed or changed.
6015 value = v8_compile("o.i = 3; o.i")->Run();
6016 CHECK_EQ(42, value->Int32Value());
6017}
6018
6019
6020static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
6021 ApiTestFuzzer::Fuzz();
6022 if (args.IsConstructCall()) {
6023 return v8::Boolean::New(true);
6024 }
6025 return v8::Boolean::New(false);
6026}
6027
6028
6029THREADED_TEST(IsConstructCall) {
6030 v8::HandleScope scope;
6031
6032 // Function template with call handler.
6033 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6034 templ->SetCallHandler(IsConstructHandler);
6035
6036 LocalContext context;
6037
6038 context->Global()->Set(v8_str("f"), templ->GetFunction());
6039 Local<Value> value = v8_compile("f()")->Run();
6040 CHECK(!value->BooleanValue());
6041 value = v8_compile("new f()")->Run();
6042 CHECK(value->BooleanValue());
6043}
6044
6045
6046THREADED_TEST(ObjectProtoToString) {
6047 v8::HandleScope scope;
6048 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6049 templ->SetClassName(v8_str("MyClass"));
6050
6051 LocalContext context;
6052
6053 Local<String> customized_tostring = v8_str("customized toString");
6054
6055 // Replace Object.prototype.toString
6056 v8_compile("Object.prototype.toString = function() {"
6057 " return 'customized toString';"
6058 "}")->Run();
6059
6060 // Normal ToString call should call replaced Object.prototype.toString
6061 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
6062 Local<String> value = instance->ToString();
6063 CHECK(value->IsString() && value->Equals(customized_tostring));
6064
6065 // ObjectProtoToString should not call replace toString function.
6066 value = instance->ObjectProtoToString();
6067 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
6068
6069 // Check global
6070 value = context->Global()->ObjectProtoToString();
6071 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
6072
6073 // Check ordinary object
6074 Local<Value> object = v8_compile("new Object()")->Run();
6075 value = Local<v8::Object>::Cast(object)->ObjectProtoToString();
6076 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
6077}
6078
6079
6080bool ApiTestFuzzer::fuzzing_ = false;
6081v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_=
6082 v8::internal::OS::CreateSemaphore(0);
6083int ApiTestFuzzer::active_tests_;
6084int ApiTestFuzzer::tests_being_run_;
6085int ApiTestFuzzer::current_;
6086
6087
6088// We are in a callback and want to switch to another thread (if we
6089// are currently running the thread fuzzing test).
6090void ApiTestFuzzer::Fuzz() {
6091 if (!fuzzing_) return;
6092 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
6093 test->ContextSwitch();
6094}
6095
6096
6097// Let the next thread go. Since it is also waiting on the V8 lock it may
6098// not start immediately.
6099bool ApiTestFuzzer::NextThread() {
6100 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00006101 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00006102 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00006103 if (kLogThreading)
6104 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00006105 return false;
6106 }
Steve Blockd0582a62009-12-15 09:54:21 +00006107 if (kLogThreading) {
6108 printf("Switch from %s to %s\n",
6109 test_name,
6110 RegisterThreadedTest::nth(test_position)->name());
6111 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006112 current_ = test_position;
6113 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
6114 return true;
6115}
6116
6117
6118void ApiTestFuzzer::Run() {
6119 // When it is our turn...
6120 gate_->Wait();
6121 {
6122 // ... get the V8 lock and start running the test.
6123 v8::Locker locker;
6124 CallTest();
6125 }
6126 // This test finished.
6127 active_ = false;
6128 active_tests_--;
6129 // If it was the last then signal that fact.
6130 if (active_tests_ == 0) {
6131 all_tests_done_->Signal();
6132 } else {
6133 // Otherwise select a new test and start that.
6134 NextThread();
6135 }
6136}
6137
6138
6139static unsigned linear_congruential_generator;
6140
6141
6142void ApiTestFuzzer::Setup(PartOfTest part) {
6143 linear_congruential_generator = i::FLAG_testing_prng_seed;
6144 fuzzing_ = true;
6145 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
6146 int end = (part == FIRST_PART)
6147 ? (RegisterThreadedTest::count() >> 1)
6148 : RegisterThreadedTest::count();
6149 active_tests_ = tests_being_run_ = end - start;
6150 for (int i = 0; i < tests_being_run_; i++) {
6151 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
6152 }
6153 for (int i = 0; i < active_tests_; i++) {
6154 RegisterThreadedTest::nth(i)->fuzzer_->Start();
6155 }
6156}
6157
6158
6159static void CallTestNumber(int test_number) {
6160 (RegisterThreadedTest::nth(test_number)->callback())();
6161}
6162
6163
6164void ApiTestFuzzer::RunAllTests() {
6165 // Set off the first test.
6166 current_ = -1;
6167 NextThread();
6168 // Wait till they are all done.
6169 all_tests_done_->Wait();
6170}
6171
6172
6173int ApiTestFuzzer::GetNextTestNumber() {
6174 int next_test;
6175 do {
6176 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
6177 linear_congruential_generator *= 1664525u;
6178 linear_congruential_generator += 1013904223u;
6179 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
6180 return next_test;
6181}
6182
6183
6184void ApiTestFuzzer::ContextSwitch() {
6185 // If the new thread is the same as the current thread there is nothing to do.
6186 if (NextThread()) {
6187 // Now it can start.
6188 v8::Unlocker unlocker;
6189 // Wait till someone starts us again.
6190 gate_->Wait();
6191 // And we're off.
6192 }
6193}
6194
6195
6196void ApiTestFuzzer::TearDown() {
6197 fuzzing_ = false;
6198 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
6199 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
6200 if (fuzzer != NULL) fuzzer->Join();
6201 }
6202}
6203
6204
6205// Lets not be needlessly self-referential.
6206TEST(Threading) {
6207 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
6208 ApiTestFuzzer::RunAllTests();
6209 ApiTestFuzzer::TearDown();
6210}
6211
6212TEST(Threading2) {
6213 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
6214 ApiTestFuzzer::RunAllTests();
6215 ApiTestFuzzer::TearDown();
6216}
6217
6218
6219void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00006220 if (kLogThreading)
6221 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00006222 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00006223 if (kLogThreading)
6224 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00006225}
6226
6227
6228static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
6229 CHECK(v8::Locker::IsLocked());
6230 ApiTestFuzzer::Fuzz();
6231 v8::Unlocker unlocker;
6232 const char* code = "throw 7;";
6233 {
6234 v8::Locker nested_locker;
6235 v8::HandleScope scope;
6236 v8::Handle<Value> exception;
6237 { v8::TryCatch try_catch;
6238 v8::Handle<Value> value = CompileRun(code);
6239 CHECK(value.IsEmpty());
6240 CHECK(try_catch.HasCaught());
6241 // Make sure to wrap the exception in a new handle because
6242 // the handle returned from the TryCatch is destroyed
6243 // when the TryCatch is destroyed.
6244 exception = Local<Value>::New(try_catch.Exception());
6245 }
6246 return v8::ThrowException(exception);
6247 }
6248}
6249
6250
6251static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
6252 CHECK(v8::Locker::IsLocked());
6253 ApiTestFuzzer::Fuzz();
6254 v8::Unlocker unlocker;
6255 const char* code = "throw 7;";
6256 {
6257 v8::Locker nested_locker;
6258 v8::HandleScope scope;
6259 v8::Handle<Value> value = CompileRun(code);
6260 CHECK(value.IsEmpty());
6261 return v8_str("foo");
6262 }
6263}
6264
6265
6266// These are locking tests that don't need to be run again
6267// as part of the locking aggregation tests.
6268TEST(NestedLockers) {
6269 v8::Locker locker;
6270 CHECK(v8::Locker::IsLocked());
6271 v8::HandleScope scope;
6272 LocalContext env;
6273 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
6274 Local<Function> fun = fun_templ->GetFunction();
6275 env->Global()->Set(v8_str("throw_in_js"), fun);
6276 Local<Script> script = v8_compile("(function () {"
6277 " try {"
6278 " throw_in_js();"
6279 " return 42;"
6280 " } catch (e) {"
6281 " return e * 13;"
6282 " }"
6283 "})();");
6284 CHECK_EQ(91, script->Run()->Int32Value());
6285}
6286
6287
6288// These are locking tests that don't need to be run again
6289// as part of the locking aggregation tests.
6290TEST(NestedLockersNoTryCatch) {
6291 v8::Locker locker;
6292 v8::HandleScope scope;
6293 LocalContext env;
6294 Local<v8::FunctionTemplate> fun_templ =
6295 v8::FunctionTemplate::New(ThrowInJSNoCatch);
6296 Local<Function> fun = fun_templ->GetFunction();
6297 env->Global()->Set(v8_str("throw_in_js"), fun);
6298 Local<Script> script = v8_compile("(function () {"
6299 " try {"
6300 " throw_in_js();"
6301 " return 42;"
6302 " } catch (e) {"
6303 " return e * 13;"
6304 " }"
6305 "})();");
6306 CHECK_EQ(91, script->Run()->Int32Value());
6307}
6308
6309
6310THREADED_TEST(RecursiveLocking) {
6311 v8::Locker locker;
6312 {
6313 v8::Locker locker2;
6314 CHECK(v8::Locker::IsLocked());
6315 }
6316}
6317
6318
6319static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
6320 ApiTestFuzzer::Fuzz();
6321 v8::Unlocker unlocker;
6322 return v8::Undefined();
6323}
6324
6325
6326THREADED_TEST(LockUnlockLock) {
6327 {
6328 v8::Locker locker;
6329 v8::HandleScope scope;
6330 LocalContext env;
6331 Local<v8::FunctionTemplate> fun_templ =
6332 v8::FunctionTemplate::New(UnlockForAMoment);
6333 Local<Function> fun = fun_templ->GetFunction();
6334 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
6335 Local<Script> script = v8_compile("(function () {"
6336 " unlock_for_a_moment();"
6337 " return 42;"
6338 "})();");
6339 CHECK_EQ(42, script->Run()->Int32Value());
6340 }
6341 {
6342 v8::Locker locker;
6343 v8::HandleScope scope;
6344 LocalContext env;
6345 Local<v8::FunctionTemplate> fun_templ =
6346 v8::FunctionTemplate::New(UnlockForAMoment);
6347 Local<Function> fun = fun_templ->GetFunction();
6348 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
6349 Local<Script> script = v8_compile("(function () {"
6350 " unlock_for_a_moment();"
6351 " return 42;"
6352 "})();");
6353 CHECK_EQ(42, script->Run()->Int32Value());
6354 }
6355}
6356
6357
Leon Clarked91b9f72010-01-27 17:25:45 +00006358static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00006359 int count = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +00006360 v8::internal::HeapIterator it;
6361 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
6362 if (object->IsJSGlobalObject()) count++;
6363 return count;
6364}
6365
6366
6367static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00006368 // We need to collect all garbage twice to be sure that everything
6369 // has been collected. This is because inline caches are cleared in
6370 // the first garbage collection but some of the maps have already
6371 // been marked at that point. Therefore some of the maps are not
6372 // collected until the second garbage collection.
6373 v8::internal::Heap::CollectAllGarbage(false);
6374 v8::internal::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00006375 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00006376#ifdef DEBUG
6377 if (count > 0) v8::internal::Heap::TracePathToGlobal();
6378#endif
6379 return count;
6380}
6381
6382
6383TEST(DontLeakGlobalObjects) {
6384 // Regression test for issues 1139850 and 1174891.
6385
6386 v8::V8::Initialize();
6387
6388 int count = GetSurvivingGlobalObjectsCount();
6389
6390 for (int i = 0; i < 5; i++) {
6391 { v8::HandleScope scope;
6392 LocalContext context;
6393 }
6394 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6395
6396 { v8::HandleScope scope;
6397 LocalContext context;
6398 v8_compile("Date")->Run();
6399 }
6400 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6401
6402 { v8::HandleScope scope;
6403 LocalContext context;
6404 v8_compile("/aaa/")->Run();
6405 }
6406 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6407
6408 { v8::HandleScope scope;
6409 const char* extension_list[] = { "v8/gc" };
6410 v8::ExtensionConfiguration extensions(1, extension_list);
6411 LocalContext context(&extensions);
6412 v8_compile("gc();")->Run();
6413 }
6414 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6415 }
6416}
6417
6418
6419v8::Persistent<v8::Object> some_object;
6420v8::Persistent<v8::Object> bad_handle;
6421
6422void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
6423 v8::HandleScope scope;
6424 bad_handle = v8::Persistent<v8::Object>::New(some_object);
6425}
6426
6427
6428THREADED_TEST(NewPersistentHandleFromWeakCallback) {
6429 LocalContext context;
6430
6431 v8::Persistent<v8::Object> handle1, handle2;
6432 {
6433 v8::HandleScope scope;
6434 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
6435 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
6436 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
6437 }
6438 // Note: order is implementation dependent alas: currently
6439 // global handle nodes are processed by PostGarbageCollectionProcessing
6440 // in reverse allocation order, so if second allocated handle is deleted,
6441 // weak callback of the first handle would be able to 'reallocate' it.
6442 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
6443 handle2.Dispose();
6444 i::Heap::CollectAllGarbage(false);
6445}
6446
6447
6448v8::Persistent<v8::Object> to_be_disposed;
6449
6450void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
6451 to_be_disposed.Dispose();
6452 i::Heap::CollectAllGarbage(false);
6453}
6454
6455
6456THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
6457 LocalContext context;
6458
6459 v8::Persistent<v8::Object> handle1, handle2;
6460 {
6461 v8::HandleScope scope;
6462 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
6463 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
6464 }
6465 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
6466 to_be_disposed = handle2;
6467 i::Heap::CollectAllGarbage(false);
6468}
6469
Steve Blockd0582a62009-12-15 09:54:21 +00006470void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
6471 handle.Dispose();
6472}
6473
6474void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
6475 v8::HandleScope scope;
6476 v8::Persistent<v8::Object>::New(v8::Object::New());
6477}
6478
6479
6480THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
6481 LocalContext context;
6482
6483 v8::Persistent<v8::Object> handle1, handle2, handle3;
6484 {
6485 v8::HandleScope scope;
6486 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
6487 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
6488 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
6489 }
6490 handle2.MakeWeak(NULL, DisposingCallback);
6491 handle3.MakeWeak(NULL, HandleCreatingCallback);
6492 i::Heap::CollectAllGarbage(false);
6493}
6494
Steve Blocka7e24c12009-10-30 11:49:00 +00006495
6496THREADED_TEST(CheckForCrossContextObjectLiterals) {
6497 v8::V8::Initialize();
6498
6499 const int nof = 2;
6500 const char* sources[nof] = {
6501 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
6502 "Object()"
6503 };
6504
6505 for (int i = 0; i < nof; i++) {
6506 const char* source = sources[i];
6507 { v8::HandleScope scope;
6508 LocalContext context;
6509 CompileRun(source);
6510 }
6511 { v8::HandleScope scope;
6512 LocalContext context;
6513 CompileRun(source);
6514 }
6515 }
6516}
6517
6518
6519static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
6520 v8::HandleScope inner;
6521 env->Enter();
6522 v8::Handle<Value> three = v8_num(3);
6523 v8::Handle<Value> value = inner.Close(three);
6524 env->Exit();
6525 return value;
6526}
6527
6528
6529THREADED_TEST(NestedHandleScopeAndContexts) {
6530 v8::HandleScope outer;
6531 v8::Persistent<Context> env = Context::New();
6532 env->Enter();
6533 v8::Handle<Value> value = NestedScope(env);
6534 v8::Handle<String> str = value->ToString();
6535 env->Exit();
6536 env.Dispose();
6537}
6538
6539
6540THREADED_TEST(ExternalAllocatedMemory) {
6541 v8::HandleScope outer;
6542 v8::Persistent<Context> env = Context::New();
6543 const int kSize = 1024*1024;
6544 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
6545 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
6546}
6547
6548
6549THREADED_TEST(DisposeEnteredContext) {
6550 v8::HandleScope scope;
6551 LocalContext outer;
6552 { v8::Persistent<v8::Context> inner = v8::Context::New();
6553 inner->Enter();
6554 inner.Dispose();
6555 inner.Clear();
6556 inner->Exit();
6557 }
6558}
6559
6560
6561// Regression test for issue 54, object templates with internal fields
6562// but no accessors or interceptors did not get their internal field
6563// count set on instances.
6564THREADED_TEST(Regress54) {
6565 v8::HandleScope outer;
6566 LocalContext context;
6567 static v8::Persistent<v8::ObjectTemplate> templ;
6568 if (templ.IsEmpty()) {
6569 v8::HandleScope inner;
6570 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
6571 local->SetInternalFieldCount(1);
6572 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
6573 }
6574 v8::Handle<v8::Object> result = templ->NewInstance();
6575 CHECK_EQ(1, result->InternalFieldCount());
6576}
6577
6578
6579// If part of the threaded tests, this test makes ThreadingTest fail
6580// on mac.
6581TEST(CatchStackOverflow) {
6582 v8::HandleScope scope;
6583 LocalContext context;
6584 v8::TryCatch try_catch;
6585 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
6586 "function f() {"
6587 " return f();"
6588 "}"
6589 ""
6590 "f();"));
6591 v8::Handle<v8::Value> result = script->Run();
6592 CHECK(result.IsEmpty());
6593}
6594
6595
6596static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
6597 const char* resource_name,
6598 int line_offset) {
6599 v8::HandleScope scope;
6600 v8::TryCatch try_catch;
6601 v8::Handle<v8::Value> result = script->Run();
6602 CHECK(result.IsEmpty());
6603 CHECK(try_catch.HasCaught());
6604 v8::Handle<v8::Message> message = try_catch.Message();
6605 CHECK(!message.IsEmpty());
6606 CHECK_EQ(10 + line_offset, message->GetLineNumber());
6607 CHECK_EQ(91, message->GetStartPosition());
6608 CHECK_EQ(92, message->GetEndPosition());
6609 CHECK_EQ(2, message->GetStartColumn());
6610 CHECK_EQ(3, message->GetEndColumn());
6611 v8::String::AsciiValue line(message->GetSourceLine());
6612 CHECK_EQ(" throw 'nirk';", *line);
6613 v8::String::AsciiValue name(message->GetScriptResourceName());
6614 CHECK_EQ(resource_name, *name);
6615}
6616
6617
6618THREADED_TEST(TryCatchSourceInfo) {
6619 v8::HandleScope scope;
6620 LocalContext context;
6621 v8::Handle<v8::String> source = v8::String::New(
6622 "function Foo() {\n"
6623 " return Bar();\n"
6624 "}\n"
6625 "\n"
6626 "function Bar() {\n"
6627 " return Baz();\n"
6628 "}\n"
6629 "\n"
6630 "function Baz() {\n"
6631 " throw 'nirk';\n"
6632 "}\n"
6633 "\n"
6634 "Foo();\n");
6635
6636 const char* resource_name;
6637 v8::Handle<v8::Script> script;
6638 resource_name = "test.js";
6639 script = v8::Script::Compile(source, v8::String::New(resource_name));
6640 CheckTryCatchSourceInfo(script, resource_name, 0);
6641
6642 resource_name = "test1.js";
6643 v8::ScriptOrigin origin1(v8::String::New(resource_name));
6644 script = v8::Script::Compile(source, &origin1);
6645 CheckTryCatchSourceInfo(script, resource_name, 0);
6646
6647 resource_name = "test2.js";
6648 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
6649 script = v8::Script::Compile(source, &origin2);
6650 CheckTryCatchSourceInfo(script, resource_name, 7);
6651}
6652
6653
6654THREADED_TEST(CompilationCache) {
6655 v8::HandleScope scope;
6656 LocalContext context;
6657 v8::Handle<v8::String> source0 = v8::String::New("1234");
6658 v8::Handle<v8::String> source1 = v8::String::New("1234");
6659 v8::Handle<v8::Script> script0 =
6660 v8::Script::Compile(source0, v8::String::New("test.js"));
6661 v8::Handle<v8::Script> script1 =
6662 v8::Script::Compile(source1, v8::String::New("test.js"));
6663 v8::Handle<v8::Script> script2 =
6664 v8::Script::Compile(source0); // different origin
6665 CHECK_EQ(1234, script0->Run()->Int32Value());
6666 CHECK_EQ(1234, script1->Run()->Int32Value());
6667 CHECK_EQ(1234, script2->Run()->Int32Value());
6668}
6669
6670
6671static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
6672 ApiTestFuzzer::Fuzz();
6673 return v8_num(42);
6674}
6675
6676
6677THREADED_TEST(CallbackFunctionName) {
6678 v8::HandleScope scope;
6679 LocalContext context;
6680 Local<ObjectTemplate> t = ObjectTemplate::New();
6681 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
6682 context->Global()->Set(v8_str("obj"), t->NewInstance());
6683 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
6684 CHECK(value->IsString());
6685 v8::String::AsciiValue name(value);
6686 CHECK_EQ("asdf", *name);
6687}
6688
6689
6690THREADED_TEST(DateAccess) {
6691 v8::HandleScope scope;
6692 LocalContext context;
6693 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
6694 CHECK(date->IsDate());
6695 CHECK_EQ(1224744689038.0, v8::Handle<v8::Date>::Cast(date)->NumberValue());
6696}
6697
6698
6699void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
6700 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
6701 v8::Handle<v8::Array> props = obj->GetPropertyNames();
6702 CHECK_EQ(elmc, props->Length());
6703 for (int i = 0; i < elmc; i++) {
6704 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
6705 CHECK_EQ(elmv[i], *elm);
6706 }
6707}
6708
6709
6710THREADED_TEST(PropertyEnumeration) {
6711 v8::HandleScope scope;
6712 LocalContext context;
6713 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
6714 "var result = [];"
6715 "result[0] = {};"
6716 "result[1] = {a: 1, b: 2};"
6717 "result[2] = [1, 2, 3];"
6718 "var proto = {x: 1, y: 2, z: 3};"
6719 "var x = { __proto__: proto, w: 0, z: 1 };"
6720 "result[3] = x;"
6721 "result;"))->Run();
6722 v8::Handle<v8::Array> elms = v8::Handle<v8::Array>::Cast(obj);
6723 CHECK_EQ(4, elms->Length());
6724 int elmc0 = 0;
6725 const char** elmv0 = NULL;
6726 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
6727 int elmc1 = 2;
6728 const char* elmv1[] = {"a", "b"};
6729 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
6730 int elmc2 = 3;
6731 const char* elmv2[] = {"0", "1", "2"};
6732 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
6733 int elmc3 = 4;
6734 const char* elmv3[] = {"w", "z", "x", "y"};
6735 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
6736}
6737
6738
Steve Blocka7e24c12009-10-30 11:49:00 +00006739static bool NamedSetAccessBlocker(Local<v8::Object> obj,
6740 Local<Value> name,
6741 v8::AccessType type,
6742 Local<Value> data) {
6743 return type != v8::ACCESS_SET;
6744}
6745
6746
6747static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
6748 uint32_t key,
6749 v8::AccessType type,
6750 Local<Value> data) {
6751 return type != v8::ACCESS_SET;
6752}
6753
6754
6755THREADED_TEST(DisableAccessChecksWhileConfiguring) {
6756 v8::HandleScope scope;
6757 LocalContext context;
6758 Local<ObjectTemplate> templ = ObjectTemplate::New();
6759 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
6760 IndexedSetAccessBlocker);
6761 templ->Set(v8_str("x"), v8::True());
6762 Local<v8::Object> instance = templ->NewInstance();
6763 context->Global()->Set(v8_str("obj"), instance);
6764 Local<Value> value = CompileRun("obj.x");
6765 CHECK(value->BooleanValue());
6766}
6767
6768
6769static bool NamedGetAccessBlocker(Local<v8::Object> obj,
6770 Local<Value> name,
6771 v8::AccessType type,
6772 Local<Value> data) {
6773 return false;
6774}
6775
6776
6777static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
6778 uint32_t key,
6779 v8::AccessType type,
6780 Local<Value> data) {
6781 return false;
6782}
6783
6784
6785
6786THREADED_TEST(AccessChecksReenabledCorrectly) {
6787 v8::HandleScope scope;
6788 LocalContext context;
6789 Local<ObjectTemplate> templ = ObjectTemplate::New();
6790 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
6791 IndexedGetAccessBlocker);
6792 templ->Set(v8_str("a"), v8_str("a"));
6793 // Add more than 8 (see kMaxFastProperties) properties
6794 // so that the constructor will force copying map.
6795 // Cannot sprintf, gcc complains unsafety.
6796 char buf[4];
6797 for (char i = '0'; i <= '9' ; i++) {
6798 buf[0] = i;
6799 for (char j = '0'; j <= '9'; j++) {
6800 buf[1] = j;
6801 for (char k = '0'; k <= '9'; k++) {
6802 buf[2] = k;
6803 buf[3] = 0;
6804 templ->Set(v8_str(buf), v8::Number::New(k));
6805 }
6806 }
6807 }
6808
6809 Local<v8::Object> instance_1 = templ->NewInstance();
6810 context->Global()->Set(v8_str("obj_1"), instance_1);
6811
6812 Local<Value> value_1 = CompileRun("obj_1.a");
6813 CHECK(value_1->IsUndefined());
6814
6815 Local<v8::Object> instance_2 = templ->NewInstance();
6816 context->Global()->Set(v8_str("obj_2"), instance_2);
6817
6818 Local<Value> value_2 = CompileRun("obj_2.a");
6819 CHECK(value_2->IsUndefined());
6820}
6821
6822
6823// This tests that access check information remains on the global
6824// object template when creating contexts.
6825THREADED_TEST(AccessControlRepeatedContextCreation) {
6826 v8::HandleScope handle_scope;
6827 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
6828 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
6829 IndexedSetAccessBlocker);
6830 i::Handle<i::ObjectTemplateInfo> internal_template =
6831 v8::Utils::OpenHandle(*global_template);
6832 CHECK(!internal_template->constructor()->IsUndefined());
6833 i::Handle<i::FunctionTemplateInfo> constructor(
6834 i::FunctionTemplateInfo::cast(internal_template->constructor()));
6835 CHECK(!constructor->access_check_info()->IsUndefined());
6836 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
6837 CHECK(!constructor->access_check_info()->IsUndefined());
6838}
6839
6840
6841THREADED_TEST(TurnOnAccessCheck) {
6842 v8::HandleScope handle_scope;
6843
6844 // Create an environment with access check to the global object disabled by
6845 // default.
6846 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
6847 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
6848 IndexedGetAccessBlocker,
6849 v8::Handle<v8::Value>(),
6850 false);
6851 v8::Persistent<Context> context = Context::New(NULL, global_template);
6852 Context::Scope context_scope(context);
6853
6854 // Set up a property and a number of functions.
6855 context->Global()->Set(v8_str("a"), v8_num(1));
6856 CompileRun("function f1() {return a;}"
6857 "function f2() {return a;}"
6858 "function g1() {return h();}"
6859 "function g2() {return h();}"
6860 "function h() {return 1;}");
6861 Local<Function> f1 =
6862 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
6863 Local<Function> f2 =
6864 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
6865 Local<Function> g1 =
6866 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
6867 Local<Function> g2 =
6868 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
6869 Local<Function> h =
6870 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
6871
6872 // Get the global object.
6873 v8::Handle<v8::Object> global = context->Global();
6874
6875 // Call f1 one time and f2 a number of times. This will ensure that f1 still
6876 // uses the runtime system to retreive property a whereas f2 uses global load
6877 // inline cache.
6878 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
6879 for (int i = 0; i < 4; i++) {
6880 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
6881 }
6882
6883 // Same for g1 and g2.
6884 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
6885 for (int i = 0; i < 4; i++) {
6886 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
6887 }
6888
6889 // Detach the global and turn on access check.
6890 context->DetachGlobal();
6891 context->Global()->TurnOnAccessCheck();
6892
6893 // Failing access check to property get results in undefined.
6894 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
6895 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
6896
6897 // Failing access check to function call results in exception.
6898 CHECK(g1->Call(global, 0, NULL).IsEmpty());
6899 CHECK(g2->Call(global, 0, NULL).IsEmpty());
6900
6901 // No failing access check when just returning a constant.
6902 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
6903}
6904
6905
6906// This test verifies that pre-compilation (aka preparsing) can be called
6907// without initializing the whole VM. Thus we cannot run this test in a
6908// multi-threaded setup.
6909TEST(PreCompile) {
6910 // TODO(155): This test would break without the initialization of V8. This is
6911 // a workaround for now to make this test not fail.
6912 v8::V8::Initialize();
6913 const char *script = "function foo(a) { return a+1; }";
Steve Blockd0582a62009-12-15 09:54:21 +00006914 v8::ScriptData *sd =
6915 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00006916 CHECK_NE(sd->Length(), 0);
6917 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00006918 CHECK(!sd->HasError());
6919 delete sd;
6920}
6921
6922
6923TEST(PreCompileWithError) {
6924 v8::V8::Initialize();
6925 const char *script = "function foo(a) { return 1 * * 2; }";
6926 v8::ScriptData *sd =
6927 v8::ScriptData::PreCompile(script, i::StrLength(script));
6928 CHECK(sd->HasError());
6929 delete sd;
6930}
6931
6932
6933TEST(Regress31661) {
6934 v8::V8::Initialize();
6935 const char *script = " The Definintive Guide";
6936 v8::ScriptData *sd =
6937 v8::ScriptData::PreCompile(script, i::StrLength(script));
6938 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00006939 delete sd;
6940}
6941
6942
6943// This tests that we do not allow dictionary load/call inline caches
6944// to use functions that have not yet been compiled. The potential
6945// problem of loading a function that has not yet been compiled can
6946// arise because we share code between contexts via the compilation
6947// cache.
6948THREADED_TEST(DictionaryICLoadedFunction) {
6949 v8::HandleScope scope;
6950 // Test LoadIC.
6951 for (int i = 0; i < 2; i++) {
6952 LocalContext context;
6953 context->Global()->Set(v8_str("tmp"), v8::True());
6954 context->Global()->Delete(v8_str("tmp"));
6955 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
6956 }
6957 // Test CallIC.
6958 for (int i = 0; i < 2; i++) {
6959 LocalContext context;
6960 context->Global()->Set(v8_str("tmp"), v8::True());
6961 context->Global()->Delete(v8_str("tmp"));
6962 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
6963 }
6964}
6965
6966
6967// Test that cross-context new calls use the context of the callee to
6968// create the new JavaScript object.
6969THREADED_TEST(CrossContextNew) {
6970 v8::HandleScope scope;
6971 v8::Persistent<Context> context0 = Context::New();
6972 v8::Persistent<Context> context1 = Context::New();
6973
6974 // Allow cross-domain access.
6975 Local<String> token = v8_str("<security token>");
6976 context0->SetSecurityToken(token);
6977 context1->SetSecurityToken(token);
6978
6979 // Set an 'x' property on the Object prototype and define a
6980 // constructor function in context0.
6981 context0->Enter();
6982 CompileRun("Object.prototype.x = 42; function C() {};");
6983 context0->Exit();
6984
6985 // Call the constructor function from context0 and check that the
6986 // result has the 'x' property.
6987 context1->Enter();
6988 context1->Global()->Set(v8_str("other"), context0->Global());
6989 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
6990 CHECK(value->IsInt32());
6991 CHECK_EQ(42, value->Int32Value());
6992 context1->Exit();
6993
6994 // Dispose the contexts to allow them to be garbage collected.
6995 context0.Dispose();
6996 context1.Dispose();
6997}
6998
6999
7000class RegExpInterruptTest {
7001 public:
7002 RegExpInterruptTest() : block_(NULL) {}
7003 ~RegExpInterruptTest() { delete block_; }
7004 void RunTest() {
7005 block_ = i::OS::CreateSemaphore(0);
7006 gc_count_ = 0;
7007 gc_during_regexp_ = 0;
7008 regexp_success_ = false;
7009 gc_success_ = false;
7010 GCThread gc_thread(this);
7011 gc_thread.Start();
7012 v8::Locker::StartPreemption(1);
7013
7014 LongRunningRegExp();
7015 {
7016 v8::Unlocker unlock;
7017 gc_thread.Join();
7018 }
7019 v8::Locker::StopPreemption();
7020 CHECK(regexp_success_);
7021 CHECK(gc_success_);
7022 }
7023 private:
7024 // Number of garbage collections required.
7025 static const int kRequiredGCs = 5;
7026
7027 class GCThread : public i::Thread {
7028 public:
7029 explicit GCThread(RegExpInterruptTest* test)
7030 : test_(test) {}
7031 virtual void Run() {
7032 test_->CollectGarbage();
7033 }
7034 private:
7035 RegExpInterruptTest* test_;
7036 };
7037
7038 void CollectGarbage() {
7039 block_->Wait();
7040 while (gc_during_regexp_ < kRequiredGCs) {
7041 {
7042 v8::Locker lock;
7043 // TODO(lrn): Perhaps create some garbage before collecting.
7044 i::Heap::CollectAllGarbage(false);
7045 gc_count_++;
7046 }
7047 i::OS::Sleep(1);
7048 }
7049 gc_success_ = true;
7050 }
7051
7052 void LongRunningRegExp() {
7053 block_->Signal(); // Enable garbage collection thread on next preemption.
7054 int rounds = 0;
7055 while (gc_during_regexp_ < kRequiredGCs) {
7056 int gc_before = gc_count_;
7057 {
7058 // Match 15-30 "a"'s against 14 and a "b".
7059 const char* c_source =
7060 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
7061 ".exec('aaaaaaaaaaaaaaab') === null";
7062 Local<String> source = String::New(c_source);
7063 Local<Script> script = Script::Compile(source);
7064 Local<Value> result = script->Run();
7065 if (!result->BooleanValue()) {
7066 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
7067 return;
7068 }
7069 }
7070 {
7071 // Match 15-30 "a"'s against 15 and a "b".
7072 const char* c_source =
7073 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
7074 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
7075 Local<String> source = String::New(c_source);
7076 Local<Script> script = Script::Compile(source);
7077 Local<Value> result = script->Run();
7078 if (!result->BooleanValue()) {
7079 gc_during_regexp_ = kRequiredGCs;
7080 return;
7081 }
7082 }
7083 int gc_after = gc_count_;
7084 gc_during_regexp_ += gc_after - gc_before;
7085 rounds++;
7086 i::OS::Sleep(1);
7087 }
7088 regexp_success_ = true;
7089 }
7090
7091 i::Semaphore* block_;
7092 int gc_count_;
7093 int gc_during_regexp_;
7094 bool regexp_success_;
7095 bool gc_success_;
7096};
7097
7098
7099// Test that a regular expression execution can be interrupted and
7100// survive a garbage collection.
7101TEST(RegExpInterruption) {
7102 v8::Locker lock;
7103 v8::V8::Initialize();
7104 v8::HandleScope scope;
7105 Local<Context> local_env;
7106 {
7107 LocalContext env;
7108 local_env = env.local();
7109 }
7110
7111 // Local context should still be live.
7112 CHECK(!local_env.IsEmpty());
7113 local_env->Enter();
7114
7115 // Should complete without problems.
7116 RegExpInterruptTest().RunTest();
7117
7118 local_env->Exit();
7119}
7120
7121
7122class ApplyInterruptTest {
7123 public:
7124 ApplyInterruptTest() : block_(NULL) {}
7125 ~ApplyInterruptTest() { delete block_; }
7126 void RunTest() {
7127 block_ = i::OS::CreateSemaphore(0);
7128 gc_count_ = 0;
7129 gc_during_apply_ = 0;
7130 apply_success_ = false;
7131 gc_success_ = false;
7132 GCThread gc_thread(this);
7133 gc_thread.Start();
7134 v8::Locker::StartPreemption(1);
7135
7136 LongRunningApply();
7137 {
7138 v8::Unlocker unlock;
7139 gc_thread.Join();
7140 }
7141 v8::Locker::StopPreemption();
7142 CHECK(apply_success_);
7143 CHECK(gc_success_);
7144 }
7145 private:
7146 // Number of garbage collections required.
7147 static const int kRequiredGCs = 2;
7148
7149 class GCThread : public i::Thread {
7150 public:
7151 explicit GCThread(ApplyInterruptTest* test)
7152 : test_(test) {}
7153 virtual void Run() {
7154 test_->CollectGarbage();
7155 }
7156 private:
7157 ApplyInterruptTest* test_;
7158 };
7159
7160 void CollectGarbage() {
7161 block_->Wait();
7162 while (gc_during_apply_ < kRequiredGCs) {
7163 {
7164 v8::Locker lock;
7165 i::Heap::CollectAllGarbage(false);
7166 gc_count_++;
7167 }
7168 i::OS::Sleep(1);
7169 }
7170 gc_success_ = true;
7171 }
7172
7173 void LongRunningApply() {
7174 block_->Signal();
7175 int rounds = 0;
7176 while (gc_during_apply_ < kRequiredGCs) {
7177 int gc_before = gc_count_;
7178 {
7179 const char* c_source =
7180 "function do_very_little(bar) {"
7181 " this.foo = bar;"
7182 "}"
7183 "for (var i = 0; i < 100000; i++) {"
7184 " do_very_little.apply(this, ['bar']);"
7185 "}";
7186 Local<String> source = String::New(c_source);
7187 Local<Script> script = Script::Compile(source);
7188 Local<Value> result = script->Run();
7189 // Check that no exception was thrown.
7190 CHECK(!result.IsEmpty());
7191 }
7192 int gc_after = gc_count_;
7193 gc_during_apply_ += gc_after - gc_before;
7194 rounds++;
7195 }
7196 apply_success_ = true;
7197 }
7198
7199 i::Semaphore* block_;
7200 int gc_count_;
7201 int gc_during_apply_;
7202 bool apply_success_;
7203 bool gc_success_;
7204};
7205
7206
7207// Test that nothing bad happens if we get a preemption just when we were
7208// about to do an apply().
7209TEST(ApplyInterruption) {
7210 v8::Locker lock;
7211 v8::V8::Initialize();
7212 v8::HandleScope scope;
7213 Local<Context> local_env;
7214 {
7215 LocalContext env;
7216 local_env = env.local();
7217 }
7218
7219 // Local context should still be live.
7220 CHECK(!local_env.IsEmpty());
7221 local_env->Enter();
7222
7223 // Should complete without problems.
7224 ApplyInterruptTest().RunTest();
7225
7226 local_env->Exit();
7227}
7228
7229
7230// Verify that we can clone an object
7231TEST(ObjectClone) {
7232 v8::HandleScope scope;
7233 LocalContext env;
7234
7235 const char* sample =
7236 "var rv = {};" \
7237 "rv.alpha = 'hello';" \
7238 "rv.beta = 123;" \
7239 "rv;";
7240
7241 // Create an object, verify basics.
7242 Local<Value> val = CompileRun(sample);
7243 CHECK(val->IsObject());
7244 Local<v8::Object> obj = Local<v8::Object>::Cast(val);
7245 obj->Set(v8_str("gamma"), v8_str("cloneme"));
7246
7247 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
7248 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
7249 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
7250
7251 // Clone it.
7252 Local<v8::Object> clone = obj->Clone();
7253 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
7254 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
7255 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
7256
7257 // Set a property on the clone, verify each object.
7258 clone->Set(v8_str("beta"), v8::Integer::New(456));
7259 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
7260 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
7261}
7262
7263
7264class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
7265 public:
7266 explicit AsciiVectorResource(i::Vector<const char> vector)
7267 : data_(vector) {}
7268 virtual ~AsciiVectorResource() {}
7269 virtual size_t length() const { return data_.length(); }
7270 virtual const char* data() const { return data_.start(); }
7271 private:
7272 i::Vector<const char> data_;
7273};
7274
7275
7276class UC16VectorResource : public v8::String::ExternalStringResource {
7277 public:
7278 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
7279 : data_(vector) {}
7280 virtual ~UC16VectorResource() {}
7281 virtual size_t length() const { return data_.length(); }
7282 virtual const i::uc16* data() const { return data_.start(); }
7283 private:
7284 i::Vector<const i::uc16> data_;
7285};
7286
7287
7288static void MorphAString(i::String* string,
7289 AsciiVectorResource* ascii_resource,
7290 UC16VectorResource* uc16_resource) {
7291 CHECK(i::StringShape(string).IsExternal());
7292 if (string->IsAsciiRepresentation()) {
7293 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00007294 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00007295 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00007296 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00007297 i::ExternalTwoByteString* morphed =
7298 i::ExternalTwoByteString::cast(string);
7299 morphed->set_resource(uc16_resource);
7300 } else {
7301 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00007302 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00007303 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00007304 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00007305 i::ExternalAsciiString* morphed =
7306 i::ExternalAsciiString::cast(string);
7307 morphed->set_resource(ascii_resource);
7308 }
7309}
7310
7311
7312// Test that we can still flatten a string if the components it is built up
7313// from have been turned into 16 bit strings in the mean time.
7314THREADED_TEST(MorphCompositeStringTest) {
7315 const char* c_string = "Now is the time for all good men"
7316 " to come to the aid of the party";
7317 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
7318 {
7319 v8::HandleScope scope;
7320 LocalContext env;
7321 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00007322 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00007323 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00007324 i::Vector<const uint16_t>(two_byte_string,
7325 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00007326
7327 Local<String> lhs(v8::Utils::ToLocal(
7328 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
7329 Local<String> rhs(v8::Utils::ToLocal(
7330 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
7331
7332 env->Global()->Set(v8_str("lhs"), lhs);
7333 env->Global()->Set(v8_str("rhs"), rhs);
7334
7335 CompileRun(
7336 "var cons = lhs + rhs;"
7337 "var slice = lhs.substring(1, lhs.length - 1);"
7338 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
7339
7340 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
7341 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
7342
7343 // Now do some stuff to make sure the strings are flattened, etc.
7344 CompileRun(
7345 "/[^a-z]/.test(cons);"
7346 "/[^a-z]/.test(slice);"
7347 "/[^a-z]/.test(slice_on_cons);");
7348 const char* expected_cons =
7349 "Now is the time for all good men to come to the aid of the party"
7350 "Now is the time for all good men to come to the aid of the party";
7351 const char* expected_slice =
7352 "ow is the time for all good men to come to the aid of the part";
7353 const char* expected_slice_on_cons =
7354 "ow is the time for all good men to come to the aid of the party"
7355 "Now is the time for all good men to come to the aid of the part";
7356 CHECK_EQ(String::New(expected_cons),
7357 env->Global()->Get(v8_str("cons")));
7358 CHECK_EQ(String::New(expected_slice),
7359 env->Global()->Get(v8_str("slice")));
7360 CHECK_EQ(String::New(expected_slice_on_cons),
7361 env->Global()->Get(v8_str("slice_on_cons")));
7362 }
7363}
7364
7365
7366TEST(CompileExternalTwoByteSource) {
7367 v8::HandleScope scope;
7368 LocalContext context;
7369
7370 // This is a very short list of sources, which currently is to check for a
7371 // regression caused by r2703.
7372 const char* ascii_sources[] = {
7373 "0.5",
7374 "-0.5", // This mainly testes PushBack in the Scanner.
7375 "--0.5", // This mainly testes PushBack in the Scanner.
7376 NULL
7377 };
7378
7379 // Compile the sources as external two byte strings.
7380 for (int i = 0; ascii_sources[i] != NULL; i++) {
7381 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
7382 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00007383 i::Vector<const uint16_t>(two_byte_string,
7384 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00007385 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
7386 v8::Script::Compile(source);
7387 }
7388}
7389
7390
7391class RegExpStringModificationTest {
7392 public:
7393 RegExpStringModificationTest()
7394 : block_(i::OS::CreateSemaphore(0)),
7395 morphs_(0),
7396 morphs_during_regexp_(0),
7397 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
7398 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
7399 ~RegExpStringModificationTest() { delete block_; }
7400 void RunTest() {
7401 regexp_success_ = false;
7402 morph_success_ = false;
7403
7404 // Initialize the contents of two_byte_content_ to be a uc16 representation
7405 // of "aaaaaaaaaaaaaab".
7406 for (int i = 0; i < 14; i++) {
7407 two_byte_content_[i] = 'a';
7408 }
7409 two_byte_content_[14] = 'b';
7410
7411 // Create the input string for the regexp - the one we are going to change
7412 // properties of.
7413 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
7414
7415 // Inject the input as a global variable.
7416 i::Handle<i::String> input_name =
7417 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
7418 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
7419
7420
7421 MorphThread morph_thread(this);
7422 morph_thread.Start();
7423 v8::Locker::StartPreemption(1);
7424 LongRunningRegExp();
7425 {
7426 v8::Unlocker unlock;
7427 morph_thread.Join();
7428 }
7429 v8::Locker::StopPreemption();
7430 CHECK(regexp_success_);
7431 CHECK(morph_success_);
7432 }
7433 private:
7434
7435 // Number of string modifications required.
7436 static const int kRequiredModifications = 5;
7437 static const int kMaxModifications = 100;
7438
7439 class MorphThread : public i::Thread {
7440 public:
7441 explicit MorphThread(RegExpStringModificationTest* test)
7442 : test_(test) {}
7443 virtual void Run() {
7444 test_->MorphString();
7445 }
7446 private:
7447 RegExpStringModificationTest* test_;
7448 };
7449
7450 void MorphString() {
7451 block_->Wait();
7452 while (morphs_during_regexp_ < kRequiredModifications &&
7453 morphs_ < kMaxModifications) {
7454 {
7455 v8::Locker lock;
7456 // Swap string between ascii and two-byte representation.
7457 i::String* string = *input_;
7458 MorphAString(string, &ascii_resource_, &uc16_resource_);
7459 morphs_++;
7460 }
7461 i::OS::Sleep(1);
7462 }
7463 morph_success_ = true;
7464 }
7465
7466 void LongRunningRegExp() {
7467 block_->Signal(); // Enable morphing thread on next preemption.
7468 while (morphs_during_regexp_ < kRequiredModifications &&
7469 morphs_ < kMaxModifications) {
7470 int morphs_before = morphs_;
7471 {
7472 // Match 15-30 "a"'s against 14 and a "b".
7473 const char* c_source =
7474 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
7475 ".exec(input) === null";
7476 Local<String> source = String::New(c_source);
7477 Local<Script> script = Script::Compile(source);
7478 Local<Value> result = script->Run();
7479 CHECK(result->IsTrue());
7480 }
7481 int morphs_after = morphs_;
7482 morphs_during_regexp_ += morphs_after - morphs_before;
7483 }
7484 regexp_success_ = true;
7485 }
7486
7487 i::uc16 two_byte_content_[15];
7488 i::Semaphore* block_;
7489 int morphs_;
7490 int morphs_during_regexp_;
7491 bool regexp_success_;
7492 bool morph_success_;
7493 i::Handle<i::String> input_;
7494 AsciiVectorResource ascii_resource_;
7495 UC16VectorResource uc16_resource_;
7496};
7497
7498
7499// Test that a regular expression execution can be interrupted and
7500// the string changed without failing.
7501TEST(RegExpStringModification) {
7502 v8::Locker lock;
7503 v8::V8::Initialize();
7504 v8::HandleScope scope;
7505 Local<Context> local_env;
7506 {
7507 LocalContext env;
7508 local_env = env.local();
7509 }
7510
7511 // Local context should still be live.
7512 CHECK(!local_env.IsEmpty());
7513 local_env->Enter();
7514
7515 // Should complete without problems.
7516 RegExpStringModificationTest().RunTest();
7517
7518 local_env->Exit();
7519}
7520
7521
7522// Test that we can set a property on the global object even if there
7523// is a read-only property in the prototype chain.
7524TEST(ReadOnlyPropertyInGlobalProto) {
7525 v8::HandleScope scope;
7526 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7527 LocalContext context(0, templ);
7528 v8::Handle<v8::Object> global = context->Global();
7529 v8::Handle<v8::Object> global_proto =
7530 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
7531 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
7532 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
7533 // Check without 'eval' or 'with'.
7534 v8::Handle<v8::Value> res =
7535 CompileRun("function f() { x = 42; return x; }; f()");
7536 // Check with 'eval'.
7537 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
7538 CHECK_EQ(v8::Integer::New(42), res);
7539 // Check with 'with'.
7540 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
7541 CHECK_EQ(v8::Integer::New(42), res);
7542}
7543
7544static int force_set_set_count = 0;
7545static int force_set_get_count = 0;
7546bool pass_on_get = false;
7547
7548static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
7549 const v8::AccessorInfo& info) {
7550 force_set_get_count++;
7551 if (pass_on_get) {
7552 return v8::Handle<v8::Value>();
7553 } else {
7554 return v8::Int32::New(3);
7555 }
7556}
7557
7558static void ForceSetSetter(v8::Local<v8::String> name,
7559 v8::Local<v8::Value> value,
7560 const v8::AccessorInfo& info) {
7561 force_set_set_count++;
7562}
7563
7564static v8::Handle<v8::Value> ForceSetInterceptSetter(
7565 v8::Local<v8::String> name,
7566 v8::Local<v8::Value> value,
7567 const v8::AccessorInfo& info) {
7568 force_set_set_count++;
7569 return v8::Undefined();
7570}
7571
7572TEST(ForceSet) {
7573 force_set_get_count = 0;
7574 force_set_set_count = 0;
7575 pass_on_get = false;
7576
7577 v8::HandleScope scope;
7578 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7579 v8::Handle<v8::String> access_property = v8::String::New("a");
7580 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
7581 LocalContext context(NULL, templ);
7582 v8::Handle<v8::Object> global = context->Global();
7583
7584 // Ordinary properties
7585 v8::Handle<v8::String> simple_property = v8::String::New("p");
7586 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
7587 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7588 // This should fail because the property is read-only
7589 global->Set(simple_property, v8::Int32::New(5));
7590 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7591 // This should succeed even though the property is read-only
7592 global->ForceSet(simple_property, v8::Int32::New(6));
7593 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
7594
7595 // Accessors
7596 CHECK_EQ(0, force_set_set_count);
7597 CHECK_EQ(0, force_set_get_count);
7598 CHECK_EQ(3, global->Get(access_property)->Int32Value());
7599 // CHECK_EQ the property shouldn't override it, just call the setter
7600 // which in this case does nothing.
7601 global->Set(access_property, v8::Int32::New(7));
7602 CHECK_EQ(3, global->Get(access_property)->Int32Value());
7603 CHECK_EQ(1, force_set_set_count);
7604 CHECK_EQ(2, force_set_get_count);
7605 // Forcing the property to be set should override the accessor without
7606 // calling it
7607 global->ForceSet(access_property, v8::Int32::New(8));
7608 CHECK_EQ(8, global->Get(access_property)->Int32Value());
7609 CHECK_EQ(1, force_set_set_count);
7610 CHECK_EQ(2, force_set_get_count);
7611}
7612
7613TEST(ForceSetWithInterceptor) {
7614 force_set_get_count = 0;
7615 force_set_set_count = 0;
7616 pass_on_get = false;
7617
7618 v8::HandleScope scope;
7619 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7620 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
7621 LocalContext context(NULL, templ);
7622 v8::Handle<v8::Object> global = context->Global();
7623
7624 v8::Handle<v8::String> some_property = v8::String::New("a");
7625 CHECK_EQ(0, force_set_set_count);
7626 CHECK_EQ(0, force_set_get_count);
7627 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7628 // Setting the property shouldn't override it, just call the setter
7629 // which in this case does nothing.
7630 global->Set(some_property, v8::Int32::New(7));
7631 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7632 CHECK_EQ(1, force_set_set_count);
7633 CHECK_EQ(2, force_set_get_count);
7634 // Getting the property when the interceptor returns an empty handle
7635 // should yield undefined, since the property isn't present on the
7636 // object itself yet.
7637 pass_on_get = true;
7638 CHECK(global->Get(some_property)->IsUndefined());
7639 CHECK_EQ(1, force_set_set_count);
7640 CHECK_EQ(3, force_set_get_count);
7641 // Forcing the property to be set should cause the value to be
7642 // set locally without calling the interceptor.
7643 global->ForceSet(some_property, v8::Int32::New(8));
7644 CHECK_EQ(8, global->Get(some_property)->Int32Value());
7645 CHECK_EQ(1, force_set_set_count);
7646 CHECK_EQ(4, force_set_get_count);
7647 // Reenabling the interceptor should cause it to take precedence over
7648 // the property
7649 pass_on_get = false;
7650 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7651 CHECK_EQ(1, force_set_set_count);
7652 CHECK_EQ(5, force_set_get_count);
7653 // The interceptor should also work for other properties
7654 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
7655 CHECK_EQ(1, force_set_set_count);
7656 CHECK_EQ(6, force_set_get_count);
7657}
7658
7659
7660THREADED_TEST(ForceDelete) {
7661 v8::HandleScope scope;
7662 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7663 LocalContext context(NULL, templ);
7664 v8::Handle<v8::Object> global = context->Global();
7665
7666 // Ordinary properties
7667 v8::Handle<v8::String> simple_property = v8::String::New("p");
7668 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
7669 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7670 // This should fail because the property is dont-delete.
7671 CHECK(!global->Delete(simple_property));
7672 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7673 // This should succeed even though the property is dont-delete.
7674 CHECK(global->ForceDelete(simple_property));
7675 CHECK(global->Get(simple_property)->IsUndefined());
7676}
7677
7678
7679static int force_delete_interceptor_count = 0;
7680static bool pass_on_delete = false;
7681
7682
7683static v8::Handle<v8::Boolean> ForceDeleteDeleter(
7684 v8::Local<v8::String> name,
7685 const v8::AccessorInfo& info) {
7686 force_delete_interceptor_count++;
7687 if (pass_on_delete) {
7688 return v8::Handle<v8::Boolean>();
7689 } else {
7690 return v8::True();
7691 }
7692}
7693
7694
7695THREADED_TEST(ForceDeleteWithInterceptor) {
7696 force_delete_interceptor_count = 0;
7697 pass_on_delete = false;
7698
7699 v8::HandleScope scope;
7700 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7701 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
7702 LocalContext context(NULL, templ);
7703 v8::Handle<v8::Object> global = context->Global();
7704
7705 v8::Handle<v8::String> some_property = v8::String::New("a");
7706 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
7707
7708 // Deleting a property should get intercepted and nothing should
7709 // happen.
7710 CHECK_EQ(0, force_delete_interceptor_count);
7711 CHECK(global->Delete(some_property));
7712 CHECK_EQ(1, force_delete_interceptor_count);
7713 CHECK_EQ(42, global->Get(some_property)->Int32Value());
7714 // Deleting the property when the interceptor returns an empty
7715 // handle should not delete the property since it is DontDelete.
7716 pass_on_delete = true;
7717 CHECK(!global->Delete(some_property));
7718 CHECK_EQ(2, force_delete_interceptor_count);
7719 CHECK_EQ(42, global->Get(some_property)->Int32Value());
7720 // Forcing the property to be deleted should delete the value
7721 // without calling the interceptor.
7722 CHECK(global->ForceDelete(some_property));
7723 CHECK(global->Get(some_property)->IsUndefined());
7724 CHECK_EQ(2, force_delete_interceptor_count);
7725}
7726
7727
7728// Make sure that forcing a delete invalidates any IC stubs, so we
7729// don't read the hole value.
7730THREADED_TEST(ForceDeleteIC) {
7731 v8::HandleScope scope;
7732 LocalContext context;
7733 // Create a DontDelete variable on the global object.
7734 CompileRun("this.__proto__ = { foo: 'horse' };"
7735 "var foo = 'fish';"
7736 "function f() { return foo.length; }");
7737 // Initialize the IC for foo in f.
7738 CompileRun("for (var i = 0; i < 4; i++) f();");
7739 // Make sure the value of foo is correct before the deletion.
7740 CHECK_EQ(4, CompileRun("f()")->Int32Value());
7741 // Force the deletion of foo.
7742 CHECK(context->Global()->ForceDelete(v8_str("foo")));
7743 // Make sure the value for foo is read from the prototype, and that
7744 // we don't get in trouble with reading the deleted cell value
7745 // sentinel.
7746 CHECK_EQ(5, CompileRun("f()")->Int32Value());
7747}
7748
7749
7750v8::Persistent<Context> calling_context0;
7751v8::Persistent<Context> calling_context1;
7752v8::Persistent<Context> calling_context2;
7753
7754
7755// Check that the call to the callback is initiated in
7756// calling_context2, the directly calling context is calling_context1
7757// and the callback itself is in calling_context0.
7758static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
7759 ApiTestFuzzer::Fuzz();
7760 CHECK(Context::GetCurrent() == calling_context0);
7761 CHECK(Context::GetCalling() == calling_context1);
7762 CHECK(Context::GetEntered() == calling_context2);
7763 return v8::Integer::New(42);
7764}
7765
7766
7767THREADED_TEST(GetCallingContext) {
7768 v8::HandleScope scope;
7769
7770 calling_context0 = Context::New();
7771 calling_context1 = Context::New();
7772 calling_context2 = Context::New();
7773
7774 // Allow cross-domain access.
7775 Local<String> token = v8_str("<security token>");
7776 calling_context0->SetSecurityToken(token);
7777 calling_context1->SetSecurityToken(token);
7778 calling_context2->SetSecurityToken(token);
7779
7780 // Create an object with a C++ callback in context0.
7781 calling_context0->Enter();
7782 Local<v8::FunctionTemplate> callback_templ =
7783 v8::FunctionTemplate::New(GetCallingContextCallback);
7784 calling_context0->Global()->Set(v8_str("callback"),
7785 callback_templ->GetFunction());
7786 calling_context0->Exit();
7787
7788 // Expose context0 in context1 and setup a function that calls the
7789 // callback function.
7790 calling_context1->Enter();
7791 calling_context1->Global()->Set(v8_str("context0"),
7792 calling_context0->Global());
7793 CompileRun("function f() { context0.callback() }");
7794 calling_context1->Exit();
7795
7796 // Expose context1 in context2 and call the callback function in
7797 // context0 indirectly through f in context1.
7798 calling_context2->Enter();
7799 calling_context2->Global()->Set(v8_str("context1"),
7800 calling_context1->Global());
7801 CompileRun("context1.f()");
7802 calling_context2->Exit();
7803
7804 // Dispose the contexts to allow them to be garbage collected.
7805 calling_context0.Dispose();
7806 calling_context1.Dispose();
7807 calling_context2.Dispose();
7808 calling_context0.Clear();
7809 calling_context1.Clear();
7810 calling_context2.Clear();
7811}
7812
7813
7814// Check that a variable declaration with no explicit initialization
7815// value does not shadow an existing property in the prototype chain.
7816//
7817// This is consistent with Firefox and Safari.
7818//
7819// See http://crbug.com/12548.
7820THREADED_TEST(InitGlobalVarInProtoChain) {
7821 v8::HandleScope scope;
7822 LocalContext context;
7823 // Introduce a variable in the prototype chain.
7824 CompileRun("__proto__.x = 42");
7825 v8::Handle<v8::Value> result = CompileRun("var x; x");
7826 CHECK(!result->IsUndefined());
7827 CHECK_EQ(42, result->Int32Value());
7828}
7829
7830
7831// Regression test for issue 398.
7832// If a function is added to an object, creating a constant function
7833// field, and the result is cloned, replacing the constant function on the
7834// original should not affect the clone.
7835// See http://code.google.com/p/v8/issues/detail?id=398
7836THREADED_TEST(ReplaceConstantFunction) {
7837 v8::HandleScope scope;
7838 LocalContext context;
7839 v8::Handle<v8::Object> obj = v8::Object::New();
7840 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
7841 v8::Handle<v8::String> foo_string = v8::String::New("foo");
7842 obj->Set(foo_string, func_templ->GetFunction());
7843 v8::Handle<v8::Object> obj_clone = obj->Clone();
7844 obj_clone->Set(foo_string, v8::String::New("Hello"));
7845 CHECK(!obj->Get(foo_string)->IsUndefined());
7846}
7847
7848
7849// Regression test for http://crbug.com/16276.
7850THREADED_TEST(Regress16276) {
7851 v8::HandleScope scope;
7852 LocalContext context;
7853 // Force the IC in f to be a dictionary load IC.
7854 CompileRun("function f(obj) { return obj.x; }\n"
7855 "var obj = { x: { foo: 42 }, y: 87 };\n"
7856 "var x = obj.x;\n"
7857 "delete obj.y;\n"
7858 "for (var i = 0; i < 5; i++) f(obj);");
7859 // Detach the global object to make 'this' refer directly to the
7860 // global object (not the proxy), and make sure that the dictionary
7861 // load IC doesn't mess up loading directly from the global object.
7862 context->DetachGlobal();
7863 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
7864}
7865
7866
7867THREADED_TEST(PixelArray) {
7868 v8::HandleScope scope;
7869 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00007870 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00007871 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
7872 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
7873 pixel_data);
7874 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
7875 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00007876 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00007877 }
7878 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
7879 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00007880 CHECK_EQ(i % 256, pixels->get(i));
7881 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00007882 }
7883
7884 v8::Handle<v8::Object> obj = v8::Object::New();
7885 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
7886 // Set the elements to be the pixels.
7887 // jsobj->set_elements(*pixels);
7888 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
7889 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
7890 obj->Set(v8_str("field"), v8::Int32::New(1503));
7891 context->Global()->Set(v8_str("pixels"), obj);
7892 v8::Handle<v8::Value> result = CompileRun("pixels.field");
7893 CHECK_EQ(1503, result->Int32Value());
7894 result = CompileRun("pixels[1]");
7895 CHECK_EQ(1, result->Int32Value());
7896
7897 result = CompileRun("var sum = 0;"
7898 "for (var i = 0; i < 8; i++) {"
7899 " sum += pixels[i] = pixels[i] = -i;"
7900 "}"
7901 "sum;");
7902 CHECK_EQ(-28, result->Int32Value());
7903
7904 result = CompileRun("var sum = 0;"
7905 "for (var i = 0; i < 8; i++) {"
7906 " sum += pixels[i] = pixels[i] = 0;"
7907 "}"
7908 "sum;");
7909 CHECK_EQ(0, result->Int32Value());
7910
7911 result = CompileRun("var sum = 0;"
7912 "for (var i = 0; i < 8; i++) {"
7913 " sum += pixels[i] = pixels[i] = 255;"
7914 "}"
7915 "sum;");
7916 CHECK_EQ(8 * 255, result->Int32Value());
7917
7918 result = CompileRun("var sum = 0;"
7919 "for (var i = 0; i < 8; i++) {"
7920 " sum += pixels[i] = pixels[i] = 256 + i;"
7921 "}"
7922 "sum;");
7923 CHECK_EQ(2076, result->Int32Value());
7924
7925 result = CompileRun("var sum = 0;"
7926 "for (var i = 0; i < 8; i++) {"
7927 " sum += pixels[i] = pixels[i] = i;"
7928 "}"
7929 "sum;");
7930 CHECK_EQ(28, result->Int32Value());
7931
7932 result = CompileRun("var sum = 0;"
7933 "for (var i = 0; i < 8; i++) {"
7934 " sum += pixels[i];"
7935 "}"
7936 "sum;");
7937 CHECK_EQ(28, result->Int32Value());
7938
7939 i::Handle<i::Smi> value(i::Smi::FromInt(2));
7940 i::SetElement(jsobj, 1, value);
7941 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
7942 *value.location() = i::Smi::FromInt(256);
7943 i::SetElement(jsobj, 1, value);
7944 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
7945 *value.location() = i::Smi::FromInt(-1);
7946 i::SetElement(jsobj, 1, value);
7947 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
7948
7949 result = CompileRun("for (var i = 0; i < 8; i++) {"
7950 " pixels[i] = (i * 65) - 109;"
7951 "}"
7952 "pixels[1] + pixels[6];");
7953 CHECK_EQ(255, result->Int32Value());
7954 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
7955 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
7956 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
7957 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
7958 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
7959 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
7960 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
7961 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
7962 result = CompileRun("var sum = 0;"
7963 "for (var i = 0; i < 8; i++) {"
7964 " sum += pixels[i];"
7965 "}"
7966 "sum;");
7967 CHECK_EQ(984, result->Int32Value());
7968
7969 result = CompileRun("for (var i = 0; i < 8; i++) {"
7970 " pixels[i] = (i * 1.1);"
7971 "}"
7972 "pixels[1] + pixels[6];");
7973 CHECK_EQ(8, result->Int32Value());
7974 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
7975 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
7976 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
7977 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
7978 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
7979 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
7980 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
7981 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
7982
7983 result = CompileRun("for (var i = 0; i < 8; i++) {"
7984 " pixels[7] = undefined;"
7985 "}"
7986 "pixels[7];");
7987 CHECK_EQ(0, result->Int32Value());
7988 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
7989
7990 result = CompileRun("for (var i = 0; i < 8; i++) {"
7991 " pixels[6] = '2.3';"
7992 "}"
7993 "pixels[6];");
7994 CHECK_EQ(2, result->Int32Value());
7995 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
7996
7997 result = CompileRun("for (var i = 0; i < 8; i++) {"
7998 " pixels[5] = NaN;"
7999 "}"
8000 "pixels[5];");
8001 CHECK_EQ(0, result->Int32Value());
8002 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
8003
8004 result = CompileRun("for (var i = 0; i < 8; i++) {"
8005 " pixels[8] = Infinity;"
8006 "}"
8007 "pixels[8];");
8008 CHECK_EQ(255, result->Int32Value());
8009 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
8010
8011 result = CompileRun("for (var i = 0; i < 8; i++) {"
8012 " pixels[9] = -Infinity;"
8013 "}"
8014 "pixels[9];");
8015 CHECK_EQ(0, result->Int32Value());
8016 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
8017
8018 result = CompileRun("pixels[3] = 33;"
8019 "delete pixels[3];"
8020 "pixels[3];");
8021 CHECK_EQ(33, result->Int32Value());
8022
8023 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
8024 "pixels[2] = 12; pixels[3] = 13;"
8025 "pixels.__defineGetter__('2',"
8026 "function() { return 120; });"
8027 "pixels[2];");
8028 CHECK_EQ(12, result->Int32Value());
8029
8030 result = CompileRun("var js_array = new Array(40);"
8031 "js_array[0] = 77;"
8032 "js_array;");
8033 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
8034
8035 result = CompileRun("pixels[1] = 23;"
8036 "pixels.__proto__ = [];"
8037 "js_array.__proto__ = pixels;"
8038 "js_array.concat(pixels);");
8039 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
8040 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
8041
8042 result = CompileRun("pixels[1] = 23;");
8043 CHECK_EQ(23, result->Int32Value());
8044
Steve Blockd0582a62009-12-15 09:54:21 +00008045 // Test for index greater than 255. Regression test for:
8046 // http://code.google.com/p/chromium/issues/detail?id=26337.
8047 result = CompileRun("pixels[256] = 255;");
8048 CHECK_EQ(255, result->Int32Value());
8049 result = CompileRun("var i = 0;"
8050 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
8051 "i");
8052 CHECK_EQ(255, result->Int32Value());
8053
Steve Blocka7e24c12009-10-30 11:49:00 +00008054 free(pixel_data);
8055}
8056
8057
Steve Block3ce2e202009-11-05 08:53:23 +00008058template <class ExternalArrayClass, class ElementType>
8059static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
8060 int64_t low,
8061 int64_t high) {
8062 v8::HandleScope scope;
8063 LocalContext context;
8064 const int kElementCount = 40;
8065 int element_size = 0;
8066 switch (array_type) {
8067 case v8::kExternalByteArray:
8068 case v8::kExternalUnsignedByteArray:
8069 element_size = 1;
8070 break;
8071 case v8::kExternalShortArray:
8072 case v8::kExternalUnsignedShortArray:
8073 element_size = 2;
8074 break;
8075 case v8::kExternalIntArray:
8076 case v8::kExternalUnsignedIntArray:
8077 case v8::kExternalFloatArray:
8078 element_size = 4;
8079 break;
8080 default:
8081 UNREACHABLE();
8082 break;
8083 }
8084 ElementType* array_data =
8085 static_cast<ElementType*>(malloc(kElementCount * element_size));
8086 i::Handle<ExternalArrayClass> array =
8087 i::Handle<ExternalArrayClass>::cast(
8088 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
8089 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
8090 for (int i = 0; i < kElementCount; i++) {
8091 array->set(i, static_cast<ElementType>(i));
8092 }
8093 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
8094 for (int i = 0; i < kElementCount; i++) {
8095 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
8096 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
8097 }
8098
8099 v8::Handle<v8::Object> obj = v8::Object::New();
8100 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
8101 // Set the elements to be the external array.
8102 obj->SetIndexedPropertiesToExternalArrayData(array_data,
8103 array_type,
8104 kElementCount);
8105 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
8106 obj->Set(v8_str("field"), v8::Int32::New(1503));
8107 context->Global()->Set(v8_str("ext_array"), obj);
8108 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
8109 CHECK_EQ(1503, result->Int32Value());
8110 result = CompileRun("ext_array[1]");
8111 CHECK_EQ(1, result->Int32Value());
8112
8113 // Check pass through of assigned smis
8114 result = CompileRun("var sum = 0;"
8115 "for (var i = 0; i < 8; i++) {"
8116 " sum += ext_array[i] = ext_array[i] = -i;"
8117 "}"
8118 "sum;");
8119 CHECK_EQ(-28, result->Int32Value());
8120
8121 // Check assigned smis
8122 result = CompileRun("for (var i = 0; i < 8; i++) {"
8123 " ext_array[i] = i;"
8124 "}"
8125 "var sum = 0;"
8126 "for (var i = 0; i < 8; i++) {"
8127 " sum += ext_array[i];"
8128 "}"
8129 "sum;");
8130 CHECK_EQ(28, result->Int32Value());
8131
8132 // Check assigned smis in reverse order
8133 result = CompileRun("for (var i = 8; --i >= 0; ) {"
8134 " ext_array[i] = i;"
8135 "}"
8136 "var sum = 0;"
8137 "for (var i = 0; i < 8; i++) {"
8138 " sum += ext_array[i];"
8139 "}"
8140 "sum;");
8141 CHECK_EQ(28, result->Int32Value());
8142
8143 // Check pass through of assigned HeapNumbers
8144 result = CompileRun("var sum = 0;"
8145 "for (var i = 0; i < 16; i+=2) {"
8146 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
8147 "}"
8148 "sum;");
8149 CHECK_EQ(-28, result->Int32Value());
8150
8151 // Check assigned HeapNumbers
8152 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
8153 " ext_array[i] = (i * 0.5);"
8154 "}"
8155 "var sum = 0;"
8156 "for (var i = 0; i < 16; i+=2) {"
8157 " sum += ext_array[i];"
8158 "}"
8159 "sum;");
8160 CHECK_EQ(28, result->Int32Value());
8161
8162 // Check assigned HeapNumbers in reverse order
8163 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
8164 " ext_array[i] = (i * 0.5);"
8165 "}"
8166 "var sum = 0;"
8167 "for (var i = 0; i < 16; i+=2) {"
8168 " sum += ext_array[i];"
8169 "}"
8170 "sum;");
8171 CHECK_EQ(28, result->Int32Value());
8172
8173 i::ScopedVector<char> test_buf(1024);
8174
8175 // Check legal boundary conditions.
8176 // The repeated loads and stores ensure the ICs are exercised.
8177 const char* boundary_program =
8178 "var res = 0;"
8179 "for (var i = 0; i < 16; i++) {"
8180 " ext_array[i] = %lld;"
8181 " if (i > 8) {"
8182 " res = ext_array[i];"
8183 " }"
8184 "}"
8185 "res;";
8186 i::OS::SNPrintF(test_buf,
8187 boundary_program,
8188 low);
8189 result = CompileRun(test_buf.start());
8190 CHECK_EQ(low, result->IntegerValue());
8191
8192 i::OS::SNPrintF(test_buf,
8193 boundary_program,
8194 high);
8195 result = CompileRun(test_buf.start());
8196 CHECK_EQ(high, result->IntegerValue());
8197
8198 // Check misprediction of type in IC.
8199 result = CompileRun("var tmp_array = ext_array;"
8200 "var sum = 0;"
8201 "for (var i = 0; i < 8; i++) {"
8202 " tmp_array[i] = i;"
8203 " sum += tmp_array[i];"
8204 " if (i == 4) {"
8205 " tmp_array = {};"
8206 " }"
8207 "}"
8208 "sum;");
8209 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
8210 CHECK_EQ(28, result->Int32Value());
8211
8212 // Make sure out-of-range loads do not throw.
8213 i::OS::SNPrintF(test_buf,
8214 "var caught_exception = false;"
8215 "try {"
8216 " ext_array[%d];"
8217 "} catch (e) {"
8218 " caught_exception = true;"
8219 "}"
8220 "caught_exception;",
8221 kElementCount);
8222 result = CompileRun(test_buf.start());
8223 CHECK_EQ(false, result->BooleanValue());
8224
8225 // Make sure out-of-range stores do not throw.
8226 i::OS::SNPrintF(test_buf,
8227 "var caught_exception = false;"
8228 "try {"
8229 " ext_array[%d] = 1;"
8230 "} catch (e) {"
8231 " caught_exception = true;"
8232 "}"
8233 "caught_exception;",
8234 kElementCount);
8235 result = CompileRun(test_buf.start());
8236 CHECK_EQ(false, result->BooleanValue());
8237
8238 // Check other boundary conditions, values and operations.
8239 result = CompileRun("for (var i = 0; i < 8; i++) {"
8240 " ext_array[7] = undefined;"
8241 "}"
8242 "ext_array[7];");
8243 CHECK_EQ(0, result->Int32Value());
8244 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
8245
8246 result = CompileRun("for (var i = 0; i < 8; i++) {"
8247 " ext_array[6] = '2.3';"
8248 "}"
8249 "ext_array[6];");
8250 CHECK_EQ(2, result->Int32Value());
8251 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
8252
8253 if (array_type != v8::kExternalFloatArray) {
8254 // Though the specification doesn't state it, be explicit about
8255 // converting NaNs and +/-Infinity to zero.
8256 result = CompileRun("for (var i = 0; i < 8; i++) {"
8257 " ext_array[i] = 5;"
8258 "}"
8259 "for (var i = 0; i < 8; i++) {"
8260 " ext_array[i] = NaN;"
8261 "}"
8262 "ext_array[5];");
8263 CHECK_EQ(0, result->Int32Value());
8264 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
8265
8266 result = CompileRun("for (var i = 0; i < 8; i++) {"
8267 " ext_array[i] = 5;"
8268 "}"
8269 "for (var i = 0; i < 8; i++) {"
8270 " ext_array[i] = Infinity;"
8271 "}"
8272 "ext_array[5];");
8273 CHECK_EQ(0, result->Int32Value());
8274 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
8275
8276 result = CompileRun("for (var i = 0; i < 8; i++) {"
8277 " ext_array[i] = 5;"
8278 "}"
8279 "for (var i = 0; i < 8; i++) {"
8280 " ext_array[i] = -Infinity;"
8281 "}"
8282 "ext_array[5];");
8283 CHECK_EQ(0, result->Int32Value());
8284 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
8285 }
8286
8287 result = CompileRun("ext_array[3] = 33;"
8288 "delete ext_array[3];"
8289 "ext_array[3];");
8290 CHECK_EQ(33, result->Int32Value());
8291
8292 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
8293 "ext_array[2] = 12; ext_array[3] = 13;"
8294 "ext_array.__defineGetter__('2',"
8295 "function() { return 120; });"
8296 "ext_array[2];");
8297 CHECK_EQ(12, result->Int32Value());
8298
8299 result = CompileRun("var js_array = new Array(40);"
8300 "js_array[0] = 77;"
8301 "js_array;");
8302 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
8303
8304 result = CompileRun("ext_array[1] = 23;"
8305 "ext_array.__proto__ = [];"
8306 "js_array.__proto__ = ext_array;"
8307 "js_array.concat(ext_array);");
8308 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
8309 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
8310
8311 result = CompileRun("ext_array[1] = 23;");
8312 CHECK_EQ(23, result->Int32Value());
8313
Steve Blockd0582a62009-12-15 09:54:21 +00008314 // Test more complex manipulations which cause eax to contain values
8315 // that won't be completely overwritten by loads from the arrays.
8316 // This catches bugs in the instructions used for the KeyedLoadIC
8317 // for byte and word types.
8318 {
8319 const int kXSize = 300;
8320 const int kYSize = 300;
8321 const int kLargeElementCount = kXSize * kYSize * 4;
8322 ElementType* large_array_data =
8323 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
8324 i::Handle<ExternalArrayClass> large_array =
8325 i::Handle<ExternalArrayClass>::cast(
8326 i::Factory::NewExternalArray(kLargeElementCount,
8327 array_type,
8328 array_data));
8329 v8::Handle<v8::Object> large_obj = v8::Object::New();
8330 // Set the elements to be the external array.
8331 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
8332 array_type,
8333 kLargeElementCount);
8334 context->Global()->Set(v8_str("large_array"), large_obj);
8335 // Initialize contents of a few rows.
8336 for (int x = 0; x < 300; x++) {
8337 int row = 0;
8338 int offset = row * 300 * 4;
8339 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
8340 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
8341 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
8342 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
8343 row = 150;
8344 offset = row * 300 * 4;
8345 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
8346 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
8347 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
8348 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
8349 row = 298;
8350 offset = row * 300 * 4;
8351 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
8352 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
8353 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
8354 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
8355 }
8356 // The goal of the code below is to make "offset" large enough
8357 // that the computation of the index (which goes into eax) has
8358 // high bits set which will not be overwritten by a byte or short
8359 // load.
8360 result = CompileRun("var failed = false;"
8361 "var offset = 0;"
8362 "for (var i = 0; i < 300; i++) {"
8363 " if (large_array[4 * i] != 127 ||"
8364 " large_array[4 * i + 1] != 0 ||"
8365 " large_array[4 * i + 2] != 0 ||"
8366 " large_array[4 * i + 3] != 127) {"
8367 " failed = true;"
8368 " }"
8369 "}"
8370 "offset = 150 * 300 * 4;"
8371 "for (var i = 0; i < 300; i++) {"
8372 " if (large_array[offset + 4 * i] != 127 ||"
8373 " large_array[offset + 4 * i + 1] != 0 ||"
8374 " large_array[offset + 4 * i + 2] != 0 ||"
8375 " large_array[offset + 4 * i + 3] != 127) {"
8376 " failed = true;"
8377 " }"
8378 "}"
8379 "offset = 298 * 300 * 4;"
8380 "for (var i = 0; i < 300; i++) {"
8381 " if (large_array[offset + 4 * i] != 127 ||"
8382 " large_array[offset + 4 * i + 1] != 0 ||"
8383 " large_array[offset + 4 * i + 2] != 0 ||"
8384 " large_array[offset + 4 * i + 3] != 127) {"
8385 " failed = true;"
8386 " }"
8387 "}"
8388 "!failed;");
8389 CHECK_EQ(true, result->BooleanValue());
8390 free(large_array_data);
8391 }
8392
Steve Block3ce2e202009-11-05 08:53:23 +00008393 free(array_data);
8394}
8395
8396
8397THREADED_TEST(ExternalByteArray) {
8398 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>(
8399 v8::kExternalByteArray,
8400 -128,
8401 127);
8402}
8403
8404
8405THREADED_TEST(ExternalUnsignedByteArray) {
8406 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>(
8407 v8::kExternalUnsignedByteArray,
8408 0,
8409 255);
8410}
8411
8412
8413THREADED_TEST(ExternalShortArray) {
8414 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>(
8415 v8::kExternalShortArray,
8416 -32768,
8417 32767);
8418}
8419
8420
8421THREADED_TEST(ExternalUnsignedShortArray) {
8422 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>(
8423 v8::kExternalUnsignedShortArray,
8424 0,
8425 65535);
8426}
8427
8428
8429THREADED_TEST(ExternalIntArray) {
8430 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>(
8431 v8::kExternalIntArray,
8432 INT_MIN, // -2147483648
8433 INT_MAX); // 2147483647
8434}
8435
8436
8437THREADED_TEST(ExternalUnsignedIntArray) {
8438 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>(
8439 v8::kExternalUnsignedIntArray,
8440 0,
8441 UINT_MAX); // 4294967295
8442}
8443
8444
8445THREADED_TEST(ExternalFloatArray) {
8446 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>(
8447 v8::kExternalFloatArray,
8448 -500,
8449 500);
8450}
8451
8452
8453THREADED_TEST(ExternalArrays) {
8454 TestExternalByteArray();
8455 TestExternalUnsignedByteArray();
8456 TestExternalShortArray();
8457 TestExternalUnsignedShortArray();
8458 TestExternalIntArray();
8459 TestExternalUnsignedIntArray();
8460 TestExternalFloatArray();
8461}
8462
8463
Steve Blocka7e24c12009-10-30 11:49:00 +00008464THREADED_TEST(ScriptContextDependence) {
8465 v8::HandleScope scope;
8466 LocalContext c1;
8467 const char *source = "foo";
8468 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
8469 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
8470 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
8471 CHECK_EQ(dep->Run()->Int32Value(), 100);
8472 CHECK_EQ(indep->Run()->Int32Value(), 100);
8473 LocalContext c2;
8474 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
8475 CHECK_EQ(dep->Run()->Int32Value(), 100);
8476 CHECK_EQ(indep->Run()->Int32Value(), 101);
8477}
8478
8479
8480THREADED_TEST(StackTrace) {
8481 v8::HandleScope scope;
8482 LocalContext context;
8483 v8::TryCatch try_catch;
8484 const char *source = "function foo() { FAIL.FAIL; }; foo();";
8485 v8::Handle<v8::String> src = v8::String::New(source);
8486 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
8487 v8::Script::New(src, origin)->Run();
8488 CHECK(try_catch.HasCaught());
8489 v8::String::Utf8Value stack(try_catch.StackTrace());
8490 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
8491}
8492
8493
Steve Block3ce2e202009-11-05 08:53:23 +00008494// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +00008495THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +00008496 bool rv = false;
8497 for (int i = 0; i < 100; i++) {
8498 rv = v8::V8::IdleNotification();
8499 if (rv)
8500 break;
8501 }
8502 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +00008503}
8504
8505
8506static uint32_t* stack_limit;
8507
8508static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
8509 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
8510 return v8::Undefined();
8511}
8512
8513
8514// Uses the address of a local variable to determine the stack top now.
8515// Given a size, returns an address that is that far from the current
8516// top of stack.
8517static uint32_t* ComputeStackLimit(uint32_t size) {
8518 uint32_t* answer = &size - (size / sizeof(size));
8519 // If the size is very large and the stack is very near the bottom of
8520 // memory then the calculation above may wrap around and give an address
8521 // that is above the (downwards-growing) stack. In that case we return
8522 // a very low address.
8523 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
8524 return answer;
8525}
8526
8527
8528TEST(SetResourceConstraints) {
8529 static const int K = 1024;
8530 uint32_t* set_limit = ComputeStackLimit(128 * K);
8531
8532 // Set stack limit.
8533 v8::ResourceConstraints constraints;
8534 constraints.set_stack_limit(set_limit);
8535 CHECK(v8::SetResourceConstraints(&constraints));
8536
8537 // Execute a script.
8538 v8::HandleScope scope;
8539 LocalContext env;
8540 Local<v8::FunctionTemplate> fun_templ =
8541 v8::FunctionTemplate::New(GetStackLimitCallback);
8542 Local<Function> fun = fun_templ->GetFunction();
8543 env->Global()->Set(v8_str("get_stack_limit"), fun);
8544 CompileRun("get_stack_limit();");
8545
8546 CHECK(stack_limit == set_limit);
8547}
8548
8549
8550TEST(SetResourceConstraintsInThread) {
8551 uint32_t* set_limit;
8552 {
8553 v8::Locker locker;
8554 static const int K = 1024;
8555 set_limit = ComputeStackLimit(128 * K);
8556
8557 // Set stack limit.
8558 v8::ResourceConstraints constraints;
8559 constraints.set_stack_limit(set_limit);
8560 CHECK(v8::SetResourceConstraints(&constraints));
8561
8562 // Execute a script.
8563 v8::HandleScope scope;
8564 LocalContext env;
8565 Local<v8::FunctionTemplate> fun_templ =
8566 v8::FunctionTemplate::New(GetStackLimitCallback);
8567 Local<Function> fun = fun_templ->GetFunction();
8568 env->Global()->Set(v8_str("get_stack_limit"), fun);
8569 CompileRun("get_stack_limit();");
8570
8571 CHECK(stack_limit == set_limit);
8572 }
8573 {
8574 v8::Locker locker;
8575 CHECK(stack_limit == set_limit);
8576 }
8577}
Steve Block3ce2e202009-11-05 08:53:23 +00008578
8579
8580THREADED_TEST(GetHeapStatistics) {
8581 v8::HandleScope scope;
8582 LocalContext c1;
8583 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +00008584 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
8585 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +00008586 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +00008587 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
8588 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
8589}
8590
8591
8592static double DoubleFromBits(uint64_t value) {
8593 double target;
8594#ifdef BIG_ENDIAN_FLOATING_POINT
8595 const int kIntSize = 4;
8596 // Somebody swapped the lower and higher half of doubles.
8597 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
8598 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
8599#else
8600 memcpy(&target, &value, sizeof(target));
8601#endif
8602 return target;
8603}
8604
8605
8606static uint64_t DoubleToBits(double value) {
8607 uint64_t target;
8608#ifdef BIG_ENDIAN_FLOATING_POINT
8609 const int kIntSize = 4;
8610 // Somebody swapped the lower and higher half of doubles.
8611 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
8612 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
8613#else
8614 memcpy(&target, &value, sizeof(target));
8615#endif
8616 return target;
8617}
8618
8619
8620static double DoubleToDateTime(double input) {
8621 double date_limit = 864e13;
8622 if (IsNaN(input) || input < -date_limit || input > date_limit) {
8623 return i::OS::nan_value();
8624 }
8625 return (input < 0) ? -(floor(-input)) : floor(input);
8626}
8627
8628// We don't have a consistent way to write 64-bit constants syntactically, so we
8629// split them into two 32-bit constants and combine them programmatically.
8630static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
8631 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
8632}
8633
8634
8635THREADED_TEST(QuietSignalingNaNs) {
8636 v8::HandleScope scope;
8637 LocalContext context;
8638 v8::TryCatch try_catch;
8639
8640 // Special double values.
8641 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
8642 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
8643 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
8644 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
8645 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
8646 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
8647 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
8648
8649 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
8650 // on either side of the epoch.
8651 double date_limit = 864e13;
8652
8653 double test_values[] = {
8654 snan,
8655 qnan,
8656 infinity,
8657 max_normal,
8658 date_limit + 1,
8659 date_limit,
8660 min_normal,
8661 max_denormal,
8662 min_denormal,
8663 0,
8664 -0,
8665 -min_denormal,
8666 -max_denormal,
8667 -min_normal,
8668 -date_limit,
8669 -date_limit - 1,
8670 -max_normal,
8671 -infinity,
8672 -qnan,
8673 -snan
8674 };
8675 int num_test_values = 20;
8676
8677 for (int i = 0; i < num_test_values; i++) {
8678 double test_value = test_values[i];
8679
8680 // Check that Number::New preserves non-NaNs and quiets SNaNs.
8681 v8::Handle<v8::Value> number = v8::Number::New(test_value);
8682 double stored_number = number->NumberValue();
8683 if (!IsNaN(test_value)) {
8684 CHECK_EQ(test_value, stored_number);
8685 } else {
8686 uint64_t stored_bits = DoubleToBits(stored_number);
8687 // Check if quiet nan (bits 51..62 all set).
8688 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
8689 }
8690
8691 // Check that Date::New preserves non-NaNs in the date range and
8692 // quiets SNaNs.
8693 v8::Handle<v8::Value> date = v8::Date::New(test_value);
8694 double expected_stored_date = DoubleToDateTime(test_value);
8695 double stored_date = date->NumberValue();
8696 if (!IsNaN(expected_stored_date)) {
8697 CHECK_EQ(expected_stored_date, stored_date);
8698 } else {
8699 uint64_t stored_bits = DoubleToBits(stored_date);
8700 // Check if quiet nan (bits 51..62 all set).
8701 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
8702 }
8703 }
8704}
8705
8706
8707static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
8708 v8::HandleScope scope;
8709 v8::TryCatch tc;
8710 v8::Handle<v8::String> str = args[0]->ToString();
8711 if (tc.HasCaught())
8712 return tc.ReThrow();
8713 return v8::Undefined();
8714}
8715
8716
8717// Test that an exception can be propagated down through a spaghetti
8718// stack using ReThrow.
8719THREADED_TEST(SpaghettiStackReThrow) {
8720 v8::HandleScope scope;
8721 LocalContext context;
8722 context->Global()->Set(
8723 v8::String::New("s"),
8724 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
8725 v8::TryCatch try_catch;
8726 CompileRun(
8727 "var i = 0;"
8728 "var o = {"
8729 " toString: function () {"
8730 " if (i == 10) {"
8731 " throw 'Hey!';"
8732 " } else {"
8733 " i++;"
8734 " return s(o);"
8735 " }"
8736 " }"
8737 "};"
8738 "s(o);");
8739 CHECK(try_catch.HasCaught());
8740 v8::String::Utf8Value value(try_catch.Exception());
8741 CHECK_EQ(0, strcmp(*value, "Hey!"));
8742}
8743
8744
Steve Blockd0582a62009-12-15 09:54:21 +00008745TEST(Regress528) {
8746 v8::V8::Initialize();
8747
8748 v8::HandleScope scope;
8749 v8::Persistent<Context> context;
8750 v8::Persistent<Context> other_context;
8751 int gc_count;
8752
8753 // Create a context used to keep the code from aging in the compilation
8754 // cache.
8755 other_context = Context::New();
8756
8757 // Context-dependent context data creates reference from the compilation
8758 // cache to the global object.
8759 const char* source_simple = "1";
8760 context = Context::New();
8761 {
8762 v8::HandleScope scope;
8763
8764 context->Enter();
8765 Local<v8::String> obj = v8::String::New("");
8766 context->SetData(obj);
8767 CompileRun(source_simple);
8768 context->Exit();
8769 }
8770 context.Dispose();
8771 for (gc_count = 1; gc_count < 10; gc_count++) {
8772 other_context->Enter();
8773 CompileRun(source_simple);
8774 other_context->Exit();
8775 v8::internal::Heap::CollectAllGarbage(false);
8776 if (GetGlobalObjectsCount() == 1) break;
8777 }
8778 CHECK_GE(2, gc_count);
8779 CHECK_EQ(1, GetGlobalObjectsCount());
8780
8781 // Eval in a function creates reference from the compilation cache to the
8782 // global object.
8783 const char* source_eval = "function f(){eval('1')}; f()";
8784 context = Context::New();
8785 {
8786 v8::HandleScope scope;
8787
8788 context->Enter();
8789 CompileRun(source_eval);
8790 context->Exit();
8791 }
8792 context.Dispose();
8793 for (gc_count = 1; gc_count < 10; gc_count++) {
8794 other_context->Enter();
8795 CompileRun(source_eval);
8796 other_context->Exit();
8797 v8::internal::Heap::CollectAllGarbage(false);
8798 if (GetGlobalObjectsCount() == 1) break;
8799 }
8800 CHECK_GE(2, gc_count);
8801 CHECK_EQ(1, GetGlobalObjectsCount());
8802
8803 // Looking up the line number for an exception creates reference from the
8804 // compilation cache to the global object.
8805 const char* source_exception = "function f(){throw 1;} f()";
8806 context = Context::New();
8807 {
8808 v8::HandleScope scope;
8809
8810 context->Enter();
8811 v8::TryCatch try_catch;
8812 CompileRun(source_exception);
8813 CHECK(try_catch.HasCaught());
8814 v8::Handle<v8::Message> message = try_catch.Message();
8815 CHECK(!message.IsEmpty());
8816 CHECK_EQ(1, message->GetLineNumber());
8817 context->Exit();
8818 }
8819 context.Dispose();
8820 for (gc_count = 1; gc_count < 10; gc_count++) {
8821 other_context->Enter();
8822 CompileRun(source_exception);
8823 other_context->Exit();
8824 v8::internal::Heap::CollectAllGarbage(false);
8825 if (GetGlobalObjectsCount() == 1) break;
8826 }
8827 CHECK_GE(2, gc_count);
8828 CHECK_EQ(1, GetGlobalObjectsCount());
8829
8830 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +00008831}