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