blob: 80bc0743324ae8be8f2ed91bf037092daa4a4927 [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
Andrei Popescu74b3c142010-03-29 12:03:09 +01004353 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004354 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
Andrei Popescu74b3c142010-03-29 12:03:09 +01004361 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004362 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
Andrei Popescu74b3c142010-03-29 12:03:09 +01004401TEST(DetachAndReattachGlobal) {
4402 v8::HandleScope scope;
4403 LocalContext env1;
4404
4405 // Create second environment.
4406 v8::Persistent<Context> env2 = Context::New();
4407
4408 Local<Value> foo = v8_str("foo");
4409
4410 // Set same security token for env1 and env2.
4411 env1->SetSecurityToken(foo);
4412 env2->SetSecurityToken(foo);
4413
4414 // Create a property on the global object in env2.
4415 {
4416 v8::Context::Scope scope(env2);
4417 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4418 }
4419
4420 // Create a reference to env2 global from env1 global.
4421 env1->Global()->Set(v8_str("other"), env2->Global());
4422
4423 // Check that we have access to other.p in env2 from env1.
4424 Local<Value> result = CompileRun("other.p");
4425 CHECK(result->IsInt32());
4426 CHECK_EQ(42, result->Int32Value());
4427
4428 // Hold on to global from env2 and detach global from env2.
4429 Local<v8::Object> global2 = env2->Global();
4430 env2->DetachGlobal();
4431
4432 // Check that the global has been detached. No other.p property can
4433 // be found.
4434 result = CompileRun("other.p");
4435 CHECK(result->IsUndefined());
4436
4437 // Reuse global2 for env3.
4438 v8::Persistent<Context> env3 =
4439 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4440 CHECK_EQ(global2, env3->Global());
4441
4442 // Start by using the same security token for env3 as for env1 and env2.
4443 env3->SetSecurityToken(foo);
4444
4445 // Create a property on the global object in env3.
4446 {
4447 v8::Context::Scope scope(env3);
4448 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4449 }
4450
4451 // Check that other.p is now the property in env3 and that we have access.
4452 result = CompileRun("other.p");
4453 CHECK(result->IsInt32());
4454 CHECK_EQ(24, result->Int32Value());
4455
4456 // Change security token for env3 to something different from env1 and env2.
4457 env3->SetSecurityToken(v8_str("bar"));
4458
4459 // Check that we do not have access to other.p in env1. |other| is now
4460 // the global object for env3 which has a different security token,
4461 // so access should be blocked.
4462 result = CompileRun("other.p");
4463 CHECK(result->IsUndefined());
4464
4465 // Detach the global for env3 and reattach it to env2.
4466 env3->DetachGlobal();
4467 env2->ReattachGlobal(global2);
4468
4469 // Check that we have access to other.p again in env1. |other| is now
4470 // the global object for env2 which has the same security token as env1.
4471 result = CompileRun("other.p");
4472 CHECK(result->IsInt32());
4473 CHECK_EQ(42, result->Int32Value());
4474
4475 env2.Dispose();
4476 env3.Dispose();
4477}
4478
4479
Steve Blocka7e24c12009-10-30 11:49:00 +00004480static bool NamedAccessBlocker(Local<v8::Object> global,
4481 Local<Value> name,
4482 v8::AccessType type,
4483 Local<Value> data) {
4484 return Context::GetCurrent()->Global()->Equals(global);
4485}
4486
4487
4488static bool IndexedAccessBlocker(Local<v8::Object> global,
4489 uint32_t key,
4490 v8::AccessType type,
4491 Local<Value> data) {
4492 return Context::GetCurrent()->Global()->Equals(global);
4493}
4494
4495
4496static int g_echo_value = -1;
4497static v8::Handle<Value> EchoGetter(Local<String> name,
4498 const AccessorInfo& info) {
4499 return v8_num(g_echo_value);
4500}
4501
4502
4503static void EchoSetter(Local<String> name,
4504 Local<Value> value,
4505 const AccessorInfo&) {
4506 if (value->IsNumber())
4507 g_echo_value = value->Int32Value();
4508}
4509
4510
4511static v8::Handle<Value> UnreachableGetter(Local<String> name,
4512 const AccessorInfo& info) {
4513 CHECK(false); // This function should not be called..
4514 return v8::Undefined();
4515}
4516
4517
4518static void UnreachableSetter(Local<String>, Local<Value>,
4519 const AccessorInfo&) {
4520 CHECK(false); // This function should nto be called.
4521}
4522
4523
4524THREADED_TEST(AccessControl) {
4525 v8::HandleScope handle_scope;
4526 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4527
4528 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4529 IndexedAccessBlocker);
4530
4531 // Add an accessor accessible by cross-domain JS code.
4532 global_template->SetAccessor(
4533 v8_str("accessible_prop"),
4534 EchoGetter, EchoSetter,
4535 v8::Handle<Value>(),
4536 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4537
4538 // Add an accessor that is not accessible by cross-domain JS code.
4539 global_template->SetAccessor(v8_str("blocked_prop"),
4540 UnreachableGetter, UnreachableSetter,
4541 v8::Handle<Value>(),
4542 v8::DEFAULT);
4543
4544 // Create an environment
4545 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4546 context0->Enter();
4547
4548 v8::Handle<v8::Object> global0 = context0->Global();
4549
4550 v8::HandleScope scope1;
4551
4552 v8::Persistent<Context> context1 = Context::New();
4553 context1->Enter();
4554
4555 v8::Handle<v8::Object> global1 = context1->Global();
4556 global1->Set(v8_str("other"), global0);
4557
4558 v8::Handle<Value> value;
4559
4560 // Access blocked property
4561 value = v8_compile("other.blocked_prop = 1")->Run();
4562 value = v8_compile("other.blocked_prop")->Run();
4563 CHECK(value->IsUndefined());
4564
4565 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4566 CHECK(value->IsFalse());
4567
4568 // Access accessible property
4569 value = v8_compile("other.accessible_prop = 3")->Run();
4570 CHECK(value->IsNumber());
4571 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00004572 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00004573
4574 value = v8_compile("other.accessible_prop")->Run();
4575 CHECK(value->IsNumber());
4576 CHECK_EQ(3, value->Int32Value());
4577
4578 value =
4579 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
4580 CHECK(value->IsTrue());
4581
4582 // Enumeration doesn't enumerate accessors from inaccessible objects in
4583 // the prototype chain even if the accessors are in themselves accessible.
4584 Local<Value> result =
4585 CompileRun("(function(){var obj = {'__proto__':other};"
4586 "for (var p in obj)"
4587 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
4588 " return false;"
4589 " }"
4590 "return true;})()");
4591 CHECK(result->IsTrue());
4592
4593 context1->Exit();
4594 context0->Exit();
4595 context1.Dispose();
4596 context0.Dispose();
4597}
4598
4599
Leon Clarke4515c472010-02-03 11:58:03 +00004600static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
4601 Local<Value> name,
4602 v8::AccessType type,
4603 Local<Value> data) {
4604 return false;
4605}
4606
4607
4608static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
4609 uint32_t key,
4610 v8::AccessType type,
4611 Local<Value> data) {
4612 return false;
4613}
4614
4615
4616THREADED_TEST(AccessControlGetOwnPropertyNames) {
4617 v8::HandleScope handle_scope;
4618 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
4619
4620 obj_template->Set(v8_str("x"), v8::Integer::New(42));
4621 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
4622 GetOwnPropertyNamesIndexedBlocker);
4623
4624 // Create an environment
4625 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
4626 context0->Enter();
4627
4628 v8::Handle<v8::Object> global0 = context0->Global();
4629
4630 v8::HandleScope scope1;
4631
4632 v8::Persistent<Context> context1 = Context::New();
4633 context1->Enter();
4634
4635 v8::Handle<v8::Object> global1 = context1->Global();
4636 global1->Set(v8_str("other"), global0);
4637 global1->Set(v8_str("object"), obj_template->NewInstance());
4638
4639 v8::Handle<Value> value;
4640
4641 // Attempt to get the property names of the other global object and
4642 // of an object that requires access checks. Accessing the other
4643 // global object should be blocked by access checks on the global
4644 // proxy object. Accessing the object that requires access checks
4645 // is blocked by the access checks on the object itself.
4646 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
4647 CHECK(value->IsTrue());
4648
4649 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
4650 CHECK(value->IsTrue());
4651
4652 context1->Exit();
4653 context0->Exit();
4654 context1.Dispose();
4655 context0.Dispose();
4656}
4657
4658
Steve Blocka7e24c12009-10-30 11:49:00 +00004659static v8::Handle<Value> ConstTenGetter(Local<String> name,
4660 const AccessorInfo& info) {
4661 return v8_num(10);
4662}
4663
4664
4665THREADED_TEST(CrossDomainAccessors) {
4666 v8::HandleScope handle_scope;
4667
4668 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
4669
4670 v8::Handle<v8::ObjectTemplate> global_template =
4671 func_template->InstanceTemplate();
4672
4673 v8::Handle<v8::ObjectTemplate> proto_template =
4674 func_template->PrototypeTemplate();
4675
4676 // Add an accessor to proto that's accessible by cross-domain JS code.
4677 proto_template->SetAccessor(v8_str("accessible"),
4678 ConstTenGetter, 0,
4679 v8::Handle<Value>(),
4680 v8::ALL_CAN_READ);
4681
4682 // Add an accessor that is not accessible by cross-domain JS code.
4683 global_template->SetAccessor(v8_str("unreachable"),
4684 UnreachableGetter, 0,
4685 v8::Handle<Value>(),
4686 v8::DEFAULT);
4687
4688 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4689 context0->Enter();
4690
4691 Local<v8::Object> global = context0->Global();
4692 // Add a normal property that shadows 'accessible'
4693 global->Set(v8_str("accessible"), v8_num(11));
4694
4695 // Enter a new context.
4696 v8::HandleScope scope1;
4697 v8::Persistent<Context> context1 = Context::New();
4698 context1->Enter();
4699
4700 v8::Handle<v8::Object> global1 = context1->Global();
4701 global1->Set(v8_str("other"), global);
4702
4703 // Should return 10, instead of 11
4704 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
4705 CHECK(value->IsNumber());
4706 CHECK_EQ(10, value->Int32Value());
4707
4708 value = v8_compile("other.unreachable")->Run();
4709 CHECK(value->IsUndefined());
4710
4711 context1->Exit();
4712 context0->Exit();
4713 context1.Dispose();
4714 context0.Dispose();
4715}
4716
4717
4718static int named_access_count = 0;
4719static int indexed_access_count = 0;
4720
4721static bool NamedAccessCounter(Local<v8::Object> global,
4722 Local<Value> name,
4723 v8::AccessType type,
4724 Local<Value> data) {
4725 named_access_count++;
4726 return true;
4727}
4728
4729
4730static bool IndexedAccessCounter(Local<v8::Object> global,
4731 uint32_t key,
4732 v8::AccessType type,
4733 Local<Value> data) {
4734 indexed_access_count++;
4735 return true;
4736}
4737
4738
4739// This one is too easily disturbed by other tests.
4740TEST(AccessControlIC) {
4741 named_access_count = 0;
4742 indexed_access_count = 0;
4743
4744 v8::HandleScope handle_scope;
4745
4746 // Create an environment.
4747 v8::Persistent<Context> context0 = Context::New();
4748 context0->Enter();
4749
4750 // Create an object that requires access-check functions to be
4751 // called for cross-domain access.
4752 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4753 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
4754 IndexedAccessCounter);
4755 Local<v8::Object> object = object_template->NewInstance();
4756
4757 v8::HandleScope scope1;
4758
4759 // Create another environment.
4760 v8::Persistent<Context> context1 = Context::New();
4761 context1->Enter();
4762
4763 // Make easy access to the object from the other environment.
4764 v8::Handle<v8::Object> global1 = context1->Global();
4765 global1->Set(v8_str("obj"), object);
4766
4767 v8::Handle<Value> value;
4768
4769 // Check that the named access-control function is called every time.
4770 CompileRun("function testProp(obj) {"
4771 " for (var i = 0; i < 10; i++) obj.prop = 1;"
4772 " for (var j = 0; j < 10; j++) obj.prop;"
4773 " return obj.prop"
4774 "}");
4775 value = CompileRun("testProp(obj)");
4776 CHECK(value->IsNumber());
4777 CHECK_EQ(1, value->Int32Value());
4778 CHECK_EQ(21, named_access_count);
4779
4780 // Check that the named access-control function is called every time.
4781 CompileRun("var p = 'prop';"
4782 "function testKeyed(obj) {"
4783 " for (var i = 0; i < 10; i++) obj[p] = 1;"
4784 " for (var j = 0; j < 10; j++) obj[p];"
4785 " return obj[p];"
4786 "}");
4787 // Use obj which requires access checks. No inline caching is used
4788 // in that case.
4789 value = CompileRun("testKeyed(obj)");
4790 CHECK(value->IsNumber());
4791 CHECK_EQ(1, value->Int32Value());
4792 CHECK_EQ(42, named_access_count);
4793 // Force the inline caches into generic state and try again.
4794 CompileRun("testKeyed({ a: 0 })");
4795 CompileRun("testKeyed({ b: 0 })");
4796 value = CompileRun("testKeyed(obj)");
4797 CHECK(value->IsNumber());
4798 CHECK_EQ(1, value->Int32Value());
4799 CHECK_EQ(63, named_access_count);
4800
4801 // Check that the indexed access-control function is called every time.
4802 CompileRun("function testIndexed(obj) {"
4803 " for (var i = 0; i < 10; i++) obj[0] = 1;"
4804 " for (var j = 0; j < 10; j++) obj[0];"
4805 " return obj[0]"
4806 "}");
4807 value = CompileRun("testIndexed(obj)");
4808 CHECK(value->IsNumber());
4809 CHECK_EQ(1, value->Int32Value());
4810 CHECK_EQ(21, indexed_access_count);
4811 // Force the inline caches into generic state.
4812 CompileRun("testIndexed(new Array(1))");
4813 // Test that the indexed access check is called.
4814 value = CompileRun("testIndexed(obj)");
4815 CHECK(value->IsNumber());
4816 CHECK_EQ(1, value->Int32Value());
4817 CHECK_EQ(42, indexed_access_count);
4818
4819 // Check that the named access check is called when invoking
4820 // functions on an object that requires access checks.
4821 CompileRun("obj.f = function() {}");
4822 CompileRun("function testCallNormal(obj) {"
4823 " for (var i = 0; i < 10; i++) obj.f();"
4824 "}");
4825 CompileRun("testCallNormal(obj)");
4826 CHECK_EQ(74, named_access_count);
4827
4828 // Force obj into slow case.
4829 value = CompileRun("delete obj.prop");
4830 CHECK(value->BooleanValue());
4831 // Force inline caches into dictionary probing mode.
4832 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
4833 // Test that the named access check is called.
4834 value = CompileRun("testProp(obj);");
4835 CHECK(value->IsNumber());
4836 CHECK_EQ(1, value->Int32Value());
4837 CHECK_EQ(96, named_access_count);
4838
4839 // Force the call inline cache into dictionary probing mode.
4840 CompileRun("o.f = function() {}; testCallNormal(o)");
4841 // Test that the named access check is still called for each
4842 // invocation of the function.
4843 value = CompileRun("testCallNormal(obj)");
4844 CHECK_EQ(106, named_access_count);
4845
4846 context1->Exit();
4847 context0->Exit();
4848 context1.Dispose();
4849 context0.Dispose();
4850}
4851
4852
4853static bool NamedAccessFlatten(Local<v8::Object> global,
4854 Local<Value> name,
4855 v8::AccessType type,
4856 Local<Value> data) {
4857 char buf[100];
4858 int len;
4859
4860 CHECK(name->IsString());
4861
4862 memset(buf, 0x1, sizeof(buf));
4863 len = Local<String>::Cast(name)->WriteAscii(buf);
4864 CHECK_EQ(4, len);
4865
4866 uint16_t buf2[100];
4867
4868 memset(buf, 0x1, sizeof(buf));
4869 len = Local<String>::Cast(name)->Write(buf2);
4870 CHECK_EQ(4, len);
4871
4872 return true;
4873}
4874
4875
4876static bool IndexedAccessFlatten(Local<v8::Object> global,
4877 uint32_t key,
4878 v8::AccessType type,
4879 Local<Value> data) {
4880 return true;
4881}
4882
4883
4884// Regression test. In access checks, operations that may cause
4885// garbage collection are not allowed. It used to be the case that
4886// using the Write operation on a string could cause a garbage
4887// collection due to flattening of the string. This is no longer the
4888// case.
4889THREADED_TEST(AccessControlFlatten) {
4890 named_access_count = 0;
4891 indexed_access_count = 0;
4892
4893 v8::HandleScope handle_scope;
4894
4895 // Create an environment.
4896 v8::Persistent<Context> context0 = Context::New();
4897 context0->Enter();
4898
4899 // Create an object that requires access-check functions to be
4900 // called for cross-domain access.
4901 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4902 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
4903 IndexedAccessFlatten);
4904 Local<v8::Object> object = object_template->NewInstance();
4905
4906 v8::HandleScope scope1;
4907
4908 // Create another environment.
4909 v8::Persistent<Context> context1 = Context::New();
4910 context1->Enter();
4911
4912 // Make easy access to the object from the other environment.
4913 v8::Handle<v8::Object> global1 = context1->Global();
4914 global1->Set(v8_str("obj"), object);
4915
4916 v8::Handle<Value> value;
4917
4918 value = v8_compile("var p = 'as' + 'df';")->Run();
4919 value = v8_compile("obj[p];")->Run();
4920
4921 context1->Exit();
4922 context0->Exit();
4923 context1.Dispose();
4924 context0.Dispose();
4925}
4926
4927
4928static v8::Handle<Value> AccessControlNamedGetter(
4929 Local<String>, const AccessorInfo&) {
4930 return v8::Integer::New(42);
4931}
4932
4933
4934static v8::Handle<Value> AccessControlNamedSetter(
4935 Local<String>, Local<Value> value, const AccessorInfo&) {
4936 return value;
4937}
4938
4939
4940static v8::Handle<Value> AccessControlIndexedGetter(
4941 uint32_t index,
4942 const AccessorInfo& info) {
4943 return v8_num(42);
4944}
4945
4946
4947static v8::Handle<Value> AccessControlIndexedSetter(
4948 uint32_t, Local<Value> value, const AccessorInfo&) {
4949 return value;
4950}
4951
4952
4953THREADED_TEST(AccessControlInterceptorIC) {
4954 named_access_count = 0;
4955 indexed_access_count = 0;
4956
4957 v8::HandleScope handle_scope;
4958
4959 // Create an environment.
4960 v8::Persistent<Context> context0 = Context::New();
4961 context0->Enter();
4962
4963 // Create an object that requires access-check functions to be
4964 // called for cross-domain access. The object also has interceptors
4965 // interceptor.
4966 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
4967 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
4968 IndexedAccessCounter);
4969 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
4970 AccessControlNamedSetter);
4971 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
4972 AccessControlIndexedSetter);
4973 Local<v8::Object> object = object_template->NewInstance();
4974
4975 v8::HandleScope scope1;
4976
4977 // Create another environment.
4978 v8::Persistent<Context> context1 = Context::New();
4979 context1->Enter();
4980
4981 // Make easy access to the object from the other environment.
4982 v8::Handle<v8::Object> global1 = context1->Global();
4983 global1->Set(v8_str("obj"), object);
4984
4985 v8::Handle<Value> value;
4986
4987 // Check that the named access-control function is called every time
4988 // eventhough there is an interceptor on the object.
4989 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
4990 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
4991 "obj.x")->Run();
4992 CHECK(value->IsNumber());
4993 CHECK_EQ(42, value->Int32Value());
4994 CHECK_EQ(21, named_access_count);
4995
4996 value = v8_compile("var p = 'x';")->Run();
4997 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
4998 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
4999 "obj[p]")->Run();
5000 CHECK(value->IsNumber());
5001 CHECK_EQ(42, value->Int32Value());
5002 CHECK_EQ(42, named_access_count);
5003
5004 // Check that the indexed access-control function is called every
5005 // time eventhough there is an interceptor on the object.
5006 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5007 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5008 "obj[0]")->Run();
5009 CHECK(value->IsNumber());
5010 CHECK_EQ(42, value->Int32Value());
5011 CHECK_EQ(21, indexed_access_count);
5012
5013 context1->Exit();
5014 context0->Exit();
5015 context1.Dispose();
5016 context0.Dispose();
5017}
5018
5019
5020THREADED_TEST(Version) {
5021 v8::V8::GetVersion();
5022}
5023
5024
5025static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5026 ApiTestFuzzer::Fuzz();
5027 return v8_num(12);
5028}
5029
5030
5031THREADED_TEST(InstanceProperties) {
5032 v8::HandleScope handle_scope;
5033 LocalContext context;
5034
5035 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5036 Local<ObjectTemplate> instance = t->InstanceTemplate();
5037
5038 instance->Set(v8_str("x"), v8_num(42));
5039 instance->Set(v8_str("f"),
5040 v8::FunctionTemplate::New(InstanceFunctionCallback));
5041
5042 Local<Value> o = t->GetFunction()->NewInstance();
5043
5044 context->Global()->Set(v8_str("i"), o);
5045 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5046 CHECK_EQ(42, value->Int32Value());
5047
5048 value = Script::Compile(v8_str("i.f()"))->Run();
5049 CHECK_EQ(12, value->Int32Value());
5050}
5051
5052
5053static v8::Handle<Value>
5054GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5055 ApiTestFuzzer::Fuzz();
5056 return v8::Handle<Value>();
5057}
5058
5059
5060THREADED_TEST(GlobalObjectInstanceProperties) {
5061 v8::HandleScope handle_scope;
5062
5063 Local<Value> global_object;
5064
5065 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5066 t->InstanceTemplate()->SetNamedPropertyHandler(
5067 GlobalObjectInstancePropertiesGet);
5068 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5069 instance_template->Set(v8_str("x"), v8_num(42));
5070 instance_template->Set(v8_str("f"),
5071 v8::FunctionTemplate::New(InstanceFunctionCallback));
5072
5073 {
5074 LocalContext env(NULL, instance_template);
5075 // Hold on to the global object so it can be used again in another
5076 // environment initialization.
5077 global_object = env->Global();
5078
5079 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5080 CHECK_EQ(42, value->Int32Value());
5081 value = Script::Compile(v8_str("f()"))->Run();
5082 CHECK_EQ(12, value->Int32Value());
5083 }
5084
5085 {
5086 // Create new environment reusing the global object.
5087 LocalContext env(NULL, instance_template, global_object);
5088 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5089 CHECK_EQ(42, value->Int32Value());
5090 value = Script::Compile(v8_str("f()"))->Run();
5091 CHECK_EQ(12, value->Int32Value());
5092 }
5093}
5094
5095
5096static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5097 ApiTestFuzzer::Fuzz();
5098 return v8_num(42);
5099}
5100
5101
5102static int shadow_y;
5103static int shadow_y_setter_call_count;
5104static int shadow_y_getter_call_count;
5105
5106
5107static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5108 shadow_y_setter_call_count++;
5109 shadow_y = 42;
5110}
5111
5112
5113static v8::Handle<Value> ShadowYGetter(Local<String> name,
5114 const AccessorInfo& info) {
5115 ApiTestFuzzer::Fuzz();
5116 shadow_y_getter_call_count++;
5117 return v8_num(shadow_y);
5118}
5119
5120
5121static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5122 const AccessorInfo& info) {
5123 return v8::Handle<Value>();
5124}
5125
5126
5127static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5128 const AccessorInfo&) {
5129 return v8::Handle<Value>();
5130}
5131
5132
5133THREADED_TEST(ShadowObject) {
5134 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5135 v8::HandleScope handle_scope;
5136
5137 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5138 LocalContext context(NULL, global_template);
5139
5140 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5141 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5142 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5143 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5144 Local<ObjectTemplate> instance = t->InstanceTemplate();
5145
5146 // Only allow calls of f on instances of t.
5147 Local<v8::Signature> signature = v8::Signature::New(t);
5148 proto->Set(v8_str("f"),
5149 v8::FunctionTemplate::New(ShadowFunctionCallback,
5150 Local<Value>(),
5151 signature));
5152 proto->Set(v8_str("x"), v8_num(12));
5153
5154 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5155
5156 Local<Value> o = t->GetFunction()->NewInstance();
5157 context->Global()->Set(v8_str("__proto__"), o);
5158
5159 Local<Value> value =
5160 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5161 CHECK(value->IsBoolean());
5162 CHECK(!value->BooleanValue());
5163
5164 value = Script::Compile(v8_str("x"))->Run();
5165 CHECK_EQ(12, value->Int32Value());
5166
5167 value = Script::Compile(v8_str("f()"))->Run();
5168 CHECK_EQ(42, value->Int32Value());
5169
5170 Script::Compile(v8_str("y = 42"))->Run();
5171 CHECK_EQ(1, shadow_y_setter_call_count);
5172 value = Script::Compile(v8_str("y"))->Run();
5173 CHECK_EQ(1, shadow_y_getter_call_count);
5174 CHECK_EQ(42, value->Int32Value());
5175}
5176
5177
5178THREADED_TEST(HiddenPrototype) {
5179 v8::HandleScope handle_scope;
5180 LocalContext context;
5181
5182 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5183 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5184 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5185 t1->SetHiddenPrototype(true);
5186 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5187 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5188 t2->SetHiddenPrototype(true);
5189 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5190 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5191 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5192
5193 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5194 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5195 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5196 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5197
5198 // Setting the prototype on an object skips hidden prototypes.
5199 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5200 o0->Set(v8_str("__proto__"), o1);
5201 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5202 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5203 o0->Set(v8_str("__proto__"), o2);
5204 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5205 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5206 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5207 o0->Set(v8_str("__proto__"), o3);
5208 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5209 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5210 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5211 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5212
5213 // Getting the prototype of o0 should get the first visible one
5214 // which is o3. Therefore, z should not be defined on the prototype
5215 // object.
5216 Local<Value> proto = o0->Get(v8_str("__proto__"));
5217 CHECK(proto->IsObject());
5218 CHECK(Local<v8::Object>::Cast(proto)->Get(v8_str("z"))->IsUndefined());
5219}
5220
5221
Andrei Popescu402d9372010-02-26 13:31:12 +00005222THREADED_TEST(SetPrototype) {
5223 v8::HandleScope handle_scope;
5224 LocalContext context;
5225
5226 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5227 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5228 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5229 t1->SetHiddenPrototype(true);
5230 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5231 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5232 t2->SetHiddenPrototype(true);
5233 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5234 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5235 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5236
5237 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5238 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5239 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5240 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5241
5242 // Setting the prototype on an object does not skip hidden prototypes.
5243 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5244 CHECK(o0->SetPrototype(o1));
5245 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5246 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5247 CHECK(o1->SetPrototype(o2));
5248 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5249 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5250 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5251 CHECK(o2->SetPrototype(o3));
5252 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5253 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5254 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5255 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5256
5257 // Getting the prototype of o0 should get the first visible one
5258 // which is o3. Therefore, z should not be defined on the prototype
5259 // object.
5260 Local<Value> proto = o0->Get(v8_str("__proto__"));
5261 CHECK(proto->IsObject());
5262 CHECK_EQ(v8::Handle<v8::Object>::Cast(proto), o3);
5263
5264 // However, Object::GetPrototype ignores hidden prototype.
5265 Local<Value> proto0 = o0->GetPrototype();
5266 CHECK(proto0->IsObject());
5267 CHECK_EQ(v8::Handle<v8::Object>::Cast(proto0), o1);
5268
5269 Local<Value> proto1 = o1->GetPrototype();
5270 CHECK(proto1->IsObject());
5271 CHECK_EQ(v8::Handle<v8::Object>::Cast(proto1), o2);
5272
5273 Local<Value> proto2 = o2->GetPrototype();
5274 CHECK(proto2->IsObject());
5275 CHECK_EQ(v8::Handle<v8::Object>::Cast(proto2), o3);
5276}
5277
5278
5279THREADED_TEST(SetPrototypeThrows) {
5280 v8::HandleScope handle_scope;
5281 LocalContext context;
5282
5283 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5284
5285 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5286 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5287
5288 CHECK(o0->SetPrototype(o1));
5289 // If setting the prototype leads to the cycle, SetPrototype should
5290 // return false and keep VM in sane state.
5291 v8::TryCatch try_catch;
5292 CHECK(!o1->SetPrototype(o0));
5293 CHECK(!try_catch.HasCaught());
5294 ASSERT(!i::Top::has_pending_exception());
5295
5296 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5297}
5298
5299
Steve Blocka7e24c12009-10-30 11:49:00 +00005300THREADED_TEST(GetterSetterExceptions) {
5301 v8::HandleScope handle_scope;
5302 LocalContext context;
5303 CompileRun(
5304 "function Foo() { };"
5305 "function Throw() { throw 5; };"
5306 "var x = { };"
5307 "x.__defineSetter__('set', Throw);"
5308 "x.__defineGetter__('get', Throw);");
5309 Local<v8::Object> x =
5310 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5311 v8::TryCatch try_catch;
5312 x->Set(v8_str("set"), v8::Integer::New(8));
5313 x->Get(v8_str("get"));
5314 x->Set(v8_str("set"), v8::Integer::New(8));
5315 x->Get(v8_str("get"));
5316 x->Set(v8_str("set"), v8::Integer::New(8));
5317 x->Get(v8_str("get"));
5318 x->Set(v8_str("set"), v8::Integer::New(8));
5319 x->Get(v8_str("get"));
5320}
5321
5322
5323THREADED_TEST(Constructor) {
5324 v8::HandleScope handle_scope;
5325 LocalContext context;
5326 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5327 templ->SetClassName(v8_str("Fun"));
5328 Local<Function> cons = templ->GetFunction();
5329 context->Global()->Set(v8_str("Fun"), cons);
5330 Local<v8::Object> inst = cons->NewInstance();
5331 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5332 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5333 CHECK(value->BooleanValue());
5334}
5335
5336THREADED_TEST(FunctionDescriptorException) {
5337 v8::HandleScope handle_scope;
5338 LocalContext context;
5339 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5340 templ->SetClassName(v8_str("Fun"));
5341 Local<Function> cons = templ->GetFunction();
5342 context->Global()->Set(v8_str("Fun"), cons);
5343 Local<Value> value = CompileRun(
5344 "function test() {"
5345 " try {"
5346 " (new Fun()).blah()"
5347 " } catch (e) {"
5348 " var str = String(e);"
5349 " if (str.indexOf('TypeError') == -1) return 1;"
5350 " if (str.indexOf('[object Fun]') != -1) return 2;"
5351 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5352 " return 0;"
5353 " }"
5354 " return 4;"
5355 "}"
5356 "test();");
5357 CHECK_EQ(0, value->Int32Value());
5358}
5359
5360
5361THREADED_TEST(EvalAliasedDynamic) {
5362 v8::HandleScope scope;
5363 LocalContext current;
5364
5365 // Tests where aliased eval can only be resolved dynamically.
5366 Local<Script> script =
5367 Script::Compile(v8_str("function f(x) { "
5368 " var foo = 2;"
5369 " with (x) { return eval('foo'); }"
5370 "}"
5371 "foo = 0;"
5372 "result1 = f(new Object());"
5373 "result2 = f(this);"
5374 "var x = new Object();"
5375 "x.eval = function(x) { return 1; };"
5376 "result3 = f(x);"));
5377 script->Run();
5378 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5379 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5380 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5381
5382 v8::TryCatch try_catch;
5383 script =
5384 Script::Compile(v8_str("function f(x) { "
5385 " var bar = 2;"
5386 " with (x) { return eval('bar'); }"
5387 "}"
5388 "f(this)"));
5389 script->Run();
5390 CHECK(try_catch.HasCaught());
5391 try_catch.Reset();
5392}
5393
5394
5395THREADED_TEST(CrossEval) {
5396 v8::HandleScope scope;
5397 LocalContext other;
5398 LocalContext current;
5399
5400 Local<String> token = v8_str("<security token>");
5401 other->SetSecurityToken(token);
5402 current->SetSecurityToken(token);
5403
5404 // Setup reference from current to other.
5405 current->Global()->Set(v8_str("other"), other->Global());
5406
5407 // Check that new variables are introduced in other context.
5408 Local<Script> script =
5409 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5410 script->Run();
5411 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5412 CHECK_EQ(1234, foo->Int32Value());
5413 CHECK(!current->Global()->Has(v8_str("foo")));
5414
5415 // Check that writing to non-existing properties introduces them in
5416 // the other context.
5417 script =
5418 Script::Compile(v8_str("other.eval('na = 1234')"));
5419 script->Run();
5420 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5421 CHECK(!current->Global()->Has(v8_str("na")));
5422
5423 // Check that global variables in current context are not visible in other
5424 // context.
5425 v8::TryCatch try_catch;
5426 script =
5427 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5428 Local<Value> result = script->Run();
5429 CHECK(try_catch.HasCaught());
5430 try_catch.Reset();
5431
5432 // Check that local variables in current context are not visible in other
5433 // context.
5434 script =
5435 Script::Compile(v8_str("(function() { "
5436 " var baz = 87;"
5437 " return other.eval('baz');"
5438 "})();"));
5439 result = script->Run();
5440 CHECK(try_catch.HasCaught());
5441 try_catch.Reset();
5442
5443 // Check that global variables in the other environment are visible
5444 // when evaluting code.
5445 other->Global()->Set(v8_str("bis"), v8_num(1234));
5446 script = Script::Compile(v8_str("other.eval('bis')"));
5447 CHECK_EQ(1234, script->Run()->Int32Value());
5448 CHECK(!try_catch.HasCaught());
5449
5450 // Check that the 'this' pointer points to the global object evaluating
5451 // code.
5452 other->Global()->Set(v8_str("t"), other->Global());
5453 script = Script::Compile(v8_str("other.eval('this == t')"));
5454 result = script->Run();
5455 CHECK(result->IsTrue());
5456 CHECK(!try_catch.HasCaught());
5457
5458 // Check that variables introduced in with-statement are not visible in
5459 // other context.
5460 script =
5461 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
5462 result = script->Run();
5463 CHECK(try_catch.HasCaught());
5464 try_catch.Reset();
5465
5466 // Check that you cannot use 'eval.call' with another object than the
5467 // current global object.
5468 script =
5469 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
5470 result = script->Run();
5471 CHECK(try_catch.HasCaught());
5472}
5473
5474
5475// Test that calling eval in a context which has been detached from
5476// its global throws an exception. This behavior is consistent with
5477// other JavaScript implementations.
5478THREADED_TEST(EvalInDetachedGlobal) {
5479 v8::HandleScope scope;
5480
5481 v8::Persistent<Context> context0 = Context::New();
5482 v8::Persistent<Context> context1 = Context::New();
5483
5484 // Setup function in context0 that uses eval from context0.
5485 context0->Enter();
5486 v8::Handle<v8::Value> fun =
5487 CompileRun("var x = 42;"
5488 "(function() {"
5489 " var e = eval;"
5490 " return function(s) { return e(s); }"
5491 "})()");
5492 context0->Exit();
5493
5494 // Put the function into context1 and call it before and after
5495 // detaching the global. Before detaching, the call succeeds and
5496 // after detaching and exception is thrown.
5497 context1->Enter();
5498 context1->Global()->Set(v8_str("fun"), fun);
5499 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
5500 CHECK_EQ(42, x_value->Int32Value());
5501 context0->DetachGlobal();
5502 v8::TryCatch catcher;
5503 x_value = CompileRun("fun('x')");
5504 CHECK(x_value.IsEmpty());
5505 CHECK(catcher.HasCaught());
5506 context1->Exit();
5507
5508 context1.Dispose();
5509 context0.Dispose();
5510}
5511
5512
5513THREADED_TEST(CrossLazyLoad) {
5514 v8::HandleScope scope;
5515 LocalContext other;
5516 LocalContext current;
5517
5518 Local<String> token = v8_str("<security token>");
5519 other->SetSecurityToken(token);
5520 current->SetSecurityToken(token);
5521
5522 // Setup reference from current to other.
5523 current->Global()->Set(v8_str("other"), other->Global());
5524
5525 // Trigger lazy loading in other context.
5526 Local<Script> script =
5527 Script::Compile(v8_str("other.eval('new Date(42)')"));
5528 Local<Value> value = script->Run();
5529 CHECK_EQ(42.0, value->NumberValue());
5530}
5531
5532
5533static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00005534 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00005535 if (args.IsConstructCall()) {
5536 if (args[0]->IsInt32()) {
5537 return v8_num(-args[0]->Int32Value());
5538 }
5539 }
5540
5541 return args[0];
5542}
5543
5544
5545// Test that a call handler can be set for objects which will allow
5546// non-function objects created through the API to be called as
5547// functions.
5548THREADED_TEST(CallAsFunction) {
5549 v8::HandleScope scope;
5550 LocalContext context;
5551
5552 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5553 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5554 instance_template->SetCallAsFunctionHandler(call_as_function);
5555 Local<v8::Object> instance = t->GetFunction()->NewInstance();
5556 context->Global()->Set(v8_str("obj"), instance);
5557 v8::TryCatch try_catch;
5558 Local<Value> value;
5559 CHECK(!try_catch.HasCaught());
5560
5561 value = CompileRun("obj(42)");
5562 CHECK(!try_catch.HasCaught());
5563 CHECK_EQ(42, value->Int32Value());
5564
5565 value = CompileRun("(function(o){return o(49)})(obj)");
5566 CHECK(!try_catch.HasCaught());
5567 CHECK_EQ(49, value->Int32Value());
5568
5569 // test special case of call as function
5570 value = CompileRun("[obj]['0'](45)");
5571 CHECK(!try_catch.HasCaught());
5572 CHECK_EQ(45, value->Int32Value());
5573
5574 value = CompileRun("obj.call = Function.prototype.call;"
5575 "obj.call(null, 87)");
5576 CHECK(!try_catch.HasCaught());
5577 CHECK_EQ(87, value->Int32Value());
5578
5579 // Regression tests for bug #1116356: Calling call through call/apply
5580 // must work for non-function receivers.
5581 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
5582 value = CompileRun(apply_99);
5583 CHECK(!try_catch.HasCaught());
5584 CHECK_EQ(99, value->Int32Value());
5585
5586 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
5587 value = CompileRun(call_17);
5588 CHECK(!try_catch.HasCaught());
5589 CHECK_EQ(17, value->Int32Value());
5590
5591 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00005592 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00005593 value = CompileRun("new obj(43)");
5594 CHECK(!try_catch.HasCaught());
5595 CHECK_EQ(-43, value->Int32Value());
5596}
5597
5598
5599static int CountHandles() {
5600 return v8::HandleScope::NumberOfHandles();
5601}
5602
5603
5604static int Recurse(int depth, int iterations) {
5605 v8::HandleScope scope;
5606 if (depth == 0) return CountHandles();
5607 for (int i = 0; i < iterations; i++) {
5608 Local<v8::Number> n = v8::Integer::New(42);
5609 }
5610 return Recurse(depth - 1, iterations);
5611}
5612
5613
5614THREADED_TEST(HandleIteration) {
5615 static const int kIterations = 500;
5616 static const int kNesting = 200;
5617 CHECK_EQ(0, CountHandles());
5618 {
5619 v8::HandleScope scope1;
5620 CHECK_EQ(0, CountHandles());
5621 for (int i = 0; i < kIterations; i++) {
5622 Local<v8::Number> n = v8::Integer::New(42);
5623 CHECK_EQ(i + 1, CountHandles());
5624 }
5625
5626 CHECK_EQ(kIterations, CountHandles());
5627 {
5628 v8::HandleScope scope2;
5629 for (int j = 0; j < kIterations; j++) {
5630 Local<v8::Number> n = v8::Integer::New(42);
5631 CHECK_EQ(j + 1 + kIterations, CountHandles());
5632 }
5633 }
5634 CHECK_EQ(kIterations, CountHandles());
5635 }
5636 CHECK_EQ(0, CountHandles());
5637 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
5638}
5639
5640
5641static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
5642 Local<String> name,
5643 const AccessorInfo& info) {
5644 ApiTestFuzzer::Fuzz();
5645 return v8::Handle<Value>();
5646}
5647
5648
5649THREADED_TEST(InterceptorHasOwnProperty) {
5650 v8::HandleScope scope;
5651 LocalContext context;
5652 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
5653 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5654 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
5655 Local<Function> function = fun_templ->GetFunction();
5656 context->Global()->Set(v8_str("constructor"), function);
5657 v8::Handle<Value> value = CompileRun(
5658 "var o = new constructor();"
5659 "o.hasOwnProperty('ostehaps');");
5660 CHECK_EQ(false, value->BooleanValue());
5661 value = CompileRun(
5662 "o.ostehaps = 42;"
5663 "o.hasOwnProperty('ostehaps');");
5664 CHECK_EQ(true, value->BooleanValue());
5665 value = CompileRun(
5666 "var p = new constructor();"
5667 "p.hasOwnProperty('ostehaps');");
5668 CHECK_EQ(false, value->BooleanValue());
5669}
5670
5671
5672static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
5673 Local<String> name,
5674 const AccessorInfo& info) {
5675 ApiTestFuzzer::Fuzz();
5676 i::Heap::CollectAllGarbage(false);
5677 return v8::Handle<Value>();
5678}
5679
5680
5681THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
5682 v8::HandleScope scope;
5683 LocalContext context;
5684 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
5685 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
5686 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
5687 Local<Function> function = fun_templ->GetFunction();
5688 context->Global()->Set(v8_str("constructor"), function);
5689 // Let's first make some stuff so we can be sure to get a good GC.
5690 CompileRun(
5691 "function makestr(size) {"
5692 " switch (size) {"
5693 " case 1: return 'f';"
5694 " case 2: return 'fo';"
5695 " case 3: return 'foo';"
5696 " }"
5697 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
5698 "}"
5699 "var x = makestr(12345);"
5700 "x = makestr(31415);"
5701 "x = makestr(23456);");
5702 v8::Handle<Value> value = CompileRun(
5703 "var o = new constructor();"
5704 "o.__proto__ = new String(x);"
5705 "o.hasOwnProperty('ostehaps');");
5706 CHECK_EQ(false, value->BooleanValue());
5707}
5708
5709
5710typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
5711 const AccessorInfo& info);
5712
5713
5714static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
5715 const char* source,
5716 int expected) {
5717 v8::HandleScope scope;
5718 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5719 templ->SetNamedPropertyHandler(getter);
5720 LocalContext context;
5721 context->Global()->Set(v8_str("o"), templ->NewInstance());
5722 v8::Handle<Value> value = CompileRun(source);
5723 CHECK_EQ(expected, value->Int32Value());
5724}
5725
5726
5727static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
5728 const AccessorInfo& info) {
5729 ApiTestFuzzer::Fuzz();
5730 CHECK(v8_str("x")->Equals(name));
5731 return v8::Integer::New(42);
5732}
5733
5734
5735// This test should hit the load IC for the interceptor case.
5736THREADED_TEST(InterceptorLoadIC) {
5737 CheckInterceptorLoadIC(InterceptorLoadICGetter,
5738 "var result = 0;"
5739 "for (var i = 0; i < 1000; i++) {"
5740 " result = o.x;"
5741 "}",
5742 42);
5743}
5744
5745
5746// Below go several tests which verify that JITing for various
5747// configurations of interceptor and explicit fields works fine
5748// (those cases are special cased to get better performance).
5749
5750static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
5751 const AccessorInfo& info) {
5752 ApiTestFuzzer::Fuzz();
5753 return v8_str("x")->Equals(name)
5754 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
5755}
5756
5757
5758THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
5759 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5760 "var result = 0;"
5761 "o.y = 239;"
5762 "for (var i = 0; i < 1000; i++) {"
5763 " result = o.y;"
5764 "}",
5765 239);
5766}
5767
5768
5769THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
5770 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5771 "var result = 0;"
5772 "o.__proto__ = { 'y': 239 };"
5773 "for (var i = 0; i < 1000; i++) {"
5774 " result = o.y + o.x;"
5775 "}",
5776 239 + 42);
5777}
5778
5779
5780THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
5781 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5782 "var result = 0;"
5783 "o.__proto__.y = 239;"
5784 "for (var i = 0; i < 1000; i++) {"
5785 " result = o.y + o.x;"
5786 "}",
5787 239 + 42);
5788}
5789
5790
5791THREADED_TEST(InterceptorLoadICUndefined) {
5792 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5793 "var result = 0;"
5794 "for (var i = 0; i < 1000; i++) {"
5795 " result = (o.y == undefined) ? 239 : 42;"
5796 "}",
5797 239);
5798}
5799
5800
5801THREADED_TEST(InterceptorLoadICWithOverride) {
5802 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5803 "fst = new Object(); fst.__proto__ = o;"
5804 "snd = new Object(); snd.__proto__ = fst;"
5805 "var result1 = 0;"
5806 "for (var i = 0; i < 1000; i++) {"
5807 " result1 = snd.x;"
5808 "}"
5809 "fst.x = 239;"
5810 "var result = 0;"
5811 "for (var i = 0; i < 1000; i++) {"
5812 " result = snd.x;"
5813 "}"
5814 "result + result1",
5815 239 + 42);
5816}
5817
5818
5819// Test the case when we stored field into
5820// a stub, but interceptor produced value on its own.
5821THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
5822 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5823 "proto = new Object();"
5824 "o.__proto__ = proto;"
5825 "proto.x = 239;"
5826 "for (var i = 0; i < 1000; i++) {"
5827 " o.x;"
5828 // Now it should be ICed and keep a reference to x defined on proto
5829 "}"
5830 "var result = 0;"
5831 "for (var i = 0; i < 1000; i++) {"
5832 " result += o.x;"
5833 "}"
5834 "result;",
5835 42 * 1000);
5836}
5837
5838
5839// Test the case when we stored field into
5840// a stub, but it got invalidated later on.
5841THREADED_TEST(InterceptorLoadICInvalidatedField) {
5842 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5843 "proto1 = new Object();"
5844 "proto2 = new Object();"
5845 "o.__proto__ = proto1;"
5846 "proto1.__proto__ = proto2;"
5847 "proto2.y = 239;"
5848 "for (var i = 0; i < 1000; i++) {"
5849 " o.y;"
5850 // Now it should be ICed and keep a reference to y defined on proto2
5851 "}"
5852 "proto1.y = 42;"
5853 "var result = 0;"
5854 "for (var i = 0; i < 1000; i++) {"
5855 " result += o.y;"
5856 "}"
5857 "result;",
5858 42 * 1000);
5859}
5860
5861
5862// Test the case when we stored field into
5863// a stub, but it got invalidated later on due to override on
5864// global object which is between interceptor and fields' holders.
5865THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
5866 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
5867 "o.__proto__ = this;" // set a global to be a proto of o.
5868 "this.__proto__.y = 239;"
5869 "for (var i = 0; i < 10; i++) {"
5870 " if (o.y != 239) throw 'oops: ' + o.y;"
5871 // Now it should be ICed and keep a reference to y defined on field_holder.
5872 "}"
5873 "this.y = 42;" // Assign on a global.
5874 "var result = 0;"
5875 "for (var i = 0; i < 10; i++) {"
5876 " result += o.y;"
5877 "}"
5878 "result;",
5879 42 * 10);
5880}
5881
5882
5883static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
5884 ApiTestFuzzer::Fuzz();
5885 return v8_num(239);
5886}
5887
5888
5889static void SetOnThis(Local<String> name,
5890 Local<Value> value,
5891 const AccessorInfo& info) {
5892 info.This()->ForceSet(name, value);
5893}
5894
5895
5896THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
5897 v8::HandleScope scope;
5898 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5899 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5900 templ->SetAccessor(v8_str("y"), Return239);
5901 LocalContext context;
5902 context->Global()->Set(v8_str("o"), templ->NewInstance());
5903 v8::Handle<Value> value = CompileRun(
5904 "var result = 0;"
5905 "for (var i = 0; i < 7; i++) {"
5906 " result = o.y;"
5907 "}");
5908 CHECK_EQ(239, value->Int32Value());
5909}
5910
5911
5912THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
5913 v8::HandleScope scope;
5914 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5915 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5916 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5917 templ_p->SetAccessor(v8_str("y"), Return239);
5918
5919 LocalContext context;
5920 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5921 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5922
5923 v8::Handle<Value> value = CompileRun(
5924 "o.__proto__ = p;"
5925 "var result = 0;"
5926 "for (var i = 0; i < 7; i++) {"
5927 " result = o.x + o.y;"
5928 "}");
5929 CHECK_EQ(239 + 42, value->Int32Value());
5930}
5931
5932
5933THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
5934 v8::HandleScope scope;
5935 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
5936 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5937 templ->SetAccessor(v8_str("y"), Return239);
5938
5939 LocalContext context;
5940 context->Global()->Set(v8_str("o"), templ->NewInstance());
5941
5942 v8::Handle<Value> value = CompileRun(
5943 "fst = new Object(); fst.__proto__ = o;"
5944 "snd = new Object(); snd.__proto__ = fst;"
5945 "var result1 = 0;"
5946 "for (var i = 0; i < 7; i++) {"
5947 " result1 = snd.x;"
5948 "}"
5949 "fst.x = 239;"
5950 "var result = 0;"
5951 "for (var i = 0; i < 7; i++) {"
5952 " result = snd.x;"
5953 "}"
5954 "result + result1");
5955 CHECK_EQ(239 + 42, value->Int32Value());
5956}
5957
5958
5959// Test the case when we stored callback into
5960// a stub, but interceptor produced value on its own.
5961THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
5962 v8::HandleScope scope;
5963 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5964 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5965 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5966 templ_p->SetAccessor(v8_str("y"), Return239);
5967
5968 LocalContext context;
5969 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5970 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5971
5972 v8::Handle<Value> value = CompileRun(
5973 "o.__proto__ = p;"
5974 "for (var i = 0; i < 7; i++) {"
5975 " o.x;"
5976 // Now it should be ICed and keep a reference to x defined on p
5977 "}"
5978 "var result = 0;"
5979 "for (var i = 0; i < 7; i++) {"
5980 " result += o.x;"
5981 "}"
5982 "result");
5983 CHECK_EQ(42 * 7, value->Int32Value());
5984}
5985
5986
5987// Test the case when we stored callback into
5988// a stub, but it got invalidated later on.
5989THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
5990 v8::HandleScope scope;
5991 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
5992 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
5993 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
5994 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
5995
5996 LocalContext context;
5997 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
5998 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
5999
6000 v8::Handle<Value> value = CompileRun(
6001 "inbetween = new Object();"
6002 "o.__proto__ = inbetween;"
6003 "inbetween.__proto__ = p;"
6004 "for (var i = 0; i < 10; i++) {"
6005 " o.y;"
6006 // Now it should be ICed and keep a reference to y defined on p
6007 "}"
6008 "inbetween.y = 42;"
6009 "var result = 0;"
6010 "for (var i = 0; i < 10; i++) {"
6011 " result += o.y;"
6012 "}"
6013 "result");
6014 CHECK_EQ(42 * 10, value->Int32Value());
6015}
6016
6017
6018// Test the case when we stored callback into
6019// a stub, but it got invalidated later on due to override on
6020// global object which is between interceptor and callbacks' holders.
6021THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6022 v8::HandleScope scope;
6023 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6024 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6025 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6026 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6027
6028 LocalContext context;
6029 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6030 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6031
6032 v8::Handle<Value> value = CompileRun(
6033 "o.__proto__ = this;"
6034 "this.__proto__ = p;"
6035 "for (var i = 0; i < 10; i++) {"
6036 " if (o.y != 239) throw 'oops: ' + o.y;"
6037 // Now it should be ICed and keep a reference to y defined on p
6038 "}"
6039 "this.y = 42;"
6040 "var result = 0;"
6041 "for (var i = 0; i < 10; i++) {"
6042 " result += o.y;"
6043 "}"
6044 "result");
6045 CHECK_EQ(42 * 10, value->Int32Value());
6046}
6047
6048
6049static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6050 const AccessorInfo& info) {
6051 ApiTestFuzzer::Fuzz();
6052 CHECK(v8_str("x")->Equals(name));
6053 return v8::Integer::New(0);
6054}
6055
6056
6057THREADED_TEST(InterceptorReturningZero) {
6058 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6059 "o.x == undefined ? 1 : 0",
6060 0);
6061}
6062
6063
6064static v8::Handle<Value> InterceptorStoreICSetter(
6065 Local<String> key, Local<Value> value, const AccessorInfo&) {
6066 CHECK(v8_str("x")->Equals(key));
6067 CHECK_EQ(42, value->Int32Value());
6068 return value;
6069}
6070
6071
6072// This test should hit the store IC for the interceptor case.
6073THREADED_TEST(InterceptorStoreIC) {
6074 v8::HandleScope scope;
6075 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6076 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6077 InterceptorStoreICSetter);
6078 LocalContext context;
6079 context->Global()->Set(v8_str("o"), templ->NewInstance());
6080 v8::Handle<Value> value = CompileRun(
6081 "for (var i = 0; i < 1000; i++) {"
6082 " o.x = 42;"
6083 "}");
6084}
6085
6086
6087THREADED_TEST(InterceptorStoreICWithNoSetter) {
6088 v8::HandleScope scope;
6089 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6090 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6091 LocalContext context;
6092 context->Global()->Set(v8_str("o"), templ->NewInstance());
6093 v8::Handle<Value> value = CompileRun(
6094 "for (var i = 0; i < 1000; i++) {"
6095 " o.y = 239;"
6096 "}"
6097 "42 + o.y");
6098 CHECK_EQ(239 + 42, value->Int32Value());
6099}
6100
6101
6102
6103
6104v8::Handle<Value> call_ic_function;
6105v8::Handle<Value> call_ic_function2;
6106v8::Handle<Value> call_ic_function3;
6107
6108static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6109 const AccessorInfo& info) {
6110 ApiTestFuzzer::Fuzz();
6111 CHECK(v8_str("x")->Equals(name));
6112 return call_ic_function;
6113}
6114
6115
6116// This test should hit the call IC for the interceptor case.
6117THREADED_TEST(InterceptorCallIC) {
6118 v8::HandleScope scope;
6119 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6120 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6121 LocalContext context;
6122 context->Global()->Set(v8_str("o"), templ->NewInstance());
6123 call_ic_function =
6124 v8_compile("function f(x) { return x + 1; }; f")->Run();
6125 v8::Handle<Value> value = CompileRun(
6126 "var result = 0;"
6127 "for (var i = 0; i < 1000; i++) {"
6128 " result = o.x(41);"
6129 "}");
6130 CHECK_EQ(42, value->Int32Value());
6131}
6132
6133
6134// This test checks that if interceptor doesn't provide
6135// a value, we can fetch regular value.
6136THREADED_TEST(InterceptorCallICSeesOthers) {
6137 v8::HandleScope scope;
6138 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6139 templ->SetNamedPropertyHandler(NoBlockGetterX);
6140 LocalContext context;
6141 context->Global()->Set(v8_str("o"), templ->NewInstance());
6142 v8::Handle<Value> value = CompileRun(
6143 "o.x = function f(x) { return x + 1; };"
6144 "var result = 0;"
6145 "for (var i = 0; i < 7; i++) {"
6146 " result = o.x(41);"
6147 "}");
6148 CHECK_EQ(42, value->Int32Value());
6149}
6150
6151
6152static v8::Handle<Value> call_ic_function4;
6153static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6154 const AccessorInfo& info) {
6155 ApiTestFuzzer::Fuzz();
6156 CHECK(v8_str("x")->Equals(name));
6157 return call_ic_function4;
6158}
6159
6160
6161// This test checks that if interceptor provides a function,
6162// even if we cached shadowed variant, interceptor's function
6163// is invoked
6164THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6165 v8::HandleScope scope;
6166 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6167 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6168 LocalContext context;
6169 context->Global()->Set(v8_str("o"), templ->NewInstance());
6170 call_ic_function4 =
6171 v8_compile("function f(x) { return x - 1; }; f")->Run();
6172 v8::Handle<Value> value = CompileRun(
6173 "o.__proto__.x = function(x) { return x + 1; };"
6174 "var result = 0;"
6175 "for (var i = 0; i < 1000; i++) {"
6176 " result = o.x(42);"
6177 "}");
6178 CHECK_EQ(41, value->Int32Value());
6179}
6180
6181
6182// Test the case when we stored cacheable lookup into
6183// a stub, but it got invalidated later on
6184THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6185 v8::HandleScope scope;
6186 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6187 templ->SetNamedPropertyHandler(NoBlockGetterX);
6188 LocalContext context;
6189 context->Global()->Set(v8_str("o"), templ->NewInstance());
6190 v8::Handle<Value> value = CompileRun(
6191 "proto1 = new Object();"
6192 "proto2 = new Object();"
6193 "o.__proto__ = proto1;"
6194 "proto1.__proto__ = proto2;"
6195 "proto2.y = function(x) { return x + 1; };"
6196 // Invoke it many times to compile a stub
6197 "for (var i = 0; i < 7; i++) {"
6198 " o.y(42);"
6199 "}"
6200 "proto1.y = function(x) { return x - 1; };"
6201 "var result = 0;"
6202 "for (var i = 0; i < 7; i++) {"
6203 " result += o.y(42);"
6204 "}");
6205 CHECK_EQ(41 * 7, value->Int32Value());
6206}
6207
6208
6209static v8::Handle<Value> call_ic_function5;
6210static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6211 const AccessorInfo& info) {
6212 ApiTestFuzzer::Fuzz();
6213 if (v8_str("x")->Equals(name))
6214 return call_ic_function5;
6215 else
6216 return Local<Value>();
6217}
6218
6219
6220// This test checks that if interceptor doesn't provide a function,
6221// cached constant function is used
6222THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6223 v8::HandleScope scope;
6224 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6225 templ->SetNamedPropertyHandler(NoBlockGetterX);
6226 LocalContext context;
6227 context->Global()->Set(v8_str("o"), templ->NewInstance());
6228 v8::Handle<Value> value = CompileRun(
6229 "function inc(x) { return x + 1; };"
6230 "inc(1);"
6231 "o.x = inc;"
6232 "var result = 0;"
6233 "for (var i = 0; i < 1000; i++) {"
6234 " result = o.x(42);"
6235 "}");
6236 CHECK_EQ(43, value->Int32Value());
6237}
6238
6239
6240// This test checks that if interceptor provides a function,
6241// even if we cached constant function, interceptor's function
6242// is invoked
6243THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6244 v8::HandleScope scope;
6245 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6246 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6247 LocalContext context;
6248 context->Global()->Set(v8_str("o"), templ->NewInstance());
6249 call_ic_function5 =
6250 v8_compile("function f(x) { return x - 1; }; f")->Run();
6251 v8::Handle<Value> value = CompileRun(
6252 "function inc(x) { return x + 1; };"
6253 "inc(1);"
6254 "o.x = inc;"
6255 "var result = 0;"
6256 "for (var i = 0; i < 1000; i++) {"
6257 " result = o.x(42);"
6258 "}");
6259 CHECK_EQ(41, value->Int32Value());
6260}
6261
6262
6263// Test the case when we stored constant function into
6264// a stub, but it got invalidated later on
6265THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6266 v8::HandleScope scope;
6267 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6268 templ->SetNamedPropertyHandler(NoBlockGetterX);
6269 LocalContext context;
6270 context->Global()->Set(v8_str("o"), templ->NewInstance());
6271 v8::Handle<Value> value = CompileRun(
6272 "function inc(x) { return x + 1; };"
6273 "inc(1);"
6274 "proto1 = new Object();"
6275 "proto2 = new Object();"
6276 "o.__proto__ = proto1;"
6277 "proto1.__proto__ = proto2;"
6278 "proto2.y = inc;"
6279 // Invoke it many times to compile a stub
6280 "for (var i = 0; i < 7; i++) {"
6281 " o.y(42);"
6282 "}"
6283 "proto1.y = function(x) { return x - 1; };"
6284 "var result = 0;"
6285 "for (var i = 0; i < 7; i++) {"
6286 " result += o.y(42);"
6287 "}");
6288 CHECK_EQ(41 * 7, value->Int32Value());
6289}
6290
6291
6292// Test the case when we stored constant function into
6293// a stub, but it got invalidated later on due to override on
6294// global object which is between interceptor and constant function' holders.
6295THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6296 v8::HandleScope scope;
6297 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6298 templ->SetNamedPropertyHandler(NoBlockGetterX);
6299 LocalContext context;
6300 context->Global()->Set(v8_str("o"), templ->NewInstance());
6301 v8::Handle<Value> value = CompileRun(
6302 "function inc(x) { return x + 1; };"
6303 "inc(1);"
6304 "o.__proto__ = this;"
6305 "this.__proto__.y = inc;"
6306 // Invoke it many times to compile a stub
6307 "for (var i = 0; i < 7; i++) {"
6308 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6309 "}"
6310 "this.y = function(x) { return x - 1; };"
6311 "var result = 0;"
6312 "for (var i = 0; i < 7; i++) {"
6313 " result += o.y(42);"
6314 "}");
6315 CHECK_EQ(41 * 7, value->Int32Value());
6316}
6317
6318
Leon Clarke4515c472010-02-03 11:58:03 +00006319// Test the case when actual function to call sits on global object.
6320THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6321 v8::HandleScope scope;
6322 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6323 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6324
6325 LocalContext context;
6326 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6327
6328 v8::Handle<Value> value = CompileRun(
6329 "try {"
6330 " o.__proto__ = this;"
6331 " for (var i = 0; i < 10; i++) {"
6332 " var v = o.parseFloat('239');"
6333 " if (v != 239) throw v;"
6334 // Now it should be ICed and keep a reference to parseFloat.
6335 " }"
6336 " var result = 0;"
6337 " for (var i = 0; i < 10; i++) {"
6338 " result += o.parseFloat('239');"
6339 " }"
6340 " result"
6341 "} catch(e) {"
6342 " e"
6343 "};");
6344 CHECK_EQ(239 * 10, value->Int32Value());
6345}
6346
Andrei Popescu402d9372010-02-26 13:31:12 +00006347static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6348 const AccessorInfo& info) {
6349 ApiTestFuzzer::Fuzz();
6350 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6351 ++(*call_count);
6352 if ((*call_count) % 20 == 0) {
6353 v8::internal::Heap::CollectAllGarbage(true);
6354 }
6355 return v8::Handle<Value>();
6356}
6357
6358static v8::Handle<Value> FastApiCallback_TrivialSignature(
6359 const v8::Arguments& args) {
6360 ApiTestFuzzer::Fuzz();
6361 CHECK_EQ(args.This(), args.Holder());
6362 CHECK(args.Data()->Equals(v8_str("method_data")));
6363 return v8::Integer::New(args[0]->Int32Value() + 1);
6364}
6365
6366static v8::Handle<Value> FastApiCallback_SimpleSignature(
6367 const v8::Arguments& args) {
6368 ApiTestFuzzer::Fuzz();
6369 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6370 CHECK(args.Data()->Equals(v8_str("method_data")));
6371 // Note, we're using HasRealNamedProperty instead of Has to avoid
6372 // invoking the interceptor again.
6373 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6374 return v8::Integer::New(args[0]->Int32Value() + 1);
6375}
6376
6377// Helper to maximize the odds of object moving.
6378static void GenerateSomeGarbage() {
6379 CompileRun(
6380 "var garbage;"
6381 "for (var i = 0; i < 1000; i++) {"
6382 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6383 "}"
6384 "garbage = undefined;");
6385}
6386
6387THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
6388 int interceptor_call_count = 0;
6389 v8::HandleScope scope;
6390 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6391 v8::Handle<v8::FunctionTemplate> method_templ =
6392 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6393 v8_str("method_data"),
6394 v8::Handle<v8::Signature>());
6395 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6396 proto_templ->Set(v8_str("method"), method_templ);
6397 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6398 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6399 NULL, NULL, NULL, NULL,
6400 v8::External::Wrap(&interceptor_call_count));
6401 LocalContext context;
6402 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6403 GenerateSomeGarbage();
6404 context->Global()->Set(v8_str("o"), fun->NewInstance());
6405 v8::Handle<Value> value = CompileRun(
6406 "var result = 0;"
6407 "for (var i = 0; i < 100; i++) {"
6408 " result = o.method(41);"
6409 "}");
6410 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6411 CHECK_EQ(100, interceptor_call_count);
6412}
6413
6414THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
6415 int interceptor_call_count = 0;
6416 v8::HandleScope scope;
6417 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6418 v8::Handle<v8::FunctionTemplate> method_templ =
6419 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6420 v8_str("method_data"),
6421 v8::Signature::New(fun_templ));
6422 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6423 proto_templ->Set(v8_str("method"), method_templ);
6424 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6425 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6426 NULL, NULL, NULL, NULL,
6427 v8::External::Wrap(&interceptor_call_count));
6428 LocalContext context;
6429 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6430 GenerateSomeGarbage();
6431 context->Global()->Set(v8_str("o"), fun->NewInstance());
6432 v8::Handle<Value> value = CompileRun(
6433 "o.foo = 17;"
6434 "var receiver = {};"
6435 "receiver.__proto__ = o;"
6436 "var result = 0;"
6437 "for (var i = 0; i < 100; i++) {"
6438 " result = receiver.method(41);"
6439 "}");
6440 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6441 CHECK_EQ(100, interceptor_call_count);
6442}
6443
6444THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
6445 int interceptor_call_count = 0;
6446 v8::HandleScope scope;
6447 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6448 v8::Handle<v8::FunctionTemplate> method_templ =
6449 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6450 v8_str("method_data"),
6451 v8::Signature::New(fun_templ));
6452 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6453 proto_templ->Set(v8_str("method"), method_templ);
6454 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6455 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6456 NULL, NULL, NULL, NULL,
6457 v8::External::Wrap(&interceptor_call_count));
6458 LocalContext context;
6459 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6460 GenerateSomeGarbage();
6461 context->Global()->Set(v8_str("o"), fun->NewInstance());
6462 v8::Handle<Value> value = CompileRun(
6463 "o.foo = 17;"
6464 "var receiver = {};"
6465 "receiver.__proto__ = o;"
6466 "var result = 0;"
6467 "var saved_result = 0;"
6468 "for (var i = 0; i < 100; i++) {"
6469 " result = receiver.method(41);"
6470 " if (i == 50) {"
6471 " saved_result = result;"
6472 " receiver = {method: function(x) { return x - 1 }};"
6473 " }"
6474 "}");
6475 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6476 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6477 CHECK_GE(interceptor_call_count, 50);
6478}
6479
6480THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
6481 int interceptor_call_count = 0;
6482 v8::HandleScope scope;
6483 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6484 v8::Handle<v8::FunctionTemplate> method_templ =
6485 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6486 v8_str("method_data"),
6487 v8::Signature::New(fun_templ));
6488 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6489 proto_templ->Set(v8_str("method"), method_templ);
6490 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6491 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6492 NULL, NULL, NULL, NULL,
6493 v8::External::Wrap(&interceptor_call_count));
6494 LocalContext context;
6495 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6496 GenerateSomeGarbage();
6497 context->Global()->Set(v8_str("o"), fun->NewInstance());
6498 v8::Handle<Value> value = CompileRun(
6499 "o.foo = 17;"
6500 "var receiver = {};"
6501 "receiver.__proto__ = o;"
6502 "var result = 0;"
6503 "var saved_result = 0;"
6504 "for (var i = 0; i < 100; i++) {"
6505 " result = receiver.method(41);"
6506 " if (i == 50) {"
6507 " saved_result = result;"
6508 " o.method = function(x) { return x - 1 };"
6509 " }"
6510 "}");
6511 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6512 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6513 CHECK_GE(interceptor_call_count, 50);
6514}
6515
6516THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
6517 int interceptor_call_count = 0;
6518 v8::HandleScope scope;
6519 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6520 v8::Handle<v8::FunctionTemplate> method_templ =
6521 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6522 v8_str("method_data"),
6523 v8::Signature::New(fun_templ));
6524 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6525 proto_templ->Set(v8_str("method"), method_templ);
6526 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6527 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6528 NULL, NULL, NULL, NULL,
6529 v8::External::Wrap(&interceptor_call_count));
6530 LocalContext context;
6531 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6532 GenerateSomeGarbage();
6533 context->Global()->Set(v8_str("o"), fun->NewInstance());
6534 v8::TryCatch try_catch;
6535 v8::Handle<Value> value = CompileRun(
6536 "o.foo = 17;"
6537 "var receiver = {};"
6538 "receiver.__proto__ = o;"
6539 "var result = 0;"
6540 "var saved_result = 0;"
6541 "for (var i = 0; i < 100; i++) {"
6542 " result = receiver.method(41);"
6543 " if (i == 50) {"
6544 " saved_result = result;"
6545 " receiver = {method: receiver.method};"
6546 " }"
6547 "}");
6548 CHECK(try_catch.HasCaught());
6549 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
6550 try_catch.Exception()->ToString());
6551 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6552 CHECK_GE(interceptor_call_count, 50);
6553}
6554
6555THREADED_TEST(CallICFastApi_TrivialSignature) {
6556 v8::HandleScope scope;
6557 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6558 v8::Handle<v8::FunctionTemplate> method_templ =
6559 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6560 v8_str("method_data"),
6561 v8::Handle<v8::Signature>());
6562 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6563 proto_templ->Set(v8_str("method"), method_templ);
6564 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6565 LocalContext context;
6566 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6567 GenerateSomeGarbage();
6568 context->Global()->Set(v8_str("o"), fun->NewInstance());
6569 v8::Handle<Value> value = CompileRun(
6570 "var result = 0;"
6571 "for (var i = 0; i < 100; i++) {"
6572 " result = o.method(41);"
6573 "}");
6574
6575 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6576}
6577
6578THREADED_TEST(CallICFastApi_SimpleSignature) {
6579 v8::HandleScope scope;
6580 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6581 v8::Handle<v8::FunctionTemplate> method_templ =
6582 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6583 v8_str("method_data"),
6584 v8::Signature::New(fun_templ));
6585 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6586 proto_templ->Set(v8_str("method"), method_templ);
6587 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6588 LocalContext context;
6589 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6590 GenerateSomeGarbage();
6591 context->Global()->Set(v8_str("o"), fun->NewInstance());
6592 v8::Handle<Value> value = CompileRun(
6593 "o.foo = 17;"
6594 "var receiver = {};"
6595 "receiver.__proto__ = o;"
6596 "var result = 0;"
6597 "for (var i = 0; i < 100; i++) {"
6598 " result = receiver.method(41);"
6599 "}");
6600
6601 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6602}
6603
6604THREADED_TEST(CallICFastApi_SimpleSignature_Miss) {
6605 v8::HandleScope scope;
6606 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6607 v8::Handle<v8::FunctionTemplate> method_templ =
6608 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6609 v8_str("method_data"),
6610 v8::Signature::New(fun_templ));
6611 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6612 proto_templ->Set(v8_str("method"), method_templ);
6613 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6614 LocalContext context;
6615 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6616 GenerateSomeGarbage();
6617 context->Global()->Set(v8_str("o"), fun->NewInstance());
6618 v8::Handle<Value> value = CompileRun(
6619 "o.foo = 17;"
6620 "var receiver = {};"
6621 "receiver.__proto__ = o;"
6622 "var result = 0;"
6623 "var saved_result = 0;"
6624 "for (var i = 0; i < 100; i++) {"
6625 " result = receiver.method(41);"
6626 " if (i == 50) {"
6627 " saved_result = result;"
6628 " receiver = {method: function(x) { return x - 1 }};"
6629 " }"
6630 "}");
6631 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6632 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6633}
6634
Leon Clarke4515c472010-02-03 11:58:03 +00006635
Steve Blocka7e24c12009-10-30 11:49:00 +00006636static int interceptor_call_count = 0;
6637
6638static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
6639 const AccessorInfo& info) {
6640 ApiTestFuzzer::Fuzz();
6641 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
6642 return call_ic_function2;
6643 }
6644 return v8::Handle<Value>();
6645}
6646
6647
6648// This test should hit load and call ICs for the interceptor case.
6649// Once in a while, the interceptor will reply that a property was not
6650// found in which case we should get a reference error.
6651THREADED_TEST(InterceptorICReferenceErrors) {
6652 v8::HandleScope scope;
6653 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6654 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
6655 LocalContext context(0, templ, v8::Handle<Value>());
6656 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
6657 v8::Handle<Value> value = CompileRun(
6658 "function f() {"
6659 " for (var i = 0; i < 1000; i++) {"
6660 " try { x; } catch(e) { return true; }"
6661 " }"
6662 " return false;"
6663 "};"
6664 "f();");
6665 CHECK_EQ(true, value->BooleanValue());
6666 interceptor_call_count = 0;
6667 value = CompileRun(
6668 "function g() {"
6669 " for (var i = 0; i < 1000; i++) {"
6670 " try { x(42); } catch(e) { return true; }"
6671 " }"
6672 " return false;"
6673 "};"
6674 "g();");
6675 CHECK_EQ(true, value->BooleanValue());
6676}
6677
6678
6679static int interceptor_ic_exception_get_count = 0;
6680
6681static v8::Handle<Value> InterceptorICExceptionGetter(
6682 Local<String> name,
6683 const AccessorInfo& info) {
6684 ApiTestFuzzer::Fuzz();
6685 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
6686 return call_ic_function3;
6687 }
6688 if (interceptor_ic_exception_get_count == 20) {
6689 return v8::ThrowException(v8_num(42));
6690 }
6691 // Do not handle get for properties other than x.
6692 return v8::Handle<Value>();
6693}
6694
6695// Test interceptor load/call IC where the interceptor throws an
6696// exception once in a while.
6697THREADED_TEST(InterceptorICGetterExceptions) {
6698 interceptor_ic_exception_get_count = 0;
6699 v8::HandleScope scope;
6700 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6701 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
6702 LocalContext context(0, templ, v8::Handle<Value>());
6703 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
6704 v8::Handle<Value> value = CompileRun(
6705 "function f() {"
6706 " for (var i = 0; i < 100; i++) {"
6707 " try { x; } catch(e) { return true; }"
6708 " }"
6709 " return false;"
6710 "};"
6711 "f();");
6712 CHECK_EQ(true, value->BooleanValue());
6713 interceptor_ic_exception_get_count = 0;
6714 value = CompileRun(
6715 "function f() {"
6716 " for (var i = 0; i < 100; i++) {"
6717 " try { x(42); } catch(e) { return true; }"
6718 " }"
6719 " return false;"
6720 "};"
6721 "f();");
6722 CHECK_EQ(true, value->BooleanValue());
6723}
6724
6725
6726static int interceptor_ic_exception_set_count = 0;
6727
6728static v8::Handle<Value> InterceptorICExceptionSetter(
6729 Local<String> key, Local<Value> value, const AccessorInfo&) {
6730 ApiTestFuzzer::Fuzz();
6731 if (++interceptor_ic_exception_set_count > 20) {
6732 return v8::ThrowException(v8_num(42));
6733 }
6734 // Do not actually handle setting.
6735 return v8::Handle<Value>();
6736}
6737
6738// Test interceptor store IC where the interceptor throws an exception
6739// once in a while.
6740THREADED_TEST(InterceptorICSetterExceptions) {
6741 interceptor_ic_exception_set_count = 0;
6742 v8::HandleScope scope;
6743 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6744 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
6745 LocalContext context(0, templ, v8::Handle<Value>());
6746 v8::Handle<Value> value = CompileRun(
6747 "function f() {"
6748 " for (var i = 0; i < 100; i++) {"
6749 " try { x = 42; } catch(e) { return true; }"
6750 " }"
6751 " return false;"
6752 "};"
6753 "f();");
6754 CHECK_EQ(true, value->BooleanValue());
6755}
6756
6757
6758// Test that we ignore null interceptors.
6759THREADED_TEST(NullNamedInterceptor) {
6760 v8::HandleScope scope;
6761 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6762 templ->SetNamedPropertyHandler(0);
6763 LocalContext context;
6764 templ->Set("x", v8_num(42));
6765 v8::Handle<v8::Object> obj = templ->NewInstance();
6766 context->Global()->Set(v8_str("obj"), obj);
6767 v8::Handle<Value> value = CompileRun("obj.x");
6768 CHECK(value->IsInt32());
6769 CHECK_EQ(42, value->Int32Value());
6770}
6771
6772
6773// Test that we ignore null interceptors.
6774THREADED_TEST(NullIndexedInterceptor) {
6775 v8::HandleScope scope;
6776 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6777 templ->SetIndexedPropertyHandler(0);
6778 LocalContext context;
6779 templ->Set("42", v8_num(42));
6780 v8::Handle<v8::Object> obj = templ->NewInstance();
6781 context->Global()->Set(v8_str("obj"), obj);
6782 v8::Handle<Value> value = CompileRun("obj[42]");
6783 CHECK(value->IsInt32());
6784 CHECK_EQ(42, value->Int32Value());
6785}
6786
6787
6788static v8::Handle<Value> ParentGetter(Local<String> name,
6789 const AccessorInfo& info) {
6790 ApiTestFuzzer::Fuzz();
6791 return v8_num(1);
6792}
6793
6794
6795static v8::Handle<Value> ChildGetter(Local<String> name,
6796 const AccessorInfo& info) {
6797 ApiTestFuzzer::Fuzz();
6798 return v8_num(42);
6799}
6800
6801
6802THREADED_TEST(Overriding) {
6803 v8::HandleScope scope;
6804 LocalContext context;
6805
6806 // Parent template.
6807 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
6808 Local<ObjectTemplate> parent_instance_templ =
6809 parent_templ->InstanceTemplate();
6810 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
6811
6812 // Template that inherits from the parent template.
6813 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
6814 Local<ObjectTemplate> child_instance_templ =
6815 child_templ->InstanceTemplate();
6816 child_templ->Inherit(parent_templ);
6817 // Override 'f'. The child version of 'f' should get called for child
6818 // instances.
6819 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
6820 // Add 'g' twice. The 'g' added last should get called for instances.
6821 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
6822 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
6823
6824 // Add 'h' as an accessor to the proto template with ReadOnly attributes
6825 // so 'h' can be shadowed on the instance object.
6826 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
6827 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
6828 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
6829
6830 // Add 'i' as an accessor to the instance template with ReadOnly attributes
6831 // but the attribute does not have effect because it is duplicated with
6832 // NULL setter.
6833 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
6834 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
6835
6836
6837
6838 // Instantiate the child template.
6839 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
6840
6841 // Check that the child function overrides the parent one.
6842 context->Global()->Set(v8_str("o"), instance);
6843 Local<Value> value = v8_compile("o.f")->Run();
6844 // Check that the 'g' that was added last is hit.
6845 CHECK_EQ(42, value->Int32Value());
6846 value = v8_compile("o.g")->Run();
6847 CHECK_EQ(42, value->Int32Value());
6848
6849 // Check 'h' can be shadowed.
6850 value = v8_compile("o.h = 3; o.h")->Run();
6851 CHECK_EQ(3, value->Int32Value());
6852
6853 // Check 'i' is cannot be shadowed or changed.
6854 value = v8_compile("o.i = 3; o.i")->Run();
6855 CHECK_EQ(42, value->Int32Value());
6856}
6857
6858
6859static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
6860 ApiTestFuzzer::Fuzz();
6861 if (args.IsConstructCall()) {
6862 return v8::Boolean::New(true);
6863 }
6864 return v8::Boolean::New(false);
6865}
6866
6867
6868THREADED_TEST(IsConstructCall) {
6869 v8::HandleScope scope;
6870
6871 // Function template with call handler.
6872 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6873 templ->SetCallHandler(IsConstructHandler);
6874
6875 LocalContext context;
6876
6877 context->Global()->Set(v8_str("f"), templ->GetFunction());
6878 Local<Value> value = v8_compile("f()")->Run();
6879 CHECK(!value->BooleanValue());
6880 value = v8_compile("new f()")->Run();
6881 CHECK(value->BooleanValue());
6882}
6883
6884
6885THREADED_TEST(ObjectProtoToString) {
6886 v8::HandleScope scope;
6887 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6888 templ->SetClassName(v8_str("MyClass"));
6889
6890 LocalContext context;
6891
6892 Local<String> customized_tostring = v8_str("customized toString");
6893
6894 // Replace Object.prototype.toString
6895 v8_compile("Object.prototype.toString = function() {"
6896 " return 'customized toString';"
6897 "}")->Run();
6898
6899 // Normal ToString call should call replaced Object.prototype.toString
6900 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
6901 Local<String> value = instance->ToString();
6902 CHECK(value->IsString() && value->Equals(customized_tostring));
6903
6904 // ObjectProtoToString should not call replace toString function.
6905 value = instance->ObjectProtoToString();
6906 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
6907
6908 // Check global
6909 value = context->Global()->ObjectProtoToString();
6910 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
6911
6912 // Check ordinary object
6913 Local<Value> object = v8_compile("new Object()")->Run();
6914 value = Local<v8::Object>::Cast(object)->ObjectProtoToString();
6915 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
6916}
6917
6918
6919bool ApiTestFuzzer::fuzzing_ = false;
6920v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_=
6921 v8::internal::OS::CreateSemaphore(0);
6922int ApiTestFuzzer::active_tests_;
6923int ApiTestFuzzer::tests_being_run_;
6924int ApiTestFuzzer::current_;
6925
6926
6927// We are in a callback and want to switch to another thread (if we
6928// are currently running the thread fuzzing test).
6929void ApiTestFuzzer::Fuzz() {
6930 if (!fuzzing_) return;
6931 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
6932 test->ContextSwitch();
6933}
6934
6935
6936// Let the next thread go. Since it is also waiting on the V8 lock it may
6937// not start immediately.
6938bool ApiTestFuzzer::NextThread() {
6939 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00006940 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00006941 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00006942 if (kLogThreading)
6943 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00006944 return false;
6945 }
Steve Blockd0582a62009-12-15 09:54:21 +00006946 if (kLogThreading) {
6947 printf("Switch from %s to %s\n",
6948 test_name,
6949 RegisterThreadedTest::nth(test_position)->name());
6950 }
Steve Blocka7e24c12009-10-30 11:49:00 +00006951 current_ = test_position;
6952 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
6953 return true;
6954}
6955
6956
6957void ApiTestFuzzer::Run() {
6958 // When it is our turn...
6959 gate_->Wait();
6960 {
6961 // ... get the V8 lock and start running the test.
6962 v8::Locker locker;
6963 CallTest();
6964 }
6965 // This test finished.
6966 active_ = false;
6967 active_tests_--;
6968 // If it was the last then signal that fact.
6969 if (active_tests_ == 0) {
6970 all_tests_done_->Signal();
6971 } else {
6972 // Otherwise select a new test and start that.
6973 NextThread();
6974 }
6975}
6976
6977
6978static unsigned linear_congruential_generator;
6979
6980
6981void ApiTestFuzzer::Setup(PartOfTest part) {
6982 linear_congruential_generator = i::FLAG_testing_prng_seed;
6983 fuzzing_ = true;
6984 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
6985 int end = (part == FIRST_PART)
6986 ? (RegisterThreadedTest::count() >> 1)
6987 : RegisterThreadedTest::count();
6988 active_tests_ = tests_being_run_ = end - start;
6989 for (int i = 0; i < tests_being_run_; i++) {
6990 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
6991 }
6992 for (int i = 0; i < active_tests_; i++) {
6993 RegisterThreadedTest::nth(i)->fuzzer_->Start();
6994 }
6995}
6996
6997
6998static void CallTestNumber(int test_number) {
6999 (RegisterThreadedTest::nth(test_number)->callback())();
7000}
7001
7002
7003void ApiTestFuzzer::RunAllTests() {
7004 // Set off the first test.
7005 current_ = -1;
7006 NextThread();
7007 // Wait till they are all done.
7008 all_tests_done_->Wait();
7009}
7010
7011
7012int ApiTestFuzzer::GetNextTestNumber() {
7013 int next_test;
7014 do {
7015 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7016 linear_congruential_generator *= 1664525u;
7017 linear_congruential_generator += 1013904223u;
7018 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7019 return next_test;
7020}
7021
7022
7023void ApiTestFuzzer::ContextSwitch() {
7024 // If the new thread is the same as the current thread there is nothing to do.
7025 if (NextThread()) {
7026 // Now it can start.
7027 v8::Unlocker unlocker;
7028 // Wait till someone starts us again.
7029 gate_->Wait();
7030 // And we're off.
7031 }
7032}
7033
7034
7035void ApiTestFuzzer::TearDown() {
7036 fuzzing_ = false;
7037 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7038 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7039 if (fuzzer != NULL) fuzzer->Join();
7040 }
7041}
7042
7043
7044// Lets not be needlessly self-referential.
7045TEST(Threading) {
7046 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7047 ApiTestFuzzer::RunAllTests();
7048 ApiTestFuzzer::TearDown();
7049}
7050
7051TEST(Threading2) {
7052 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7053 ApiTestFuzzer::RunAllTests();
7054 ApiTestFuzzer::TearDown();
7055}
7056
7057
7058void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007059 if (kLogThreading)
7060 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007061 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007062 if (kLogThreading)
7063 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007064}
7065
7066
7067static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7068 CHECK(v8::Locker::IsLocked());
7069 ApiTestFuzzer::Fuzz();
7070 v8::Unlocker unlocker;
7071 const char* code = "throw 7;";
7072 {
7073 v8::Locker nested_locker;
7074 v8::HandleScope scope;
7075 v8::Handle<Value> exception;
7076 { v8::TryCatch try_catch;
7077 v8::Handle<Value> value = CompileRun(code);
7078 CHECK(value.IsEmpty());
7079 CHECK(try_catch.HasCaught());
7080 // Make sure to wrap the exception in a new handle because
7081 // the handle returned from the TryCatch is destroyed
7082 // when the TryCatch is destroyed.
7083 exception = Local<Value>::New(try_catch.Exception());
7084 }
7085 return v8::ThrowException(exception);
7086 }
7087}
7088
7089
7090static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7091 CHECK(v8::Locker::IsLocked());
7092 ApiTestFuzzer::Fuzz();
7093 v8::Unlocker unlocker;
7094 const char* code = "throw 7;";
7095 {
7096 v8::Locker nested_locker;
7097 v8::HandleScope scope;
7098 v8::Handle<Value> value = CompileRun(code);
7099 CHECK(value.IsEmpty());
7100 return v8_str("foo");
7101 }
7102}
7103
7104
7105// These are locking tests that don't need to be run again
7106// as part of the locking aggregation tests.
7107TEST(NestedLockers) {
7108 v8::Locker locker;
7109 CHECK(v8::Locker::IsLocked());
7110 v8::HandleScope scope;
7111 LocalContext env;
7112 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7113 Local<Function> fun = fun_templ->GetFunction();
7114 env->Global()->Set(v8_str("throw_in_js"), fun);
7115 Local<Script> script = v8_compile("(function () {"
7116 " try {"
7117 " throw_in_js();"
7118 " return 42;"
7119 " } catch (e) {"
7120 " return e * 13;"
7121 " }"
7122 "})();");
7123 CHECK_EQ(91, script->Run()->Int32Value());
7124}
7125
7126
7127// These are locking tests that don't need to be run again
7128// as part of the locking aggregation tests.
7129TEST(NestedLockersNoTryCatch) {
7130 v8::Locker locker;
7131 v8::HandleScope scope;
7132 LocalContext env;
7133 Local<v8::FunctionTemplate> fun_templ =
7134 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7135 Local<Function> fun = fun_templ->GetFunction();
7136 env->Global()->Set(v8_str("throw_in_js"), fun);
7137 Local<Script> script = v8_compile("(function () {"
7138 " try {"
7139 " throw_in_js();"
7140 " return 42;"
7141 " } catch (e) {"
7142 " return e * 13;"
7143 " }"
7144 "})();");
7145 CHECK_EQ(91, script->Run()->Int32Value());
7146}
7147
7148
7149THREADED_TEST(RecursiveLocking) {
7150 v8::Locker locker;
7151 {
7152 v8::Locker locker2;
7153 CHECK(v8::Locker::IsLocked());
7154 }
7155}
7156
7157
7158static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
7159 ApiTestFuzzer::Fuzz();
7160 v8::Unlocker unlocker;
7161 return v8::Undefined();
7162}
7163
7164
7165THREADED_TEST(LockUnlockLock) {
7166 {
7167 v8::Locker locker;
7168 v8::HandleScope scope;
7169 LocalContext env;
7170 Local<v8::FunctionTemplate> fun_templ =
7171 v8::FunctionTemplate::New(UnlockForAMoment);
7172 Local<Function> fun = fun_templ->GetFunction();
7173 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7174 Local<Script> script = v8_compile("(function () {"
7175 " unlock_for_a_moment();"
7176 " return 42;"
7177 "})();");
7178 CHECK_EQ(42, script->Run()->Int32Value());
7179 }
7180 {
7181 v8::Locker locker;
7182 v8::HandleScope scope;
7183 LocalContext env;
7184 Local<v8::FunctionTemplate> fun_templ =
7185 v8::FunctionTemplate::New(UnlockForAMoment);
7186 Local<Function> fun = fun_templ->GetFunction();
7187 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7188 Local<Script> script = v8_compile("(function () {"
7189 " unlock_for_a_moment();"
7190 " return 42;"
7191 "})();");
7192 CHECK_EQ(42, script->Run()->Int32Value());
7193 }
7194}
7195
7196
Leon Clarked91b9f72010-01-27 17:25:45 +00007197static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00007198 int count = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +00007199 v8::internal::HeapIterator it;
7200 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
7201 if (object->IsJSGlobalObject()) count++;
7202 return count;
7203}
7204
7205
7206static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00007207 // We need to collect all garbage twice to be sure that everything
7208 // has been collected. This is because inline caches are cleared in
7209 // the first garbage collection but some of the maps have already
7210 // been marked at that point. Therefore some of the maps are not
7211 // collected until the second garbage collection.
7212 v8::internal::Heap::CollectAllGarbage(false);
7213 v8::internal::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00007214 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00007215#ifdef DEBUG
7216 if (count > 0) v8::internal::Heap::TracePathToGlobal();
7217#endif
7218 return count;
7219}
7220
7221
7222TEST(DontLeakGlobalObjects) {
7223 // Regression test for issues 1139850 and 1174891.
7224
7225 v8::V8::Initialize();
7226
7227 int count = GetSurvivingGlobalObjectsCount();
7228
7229 for (int i = 0; i < 5; i++) {
7230 { v8::HandleScope scope;
7231 LocalContext context;
7232 }
7233 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7234
7235 { v8::HandleScope scope;
7236 LocalContext context;
7237 v8_compile("Date")->Run();
7238 }
7239 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7240
7241 { v8::HandleScope scope;
7242 LocalContext context;
7243 v8_compile("/aaa/")->Run();
7244 }
7245 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7246
7247 { v8::HandleScope scope;
7248 const char* extension_list[] = { "v8/gc" };
7249 v8::ExtensionConfiguration extensions(1, extension_list);
7250 LocalContext context(&extensions);
7251 v8_compile("gc();")->Run();
7252 }
7253 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7254 }
7255}
7256
7257
7258v8::Persistent<v8::Object> some_object;
7259v8::Persistent<v8::Object> bad_handle;
7260
7261void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
7262 v8::HandleScope scope;
7263 bad_handle = v8::Persistent<v8::Object>::New(some_object);
7264}
7265
7266
7267THREADED_TEST(NewPersistentHandleFromWeakCallback) {
7268 LocalContext context;
7269
7270 v8::Persistent<v8::Object> handle1, handle2;
7271 {
7272 v8::HandleScope scope;
7273 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
7274 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7275 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7276 }
7277 // Note: order is implementation dependent alas: currently
7278 // global handle nodes are processed by PostGarbageCollectionProcessing
7279 // in reverse allocation order, so if second allocated handle is deleted,
7280 // weak callback of the first handle would be able to 'reallocate' it.
7281 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
7282 handle2.Dispose();
7283 i::Heap::CollectAllGarbage(false);
7284}
7285
7286
7287v8::Persistent<v8::Object> to_be_disposed;
7288
7289void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
7290 to_be_disposed.Dispose();
7291 i::Heap::CollectAllGarbage(false);
7292}
7293
7294
7295THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
7296 LocalContext context;
7297
7298 v8::Persistent<v8::Object> handle1, handle2;
7299 {
7300 v8::HandleScope scope;
7301 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7302 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7303 }
7304 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
7305 to_be_disposed = handle2;
7306 i::Heap::CollectAllGarbage(false);
7307}
7308
Steve Blockd0582a62009-12-15 09:54:21 +00007309void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
7310 handle.Dispose();
7311}
7312
7313void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
7314 v8::HandleScope scope;
7315 v8::Persistent<v8::Object>::New(v8::Object::New());
7316}
7317
7318
7319THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
7320 LocalContext context;
7321
7322 v8::Persistent<v8::Object> handle1, handle2, handle3;
7323 {
7324 v8::HandleScope scope;
7325 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
7326 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7327 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7328 }
7329 handle2.MakeWeak(NULL, DisposingCallback);
7330 handle3.MakeWeak(NULL, HandleCreatingCallback);
7331 i::Heap::CollectAllGarbage(false);
7332}
7333
Steve Blocka7e24c12009-10-30 11:49:00 +00007334
7335THREADED_TEST(CheckForCrossContextObjectLiterals) {
7336 v8::V8::Initialize();
7337
7338 const int nof = 2;
7339 const char* sources[nof] = {
7340 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
7341 "Object()"
7342 };
7343
7344 for (int i = 0; i < nof; i++) {
7345 const char* source = sources[i];
7346 { v8::HandleScope scope;
7347 LocalContext context;
7348 CompileRun(source);
7349 }
7350 { v8::HandleScope scope;
7351 LocalContext context;
7352 CompileRun(source);
7353 }
7354 }
7355}
7356
7357
7358static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
7359 v8::HandleScope inner;
7360 env->Enter();
7361 v8::Handle<Value> three = v8_num(3);
7362 v8::Handle<Value> value = inner.Close(three);
7363 env->Exit();
7364 return value;
7365}
7366
7367
7368THREADED_TEST(NestedHandleScopeAndContexts) {
7369 v8::HandleScope outer;
7370 v8::Persistent<Context> env = Context::New();
7371 env->Enter();
7372 v8::Handle<Value> value = NestedScope(env);
7373 v8::Handle<String> str = value->ToString();
7374 env->Exit();
7375 env.Dispose();
7376}
7377
7378
7379THREADED_TEST(ExternalAllocatedMemory) {
7380 v8::HandleScope outer;
7381 v8::Persistent<Context> env = Context::New();
7382 const int kSize = 1024*1024;
7383 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
7384 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
7385}
7386
7387
7388THREADED_TEST(DisposeEnteredContext) {
7389 v8::HandleScope scope;
7390 LocalContext outer;
7391 { v8::Persistent<v8::Context> inner = v8::Context::New();
7392 inner->Enter();
7393 inner.Dispose();
7394 inner.Clear();
7395 inner->Exit();
7396 }
7397}
7398
7399
7400// Regression test for issue 54, object templates with internal fields
7401// but no accessors or interceptors did not get their internal field
7402// count set on instances.
7403THREADED_TEST(Regress54) {
7404 v8::HandleScope outer;
7405 LocalContext context;
7406 static v8::Persistent<v8::ObjectTemplate> templ;
7407 if (templ.IsEmpty()) {
7408 v8::HandleScope inner;
7409 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
7410 local->SetInternalFieldCount(1);
7411 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
7412 }
7413 v8::Handle<v8::Object> result = templ->NewInstance();
7414 CHECK_EQ(1, result->InternalFieldCount());
7415}
7416
7417
7418// If part of the threaded tests, this test makes ThreadingTest fail
7419// on mac.
7420TEST(CatchStackOverflow) {
7421 v8::HandleScope scope;
7422 LocalContext context;
7423 v8::TryCatch try_catch;
7424 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
7425 "function f() {"
7426 " return f();"
7427 "}"
7428 ""
7429 "f();"));
7430 v8::Handle<v8::Value> result = script->Run();
7431 CHECK(result.IsEmpty());
7432}
7433
7434
7435static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
7436 const char* resource_name,
7437 int line_offset) {
7438 v8::HandleScope scope;
7439 v8::TryCatch try_catch;
7440 v8::Handle<v8::Value> result = script->Run();
7441 CHECK(result.IsEmpty());
7442 CHECK(try_catch.HasCaught());
7443 v8::Handle<v8::Message> message = try_catch.Message();
7444 CHECK(!message.IsEmpty());
7445 CHECK_EQ(10 + line_offset, message->GetLineNumber());
7446 CHECK_EQ(91, message->GetStartPosition());
7447 CHECK_EQ(92, message->GetEndPosition());
7448 CHECK_EQ(2, message->GetStartColumn());
7449 CHECK_EQ(3, message->GetEndColumn());
7450 v8::String::AsciiValue line(message->GetSourceLine());
7451 CHECK_EQ(" throw 'nirk';", *line);
7452 v8::String::AsciiValue name(message->GetScriptResourceName());
7453 CHECK_EQ(resource_name, *name);
7454}
7455
7456
7457THREADED_TEST(TryCatchSourceInfo) {
7458 v8::HandleScope scope;
7459 LocalContext context;
7460 v8::Handle<v8::String> source = v8::String::New(
7461 "function Foo() {\n"
7462 " return Bar();\n"
7463 "}\n"
7464 "\n"
7465 "function Bar() {\n"
7466 " return Baz();\n"
7467 "}\n"
7468 "\n"
7469 "function Baz() {\n"
7470 " throw 'nirk';\n"
7471 "}\n"
7472 "\n"
7473 "Foo();\n");
7474
7475 const char* resource_name;
7476 v8::Handle<v8::Script> script;
7477 resource_name = "test.js";
7478 script = v8::Script::Compile(source, v8::String::New(resource_name));
7479 CheckTryCatchSourceInfo(script, resource_name, 0);
7480
7481 resource_name = "test1.js";
7482 v8::ScriptOrigin origin1(v8::String::New(resource_name));
7483 script = v8::Script::Compile(source, &origin1);
7484 CheckTryCatchSourceInfo(script, resource_name, 0);
7485
7486 resource_name = "test2.js";
7487 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
7488 script = v8::Script::Compile(source, &origin2);
7489 CheckTryCatchSourceInfo(script, resource_name, 7);
7490}
7491
7492
7493THREADED_TEST(CompilationCache) {
7494 v8::HandleScope scope;
7495 LocalContext context;
7496 v8::Handle<v8::String> source0 = v8::String::New("1234");
7497 v8::Handle<v8::String> source1 = v8::String::New("1234");
7498 v8::Handle<v8::Script> script0 =
7499 v8::Script::Compile(source0, v8::String::New("test.js"));
7500 v8::Handle<v8::Script> script1 =
7501 v8::Script::Compile(source1, v8::String::New("test.js"));
7502 v8::Handle<v8::Script> script2 =
7503 v8::Script::Compile(source0); // different origin
7504 CHECK_EQ(1234, script0->Run()->Int32Value());
7505 CHECK_EQ(1234, script1->Run()->Int32Value());
7506 CHECK_EQ(1234, script2->Run()->Int32Value());
7507}
7508
7509
7510static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
7511 ApiTestFuzzer::Fuzz();
7512 return v8_num(42);
7513}
7514
7515
7516THREADED_TEST(CallbackFunctionName) {
7517 v8::HandleScope scope;
7518 LocalContext context;
7519 Local<ObjectTemplate> t = ObjectTemplate::New();
7520 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
7521 context->Global()->Set(v8_str("obj"), t->NewInstance());
7522 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
7523 CHECK(value->IsString());
7524 v8::String::AsciiValue name(value);
7525 CHECK_EQ("asdf", *name);
7526}
7527
7528
7529THREADED_TEST(DateAccess) {
7530 v8::HandleScope scope;
7531 LocalContext context;
7532 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
7533 CHECK(date->IsDate());
7534 CHECK_EQ(1224744689038.0, v8::Handle<v8::Date>::Cast(date)->NumberValue());
7535}
7536
7537
7538void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
7539 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val);
7540 v8::Handle<v8::Array> props = obj->GetPropertyNames();
7541 CHECK_EQ(elmc, props->Length());
7542 for (int i = 0; i < elmc; i++) {
7543 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
7544 CHECK_EQ(elmv[i], *elm);
7545 }
7546}
7547
7548
7549THREADED_TEST(PropertyEnumeration) {
7550 v8::HandleScope scope;
7551 LocalContext context;
7552 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
7553 "var result = [];"
7554 "result[0] = {};"
7555 "result[1] = {a: 1, b: 2};"
7556 "result[2] = [1, 2, 3];"
7557 "var proto = {x: 1, y: 2, z: 3};"
7558 "var x = { __proto__: proto, w: 0, z: 1 };"
7559 "result[3] = x;"
7560 "result;"))->Run();
7561 v8::Handle<v8::Array> elms = v8::Handle<v8::Array>::Cast(obj);
7562 CHECK_EQ(4, elms->Length());
7563 int elmc0 = 0;
7564 const char** elmv0 = NULL;
7565 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
7566 int elmc1 = 2;
7567 const char* elmv1[] = {"a", "b"};
7568 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
7569 int elmc2 = 3;
7570 const char* elmv2[] = {"0", "1", "2"};
7571 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
7572 int elmc3 = 4;
7573 const char* elmv3[] = {"w", "z", "x", "y"};
7574 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
7575}
7576
7577
Steve Blocka7e24c12009-10-30 11:49:00 +00007578static bool NamedSetAccessBlocker(Local<v8::Object> obj,
7579 Local<Value> name,
7580 v8::AccessType type,
7581 Local<Value> data) {
7582 return type != v8::ACCESS_SET;
7583}
7584
7585
7586static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
7587 uint32_t key,
7588 v8::AccessType type,
7589 Local<Value> data) {
7590 return type != v8::ACCESS_SET;
7591}
7592
7593
7594THREADED_TEST(DisableAccessChecksWhileConfiguring) {
7595 v8::HandleScope scope;
7596 LocalContext context;
7597 Local<ObjectTemplate> templ = ObjectTemplate::New();
7598 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
7599 IndexedSetAccessBlocker);
7600 templ->Set(v8_str("x"), v8::True());
7601 Local<v8::Object> instance = templ->NewInstance();
7602 context->Global()->Set(v8_str("obj"), instance);
7603 Local<Value> value = CompileRun("obj.x");
7604 CHECK(value->BooleanValue());
7605}
7606
7607
7608static bool NamedGetAccessBlocker(Local<v8::Object> obj,
7609 Local<Value> name,
7610 v8::AccessType type,
7611 Local<Value> data) {
7612 return false;
7613}
7614
7615
7616static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
7617 uint32_t key,
7618 v8::AccessType type,
7619 Local<Value> data) {
7620 return false;
7621}
7622
7623
7624
7625THREADED_TEST(AccessChecksReenabledCorrectly) {
7626 v8::HandleScope scope;
7627 LocalContext context;
7628 Local<ObjectTemplate> templ = ObjectTemplate::New();
7629 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
7630 IndexedGetAccessBlocker);
7631 templ->Set(v8_str("a"), v8_str("a"));
7632 // Add more than 8 (see kMaxFastProperties) properties
7633 // so that the constructor will force copying map.
7634 // Cannot sprintf, gcc complains unsafety.
7635 char buf[4];
7636 for (char i = '0'; i <= '9' ; i++) {
7637 buf[0] = i;
7638 for (char j = '0'; j <= '9'; j++) {
7639 buf[1] = j;
7640 for (char k = '0'; k <= '9'; k++) {
7641 buf[2] = k;
7642 buf[3] = 0;
7643 templ->Set(v8_str(buf), v8::Number::New(k));
7644 }
7645 }
7646 }
7647
7648 Local<v8::Object> instance_1 = templ->NewInstance();
7649 context->Global()->Set(v8_str("obj_1"), instance_1);
7650
7651 Local<Value> value_1 = CompileRun("obj_1.a");
7652 CHECK(value_1->IsUndefined());
7653
7654 Local<v8::Object> instance_2 = templ->NewInstance();
7655 context->Global()->Set(v8_str("obj_2"), instance_2);
7656
7657 Local<Value> value_2 = CompileRun("obj_2.a");
7658 CHECK(value_2->IsUndefined());
7659}
7660
7661
7662// This tests that access check information remains on the global
7663// object template when creating contexts.
7664THREADED_TEST(AccessControlRepeatedContextCreation) {
7665 v8::HandleScope handle_scope;
7666 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
7667 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
7668 IndexedSetAccessBlocker);
7669 i::Handle<i::ObjectTemplateInfo> internal_template =
7670 v8::Utils::OpenHandle(*global_template);
7671 CHECK(!internal_template->constructor()->IsUndefined());
7672 i::Handle<i::FunctionTemplateInfo> constructor(
7673 i::FunctionTemplateInfo::cast(internal_template->constructor()));
7674 CHECK(!constructor->access_check_info()->IsUndefined());
7675 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
7676 CHECK(!constructor->access_check_info()->IsUndefined());
7677}
7678
7679
7680THREADED_TEST(TurnOnAccessCheck) {
7681 v8::HandleScope handle_scope;
7682
7683 // Create an environment with access check to the global object disabled by
7684 // default.
7685 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
7686 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
7687 IndexedGetAccessBlocker,
7688 v8::Handle<v8::Value>(),
7689 false);
7690 v8::Persistent<Context> context = Context::New(NULL, global_template);
7691 Context::Scope context_scope(context);
7692
7693 // Set up a property and a number of functions.
7694 context->Global()->Set(v8_str("a"), v8_num(1));
7695 CompileRun("function f1() {return a;}"
7696 "function f2() {return a;}"
7697 "function g1() {return h();}"
7698 "function g2() {return h();}"
7699 "function h() {return 1;}");
7700 Local<Function> f1 =
7701 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
7702 Local<Function> f2 =
7703 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
7704 Local<Function> g1 =
7705 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
7706 Local<Function> g2 =
7707 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
7708 Local<Function> h =
7709 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
7710
7711 // Get the global object.
7712 v8::Handle<v8::Object> global = context->Global();
7713
7714 // Call f1 one time and f2 a number of times. This will ensure that f1 still
7715 // uses the runtime system to retreive property a whereas f2 uses global load
7716 // inline cache.
7717 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
7718 for (int i = 0; i < 4; i++) {
7719 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
7720 }
7721
7722 // Same for g1 and g2.
7723 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
7724 for (int i = 0; i < 4; i++) {
7725 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
7726 }
7727
7728 // Detach the global and turn on access check.
7729 context->DetachGlobal();
7730 context->Global()->TurnOnAccessCheck();
7731
7732 // Failing access check to property get results in undefined.
7733 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
7734 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
7735
7736 // Failing access check to function call results in exception.
7737 CHECK(g1->Call(global, 0, NULL).IsEmpty());
7738 CHECK(g2->Call(global, 0, NULL).IsEmpty());
7739
7740 // No failing access check when just returning a constant.
7741 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
7742}
7743
7744
7745// This test verifies that pre-compilation (aka preparsing) can be called
7746// without initializing the whole VM. Thus we cannot run this test in a
7747// multi-threaded setup.
7748TEST(PreCompile) {
7749 // TODO(155): This test would break without the initialization of V8. This is
7750 // a workaround for now to make this test not fail.
7751 v8::V8::Initialize();
7752 const char *script = "function foo(a) { return a+1; }";
Steve Blockd0582a62009-12-15 09:54:21 +00007753 v8::ScriptData *sd =
7754 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00007755 CHECK_NE(sd->Length(), 0);
7756 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00007757 CHECK(!sd->HasError());
7758 delete sd;
7759}
7760
7761
7762TEST(PreCompileWithError) {
7763 v8::V8::Initialize();
7764 const char *script = "function foo(a) { return 1 * * 2; }";
7765 v8::ScriptData *sd =
7766 v8::ScriptData::PreCompile(script, i::StrLength(script));
7767 CHECK(sd->HasError());
7768 delete sd;
7769}
7770
7771
7772TEST(Regress31661) {
7773 v8::V8::Initialize();
7774 const char *script = " The Definintive Guide";
7775 v8::ScriptData *sd =
7776 v8::ScriptData::PreCompile(script, i::StrLength(script));
7777 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00007778 delete sd;
7779}
7780
7781
7782// This tests that we do not allow dictionary load/call inline caches
7783// to use functions that have not yet been compiled. The potential
7784// problem of loading a function that has not yet been compiled can
7785// arise because we share code between contexts via the compilation
7786// cache.
7787THREADED_TEST(DictionaryICLoadedFunction) {
7788 v8::HandleScope scope;
7789 // Test LoadIC.
7790 for (int i = 0; i < 2; i++) {
7791 LocalContext context;
7792 context->Global()->Set(v8_str("tmp"), v8::True());
7793 context->Global()->Delete(v8_str("tmp"));
7794 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
7795 }
7796 // Test CallIC.
7797 for (int i = 0; i < 2; i++) {
7798 LocalContext context;
7799 context->Global()->Set(v8_str("tmp"), v8::True());
7800 context->Global()->Delete(v8_str("tmp"));
7801 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
7802 }
7803}
7804
7805
7806// Test that cross-context new calls use the context of the callee to
7807// create the new JavaScript object.
7808THREADED_TEST(CrossContextNew) {
7809 v8::HandleScope scope;
7810 v8::Persistent<Context> context0 = Context::New();
7811 v8::Persistent<Context> context1 = Context::New();
7812
7813 // Allow cross-domain access.
7814 Local<String> token = v8_str("<security token>");
7815 context0->SetSecurityToken(token);
7816 context1->SetSecurityToken(token);
7817
7818 // Set an 'x' property on the Object prototype and define a
7819 // constructor function in context0.
7820 context0->Enter();
7821 CompileRun("Object.prototype.x = 42; function C() {};");
7822 context0->Exit();
7823
7824 // Call the constructor function from context0 and check that the
7825 // result has the 'x' property.
7826 context1->Enter();
7827 context1->Global()->Set(v8_str("other"), context0->Global());
7828 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
7829 CHECK(value->IsInt32());
7830 CHECK_EQ(42, value->Int32Value());
7831 context1->Exit();
7832
7833 // Dispose the contexts to allow them to be garbage collected.
7834 context0.Dispose();
7835 context1.Dispose();
7836}
7837
7838
7839class RegExpInterruptTest {
7840 public:
7841 RegExpInterruptTest() : block_(NULL) {}
7842 ~RegExpInterruptTest() { delete block_; }
7843 void RunTest() {
7844 block_ = i::OS::CreateSemaphore(0);
7845 gc_count_ = 0;
7846 gc_during_regexp_ = 0;
7847 regexp_success_ = false;
7848 gc_success_ = false;
7849 GCThread gc_thread(this);
7850 gc_thread.Start();
7851 v8::Locker::StartPreemption(1);
7852
7853 LongRunningRegExp();
7854 {
7855 v8::Unlocker unlock;
7856 gc_thread.Join();
7857 }
7858 v8::Locker::StopPreemption();
7859 CHECK(regexp_success_);
7860 CHECK(gc_success_);
7861 }
7862 private:
7863 // Number of garbage collections required.
7864 static const int kRequiredGCs = 5;
7865
7866 class GCThread : public i::Thread {
7867 public:
7868 explicit GCThread(RegExpInterruptTest* test)
7869 : test_(test) {}
7870 virtual void Run() {
7871 test_->CollectGarbage();
7872 }
7873 private:
7874 RegExpInterruptTest* test_;
7875 };
7876
7877 void CollectGarbage() {
7878 block_->Wait();
7879 while (gc_during_regexp_ < kRequiredGCs) {
7880 {
7881 v8::Locker lock;
7882 // TODO(lrn): Perhaps create some garbage before collecting.
7883 i::Heap::CollectAllGarbage(false);
7884 gc_count_++;
7885 }
7886 i::OS::Sleep(1);
7887 }
7888 gc_success_ = true;
7889 }
7890
7891 void LongRunningRegExp() {
7892 block_->Signal(); // Enable garbage collection thread on next preemption.
7893 int rounds = 0;
7894 while (gc_during_regexp_ < kRequiredGCs) {
7895 int gc_before = gc_count_;
7896 {
7897 // Match 15-30 "a"'s against 14 and a "b".
7898 const char* c_source =
7899 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
7900 ".exec('aaaaaaaaaaaaaaab') === null";
7901 Local<String> source = String::New(c_source);
7902 Local<Script> script = Script::Compile(source);
7903 Local<Value> result = script->Run();
7904 if (!result->BooleanValue()) {
7905 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
7906 return;
7907 }
7908 }
7909 {
7910 // Match 15-30 "a"'s against 15 and a "b".
7911 const char* c_source =
7912 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
7913 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
7914 Local<String> source = String::New(c_source);
7915 Local<Script> script = Script::Compile(source);
7916 Local<Value> result = script->Run();
7917 if (!result->BooleanValue()) {
7918 gc_during_regexp_ = kRequiredGCs;
7919 return;
7920 }
7921 }
7922 int gc_after = gc_count_;
7923 gc_during_regexp_ += gc_after - gc_before;
7924 rounds++;
7925 i::OS::Sleep(1);
7926 }
7927 regexp_success_ = true;
7928 }
7929
7930 i::Semaphore* block_;
7931 int gc_count_;
7932 int gc_during_regexp_;
7933 bool regexp_success_;
7934 bool gc_success_;
7935};
7936
7937
7938// Test that a regular expression execution can be interrupted and
7939// survive a garbage collection.
7940TEST(RegExpInterruption) {
7941 v8::Locker lock;
7942 v8::V8::Initialize();
7943 v8::HandleScope scope;
7944 Local<Context> local_env;
7945 {
7946 LocalContext env;
7947 local_env = env.local();
7948 }
7949
7950 // Local context should still be live.
7951 CHECK(!local_env.IsEmpty());
7952 local_env->Enter();
7953
7954 // Should complete without problems.
7955 RegExpInterruptTest().RunTest();
7956
7957 local_env->Exit();
7958}
7959
7960
7961class ApplyInterruptTest {
7962 public:
7963 ApplyInterruptTest() : block_(NULL) {}
7964 ~ApplyInterruptTest() { delete block_; }
7965 void RunTest() {
7966 block_ = i::OS::CreateSemaphore(0);
7967 gc_count_ = 0;
7968 gc_during_apply_ = 0;
7969 apply_success_ = false;
7970 gc_success_ = false;
7971 GCThread gc_thread(this);
7972 gc_thread.Start();
7973 v8::Locker::StartPreemption(1);
7974
7975 LongRunningApply();
7976 {
7977 v8::Unlocker unlock;
7978 gc_thread.Join();
7979 }
7980 v8::Locker::StopPreemption();
7981 CHECK(apply_success_);
7982 CHECK(gc_success_);
7983 }
7984 private:
7985 // Number of garbage collections required.
7986 static const int kRequiredGCs = 2;
7987
7988 class GCThread : public i::Thread {
7989 public:
7990 explicit GCThread(ApplyInterruptTest* test)
7991 : test_(test) {}
7992 virtual void Run() {
7993 test_->CollectGarbage();
7994 }
7995 private:
7996 ApplyInterruptTest* test_;
7997 };
7998
7999 void CollectGarbage() {
8000 block_->Wait();
8001 while (gc_during_apply_ < kRequiredGCs) {
8002 {
8003 v8::Locker lock;
8004 i::Heap::CollectAllGarbage(false);
8005 gc_count_++;
8006 }
8007 i::OS::Sleep(1);
8008 }
8009 gc_success_ = true;
8010 }
8011
8012 void LongRunningApply() {
8013 block_->Signal();
8014 int rounds = 0;
8015 while (gc_during_apply_ < kRequiredGCs) {
8016 int gc_before = gc_count_;
8017 {
8018 const char* c_source =
8019 "function do_very_little(bar) {"
8020 " this.foo = bar;"
8021 "}"
8022 "for (var i = 0; i < 100000; i++) {"
8023 " do_very_little.apply(this, ['bar']);"
8024 "}";
8025 Local<String> source = String::New(c_source);
8026 Local<Script> script = Script::Compile(source);
8027 Local<Value> result = script->Run();
8028 // Check that no exception was thrown.
8029 CHECK(!result.IsEmpty());
8030 }
8031 int gc_after = gc_count_;
8032 gc_during_apply_ += gc_after - gc_before;
8033 rounds++;
8034 }
8035 apply_success_ = true;
8036 }
8037
8038 i::Semaphore* block_;
8039 int gc_count_;
8040 int gc_during_apply_;
8041 bool apply_success_;
8042 bool gc_success_;
8043};
8044
8045
8046// Test that nothing bad happens if we get a preemption just when we were
8047// about to do an apply().
8048TEST(ApplyInterruption) {
8049 v8::Locker lock;
8050 v8::V8::Initialize();
8051 v8::HandleScope scope;
8052 Local<Context> local_env;
8053 {
8054 LocalContext env;
8055 local_env = env.local();
8056 }
8057
8058 // Local context should still be live.
8059 CHECK(!local_env.IsEmpty());
8060 local_env->Enter();
8061
8062 // Should complete without problems.
8063 ApplyInterruptTest().RunTest();
8064
8065 local_env->Exit();
8066}
8067
8068
8069// Verify that we can clone an object
8070TEST(ObjectClone) {
8071 v8::HandleScope scope;
8072 LocalContext env;
8073
8074 const char* sample =
8075 "var rv = {};" \
8076 "rv.alpha = 'hello';" \
8077 "rv.beta = 123;" \
8078 "rv;";
8079
8080 // Create an object, verify basics.
8081 Local<Value> val = CompileRun(sample);
8082 CHECK(val->IsObject());
8083 Local<v8::Object> obj = Local<v8::Object>::Cast(val);
8084 obj->Set(v8_str("gamma"), v8_str("cloneme"));
8085
8086 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
8087 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8088 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
8089
8090 // Clone it.
8091 Local<v8::Object> clone = obj->Clone();
8092 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
8093 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
8094 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
8095
8096 // Set a property on the clone, verify each object.
8097 clone->Set(v8_str("beta"), v8::Integer::New(456));
8098 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8099 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
8100}
8101
8102
8103class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
8104 public:
8105 explicit AsciiVectorResource(i::Vector<const char> vector)
8106 : data_(vector) {}
8107 virtual ~AsciiVectorResource() {}
8108 virtual size_t length() const { return data_.length(); }
8109 virtual const char* data() const { return data_.start(); }
8110 private:
8111 i::Vector<const char> data_;
8112};
8113
8114
8115class UC16VectorResource : public v8::String::ExternalStringResource {
8116 public:
8117 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
8118 : data_(vector) {}
8119 virtual ~UC16VectorResource() {}
8120 virtual size_t length() const { return data_.length(); }
8121 virtual const i::uc16* data() const { return data_.start(); }
8122 private:
8123 i::Vector<const i::uc16> data_;
8124};
8125
8126
8127static void MorphAString(i::String* string,
8128 AsciiVectorResource* ascii_resource,
8129 UC16VectorResource* uc16_resource) {
8130 CHECK(i::StringShape(string).IsExternal());
8131 if (string->IsAsciiRepresentation()) {
8132 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008133 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008134 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00008135 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008136 i::ExternalTwoByteString* morphed =
8137 i::ExternalTwoByteString::cast(string);
8138 morphed->set_resource(uc16_resource);
8139 } else {
8140 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008141 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008142 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00008143 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008144 i::ExternalAsciiString* morphed =
8145 i::ExternalAsciiString::cast(string);
8146 morphed->set_resource(ascii_resource);
8147 }
8148}
8149
8150
8151// Test that we can still flatten a string if the components it is built up
8152// from have been turned into 16 bit strings in the mean time.
8153THREADED_TEST(MorphCompositeStringTest) {
8154 const char* c_string = "Now is the time for all good men"
8155 " to come to the aid of the party";
8156 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
8157 {
8158 v8::HandleScope scope;
8159 LocalContext env;
8160 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008161 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008162 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008163 i::Vector<const uint16_t>(two_byte_string,
8164 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008165
8166 Local<String> lhs(v8::Utils::ToLocal(
8167 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8168 Local<String> rhs(v8::Utils::ToLocal(
8169 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8170
8171 env->Global()->Set(v8_str("lhs"), lhs);
8172 env->Global()->Set(v8_str("rhs"), rhs);
8173
8174 CompileRun(
8175 "var cons = lhs + rhs;"
8176 "var slice = lhs.substring(1, lhs.length - 1);"
8177 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
8178
8179 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
8180 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
8181
8182 // Now do some stuff to make sure the strings are flattened, etc.
8183 CompileRun(
8184 "/[^a-z]/.test(cons);"
8185 "/[^a-z]/.test(slice);"
8186 "/[^a-z]/.test(slice_on_cons);");
8187 const char* expected_cons =
8188 "Now is the time for all good men to come to the aid of the party"
8189 "Now is the time for all good men to come to the aid of the party";
8190 const char* expected_slice =
8191 "ow is the time for all good men to come to the aid of the part";
8192 const char* expected_slice_on_cons =
8193 "ow is the time for all good men to come to the aid of the party"
8194 "Now is the time for all good men to come to the aid of the part";
8195 CHECK_EQ(String::New(expected_cons),
8196 env->Global()->Get(v8_str("cons")));
8197 CHECK_EQ(String::New(expected_slice),
8198 env->Global()->Get(v8_str("slice")));
8199 CHECK_EQ(String::New(expected_slice_on_cons),
8200 env->Global()->Get(v8_str("slice_on_cons")));
8201 }
8202}
8203
8204
8205TEST(CompileExternalTwoByteSource) {
8206 v8::HandleScope scope;
8207 LocalContext context;
8208
8209 // This is a very short list of sources, which currently is to check for a
8210 // regression caused by r2703.
8211 const char* ascii_sources[] = {
8212 "0.5",
8213 "-0.5", // This mainly testes PushBack in the Scanner.
8214 "--0.5", // This mainly testes PushBack in the Scanner.
8215 NULL
8216 };
8217
8218 // Compile the sources as external two byte strings.
8219 for (int i = 0; ascii_sources[i] != NULL; i++) {
8220 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
8221 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008222 i::Vector<const uint16_t>(two_byte_string,
8223 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00008224 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
8225 v8::Script::Compile(source);
8226 }
8227}
8228
8229
8230class RegExpStringModificationTest {
8231 public:
8232 RegExpStringModificationTest()
8233 : block_(i::OS::CreateSemaphore(0)),
8234 morphs_(0),
8235 morphs_during_regexp_(0),
8236 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
8237 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
8238 ~RegExpStringModificationTest() { delete block_; }
8239 void RunTest() {
8240 regexp_success_ = false;
8241 morph_success_ = false;
8242
8243 // Initialize the contents of two_byte_content_ to be a uc16 representation
8244 // of "aaaaaaaaaaaaaab".
8245 for (int i = 0; i < 14; i++) {
8246 two_byte_content_[i] = 'a';
8247 }
8248 two_byte_content_[14] = 'b';
8249
8250 // Create the input string for the regexp - the one we are going to change
8251 // properties of.
8252 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
8253
8254 // Inject the input as a global variable.
8255 i::Handle<i::String> input_name =
8256 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
8257 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
8258
8259
8260 MorphThread morph_thread(this);
8261 morph_thread.Start();
8262 v8::Locker::StartPreemption(1);
8263 LongRunningRegExp();
8264 {
8265 v8::Unlocker unlock;
8266 morph_thread.Join();
8267 }
8268 v8::Locker::StopPreemption();
8269 CHECK(regexp_success_);
8270 CHECK(morph_success_);
8271 }
8272 private:
8273
8274 // Number of string modifications required.
8275 static const int kRequiredModifications = 5;
8276 static const int kMaxModifications = 100;
8277
8278 class MorphThread : public i::Thread {
8279 public:
8280 explicit MorphThread(RegExpStringModificationTest* test)
8281 : test_(test) {}
8282 virtual void Run() {
8283 test_->MorphString();
8284 }
8285 private:
8286 RegExpStringModificationTest* test_;
8287 };
8288
8289 void MorphString() {
8290 block_->Wait();
8291 while (morphs_during_regexp_ < kRequiredModifications &&
8292 morphs_ < kMaxModifications) {
8293 {
8294 v8::Locker lock;
8295 // Swap string between ascii and two-byte representation.
8296 i::String* string = *input_;
8297 MorphAString(string, &ascii_resource_, &uc16_resource_);
8298 morphs_++;
8299 }
8300 i::OS::Sleep(1);
8301 }
8302 morph_success_ = true;
8303 }
8304
8305 void LongRunningRegExp() {
8306 block_->Signal(); // Enable morphing thread on next preemption.
8307 while (morphs_during_regexp_ < kRequiredModifications &&
8308 morphs_ < kMaxModifications) {
8309 int morphs_before = morphs_;
8310 {
8311 // Match 15-30 "a"'s against 14 and a "b".
8312 const char* c_source =
8313 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8314 ".exec(input) === null";
8315 Local<String> source = String::New(c_source);
8316 Local<Script> script = Script::Compile(source);
8317 Local<Value> result = script->Run();
8318 CHECK(result->IsTrue());
8319 }
8320 int morphs_after = morphs_;
8321 morphs_during_regexp_ += morphs_after - morphs_before;
8322 }
8323 regexp_success_ = true;
8324 }
8325
8326 i::uc16 two_byte_content_[15];
8327 i::Semaphore* block_;
8328 int morphs_;
8329 int morphs_during_regexp_;
8330 bool regexp_success_;
8331 bool morph_success_;
8332 i::Handle<i::String> input_;
8333 AsciiVectorResource ascii_resource_;
8334 UC16VectorResource uc16_resource_;
8335};
8336
8337
8338// Test that a regular expression execution can be interrupted and
8339// the string changed without failing.
8340TEST(RegExpStringModification) {
8341 v8::Locker lock;
8342 v8::V8::Initialize();
8343 v8::HandleScope scope;
8344 Local<Context> local_env;
8345 {
8346 LocalContext env;
8347 local_env = env.local();
8348 }
8349
8350 // Local context should still be live.
8351 CHECK(!local_env.IsEmpty());
8352 local_env->Enter();
8353
8354 // Should complete without problems.
8355 RegExpStringModificationTest().RunTest();
8356
8357 local_env->Exit();
8358}
8359
8360
8361// Test that we can set a property on the global object even if there
8362// is a read-only property in the prototype chain.
8363TEST(ReadOnlyPropertyInGlobalProto) {
8364 v8::HandleScope scope;
8365 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8366 LocalContext context(0, templ);
8367 v8::Handle<v8::Object> global = context->Global();
8368 v8::Handle<v8::Object> global_proto =
8369 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
8370 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
8371 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
8372 // Check without 'eval' or 'with'.
8373 v8::Handle<v8::Value> res =
8374 CompileRun("function f() { x = 42; return x; }; f()");
8375 // Check with 'eval'.
8376 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
8377 CHECK_EQ(v8::Integer::New(42), res);
8378 // Check with 'with'.
8379 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
8380 CHECK_EQ(v8::Integer::New(42), res);
8381}
8382
8383static int force_set_set_count = 0;
8384static int force_set_get_count = 0;
8385bool pass_on_get = false;
8386
8387static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
8388 const v8::AccessorInfo& info) {
8389 force_set_get_count++;
8390 if (pass_on_get) {
8391 return v8::Handle<v8::Value>();
8392 } else {
8393 return v8::Int32::New(3);
8394 }
8395}
8396
8397static void ForceSetSetter(v8::Local<v8::String> name,
8398 v8::Local<v8::Value> value,
8399 const v8::AccessorInfo& info) {
8400 force_set_set_count++;
8401}
8402
8403static v8::Handle<v8::Value> ForceSetInterceptSetter(
8404 v8::Local<v8::String> name,
8405 v8::Local<v8::Value> value,
8406 const v8::AccessorInfo& info) {
8407 force_set_set_count++;
8408 return v8::Undefined();
8409}
8410
8411TEST(ForceSet) {
8412 force_set_get_count = 0;
8413 force_set_set_count = 0;
8414 pass_on_get = false;
8415
8416 v8::HandleScope scope;
8417 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8418 v8::Handle<v8::String> access_property = v8::String::New("a");
8419 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
8420 LocalContext context(NULL, templ);
8421 v8::Handle<v8::Object> global = context->Global();
8422
8423 // Ordinary properties
8424 v8::Handle<v8::String> simple_property = v8::String::New("p");
8425 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
8426 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
8427 // This should fail because the property is read-only
8428 global->Set(simple_property, v8::Int32::New(5));
8429 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
8430 // This should succeed even though the property is read-only
8431 global->ForceSet(simple_property, v8::Int32::New(6));
8432 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
8433
8434 // Accessors
8435 CHECK_EQ(0, force_set_set_count);
8436 CHECK_EQ(0, force_set_get_count);
8437 CHECK_EQ(3, global->Get(access_property)->Int32Value());
8438 // CHECK_EQ the property shouldn't override it, just call the setter
8439 // which in this case does nothing.
8440 global->Set(access_property, v8::Int32::New(7));
8441 CHECK_EQ(3, global->Get(access_property)->Int32Value());
8442 CHECK_EQ(1, force_set_set_count);
8443 CHECK_EQ(2, force_set_get_count);
8444 // Forcing the property to be set should override the accessor without
8445 // calling it
8446 global->ForceSet(access_property, v8::Int32::New(8));
8447 CHECK_EQ(8, global->Get(access_property)->Int32Value());
8448 CHECK_EQ(1, force_set_set_count);
8449 CHECK_EQ(2, force_set_get_count);
8450}
8451
8452TEST(ForceSetWithInterceptor) {
8453 force_set_get_count = 0;
8454 force_set_set_count = 0;
8455 pass_on_get = false;
8456
8457 v8::HandleScope scope;
8458 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8459 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
8460 LocalContext context(NULL, templ);
8461 v8::Handle<v8::Object> global = context->Global();
8462
8463 v8::Handle<v8::String> some_property = v8::String::New("a");
8464 CHECK_EQ(0, force_set_set_count);
8465 CHECK_EQ(0, force_set_get_count);
8466 CHECK_EQ(3, global->Get(some_property)->Int32Value());
8467 // Setting the property shouldn't override it, just call the setter
8468 // which in this case does nothing.
8469 global->Set(some_property, v8::Int32::New(7));
8470 CHECK_EQ(3, global->Get(some_property)->Int32Value());
8471 CHECK_EQ(1, force_set_set_count);
8472 CHECK_EQ(2, force_set_get_count);
8473 // Getting the property when the interceptor returns an empty handle
8474 // should yield undefined, since the property isn't present on the
8475 // object itself yet.
8476 pass_on_get = true;
8477 CHECK(global->Get(some_property)->IsUndefined());
8478 CHECK_EQ(1, force_set_set_count);
8479 CHECK_EQ(3, force_set_get_count);
8480 // Forcing the property to be set should cause the value to be
8481 // set locally without calling the interceptor.
8482 global->ForceSet(some_property, v8::Int32::New(8));
8483 CHECK_EQ(8, global->Get(some_property)->Int32Value());
8484 CHECK_EQ(1, force_set_set_count);
8485 CHECK_EQ(4, force_set_get_count);
8486 // Reenabling the interceptor should cause it to take precedence over
8487 // the property
8488 pass_on_get = false;
8489 CHECK_EQ(3, global->Get(some_property)->Int32Value());
8490 CHECK_EQ(1, force_set_set_count);
8491 CHECK_EQ(5, force_set_get_count);
8492 // The interceptor should also work for other properties
8493 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
8494 CHECK_EQ(1, force_set_set_count);
8495 CHECK_EQ(6, force_set_get_count);
8496}
8497
8498
8499THREADED_TEST(ForceDelete) {
8500 v8::HandleScope scope;
8501 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8502 LocalContext context(NULL, templ);
8503 v8::Handle<v8::Object> global = context->Global();
8504
8505 // Ordinary properties
8506 v8::Handle<v8::String> simple_property = v8::String::New("p");
8507 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
8508 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
8509 // This should fail because the property is dont-delete.
8510 CHECK(!global->Delete(simple_property));
8511 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
8512 // This should succeed even though the property is dont-delete.
8513 CHECK(global->ForceDelete(simple_property));
8514 CHECK(global->Get(simple_property)->IsUndefined());
8515}
8516
8517
8518static int force_delete_interceptor_count = 0;
8519static bool pass_on_delete = false;
8520
8521
8522static v8::Handle<v8::Boolean> ForceDeleteDeleter(
8523 v8::Local<v8::String> name,
8524 const v8::AccessorInfo& info) {
8525 force_delete_interceptor_count++;
8526 if (pass_on_delete) {
8527 return v8::Handle<v8::Boolean>();
8528 } else {
8529 return v8::True();
8530 }
8531}
8532
8533
8534THREADED_TEST(ForceDeleteWithInterceptor) {
8535 force_delete_interceptor_count = 0;
8536 pass_on_delete = false;
8537
8538 v8::HandleScope scope;
8539 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
8540 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
8541 LocalContext context(NULL, templ);
8542 v8::Handle<v8::Object> global = context->Global();
8543
8544 v8::Handle<v8::String> some_property = v8::String::New("a");
8545 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
8546
8547 // Deleting a property should get intercepted and nothing should
8548 // happen.
8549 CHECK_EQ(0, force_delete_interceptor_count);
8550 CHECK(global->Delete(some_property));
8551 CHECK_EQ(1, force_delete_interceptor_count);
8552 CHECK_EQ(42, global->Get(some_property)->Int32Value());
8553 // Deleting the property when the interceptor returns an empty
8554 // handle should not delete the property since it is DontDelete.
8555 pass_on_delete = true;
8556 CHECK(!global->Delete(some_property));
8557 CHECK_EQ(2, force_delete_interceptor_count);
8558 CHECK_EQ(42, global->Get(some_property)->Int32Value());
8559 // Forcing the property to be deleted should delete the value
8560 // without calling the interceptor.
8561 CHECK(global->ForceDelete(some_property));
8562 CHECK(global->Get(some_property)->IsUndefined());
8563 CHECK_EQ(2, force_delete_interceptor_count);
8564}
8565
8566
8567// Make sure that forcing a delete invalidates any IC stubs, so we
8568// don't read the hole value.
8569THREADED_TEST(ForceDeleteIC) {
8570 v8::HandleScope scope;
8571 LocalContext context;
8572 // Create a DontDelete variable on the global object.
8573 CompileRun("this.__proto__ = { foo: 'horse' };"
8574 "var foo = 'fish';"
8575 "function f() { return foo.length; }");
8576 // Initialize the IC for foo in f.
8577 CompileRun("for (var i = 0; i < 4; i++) f();");
8578 // Make sure the value of foo is correct before the deletion.
8579 CHECK_EQ(4, CompileRun("f()")->Int32Value());
8580 // Force the deletion of foo.
8581 CHECK(context->Global()->ForceDelete(v8_str("foo")));
8582 // Make sure the value for foo is read from the prototype, and that
8583 // we don't get in trouble with reading the deleted cell value
8584 // sentinel.
8585 CHECK_EQ(5, CompileRun("f()")->Int32Value());
8586}
8587
8588
8589v8::Persistent<Context> calling_context0;
8590v8::Persistent<Context> calling_context1;
8591v8::Persistent<Context> calling_context2;
8592
8593
8594// Check that the call to the callback is initiated in
8595// calling_context2, the directly calling context is calling_context1
8596// and the callback itself is in calling_context0.
8597static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
8598 ApiTestFuzzer::Fuzz();
8599 CHECK(Context::GetCurrent() == calling_context0);
8600 CHECK(Context::GetCalling() == calling_context1);
8601 CHECK(Context::GetEntered() == calling_context2);
8602 return v8::Integer::New(42);
8603}
8604
8605
8606THREADED_TEST(GetCallingContext) {
8607 v8::HandleScope scope;
8608
8609 calling_context0 = Context::New();
8610 calling_context1 = Context::New();
8611 calling_context2 = Context::New();
8612
8613 // Allow cross-domain access.
8614 Local<String> token = v8_str("<security token>");
8615 calling_context0->SetSecurityToken(token);
8616 calling_context1->SetSecurityToken(token);
8617 calling_context2->SetSecurityToken(token);
8618
8619 // Create an object with a C++ callback in context0.
8620 calling_context0->Enter();
8621 Local<v8::FunctionTemplate> callback_templ =
8622 v8::FunctionTemplate::New(GetCallingContextCallback);
8623 calling_context0->Global()->Set(v8_str("callback"),
8624 callback_templ->GetFunction());
8625 calling_context0->Exit();
8626
8627 // Expose context0 in context1 and setup a function that calls the
8628 // callback function.
8629 calling_context1->Enter();
8630 calling_context1->Global()->Set(v8_str("context0"),
8631 calling_context0->Global());
8632 CompileRun("function f() { context0.callback() }");
8633 calling_context1->Exit();
8634
8635 // Expose context1 in context2 and call the callback function in
8636 // context0 indirectly through f in context1.
8637 calling_context2->Enter();
8638 calling_context2->Global()->Set(v8_str("context1"),
8639 calling_context1->Global());
8640 CompileRun("context1.f()");
8641 calling_context2->Exit();
8642
8643 // Dispose the contexts to allow them to be garbage collected.
8644 calling_context0.Dispose();
8645 calling_context1.Dispose();
8646 calling_context2.Dispose();
8647 calling_context0.Clear();
8648 calling_context1.Clear();
8649 calling_context2.Clear();
8650}
8651
8652
8653// Check that a variable declaration with no explicit initialization
8654// value does not shadow an existing property in the prototype chain.
8655//
8656// This is consistent with Firefox and Safari.
8657//
8658// See http://crbug.com/12548.
8659THREADED_TEST(InitGlobalVarInProtoChain) {
8660 v8::HandleScope scope;
8661 LocalContext context;
8662 // Introduce a variable in the prototype chain.
8663 CompileRun("__proto__.x = 42");
8664 v8::Handle<v8::Value> result = CompileRun("var x; x");
8665 CHECK(!result->IsUndefined());
8666 CHECK_EQ(42, result->Int32Value());
8667}
8668
8669
8670// Regression test for issue 398.
8671// If a function is added to an object, creating a constant function
8672// field, and the result is cloned, replacing the constant function on the
8673// original should not affect the clone.
8674// See http://code.google.com/p/v8/issues/detail?id=398
8675THREADED_TEST(ReplaceConstantFunction) {
8676 v8::HandleScope scope;
8677 LocalContext context;
8678 v8::Handle<v8::Object> obj = v8::Object::New();
8679 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
8680 v8::Handle<v8::String> foo_string = v8::String::New("foo");
8681 obj->Set(foo_string, func_templ->GetFunction());
8682 v8::Handle<v8::Object> obj_clone = obj->Clone();
8683 obj_clone->Set(foo_string, v8::String::New("Hello"));
8684 CHECK(!obj->Get(foo_string)->IsUndefined());
8685}
8686
8687
8688// Regression test for http://crbug.com/16276.
8689THREADED_TEST(Regress16276) {
8690 v8::HandleScope scope;
8691 LocalContext context;
8692 // Force the IC in f to be a dictionary load IC.
8693 CompileRun("function f(obj) { return obj.x; }\n"
8694 "var obj = { x: { foo: 42 }, y: 87 };\n"
8695 "var x = obj.x;\n"
8696 "delete obj.y;\n"
8697 "for (var i = 0; i < 5; i++) f(obj);");
8698 // Detach the global object to make 'this' refer directly to the
8699 // global object (not the proxy), and make sure that the dictionary
8700 // load IC doesn't mess up loading directly from the global object.
8701 context->DetachGlobal();
8702 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
8703}
8704
8705
8706THREADED_TEST(PixelArray) {
8707 v8::HandleScope scope;
8708 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00008709 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00008710 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
8711 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
8712 pixel_data);
8713 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
8714 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00008715 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00008716 }
8717 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
8718 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00008719 CHECK_EQ(i % 256, pixels->get(i));
8720 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00008721 }
8722
8723 v8::Handle<v8::Object> obj = v8::Object::New();
8724 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
8725 // Set the elements to be the pixels.
8726 // jsobj->set_elements(*pixels);
8727 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
8728 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
8729 obj->Set(v8_str("field"), v8::Int32::New(1503));
8730 context->Global()->Set(v8_str("pixels"), obj);
8731 v8::Handle<v8::Value> result = CompileRun("pixels.field");
8732 CHECK_EQ(1503, result->Int32Value());
8733 result = CompileRun("pixels[1]");
8734 CHECK_EQ(1, result->Int32Value());
8735
8736 result = CompileRun("var sum = 0;"
8737 "for (var i = 0; i < 8; i++) {"
8738 " sum += pixels[i] = pixels[i] = -i;"
8739 "}"
8740 "sum;");
8741 CHECK_EQ(-28, result->Int32Value());
8742
8743 result = CompileRun("var sum = 0;"
8744 "for (var i = 0; i < 8; i++) {"
8745 " sum += pixels[i] = pixels[i] = 0;"
8746 "}"
8747 "sum;");
8748 CHECK_EQ(0, result->Int32Value());
8749
8750 result = CompileRun("var sum = 0;"
8751 "for (var i = 0; i < 8; i++) {"
8752 " sum += pixels[i] = pixels[i] = 255;"
8753 "}"
8754 "sum;");
8755 CHECK_EQ(8 * 255, result->Int32Value());
8756
8757 result = CompileRun("var sum = 0;"
8758 "for (var i = 0; i < 8; i++) {"
8759 " sum += pixels[i] = pixels[i] = 256 + i;"
8760 "}"
8761 "sum;");
8762 CHECK_EQ(2076, result->Int32Value());
8763
8764 result = CompileRun("var sum = 0;"
8765 "for (var i = 0; i < 8; i++) {"
8766 " sum += pixels[i] = pixels[i] = i;"
8767 "}"
8768 "sum;");
8769 CHECK_EQ(28, result->Int32Value());
8770
8771 result = CompileRun("var sum = 0;"
8772 "for (var i = 0; i < 8; i++) {"
8773 " sum += pixels[i];"
8774 "}"
8775 "sum;");
8776 CHECK_EQ(28, result->Int32Value());
8777
8778 i::Handle<i::Smi> value(i::Smi::FromInt(2));
8779 i::SetElement(jsobj, 1, value);
8780 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
8781 *value.location() = i::Smi::FromInt(256);
8782 i::SetElement(jsobj, 1, value);
8783 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
8784 *value.location() = i::Smi::FromInt(-1);
8785 i::SetElement(jsobj, 1, value);
8786 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
8787
8788 result = CompileRun("for (var i = 0; i < 8; i++) {"
8789 " pixels[i] = (i * 65) - 109;"
8790 "}"
8791 "pixels[1] + pixels[6];");
8792 CHECK_EQ(255, result->Int32Value());
8793 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
8794 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
8795 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
8796 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
8797 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
8798 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
8799 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
8800 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
8801 result = CompileRun("var sum = 0;"
8802 "for (var i = 0; i < 8; i++) {"
8803 " sum += pixels[i];"
8804 "}"
8805 "sum;");
8806 CHECK_EQ(984, result->Int32Value());
8807
8808 result = CompileRun("for (var i = 0; i < 8; i++) {"
8809 " pixels[i] = (i * 1.1);"
8810 "}"
8811 "pixels[1] + pixels[6];");
8812 CHECK_EQ(8, result->Int32Value());
8813 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
8814 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
8815 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
8816 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
8817 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
8818 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
8819 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
8820 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
8821
8822 result = CompileRun("for (var i = 0; i < 8; i++) {"
8823 " pixels[7] = undefined;"
8824 "}"
8825 "pixels[7];");
8826 CHECK_EQ(0, result->Int32Value());
8827 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
8828
8829 result = CompileRun("for (var i = 0; i < 8; i++) {"
8830 " pixels[6] = '2.3';"
8831 "}"
8832 "pixels[6];");
8833 CHECK_EQ(2, result->Int32Value());
8834 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
8835
8836 result = CompileRun("for (var i = 0; i < 8; i++) {"
8837 " pixels[5] = NaN;"
8838 "}"
8839 "pixels[5];");
8840 CHECK_EQ(0, result->Int32Value());
8841 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
8842
8843 result = CompileRun("for (var i = 0; i < 8; i++) {"
8844 " pixels[8] = Infinity;"
8845 "}"
8846 "pixels[8];");
8847 CHECK_EQ(255, result->Int32Value());
8848 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
8849
8850 result = CompileRun("for (var i = 0; i < 8; i++) {"
8851 " pixels[9] = -Infinity;"
8852 "}"
8853 "pixels[9];");
8854 CHECK_EQ(0, result->Int32Value());
8855 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
8856
8857 result = CompileRun("pixels[3] = 33;"
8858 "delete pixels[3];"
8859 "pixels[3];");
8860 CHECK_EQ(33, result->Int32Value());
8861
8862 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
8863 "pixels[2] = 12; pixels[3] = 13;"
8864 "pixels.__defineGetter__('2',"
8865 "function() { return 120; });"
8866 "pixels[2];");
8867 CHECK_EQ(12, result->Int32Value());
8868
8869 result = CompileRun("var js_array = new Array(40);"
8870 "js_array[0] = 77;"
8871 "js_array;");
8872 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
8873
8874 result = CompileRun("pixels[1] = 23;"
8875 "pixels.__proto__ = [];"
8876 "js_array.__proto__ = pixels;"
8877 "js_array.concat(pixels);");
8878 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
8879 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
8880
8881 result = CompileRun("pixels[1] = 23;");
8882 CHECK_EQ(23, result->Int32Value());
8883
Steve Blockd0582a62009-12-15 09:54:21 +00008884 // Test for index greater than 255. Regression test for:
8885 // http://code.google.com/p/chromium/issues/detail?id=26337.
8886 result = CompileRun("pixels[256] = 255;");
8887 CHECK_EQ(255, result->Int32Value());
8888 result = CompileRun("var i = 0;"
8889 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
8890 "i");
8891 CHECK_EQ(255, result->Int32Value());
8892
Steve Blocka7e24c12009-10-30 11:49:00 +00008893 free(pixel_data);
8894}
8895
8896
Steve Block3ce2e202009-11-05 08:53:23 +00008897template <class ExternalArrayClass, class ElementType>
8898static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
8899 int64_t low,
8900 int64_t high) {
8901 v8::HandleScope scope;
8902 LocalContext context;
8903 const int kElementCount = 40;
8904 int element_size = 0;
8905 switch (array_type) {
8906 case v8::kExternalByteArray:
8907 case v8::kExternalUnsignedByteArray:
8908 element_size = 1;
8909 break;
8910 case v8::kExternalShortArray:
8911 case v8::kExternalUnsignedShortArray:
8912 element_size = 2;
8913 break;
8914 case v8::kExternalIntArray:
8915 case v8::kExternalUnsignedIntArray:
8916 case v8::kExternalFloatArray:
8917 element_size = 4;
8918 break;
8919 default:
8920 UNREACHABLE();
8921 break;
8922 }
8923 ElementType* array_data =
8924 static_cast<ElementType*>(malloc(kElementCount * element_size));
8925 i::Handle<ExternalArrayClass> array =
8926 i::Handle<ExternalArrayClass>::cast(
8927 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
8928 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
8929 for (int i = 0; i < kElementCount; i++) {
8930 array->set(i, static_cast<ElementType>(i));
8931 }
8932 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
8933 for (int i = 0; i < kElementCount; i++) {
8934 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
8935 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
8936 }
8937
8938 v8::Handle<v8::Object> obj = v8::Object::New();
8939 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
8940 // Set the elements to be the external array.
8941 obj->SetIndexedPropertiesToExternalArrayData(array_data,
8942 array_type,
8943 kElementCount);
8944 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
8945 obj->Set(v8_str("field"), v8::Int32::New(1503));
8946 context->Global()->Set(v8_str("ext_array"), obj);
8947 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
8948 CHECK_EQ(1503, result->Int32Value());
8949 result = CompileRun("ext_array[1]");
8950 CHECK_EQ(1, result->Int32Value());
8951
8952 // Check pass through of assigned smis
8953 result = CompileRun("var sum = 0;"
8954 "for (var i = 0; i < 8; i++) {"
8955 " sum += ext_array[i] = ext_array[i] = -i;"
8956 "}"
8957 "sum;");
8958 CHECK_EQ(-28, result->Int32Value());
8959
8960 // Check assigned smis
8961 result = CompileRun("for (var i = 0; i < 8; i++) {"
8962 " ext_array[i] = i;"
8963 "}"
8964 "var sum = 0;"
8965 "for (var i = 0; i < 8; i++) {"
8966 " sum += ext_array[i];"
8967 "}"
8968 "sum;");
8969 CHECK_EQ(28, result->Int32Value());
8970
8971 // Check assigned smis in reverse order
8972 result = CompileRun("for (var i = 8; --i >= 0; ) {"
8973 " ext_array[i] = i;"
8974 "}"
8975 "var sum = 0;"
8976 "for (var i = 0; i < 8; i++) {"
8977 " sum += ext_array[i];"
8978 "}"
8979 "sum;");
8980 CHECK_EQ(28, result->Int32Value());
8981
8982 // Check pass through of assigned HeapNumbers
8983 result = CompileRun("var sum = 0;"
8984 "for (var i = 0; i < 16; i+=2) {"
8985 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
8986 "}"
8987 "sum;");
8988 CHECK_EQ(-28, result->Int32Value());
8989
8990 // Check assigned HeapNumbers
8991 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
8992 " ext_array[i] = (i * 0.5);"
8993 "}"
8994 "var sum = 0;"
8995 "for (var i = 0; i < 16; i+=2) {"
8996 " sum += ext_array[i];"
8997 "}"
8998 "sum;");
8999 CHECK_EQ(28, result->Int32Value());
9000
9001 // Check assigned HeapNumbers in reverse order
9002 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9003 " ext_array[i] = (i * 0.5);"
9004 "}"
9005 "var sum = 0;"
9006 "for (var i = 0; i < 16; i+=2) {"
9007 " sum += ext_array[i];"
9008 "}"
9009 "sum;");
9010 CHECK_EQ(28, result->Int32Value());
9011
9012 i::ScopedVector<char> test_buf(1024);
9013
9014 // Check legal boundary conditions.
9015 // The repeated loads and stores ensure the ICs are exercised.
9016 const char* boundary_program =
9017 "var res = 0;"
9018 "for (var i = 0; i < 16; i++) {"
9019 " ext_array[i] = %lld;"
9020 " if (i > 8) {"
9021 " res = ext_array[i];"
9022 " }"
9023 "}"
9024 "res;";
9025 i::OS::SNPrintF(test_buf,
9026 boundary_program,
9027 low);
9028 result = CompileRun(test_buf.start());
9029 CHECK_EQ(low, result->IntegerValue());
9030
9031 i::OS::SNPrintF(test_buf,
9032 boundary_program,
9033 high);
9034 result = CompileRun(test_buf.start());
9035 CHECK_EQ(high, result->IntegerValue());
9036
9037 // Check misprediction of type in IC.
9038 result = CompileRun("var tmp_array = ext_array;"
9039 "var sum = 0;"
9040 "for (var i = 0; i < 8; i++) {"
9041 " tmp_array[i] = i;"
9042 " sum += tmp_array[i];"
9043 " if (i == 4) {"
9044 " tmp_array = {};"
9045 " }"
9046 "}"
9047 "sum;");
9048 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9049 CHECK_EQ(28, result->Int32Value());
9050
9051 // Make sure out-of-range loads do not throw.
9052 i::OS::SNPrintF(test_buf,
9053 "var caught_exception = false;"
9054 "try {"
9055 " ext_array[%d];"
9056 "} catch (e) {"
9057 " caught_exception = true;"
9058 "}"
9059 "caught_exception;",
9060 kElementCount);
9061 result = CompileRun(test_buf.start());
9062 CHECK_EQ(false, result->BooleanValue());
9063
9064 // Make sure out-of-range stores do not throw.
9065 i::OS::SNPrintF(test_buf,
9066 "var caught_exception = false;"
9067 "try {"
9068 " ext_array[%d] = 1;"
9069 "} catch (e) {"
9070 " caught_exception = true;"
9071 "}"
9072 "caught_exception;",
9073 kElementCount);
9074 result = CompileRun(test_buf.start());
9075 CHECK_EQ(false, result->BooleanValue());
9076
9077 // Check other boundary conditions, values and operations.
9078 result = CompileRun("for (var i = 0; i < 8; i++) {"
9079 " ext_array[7] = undefined;"
9080 "}"
9081 "ext_array[7];");
9082 CHECK_EQ(0, result->Int32Value());
9083 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
9084
9085 result = CompileRun("for (var i = 0; i < 8; i++) {"
9086 " ext_array[6] = '2.3';"
9087 "}"
9088 "ext_array[6];");
9089 CHECK_EQ(2, result->Int32Value());
9090 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
9091
9092 if (array_type != v8::kExternalFloatArray) {
9093 // Though the specification doesn't state it, be explicit about
9094 // converting NaNs and +/-Infinity to zero.
9095 result = CompileRun("for (var i = 0; i < 8; i++) {"
9096 " ext_array[i] = 5;"
9097 "}"
9098 "for (var i = 0; i < 8; i++) {"
9099 " ext_array[i] = NaN;"
9100 "}"
9101 "ext_array[5];");
9102 CHECK_EQ(0, result->Int32Value());
9103 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9104
9105 result = CompileRun("for (var i = 0; i < 8; i++) {"
9106 " ext_array[i] = 5;"
9107 "}"
9108 "for (var i = 0; i < 8; i++) {"
9109 " ext_array[i] = Infinity;"
9110 "}"
9111 "ext_array[5];");
9112 CHECK_EQ(0, result->Int32Value());
9113 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9114
9115 result = CompileRun("for (var i = 0; i < 8; i++) {"
9116 " ext_array[i] = 5;"
9117 "}"
9118 "for (var i = 0; i < 8; i++) {"
9119 " ext_array[i] = -Infinity;"
9120 "}"
9121 "ext_array[5];");
9122 CHECK_EQ(0, result->Int32Value());
9123 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9124 }
9125
9126 result = CompileRun("ext_array[3] = 33;"
9127 "delete ext_array[3];"
9128 "ext_array[3];");
9129 CHECK_EQ(33, result->Int32Value());
9130
9131 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
9132 "ext_array[2] = 12; ext_array[3] = 13;"
9133 "ext_array.__defineGetter__('2',"
9134 "function() { return 120; });"
9135 "ext_array[2];");
9136 CHECK_EQ(12, result->Int32Value());
9137
9138 result = CompileRun("var js_array = new Array(40);"
9139 "js_array[0] = 77;"
9140 "js_array;");
9141 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9142
9143 result = CompileRun("ext_array[1] = 23;"
9144 "ext_array.__proto__ = [];"
9145 "js_array.__proto__ = ext_array;"
9146 "js_array.concat(ext_array);");
9147 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9148 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9149
9150 result = CompileRun("ext_array[1] = 23;");
9151 CHECK_EQ(23, result->Int32Value());
9152
Steve Blockd0582a62009-12-15 09:54:21 +00009153 // Test more complex manipulations which cause eax to contain values
9154 // that won't be completely overwritten by loads from the arrays.
9155 // This catches bugs in the instructions used for the KeyedLoadIC
9156 // for byte and word types.
9157 {
9158 const int kXSize = 300;
9159 const int kYSize = 300;
9160 const int kLargeElementCount = kXSize * kYSize * 4;
9161 ElementType* large_array_data =
9162 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
9163 i::Handle<ExternalArrayClass> large_array =
9164 i::Handle<ExternalArrayClass>::cast(
9165 i::Factory::NewExternalArray(kLargeElementCount,
9166 array_type,
9167 array_data));
9168 v8::Handle<v8::Object> large_obj = v8::Object::New();
9169 // Set the elements to be the external array.
9170 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
9171 array_type,
9172 kLargeElementCount);
9173 context->Global()->Set(v8_str("large_array"), large_obj);
9174 // Initialize contents of a few rows.
9175 for (int x = 0; x < 300; x++) {
9176 int row = 0;
9177 int offset = row * 300 * 4;
9178 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9179 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9180 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9181 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9182 row = 150;
9183 offset = row * 300 * 4;
9184 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9185 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9186 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9187 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9188 row = 298;
9189 offset = row * 300 * 4;
9190 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9191 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9192 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9193 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9194 }
9195 // The goal of the code below is to make "offset" large enough
9196 // that the computation of the index (which goes into eax) has
9197 // high bits set which will not be overwritten by a byte or short
9198 // load.
9199 result = CompileRun("var failed = false;"
9200 "var offset = 0;"
9201 "for (var i = 0; i < 300; i++) {"
9202 " if (large_array[4 * i] != 127 ||"
9203 " large_array[4 * i + 1] != 0 ||"
9204 " large_array[4 * i + 2] != 0 ||"
9205 " large_array[4 * i + 3] != 127) {"
9206 " failed = true;"
9207 " }"
9208 "}"
9209 "offset = 150 * 300 * 4;"
9210 "for (var i = 0; i < 300; i++) {"
9211 " if (large_array[offset + 4 * i] != 127 ||"
9212 " large_array[offset + 4 * i + 1] != 0 ||"
9213 " large_array[offset + 4 * i + 2] != 0 ||"
9214 " large_array[offset + 4 * i + 3] != 127) {"
9215 " failed = true;"
9216 " }"
9217 "}"
9218 "offset = 298 * 300 * 4;"
9219 "for (var i = 0; i < 300; i++) {"
9220 " if (large_array[offset + 4 * i] != 127 ||"
9221 " large_array[offset + 4 * i + 1] != 0 ||"
9222 " large_array[offset + 4 * i + 2] != 0 ||"
9223 " large_array[offset + 4 * i + 3] != 127) {"
9224 " failed = true;"
9225 " }"
9226 "}"
9227 "!failed;");
9228 CHECK_EQ(true, result->BooleanValue());
9229 free(large_array_data);
9230 }
9231
Steve Block3ce2e202009-11-05 08:53:23 +00009232 free(array_data);
9233}
9234
9235
9236THREADED_TEST(ExternalByteArray) {
9237 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>(
9238 v8::kExternalByteArray,
9239 -128,
9240 127);
9241}
9242
9243
9244THREADED_TEST(ExternalUnsignedByteArray) {
9245 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>(
9246 v8::kExternalUnsignedByteArray,
9247 0,
9248 255);
9249}
9250
9251
9252THREADED_TEST(ExternalShortArray) {
9253 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>(
9254 v8::kExternalShortArray,
9255 -32768,
9256 32767);
9257}
9258
9259
9260THREADED_TEST(ExternalUnsignedShortArray) {
9261 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>(
9262 v8::kExternalUnsignedShortArray,
9263 0,
9264 65535);
9265}
9266
9267
9268THREADED_TEST(ExternalIntArray) {
9269 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>(
9270 v8::kExternalIntArray,
9271 INT_MIN, // -2147483648
9272 INT_MAX); // 2147483647
9273}
9274
9275
9276THREADED_TEST(ExternalUnsignedIntArray) {
9277 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>(
9278 v8::kExternalUnsignedIntArray,
9279 0,
9280 UINT_MAX); // 4294967295
9281}
9282
9283
9284THREADED_TEST(ExternalFloatArray) {
9285 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>(
9286 v8::kExternalFloatArray,
9287 -500,
9288 500);
9289}
9290
9291
9292THREADED_TEST(ExternalArrays) {
9293 TestExternalByteArray();
9294 TestExternalUnsignedByteArray();
9295 TestExternalShortArray();
9296 TestExternalUnsignedShortArray();
9297 TestExternalIntArray();
9298 TestExternalUnsignedIntArray();
9299 TestExternalFloatArray();
9300}
9301
9302
Steve Blocka7e24c12009-10-30 11:49:00 +00009303THREADED_TEST(ScriptContextDependence) {
9304 v8::HandleScope scope;
9305 LocalContext c1;
9306 const char *source = "foo";
9307 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
9308 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
9309 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
9310 CHECK_EQ(dep->Run()->Int32Value(), 100);
9311 CHECK_EQ(indep->Run()->Int32Value(), 100);
9312 LocalContext c2;
9313 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
9314 CHECK_EQ(dep->Run()->Int32Value(), 100);
9315 CHECK_EQ(indep->Run()->Int32Value(), 101);
9316}
9317
9318
9319THREADED_TEST(StackTrace) {
9320 v8::HandleScope scope;
9321 LocalContext context;
9322 v8::TryCatch try_catch;
9323 const char *source = "function foo() { FAIL.FAIL; }; foo();";
9324 v8::Handle<v8::String> src = v8::String::New(source);
9325 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
9326 v8::Script::New(src, origin)->Run();
9327 CHECK(try_catch.HasCaught());
9328 v8::String::Utf8Value stack(try_catch.StackTrace());
9329 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
9330}
9331
9332
Steve Block3ce2e202009-11-05 08:53:23 +00009333// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +00009334THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +00009335 bool rv = false;
9336 for (int i = 0; i < 100; i++) {
9337 rv = v8::V8::IdleNotification();
9338 if (rv)
9339 break;
9340 }
9341 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +00009342}
9343
9344
9345static uint32_t* stack_limit;
9346
9347static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
9348 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
9349 return v8::Undefined();
9350}
9351
9352
9353// Uses the address of a local variable to determine the stack top now.
9354// Given a size, returns an address that is that far from the current
9355// top of stack.
9356static uint32_t* ComputeStackLimit(uint32_t size) {
9357 uint32_t* answer = &size - (size / sizeof(size));
9358 // If the size is very large and the stack is very near the bottom of
9359 // memory then the calculation above may wrap around and give an address
9360 // that is above the (downwards-growing) stack. In that case we return
9361 // a very low address.
9362 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
9363 return answer;
9364}
9365
9366
9367TEST(SetResourceConstraints) {
9368 static const int K = 1024;
9369 uint32_t* set_limit = ComputeStackLimit(128 * K);
9370
9371 // Set stack limit.
9372 v8::ResourceConstraints constraints;
9373 constraints.set_stack_limit(set_limit);
9374 CHECK(v8::SetResourceConstraints(&constraints));
9375
9376 // Execute a script.
9377 v8::HandleScope scope;
9378 LocalContext env;
9379 Local<v8::FunctionTemplate> fun_templ =
9380 v8::FunctionTemplate::New(GetStackLimitCallback);
9381 Local<Function> fun = fun_templ->GetFunction();
9382 env->Global()->Set(v8_str("get_stack_limit"), fun);
9383 CompileRun("get_stack_limit();");
9384
9385 CHECK(stack_limit == set_limit);
9386}
9387
9388
9389TEST(SetResourceConstraintsInThread) {
9390 uint32_t* set_limit;
9391 {
9392 v8::Locker locker;
9393 static const int K = 1024;
9394 set_limit = ComputeStackLimit(128 * K);
9395
9396 // Set stack limit.
9397 v8::ResourceConstraints constraints;
9398 constraints.set_stack_limit(set_limit);
9399 CHECK(v8::SetResourceConstraints(&constraints));
9400
9401 // Execute a script.
9402 v8::HandleScope scope;
9403 LocalContext env;
9404 Local<v8::FunctionTemplate> fun_templ =
9405 v8::FunctionTemplate::New(GetStackLimitCallback);
9406 Local<Function> fun = fun_templ->GetFunction();
9407 env->Global()->Set(v8_str("get_stack_limit"), fun);
9408 CompileRun("get_stack_limit();");
9409
9410 CHECK(stack_limit == set_limit);
9411 }
9412 {
9413 v8::Locker locker;
9414 CHECK(stack_limit == set_limit);
9415 }
9416}
Steve Block3ce2e202009-11-05 08:53:23 +00009417
9418
9419THREADED_TEST(GetHeapStatistics) {
9420 v8::HandleScope scope;
9421 LocalContext c1;
9422 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +00009423 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
9424 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +00009425 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +00009426 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
9427 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
9428}
9429
9430
9431static double DoubleFromBits(uint64_t value) {
9432 double target;
9433#ifdef BIG_ENDIAN_FLOATING_POINT
9434 const int kIntSize = 4;
9435 // Somebody swapped the lower and higher half of doubles.
9436 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
9437 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
9438#else
9439 memcpy(&target, &value, sizeof(target));
9440#endif
9441 return target;
9442}
9443
9444
9445static uint64_t DoubleToBits(double value) {
9446 uint64_t target;
9447#ifdef BIG_ENDIAN_FLOATING_POINT
9448 const int kIntSize = 4;
9449 // Somebody swapped the lower and higher half of doubles.
9450 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
9451 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
9452#else
9453 memcpy(&target, &value, sizeof(target));
9454#endif
9455 return target;
9456}
9457
9458
9459static double DoubleToDateTime(double input) {
9460 double date_limit = 864e13;
9461 if (IsNaN(input) || input < -date_limit || input > date_limit) {
9462 return i::OS::nan_value();
9463 }
9464 return (input < 0) ? -(floor(-input)) : floor(input);
9465}
9466
9467// We don't have a consistent way to write 64-bit constants syntactically, so we
9468// split them into two 32-bit constants and combine them programmatically.
9469static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
9470 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
9471}
9472
9473
9474THREADED_TEST(QuietSignalingNaNs) {
9475 v8::HandleScope scope;
9476 LocalContext context;
9477 v8::TryCatch try_catch;
9478
9479 // Special double values.
9480 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
9481 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
9482 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
9483 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
9484 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
9485 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
9486 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
9487
9488 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
9489 // on either side of the epoch.
9490 double date_limit = 864e13;
9491
9492 double test_values[] = {
9493 snan,
9494 qnan,
9495 infinity,
9496 max_normal,
9497 date_limit + 1,
9498 date_limit,
9499 min_normal,
9500 max_denormal,
9501 min_denormal,
9502 0,
9503 -0,
9504 -min_denormal,
9505 -max_denormal,
9506 -min_normal,
9507 -date_limit,
9508 -date_limit - 1,
9509 -max_normal,
9510 -infinity,
9511 -qnan,
9512 -snan
9513 };
9514 int num_test_values = 20;
9515
9516 for (int i = 0; i < num_test_values; i++) {
9517 double test_value = test_values[i];
9518
9519 // Check that Number::New preserves non-NaNs and quiets SNaNs.
9520 v8::Handle<v8::Value> number = v8::Number::New(test_value);
9521 double stored_number = number->NumberValue();
9522 if (!IsNaN(test_value)) {
9523 CHECK_EQ(test_value, stored_number);
9524 } else {
9525 uint64_t stored_bits = DoubleToBits(stored_number);
9526 // Check if quiet nan (bits 51..62 all set).
9527 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
9528 }
9529
9530 // Check that Date::New preserves non-NaNs in the date range and
9531 // quiets SNaNs.
9532 v8::Handle<v8::Value> date = v8::Date::New(test_value);
9533 double expected_stored_date = DoubleToDateTime(test_value);
9534 double stored_date = date->NumberValue();
9535 if (!IsNaN(expected_stored_date)) {
9536 CHECK_EQ(expected_stored_date, stored_date);
9537 } else {
9538 uint64_t stored_bits = DoubleToBits(stored_date);
9539 // Check if quiet nan (bits 51..62 all set).
9540 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
9541 }
9542 }
9543}
9544
9545
9546static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
9547 v8::HandleScope scope;
9548 v8::TryCatch tc;
9549 v8::Handle<v8::String> str = args[0]->ToString();
9550 if (tc.HasCaught())
9551 return tc.ReThrow();
9552 return v8::Undefined();
9553}
9554
9555
9556// Test that an exception can be propagated down through a spaghetti
9557// stack using ReThrow.
9558THREADED_TEST(SpaghettiStackReThrow) {
9559 v8::HandleScope scope;
9560 LocalContext context;
9561 context->Global()->Set(
9562 v8::String::New("s"),
9563 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
9564 v8::TryCatch try_catch;
9565 CompileRun(
9566 "var i = 0;"
9567 "var o = {"
9568 " toString: function () {"
9569 " if (i == 10) {"
9570 " throw 'Hey!';"
9571 " } else {"
9572 " i++;"
9573 " return s(o);"
9574 " }"
9575 " }"
9576 "};"
9577 "s(o);");
9578 CHECK(try_catch.HasCaught());
9579 v8::String::Utf8Value value(try_catch.Exception());
9580 CHECK_EQ(0, strcmp(*value, "Hey!"));
9581}
9582
9583
Steve Blockd0582a62009-12-15 09:54:21 +00009584TEST(Regress528) {
9585 v8::V8::Initialize();
9586
9587 v8::HandleScope scope;
9588 v8::Persistent<Context> context;
9589 v8::Persistent<Context> other_context;
9590 int gc_count;
9591
9592 // Create a context used to keep the code from aging in the compilation
9593 // cache.
9594 other_context = Context::New();
9595
9596 // Context-dependent context data creates reference from the compilation
9597 // cache to the global object.
9598 const char* source_simple = "1";
9599 context = Context::New();
9600 {
9601 v8::HandleScope scope;
9602
9603 context->Enter();
9604 Local<v8::String> obj = v8::String::New("");
9605 context->SetData(obj);
9606 CompileRun(source_simple);
9607 context->Exit();
9608 }
9609 context.Dispose();
9610 for (gc_count = 1; gc_count < 10; gc_count++) {
9611 other_context->Enter();
9612 CompileRun(source_simple);
9613 other_context->Exit();
9614 v8::internal::Heap::CollectAllGarbage(false);
9615 if (GetGlobalObjectsCount() == 1) break;
9616 }
9617 CHECK_GE(2, gc_count);
9618 CHECK_EQ(1, GetGlobalObjectsCount());
9619
9620 // Eval in a function creates reference from the compilation cache to the
9621 // global object.
9622 const char* source_eval = "function f(){eval('1')}; f()";
9623 context = Context::New();
9624 {
9625 v8::HandleScope scope;
9626
9627 context->Enter();
9628 CompileRun(source_eval);
9629 context->Exit();
9630 }
9631 context.Dispose();
9632 for (gc_count = 1; gc_count < 10; gc_count++) {
9633 other_context->Enter();
9634 CompileRun(source_eval);
9635 other_context->Exit();
9636 v8::internal::Heap::CollectAllGarbage(false);
9637 if (GetGlobalObjectsCount() == 1) break;
9638 }
9639 CHECK_GE(2, gc_count);
9640 CHECK_EQ(1, GetGlobalObjectsCount());
9641
9642 // Looking up the line number for an exception creates reference from the
9643 // compilation cache to the global object.
9644 const char* source_exception = "function f(){throw 1;} f()";
9645 context = Context::New();
9646 {
9647 v8::HandleScope scope;
9648
9649 context->Enter();
9650 v8::TryCatch try_catch;
9651 CompileRun(source_exception);
9652 CHECK(try_catch.HasCaught());
9653 v8::Handle<v8::Message> message = try_catch.Message();
9654 CHECK(!message.IsEmpty());
9655 CHECK_EQ(1, message->GetLineNumber());
9656 context->Exit();
9657 }
9658 context.Dispose();
9659 for (gc_count = 1; gc_count < 10; gc_count++) {
9660 other_context->Enter();
9661 CompileRun(source_exception);
9662 other_context->Exit();
9663 v8::internal::Heap::CollectAllGarbage(false);
9664 if (GetGlobalObjectsCount() == 1) break;
9665 }
9666 CHECK_GE(2, gc_count);
9667 CHECK_EQ(1, GetGlobalObjectsCount());
9668
9669 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +00009670}
Andrei Popescu402d9372010-02-26 13:31:12 +00009671
9672
9673THREADED_TEST(ScriptOrigin) {
9674 v8::HandleScope scope;
9675 LocalContext env;
9676 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
9677 v8::Handle<v8::String> script = v8::String::New(
9678 "function f() {}\n\nfunction g() {}");
9679 v8::Script::Compile(script, &origin)->Run();
9680 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
9681 env->Global()->Get(v8::String::New("f")));
9682 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
9683 env->Global()->Get(v8::String::New("g")));
9684
9685 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
9686 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
9687 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
9688
9689 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
9690 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
9691 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
9692}
9693
9694
9695THREADED_TEST(ScriptLineNumber) {
9696 v8::HandleScope scope;
9697 LocalContext env;
9698 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
9699 v8::Handle<v8::String> script = v8::String::New(
9700 "function f() {}\n\nfunction g() {}");
9701 v8::Script::Compile(script, &origin)->Run();
9702 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
9703 env->Global()->Get(v8::String::New("f")));
9704 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
9705 env->Global()->Get(v8::String::New("g")));
9706 CHECK_EQ(0, f->GetScriptLineNumber());
9707 CHECK_EQ(2, g->GetScriptLineNumber());
9708}
9709
9710
9711static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
9712 const AccessorInfo& info) {
9713 return v8_num(42);
9714}
9715
9716
9717static void SetterWhichSetsYOnThisTo23(Local<String> name,
9718 Local<Value> value,
9719 const AccessorInfo& info) {
9720 info.This()->Set(v8_str("y"), v8_num(23));
9721}
9722
9723
9724THREADED_TEST(SetterOnConstructorPrototype) {
9725 v8::HandleScope scope;
9726 Local<ObjectTemplate> templ = ObjectTemplate::New();
9727 templ->SetAccessor(v8_str("x"),
9728 GetterWhichReturns42,
9729 SetterWhichSetsYOnThisTo23);
9730 LocalContext context;
9731 context->Global()->Set(v8_str("P"), templ->NewInstance());
9732 CompileRun("function C1() {"
9733 " this.x = 23;"
9734 "};"
9735 "C1.prototype = P;"
9736 "function C2() {"
9737 " this.x = 23"
9738 "};"
9739 "C2.prototype = { };"
9740 "C2.prototype.__proto__ = P;");
9741
9742 v8::Local<v8::Script> script;
9743 script = v8::Script::Compile(v8_str("new C1();"));
9744 for (int i = 0; i < 10; i++) {
9745 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
9746 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
9747 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
9748 }
9749
9750 script = v8::Script::Compile(v8_str("new C2();"));
9751 for (int i = 0; i < 10; i++) {
9752 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
9753 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
9754 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
9755 }
9756}
9757
9758
9759static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
9760 Local<String> name, const AccessorInfo& info) {
9761 return v8_num(42);
9762}
9763
9764
9765static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
9766 Local<String> name, Local<Value> value, const AccessorInfo& info) {
9767 if (name->Equals(v8_str("x"))) {
9768 info.This()->Set(v8_str("y"), v8_num(23));
9769 }
9770 return v8::Handle<Value>();
9771}
9772
9773
9774THREADED_TEST(InterceptorOnConstructorPrototype) {
9775 v8::HandleScope scope;
9776 Local<ObjectTemplate> templ = ObjectTemplate::New();
9777 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
9778 NamedPropertySetterWhichSetsYOnThisTo23);
9779 LocalContext context;
9780 context->Global()->Set(v8_str("P"), templ->NewInstance());
9781 CompileRun("function C1() {"
9782 " this.x = 23;"
9783 "};"
9784 "C1.prototype = P;"
9785 "function C2() {"
9786 " this.x = 23"
9787 "};"
9788 "C2.prototype = { };"
9789 "C2.prototype.__proto__ = P;");
9790
9791 v8::Local<v8::Script> script;
9792 script = v8::Script::Compile(v8_str("new C1();"));
9793 for (int i = 0; i < 10; i++) {
9794 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
9795 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
9796 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
9797 }
9798
9799 script = v8::Script::Compile(v8_str("new C2();"));
9800 for (int i = 0; i < 10; i++) {
9801 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
9802 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
9803 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
9804 }
9805}