blob: a00097b54f0fc286f7dff226b9f9d5fcdda4a869 [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;
1154static const int kTargetRecursionDepth = 300; // near maximum
1155
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
3605
3606TEST(CompilationErrorUsingTryCatchHandler) {
3607 v8::HandleScope scope;
3608 LocalContext env;
3609 v8::TryCatch try_catch;
3610 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
3611 CHECK_NE(NULL, *try_catch.Exception());
3612 CHECK(try_catch.HasCaught());
3613}
3614
3615
3616TEST(TryCatchFinallyUsingTryCatchHandler) {
3617 v8::HandleScope scope;
3618 LocalContext env;
3619 v8::TryCatch try_catch;
3620 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
3621 CHECK(!try_catch.HasCaught());
3622 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
3623 CHECK(try_catch.HasCaught());
3624 try_catch.Reset();
3625 Script::Compile(v8_str("(function() {"
3626 "try { throw ''; } finally { return; }"
3627 "})()"))->Run();
3628 CHECK(!try_catch.HasCaught());
3629 Script::Compile(v8_str("(function()"
3630 " { try { throw ''; } finally { throw 0; }"
3631 "})()"))->Run();
3632 CHECK(try_catch.HasCaught());
3633}
3634
3635
3636// SecurityHandler can't be run twice
3637TEST(SecurityHandler) {
3638 v8::HandleScope scope0;
3639 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
3640 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
3641 IndexedSecurityTestCallback);
3642 // Create an environment
3643 v8::Persistent<Context> context0 =
3644 Context::New(NULL, global_template);
3645 context0->Enter();
3646
3647 v8::Handle<v8::Object> global0 = context0->Global();
3648 v8::Handle<Script> script0 = v8_compile("foo = 111");
3649 script0->Run();
3650 global0->Set(v8_str("0"), v8_num(999));
3651 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
3652 CHECK_EQ(111, foo0->Int32Value());
3653 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
3654 CHECK_EQ(999, z0->Int32Value());
3655
3656 // Create another environment, should fail security checks.
3657 v8::HandleScope scope1;
3658
3659 v8::Persistent<Context> context1 =
3660 Context::New(NULL, global_template);
3661 context1->Enter();
3662
3663 v8::Handle<v8::Object> global1 = context1->Global();
3664 global1->Set(v8_str("othercontext"), global0);
3665 // This set will fail the security check.
3666 v8::Handle<Script> script1 =
3667 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
3668 script1->Run();
3669 // This read will pass the security check.
3670 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
3671 CHECK_EQ(111, foo1->Int32Value());
3672 // This read will pass the security check.
3673 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
3674 CHECK_EQ(999, z1->Int32Value());
3675
3676 // Create another environment, should pass security checks.
3677 { g_security_callback_result = true; // allow security handler to pass.
3678 v8::HandleScope scope2;
3679 LocalContext context2;
3680 v8::Handle<v8::Object> global2 = context2->Global();
3681 global2->Set(v8_str("othercontext"), global0);
3682 v8::Handle<Script> script2 =
3683 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
3684 script2->Run();
3685 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
3686 CHECK_EQ(333, foo2->Int32Value());
3687 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
3688 CHECK_EQ(888, z2->Int32Value());
3689 }
3690
3691 context1->Exit();
3692 context1.Dispose();
3693
3694 context0->Exit();
3695 context0.Dispose();
3696}
3697
3698
3699THREADED_TEST(SecurityChecks) {
3700 v8::HandleScope handle_scope;
3701 LocalContext env1;
3702 v8::Persistent<Context> env2 = Context::New();
3703
3704 Local<Value> foo = v8_str("foo");
3705 Local<Value> bar = v8_str("bar");
3706
3707 // Set to the same domain.
3708 env1->SetSecurityToken(foo);
3709
3710 // Create a function in env1.
3711 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
3712 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
3713 CHECK(spy->IsFunction());
3714
3715 // Create another function accessing global objects.
3716 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
3717 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
3718 CHECK(spy2->IsFunction());
3719
3720 // Switch to env2 in the same domain and invoke spy on env2.
3721 {
3722 env2->SetSecurityToken(foo);
3723 // Enter env2
3724 Context::Scope scope_env2(env2);
3725 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
3726 CHECK(result->IsFunction());
3727 }
3728
3729 {
3730 env2->SetSecurityToken(bar);
3731 Context::Scope scope_env2(env2);
3732
3733 // Call cross_domain_call, it should throw an exception
3734 v8::TryCatch try_catch;
3735 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
3736 CHECK(try_catch.HasCaught());
3737 }
3738
3739 env2.Dispose();
3740}
3741
3742
3743// Regression test case for issue 1183439.
3744THREADED_TEST(SecurityChecksForPrototypeChain) {
3745 v8::HandleScope scope;
3746 LocalContext current;
3747 v8::Persistent<Context> other = Context::New();
3748
3749 // Change context to be able to get to the Object function in the
3750 // other context without hitting the security checks.
3751 v8::Local<Value> other_object;
3752 { Context::Scope scope(other);
3753 other_object = other->Global()->Get(v8_str("Object"));
3754 other->Global()->Set(v8_num(42), v8_num(87));
3755 }
3756
3757 current->Global()->Set(v8_str("other"), other->Global());
3758 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
3759
3760 // Make sure the security check fails here and we get an undefined
3761 // result instead of getting the Object function. Repeat in a loop
3762 // to make sure to exercise the IC code.
3763 v8::Local<Script> access_other0 = v8_compile("other.Object");
3764 v8::Local<Script> access_other1 = v8_compile("other[42]");
3765 for (int i = 0; i < 5; i++) {
3766 CHECK(!access_other0->Run()->Equals(other_object));
3767 CHECK(access_other0->Run()->IsUndefined());
3768 CHECK(!access_other1->Run()->Equals(v8_num(87)));
3769 CHECK(access_other1->Run()->IsUndefined());
3770 }
3771
3772 // Create an object that has 'other' in its prototype chain and make
3773 // sure we cannot access the Object function indirectly through
3774 // that. Repeat in a loop to make sure to exercise the IC code.
3775 v8_compile("function F() { };"
3776 "F.prototype = other;"
3777 "var f = new F();")->Run();
3778 v8::Local<Script> access_f0 = v8_compile("f.Object");
3779 v8::Local<Script> access_f1 = v8_compile("f[42]");
3780 for (int j = 0; j < 5; j++) {
3781 CHECK(!access_f0->Run()->Equals(other_object));
3782 CHECK(access_f0->Run()->IsUndefined());
3783 CHECK(!access_f1->Run()->Equals(v8_num(87)));
3784 CHECK(access_f1->Run()->IsUndefined());
3785 }
3786
3787 // Now it gets hairy: Set the prototype for the other global object
3788 // to be the current global object. The prototype chain for 'f' now
3789 // goes through 'other' but ends up in the current global object.
3790 { Context::Scope scope(other);
3791 other->Global()->Set(v8_str("__proto__"), current->Global());
3792 }
3793 // Set a named and an index property on the current global
3794 // object. To force the lookup to go through the other global object,
3795 // the properties must not exist in the other global object.
3796 current->Global()->Set(v8_str("foo"), v8_num(100));
3797 current->Global()->Set(v8_num(99), v8_num(101));
3798 // Try to read the properties from f and make sure that the access
3799 // gets stopped by the security checks on the other global object.
3800 Local<Script> access_f2 = v8_compile("f.foo");
3801 Local<Script> access_f3 = v8_compile("f[99]");
3802 for (int k = 0; k < 5; k++) {
3803 CHECK(!access_f2->Run()->Equals(v8_num(100)));
3804 CHECK(access_f2->Run()->IsUndefined());
3805 CHECK(!access_f3->Run()->Equals(v8_num(101)));
3806 CHECK(access_f3->Run()->IsUndefined());
3807 }
3808 other.Dispose();
3809}
3810
3811
3812THREADED_TEST(CrossDomainDelete) {
3813 v8::HandleScope handle_scope;
3814 LocalContext env1;
3815 v8::Persistent<Context> env2 = Context::New();
3816
3817 Local<Value> foo = v8_str("foo");
3818 Local<Value> bar = v8_str("bar");
3819
3820 // Set to the same domain.
3821 env1->SetSecurityToken(foo);
3822 env2->SetSecurityToken(foo);
3823
3824 env1->Global()->Set(v8_str("prop"), v8_num(3));
3825 env2->Global()->Set(v8_str("env1"), env1->Global());
3826
3827 // Change env2 to a different domain and delete env1.prop.
3828 env2->SetSecurityToken(bar);
3829 {
3830 Context::Scope scope_env2(env2);
3831 Local<Value> result =
3832 Script::Compile(v8_str("delete env1.prop"))->Run();
3833 CHECK(result->IsFalse());
3834 }
3835
3836 // Check that env1.prop still exists.
3837 Local<Value> v = env1->Global()->Get(v8_str("prop"));
3838 CHECK(v->IsNumber());
3839 CHECK_EQ(3, v->Int32Value());
3840
3841 env2.Dispose();
3842}
3843
3844
3845THREADED_TEST(CrossDomainIsPropertyEnumerable) {
3846 v8::HandleScope handle_scope;
3847 LocalContext env1;
3848 v8::Persistent<Context> env2 = Context::New();
3849
3850 Local<Value> foo = v8_str("foo");
3851 Local<Value> bar = v8_str("bar");
3852
3853 // Set to the same domain.
3854 env1->SetSecurityToken(foo);
3855 env2->SetSecurityToken(foo);
3856
3857 env1->Global()->Set(v8_str("prop"), v8_num(3));
3858 env2->Global()->Set(v8_str("env1"), env1->Global());
3859
3860 // env1.prop is enumerable in env2.
3861 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
3862 {
3863 Context::Scope scope_env2(env2);
3864 Local<Value> result = Script::Compile(test)->Run();
3865 CHECK(result->IsTrue());
3866 }
3867
3868 // Change env2 to a different domain and test again.
3869 env2->SetSecurityToken(bar);
3870 {
3871 Context::Scope scope_env2(env2);
3872 Local<Value> result = Script::Compile(test)->Run();
3873 CHECK(result->IsFalse());
3874 }
3875
3876 env2.Dispose();
3877}
3878
3879
3880THREADED_TEST(CrossDomainForIn) {
3881 v8::HandleScope handle_scope;
3882 LocalContext env1;
3883 v8::Persistent<Context> env2 = Context::New();
3884
3885 Local<Value> foo = v8_str("foo");
3886 Local<Value> bar = v8_str("bar");
3887
3888 // Set to the same domain.
3889 env1->SetSecurityToken(foo);
3890 env2->SetSecurityToken(foo);
3891
3892 env1->Global()->Set(v8_str("prop"), v8_num(3));
3893 env2->Global()->Set(v8_str("env1"), env1->Global());
3894
3895 // Change env2 to a different domain and set env1's global object
3896 // as the __proto__ of an object in env2 and enumerate properties
3897 // in for-in. It shouldn't enumerate properties on env1's global
3898 // object.
3899 env2->SetSecurityToken(bar);
3900 {
3901 Context::Scope scope_env2(env2);
3902 Local<Value> result =
3903 CompileRun("(function(){var obj = {'__proto__':env1};"
3904 "for (var p in obj)"
3905 " if (p == 'prop') return false;"
3906 "return true;})()");
3907 CHECK(result->IsTrue());
3908 }
3909 env2.Dispose();
3910}
3911
3912
3913TEST(ContextDetachGlobal) {
3914 v8::HandleScope handle_scope;
3915 LocalContext env1;
3916 v8::Persistent<Context> env2 = Context::New();
3917
3918 Local<v8::Object> global1 = env1->Global();
3919
3920 Local<Value> foo = v8_str("foo");
3921
3922 // Set to the same domain.
3923 env1->SetSecurityToken(foo);
3924 env2->SetSecurityToken(foo);
3925
3926 // Enter env2
3927 env2->Enter();
3928
3929 // Create a function in env1
3930 Local<v8::Object> global2 = env2->Global();
3931 global2->Set(v8_str("prop"), v8::Integer::New(1));
3932 CompileRun("function getProp() {return prop;}");
3933
3934 env1->Global()->Set(v8_str("getProp"),
3935 global2->Get(v8_str("getProp")));
3936
3937 // Detach env1's global, and reuse the global object of env1
3938 env2->Exit();
3939 env2->DetachGlobal();
3940 // env2 has a new global object.
3941 CHECK(!env2->Global()->Equals(global2));
3942
3943 v8::Persistent<Context> env3 =
3944 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
3945 env3->SetSecurityToken(v8_str("bar"));
3946 env3->Enter();
3947
3948 Local<v8::Object> global3 = env3->Global();
3949 CHECK_EQ(global2, global3);
3950 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
3951 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
3952 global3->Set(v8_str("prop"), v8::Integer::New(-1));
3953 global3->Set(v8_str("prop2"), v8::Integer::New(2));
3954 env3->Exit();
3955
3956 // Call getProp in env1, and it should return the value 1
3957 {
3958 Local<Value> get_prop = global1->Get(v8_str("getProp"));
3959 CHECK(get_prop->IsFunction());
3960 v8::TryCatch try_catch;
3961 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
3962 CHECK(!try_catch.HasCaught());
3963 CHECK_EQ(1, r->Int32Value());
3964 }
3965
3966 // Check that env3 is not accessible from env1
3967 {
3968 Local<Value> r = global3->Get(v8_str("prop2"));
3969 CHECK(r->IsUndefined());
3970 }
3971
3972 env2.Dispose();
3973 env3.Dispose();
3974}
3975
3976
3977static bool NamedAccessBlocker(Local<v8::Object> global,
3978 Local<Value> name,
3979 v8::AccessType type,
3980 Local<Value> data) {
3981 return Context::GetCurrent()->Global()->Equals(global);
3982}
3983
3984
3985static bool IndexedAccessBlocker(Local<v8::Object> global,
3986 uint32_t key,
3987 v8::AccessType type,
3988 Local<Value> data) {
3989 return Context::GetCurrent()->Global()->Equals(global);
3990}
3991
3992
3993static int g_echo_value = -1;
3994static v8::Handle<Value> EchoGetter(Local<String> name,
3995 const AccessorInfo& info) {
3996 return v8_num(g_echo_value);
3997}
3998
3999
4000static void EchoSetter(Local<String> name,
4001 Local<Value> value,
4002 const AccessorInfo&) {
4003 if (value->IsNumber())
4004 g_echo_value = value->Int32Value();
4005}
4006
4007
4008static v8::Handle<Value> UnreachableGetter(Local<String> name,
4009 const AccessorInfo& info) {
4010 CHECK(false); // This function should not be called..
4011 return v8::Undefined();
4012}
4013
4014
4015static void UnreachableSetter(Local<String>, Local<Value>,
4016 const AccessorInfo&) {
4017 CHECK(false); // This function should nto be called.
4018}
4019
4020
4021THREADED_TEST(AccessControl) {
4022 v8::HandleScope handle_scope;
4023 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4024
4025 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4026 IndexedAccessBlocker);
4027
4028 // Add an accessor accessible by cross-domain JS code.
4029 global_template->SetAccessor(
4030 v8_str("accessible_prop"),
4031 EchoGetter, EchoSetter,
4032 v8::Handle<Value>(),
4033 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4034
4035 // Add an accessor that is not accessible by cross-domain JS code.
4036 global_template->SetAccessor(v8_str("blocked_prop"),
4037 UnreachableGetter, UnreachableSetter,
4038 v8::Handle<Value>(),
4039 v8::DEFAULT);
4040
4041 // Create an environment
4042 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4043 context0->Enter();
4044
4045 v8::Handle<v8::Object> global0 = context0->Global();
4046
4047 v8::HandleScope scope1;
4048
4049 v8::Persistent<Context> context1 = Context::New();
4050 context1->Enter();
4051
4052 v8::Handle<v8::Object> global1 = context1->Global();
4053 global1->Set(v8_str("other"), global0);
4054
4055 v8::Handle<Value> value;
4056
4057 // Access blocked property
4058 value = v8_compile("other.blocked_prop = 1")->Run();
4059 value = v8_compile("other.blocked_prop")->Run();
4060 CHECK(value->IsUndefined());
4061
4062 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4063 CHECK(value->IsFalse());
4064
4065 // Access accessible property
4066 value = v8_compile("other.accessible_prop = 3")->Run();
4067 CHECK(value->IsNumber());
4068 CHECK_EQ(3, value->Int32Value());
4069
4070 value = v8_compile("other.accessible_prop")->Run();
4071 CHECK(value->IsNumber());
4072 CHECK_EQ(3, value->Int32Value());
4073
4074 value =
4075 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
4076 CHECK(value->IsTrue());
4077
4078 // Enumeration doesn't enumerate accessors from inaccessible objects in
4079 // the prototype chain even if the accessors are in themselves accessible.
4080 Local<Value> result =
4081 CompileRun("(function(){var obj = {'__proto__':other};"
4082 "for (var p in obj)"
4083 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
4084 " return false;"
4085 " }"
4086 "return true;})()");
4087 CHECK(result->IsTrue());
4088
4089 context1->Exit();
4090 context0->Exit();
4091 context1.Dispose();
4092 context0.Dispose();
4093}
4094
4095
4096static v8::Handle<Value> ConstTenGetter(Local<String> name,
4097 const AccessorInfo& info) {
4098 return v8_num(10);
4099}
4100
4101
4102THREADED_TEST(CrossDomainAccessors) {
4103 v8::HandleScope handle_scope;
4104
4105 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
4106
4107 v8::Handle<v8::ObjectTemplate> global_template =
4108 func_template->InstanceTemplate();
4109
4110 v8::Handle<v8::ObjectTemplate> proto_template =
4111 func_template->PrototypeTemplate();
4112
4113 // Add an accessor to proto that's accessible by cross-domain JS code.
4114 proto_template->SetAccessor(v8_str("accessible"),
4115 ConstTenGetter, 0,
4116 v8::Handle<Value>(),
4117 v8::ALL_CAN_READ);
4118
4119 // Add an accessor that is not accessible by cross-domain JS code.
4120 global_template->SetAccessor(v8_str("unreachable"),
4121 UnreachableGetter, 0,
4122 v8::Handle<Value>(),
4123 v8::DEFAULT);
4124
4125 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4126 context0->Enter();
4127
4128 Local<v8::Object> global = context0->Global();
4129 // Add a normal property that shadows 'accessible'
4130 global->Set(v8_str("accessible"), v8_num(11));
4131
4132 // Enter a new context.
4133 v8::HandleScope scope1;
4134 v8::Persistent<Context> context1 = Context::New();
4135 context1->Enter();
4136
4137 v8::Handle<v8::Object> global1 = context1->Global();
4138 global1->Set(v8_str("other"), global);
4139
4140 // Should return 10, instead of 11
4141 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
4142 CHECK(value->IsNumber());
4143 CHECK_EQ(10, value->Int32Value());
4144
4145 value = v8_compile("other.unreachable")->Run();
4146 CHECK(value->IsUndefined());
4147
4148 context1->Exit();
4149 context0->Exit();
4150 context1.Dispose();
4151 context0.Dispose();
4152}
4153
4154
4155static int named_access_count = 0;
4156static int indexed_access_count = 0;
4157
4158static bool NamedAccessCounter(Local<v8::Object> global,
4159 Local<Value> name,
4160 v8::AccessType type,
4161 Local<Value> data) {
4162 named_access_count++;
4163 return true;
4164}
4165
4166
4167static bool IndexedAccessCounter(Local<v8::Object> global,
4168 uint32_t key,
4169 v8::AccessType type,
4170 Local<Value> data) {
4171 indexed_access_count++;
4172 return true;
4173}
4174
4175
4176// This one is too easily disturbed by other tests.
4177TEST(AccessControlIC) {
4178 named_access_count = 0;
4179 indexed_access_count = 0;
4180
4181 v8::HandleScope handle_scope;
4182
4183 // Create an environment.
4184 v8::Persistent<Context> context0 = Context::New();
4185 context0->Enter();
4186
4187 // Create an object that requires access-check functions to be
4188 // called for cross-domain access.
4189 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4190 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
4191 IndexedAccessCounter);
4192 Local<v8::Object> object = object_template->NewInstance();
4193
4194 v8::HandleScope scope1;
4195
4196 // Create another environment.
4197 v8::Persistent<Context> context1 = Context::New();
4198 context1->Enter();
4199
4200 // Make easy access to the object from the other environment.
4201 v8::Handle<v8::Object> global1 = context1->Global();
4202 global1->Set(v8_str("obj"), object);
4203
4204 v8::Handle<Value> value;
4205
4206 // Check that the named access-control function is called every time.
4207 CompileRun("function testProp(obj) {"
4208 " for (var i = 0; i < 10; i++) obj.prop = 1;"
4209 " for (var j = 0; j < 10; j++) obj.prop;"
4210 " return obj.prop"
4211 "}");
4212 value = CompileRun("testProp(obj)");
4213 CHECK(value->IsNumber());
4214 CHECK_EQ(1, value->Int32Value());
4215 CHECK_EQ(21, named_access_count);
4216
4217 // Check that the named access-control function is called every time.
4218 CompileRun("var p = 'prop';"
4219 "function testKeyed(obj) {"
4220 " for (var i = 0; i < 10; i++) obj[p] = 1;"
4221 " for (var j = 0; j < 10; j++) obj[p];"
4222 " return obj[p];"
4223 "}");
4224 // Use obj which requires access checks. No inline caching is used
4225 // in that case.
4226 value = CompileRun("testKeyed(obj)");
4227 CHECK(value->IsNumber());
4228 CHECK_EQ(1, value->Int32Value());
4229 CHECK_EQ(42, named_access_count);
4230 // Force the inline caches into generic state and try again.
4231 CompileRun("testKeyed({ a: 0 })");
4232 CompileRun("testKeyed({ b: 0 })");
4233 value = CompileRun("testKeyed(obj)");
4234 CHECK(value->IsNumber());
4235 CHECK_EQ(1, value->Int32Value());
4236 CHECK_EQ(63, named_access_count);
4237
4238 // Check that the indexed access-control function is called every time.
4239 CompileRun("function testIndexed(obj) {"
4240 " for (var i = 0; i < 10; i++) obj[0] = 1;"
4241 " for (var j = 0; j < 10; j++) obj[0];"
4242 " return obj[0]"
4243 "}");
4244 value = CompileRun("testIndexed(obj)");
4245 CHECK(value->IsNumber());
4246 CHECK_EQ(1, value->Int32Value());
4247 CHECK_EQ(21, indexed_access_count);
4248 // Force the inline caches into generic state.
4249 CompileRun("testIndexed(new Array(1))");
4250 // Test that the indexed access check is called.
4251 value = CompileRun("testIndexed(obj)");
4252 CHECK(value->IsNumber());
4253 CHECK_EQ(1, value->Int32Value());
4254 CHECK_EQ(42, indexed_access_count);
4255
4256 // Check that the named access check is called when invoking
4257 // functions on an object that requires access checks.
4258 CompileRun("obj.f = function() {}");
4259 CompileRun("function testCallNormal(obj) {"
4260 " for (var i = 0; i < 10; i++) obj.f();"
4261 "}");
4262 CompileRun("testCallNormal(obj)");
4263 CHECK_EQ(74, named_access_count);
4264
4265 // Force obj into slow case.
4266 value = CompileRun("delete obj.prop");
4267 CHECK(value->BooleanValue());
4268 // Force inline caches into dictionary probing mode.
4269 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
4270 // Test that the named access check is called.
4271 value = CompileRun("testProp(obj);");
4272 CHECK(value->IsNumber());
4273 CHECK_EQ(1, value->Int32Value());
4274 CHECK_EQ(96, named_access_count);
4275
4276 // Force the call inline cache into dictionary probing mode.
4277 CompileRun("o.f = function() {}; testCallNormal(o)");
4278 // Test that the named access check is still called for each
4279 // invocation of the function.
4280 value = CompileRun("testCallNormal(obj)");
4281 CHECK_EQ(106, named_access_count);
4282
4283 context1->Exit();
4284 context0->Exit();
4285 context1.Dispose();
4286 context0.Dispose();
4287}
4288
4289
4290static bool NamedAccessFlatten(Local<v8::Object> global,
4291 Local<Value> name,
4292 v8::AccessType type,
4293 Local<Value> data) {
4294 char buf[100];
4295 int len;
4296
4297 CHECK(name->IsString());
4298
4299 memset(buf, 0x1, sizeof(buf));
4300 len = Local<String>::Cast(name)->WriteAscii(buf);
4301 CHECK_EQ(4, len);
4302
4303 uint16_t buf2[100];
4304
4305 memset(buf, 0x1, sizeof(buf));
4306 len = Local<String>::Cast(name)->Write(buf2);
4307 CHECK_EQ(4, len);
4308
4309 return true;
4310}
4311
4312
4313static bool IndexedAccessFlatten(Local<v8::Object> global,
4314 uint32_t key,
4315 v8::AccessType type,
4316 Local<Value> data) {
4317 return true;
4318}
4319
4320
4321// Regression test. In access checks, operations that may cause
4322// garbage collection are not allowed. It used to be the case that
4323// using the Write operation on a string could cause a garbage
4324// collection due to flattening of the string. This is no longer the
4325// case.
4326THREADED_TEST(AccessControlFlatten) {
4327 named_access_count = 0;
4328 indexed_access_count = 0;
4329
4330 v8::HandleScope handle_scope;
4331
4332 // Create an environment.
4333 v8::Persistent<Context> context0 = Context::New();
4334 context0->Enter();
4335
4336 // Create an object that requires access-check functions to be
4337 // called for cross-domain access.
4338 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4339 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
4340 IndexedAccessFlatten);
4341 Local<v8::Object> object = object_template->NewInstance();
4342
4343 v8::HandleScope scope1;
4344
4345 // Create another environment.
4346 v8::Persistent<Context> context1 = Context::New();
4347 context1->Enter();
4348
4349 // Make easy access to the object from the other environment.
4350 v8::Handle<v8::Object> global1 = context1->Global();
4351 global1->Set(v8_str("obj"), object);
4352
4353 v8::Handle<Value> value;
4354
4355 value = v8_compile("var p = 'as' + 'df';")->Run();
4356 value = v8_compile("obj[p];")->Run();
4357
4358 context1->Exit();
4359 context0->Exit();
4360 context1.Dispose();
4361 context0.Dispose();
4362}
4363
4364
4365static v8::Handle<Value> AccessControlNamedGetter(
4366 Local<String>, const AccessorInfo&) {
4367 return v8::Integer::New(42);
4368}
4369
4370
4371static v8::Handle<Value> AccessControlNamedSetter(
4372 Local<String>, Local<Value> value, const AccessorInfo&) {
4373 return value;
4374}
4375
4376
4377static v8::Handle<Value> AccessControlIndexedGetter(
4378 uint32_t index,
4379 const AccessorInfo& info) {
4380 return v8_num(42);
4381}
4382
4383
4384static v8::Handle<Value> AccessControlIndexedSetter(
4385 uint32_t, Local<Value> value, const AccessorInfo&) {
4386 return value;
4387}
4388
4389
4390THREADED_TEST(AccessControlInterceptorIC) {
4391 named_access_count = 0;
4392 indexed_access_count = 0;
4393
4394 v8::HandleScope handle_scope;
4395
4396 // Create an environment.
4397 v8::Persistent<Context> context0 = Context::New();
4398 context0->Enter();
4399
4400 // Create an object that requires access-check functions to be
4401 // called for cross-domain access. The object also has interceptors
4402 // interceptor.
4403 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4404 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
4405 IndexedAccessCounter);
4406 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
4407 AccessControlNamedSetter);
4408 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
4409 AccessControlIndexedSetter);
4410 Local<v8::Object> object = object_template->NewInstance();
4411
4412 v8::HandleScope scope1;
4413
4414 // Create another environment.
4415 v8::Persistent<Context> context1 = Context::New();
4416 context1->Enter();
4417
4418 // Make easy access to the object from the other environment.
4419 v8::Handle<v8::Object> global1 = context1->Global();
4420 global1->Set(v8_str("obj"), object);
4421
4422 v8::Handle<Value> value;
4423
4424 // Check that the named access-control function is called every time
4425 // eventhough there is an interceptor on the object.
4426 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
4427 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
4428 "obj.x")->Run();
4429 CHECK(value->IsNumber());
4430 CHECK_EQ(42, value->Int32Value());
4431 CHECK_EQ(21, named_access_count);
4432
4433 value = v8_compile("var p = 'x';")->Run();
4434 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
4435 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
4436 "obj[p]")->Run();
4437 CHECK(value->IsNumber());
4438 CHECK_EQ(42, value->Int32Value());
4439 CHECK_EQ(42, named_access_count);
4440
4441 // Check that the indexed access-control function is called every
4442 // time eventhough there is an interceptor on the object.
4443 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
4444 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
4445 "obj[0]")->Run();
4446 CHECK(value->IsNumber());
4447 CHECK_EQ(42, value->Int32Value());
4448 CHECK_EQ(21, indexed_access_count);
4449
4450 context1->Exit();
4451 context0->Exit();
4452 context1.Dispose();
4453 context0.Dispose();
4454}
4455
4456
4457THREADED_TEST(Version) {
4458 v8::V8::GetVersion();
4459}
4460
4461
4462static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
4463 ApiTestFuzzer::Fuzz();
4464 return v8_num(12);
4465}
4466
4467
4468THREADED_TEST(InstanceProperties) {
4469 v8::HandleScope handle_scope;
4470 LocalContext context;
4471
4472 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4473 Local<ObjectTemplate> instance = t->InstanceTemplate();
4474
4475 instance->Set(v8_str("x"), v8_num(42));
4476 instance->Set(v8_str("f"),
4477 v8::FunctionTemplate::New(InstanceFunctionCallback));
4478
4479 Local<Value> o = t->GetFunction()->NewInstance();
4480
4481 context->Global()->Set(v8_str("i"), o);
4482 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
4483 CHECK_EQ(42, value->Int32Value());
4484
4485 value = Script::Compile(v8_str("i.f()"))->Run();
4486 CHECK_EQ(12, value->Int32Value());
4487}
4488
4489
4490static v8::Handle<Value>
4491GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
4492 ApiTestFuzzer::Fuzz();
4493 return v8::Handle<Value>();
4494}
4495
4496
4497THREADED_TEST(GlobalObjectInstanceProperties) {
4498 v8::HandleScope handle_scope;
4499
4500 Local<Value> global_object;
4501
4502 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4503 t->InstanceTemplate()->SetNamedPropertyHandler(
4504 GlobalObjectInstancePropertiesGet);
4505 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
4506 instance_template->Set(v8_str("x"), v8_num(42));
4507 instance_template->Set(v8_str("f"),
4508 v8::FunctionTemplate::New(InstanceFunctionCallback));
4509
4510 {
4511 LocalContext env(NULL, instance_template);
4512 // Hold on to the global object so it can be used again in another
4513 // environment initialization.
4514 global_object = env->Global();
4515
4516 Local<Value> value = Script::Compile(v8_str("x"))->Run();
4517 CHECK_EQ(42, value->Int32Value());
4518 value = Script::Compile(v8_str("f()"))->Run();
4519 CHECK_EQ(12, value->Int32Value());
4520 }
4521
4522 {
4523 // Create new environment reusing the global object.
4524 LocalContext env(NULL, instance_template, global_object);
4525 Local<Value> value = Script::Compile(v8_str("x"))->Run();
4526 CHECK_EQ(42, value->Int32Value());
4527 value = Script::Compile(v8_str("f()"))->Run();
4528 CHECK_EQ(12, value->Int32Value());
4529 }
4530}
4531
4532
4533static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
4534 ApiTestFuzzer::Fuzz();
4535 return v8_num(42);
4536}
4537
4538
4539static int shadow_y;
4540static int shadow_y_setter_call_count;
4541static int shadow_y_getter_call_count;
4542
4543
4544static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
4545 shadow_y_setter_call_count++;
4546 shadow_y = 42;
4547}
4548
4549
4550static v8::Handle<Value> ShadowYGetter(Local<String> name,
4551 const AccessorInfo& info) {
4552 ApiTestFuzzer::Fuzz();
4553 shadow_y_getter_call_count++;
4554 return v8_num(shadow_y);
4555}
4556
4557
4558static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
4559 const AccessorInfo& info) {
4560 return v8::Handle<Value>();
4561}
4562
4563
4564static v8::Handle<Value> ShadowNamedGet(Local<String> key,
4565 const AccessorInfo&) {
4566 return v8::Handle<Value>();
4567}
4568
4569
4570THREADED_TEST(ShadowObject) {
4571 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
4572 v8::HandleScope handle_scope;
4573
4574 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
4575 LocalContext context(NULL, global_template);
4576
4577 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4578 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
4579 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
4580 Local<ObjectTemplate> proto = t->PrototypeTemplate();
4581 Local<ObjectTemplate> instance = t->InstanceTemplate();
4582
4583 // Only allow calls of f on instances of t.
4584 Local<v8::Signature> signature = v8::Signature::New(t);
4585 proto->Set(v8_str("f"),
4586 v8::FunctionTemplate::New(ShadowFunctionCallback,
4587 Local<Value>(),
4588 signature));
4589 proto->Set(v8_str("x"), v8_num(12));
4590
4591 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
4592
4593 Local<Value> o = t->GetFunction()->NewInstance();
4594 context->Global()->Set(v8_str("__proto__"), o);
4595
4596 Local<Value> value =
4597 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
4598 CHECK(value->IsBoolean());
4599 CHECK(!value->BooleanValue());
4600
4601 value = Script::Compile(v8_str("x"))->Run();
4602 CHECK_EQ(12, value->Int32Value());
4603
4604 value = Script::Compile(v8_str("f()"))->Run();
4605 CHECK_EQ(42, value->Int32Value());
4606
4607 Script::Compile(v8_str("y = 42"))->Run();
4608 CHECK_EQ(1, shadow_y_setter_call_count);
4609 value = Script::Compile(v8_str("y"))->Run();
4610 CHECK_EQ(1, shadow_y_getter_call_count);
4611 CHECK_EQ(42, value->Int32Value());
4612}
4613
4614
4615THREADED_TEST(HiddenPrototype) {
4616 v8::HandleScope handle_scope;
4617 LocalContext context;
4618
4619 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
4620 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
4621 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
4622 t1->SetHiddenPrototype(true);
4623 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
4624 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
4625 t2->SetHiddenPrototype(true);
4626 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
4627 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
4628 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
4629
4630 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
4631 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
4632 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
4633 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
4634
4635 // Setting the prototype on an object skips hidden prototypes.
4636 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4637 o0->Set(v8_str("__proto__"), o1);
4638 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4639 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4640 o0->Set(v8_str("__proto__"), o2);
4641 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4642 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4643 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
4644 o0->Set(v8_str("__proto__"), o3);
4645 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
4646 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
4647 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
4648 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
4649
4650 // Getting the prototype of o0 should get the first visible one
4651 // which is o3. Therefore, z should not be defined on the prototype
4652 // object.
4653 Local<Value> proto = o0->Get(v8_str("__proto__"));
4654 CHECK(proto->IsObject());
4655 CHECK(Local<v8::Object>::Cast(proto)->Get(v8_str("z"))->IsUndefined());
4656}
4657
4658
4659THREADED_TEST(GetterSetterExceptions) {
4660 v8::HandleScope handle_scope;
4661 LocalContext context;
4662 CompileRun(
4663 "function Foo() { };"
4664 "function Throw() { throw 5; };"
4665 "var x = { };"
4666 "x.__defineSetter__('set', Throw);"
4667 "x.__defineGetter__('get', Throw);");
4668 Local<v8::Object> x =
4669 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
4670 v8::TryCatch try_catch;
4671 x->Set(v8_str("set"), v8::Integer::New(8));
4672 x->Get(v8_str("get"));
4673 x->Set(v8_str("set"), v8::Integer::New(8));
4674 x->Get(v8_str("get"));
4675 x->Set(v8_str("set"), v8::Integer::New(8));
4676 x->Get(v8_str("get"));
4677 x->Set(v8_str("set"), v8::Integer::New(8));
4678 x->Get(v8_str("get"));
4679}
4680
4681
4682THREADED_TEST(Constructor) {
4683 v8::HandleScope handle_scope;
4684 LocalContext context;
4685 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
4686 templ->SetClassName(v8_str("Fun"));
4687 Local<Function> cons = templ->GetFunction();
4688 context->Global()->Set(v8_str("Fun"), cons);
4689 Local<v8::Object> inst = cons->NewInstance();
4690 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
4691 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
4692 CHECK(value->BooleanValue());
4693}
4694
4695THREADED_TEST(FunctionDescriptorException) {
4696 v8::HandleScope handle_scope;
4697 LocalContext context;
4698 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
4699 templ->SetClassName(v8_str("Fun"));
4700 Local<Function> cons = templ->GetFunction();
4701 context->Global()->Set(v8_str("Fun"), cons);
4702 Local<Value> value = CompileRun(
4703 "function test() {"
4704 " try {"
4705 " (new Fun()).blah()"
4706 " } catch (e) {"
4707 " var str = String(e);"
4708 " if (str.indexOf('TypeError') == -1) return 1;"
4709 " if (str.indexOf('[object Fun]') != -1) return 2;"
4710 " if (str.indexOf('#<a Fun>') == -1) return 3;"
4711 " return 0;"
4712 " }"
4713 " return 4;"
4714 "}"
4715 "test();");
4716 CHECK_EQ(0, value->Int32Value());
4717}
4718
4719
4720THREADED_TEST(EvalAliasedDynamic) {
4721 v8::HandleScope scope;
4722 LocalContext current;
4723
4724 // Tests where aliased eval can only be resolved dynamically.
4725 Local<Script> script =
4726 Script::Compile(v8_str("function f(x) { "
4727 " var foo = 2;"
4728 " with (x) { return eval('foo'); }"
4729 "}"
4730 "foo = 0;"
4731 "result1 = f(new Object());"
4732 "result2 = f(this);"
4733 "var x = new Object();"
4734 "x.eval = function(x) { return 1; };"
4735 "result3 = f(x);"));
4736 script->Run();
4737 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
4738 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
4739 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
4740
4741 v8::TryCatch try_catch;
4742 script =
4743 Script::Compile(v8_str("function f(x) { "
4744 " var bar = 2;"
4745 " with (x) { return eval('bar'); }"
4746 "}"
4747 "f(this)"));
4748 script->Run();
4749 CHECK(try_catch.HasCaught());
4750 try_catch.Reset();
4751}
4752
4753
4754THREADED_TEST(CrossEval) {
4755 v8::HandleScope scope;
4756 LocalContext other;
4757 LocalContext current;
4758
4759 Local<String> token = v8_str("<security token>");
4760 other->SetSecurityToken(token);
4761 current->SetSecurityToken(token);
4762
4763 // Setup reference from current to other.
4764 current->Global()->Set(v8_str("other"), other->Global());
4765
4766 // Check that new variables are introduced in other context.
4767 Local<Script> script =
4768 Script::Compile(v8_str("other.eval('var foo = 1234')"));
4769 script->Run();
4770 Local<Value> foo = other->Global()->Get(v8_str("foo"));
4771 CHECK_EQ(1234, foo->Int32Value());
4772 CHECK(!current->Global()->Has(v8_str("foo")));
4773
4774 // Check that writing to non-existing properties introduces them in
4775 // the other context.
4776 script =
4777 Script::Compile(v8_str("other.eval('na = 1234')"));
4778 script->Run();
4779 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
4780 CHECK(!current->Global()->Has(v8_str("na")));
4781
4782 // Check that global variables in current context are not visible in other
4783 // context.
4784 v8::TryCatch try_catch;
4785 script =
4786 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
4787 Local<Value> result = script->Run();
4788 CHECK(try_catch.HasCaught());
4789 try_catch.Reset();
4790
4791 // Check that local variables in current context are not visible in other
4792 // context.
4793 script =
4794 Script::Compile(v8_str("(function() { "
4795 " var baz = 87;"
4796 " return other.eval('baz');"
4797 "})();"));
4798 result = script->Run();
4799 CHECK(try_catch.HasCaught());
4800 try_catch.Reset();
4801
4802 // Check that global variables in the other environment are visible
4803 // when evaluting code.
4804 other->Global()->Set(v8_str("bis"), v8_num(1234));
4805 script = Script::Compile(v8_str("other.eval('bis')"));
4806 CHECK_EQ(1234, script->Run()->Int32Value());
4807 CHECK(!try_catch.HasCaught());
4808
4809 // Check that the 'this' pointer points to the global object evaluating
4810 // code.
4811 other->Global()->Set(v8_str("t"), other->Global());
4812 script = Script::Compile(v8_str("other.eval('this == t')"));
4813 result = script->Run();
4814 CHECK(result->IsTrue());
4815 CHECK(!try_catch.HasCaught());
4816
4817 // Check that variables introduced in with-statement are not visible in
4818 // other context.
4819 script =
4820 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
4821 result = script->Run();
4822 CHECK(try_catch.HasCaught());
4823 try_catch.Reset();
4824
4825 // Check that you cannot use 'eval.call' with another object than the
4826 // current global object.
4827 script =
4828 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
4829 result = script->Run();
4830 CHECK(try_catch.HasCaught());
4831}
4832
4833
4834// Test that calling eval in a context which has been detached from
4835// its global throws an exception. This behavior is consistent with
4836// other JavaScript implementations.
4837THREADED_TEST(EvalInDetachedGlobal) {
4838 v8::HandleScope scope;
4839
4840 v8::Persistent<Context> context0 = Context::New();
4841 v8::Persistent<Context> context1 = Context::New();
4842
4843 // Setup function in context0 that uses eval from context0.
4844 context0->Enter();
4845 v8::Handle<v8::Value> fun =
4846 CompileRun("var x = 42;"
4847 "(function() {"
4848 " var e = eval;"
4849 " return function(s) { return e(s); }"
4850 "})()");
4851 context0->Exit();
4852
4853 // Put the function into context1 and call it before and after
4854 // detaching the global. Before detaching, the call succeeds and
4855 // after detaching and exception is thrown.
4856 context1->Enter();
4857 context1->Global()->Set(v8_str("fun"), fun);
4858 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
4859 CHECK_EQ(42, x_value->Int32Value());
4860 context0->DetachGlobal();
4861 v8::TryCatch catcher;
4862 x_value = CompileRun("fun('x')");
4863 CHECK(x_value.IsEmpty());
4864 CHECK(catcher.HasCaught());
4865 context1->Exit();
4866
4867 context1.Dispose();
4868 context0.Dispose();
4869}
4870
4871
4872THREADED_TEST(CrossLazyLoad) {
4873 v8::HandleScope scope;
4874 LocalContext other;
4875 LocalContext current;
4876
4877 Local<String> token = v8_str("<security token>");
4878 other->SetSecurityToken(token);
4879 current->SetSecurityToken(token);
4880
4881 // Setup reference from current to other.
4882 current->Global()->Set(v8_str("other"), other->Global());
4883
4884 // Trigger lazy loading in other context.
4885 Local<Script> script =
4886 Script::Compile(v8_str("other.eval('new Date(42)')"));
4887 Local<Value> value = script->Run();
4888 CHECK_EQ(42.0, value->NumberValue());
4889}
4890
4891
4892static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
4893 ApiTestFuzzer::Fuzz();
4894 if (args.IsConstructCall()) {
4895 if (args[0]->IsInt32()) {
4896 return v8_num(-args[0]->Int32Value());
4897 }
4898 }
4899
4900 return args[0];
4901}
4902
4903
4904// Test that a call handler can be set for objects which will allow
4905// non-function objects created through the API to be called as
4906// functions.
4907THREADED_TEST(CallAsFunction) {
4908 v8::HandleScope scope;
4909 LocalContext context;
4910
4911 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
4912 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
4913 instance_template->SetCallAsFunctionHandler(call_as_function);
4914 Local<v8::Object> instance = t->GetFunction()->NewInstance();
4915 context->Global()->Set(v8_str("obj"), instance);
4916 v8::TryCatch try_catch;
4917 Local<Value> value;
4918 CHECK(!try_catch.HasCaught());
4919
4920 value = CompileRun("obj(42)");
4921 CHECK(!try_catch.HasCaught());
4922 CHECK_EQ(42, value->Int32Value());
4923
4924 value = CompileRun("(function(o){return o(49)})(obj)");
4925 CHECK(!try_catch.HasCaught());
4926 CHECK_EQ(49, value->Int32Value());
4927
4928 // test special case of call as function
4929 value = CompileRun("[obj]['0'](45)");
4930 CHECK(!try_catch.HasCaught());
4931 CHECK_EQ(45, value->Int32Value());
4932
4933 value = CompileRun("obj.call = Function.prototype.call;"
4934 "obj.call(null, 87)");
4935 CHECK(!try_catch.HasCaught());
4936 CHECK_EQ(87, value->Int32Value());
4937
4938 // Regression tests for bug #1116356: Calling call through call/apply
4939 // must work for non-function receivers.
4940 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
4941 value = CompileRun(apply_99);
4942 CHECK(!try_catch.HasCaught());
4943 CHECK_EQ(99, value->Int32Value());
4944
4945 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
4946 value = CompileRun(call_17);
4947 CHECK(!try_catch.HasCaught());
4948 CHECK_EQ(17, value->Int32Value());
4949
4950 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00004951 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00004952 value = CompileRun("new obj(43)");
4953 CHECK(!try_catch.HasCaught());
4954 CHECK_EQ(-43, value->Int32Value());
4955}
4956
4957
4958static int CountHandles() {
4959 return v8::HandleScope::NumberOfHandles();
4960}
4961
4962
4963static int Recurse(int depth, int iterations) {
4964 v8::HandleScope scope;
4965 if (depth == 0) return CountHandles();
4966 for (int i = 0; i < iterations; i++) {
4967 Local<v8::Number> n = v8::Integer::New(42);
4968 }
4969 return Recurse(depth - 1, iterations);
4970}
4971
4972
4973THREADED_TEST(HandleIteration) {
4974 static const int kIterations = 500;
4975 static const int kNesting = 200;
4976 CHECK_EQ(0, CountHandles());
4977 {
4978 v8::HandleScope scope1;
4979 CHECK_EQ(0, CountHandles());
4980 for (int i = 0; i < kIterations; i++) {
4981 Local<v8::Number> n = v8::Integer::New(42);
4982 CHECK_EQ(i + 1, CountHandles());
4983 }
4984
4985 CHECK_EQ(kIterations, CountHandles());
4986 {
4987 v8::HandleScope scope2;
4988 for (int j = 0; j < kIterations; j++) {
4989 Local<v8::Number> n = v8::Integer::New(42);
4990 CHECK_EQ(j + 1 + kIterations, CountHandles());
4991 }
4992 }
4993 CHECK_EQ(kIterations, CountHandles());
4994 }
4995 CHECK_EQ(0, CountHandles());
4996 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
4997}
4998
4999
5000static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
5001 Local<String> name,
5002 const AccessorInfo& info) {
5003 ApiTestFuzzer::Fuzz();
5004 return v8::Handle<Value>();
5005}
5006
5007
5008THREADED_TEST(InterceptorHasOwnProperty) {
5009 v8::HandleScope scope;
5010 LocalContext context;
5011 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
5012 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5013 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
5014 Local<Function> function = fun_templ->GetFunction();
5015 context->Global()->Set(v8_str("constructor"), function);
5016 v8::Handle<Value> value = CompileRun(
5017 "var o = new constructor();"
5018 "o.hasOwnProperty('ostehaps');");
5019 CHECK_EQ(false, value->BooleanValue());
5020 value = CompileRun(
5021 "o.ostehaps = 42;"
5022 "o.hasOwnProperty('ostehaps');");
5023 CHECK_EQ(true, value->BooleanValue());
5024 value = CompileRun(
5025 "var p = new constructor();"
5026 "p.hasOwnProperty('ostehaps');");
5027 CHECK_EQ(false, value->BooleanValue());
5028}
5029
5030
5031static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
5032 Local<String> name,
5033 const AccessorInfo& info) {
5034 ApiTestFuzzer::Fuzz();
5035 i::Heap::CollectAllGarbage(false);
5036 return v8::Handle<Value>();
5037}
5038
5039
5040THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
5041 v8::HandleScope scope;
5042 LocalContext context;
5043 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
5044 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5045 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
5046 Local<Function> function = fun_templ->GetFunction();
5047 context->Global()->Set(v8_str("constructor"), function);
5048 // Let's first make some stuff so we can be sure to get a good GC.
5049 CompileRun(
5050 "function makestr(size) {"
5051 " switch (size) {"
5052 " case 1: return 'f';"
5053 " case 2: return 'fo';"
5054 " case 3: return 'foo';"
5055 " }"
5056 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
5057 "}"
5058 "var x = makestr(12345);"
5059 "x = makestr(31415);"
5060 "x = makestr(23456);");
5061 v8::Handle<Value> value = CompileRun(
5062 "var o = new constructor();"
5063 "o.__proto__ = new String(x);"
5064 "o.hasOwnProperty('ostehaps');");
5065 CHECK_EQ(false, value->BooleanValue());
5066}
5067
5068
5069typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
5070 const AccessorInfo& info);
5071
5072
5073static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
5074 const char* source,
5075 int expected) {
5076 v8::HandleScope scope;
5077 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5078 templ->SetNamedPropertyHandler(getter);
5079 LocalContext context;
5080 context->Global()->Set(v8_str("o"), templ->NewInstance());
5081 v8::Handle<Value> value = CompileRun(source);
5082 CHECK_EQ(expected, value->Int32Value());
5083}
5084
5085
5086static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
5087 const AccessorInfo& info) {
5088 ApiTestFuzzer::Fuzz();
5089 CHECK(v8_str("x")->Equals(name));
5090 return v8::Integer::New(42);
5091}
5092
5093
5094// This test should hit the load IC for the interceptor case.
5095THREADED_TEST(InterceptorLoadIC) {
5096 CheckInterceptorLoadIC(InterceptorLoadICGetter,
5097 "var result = 0;"
5098 "for (var i = 0; i < 1000; i++) {"
5099 " result = o.x;"
5100 "}",
5101 42);
5102}
5103
5104
5105// Below go several tests which verify that JITing for various
5106// configurations of interceptor and explicit fields works fine
5107// (those cases are special cased to get better performance).
5108
5109static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
5110 const AccessorInfo& info) {
5111 ApiTestFuzzer::Fuzz();
5112 return v8_str("x")->Equals(name)
5113 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
5114}
5115
5116
5117THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
5118 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5119 "var result = 0;"
5120 "o.y = 239;"
5121 "for (var i = 0; i < 1000; i++) {"
5122 " result = o.y;"
5123 "}",
5124 239);
5125}
5126
5127
5128THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
5129 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5130 "var result = 0;"
5131 "o.__proto__ = { 'y': 239 };"
5132 "for (var i = 0; i < 1000; i++) {"
5133 " result = o.y + o.x;"
5134 "}",
5135 239 + 42);
5136}
5137
5138
5139THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
5140 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5141 "var result = 0;"
5142 "o.__proto__.y = 239;"
5143 "for (var i = 0; i < 1000; i++) {"
5144 " result = o.y + o.x;"
5145 "}",
5146 239 + 42);
5147}
5148
5149
5150THREADED_TEST(InterceptorLoadICUndefined) {
5151 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5152 "var result = 0;"
5153 "for (var i = 0; i < 1000; i++) {"
5154 " result = (o.y == undefined) ? 239 : 42;"
5155 "}",
5156 239);
5157}
5158
5159
5160THREADED_TEST(InterceptorLoadICWithOverride) {
5161 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5162 "fst = new Object(); fst.__proto__ = o;"
5163 "snd = new Object(); snd.__proto__ = fst;"
5164 "var result1 = 0;"
5165 "for (var i = 0; i < 1000; i++) {"
5166 " result1 = snd.x;"
5167 "}"
5168 "fst.x = 239;"
5169 "var result = 0;"
5170 "for (var i = 0; i < 1000; i++) {"
5171 " result = snd.x;"
5172 "}"
5173 "result + result1",
5174 239 + 42);
5175}
5176
5177
5178// Test the case when we stored field into
5179// a stub, but interceptor produced value on its own.
5180THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
5181 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5182 "proto = new Object();"
5183 "o.__proto__ = proto;"
5184 "proto.x = 239;"
5185 "for (var i = 0; i < 1000; i++) {"
5186 " o.x;"
5187 // Now it should be ICed and keep a reference to x defined on proto
5188 "}"
5189 "var result = 0;"
5190 "for (var i = 0; i < 1000; i++) {"
5191 " result += o.x;"
5192 "}"
5193 "result;",
5194 42 * 1000);
5195}
5196
5197
5198// Test the case when we stored field into
5199// a stub, but it got invalidated later on.
5200THREADED_TEST(InterceptorLoadICInvalidatedField) {
5201 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5202 "proto1 = new Object();"
5203 "proto2 = new Object();"
5204 "o.__proto__ = proto1;"
5205 "proto1.__proto__ = proto2;"
5206 "proto2.y = 239;"
5207 "for (var i = 0; i < 1000; i++) {"
5208 " o.y;"
5209 // Now it should be ICed and keep a reference to y defined on proto2
5210 "}"
5211 "proto1.y = 42;"
5212 "var result = 0;"
5213 "for (var i = 0; i < 1000; i++) {"
5214 " result += o.y;"
5215 "}"
5216 "result;",
5217 42 * 1000);
5218}
5219
5220
5221// Test the case when we stored field into
5222// a stub, but it got invalidated later on due to override on
5223// global object which is between interceptor and fields' holders.
5224THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
5225 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5226 "o.__proto__ = this;" // set a global to be a proto of o.
5227 "this.__proto__.y = 239;"
5228 "for (var i = 0; i < 10; i++) {"
5229 " if (o.y != 239) throw 'oops: ' + o.y;"
5230 // Now it should be ICed and keep a reference to y defined on field_holder.
5231 "}"
5232 "this.y = 42;" // Assign on a global.
5233 "var result = 0;"
5234 "for (var i = 0; i < 10; i++) {"
5235 " result += o.y;"
5236 "}"
5237 "result;",
5238 42 * 10);
5239}
5240
5241
5242static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
5243 ApiTestFuzzer::Fuzz();
5244 return v8_num(239);
5245}
5246
5247
5248static void SetOnThis(Local<String> name,
5249 Local<Value> value,
5250 const AccessorInfo& info) {
5251 info.This()->ForceSet(name, value);
5252}
5253
5254
5255THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
5256 v8::HandleScope scope;
5257 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5258 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5259 templ->SetAccessor(v8_str("y"), Return239);
5260 LocalContext context;
5261 context->Global()->Set(v8_str("o"), templ->NewInstance());
5262 v8::Handle<Value> value = CompileRun(
5263 "var result = 0;"
5264 "for (var i = 0; i < 7; i++) {"
5265 " result = o.y;"
5266 "}");
5267 CHECK_EQ(239, value->Int32Value());
5268}
5269
5270
5271THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
5272 v8::HandleScope scope;
5273 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5274 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5275 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5276 templ_p->SetAccessor(v8_str("y"), Return239);
5277
5278 LocalContext context;
5279 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5280 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5281
5282 v8::Handle<Value> value = CompileRun(
5283 "o.__proto__ = p;"
5284 "var result = 0;"
5285 "for (var i = 0; i < 7; i++) {"
5286 " result = o.x + o.y;"
5287 "}");
5288 CHECK_EQ(239 + 42, value->Int32Value());
5289}
5290
5291
5292THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
5293 v8::HandleScope scope;
5294 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5295 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5296 templ->SetAccessor(v8_str("y"), Return239);
5297
5298 LocalContext context;
5299 context->Global()->Set(v8_str("o"), templ->NewInstance());
5300
5301 v8::Handle<Value> value = CompileRun(
5302 "fst = new Object(); fst.__proto__ = o;"
5303 "snd = new Object(); snd.__proto__ = fst;"
5304 "var result1 = 0;"
5305 "for (var i = 0; i < 7; i++) {"
5306 " result1 = snd.x;"
5307 "}"
5308 "fst.x = 239;"
5309 "var result = 0;"
5310 "for (var i = 0; i < 7; i++) {"
5311 " result = snd.x;"
5312 "}"
5313 "result + result1");
5314 CHECK_EQ(239 + 42, value->Int32Value());
5315}
5316
5317
5318// Test the case when we stored callback into
5319// a stub, but interceptor produced value on its own.
5320THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
5321 v8::HandleScope scope;
5322 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5323 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5324 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5325 templ_p->SetAccessor(v8_str("y"), Return239);
5326
5327 LocalContext context;
5328 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5329 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5330
5331 v8::Handle<Value> value = CompileRun(
5332 "o.__proto__ = p;"
5333 "for (var i = 0; i < 7; i++) {"
5334 " o.x;"
5335 // Now it should be ICed and keep a reference to x defined on p
5336 "}"
5337 "var result = 0;"
5338 "for (var i = 0; i < 7; i++) {"
5339 " result += o.x;"
5340 "}"
5341 "result");
5342 CHECK_EQ(42 * 7, value->Int32Value());
5343}
5344
5345
5346// Test the case when we stored callback into
5347// a stub, but it got invalidated later on.
5348THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
5349 v8::HandleScope scope;
5350 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5351 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5352 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5353 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
5354
5355 LocalContext context;
5356 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5357 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5358
5359 v8::Handle<Value> value = CompileRun(
5360 "inbetween = new Object();"
5361 "o.__proto__ = inbetween;"
5362 "inbetween.__proto__ = p;"
5363 "for (var i = 0; i < 10; i++) {"
5364 " o.y;"
5365 // Now it should be ICed and keep a reference to y defined on p
5366 "}"
5367 "inbetween.y = 42;"
5368 "var result = 0;"
5369 "for (var i = 0; i < 10; i++) {"
5370 " result += o.y;"
5371 "}"
5372 "result");
5373 CHECK_EQ(42 * 10, value->Int32Value());
5374}
5375
5376
5377// Test the case when we stored callback into
5378// a stub, but it got invalidated later on due to override on
5379// global object which is between interceptor and callbacks' holders.
5380THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
5381 v8::HandleScope scope;
5382 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5383 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5384 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5385 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
5386
5387 LocalContext context;
5388 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5389 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5390
5391 v8::Handle<Value> value = CompileRun(
5392 "o.__proto__ = this;"
5393 "this.__proto__ = p;"
5394 "for (var i = 0; i < 10; i++) {"
5395 " if (o.y != 239) throw 'oops: ' + o.y;"
5396 // Now it should be ICed and keep a reference to y defined on p
5397 "}"
5398 "this.y = 42;"
5399 "var result = 0;"
5400 "for (var i = 0; i < 10; i++) {"
5401 " result += o.y;"
5402 "}"
5403 "result");
5404 CHECK_EQ(42 * 10, value->Int32Value());
5405}
5406
5407
5408static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
5409 const AccessorInfo& info) {
5410 ApiTestFuzzer::Fuzz();
5411 CHECK(v8_str("x")->Equals(name));
5412 return v8::Integer::New(0);
5413}
5414
5415
5416THREADED_TEST(InterceptorReturningZero) {
5417 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
5418 "o.x == undefined ? 1 : 0",
5419 0);
5420}
5421
5422
5423static v8::Handle<Value> InterceptorStoreICSetter(
5424 Local<String> key, Local<Value> value, const AccessorInfo&) {
5425 CHECK(v8_str("x")->Equals(key));
5426 CHECK_EQ(42, value->Int32Value());
5427 return value;
5428}
5429
5430
5431// This test should hit the store IC for the interceptor case.
5432THREADED_TEST(InterceptorStoreIC) {
5433 v8::HandleScope scope;
5434 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5435 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
5436 InterceptorStoreICSetter);
5437 LocalContext context;
5438 context->Global()->Set(v8_str("o"), templ->NewInstance());
5439 v8::Handle<Value> value = CompileRun(
5440 "for (var i = 0; i < 1000; i++) {"
5441 " o.x = 42;"
5442 "}");
5443}
5444
5445
5446THREADED_TEST(InterceptorStoreICWithNoSetter) {
5447 v8::HandleScope scope;
5448 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5449 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5450 LocalContext context;
5451 context->Global()->Set(v8_str("o"), templ->NewInstance());
5452 v8::Handle<Value> value = CompileRun(
5453 "for (var i = 0; i < 1000; i++) {"
5454 " o.y = 239;"
5455 "}"
5456 "42 + o.y");
5457 CHECK_EQ(239 + 42, value->Int32Value());
5458}
5459
5460
5461
5462
5463v8::Handle<Value> call_ic_function;
5464v8::Handle<Value> call_ic_function2;
5465v8::Handle<Value> call_ic_function3;
5466
5467static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
5468 const AccessorInfo& info) {
5469 ApiTestFuzzer::Fuzz();
5470 CHECK(v8_str("x")->Equals(name));
5471 return call_ic_function;
5472}
5473
5474
5475// This test should hit the call IC for the interceptor case.
5476THREADED_TEST(InterceptorCallIC) {
5477 v8::HandleScope scope;
5478 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5479 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
5480 LocalContext context;
5481 context->Global()->Set(v8_str("o"), templ->NewInstance());
5482 call_ic_function =
5483 v8_compile("function f(x) { return x + 1; }; f")->Run();
5484 v8::Handle<Value> value = CompileRun(
5485 "var result = 0;"
5486 "for (var i = 0; i < 1000; i++) {"
5487 " result = o.x(41);"
5488 "}");
5489 CHECK_EQ(42, value->Int32Value());
5490}
5491
5492
5493// This test checks that if interceptor doesn't provide
5494// a value, we can fetch regular value.
5495THREADED_TEST(InterceptorCallICSeesOthers) {
5496 v8::HandleScope scope;
5497 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5498 templ->SetNamedPropertyHandler(NoBlockGetterX);
5499 LocalContext context;
5500 context->Global()->Set(v8_str("o"), templ->NewInstance());
5501 v8::Handle<Value> value = CompileRun(
5502 "o.x = function f(x) { return x + 1; };"
5503 "var result = 0;"
5504 "for (var i = 0; i < 7; i++) {"
5505 " result = o.x(41);"
5506 "}");
5507 CHECK_EQ(42, value->Int32Value());
5508}
5509
5510
5511static v8::Handle<Value> call_ic_function4;
5512static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
5513 const AccessorInfo& info) {
5514 ApiTestFuzzer::Fuzz();
5515 CHECK(v8_str("x")->Equals(name));
5516 return call_ic_function4;
5517}
5518
5519
5520// This test checks that if interceptor provides a function,
5521// even if we cached shadowed variant, interceptor's function
5522// is invoked
5523THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
5524 v8::HandleScope scope;
5525 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5526 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
5527 LocalContext context;
5528 context->Global()->Set(v8_str("o"), templ->NewInstance());
5529 call_ic_function4 =
5530 v8_compile("function f(x) { return x - 1; }; f")->Run();
5531 v8::Handle<Value> value = CompileRun(
5532 "o.__proto__.x = function(x) { return x + 1; };"
5533 "var result = 0;"
5534 "for (var i = 0; i < 1000; i++) {"
5535 " result = o.x(42);"
5536 "}");
5537 CHECK_EQ(41, value->Int32Value());
5538}
5539
5540
5541// Test the case when we stored cacheable lookup into
5542// a stub, but it got invalidated later on
5543THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
5544 v8::HandleScope scope;
5545 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5546 templ->SetNamedPropertyHandler(NoBlockGetterX);
5547 LocalContext context;
5548 context->Global()->Set(v8_str("o"), templ->NewInstance());
5549 v8::Handle<Value> value = CompileRun(
5550 "proto1 = new Object();"
5551 "proto2 = new Object();"
5552 "o.__proto__ = proto1;"
5553 "proto1.__proto__ = proto2;"
5554 "proto2.y = function(x) { return x + 1; };"
5555 // Invoke it many times to compile a stub
5556 "for (var i = 0; i < 7; i++) {"
5557 " o.y(42);"
5558 "}"
5559 "proto1.y = function(x) { return x - 1; };"
5560 "var result = 0;"
5561 "for (var i = 0; i < 7; i++) {"
5562 " result += o.y(42);"
5563 "}");
5564 CHECK_EQ(41 * 7, value->Int32Value());
5565}
5566
5567
5568static v8::Handle<Value> call_ic_function5;
5569static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
5570 const AccessorInfo& info) {
5571 ApiTestFuzzer::Fuzz();
5572 if (v8_str("x")->Equals(name))
5573 return call_ic_function5;
5574 else
5575 return Local<Value>();
5576}
5577
5578
5579// This test checks that if interceptor doesn't provide a function,
5580// cached constant function is used
5581THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
5582 v8::HandleScope scope;
5583 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5584 templ->SetNamedPropertyHandler(NoBlockGetterX);
5585 LocalContext context;
5586 context->Global()->Set(v8_str("o"), templ->NewInstance());
5587 v8::Handle<Value> value = CompileRun(
5588 "function inc(x) { return x + 1; };"
5589 "inc(1);"
5590 "o.x = inc;"
5591 "var result = 0;"
5592 "for (var i = 0; i < 1000; i++) {"
5593 " result = o.x(42);"
5594 "}");
5595 CHECK_EQ(43, value->Int32Value());
5596}
5597
5598
5599// This test checks that if interceptor provides a function,
5600// even if we cached constant function, interceptor's function
5601// is invoked
5602THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
5603 v8::HandleScope scope;
5604 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5605 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
5606 LocalContext context;
5607 context->Global()->Set(v8_str("o"), templ->NewInstance());
5608 call_ic_function5 =
5609 v8_compile("function f(x) { return x - 1; }; f")->Run();
5610 v8::Handle<Value> value = CompileRun(
5611 "function inc(x) { return x + 1; };"
5612 "inc(1);"
5613 "o.x = inc;"
5614 "var result = 0;"
5615 "for (var i = 0; i < 1000; i++) {"
5616 " result = o.x(42);"
5617 "}");
5618 CHECK_EQ(41, value->Int32Value());
5619}
5620
5621
5622// Test the case when we stored constant function into
5623// a stub, but it got invalidated later on
5624THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
5625 v8::HandleScope scope;
5626 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5627 templ->SetNamedPropertyHandler(NoBlockGetterX);
5628 LocalContext context;
5629 context->Global()->Set(v8_str("o"), templ->NewInstance());
5630 v8::Handle<Value> value = CompileRun(
5631 "function inc(x) { return x + 1; };"
5632 "inc(1);"
5633 "proto1 = new Object();"
5634 "proto2 = new Object();"
5635 "o.__proto__ = proto1;"
5636 "proto1.__proto__ = proto2;"
5637 "proto2.y = inc;"
5638 // Invoke it many times to compile a stub
5639 "for (var i = 0; i < 7; i++) {"
5640 " o.y(42);"
5641 "}"
5642 "proto1.y = function(x) { return x - 1; };"
5643 "var result = 0;"
5644 "for (var i = 0; i < 7; i++) {"
5645 " result += o.y(42);"
5646 "}");
5647 CHECK_EQ(41 * 7, value->Int32Value());
5648}
5649
5650
5651// Test the case when we stored constant function into
5652// a stub, but it got invalidated later on due to override on
5653// global object which is between interceptor and constant function' holders.
5654THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
5655 v8::HandleScope scope;
5656 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5657 templ->SetNamedPropertyHandler(NoBlockGetterX);
5658 LocalContext context;
5659 context->Global()->Set(v8_str("o"), templ->NewInstance());
5660 v8::Handle<Value> value = CompileRun(
5661 "function inc(x) { return x + 1; };"
5662 "inc(1);"
5663 "o.__proto__ = this;"
5664 "this.__proto__.y = inc;"
5665 // Invoke it many times to compile a stub
5666 "for (var i = 0; i < 7; i++) {"
5667 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
5668 "}"
5669 "this.y = function(x) { return x - 1; };"
5670 "var result = 0;"
5671 "for (var i = 0; i < 7; i++) {"
5672 " result += o.y(42);"
5673 "}");
5674 CHECK_EQ(41 * 7, value->Int32Value());
5675}
5676
5677
5678static int interceptor_call_count = 0;
5679
5680static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
5681 const AccessorInfo& info) {
5682 ApiTestFuzzer::Fuzz();
5683 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
5684 return call_ic_function2;
5685 }
5686 return v8::Handle<Value>();
5687}
5688
5689
5690// This test should hit load and call ICs for the interceptor case.
5691// Once in a while, the interceptor will reply that a property was not
5692// found in which case we should get a reference error.
5693THREADED_TEST(InterceptorICReferenceErrors) {
5694 v8::HandleScope scope;
5695 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5696 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
5697 LocalContext context(0, templ, v8::Handle<Value>());
5698 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
5699 v8::Handle<Value> value = CompileRun(
5700 "function f() {"
5701 " for (var i = 0; i < 1000; i++) {"
5702 " try { x; } catch(e) { return true; }"
5703 " }"
5704 " return false;"
5705 "};"
5706 "f();");
5707 CHECK_EQ(true, value->BooleanValue());
5708 interceptor_call_count = 0;
5709 value = CompileRun(
5710 "function g() {"
5711 " for (var i = 0; i < 1000; i++) {"
5712 " try { x(42); } catch(e) { return true; }"
5713 " }"
5714 " return false;"
5715 "};"
5716 "g();");
5717 CHECK_EQ(true, value->BooleanValue());
5718}
5719
5720
5721static int interceptor_ic_exception_get_count = 0;
5722
5723static v8::Handle<Value> InterceptorICExceptionGetter(
5724 Local<String> name,
5725 const AccessorInfo& info) {
5726 ApiTestFuzzer::Fuzz();
5727 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
5728 return call_ic_function3;
5729 }
5730 if (interceptor_ic_exception_get_count == 20) {
5731 return v8::ThrowException(v8_num(42));
5732 }
5733 // Do not handle get for properties other than x.
5734 return v8::Handle<Value>();
5735}
5736
5737// Test interceptor load/call IC where the interceptor throws an
5738// exception once in a while.
5739THREADED_TEST(InterceptorICGetterExceptions) {
5740 interceptor_ic_exception_get_count = 0;
5741 v8::HandleScope scope;
5742 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5743 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
5744 LocalContext context(0, templ, v8::Handle<Value>());
5745 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
5746 v8::Handle<Value> value = CompileRun(
5747 "function f() {"
5748 " for (var i = 0; i < 100; i++) {"
5749 " try { x; } catch(e) { return true; }"
5750 " }"
5751 " return false;"
5752 "};"
5753 "f();");
5754 CHECK_EQ(true, value->BooleanValue());
5755 interceptor_ic_exception_get_count = 0;
5756 value = CompileRun(
5757 "function f() {"
5758 " for (var i = 0; i < 100; i++) {"
5759 " try { x(42); } catch(e) { return true; }"
5760 " }"
5761 " return false;"
5762 "};"
5763 "f();");
5764 CHECK_EQ(true, value->BooleanValue());
5765}
5766
5767
5768static int interceptor_ic_exception_set_count = 0;
5769
5770static v8::Handle<Value> InterceptorICExceptionSetter(
5771 Local<String> key, Local<Value> value, const AccessorInfo&) {
5772 ApiTestFuzzer::Fuzz();
5773 if (++interceptor_ic_exception_set_count > 20) {
5774 return v8::ThrowException(v8_num(42));
5775 }
5776 // Do not actually handle setting.
5777 return v8::Handle<Value>();
5778}
5779
5780// Test interceptor store IC where the interceptor throws an exception
5781// once in a while.
5782THREADED_TEST(InterceptorICSetterExceptions) {
5783 interceptor_ic_exception_set_count = 0;
5784 v8::HandleScope scope;
5785 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5786 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
5787 LocalContext context(0, templ, v8::Handle<Value>());
5788 v8::Handle<Value> value = CompileRun(
5789 "function f() {"
5790 " for (var i = 0; i < 100; i++) {"
5791 " try { x = 42; } catch(e) { return true; }"
5792 " }"
5793 " return false;"
5794 "};"
5795 "f();");
5796 CHECK_EQ(true, value->BooleanValue());
5797}
5798
5799
5800// Test that we ignore null interceptors.
5801THREADED_TEST(NullNamedInterceptor) {
5802 v8::HandleScope scope;
5803 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5804 templ->SetNamedPropertyHandler(0);
5805 LocalContext context;
5806 templ->Set("x", v8_num(42));
5807 v8::Handle<v8::Object> obj = templ->NewInstance();
5808 context->Global()->Set(v8_str("obj"), obj);
5809 v8::Handle<Value> value = CompileRun("obj.x");
5810 CHECK(value->IsInt32());
5811 CHECK_EQ(42, value->Int32Value());
5812}
5813
5814
5815// Test that we ignore null interceptors.
5816THREADED_TEST(NullIndexedInterceptor) {
5817 v8::HandleScope scope;
5818 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5819 templ->SetIndexedPropertyHandler(0);
5820 LocalContext context;
5821 templ->Set("42", v8_num(42));
5822 v8::Handle<v8::Object> obj = templ->NewInstance();
5823 context->Global()->Set(v8_str("obj"), obj);
5824 v8::Handle<Value> value = CompileRun("obj[42]");
5825 CHECK(value->IsInt32());
5826 CHECK_EQ(42, value->Int32Value());
5827}
5828
5829
5830static v8::Handle<Value> ParentGetter(Local<String> name,
5831 const AccessorInfo& info) {
5832 ApiTestFuzzer::Fuzz();
5833 return v8_num(1);
5834}
5835
5836
5837static v8::Handle<Value> ChildGetter(Local<String> name,
5838 const AccessorInfo& info) {
5839 ApiTestFuzzer::Fuzz();
5840 return v8_num(42);
5841}
5842
5843
5844THREADED_TEST(Overriding) {
5845 v8::HandleScope scope;
5846 LocalContext context;
5847
5848 // Parent template.
5849 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
5850 Local<ObjectTemplate> parent_instance_templ =
5851 parent_templ->InstanceTemplate();
5852 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
5853
5854 // Template that inherits from the parent template.
5855 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
5856 Local<ObjectTemplate> child_instance_templ =
5857 child_templ->InstanceTemplate();
5858 child_templ->Inherit(parent_templ);
5859 // Override 'f'. The child version of 'f' should get called for child
5860 // instances.
5861 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
5862 // Add 'g' twice. The 'g' added last should get called for instances.
5863 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
5864 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
5865
5866 // Add 'h' as an accessor to the proto template with ReadOnly attributes
5867 // so 'h' can be shadowed on the instance object.
5868 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
5869 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
5870 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
5871
5872 // Add 'i' as an accessor to the instance template with ReadOnly attributes
5873 // but the attribute does not have effect because it is duplicated with
5874 // NULL setter.
5875 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
5876 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
5877
5878
5879
5880 // Instantiate the child template.
5881 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
5882
5883 // Check that the child function overrides the parent one.
5884 context->Global()->Set(v8_str("o"), instance);
5885 Local<Value> value = v8_compile("o.f")->Run();
5886 // Check that the 'g' that was added last is hit.
5887 CHECK_EQ(42, value->Int32Value());
5888 value = v8_compile("o.g")->Run();
5889 CHECK_EQ(42, value->Int32Value());
5890
5891 // Check 'h' can be shadowed.
5892 value = v8_compile("o.h = 3; o.h")->Run();
5893 CHECK_EQ(3, value->Int32Value());
5894
5895 // Check 'i' is cannot be shadowed or changed.
5896 value = v8_compile("o.i = 3; o.i")->Run();
5897 CHECK_EQ(42, value->Int32Value());
5898}
5899
5900
5901static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
5902 ApiTestFuzzer::Fuzz();
5903 if (args.IsConstructCall()) {
5904 return v8::Boolean::New(true);
5905 }
5906 return v8::Boolean::New(false);
5907}
5908
5909
5910THREADED_TEST(IsConstructCall) {
5911 v8::HandleScope scope;
5912
5913 // Function template with call handler.
5914 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5915 templ->SetCallHandler(IsConstructHandler);
5916
5917 LocalContext context;
5918
5919 context->Global()->Set(v8_str("f"), templ->GetFunction());
5920 Local<Value> value = v8_compile("f()")->Run();
5921 CHECK(!value->BooleanValue());
5922 value = v8_compile("new f()")->Run();
5923 CHECK(value->BooleanValue());
5924}
5925
5926
5927THREADED_TEST(ObjectProtoToString) {
5928 v8::HandleScope scope;
5929 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5930 templ->SetClassName(v8_str("MyClass"));
5931
5932 LocalContext context;
5933
5934 Local<String> customized_tostring = v8_str("customized toString");
5935
5936 // Replace Object.prototype.toString
5937 v8_compile("Object.prototype.toString = function() {"
5938 " return 'customized toString';"
5939 "}")->Run();
5940
5941 // Normal ToString call should call replaced Object.prototype.toString
5942 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
5943 Local<String> value = instance->ToString();
5944 CHECK(value->IsString() && value->Equals(customized_tostring));
5945
5946 // ObjectProtoToString should not call replace toString function.
5947 value = instance->ObjectProtoToString();
5948 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
5949
5950 // Check global
5951 value = context->Global()->ObjectProtoToString();
5952 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
5953
5954 // Check ordinary object
5955 Local<Value> object = v8_compile("new Object()")->Run();
5956 value = Local<v8::Object>::Cast(object)->ObjectProtoToString();
5957 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
5958}
5959
5960
5961bool ApiTestFuzzer::fuzzing_ = false;
5962v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_=
5963 v8::internal::OS::CreateSemaphore(0);
5964int ApiTestFuzzer::active_tests_;
5965int ApiTestFuzzer::tests_being_run_;
5966int ApiTestFuzzer::current_;
5967
5968
5969// We are in a callback and want to switch to another thread (if we
5970// are currently running the thread fuzzing test).
5971void ApiTestFuzzer::Fuzz() {
5972 if (!fuzzing_) return;
5973 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
5974 test->ContextSwitch();
5975}
5976
5977
5978// Let the next thread go. Since it is also waiting on the V8 lock it may
5979// not start immediately.
5980bool ApiTestFuzzer::NextThread() {
5981 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00005982 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00005983 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00005984 if (kLogThreading)
5985 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00005986 return false;
5987 }
Steve Blockd0582a62009-12-15 09:54:21 +00005988 if (kLogThreading) {
5989 printf("Switch from %s to %s\n",
5990 test_name,
5991 RegisterThreadedTest::nth(test_position)->name());
5992 }
Steve Blocka7e24c12009-10-30 11:49:00 +00005993 current_ = test_position;
5994 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
5995 return true;
5996}
5997
5998
5999void ApiTestFuzzer::Run() {
6000 // When it is our turn...
6001 gate_->Wait();
6002 {
6003 // ... get the V8 lock and start running the test.
6004 v8::Locker locker;
6005 CallTest();
6006 }
6007 // This test finished.
6008 active_ = false;
6009 active_tests_--;
6010 // If it was the last then signal that fact.
6011 if (active_tests_ == 0) {
6012 all_tests_done_->Signal();
6013 } else {
6014 // Otherwise select a new test and start that.
6015 NextThread();
6016 }
6017}
6018
6019
6020static unsigned linear_congruential_generator;
6021
6022
6023void ApiTestFuzzer::Setup(PartOfTest part) {
6024 linear_congruential_generator = i::FLAG_testing_prng_seed;
6025 fuzzing_ = true;
6026 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
6027 int end = (part == FIRST_PART)
6028 ? (RegisterThreadedTest::count() >> 1)
6029 : RegisterThreadedTest::count();
6030 active_tests_ = tests_being_run_ = end - start;
6031 for (int i = 0; i < tests_being_run_; i++) {
6032 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
6033 }
6034 for (int i = 0; i < active_tests_; i++) {
6035 RegisterThreadedTest::nth(i)->fuzzer_->Start();
6036 }
6037}
6038
6039
6040static void CallTestNumber(int test_number) {
6041 (RegisterThreadedTest::nth(test_number)->callback())();
6042}
6043
6044
6045void ApiTestFuzzer::RunAllTests() {
6046 // Set off the first test.
6047 current_ = -1;
6048 NextThread();
6049 // Wait till they are all done.
6050 all_tests_done_->Wait();
6051}
6052
6053
6054int ApiTestFuzzer::GetNextTestNumber() {
6055 int next_test;
6056 do {
6057 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
6058 linear_congruential_generator *= 1664525u;
6059 linear_congruential_generator += 1013904223u;
6060 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
6061 return next_test;
6062}
6063
6064
6065void ApiTestFuzzer::ContextSwitch() {
6066 // If the new thread is the same as the current thread there is nothing to do.
6067 if (NextThread()) {
6068 // Now it can start.
6069 v8::Unlocker unlocker;
6070 // Wait till someone starts us again.
6071 gate_->Wait();
6072 // And we're off.
6073 }
6074}
6075
6076
6077void ApiTestFuzzer::TearDown() {
6078 fuzzing_ = false;
6079 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
6080 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
6081 if (fuzzer != NULL) fuzzer->Join();
6082 }
6083}
6084
6085
6086// Lets not be needlessly self-referential.
6087TEST(Threading) {
6088 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
6089 ApiTestFuzzer::RunAllTests();
6090 ApiTestFuzzer::TearDown();
6091}
6092
6093TEST(Threading2) {
6094 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
6095 ApiTestFuzzer::RunAllTests();
6096 ApiTestFuzzer::TearDown();
6097}
6098
6099
6100void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00006101 if (kLogThreading)
6102 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00006103 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00006104 if (kLogThreading)
6105 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00006106}
6107
6108
6109static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
6110 CHECK(v8::Locker::IsLocked());
6111 ApiTestFuzzer::Fuzz();
6112 v8::Unlocker unlocker;
6113 const char* code = "throw 7;";
6114 {
6115 v8::Locker nested_locker;
6116 v8::HandleScope scope;
6117 v8::Handle<Value> exception;
6118 { v8::TryCatch try_catch;
6119 v8::Handle<Value> value = CompileRun(code);
6120 CHECK(value.IsEmpty());
6121 CHECK(try_catch.HasCaught());
6122 // Make sure to wrap the exception in a new handle because
6123 // the handle returned from the TryCatch is destroyed
6124 // when the TryCatch is destroyed.
6125 exception = Local<Value>::New(try_catch.Exception());
6126 }
6127 return v8::ThrowException(exception);
6128 }
6129}
6130
6131
6132static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
6133 CHECK(v8::Locker::IsLocked());
6134 ApiTestFuzzer::Fuzz();
6135 v8::Unlocker unlocker;
6136 const char* code = "throw 7;";
6137 {
6138 v8::Locker nested_locker;
6139 v8::HandleScope scope;
6140 v8::Handle<Value> value = CompileRun(code);
6141 CHECK(value.IsEmpty());
6142 return v8_str("foo");
6143 }
6144}
6145
6146
6147// These are locking tests that don't need to be run again
6148// as part of the locking aggregation tests.
6149TEST(NestedLockers) {
6150 v8::Locker locker;
6151 CHECK(v8::Locker::IsLocked());
6152 v8::HandleScope scope;
6153 LocalContext env;
6154 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
6155 Local<Function> fun = fun_templ->GetFunction();
6156 env->Global()->Set(v8_str("throw_in_js"), fun);
6157 Local<Script> script = v8_compile("(function () {"
6158 " try {"
6159 " throw_in_js();"
6160 " return 42;"
6161 " } catch (e) {"
6162 " return e * 13;"
6163 " }"
6164 "})();");
6165 CHECK_EQ(91, script->Run()->Int32Value());
6166}
6167
6168
6169// These are locking tests that don't need to be run again
6170// as part of the locking aggregation tests.
6171TEST(NestedLockersNoTryCatch) {
6172 v8::Locker locker;
6173 v8::HandleScope scope;
6174 LocalContext env;
6175 Local<v8::FunctionTemplate> fun_templ =
6176 v8::FunctionTemplate::New(ThrowInJSNoCatch);
6177 Local<Function> fun = fun_templ->GetFunction();
6178 env->Global()->Set(v8_str("throw_in_js"), fun);
6179 Local<Script> script = v8_compile("(function () {"
6180 " try {"
6181 " throw_in_js();"
6182 " return 42;"
6183 " } catch (e) {"
6184 " return e * 13;"
6185 " }"
6186 "})();");
6187 CHECK_EQ(91, script->Run()->Int32Value());
6188}
6189
6190
6191THREADED_TEST(RecursiveLocking) {
6192 v8::Locker locker;
6193 {
6194 v8::Locker locker2;
6195 CHECK(v8::Locker::IsLocked());
6196 }
6197}
6198
6199
6200static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
6201 ApiTestFuzzer::Fuzz();
6202 v8::Unlocker unlocker;
6203 return v8::Undefined();
6204}
6205
6206
6207THREADED_TEST(LockUnlockLock) {
6208 {
6209 v8::Locker locker;
6210 v8::HandleScope scope;
6211 LocalContext env;
6212 Local<v8::FunctionTemplate> fun_templ =
6213 v8::FunctionTemplate::New(UnlockForAMoment);
6214 Local<Function> fun = fun_templ->GetFunction();
6215 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
6216 Local<Script> script = v8_compile("(function () {"
6217 " unlock_for_a_moment();"
6218 " return 42;"
6219 "})();");
6220 CHECK_EQ(42, script->Run()->Int32Value());
6221 }
6222 {
6223 v8::Locker locker;
6224 v8::HandleScope scope;
6225 LocalContext env;
6226 Local<v8::FunctionTemplate> fun_templ =
6227 v8::FunctionTemplate::New(UnlockForAMoment);
6228 Local<Function> fun = fun_templ->GetFunction();
6229 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
6230 Local<Script> script = v8_compile("(function () {"
6231 " unlock_for_a_moment();"
6232 " return 42;"
6233 "})();");
6234 CHECK_EQ(42, script->Run()->Int32Value());
6235 }
6236}
6237
6238
Leon Clarked91b9f72010-01-27 17:25:45 +00006239static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00006240 int count = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +00006241 v8::internal::HeapIterator it;
6242 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
6243 if (object->IsJSGlobalObject()) count++;
6244 return count;
6245}
6246
6247
6248static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00006249 // We need to collect all garbage twice to be sure that everything
6250 // has been collected. This is because inline caches are cleared in
6251 // the first garbage collection but some of the maps have already
6252 // been marked at that point. Therefore some of the maps are not
6253 // collected until the second garbage collection.
6254 v8::internal::Heap::CollectAllGarbage(false);
6255 v8::internal::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00006256 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00006257#ifdef DEBUG
6258 if (count > 0) v8::internal::Heap::TracePathToGlobal();
6259#endif
6260 return count;
6261}
6262
6263
6264TEST(DontLeakGlobalObjects) {
6265 // Regression test for issues 1139850 and 1174891.
6266
6267 v8::V8::Initialize();
6268
6269 int count = GetSurvivingGlobalObjectsCount();
6270
6271 for (int i = 0; i < 5; i++) {
6272 { v8::HandleScope scope;
6273 LocalContext context;
6274 }
6275 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6276
6277 { v8::HandleScope scope;
6278 LocalContext context;
6279 v8_compile("Date")->Run();
6280 }
6281 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6282
6283 { v8::HandleScope scope;
6284 LocalContext context;
6285 v8_compile("/aaa/")->Run();
6286 }
6287 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6288
6289 { v8::HandleScope scope;
6290 const char* extension_list[] = { "v8/gc" };
6291 v8::ExtensionConfiguration extensions(1, extension_list);
6292 LocalContext context(&extensions);
6293 v8_compile("gc();")->Run();
6294 }
6295 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
6296 }
6297}
6298
6299
6300v8::Persistent<v8::Object> some_object;
6301v8::Persistent<v8::Object> bad_handle;
6302
6303void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
6304 v8::HandleScope scope;
6305 bad_handle = v8::Persistent<v8::Object>::New(some_object);
6306}
6307
6308
6309THREADED_TEST(NewPersistentHandleFromWeakCallback) {
6310 LocalContext context;
6311
6312 v8::Persistent<v8::Object> handle1, handle2;
6313 {
6314 v8::HandleScope scope;
6315 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
6316 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
6317 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
6318 }
6319 // Note: order is implementation dependent alas: currently
6320 // global handle nodes are processed by PostGarbageCollectionProcessing
6321 // in reverse allocation order, so if second allocated handle is deleted,
6322 // weak callback of the first handle would be able to 'reallocate' it.
6323 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
6324 handle2.Dispose();
6325 i::Heap::CollectAllGarbage(false);
6326}
6327
6328
6329v8::Persistent<v8::Object> to_be_disposed;
6330
6331void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
6332 to_be_disposed.Dispose();
6333 i::Heap::CollectAllGarbage(false);
6334}
6335
6336
6337THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
6338 LocalContext context;
6339
6340 v8::Persistent<v8::Object> handle1, handle2;
6341 {
6342 v8::HandleScope scope;
6343 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
6344 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
6345 }
6346 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
6347 to_be_disposed = handle2;
6348 i::Heap::CollectAllGarbage(false);
6349}
6350
Steve Blockd0582a62009-12-15 09:54:21 +00006351void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
6352 handle.Dispose();
6353}
6354
6355void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
6356 v8::HandleScope scope;
6357 v8::Persistent<v8::Object>::New(v8::Object::New());
6358}
6359
6360
6361THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
6362 LocalContext context;
6363
6364 v8::Persistent<v8::Object> handle1, handle2, handle3;
6365 {
6366 v8::HandleScope scope;
6367 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
6368 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
6369 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
6370 }
6371 handle2.MakeWeak(NULL, DisposingCallback);
6372 handle3.MakeWeak(NULL, HandleCreatingCallback);
6373 i::Heap::CollectAllGarbage(false);
6374}
6375
Steve Blocka7e24c12009-10-30 11:49:00 +00006376
6377THREADED_TEST(CheckForCrossContextObjectLiterals) {
6378 v8::V8::Initialize();
6379
6380 const int nof = 2;
6381 const char* sources[nof] = {
6382 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
6383 "Object()"
6384 };
6385
6386 for (int i = 0; i < nof; i++) {
6387 const char* source = sources[i];
6388 { v8::HandleScope scope;
6389 LocalContext context;
6390 CompileRun(source);
6391 }
6392 { v8::HandleScope scope;
6393 LocalContext context;
6394 CompileRun(source);
6395 }
6396 }
6397}
6398
6399
6400static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
6401 v8::HandleScope inner;
6402 env->Enter();
6403 v8::Handle<Value> three = v8_num(3);
6404 v8::Handle<Value> value = inner.Close(three);
6405 env->Exit();
6406 return value;
6407}
6408
6409
6410THREADED_TEST(NestedHandleScopeAndContexts) {
6411 v8::HandleScope outer;
6412 v8::Persistent<Context> env = Context::New();
6413 env->Enter();
6414 v8::Handle<Value> value = NestedScope(env);
6415 v8::Handle<String> str = value->ToString();
6416 env->Exit();
6417 env.Dispose();
6418}
6419
6420
6421THREADED_TEST(ExternalAllocatedMemory) {
6422 v8::HandleScope outer;
6423 v8::Persistent<Context> env = Context::New();
6424 const int kSize = 1024*1024;
6425 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
6426 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
6427}
6428
6429
6430THREADED_TEST(DisposeEnteredContext) {
6431 v8::HandleScope scope;
6432 LocalContext outer;
6433 { v8::Persistent<v8::Context> inner = v8::Context::New();
6434 inner->Enter();
6435 inner.Dispose();
6436 inner.Clear();
6437 inner->Exit();
6438 }
6439}
6440
6441
6442// Regression test for issue 54, object templates with internal fields
6443// but no accessors or interceptors did not get their internal field
6444// count set on instances.
6445THREADED_TEST(Regress54) {
6446 v8::HandleScope outer;
6447 LocalContext context;
6448 static v8::Persistent<v8::ObjectTemplate> templ;
6449 if (templ.IsEmpty()) {
6450 v8::HandleScope inner;
6451 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
6452 local->SetInternalFieldCount(1);
6453 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
6454 }
6455 v8::Handle<v8::Object> result = templ->NewInstance();
6456 CHECK_EQ(1, result->InternalFieldCount());
6457}
6458
6459
6460// If part of the threaded tests, this test makes ThreadingTest fail
6461// on mac.
6462TEST(CatchStackOverflow) {
6463 v8::HandleScope scope;
6464 LocalContext context;
6465 v8::TryCatch try_catch;
6466 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
6467 "function f() {"
6468 " return f();"
6469 "}"
6470 ""
6471 "f();"));
6472 v8::Handle<v8::Value> result = script->Run();
6473 CHECK(result.IsEmpty());
6474}
6475
6476
6477static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
6478 const char* resource_name,
6479 int line_offset) {
6480 v8::HandleScope scope;
6481 v8::TryCatch try_catch;
6482 v8::Handle<v8::Value> result = script->Run();
6483 CHECK(result.IsEmpty());
6484 CHECK(try_catch.HasCaught());
6485 v8::Handle<v8::Message> message = try_catch.Message();
6486 CHECK(!message.IsEmpty());
6487 CHECK_EQ(10 + line_offset, message->GetLineNumber());
6488 CHECK_EQ(91, message->GetStartPosition());
6489 CHECK_EQ(92, message->GetEndPosition());
6490 CHECK_EQ(2, message->GetStartColumn());
6491 CHECK_EQ(3, message->GetEndColumn());
6492 v8::String::AsciiValue line(message->GetSourceLine());
6493 CHECK_EQ(" throw 'nirk';", *line);
6494 v8::String::AsciiValue name(message->GetScriptResourceName());
6495 CHECK_EQ(resource_name, *name);
6496}
6497
6498
6499THREADED_TEST(TryCatchSourceInfo) {
6500 v8::HandleScope scope;
6501 LocalContext context;
6502 v8::Handle<v8::String> source = v8::String::New(
6503 "function Foo() {\n"
6504 " return Bar();\n"
6505 "}\n"
6506 "\n"
6507 "function Bar() {\n"
6508 " return Baz();\n"
6509 "}\n"
6510 "\n"
6511 "function Baz() {\n"
6512 " throw 'nirk';\n"
6513 "}\n"
6514 "\n"
6515 "Foo();\n");
6516
6517 const char* resource_name;
6518 v8::Handle<v8::Script> script;
6519 resource_name = "test.js";
6520 script = v8::Script::Compile(source, v8::String::New(resource_name));
6521 CheckTryCatchSourceInfo(script, resource_name, 0);
6522
6523 resource_name = "test1.js";
6524 v8::ScriptOrigin origin1(v8::String::New(resource_name));
6525 script = v8::Script::Compile(source, &origin1);
6526 CheckTryCatchSourceInfo(script, resource_name, 0);
6527
6528 resource_name = "test2.js";
6529 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
6530 script = v8::Script::Compile(source, &origin2);
6531 CheckTryCatchSourceInfo(script, resource_name, 7);
6532}
6533
6534
6535THREADED_TEST(CompilationCache) {
6536 v8::HandleScope scope;
6537 LocalContext context;
6538 v8::Handle<v8::String> source0 = v8::String::New("1234");
6539 v8::Handle<v8::String> source1 = v8::String::New("1234");
6540 v8::Handle<v8::Script> script0 =
6541 v8::Script::Compile(source0, v8::String::New("test.js"));
6542 v8::Handle<v8::Script> script1 =
6543 v8::Script::Compile(source1, v8::String::New("test.js"));
6544 v8::Handle<v8::Script> script2 =
6545 v8::Script::Compile(source0); // different origin
6546 CHECK_EQ(1234, script0->Run()->Int32Value());
6547 CHECK_EQ(1234, script1->Run()->Int32Value());
6548 CHECK_EQ(1234, script2->Run()->Int32Value());
6549}
6550
6551
6552static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
6553 ApiTestFuzzer::Fuzz();
6554 return v8_num(42);
6555}
6556
6557
6558THREADED_TEST(CallbackFunctionName) {
6559 v8::HandleScope scope;
6560 LocalContext context;
6561 Local<ObjectTemplate> t = ObjectTemplate::New();
6562 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
6563 context->Global()->Set(v8_str("obj"), t->NewInstance());
6564 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
6565 CHECK(value->IsString());
6566 v8::String::AsciiValue name(value);
6567 CHECK_EQ("asdf", *name);
6568}
6569
6570
6571THREADED_TEST(DateAccess) {
6572 v8::HandleScope scope;
6573 LocalContext context;
6574 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
6575 CHECK(date->IsDate());
6576 CHECK_EQ(1224744689038.0, v8::Handle<v8::Date>::Cast(date)->NumberValue());
6577}
6578
6579
6580void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
6581 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
6582 v8::Handle<v8::Array> props = obj->GetPropertyNames();
6583 CHECK_EQ(elmc, props->Length());
6584 for (int i = 0; i < elmc; i++) {
6585 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
6586 CHECK_EQ(elmv[i], *elm);
6587 }
6588}
6589
6590
6591THREADED_TEST(PropertyEnumeration) {
6592 v8::HandleScope scope;
6593 LocalContext context;
6594 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
6595 "var result = [];"
6596 "result[0] = {};"
6597 "result[1] = {a: 1, b: 2};"
6598 "result[2] = [1, 2, 3];"
6599 "var proto = {x: 1, y: 2, z: 3};"
6600 "var x = { __proto__: proto, w: 0, z: 1 };"
6601 "result[3] = x;"
6602 "result;"))->Run();
6603 v8::Handle<v8::Array> elms = v8::Handle<v8::Array>::Cast(obj);
6604 CHECK_EQ(4, elms->Length());
6605 int elmc0 = 0;
6606 const char** elmv0 = NULL;
6607 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
6608 int elmc1 = 2;
6609 const char* elmv1[] = {"a", "b"};
6610 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
6611 int elmc2 = 3;
6612 const char* elmv2[] = {"0", "1", "2"};
6613 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
6614 int elmc3 = 4;
6615 const char* elmv3[] = {"w", "z", "x", "y"};
6616 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
6617}
6618
6619
Steve Blocka7e24c12009-10-30 11:49:00 +00006620static bool NamedSetAccessBlocker(Local<v8::Object> obj,
6621 Local<Value> name,
6622 v8::AccessType type,
6623 Local<Value> data) {
6624 return type != v8::ACCESS_SET;
6625}
6626
6627
6628static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
6629 uint32_t key,
6630 v8::AccessType type,
6631 Local<Value> data) {
6632 return type != v8::ACCESS_SET;
6633}
6634
6635
6636THREADED_TEST(DisableAccessChecksWhileConfiguring) {
6637 v8::HandleScope scope;
6638 LocalContext context;
6639 Local<ObjectTemplate> templ = ObjectTemplate::New();
6640 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
6641 IndexedSetAccessBlocker);
6642 templ->Set(v8_str("x"), v8::True());
6643 Local<v8::Object> instance = templ->NewInstance();
6644 context->Global()->Set(v8_str("obj"), instance);
6645 Local<Value> value = CompileRun("obj.x");
6646 CHECK(value->BooleanValue());
6647}
6648
6649
6650static bool NamedGetAccessBlocker(Local<v8::Object> obj,
6651 Local<Value> name,
6652 v8::AccessType type,
6653 Local<Value> data) {
6654 return false;
6655}
6656
6657
6658static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
6659 uint32_t key,
6660 v8::AccessType type,
6661 Local<Value> data) {
6662 return false;
6663}
6664
6665
6666
6667THREADED_TEST(AccessChecksReenabledCorrectly) {
6668 v8::HandleScope scope;
6669 LocalContext context;
6670 Local<ObjectTemplate> templ = ObjectTemplate::New();
6671 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
6672 IndexedGetAccessBlocker);
6673 templ->Set(v8_str("a"), v8_str("a"));
6674 // Add more than 8 (see kMaxFastProperties) properties
6675 // so that the constructor will force copying map.
6676 // Cannot sprintf, gcc complains unsafety.
6677 char buf[4];
6678 for (char i = '0'; i <= '9' ; i++) {
6679 buf[0] = i;
6680 for (char j = '0'; j <= '9'; j++) {
6681 buf[1] = j;
6682 for (char k = '0'; k <= '9'; k++) {
6683 buf[2] = k;
6684 buf[3] = 0;
6685 templ->Set(v8_str(buf), v8::Number::New(k));
6686 }
6687 }
6688 }
6689
6690 Local<v8::Object> instance_1 = templ->NewInstance();
6691 context->Global()->Set(v8_str("obj_1"), instance_1);
6692
6693 Local<Value> value_1 = CompileRun("obj_1.a");
6694 CHECK(value_1->IsUndefined());
6695
6696 Local<v8::Object> instance_2 = templ->NewInstance();
6697 context->Global()->Set(v8_str("obj_2"), instance_2);
6698
6699 Local<Value> value_2 = CompileRun("obj_2.a");
6700 CHECK(value_2->IsUndefined());
6701}
6702
6703
6704// This tests that access check information remains on the global
6705// object template when creating contexts.
6706THREADED_TEST(AccessControlRepeatedContextCreation) {
6707 v8::HandleScope handle_scope;
6708 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
6709 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
6710 IndexedSetAccessBlocker);
6711 i::Handle<i::ObjectTemplateInfo> internal_template =
6712 v8::Utils::OpenHandle(*global_template);
6713 CHECK(!internal_template->constructor()->IsUndefined());
6714 i::Handle<i::FunctionTemplateInfo> constructor(
6715 i::FunctionTemplateInfo::cast(internal_template->constructor()));
6716 CHECK(!constructor->access_check_info()->IsUndefined());
6717 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
6718 CHECK(!constructor->access_check_info()->IsUndefined());
6719}
6720
6721
6722THREADED_TEST(TurnOnAccessCheck) {
6723 v8::HandleScope handle_scope;
6724
6725 // Create an environment with access check to the global object disabled by
6726 // default.
6727 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
6728 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
6729 IndexedGetAccessBlocker,
6730 v8::Handle<v8::Value>(),
6731 false);
6732 v8::Persistent<Context> context = Context::New(NULL, global_template);
6733 Context::Scope context_scope(context);
6734
6735 // Set up a property and a number of functions.
6736 context->Global()->Set(v8_str("a"), v8_num(1));
6737 CompileRun("function f1() {return a;}"
6738 "function f2() {return a;}"
6739 "function g1() {return h();}"
6740 "function g2() {return h();}"
6741 "function h() {return 1;}");
6742 Local<Function> f1 =
6743 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
6744 Local<Function> f2 =
6745 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
6746 Local<Function> g1 =
6747 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
6748 Local<Function> g2 =
6749 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
6750 Local<Function> h =
6751 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
6752
6753 // Get the global object.
6754 v8::Handle<v8::Object> global = context->Global();
6755
6756 // Call f1 one time and f2 a number of times. This will ensure that f1 still
6757 // uses the runtime system to retreive property a whereas f2 uses global load
6758 // inline cache.
6759 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
6760 for (int i = 0; i < 4; i++) {
6761 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
6762 }
6763
6764 // Same for g1 and g2.
6765 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
6766 for (int i = 0; i < 4; i++) {
6767 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
6768 }
6769
6770 // Detach the global and turn on access check.
6771 context->DetachGlobal();
6772 context->Global()->TurnOnAccessCheck();
6773
6774 // Failing access check to property get results in undefined.
6775 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
6776 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
6777
6778 // Failing access check to function call results in exception.
6779 CHECK(g1->Call(global, 0, NULL).IsEmpty());
6780 CHECK(g2->Call(global, 0, NULL).IsEmpty());
6781
6782 // No failing access check when just returning a constant.
6783 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
6784}
6785
6786
6787// This test verifies that pre-compilation (aka preparsing) can be called
6788// without initializing the whole VM. Thus we cannot run this test in a
6789// multi-threaded setup.
6790TEST(PreCompile) {
6791 // TODO(155): This test would break without the initialization of V8. This is
6792 // a workaround for now to make this test not fail.
6793 v8::V8::Initialize();
6794 const char *script = "function foo(a) { return a+1; }";
Steve Blockd0582a62009-12-15 09:54:21 +00006795 v8::ScriptData *sd =
6796 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00006797 CHECK_NE(sd->Length(), 0);
6798 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00006799 CHECK(!sd->HasError());
6800 delete sd;
6801}
6802
6803
6804TEST(PreCompileWithError) {
6805 v8::V8::Initialize();
6806 const char *script = "function foo(a) { return 1 * * 2; }";
6807 v8::ScriptData *sd =
6808 v8::ScriptData::PreCompile(script, i::StrLength(script));
6809 CHECK(sd->HasError());
6810 delete sd;
6811}
6812
6813
6814TEST(Regress31661) {
6815 v8::V8::Initialize();
6816 const char *script = " The Definintive Guide";
6817 v8::ScriptData *sd =
6818 v8::ScriptData::PreCompile(script, i::StrLength(script));
6819 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00006820 delete sd;
6821}
6822
6823
6824// This tests that we do not allow dictionary load/call inline caches
6825// to use functions that have not yet been compiled. The potential
6826// problem of loading a function that has not yet been compiled can
6827// arise because we share code between contexts via the compilation
6828// cache.
6829THREADED_TEST(DictionaryICLoadedFunction) {
6830 v8::HandleScope scope;
6831 // Test LoadIC.
6832 for (int i = 0; i < 2; i++) {
6833 LocalContext context;
6834 context->Global()->Set(v8_str("tmp"), v8::True());
6835 context->Global()->Delete(v8_str("tmp"));
6836 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
6837 }
6838 // Test CallIC.
6839 for (int i = 0; i < 2; i++) {
6840 LocalContext context;
6841 context->Global()->Set(v8_str("tmp"), v8::True());
6842 context->Global()->Delete(v8_str("tmp"));
6843 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
6844 }
6845}
6846
6847
6848// Test that cross-context new calls use the context of the callee to
6849// create the new JavaScript object.
6850THREADED_TEST(CrossContextNew) {
6851 v8::HandleScope scope;
6852 v8::Persistent<Context> context0 = Context::New();
6853 v8::Persistent<Context> context1 = Context::New();
6854
6855 // Allow cross-domain access.
6856 Local<String> token = v8_str("<security token>");
6857 context0->SetSecurityToken(token);
6858 context1->SetSecurityToken(token);
6859
6860 // Set an 'x' property on the Object prototype and define a
6861 // constructor function in context0.
6862 context0->Enter();
6863 CompileRun("Object.prototype.x = 42; function C() {};");
6864 context0->Exit();
6865
6866 // Call the constructor function from context0 and check that the
6867 // result has the 'x' property.
6868 context1->Enter();
6869 context1->Global()->Set(v8_str("other"), context0->Global());
6870 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
6871 CHECK(value->IsInt32());
6872 CHECK_EQ(42, value->Int32Value());
6873 context1->Exit();
6874
6875 // Dispose the contexts to allow them to be garbage collected.
6876 context0.Dispose();
6877 context1.Dispose();
6878}
6879
6880
6881class RegExpInterruptTest {
6882 public:
6883 RegExpInterruptTest() : block_(NULL) {}
6884 ~RegExpInterruptTest() { delete block_; }
6885 void RunTest() {
6886 block_ = i::OS::CreateSemaphore(0);
6887 gc_count_ = 0;
6888 gc_during_regexp_ = 0;
6889 regexp_success_ = false;
6890 gc_success_ = false;
6891 GCThread gc_thread(this);
6892 gc_thread.Start();
6893 v8::Locker::StartPreemption(1);
6894
6895 LongRunningRegExp();
6896 {
6897 v8::Unlocker unlock;
6898 gc_thread.Join();
6899 }
6900 v8::Locker::StopPreemption();
6901 CHECK(regexp_success_);
6902 CHECK(gc_success_);
6903 }
6904 private:
6905 // Number of garbage collections required.
6906 static const int kRequiredGCs = 5;
6907
6908 class GCThread : public i::Thread {
6909 public:
6910 explicit GCThread(RegExpInterruptTest* test)
6911 : test_(test) {}
6912 virtual void Run() {
6913 test_->CollectGarbage();
6914 }
6915 private:
6916 RegExpInterruptTest* test_;
6917 };
6918
6919 void CollectGarbage() {
6920 block_->Wait();
6921 while (gc_during_regexp_ < kRequiredGCs) {
6922 {
6923 v8::Locker lock;
6924 // TODO(lrn): Perhaps create some garbage before collecting.
6925 i::Heap::CollectAllGarbage(false);
6926 gc_count_++;
6927 }
6928 i::OS::Sleep(1);
6929 }
6930 gc_success_ = true;
6931 }
6932
6933 void LongRunningRegExp() {
6934 block_->Signal(); // Enable garbage collection thread on next preemption.
6935 int rounds = 0;
6936 while (gc_during_regexp_ < kRequiredGCs) {
6937 int gc_before = gc_count_;
6938 {
6939 // Match 15-30 "a"'s against 14 and a "b".
6940 const char* c_source =
6941 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
6942 ".exec('aaaaaaaaaaaaaaab') === null";
6943 Local<String> source = String::New(c_source);
6944 Local<Script> script = Script::Compile(source);
6945 Local<Value> result = script->Run();
6946 if (!result->BooleanValue()) {
6947 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
6948 return;
6949 }
6950 }
6951 {
6952 // Match 15-30 "a"'s against 15 and a "b".
6953 const char* c_source =
6954 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
6955 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
6956 Local<String> source = String::New(c_source);
6957 Local<Script> script = Script::Compile(source);
6958 Local<Value> result = script->Run();
6959 if (!result->BooleanValue()) {
6960 gc_during_regexp_ = kRequiredGCs;
6961 return;
6962 }
6963 }
6964 int gc_after = gc_count_;
6965 gc_during_regexp_ += gc_after - gc_before;
6966 rounds++;
6967 i::OS::Sleep(1);
6968 }
6969 regexp_success_ = true;
6970 }
6971
6972 i::Semaphore* block_;
6973 int gc_count_;
6974 int gc_during_regexp_;
6975 bool regexp_success_;
6976 bool gc_success_;
6977};
6978
6979
6980// Test that a regular expression execution can be interrupted and
6981// survive a garbage collection.
6982TEST(RegExpInterruption) {
6983 v8::Locker lock;
6984 v8::V8::Initialize();
6985 v8::HandleScope scope;
6986 Local<Context> local_env;
6987 {
6988 LocalContext env;
6989 local_env = env.local();
6990 }
6991
6992 // Local context should still be live.
6993 CHECK(!local_env.IsEmpty());
6994 local_env->Enter();
6995
6996 // Should complete without problems.
6997 RegExpInterruptTest().RunTest();
6998
6999 local_env->Exit();
7000}
7001
7002
7003class ApplyInterruptTest {
7004 public:
7005 ApplyInterruptTest() : block_(NULL) {}
7006 ~ApplyInterruptTest() { delete block_; }
7007 void RunTest() {
7008 block_ = i::OS::CreateSemaphore(0);
7009 gc_count_ = 0;
7010 gc_during_apply_ = 0;
7011 apply_success_ = false;
7012 gc_success_ = false;
7013 GCThread gc_thread(this);
7014 gc_thread.Start();
7015 v8::Locker::StartPreemption(1);
7016
7017 LongRunningApply();
7018 {
7019 v8::Unlocker unlock;
7020 gc_thread.Join();
7021 }
7022 v8::Locker::StopPreemption();
7023 CHECK(apply_success_);
7024 CHECK(gc_success_);
7025 }
7026 private:
7027 // Number of garbage collections required.
7028 static const int kRequiredGCs = 2;
7029
7030 class GCThread : public i::Thread {
7031 public:
7032 explicit GCThread(ApplyInterruptTest* test)
7033 : test_(test) {}
7034 virtual void Run() {
7035 test_->CollectGarbage();
7036 }
7037 private:
7038 ApplyInterruptTest* test_;
7039 };
7040
7041 void CollectGarbage() {
7042 block_->Wait();
7043 while (gc_during_apply_ < kRequiredGCs) {
7044 {
7045 v8::Locker lock;
7046 i::Heap::CollectAllGarbage(false);
7047 gc_count_++;
7048 }
7049 i::OS::Sleep(1);
7050 }
7051 gc_success_ = true;
7052 }
7053
7054 void LongRunningApply() {
7055 block_->Signal();
7056 int rounds = 0;
7057 while (gc_during_apply_ < kRequiredGCs) {
7058 int gc_before = gc_count_;
7059 {
7060 const char* c_source =
7061 "function do_very_little(bar) {"
7062 " this.foo = bar;"
7063 "}"
7064 "for (var i = 0; i < 100000; i++) {"
7065 " do_very_little.apply(this, ['bar']);"
7066 "}";
7067 Local<String> source = String::New(c_source);
7068 Local<Script> script = Script::Compile(source);
7069 Local<Value> result = script->Run();
7070 // Check that no exception was thrown.
7071 CHECK(!result.IsEmpty());
7072 }
7073 int gc_after = gc_count_;
7074 gc_during_apply_ += gc_after - gc_before;
7075 rounds++;
7076 }
7077 apply_success_ = true;
7078 }
7079
7080 i::Semaphore* block_;
7081 int gc_count_;
7082 int gc_during_apply_;
7083 bool apply_success_;
7084 bool gc_success_;
7085};
7086
7087
7088// Test that nothing bad happens if we get a preemption just when we were
7089// about to do an apply().
7090TEST(ApplyInterruption) {
7091 v8::Locker lock;
7092 v8::V8::Initialize();
7093 v8::HandleScope scope;
7094 Local<Context> local_env;
7095 {
7096 LocalContext env;
7097 local_env = env.local();
7098 }
7099
7100 // Local context should still be live.
7101 CHECK(!local_env.IsEmpty());
7102 local_env->Enter();
7103
7104 // Should complete without problems.
7105 ApplyInterruptTest().RunTest();
7106
7107 local_env->Exit();
7108}
7109
7110
7111// Verify that we can clone an object
7112TEST(ObjectClone) {
7113 v8::HandleScope scope;
7114 LocalContext env;
7115
7116 const char* sample =
7117 "var rv = {};" \
7118 "rv.alpha = 'hello';" \
7119 "rv.beta = 123;" \
7120 "rv;";
7121
7122 // Create an object, verify basics.
7123 Local<Value> val = CompileRun(sample);
7124 CHECK(val->IsObject());
7125 Local<v8::Object> obj = Local<v8::Object>::Cast(val);
7126 obj->Set(v8_str("gamma"), v8_str("cloneme"));
7127
7128 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
7129 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
7130 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
7131
7132 // Clone it.
7133 Local<v8::Object> clone = obj->Clone();
7134 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
7135 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
7136 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
7137
7138 // Set a property on the clone, verify each object.
7139 clone->Set(v8_str("beta"), v8::Integer::New(456));
7140 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
7141 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
7142}
7143
7144
7145class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
7146 public:
7147 explicit AsciiVectorResource(i::Vector<const char> vector)
7148 : data_(vector) {}
7149 virtual ~AsciiVectorResource() {}
7150 virtual size_t length() const { return data_.length(); }
7151 virtual const char* data() const { return data_.start(); }
7152 private:
7153 i::Vector<const char> data_;
7154};
7155
7156
7157class UC16VectorResource : public v8::String::ExternalStringResource {
7158 public:
7159 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
7160 : data_(vector) {}
7161 virtual ~UC16VectorResource() {}
7162 virtual size_t length() const { return data_.length(); }
7163 virtual const i::uc16* data() const { return data_.start(); }
7164 private:
7165 i::Vector<const i::uc16> data_;
7166};
7167
7168
7169static void MorphAString(i::String* string,
7170 AsciiVectorResource* ascii_resource,
7171 UC16VectorResource* uc16_resource) {
7172 CHECK(i::StringShape(string).IsExternal());
7173 if (string->IsAsciiRepresentation()) {
7174 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00007175 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00007176 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00007177 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00007178 i::ExternalTwoByteString* morphed =
7179 i::ExternalTwoByteString::cast(string);
7180 morphed->set_resource(uc16_resource);
7181 } else {
7182 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00007183 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00007184 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00007185 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00007186 i::ExternalAsciiString* morphed =
7187 i::ExternalAsciiString::cast(string);
7188 morphed->set_resource(ascii_resource);
7189 }
7190}
7191
7192
7193// Test that we can still flatten a string if the components it is built up
7194// from have been turned into 16 bit strings in the mean time.
7195THREADED_TEST(MorphCompositeStringTest) {
7196 const char* c_string = "Now is the time for all good men"
7197 " to come to the aid of the party";
7198 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
7199 {
7200 v8::HandleScope scope;
7201 LocalContext env;
7202 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00007203 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00007204 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00007205 i::Vector<const uint16_t>(two_byte_string,
7206 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00007207
7208 Local<String> lhs(v8::Utils::ToLocal(
7209 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
7210 Local<String> rhs(v8::Utils::ToLocal(
7211 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
7212
7213 env->Global()->Set(v8_str("lhs"), lhs);
7214 env->Global()->Set(v8_str("rhs"), rhs);
7215
7216 CompileRun(
7217 "var cons = lhs + rhs;"
7218 "var slice = lhs.substring(1, lhs.length - 1);"
7219 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
7220
7221 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
7222 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
7223
7224 // Now do some stuff to make sure the strings are flattened, etc.
7225 CompileRun(
7226 "/[^a-z]/.test(cons);"
7227 "/[^a-z]/.test(slice);"
7228 "/[^a-z]/.test(slice_on_cons);");
7229 const char* expected_cons =
7230 "Now is the time for all good men to come to the aid of the party"
7231 "Now is the time for all good men to come to the aid of the party";
7232 const char* expected_slice =
7233 "ow is the time for all good men to come to the aid of the part";
7234 const char* expected_slice_on_cons =
7235 "ow is the time for all good men to come to the aid of the party"
7236 "Now is the time for all good men to come to the aid of the part";
7237 CHECK_EQ(String::New(expected_cons),
7238 env->Global()->Get(v8_str("cons")));
7239 CHECK_EQ(String::New(expected_slice),
7240 env->Global()->Get(v8_str("slice")));
7241 CHECK_EQ(String::New(expected_slice_on_cons),
7242 env->Global()->Get(v8_str("slice_on_cons")));
7243 }
7244}
7245
7246
7247TEST(CompileExternalTwoByteSource) {
7248 v8::HandleScope scope;
7249 LocalContext context;
7250
7251 // This is a very short list of sources, which currently is to check for a
7252 // regression caused by r2703.
7253 const char* ascii_sources[] = {
7254 "0.5",
7255 "-0.5", // This mainly testes PushBack in the Scanner.
7256 "--0.5", // This mainly testes PushBack in the Scanner.
7257 NULL
7258 };
7259
7260 // Compile the sources as external two byte strings.
7261 for (int i = 0; ascii_sources[i] != NULL; i++) {
7262 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
7263 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00007264 i::Vector<const uint16_t>(two_byte_string,
7265 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00007266 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
7267 v8::Script::Compile(source);
7268 }
7269}
7270
7271
7272class RegExpStringModificationTest {
7273 public:
7274 RegExpStringModificationTest()
7275 : block_(i::OS::CreateSemaphore(0)),
7276 morphs_(0),
7277 morphs_during_regexp_(0),
7278 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
7279 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
7280 ~RegExpStringModificationTest() { delete block_; }
7281 void RunTest() {
7282 regexp_success_ = false;
7283 morph_success_ = false;
7284
7285 // Initialize the contents of two_byte_content_ to be a uc16 representation
7286 // of "aaaaaaaaaaaaaab".
7287 for (int i = 0; i < 14; i++) {
7288 two_byte_content_[i] = 'a';
7289 }
7290 two_byte_content_[14] = 'b';
7291
7292 // Create the input string for the regexp - the one we are going to change
7293 // properties of.
7294 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
7295
7296 // Inject the input as a global variable.
7297 i::Handle<i::String> input_name =
7298 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
7299 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
7300
7301
7302 MorphThread morph_thread(this);
7303 morph_thread.Start();
7304 v8::Locker::StartPreemption(1);
7305 LongRunningRegExp();
7306 {
7307 v8::Unlocker unlock;
7308 morph_thread.Join();
7309 }
7310 v8::Locker::StopPreemption();
7311 CHECK(regexp_success_);
7312 CHECK(morph_success_);
7313 }
7314 private:
7315
7316 // Number of string modifications required.
7317 static const int kRequiredModifications = 5;
7318 static const int kMaxModifications = 100;
7319
7320 class MorphThread : public i::Thread {
7321 public:
7322 explicit MorphThread(RegExpStringModificationTest* test)
7323 : test_(test) {}
7324 virtual void Run() {
7325 test_->MorphString();
7326 }
7327 private:
7328 RegExpStringModificationTest* test_;
7329 };
7330
7331 void MorphString() {
7332 block_->Wait();
7333 while (morphs_during_regexp_ < kRequiredModifications &&
7334 morphs_ < kMaxModifications) {
7335 {
7336 v8::Locker lock;
7337 // Swap string between ascii and two-byte representation.
7338 i::String* string = *input_;
7339 MorphAString(string, &ascii_resource_, &uc16_resource_);
7340 morphs_++;
7341 }
7342 i::OS::Sleep(1);
7343 }
7344 morph_success_ = true;
7345 }
7346
7347 void LongRunningRegExp() {
7348 block_->Signal(); // Enable morphing thread on next preemption.
7349 while (morphs_during_regexp_ < kRequiredModifications &&
7350 morphs_ < kMaxModifications) {
7351 int morphs_before = morphs_;
7352 {
7353 // Match 15-30 "a"'s against 14 and a "b".
7354 const char* c_source =
7355 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
7356 ".exec(input) === null";
7357 Local<String> source = String::New(c_source);
7358 Local<Script> script = Script::Compile(source);
7359 Local<Value> result = script->Run();
7360 CHECK(result->IsTrue());
7361 }
7362 int morphs_after = morphs_;
7363 morphs_during_regexp_ += morphs_after - morphs_before;
7364 }
7365 regexp_success_ = true;
7366 }
7367
7368 i::uc16 two_byte_content_[15];
7369 i::Semaphore* block_;
7370 int morphs_;
7371 int morphs_during_regexp_;
7372 bool regexp_success_;
7373 bool morph_success_;
7374 i::Handle<i::String> input_;
7375 AsciiVectorResource ascii_resource_;
7376 UC16VectorResource uc16_resource_;
7377};
7378
7379
7380// Test that a regular expression execution can be interrupted and
7381// the string changed without failing.
7382TEST(RegExpStringModification) {
7383 v8::Locker lock;
7384 v8::V8::Initialize();
7385 v8::HandleScope scope;
7386 Local<Context> local_env;
7387 {
7388 LocalContext env;
7389 local_env = env.local();
7390 }
7391
7392 // Local context should still be live.
7393 CHECK(!local_env.IsEmpty());
7394 local_env->Enter();
7395
7396 // Should complete without problems.
7397 RegExpStringModificationTest().RunTest();
7398
7399 local_env->Exit();
7400}
7401
7402
7403// Test that we can set a property on the global object even if there
7404// is a read-only property in the prototype chain.
7405TEST(ReadOnlyPropertyInGlobalProto) {
7406 v8::HandleScope scope;
7407 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7408 LocalContext context(0, templ);
7409 v8::Handle<v8::Object> global = context->Global();
7410 v8::Handle<v8::Object> global_proto =
7411 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
7412 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
7413 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
7414 // Check without 'eval' or 'with'.
7415 v8::Handle<v8::Value> res =
7416 CompileRun("function f() { x = 42; return x; }; f()");
7417 // Check with 'eval'.
7418 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
7419 CHECK_EQ(v8::Integer::New(42), res);
7420 // Check with 'with'.
7421 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
7422 CHECK_EQ(v8::Integer::New(42), res);
7423}
7424
7425static int force_set_set_count = 0;
7426static int force_set_get_count = 0;
7427bool pass_on_get = false;
7428
7429static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
7430 const v8::AccessorInfo& info) {
7431 force_set_get_count++;
7432 if (pass_on_get) {
7433 return v8::Handle<v8::Value>();
7434 } else {
7435 return v8::Int32::New(3);
7436 }
7437}
7438
7439static void ForceSetSetter(v8::Local<v8::String> name,
7440 v8::Local<v8::Value> value,
7441 const v8::AccessorInfo& info) {
7442 force_set_set_count++;
7443}
7444
7445static v8::Handle<v8::Value> ForceSetInterceptSetter(
7446 v8::Local<v8::String> name,
7447 v8::Local<v8::Value> value,
7448 const v8::AccessorInfo& info) {
7449 force_set_set_count++;
7450 return v8::Undefined();
7451}
7452
7453TEST(ForceSet) {
7454 force_set_get_count = 0;
7455 force_set_set_count = 0;
7456 pass_on_get = false;
7457
7458 v8::HandleScope scope;
7459 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7460 v8::Handle<v8::String> access_property = v8::String::New("a");
7461 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
7462 LocalContext context(NULL, templ);
7463 v8::Handle<v8::Object> global = context->Global();
7464
7465 // Ordinary properties
7466 v8::Handle<v8::String> simple_property = v8::String::New("p");
7467 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
7468 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7469 // This should fail because the property is read-only
7470 global->Set(simple_property, v8::Int32::New(5));
7471 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7472 // This should succeed even though the property is read-only
7473 global->ForceSet(simple_property, v8::Int32::New(6));
7474 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
7475
7476 // Accessors
7477 CHECK_EQ(0, force_set_set_count);
7478 CHECK_EQ(0, force_set_get_count);
7479 CHECK_EQ(3, global->Get(access_property)->Int32Value());
7480 // CHECK_EQ the property shouldn't override it, just call the setter
7481 // which in this case does nothing.
7482 global->Set(access_property, v8::Int32::New(7));
7483 CHECK_EQ(3, global->Get(access_property)->Int32Value());
7484 CHECK_EQ(1, force_set_set_count);
7485 CHECK_EQ(2, force_set_get_count);
7486 // Forcing the property to be set should override the accessor without
7487 // calling it
7488 global->ForceSet(access_property, v8::Int32::New(8));
7489 CHECK_EQ(8, global->Get(access_property)->Int32Value());
7490 CHECK_EQ(1, force_set_set_count);
7491 CHECK_EQ(2, force_set_get_count);
7492}
7493
7494TEST(ForceSetWithInterceptor) {
7495 force_set_get_count = 0;
7496 force_set_set_count = 0;
7497 pass_on_get = false;
7498
7499 v8::HandleScope scope;
7500 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7501 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
7502 LocalContext context(NULL, templ);
7503 v8::Handle<v8::Object> global = context->Global();
7504
7505 v8::Handle<v8::String> some_property = v8::String::New("a");
7506 CHECK_EQ(0, force_set_set_count);
7507 CHECK_EQ(0, force_set_get_count);
7508 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7509 // Setting the property shouldn't override it, just call the setter
7510 // which in this case does nothing.
7511 global->Set(some_property, v8::Int32::New(7));
7512 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7513 CHECK_EQ(1, force_set_set_count);
7514 CHECK_EQ(2, force_set_get_count);
7515 // Getting the property when the interceptor returns an empty handle
7516 // should yield undefined, since the property isn't present on the
7517 // object itself yet.
7518 pass_on_get = true;
7519 CHECK(global->Get(some_property)->IsUndefined());
7520 CHECK_EQ(1, force_set_set_count);
7521 CHECK_EQ(3, force_set_get_count);
7522 // Forcing the property to be set should cause the value to be
7523 // set locally without calling the interceptor.
7524 global->ForceSet(some_property, v8::Int32::New(8));
7525 CHECK_EQ(8, global->Get(some_property)->Int32Value());
7526 CHECK_EQ(1, force_set_set_count);
7527 CHECK_EQ(4, force_set_get_count);
7528 // Reenabling the interceptor should cause it to take precedence over
7529 // the property
7530 pass_on_get = false;
7531 CHECK_EQ(3, global->Get(some_property)->Int32Value());
7532 CHECK_EQ(1, force_set_set_count);
7533 CHECK_EQ(5, force_set_get_count);
7534 // The interceptor should also work for other properties
7535 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
7536 CHECK_EQ(1, force_set_set_count);
7537 CHECK_EQ(6, force_set_get_count);
7538}
7539
7540
7541THREADED_TEST(ForceDelete) {
7542 v8::HandleScope scope;
7543 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7544 LocalContext context(NULL, templ);
7545 v8::Handle<v8::Object> global = context->Global();
7546
7547 // Ordinary properties
7548 v8::Handle<v8::String> simple_property = v8::String::New("p");
7549 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
7550 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7551 // This should fail because the property is dont-delete.
7552 CHECK(!global->Delete(simple_property));
7553 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
7554 // This should succeed even though the property is dont-delete.
7555 CHECK(global->ForceDelete(simple_property));
7556 CHECK(global->Get(simple_property)->IsUndefined());
7557}
7558
7559
7560static int force_delete_interceptor_count = 0;
7561static bool pass_on_delete = false;
7562
7563
7564static v8::Handle<v8::Boolean> ForceDeleteDeleter(
7565 v8::Local<v8::String> name,
7566 const v8::AccessorInfo& info) {
7567 force_delete_interceptor_count++;
7568 if (pass_on_delete) {
7569 return v8::Handle<v8::Boolean>();
7570 } else {
7571 return v8::True();
7572 }
7573}
7574
7575
7576THREADED_TEST(ForceDeleteWithInterceptor) {
7577 force_delete_interceptor_count = 0;
7578 pass_on_delete = false;
7579
7580 v8::HandleScope scope;
7581 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
7582 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
7583 LocalContext context(NULL, templ);
7584 v8::Handle<v8::Object> global = context->Global();
7585
7586 v8::Handle<v8::String> some_property = v8::String::New("a");
7587 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
7588
7589 // Deleting a property should get intercepted and nothing should
7590 // happen.
7591 CHECK_EQ(0, force_delete_interceptor_count);
7592 CHECK(global->Delete(some_property));
7593 CHECK_EQ(1, force_delete_interceptor_count);
7594 CHECK_EQ(42, global->Get(some_property)->Int32Value());
7595 // Deleting the property when the interceptor returns an empty
7596 // handle should not delete the property since it is DontDelete.
7597 pass_on_delete = true;
7598 CHECK(!global->Delete(some_property));
7599 CHECK_EQ(2, force_delete_interceptor_count);
7600 CHECK_EQ(42, global->Get(some_property)->Int32Value());
7601 // Forcing the property to be deleted should delete the value
7602 // without calling the interceptor.
7603 CHECK(global->ForceDelete(some_property));
7604 CHECK(global->Get(some_property)->IsUndefined());
7605 CHECK_EQ(2, force_delete_interceptor_count);
7606}
7607
7608
7609// Make sure that forcing a delete invalidates any IC stubs, so we
7610// don't read the hole value.
7611THREADED_TEST(ForceDeleteIC) {
7612 v8::HandleScope scope;
7613 LocalContext context;
7614 // Create a DontDelete variable on the global object.
7615 CompileRun("this.__proto__ = { foo: 'horse' };"
7616 "var foo = 'fish';"
7617 "function f() { return foo.length; }");
7618 // Initialize the IC for foo in f.
7619 CompileRun("for (var i = 0; i < 4; i++) f();");
7620 // Make sure the value of foo is correct before the deletion.
7621 CHECK_EQ(4, CompileRun("f()")->Int32Value());
7622 // Force the deletion of foo.
7623 CHECK(context->Global()->ForceDelete(v8_str("foo")));
7624 // Make sure the value for foo is read from the prototype, and that
7625 // we don't get in trouble with reading the deleted cell value
7626 // sentinel.
7627 CHECK_EQ(5, CompileRun("f()")->Int32Value());
7628}
7629
7630
7631v8::Persistent<Context> calling_context0;
7632v8::Persistent<Context> calling_context1;
7633v8::Persistent<Context> calling_context2;
7634
7635
7636// Check that the call to the callback is initiated in
7637// calling_context2, the directly calling context is calling_context1
7638// and the callback itself is in calling_context0.
7639static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
7640 ApiTestFuzzer::Fuzz();
7641 CHECK(Context::GetCurrent() == calling_context0);
7642 CHECK(Context::GetCalling() == calling_context1);
7643 CHECK(Context::GetEntered() == calling_context2);
7644 return v8::Integer::New(42);
7645}
7646
7647
7648THREADED_TEST(GetCallingContext) {
7649 v8::HandleScope scope;
7650
7651 calling_context0 = Context::New();
7652 calling_context1 = Context::New();
7653 calling_context2 = Context::New();
7654
7655 // Allow cross-domain access.
7656 Local<String> token = v8_str("<security token>");
7657 calling_context0->SetSecurityToken(token);
7658 calling_context1->SetSecurityToken(token);
7659 calling_context2->SetSecurityToken(token);
7660
7661 // Create an object with a C++ callback in context0.
7662 calling_context0->Enter();
7663 Local<v8::FunctionTemplate> callback_templ =
7664 v8::FunctionTemplate::New(GetCallingContextCallback);
7665 calling_context0->Global()->Set(v8_str("callback"),
7666 callback_templ->GetFunction());
7667 calling_context0->Exit();
7668
7669 // Expose context0 in context1 and setup a function that calls the
7670 // callback function.
7671 calling_context1->Enter();
7672 calling_context1->Global()->Set(v8_str("context0"),
7673 calling_context0->Global());
7674 CompileRun("function f() { context0.callback() }");
7675 calling_context1->Exit();
7676
7677 // Expose context1 in context2 and call the callback function in
7678 // context0 indirectly through f in context1.
7679 calling_context2->Enter();
7680 calling_context2->Global()->Set(v8_str("context1"),
7681 calling_context1->Global());
7682 CompileRun("context1.f()");
7683 calling_context2->Exit();
7684
7685 // Dispose the contexts to allow them to be garbage collected.
7686 calling_context0.Dispose();
7687 calling_context1.Dispose();
7688 calling_context2.Dispose();
7689 calling_context0.Clear();
7690 calling_context1.Clear();
7691 calling_context2.Clear();
7692}
7693
7694
7695// Check that a variable declaration with no explicit initialization
7696// value does not shadow an existing property in the prototype chain.
7697//
7698// This is consistent with Firefox and Safari.
7699//
7700// See http://crbug.com/12548.
7701THREADED_TEST(InitGlobalVarInProtoChain) {
7702 v8::HandleScope scope;
7703 LocalContext context;
7704 // Introduce a variable in the prototype chain.
7705 CompileRun("__proto__.x = 42");
7706 v8::Handle<v8::Value> result = CompileRun("var x; x");
7707 CHECK(!result->IsUndefined());
7708 CHECK_EQ(42, result->Int32Value());
7709}
7710
7711
7712// Regression test for issue 398.
7713// If a function is added to an object, creating a constant function
7714// field, and the result is cloned, replacing the constant function on the
7715// original should not affect the clone.
7716// See http://code.google.com/p/v8/issues/detail?id=398
7717THREADED_TEST(ReplaceConstantFunction) {
7718 v8::HandleScope scope;
7719 LocalContext context;
7720 v8::Handle<v8::Object> obj = v8::Object::New();
7721 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
7722 v8::Handle<v8::String> foo_string = v8::String::New("foo");
7723 obj->Set(foo_string, func_templ->GetFunction());
7724 v8::Handle<v8::Object> obj_clone = obj->Clone();
7725 obj_clone->Set(foo_string, v8::String::New("Hello"));
7726 CHECK(!obj->Get(foo_string)->IsUndefined());
7727}
7728
7729
7730// Regression test for http://crbug.com/16276.
7731THREADED_TEST(Regress16276) {
7732 v8::HandleScope scope;
7733 LocalContext context;
7734 // Force the IC in f to be a dictionary load IC.
7735 CompileRun("function f(obj) { return obj.x; }\n"
7736 "var obj = { x: { foo: 42 }, y: 87 };\n"
7737 "var x = obj.x;\n"
7738 "delete obj.y;\n"
7739 "for (var i = 0; i < 5; i++) f(obj);");
7740 // Detach the global object to make 'this' refer directly to the
7741 // global object (not the proxy), and make sure that the dictionary
7742 // load IC doesn't mess up loading directly from the global object.
7743 context->DetachGlobal();
7744 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
7745}
7746
7747
7748THREADED_TEST(PixelArray) {
7749 v8::HandleScope scope;
7750 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00007751 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00007752 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
7753 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
7754 pixel_data);
7755 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
7756 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00007757 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00007758 }
7759 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
7760 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00007761 CHECK_EQ(i % 256, pixels->get(i));
7762 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00007763 }
7764
7765 v8::Handle<v8::Object> obj = v8::Object::New();
7766 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
7767 // Set the elements to be the pixels.
7768 // jsobj->set_elements(*pixels);
7769 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
7770 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
7771 obj->Set(v8_str("field"), v8::Int32::New(1503));
7772 context->Global()->Set(v8_str("pixels"), obj);
7773 v8::Handle<v8::Value> result = CompileRun("pixels.field");
7774 CHECK_EQ(1503, result->Int32Value());
7775 result = CompileRun("pixels[1]");
7776 CHECK_EQ(1, result->Int32Value());
7777
7778 result = CompileRun("var sum = 0;"
7779 "for (var i = 0; i < 8; i++) {"
7780 " sum += pixels[i] = pixels[i] = -i;"
7781 "}"
7782 "sum;");
7783 CHECK_EQ(-28, result->Int32Value());
7784
7785 result = CompileRun("var sum = 0;"
7786 "for (var i = 0; i < 8; i++) {"
7787 " sum += pixels[i] = pixels[i] = 0;"
7788 "}"
7789 "sum;");
7790 CHECK_EQ(0, result->Int32Value());
7791
7792 result = CompileRun("var sum = 0;"
7793 "for (var i = 0; i < 8; i++) {"
7794 " sum += pixels[i] = pixels[i] = 255;"
7795 "}"
7796 "sum;");
7797 CHECK_EQ(8 * 255, result->Int32Value());
7798
7799 result = CompileRun("var sum = 0;"
7800 "for (var i = 0; i < 8; i++) {"
7801 " sum += pixels[i] = pixels[i] = 256 + i;"
7802 "}"
7803 "sum;");
7804 CHECK_EQ(2076, result->Int32Value());
7805
7806 result = CompileRun("var sum = 0;"
7807 "for (var i = 0; i < 8; i++) {"
7808 " sum += pixels[i] = pixels[i] = i;"
7809 "}"
7810 "sum;");
7811 CHECK_EQ(28, result->Int32Value());
7812
7813 result = CompileRun("var sum = 0;"
7814 "for (var i = 0; i < 8; i++) {"
7815 " sum += pixels[i];"
7816 "}"
7817 "sum;");
7818 CHECK_EQ(28, result->Int32Value());
7819
7820 i::Handle<i::Smi> value(i::Smi::FromInt(2));
7821 i::SetElement(jsobj, 1, value);
7822 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
7823 *value.location() = i::Smi::FromInt(256);
7824 i::SetElement(jsobj, 1, value);
7825 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
7826 *value.location() = i::Smi::FromInt(-1);
7827 i::SetElement(jsobj, 1, value);
7828 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
7829
7830 result = CompileRun("for (var i = 0; i < 8; i++) {"
7831 " pixels[i] = (i * 65) - 109;"
7832 "}"
7833 "pixels[1] + pixels[6];");
7834 CHECK_EQ(255, result->Int32Value());
7835 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
7836 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
7837 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
7838 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
7839 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
7840 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
7841 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
7842 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
7843 result = CompileRun("var sum = 0;"
7844 "for (var i = 0; i < 8; i++) {"
7845 " sum += pixels[i];"
7846 "}"
7847 "sum;");
7848 CHECK_EQ(984, result->Int32Value());
7849
7850 result = CompileRun("for (var i = 0; i < 8; i++) {"
7851 " pixels[i] = (i * 1.1);"
7852 "}"
7853 "pixels[1] + pixels[6];");
7854 CHECK_EQ(8, result->Int32Value());
7855 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
7856 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
7857 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
7858 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
7859 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
7860 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
7861 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
7862 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
7863
7864 result = CompileRun("for (var i = 0; i < 8; i++) {"
7865 " pixels[7] = undefined;"
7866 "}"
7867 "pixels[7];");
7868 CHECK_EQ(0, result->Int32Value());
7869 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
7870
7871 result = CompileRun("for (var i = 0; i < 8; i++) {"
7872 " pixels[6] = '2.3';"
7873 "}"
7874 "pixels[6];");
7875 CHECK_EQ(2, result->Int32Value());
7876 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
7877
7878 result = CompileRun("for (var i = 0; i < 8; i++) {"
7879 " pixels[5] = NaN;"
7880 "}"
7881 "pixels[5];");
7882 CHECK_EQ(0, result->Int32Value());
7883 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
7884
7885 result = CompileRun("for (var i = 0; i < 8; i++) {"
7886 " pixels[8] = Infinity;"
7887 "}"
7888 "pixels[8];");
7889 CHECK_EQ(255, result->Int32Value());
7890 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
7891
7892 result = CompileRun("for (var i = 0; i < 8; i++) {"
7893 " pixels[9] = -Infinity;"
7894 "}"
7895 "pixels[9];");
7896 CHECK_EQ(0, result->Int32Value());
7897 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
7898
7899 result = CompileRun("pixels[3] = 33;"
7900 "delete pixels[3];"
7901 "pixels[3];");
7902 CHECK_EQ(33, result->Int32Value());
7903
7904 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
7905 "pixels[2] = 12; pixels[3] = 13;"
7906 "pixels.__defineGetter__('2',"
7907 "function() { return 120; });"
7908 "pixels[2];");
7909 CHECK_EQ(12, result->Int32Value());
7910
7911 result = CompileRun("var js_array = new Array(40);"
7912 "js_array[0] = 77;"
7913 "js_array;");
7914 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
7915
7916 result = CompileRun("pixels[1] = 23;"
7917 "pixels.__proto__ = [];"
7918 "js_array.__proto__ = pixels;"
7919 "js_array.concat(pixels);");
7920 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
7921 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
7922
7923 result = CompileRun("pixels[1] = 23;");
7924 CHECK_EQ(23, result->Int32Value());
7925
Steve Blockd0582a62009-12-15 09:54:21 +00007926 // Test for index greater than 255. Regression test for:
7927 // http://code.google.com/p/chromium/issues/detail?id=26337.
7928 result = CompileRun("pixels[256] = 255;");
7929 CHECK_EQ(255, result->Int32Value());
7930 result = CompileRun("var i = 0;"
7931 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
7932 "i");
7933 CHECK_EQ(255, result->Int32Value());
7934
Steve Blocka7e24c12009-10-30 11:49:00 +00007935 free(pixel_data);
7936}
7937
7938
Steve Block3ce2e202009-11-05 08:53:23 +00007939template <class ExternalArrayClass, class ElementType>
7940static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
7941 int64_t low,
7942 int64_t high) {
7943 v8::HandleScope scope;
7944 LocalContext context;
7945 const int kElementCount = 40;
7946 int element_size = 0;
7947 switch (array_type) {
7948 case v8::kExternalByteArray:
7949 case v8::kExternalUnsignedByteArray:
7950 element_size = 1;
7951 break;
7952 case v8::kExternalShortArray:
7953 case v8::kExternalUnsignedShortArray:
7954 element_size = 2;
7955 break;
7956 case v8::kExternalIntArray:
7957 case v8::kExternalUnsignedIntArray:
7958 case v8::kExternalFloatArray:
7959 element_size = 4;
7960 break;
7961 default:
7962 UNREACHABLE();
7963 break;
7964 }
7965 ElementType* array_data =
7966 static_cast<ElementType*>(malloc(kElementCount * element_size));
7967 i::Handle<ExternalArrayClass> array =
7968 i::Handle<ExternalArrayClass>::cast(
7969 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
7970 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
7971 for (int i = 0; i < kElementCount; i++) {
7972 array->set(i, static_cast<ElementType>(i));
7973 }
7974 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
7975 for (int i = 0; i < kElementCount; i++) {
7976 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
7977 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
7978 }
7979
7980 v8::Handle<v8::Object> obj = v8::Object::New();
7981 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
7982 // Set the elements to be the external array.
7983 obj->SetIndexedPropertiesToExternalArrayData(array_data,
7984 array_type,
7985 kElementCount);
7986 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
7987 obj->Set(v8_str("field"), v8::Int32::New(1503));
7988 context->Global()->Set(v8_str("ext_array"), obj);
7989 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
7990 CHECK_EQ(1503, result->Int32Value());
7991 result = CompileRun("ext_array[1]");
7992 CHECK_EQ(1, result->Int32Value());
7993
7994 // Check pass through of assigned smis
7995 result = CompileRun("var sum = 0;"
7996 "for (var i = 0; i < 8; i++) {"
7997 " sum += ext_array[i] = ext_array[i] = -i;"
7998 "}"
7999 "sum;");
8000 CHECK_EQ(-28, result->Int32Value());
8001
8002 // Check assigned smis
8003 result = CompileRun("for (var i = 0; i < 8; i++) {"
8004 " ext_array[i] = i;"
8005 "}"
8006 "var sum = 0;"
8007 "for (var i = 0; i < 8; i++) {"
8008 " sum += ext_array[i];"
8009 "}"
8010 "sum;");
8011 CHECK_EQ(28, result->Int32Value());
8012
8013 // Check assigned smis in reverse order
8014 result = CompileRun("for (var i = 8; --i >= 0; ) {"
8015 " ext_array[i] = i;"
8016 "}"
8017 "var sum = 0;"
8018 "for (var i = 0; i < 8; i++) {"
8019 " sum += ext_array[i];"
8020 "}"
8021 "sum;");
8022 CHECK_EQ(28, result->Int32Value());
8023
8024 // Check pass through of assigned HeapNumbers
8025 result = CompileRun("var sum = 0;"
8026 "for (var i = 0; i < 16; i+=2) {"
8027 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
8028 "}"
8029 "sum;");
8030 CHECK_EQ(-28, result->Int32Value());
8031
8032 // Check assigned HeapNumbers
8033 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
8034 " ext_array[i] = (i * 0.5);"
8035 "}"
8036 "var sum = 0;"
8037 "for (var i = 0; i < 16; i+=2) {"
8038 " sum += ext_array[i];"
8039 "}"
8040 "sum;");
8041 CHECK_EQ(28, result->Int32Value());
8042
8043 // Check assigned HeapNumbers in reverse order
8044 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
8045 " ext_array[i] = (i * 0.5);"
8046 "}"
8047 "var sum = 0;"
8048 "for (var i = 0; i < 16; i+=2) {"
8049 " sum += ext_array[i];"
8050 "}"
8051 "sum;");
8052 CHECK_EQ(28, result->Int32Value());
8053
8054 i::ScopedVector<char> test_buf(1024);
8055
8056 // Check legal boundary conditions.
8057 // The repeated loads and stores ensure the ICs are exercised.
8058 const char* boundary_program =
8059 "var res = 0;"
8060 "for (var i = 0; i < 16; i++) {"
8061 " ext_array[i] = %lld;"
8062 " if (i > 8) {"
8063 " res = ext_array[i];"
8064 " }"
8065 "}"
8066 "res;";
8067 i::OS::SNPrintF(test_buf,
8068 boundary_program,
8069 low);
8070 result = CompileRun(test_buf.start());
8071 CHECK_EQ(low, result->IntegerValue());
8072
8073 i::OS::SNPrintF(test_buf,
8074 boundary_program,
8075 high);
8076 result = CompileRun(test_buf.start());
8077 CHECK_EQ(high, result->IntegerValue());
8078
8079 // Check misprediction of type in IC.
8080 result = CompileRun("var tmp_array = ext_array;"
8081 "var sum = 0;"
8082 "for (var i = 0; i < 8; i++) {"
8083 " tmp_array[i] = i;"
8084 " sum += tmp_array[i];"
8085 " if (i == 4) {"
8086 " tmp_array = {};"
8087 " }"
8088 "}"
8089 "sum;");
8090 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
8091 CHECK_EQ(28, result->Int32Value());
8092
8093 // Make sure out-of-range loads do not throw.
8094 i::OS::SNPrintF(test_buf,
8095 "var caught_exception = false;"
8096 "try {"
8097 " ext_array[%d];"
8098 "} catch (e) {"
8099 " caught_exception = true;"
8100 "}"
8101 "caught_exception;",
8102 kElementCount);
8103 result = CompileRun(test_buf.start());
8104 CHECK_EQ(false, result->BooleanValue());
8105
8106 // Make sure out-of-range stores do not throw.
8107 i::OS::SNPrintF(test_buf,
8108 "var caught_exception = false;"
8109 "try {"
8110 " ext_array[%d] = 1;"
8111 "} catch (e) {"
8112 " caught_exception = true;"
8113 "}"
8114 "caught_exception;",
8115 kElementCount);
8116 result = CompileRun(test_buf.start());
8117 CHECK_EQ(false, result->BooleanValue());
8118
8119 // Check other boundary conditions, values and operations.
8120 result = CompileRun("for (var i = 0; i < 8; i++) {"
8121 " ext_array[7] = undefined;"
8122 "}"
8123 "ext_array[7];");
8124 CHECK_EQ(0, result->Int32Value());
8125 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
8126
8127 result = CompileRun("for (var i = 0; i < 8; i++) {"
8128 " ext_array[6] = '2.3';"
8129 "}"
8130 "ext_array[6];");
8131 CHECK_EQ(2, result->Int32Value());
8132 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
8133
8134 if (array_type != v8::kExternalFloatArray) {
8135 // Though the specification doesn't state it, be explicit about
8136 // converting NaNs and +/-Infinity to zero.
8137 result = CompileRun("for (var i = 0; i < 8; i++) {"
8138 " ext_array[i] = 5;"
8139 "}"
8140 "for (var i = 0; i < 8; i++) {"
8141 " ext_array[i] = NaN;"
8142 "}"
8143 "ext_array[5];");
8144 CHECK_EQ(0, result->Int32Value());
8145 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
8146
8147 result = CompileRun("for (var i = 0; i < 8; i++) {"
8148 " ext_array[i] = 5;"
8149 "}"
8150 "for (var i = 0; i < 8; i++) {"
8151 " ext_array[i] = Infinity;"
8152 "}"
8153 "ext_array[5];");
8154 CHECK_EQ(0, result->Int32Value());
8155 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
8156
8157 result = CompileRun("for (var i = 0; i < 8; i++) {"
8158 " ext_array[i] = 5;"
8159 "}"
8160 "for (var i = 0; i < 8; i++) {"
8161 " ext_array[i] = -Infinity;"
8162 "}"
8163 "ext_array[5];");
8164 CHECK_EQ(0, result->Int32Value());
8165 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
8166 }
8167
8168 result = CompileRun("ext_array[3] = 33;"
8169 "delete ext_array[3];"
8170 "ext_array[3];");
8171 CHECK_EQ(33, result->Int32Value());
8172
8173 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
8174 "ext_array[2] = 12; ext_array[3] = 13;"
8175 "ext_array.__defineGetter__('2',"
8176 "function() { return 120; });"
8177 "ext_array[2];");
8178 CHECK_EQ(12, result->Int32Value());
8179
8180 result = CompileRun("var js_array = new Array(40);"
8181 "js_array[0] = 77;"
8182 "js_array;");
8183 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
8184
8185 result = CompileRun("ext_array[1] = 23;"
8186 "ext_array.__proto__ = [];"
8187 "js_array.__proto__ = ext_array;"
8188 "js_array.concat(ext_array);");
8189 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
8190 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
8191
8192 result = CompileRun("ext_array[1] = 23;");
8193 CHECK_EQ(23, result->Int32Value());
8194
Steve Blockd0582a62009-12-15 09:54:21 +00008195 // Test more complex manipulations which cause eax to contain values
8196 // that won't be completely overwritten by loads from the arrays.
8197 // This catches bugs in the instructions used for the KeyedLoadIC
8198 // for byte and word types.
8199 {
8200 const int kXSize = 300;
8201 const int kYSize = 300;
8202 const int kLargeElementCount = kXSize * kYSize * 4;
8203 ElementType* large_array_data =
8204 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
8205 i::Handle<ExternalArrayClass> large_array =
8206 i::Handle<ExternalArrayClass>::cast(
8207 i::Factory::NewExternalArray(kLargeElementCount,
8208 array_type,
8209 array_data));
8210 v8::Handle<v8::Object> large_obj = v8::Object::New();
8211 // Set the elements to be the external array.
8212 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
8213 array_type,
8214 kLargeElementCount);
8215 context->Global()->Set(v8_str("large_array"), large_obj);
8216 // Initialize contents of a few rows.
8217 for (int x = 0; x < 300; x++) {
8218 int row = 0;
8219 int offset = row * 300 * 4;
8220 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
8221 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
8222 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
8223 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
8224 row = 150;
8225 offset = row * 300 * 4;
8226 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
8227 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
8228 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
8229 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
8230 row = 298;
8231 offset = row * 300 * 4;
8232 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
8233 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
8234 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
8235 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
8236 }
8237 // The goal of the code below is to make "offset" large enough
8238 // that the computation of the index (which goes into eax) has
8239 // high bits set which will not be overwritten by a byte or short
8240 // load.
8241 result = CompileRun("var failed = false;"
8242 "var offset = 0;"
8243 "for (var i = 0; i < 300; i++) {"
8244 " if (large_array[4 * i] != 127 ||"
8245 " large_array[4 * i + 1] != 0 ||"
8246 " large_array[4 * i + 2] != 0 ||"
8247 " large_array[4 * i + 3] != 127) {"
8248 " failed = true;"
8249 " }"
8250 "}"
8251 "offset = 150 * 300 * 4;"
8252 "for (var i = 0; i < 300; i++) {"
8253 " if (large_array[offset + 4 * i] != 127 ||"
8254 " large_array[offset + 4 * i + 1] != 0 ||"
8255 " large_array[offset + 4 * i + 2] != 0 ||"
8256 " large_array[offset + 4 * i + 3] != 127) {"
8257 " failed = true;"
8258 " }"
8259 "}"
8260 "offset = 298 * 300 * 4;"
8261 "for (var i = 0; i < 300; i++) {"
8262 " if (large_array[offset + 4 * i] != 127 ||"
8263 " large_array[offset + 4 * i + 1] != 0 ||"
8264 " large_array[offset + 4 * i + 2] != 0 ||"
8265 " large_array[offset + 4 * i + 3] != 127) {"
8266 " failed = true;"
8267 " }"
8268 "}"
8269 "!failed;");
8270 CHECK_EQ(true, result->BooleanValue());
8271 free(large_array_data);
8272 }
8273
Steve Block3ce2e202009-11-05 08:53:23 +00008274 free(array_data);
8275}
8276
8277
8278THREADED_TEST(ExternalByteArray) {
8279 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>(
8280 v8::kExternalByteArray,
8281 -128,
8282 127);
8283}
8284
8285
8286THREADED_TEST(ExternalUnsignedByteArray) {
8287 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>(
8288 v8::kExternalUnsignedByteArray,
8289 0,
8290 255);
8291}
8292
8293
8294THREADED_TEST(ExternalShortArray) {
8295 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>(
8296 v8::kExternalShortArray,
8297 -32768,
8298 32767);
8299}
8300
8301
8302THREADED_TEST(ExternalUnsignedShortArray) {
8303 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>(
8304 v8::kExternalUnsignedShortArray,
8305 0,
8306 65535);
8307}
8308
8309
8310THREADED_TEST(ExternalIntArray) {
8311 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>(
8312 v8::kExternalIntArray,
8313 INT_MIN, // -2147483648
8314 INT_MAX); // 2147483647
8315}
8316
8317
8318THREADED_TEST(ExternalUnsignedIntArray) {
8319 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>(
8320 v8::kExternalUnsignedIntArray,
8321 0,
8322 UINT_MAX); // 4294967295
8323}
8324
8325
8326THREADED_TEST(ExternalFloatArray) {
8327 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>(
8328 v8::kExternalFloatArray,
8329 -500,
8330 500);
8331}
8332
8333
8334THREADED_TEST(ExternalArrays) {
8335 TestExternalByteArray();
8336 TestExternalUnsignedByteArray();
8337 TestExternalShortArray();
8338 TestExternalUnsignedShortArray();
8339 TestExternalIntArray();
8340 TestExternalUnsignedIntArray();
8341 TestExternalFloatArray();
8342}
8343
8344
Steve Blocka7e24c12009-10-30 11:49:00 +00008345THREADED_TEST(ScriptContextDependence) {
8346 v8::HandleScope scope;
8347 LocalContext c1;
8348 const char *source = "foo";
8349 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
8350 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
8351 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
8352 CHECK_EQ(dep->Run()->Int32Value(), 100);
8353 CHECK_EQ(indep->Run()->Int32Value(), 100);
8354 LocalContext c2;
8355 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
8356 CHECK_EQ(dep->Run()->Int32Value(), 100);
8357 CHECK_EQ(indep->Run()->Int32Value(), 101);
8358}
8359
8360
8361THREADED_TEST(StackTrace) {
8362 v8::HandleScope scope;
8363 LocalContext context;
8364 v8::TryCatch try_catch;
8365 const char *source = "function foo() { FAIL.FAIL; }; foo();";
8366 v8::Handle<v8::String> src = v8::String::New(source);
8367 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
8368 v8::Script::New(src, origin)->Run();
8369 CHECK(try_catch.HasCaught());
8370 v8::String::Utf8Value stack(try_catch.StackTrace());
8371 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
8372}
8373
8374
Steve Block3ce2e202009-11-05 08:53:23 +00008375// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +00008376THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +00008377 bool rv = false;
8378 for (int i = 0; i < 100; i++) {
8379 rv = v8::V8::IdleNotification();
8380 if (rv)
8381 break;
8382 }
8383 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +00008384}
8385
8386
8387static uint32_t* stack_limit;
8388
8389static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
8390 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
8391 return v8::Undefined();
8392}
8393
8394
8395// Uses the address of a local variable to determine the stack top now.
8396// Given a size, returns an address that is that far from the current
8397// top of stack.
8398static uint32_t* ComputeStackLimit(uint32_t size) {
8399 uint32_t* answer = &size - (size / sizeof(size));
8400 // If the size is very large and the stack is very near the bottom of
8401 // memory then the calculation above may wrap around and give an address
8402 // that is above the (downwards-growing) stack. In that case we return
8403 // a very low address.
8404 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
8405 return answer;
8406}
8407
8408
8409TEST(SetResourceConstraints) {
8410 static const int K = 1024;
8411 uint32_t* set_limit = ComputeStackLimit(128 * K);
8412
8413 // Set stack limit.
8414 v8::ResourceConstraints constraints;
8415 constraints.set_stack_limit(set_limit);
8416 CHECK(v8::SetResourceConstraints(&constraints));
8417
8418 // Execute a script.
8419 v8::HandleScope scope;
8420 LocalContext env;
8421 Local<v8::FunctionTemplate> fun_templ =
8422 v8::FunctionTemplate::New(GetStackLimitCallback);
8423 Local<Function> fun = fun_templ->GetFunction();
8424 env->Global()->Set(v8_str("get_stack_limit"), fun);
8425 CompileRun("get_stack_limit();");
8426
8427 CHECK(stack_limit == set_limit);
8428}
8429
8430
8431TEST(SetResourceConstraintsInThread) {
8432 uint32_t* set_limit;
8433 {
8434 v8::Locker locker;
8435 static const int K = 1024;
8436 set_limit = ComputeStackLimit(128 * K);
8437
8438 // Set stack limit.
8439 v8::ResourceConstraints constraints;
8440 constraints.set_stack_limit(set_limit);
8441 CHECK(v8::SetResourceConstraints(&constraints));
8442
8443 // Execute a script.
8444 v8::HandleScope scope;
8445 LocalContext env;
8446 Local<v8::FunctionTemplate> fun_templ =
8447 v8::FunctionTemplate::New(GetStackLimitCallback);
8448 Local<Function> fun = fun_templ->GetFunction();
8449 env->Global()->Set(v8_str("get_stack_limit"), fun);
8450 CompileRun("get_stack_limit();");
8451
8452 CHECK(stack_limit == set_limit);
8453 }
8454 {
8455 v8::Locker locker;
8456 CHECK(stack_limit == set_limit);
8457 }
8458}
Steve Block3ce2e202009-11-05 08:53:23 +00008459
8460
8461THREADED_TEST(GetHeapStatistics) {
8462 v8::HandleScope scope;
8463 LocalContext c1;
8464 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +00008465 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
8466 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +00008467 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +00008468 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
8469 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
8470}
8471
8472
8473static double DoubleFromBits(uint64_t value) {
8474 double target;
8475#ifdef BIG_ENDIAN_FLOATING_POINT
8476 const int kIntSize = 4;
8477 // Somebody swapped the lower and higher half of doubles.
8478 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
8479 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
8480#else
8481 memcpy(&target, &value, sizeof(target));
8482#endif
8483 return target;
8484}
8485
8486
8487static uint64_t DoubleToBits(double value) {
8488 uint64_t target;
8489#ifdef BIG_ENDIAN_FLOATING_POINT
8490 const int kIntSize = 4;
8491 // Somebody swapped the lower and higher half of doubles.
8492 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
8493 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
8494#else
8495 memcpy(&target, &value, sizeof(target));
8496#endif
8497 return target;
8498}
8499
8500
8501static double DoubleToDateTime(double input) {
8502 double date_limit = 864e13;
8503 if (IsNaN(input) || input < -date_limit || input > date_limit) {
8504 return i::OS::nan_value();
8505 }
8506 return (input < 0) ? -(floor(-input)) : floor(input);
8507}
8508
8509// We don't have a consistent way to write 64-bit constants syntactically, so we
8510// split them into two 32-bit constants and combine them programmatically.
8511static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
8512 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
8513}
8514
8515
8516THREADED_TEST(QuietSignalingNaNs) {
8517 v8::HandleScope scope;
8518 LocalContext context;
8519 v8::TryCatch try_catch;
8520
8521 // Special double values.
8522 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
8523 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
8524 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
8525 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
8526 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
8527 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
8528 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
8529
8530 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
8531 // on either side of the epoch.
8532 double date_limit = 864e13;
8533
8534 double test_values[] = {
8535 snan,
8536 qnan,
8537 infinity,
8538 max_normal,
8539 date_limit + 1,
8540 date_limit,
8541 min_normal,
8542 max_denormal,
8543 min_denormal,
8544 0,
8545 -0,
8546 -min_denormal,
8547 -max_denormal,
8548 -min_normal,
8549 -date_limit,
8550 -date_limit - 1,
8551 -max_normal,
8552 -infinity,
8553 -qnan,
8554 -snan
8555 };
8556 int num_test_values = 20;
8557
8558 for (int i = 0; i < num_test_values; i++) {
8559 double test_value = test_values[i];
8560
8561 // Check that Number::New preserves non-NaNs and quiets SNaNs.
8562 v8::Handle<v8::Value> number = v8::Number::New(test_value);
8563 double stored_number = number->NumberValue();
8564 if (!IsNaN(test_value)) {
8565 CHECK_EQ(test_value, stored_number);
8566 } else {
8567 uint64_t stored_bits = DoubleToBits(stored_number);
8568 // Check if quiet nan (bits 51..62 all set).
8569 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
8570 }
8571
8572 // Check that Date::New preserves non-NaNs in the date range and
8573 // quiets SNaNs.
8574 v8::Handle<v8::Value> date = v8::Date::New(test_value);
8575 double expected_stored_date = DoubleToDateTime(test_value);
8576 double stored_date = date->NumberValue();
8577 if (!IsNaN(expected_stored_date)) {
8578 CHECK_EQ(expected_stored_date, stored_date);
8579 } else {
8580 uint64_t stored_bits = DoubleToBits(stored_date);
8581 // Check if quiet nan (bits 51..62 all set).
8582 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
8583 }
8584 }
8585}
8586
8587
8588static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
8589 v8::HandleScope scope;
8590 v8::TryCatch tc;
8591 v8::Handle<v8::String> str = args[0]->ToString();
8592 if (tc.HasCaught())
8593 return tc.ReThrow();
8594 return v8::Undefined();
8595}
8596
8597
8598// Test that an exception can be propagated down through a spaghetti
8599// stack using ReThrow.
8600THREADED_TEST(SpaghettiStackReThrow) {
8601 v8::HandleScope scope;
8602 LocalContext context;
8603 context->Global()->Set(
8604 v8::String::New("s"),
8605 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
8606 v8::TryCatch try_catch;
8607 CompileRun(
8608 "var i = 0;"
8609 "var o = {"
8610 " toString: function () {"
8611 " if (i == 10) {"
8612 " throw 'Hey!';"
8613 " } else {"
8614 " i++;"
8615 " return s(o);"
8616 " }"
8617 " }"
8618 "};"
8619 "s(o);");
8620 CHECK(try_catch.HasCaught());
8621 v8::String::Utf8Value value(try_catch.Exception());
8622 CHECK_EQ(0, strcmp(*value, "Hey!"));
8623}
8624
8625
Steve Blockd0582a62009-12-15 09:54:21 +00008626TEST(Regress528) {
8627 v8::V8::Initialize();
8628
8629 v8::HandleScope scope;
8630 v8::Persistent<Context> context;
8631 v8::Persistent<Context> other_context;
8632 int gc_count;
8633
8634 // Create a context used to keep the code from aging in the compilation
8635 // cache.
8636 other_context = Context::New();
8637
8638 // Context-dependent context data creates reference from the compilation
8639 // cache to the global object.
8640 const char* source_simple = "1";
8641 context = Context::New();
8642 {
8643 v8::HandleScope scope;
8644
8645 context->Enter();
8646 Local<v8::String> obj = v8::String::New("");
8647 context->SetData(obj);
8648 CompileRun(source_simple);
8649 context->Exit();
8650 }
8651 context.Dispose();
8652 for (gc_count = 1; gc_count < 10; gc_count++) {
8653 other_context->Enter();
8654 CompileRun(source_simple);
8655 other_context->Exit();
8656 v8::internal::Heap::CollectAllGarbage(false);
8657 if (GetGlobalObjectsCount() == 1) break;
8658 }
8659 CHECK_GE(2, gc_count);
8660 CHECK_EQ(1, GetGlobalObjectsCount());
8661
8662 // Eval in a function creates reference from the compilation cache to the
8663 // global object.
8664 const char* source_eval = "function f(){eval('1')}; f()";
8665 context = Context::New();
8666 {
8667 v8::HandleScope scope;
8668
8669 context->Enter();
8670 CompileRun(source_eval);
8671 context->Exit();
8672 }
8673 context.Dispose();
8674 for (gc_count = 1; gc_count < 10; gc_count++) {
8675 other_context->Enter();
8676 CompileRun(source_eval);
8677 other_context->Exit();
8678 v8::internal::Heap::CollectAllGarbage(false);
8679 if (GetGlobalObjectsCount() == 1) break;
8680 }
8681 CHECK_GE(2, gc_count);
8682 CHECK_EQ(1, GetGlobalObjectsCount());
8683
8684 // Looking up the line number for an exception creates reference from the
8685 // compilation cache to the global object.
8686 const char* source_exception = "function f(){throw 1;} f()";
8687 context = Context::New();
8688 {
8689 v8::HandleScope scope;
8690
8691 context->Enter();
8692 v8::TryCatch try_catch;
8693 CompileRun(source_exception);
8694 CHECK(try_catch.HasCaught());
8695 v8::Handle<v8::Message> message = try_catch.Message();
8696 CHECK(!message.IsEmpty());
8697 CHECK_EQ(1, message->GetLineNumber());
8698 context->Exit();
8699 }
8700 context.Dispose();
8701 for (gc_count = 1; gc_count < 10; gc_count++) {
8702 other_context->Enter();
8703 CompileRun(source_exception);
8704 other_context->Exit();
8705 v8::internal::Heap::CollectAllGarbage(false);
8706 if (GetGlobalObjectsCount() == 1) break;
8707 }
8708 CHECK_GE(2, gc_count);
8709 CHECK_EQ(1, GetGlobalObjectsCount());
8710
8711 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +00008712}