blob: c426db40b5c7271734470dd6cc23671553568c1f [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
Leon Clarkef7060e22010-06-03 12:02:55 +010079static void ExpectTrue(const char* code) {
80 ExpectBoolean(code, true);
81}
82
83
Leon Clarked91b9f72010-01-27 17:25:45 +000084static void ExpectObject(const char* code, Local<Value> expected) {
85 Local<Value> result = CompileRun(code);
86 CHECK(result->Equals(expected));
87}
88
89
Steve Blocka7e24c12009-10-30 11:49:00 +000090static int signature_callback_count;
91static v8::Handle<Value> IncrementingSignatureCallback(
92 const v8::Arguments& args) {
93 ApiTestFuzzer::Fuzz();
94 signature_callback_count++;
95 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
96 for (int i = 0; i < args.Length(); i++)
97 result->Set(v8::Integer::New(i), args[i]);
98 return result;
99}
100
101
102static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
103 ApiTestFuzzer::Fuzz();
104 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
105 for (int i = 0; i < args.Length(); i++) {
106 result->Set(v8::Integer::New(i), args[i]);
107 }
108 return result;
109}
110
111
112THREADED_TEST(Handles) {
113 v8::HandleScope scope;
114 Local<Context> local_env;
115 {
116 LocalContext env;
117 local_env = env.local();
118 }
119
120 // Local context should still be live.
121 CHECK(!local_env.IsEmpty());
122 local_env->Enter();
123
124 v8::Handle<v8::Primitive> undef = v8::Undefined();
125 CHECK(!undef.IsEmpty());
126 CHECK(undef->IsUndefined());
127
128 const char* c_source = "1 + 2 + 3";
129 Local<String> source = String::New(c_source);
130 Local<Script> script = Script::Compile(source);
131 CHECK_EQ(6, script->Run()->Int32Value());
132
133 local_env->Exit();
134}
135
136
Steve Blocka7e24c12009-10-30 11:49:00 +0000137THREADED_TEST(ReceiverSignature) {
138 v8::HandleScope scope;
139 LocalContext env;
140 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
141 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
142 fun->PrototypeTemplate()->Set(
143 v8_str("m"),
144 v8::FunctionTemplate::New(IncrementingSignatureCallback,
145 v8::Handle<Value>(),
146 sig));
147 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
148 signature_callback_count = 0;
149 CompileRun(
150 "var o = new Fun();"
151 "o.m();");
152 CHECK_EQ(1, signature_callback_count);
153 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
154 sub_fun->Inherit(fun);
155 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
156 CompileRun(
157 "var o = new SubFun();"
158 "o.m();");
159 CHECK_EQ(2, signature_callback_count);
160
161 v8::TryCatch try_catch;
162 CompileRun(
163 "var o = { };"
164 "o.m = Fun.prototype.m;"
165 "o.m();");
166 CHECK_EQ(2, signature_callback_count);
167 CHECK(try_catch.HasCaught());
168 try_catch.Reset();
169 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
170 sub_fun->Inherit(fun);
171 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
172 CompileRun(
173 "var o = new UnrelFun();"
174 "o.m = Fun.prototype.m;"
175 "o.m();");
176 CHECK_EQ(2, signature_callback_count);
177 CHECK(try_catch.HasCaught());
178}
179
180
181
182
183THREADED_TEST(ArgumentSignature) {
184 v8::HandleScope scope;
185 LocalContext env;
186 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
187 cons->SetClassName(v8_str("Cons"));
188 v8::Handle<v8::Signature> sig =
189 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
190 v8::Handle<v8::FunctionTemplate> fun =
191 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
192 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
193 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
194
195 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
196 CHECK(value1->IsTrue());
197
198 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
199 CHECK(value2->IsTrue());
200
201 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
202 CHECK(value3->IsTrue());
203
204 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
205 cons1->SetClassName(v8_str("Cons1"));
206 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
207 cons2->SetClassName(v8_str("Cons2"));
208 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
209 cons3->SetClassName(v8_str("Cons3"));
210
211 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
212 v8::Handle<v8::Signature> wsig =
213 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
214 v8::Handle<v8::FunctionTemplate> fun2 =
215 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
216
217 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
218 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
219 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
220 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
221 v8::Handle<Value> value4 = CompileRun(
222 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
223 "'[object Cons1],[object Cons2],[object Cons3]'");
224 CHECK(value4->IsTrue());
225
226 v8::Handle<Value> value5 = CompileRun(
227 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
228 CHECK(value5->IsTrue());
229
230 v8::Handle<Value> value6 = CompileRun(
231 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
232 CHECK(value6->IsTrue());
233
234 v8::Handle<Value> value7 = CompileRun(
235 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
236 "'[object Cons1],[object Cons2],[object Cons3],d';");
237 CHECK(value7->IsTrue());
238
239 v8::Handle<Value> value8 = CompileRun(
240 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
241 CHECK(value8->IsTrue());
242}
243
244
245THREADED_TEST(HulIgennem) {
246 v8::HandleScope scope;
247 LocalContext env;
248 v8::Handle<v8::Primitive> undef = v8::Undefined();
249 Local<String> undef_str = undef->ToString();
250 char* value = i::NewArray<char>(undef_str->Length() + 1);
251 undef_str->WriteAscii(value);
252 CHECK_EQ(0, strcmp(value, "undefined"));
253 i::DeleteArray(value);
254}
255
256
257THREADED_TEST(Access) {
258 v8::HandleScope scope;
259 LocalContext env;
260 Local<v8::Object> obj = v8::Object::New();
261 Local<Value> foo_before = obj->Get(v8_str("foo"));
262 CHECK(foo_before->IsUndefined());
263 Local<String> bar_str = v8_str("bar");
264 obj->Set(v8_str("foo"), bar_str);
265 Local<Value> foo_after = obj->Get(v8_str("foo"));
266 CHECK(!foo_after->IsUndefined());
267 CHECK(foo_after->IsString());
268 CHECK_EQ(bar_str, foo_after);
269}
270
271
Steve Block6ded16b2010-05-10 14:33:55 +0100272THREADED_TEST(AccessElement) {
273 v8::HandleScope scope;
274 LocalContext env;
275 Local<v8::Object> obj = v8::Object::New();
276 Local<Value> before = obj->Get(1);
277 CHECK(before->IsUndefined());
278 Local<String> bar_str = v8_str("bar");
279 obj->Set(1, bar_str);
280 Local<Value> after = obj->Get(1);
281 CHECK(!after->IsUndefined());
282 CHECK(after->IsString());
283 CHECK_EQ(bar_str, after);
284
285 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
286 CHECK_EQ(v8_str("a"), value->Get(0));
287 CHECK_EQ(v8_str("b"), value->Get(1));
288}
289
290
Steve Blocka7e24c12009-10-30 11:49:00 +0000291THREADED_TEST(Script) {
292 v8::HandleScope scope;
293 LocalContext env;
294 const char* c_source = "1 + 2 + 3";
295 Local<String> source = String::New(c_source);
296 Local<Script> script = Script::Compile(source);
297 CHECK_EQ(6, script->Run()->Int32Value());
298}
299
300
301static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000302 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000303 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000304 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000305 return converted;
306}
307
308
309class TestResource: public String::ExternalStringResource {
310 public:
311 static int dispose_count;
312
313 explicit TestResource(uint16_t* data)
314 : data_(data), length_(0) {
315 while (data[length_]) ++length_;
316 }
317
318 ~TestResource() {
319 i::DeleteArray(data_);
320 ++dispose_count;
321 }
322
323 const uint16_t* data() const {
324 return data_;
325 }
326
327 size_t length() const {
328 return length_;
329 }
330 private:
331 uint16_t* data_;
332 size_t length_;
333};
334
335
336int TestResource::dispose_count = 0;
337
338
339class TestAsciiResource: public String::ExternalAsciiStringResource {
340 public:
341 static int dispose_count;
342
343 explicit TestAsciiResource(const char* data)
344 : data_(data),
345 length_(strlen(data)) { }
346
347 ~TestAsciiResource() {
348 i::DeleteArray(data_);
349 ++dispose_count;
350 }
351
352 const char* data() const {
353 return data_;
354 }
355
356 size_t length() const {
357 return length_;
358 }
359 private:
360 const char* data_;
361 size_t length_;
362};
363
364
365int TestAsciiResource::dispose_count = 0;
366
367
368THREADED_TEST(ScriptUsingStringResource) {
369 TestResource::dispose_count = 0;
370 const char* c_source = "1 + 2 * 3";
371 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
372 {
373 v8::HandleScope scope;
374 LocalContext env;
375 TestResource* resource = new TestResource(two_byte_source);
376 Local<String> source = String::NewExternal(resource);
377 Local<Script> script = Script::Compile(source);
378 Local<Value> value = script->Run();
379 CHECK(value->IsNumber());
380 CHECK_EQ(7, value->Int32Value());
381 CHECK(source->IsExternal());
382 CHECK_EQ(resource,
383 static_cast<TestResource*>(source->GetExternalStringResource()));
384 v8::internal::Heap::CollectAllGarbage(false);
385 CHECK_EQ(0, TestResource::dispose_count);
386 }
387 v8::internal::CompilationCache::Clear();
388 v8::internal::Heap::CollectAllGarbage(false);
389 CHECK_EQ(1, TestResource::dispose_count);
390}
391
392
393THREADED_TEST(ScriptUsingAsciiStringResource) {
394 TestAsciiResource::dispose_count = 0;
395 const char* c_source = "1 + 2 * 3";
396 {
397 v8::HandleScope scope;
398 LocalContext env;
399 Local<String> source =
400 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
401 Local<Script> script = Script::Compile(source);
402 Local<Value> value = script->Run();
403 CHECK(value->IsNumber());
404 CHECK_EQ(7, value->Int32Value());
405 v8::internal::Heap::CollectAllGarbage(false);
406 CHECK_EQ(0, TestAsciiResource::dispose_count);
407 }
408 v8::internal::CompilationCache::Clear();
409 v8::internal::Heap::CollectAllGarbage(false);
410 CHECK_EQ(1, TestAsciiResource::dispose_count);
411}
412
413
414THREADED_TEST(ScriptMakingExternalString) {
415 TestResource::dispose_count = 0;
416 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
417 {
418 v8::HandleScope scope;
419 LocalContext env;
420 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000421 // Trigger GCs so that the newly allocated string moves to old gen.
422 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
423 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000424 bool success = source->MakeExternal(new TestResource(two_byte_source));
425 CHECK(success);
426 Local<Script> script = Script::Compile(source);
427 Local<Value> value = script->Run();
428 CHECK(value->IsNumber());
429 CHECK_EQ(7, value->Int32Value());
430 v8::internal::Heap::CollectAllGarbage(false);
431 CHECK_EQ(0, TestResource::dispose_count);
432 }
433 v8::internal::CompilationCache::Clear();
434 v8::internal::Heap::CollectAllGarbage(false);
435 CHECK_EQ(1, TestResource::dispose_count);
436}
437
438
439THREADED_TEST(ScriptMakingExternalAsciiString) {
440 TestAsciiResource::dispose_count = 0;
441 const char* c_source = "1 + 2 * 3";
442 {
443 v8::HandleScope scope;
444 LocalContext env;
445 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000446 // Trigger GCs so that the newly allocated string moves to old gen.
447 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
448 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000449 bool success = source->MakeExternal(
450 new TestAsciiResource(i::StrDup(c_source)));
451 CHECK(success);
452 Local<Script> script = Script::Compile(source);
453 Local<Value> value = script->Run();
454 CHECK(value->IsNumber());
455 CHECK_EQ(7, value->Int32Value());
456 v8::internal::Heap::CollectAllGarbage(false);
457 CHECK_EQ(0, TestAsciiResource::dispose_count);
458 }
459 v8::internal::CompilationCache::Clear();
460 v8::internal::Heap::CollectAllGarbage(false);
461 CHECK_EQ(1, TestAsciiResource::dispose_count);
462}
463
464
Andrei Popescu402d9372010-02-26 13:31:12 +0000465TEST(MakingExternalStringConditions) {
466 v8::HandleScope scope;
467 LocalContext env;
468
469 // Free some space in the new space so that we can check freshness.
470 i::Heap::CollectGarbage(0, i::NEW_SPACE);
471 i::Heap::CollectGarbage(0, i::NEW_SPACE);
472
473 Local<String> small_string = String::New(AsciiToTwoByteString("small"));
474 // We should refuse to externalize newly created small string.
475 CHECK(!small_string->CanMakeExternal());
476 // Trigger GCs so that the newly allocated string moves to old gen.
477 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
478 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
479 // Old space strings should be accepted.
480 CHECK(small_string->CanMakeExternal());
481
482 small_string = String::New(AsciiToTwoByteString("small 2"));
483 // We should refuse externalizing newly created small string.
484 CHECK(!small_string->CanMakeExternal());
485 for (int i = 0; i < 100; i++) {
486 String::Value value(small_string);
487 }
488 // Frequently used strings should be accepted.
489 CHECK(small_string->CanMakeExternal());
490
491 const int buf_size = 10 * 1024;
492 char* buf = i::NewArray<char>(buf_size);
493 memset(buf, 'a', buf_size);
494 buf[buf_size - 1] = '\0';
495 Local<String> large_string = String::New(AsciiToTwoByteString(buf));
496 i::DeleteArray(buf);
497 // Large strings should be immediately accepted.
498 CHECK(large_string->CanMakeExternal());
499}
500
501
502TEST(MakingExternalAsciiStringConditions) {
503 v8::HandleScope scope;
504 LocalContext env;
505
506 // Free some space in the new space so that we can check freshness.
507 i::Heap::CollectGarbage(0, i::NEW_SPACE);
508 i::Heap::CollectGarbage(0, i::NEW_SPACE);
509
510 Local<String> small_string = String::New("small");
511 // We should refuse to externalize newly created small string.
512 CHECK(!small_string->CanMakeExternal());
513 // Trigger GCs so that the newly allocated string moves to old gen.
514 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
515 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
516 // Old space strings should be accepted.
517 CHECK(small_string->CanMakeExternal());
518
519 small_string = String::New("small 2");
520 // We should refuse externalizing newly created small string.
521 CHECK(!small_string->CanMakeExternal());
522 for (int i = 0; i < 100; i++) {
523 String::Value value(small_string);
524 }
525 // Frequently used strings should be accepted.
526 CHECK(small_string->CanMakeExternal());
527
528 const int buf_size = 10 * 1024;
529 char* buf = i::NewArray<char>(buf_size);
530 memset(buf, 'a', buf_size);
531 buf[buf_size - 1] = '\0';
532 Local<String> large_string = String::New(buf);
533 i::DeleteArray(buf);
534 // Large strings should be immediately accepted.
535 CHECK(large_string->CanMakeExternal());
536}
537
538
Steve Blocka7e24c12009-10-30 11:49:00 +0000539THREADED_TEST(UsingExternalString) {
540 {
541 v8::HandleScope scope;
542 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
543 Local<String> string =
544 String::NewExternal(new TestResource(two_byte_string));
545 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
546 // Trigger GCs so that the newly allocated string moves to old gen.
547 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
548 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
549 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
550 CHECK(isymbol->IsSymbol());
551 }
552 i::Heap::CollectAllGarbage(false);
553 i::Heap::CollectAllGarbage(false);
554}
555
556
557THREADED_TEST(UsingExternalAsciiString) {
558 {
559 v8::HandleScope scope;
560 const char* one_byte_string = "test string";
561 Local<String> string = String::NewExternal(
562 new TestAsciiResource(i::StrDup(one_byte_string)));
563 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
564 // Trigger GCs so that the newly allocated string moves to old gen.
565 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
566 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
567 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
568 CHECK(isymbol->IsSymbol());
569 }
570 i::Heap::CollectAllGarbage(false);
571 i::Heap::CollectAllGarbage(false);
572}
573
574
Leon Clarkee46be812010-01-19 14:06:41 +0000575THREADED_TEST(ScavengeExternalString) {
576 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100577 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000578 {
579 v8::HandleScope scope;
580 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
581 Local<String> string =
582 String::NewExternal(new TestResource(two_byte_string));
583 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
584 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100585 in_new_space = i::Heap::InNewSpace(*istring);
586 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000587 CHECK_EQ(0, TestResource::dispose_count);
588 }
Steve Block6ded16b2010-05-10 14:33:55 +0100589 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000590 CHECK_EQ(1, TestResource::dispose_count);
591}
592
593
594THREADED_TEST(ScavengeExternalAsciiString) {
595 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100596 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000597 {
598 v8::HandleScope scope;
599 const char* one_byte_string = "test string";
600 Local<String> string = String::NewExternal(
601 new TestAsciiResource(i::StrDup(one_byte_string)));
602 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
603 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100604 in_new_space = i::Heap::InNewSpace(*istring);
605 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000606 CHECK_EQ(0, TestAsciiResource::dispose_count);
607 }
Steve Block6ded16b2010-05-10 14:33:55 +0100608 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000609 CHECK_EQ(1, TestAsciiResource::dispose_count);
610}
611
612
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100613class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
614 public:
615 static int dispose_calls;
616
617 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
618 : TestAsciiResource(data),
619 dispose_(dispose) { }
620
621 void Dispose() {
622 ++dispose_calls;
623 if (dispose_) delete this;
624 }
625 private:
626 bool dispose_;
627};
628
629
630int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
631
632
633TEST(ExternalStringWithDisposeHandling) {
634 const char* c_source = "1 + 2 * 3";
635
636 // Use a stack allocated external string resource allocated object.
637 TestAsciiResource::dispose_count = 0;
638 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
639 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
640 {
641 v8::HandleScope scope;
642 LocalContext env;
643 Local<String> source = String::NewExternal(&res_stack);
644 Local<Script> script = Script::Compile(source);
645 Local<Value> value = script->Run();
646 CHECK(value->IsNumber());
647 CHECK_EQ(7, value->Int32Value());
648 v8::internal::Heap::CollectAllGarbage(false);
649 CHECK_EQ(0, TestAsciiResource::dispose_count);
650 }
651 v8::internal::CompilationCache::Clear();
652 v8::internal::Heap::CollectAllGarbage(false);
653 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
654 CHECK_EQ(0, TestAsciiResource::dispose_count);
655
656 // Use a heap allocated external string resource allocated object.
657 TestAsciiResource::dispose_count = 0;
658 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
659 TestAsciiResource* res_heap =
660 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
661 {
662 v8::HandleScope scope;
663 LocalContext env;
664 Local<String> source = String::NewExternal(res_heap);
665 Local<Script> script = Script::Compile(source);
666 Local<Value> value = script->Run();
667 CHECK(value->IsNumber());
668 CHECK_EQ(7, value->Int32Value());
669 v8::internal::Heap::CollectAllGarbage(false);
670 CHECK_EQ(0, TestAsciiResource::dispose_count);
671 }
672 v8::internal::CompilationCache::Clear();
673 v8::internal::Heap::CollectAllGarbage(false);
674 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
675 CHECK_EQ(1, TestAsciiResource::dispose_count);
676}
677
678
Steve Block3ce2e202009-11-05 08:53:23 +0000679THREADED_TEST(StringConcat) {
680 {
681 v8::HandleScope scope;
682 LocalContext env;
683 const char* one_byte_string_1 = "function a_times_t";
684 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
685 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
686 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
687 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
688 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
689 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
690 Local<String> left = v8_str(one_byte_string_1);
691 Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1));
692 Local<String> source = String::Concat(left, right);
693 right = String::NewExternal(
694 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
695 source = String::Concat(source, right);
696 right = String::NewExternal(
697 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
698 source = String::Concat(source, right);
699 right = v8_str(one_byte_string_2);
700 source = String::Concat(source, right);
701 right = String::New(AsciiToTwoByteString(two_byte_string_2));
702 source = String::Concat(source, right);
703 right = String::NewExternal(
704 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
705 source = String::Concat(source, right);
706 Local<Script> script = Script::Compile(source);
707 Local<Value> value = script->Run();
708 CHECK(value->IsNumber());
709 CHECK_EQ(68, value->Int32Value());
710 }
711 v8::internal::CompilationCache::Clear();
712 i::Heap::CollectAllGarbage(false);
713 i::Heap::CollectAllGarbage(false);
714}
715
716
Steve Blocka7e24c12009-10-30 11:49:00 +0000717THREADED_TEST(GlobalProperties) {
718 v8::HandleScope scope;
719 LocalContext env;
720 v8::Handle<v8::Object> global = env->Global();
721 global->Set(v8_str("pi"), v8_num(3.1415926));
722 Local<Value> pi = global->Get(v8_str("pi"));
723 CHECK_EQ(3.1415926, pi->NumberValue());
724}
725
726
727static v8::Handle<Value> handle_call(const v8::Arguments& args) {
728 ApiTestFuzzer::Fuzz();
729 return v8_num(102);
730}
731
732
733static v8::Handle<Value> construct_call(const v8::Arguments& args) {
734 ApiTestFuzzer::Fuzz();
735 args.This()->Set(v8_str("x"), v8_num(1));
736 args.This()->Set(v8_str("y"), v8_num(2));
737 return args.This();
738}
739
740THREADED_TEST(FunctionTemplate) {
741 v8::HandleScope scope;
742 LocalContext env;
743 {
744 Local<v8::FunctionTemplate> fun_templ =
745 v8::FunctionTemplate::New(handle_call);
746 Local<Function> fun = fun_templ->GetFunction();
747 env->Global()->Set(v8_str("obj"), fun);
748 Local<Script> script = v8_compile("obj()");
749 CHECK_EQ(102, script->Run()->Int32Value());
750 }
751 // Use SetCallHandler to initialize a function template, should work like the
752 // previous one.
753 {
754 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
755 fun_templ->SetCallHandler(handle_call);
756 Local<Function> fun = fun_templ->GetFunction();
757 env->Global()->Set(v8_str("obj"), fun);
758 Local<Script> script = v8_compile("obj()");
759 CHECK_EQ(102, script->Run()->Int32Value());
760 }
761 // Test constructor calls.
762 {
763 Local<v8::FunctionTemplate> fun_templ =
764 v8::FunctionTemplate::New(construct_call);
765 fun_templ->SetClassName(v8_str("funky"));
766 Local<Function> fun = fun_templ->GetFunction();
767 env->Global()->Set(v8_str("obj"), fun);
768 Local<Script> script = v8_compile("var s = new obj(); s.x");
769 CHECK_EQ(1, script->Run()->Int32Value());
770
771 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
772 CHECK_EQ(v8_str("[object funky]"), result);
773 }
774}
775
776
777THREADED_TEST(FindInstanceInPrototypeChain) {
778 v8::HandleScope scope;
779 LocalContext env;
780
781 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
782 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
783 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
784 derived->Inherit(base);
785
786 Local<v8::Function> base_function = base->GetFunction();
787 Local<v8::Function> derived_function = derived->GetFunction();
788 Local<v8::Function> other_function = other->GetFunction();
789
790 Local<v8::Object> base_instance = base_function->NewInstance();
791 Local<v8::Object> derived_instance = derived_function->NewInstance();
792 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
793 Local<v8::Object> other_instance = other_function->NewInstance();
794 derived_instance2->Set(v8_str("__proto__"), derived_instance);
795 other_instance->Set(v8_str("__proto__"), derived_instance2);
796
797 // base_instance is only an instance of base.
798 CHECK_EQ(base_instance,
799 base_instance->FindInstanceInPrototypeChain(base));
800 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
801 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
802
803 // derived_instance is an instance of base and derived.
804 CHECK_EQ(derived_instance,
805 derived_instance->FindInstanceInPrototypeChain(base));
806 CHECK_EQ(derived_instance,
807 derived_instance->FindInstanceInPrototypeChain(derived));
808 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
809
810 // other_instance is an instance of other and its immediate
811 // prototype derived_instance2 is an instance of base and derived.
812 // Note, derived_instance is an instance of base and derived too,
813 // but it comes after derived_instance2 in the prototype chain of
814 // other_instance.
815 CHECK_EQ(derived_instance2,
816 other_instance->FindInstanceInPrototypeChain(base));
817 CHECK_EQ(derived_instance2,
818 other_instance->FindInstanceInPrototypeChain(derived));
819 CHECK_EQ(other_instance,
820 other_instance->FindInstanceInPrototypeChain(other));
821}
822
823
Steve Block3ce2e202009-11-05 08:53:23 +0000824THREADED_TEST(TinyInteger) {
825 v8::HandleScope scope;
826 LocalContext env;
827 int32_t value = 239;
828 Local<v8::Integer> value_obj = v8::Integer::New(value);
829 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
830}
831
832
833THREADED_TEST(BigSmiInteger) {
834 v8::HandleScope scope;
835 LocalContext env;
836 int32_t value = i::Smi::kMaxValue;
837 // We cannot add one to a Smi::kMaxValue without wrapping.
838 if (i::kSmiValueSize < 32) {
839 CHECK(i::Smi::IsValid(value));
840 CHECK(!i::Smi::IsValid(value + 1));
841 Local<v8::Integer> value_obj = v8::Integer::New(value);
842 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
843 }
844}
845
846
847THREADED_TEST(BigInteger) {
848 v8::HandleScope scope;
849 LocalContext env;
850 // We cannot add one to a Smi::kMaxValue without wrapping.
851 if (i::kSmiValueSize < 32) {
852 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
853 // The code will not be run in that case, due to the "if" guard.
854 int32_t value =
855 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
856 CHECK(value > i::Smi::kMaxValue);
857 CHECK(!i::Smi::IsValid(value));
858 Local<v8::Integer> value_obj = v8::Integer::New(value);
859 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
860 }
861}
862
863
864THREADED_TEST(TinyUnsignedInteger) {
865 v8::HandleScope scope;
866 LocalContext env;
867 uint32_t value = 239;
868 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
869 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
870}
871
872
873THREADED_TEST(BigUnsignedSmiInteger) {
874 v8::HandleScope scope;
875 LocalContext env;
876 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
877 CHECK(i::Smi::IsValid(value));
878 CHECK(!i::Smi::IsValid(value + 1));
879 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
880 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
881}
882
883
884THREADED_TEST(BigUnsignedInteger) {
885 v8::HandleScope scope;
886 LocalContext env;
887 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
888 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
889 CHECK(!i::Smi::IsValid(value));
890 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
891 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
892}
893
894
895THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
896 v8::HandleScope scope;
897 LocalContext env;
898 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
899 uint32_t value = INT32_MAX_AS_UINT + 1;
900 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
901 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
902 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
903}
904
905
Steve Blocka7e24c12009-10-30 11:49:00 +0000906THREADED_TEST(Number) {
907 v8::HandleScope scope;
908 LocalContext env;
909 double PI = 3.1415926;
910 Local<v8::Number> pi_obj = v8::Number::New(PI);
911 CHECK_EQ(PI, pi_obj->NumberValue());
912}
913
914
915THREADED_TEST(ToNumber) {
916 v8::HandleScope scope;
917 LocalContext env;
918 Local<String> str = v8_str("3.1415926");
919 CHECK_EQ(3.1415926, str->NumberValue());
920 v8::Handle<v8::Boolean> t = v8::True();
921 CHECK_EQ(1.0, t->NumberValue());
922 v8::Handle<v8::Boolean> f = v8::False();
923 CHECK_EQ(0.0, f->NumberValue());
924}
925
926
927THREADED_TEST(Date) {
928 v8::HandleScope scope;
929 LocalContext env;
930 double PI = 3.1415926;
931 Local<Value> date_obj = v8::Date::New(PI);
932 CHECK_EQ(3.0, date_obj->NumberValue());
933}
934
935
936THREADED_TEST(Boolean) {
937 v8::HandleScope scope;
938 LocalContext env;
939 v8::Handle<v8::Boolean> t = v8::True();
940 CHECK(t->Value());
941 v8::Handle<v8::Boolean> f = v8::False();
942 CHECK(!f->Value());
943 v8::Handle<v8::Primitive> u = v8::Undefined();
944 CHECK(!u->BooleanValue());
945 v8::Handle<v8::Primitive> n = v8::Null();
946 CHECK(!n->BooleanValue());
947 v8::Handle<String> str1 = v8_str("");
948 CHECK(!str1->BooleanValue());
949 v8::Handle<String> str2 = v8_str("x");
950 CHECK(str2->BooleanValue());
951 CHECK(!v8::Number::New(0)->BooleanValue());
952 CHECK(v8::Number::New(-1)->BooleanValue());
953 CHECK(v8::Number::New(1)->BooleanValue());
954 CHECK(v8::Number::New(42)->BooleanValue());
955 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
956}
957
958
959static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
960 ApiTestFuzzer::Fuzz();
961 return v8_num(13.4);
962}
963
964
965static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
966 ApiTestFuzzer::Fuzz();
967 return v8_num(876);
968}
969
970
971THREADED_TEST(GlobalPrototype) {
972 v8::HandleScope scope;
973 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
974 func_templ->PrototypeTemplate()->Set(
975 "dummy",
976 v8::FunctionTemplate::New(DummyCallHandler));
977 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
978 templ->Set("x", v8_num(200));
979 templ->SetAccessor(v8_str("m"), GetM);
980 LocalContext env(0, templ);
981 v8::Handle<v8::Object> obj = env->Global();
982 v8::Handle<Script> script = v8_compile("dummy()");
983 v8::Handle<Value> result = script->Run();
984 CHECK_EQ(13.4, result->NumberValue());
985 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
986 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
987}
988
989
Steve Blocka7e24c12009-10-30 11:49:00 +0000990THREADED_TEST(ObjectTemplate) {
991 v8::HandleScope scope;
992 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
993 templ1->Set("x", v8_num(10));
994 templ1->Set("y", v8_num(13));
995 LocalContext env;
996 Local<v8::Object> instance1 = templ1->NewInstance();
997 env->Global()->Set(v8_str("p"), instance1);
998 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
999 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1000 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1001 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1002 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1003 templ2->Set("a", v8_num(12));
1004 templ2->Set("b", templ1);
1005 Local<v8::Object> instance2 = templ2->NewInstance();
1006 env->Global()->Set(v8_str("q"), instance2);
1007 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1008 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1009 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1010 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1011}
1012
1013
1014static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1015 ApiTestFuzzer::Fuzz();
1016 return v8_num(17.2);
1017}
1018
1019
1020static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1021 ApiTestFuzzer::Fuzz();
1022 return v8_num(15.2);
1023}
1024
1025
1026THREADED_TEST(DescriptorInheritance) {
1027 v8::HandleScope scope;
1028 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1029 super->PrototypeTemplate()->Set("flabby",
1030 v8::FunctionTemplate::New(GetFlabby));
1031 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1032
1033 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1034
1035 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1036 base1->Inherit(super);
1037 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1038
1039 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1040 base2->Inherit(super);
1041 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1042
1043 LocalContext env;
1044
1045 env->Global()->Set(v8_str("s"), super->GetFunction());
1046 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1047 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1048
1049 // Checks right __proto__ chain.
1050 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1051 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1052
1053 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1054
1055 // Instance accessor should not be visible on function object or its prototype
1056 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1057 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1058 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1059
1060 env->Global()->Set(v8_str("obj"),
1061 base1->GetFunction()->NewInstance());
1062 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1063 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1064 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1065 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1066 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1067
1068 env->Global()->Set(v8_str("obj2"),
1069 base2->GetFunction()->NewInstance());
1070 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1071 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1072 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1073 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1074 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1075
1076 // base1 and base2 cannot cross reference to each's prototype
1077 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1078 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1079}
1080
1081
1082int echo_named_call_count;
1083
1084
1085static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1086 const AccessorInfo& info) {
1087 ApiTestFuzzer::Fuzz();
1088 CHECK_EQ(v8_str("data"), info.Data());
1089 echo_named_call_count++;
1090 return name;
1091}
1092
1093
1094THREADED_TEST(NamedPropertyHandlerGetter) {
1095 echo_named_call_count = 0;
1096 v8::HandleScope scope;
1097 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1098 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1099 0, 0, 0, 0,
1100 v8_str("data"));
1101 LocalContext env;
1102 env->Global()->Set(v8_str("obj"),
1103 templ->GetFunction()->NewInstance());
1104 CHECK_EQ(echo_named_call_count, 0);
1105 v8_compile("obj.x")->Run();
1106 CHECK_EQ(echo_named_call_count, 1);
1107 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1108 v8::Handle<Value> str = CompileRun(code);
1109 String::AsciiValue value(str);
1110 CHECK_EQ(*value, "oddlepoddle");
1111 // Check default behavior
1112 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1113 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1114 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1115}
1116
1117
1118int echo_indexed_call_count = 0;
1119
1120
1121static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1122 const AccessorInfo& info) {
1123 ApiTestFuzzer::Fuzz();
1124 CHECK_EQ(v8_num(637), info.Data());
1125 echo_indexed_call_count++;
1126 return v8_num(index);
1127}
1128
1129
1130THREADED_TEST(IndexedPropertyHandlerGetter) {
1131 v8::HandleScope scope;
1132 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1133 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1134 0, 0, 0, 0,
1135 v8_num(637));
1136 LocalContext env;
1137 env->Global()->Set(v8_str("obj"),
1138 templ->GetFunction()->NewInstance());
1139 Local<Script> script = v8_compile("obj[900]");
1140 CHECK_EQ(script->Run()->Int32Value(), 900);
1141}
1142
1143
1144v8::Handle<v8::Object> bottom;
1145
1146static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1147 uint32_t index,
1148 const AccessorInfo& info) {
1149 ApiTestFuzzer::Fuzz();
1150 CHECK(info.This()->Equals(bottom));
1151 return v8::Handle<Value>();
1152}
1153
1154static v8::Handle<Value> CheckThisNamedPropertyHandler(
1155 Local<String> name,
1156 const AccessorInfo& info) {
1157 ApiTestFuzzer::Fuzz();
1158 CHECK(info.This()->Equals(bottom));
1159 return v8::Handle<Value>();
1160}
1161
1162
1163v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1164 Local<Value> value,
1165 const AccessorInfo& info) {
1166 ApiTestFuzzer::Fuzz();
1167 CHECK(info.This()->Equals(bottom));
1168 return v8::Handle<Value>();
1169}
1170
1171
1172v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1173 Local<Value> value,
1174 const AccessorInfo& info) {
1175 ApiTestFuzzer::Fuzz();
1176 CHECK(info.This()->Equals(bottom));
1177 return v8::Handle<Value>();
1178}
1179
1180v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery(
1181 uint32_t index,
1182 const AccessorInfo& info) {
1183 ApiTestFuzzer::Fuzz();
1184 CHECK(info.This()->Equals(bottom));
1185 return v8::Handle<v8::Boolean>();
1186}
1187
1188
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001189v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001190 const AccessorInfo& info) {
1191 ApiTestFuzzer::Fuzz();
1192 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001193 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001194}
1195
1196
1197v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1198 uint32_t index,
1199 const AccessorInfo& info) {
1200 ApiTestFuzzer::Fuzz();
1201 CHECK(info.This()->Equals(bottom));
1202 return v8::Handle<v8::Boolean>();
1203}
1204
1205
1206v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1207 Local<String> property,
1208 const AccessorInfo& info) {
1209 ApiTestFuzzer::Fuzz();
1210 CHECK(info.This()->Equals(bottom));
1211 return v8::Handle<v8::Boolean>();
1212}
1213
1214
1215v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1216 const AccessorInfo& info) {
1217 ApiTestFuzzer::Fuzz();
1218 CHECK(info.This()->Equals(bottom));
1219 return v8::Handle<v8::Array>();
1220}
1221
1222
1223v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1224 const AccessorInfo& info) {
1225 ApiTestFuzzer::Fuzz();
1226 CHECK(info.This()->Equals(bottom));
1227 return v8::Handle<v8::Array>();
1228}
1229
1230
1231THREADED_TEST(PropertyHandlerInPrototype) {
1232 v8::HandleScope scope;
1233 LocalContext env;
1234
1235 // Set up a prototype chain with three interceptors.
1236 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1237 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1238 CheckThisIndexedPropertyHandler,
1239 CheckThisIndexedPropertySetter,
1240 CheckThisIndexedPropertyQuery,
1241 CheckThisIndexedPropertyDeleter,
1242 CheckThisIndexedPropertyEnumerator);
1243
1244 templ->InstanceTemplate()->SetNamedPropertyHandler(
1245 CheckThisNamedPropertyHandler,
1246 CheckThisNamedPropertySetter,
1247 CheckThisNamedPropertyQuery,
1248 CheckThisNamedPropertyDeleter,
1249 CheckThisNamedPropertyEnumerator);
1250
1251 bottom = templ->GetFunction()->NewInstance();
1252 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1253 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1254
1255 bottom->Set(v8_str("__proto__"), middle);
1256 middle->Set(v8_str("__proto__"), top);
1257 env->Global()->Set(v8_str("obj"), bottom);
1258
1259 // Indexed and named get.
1260 Script::Compile(v8_str("obj[0]"))->Run();
1261 Script::Compile(v8_str("obj.x"))->Run();
1262
1263 // Indexed and named set.
1264 Script::Compile(v8_str("obj[1] = 42"))->Run();
1265 Script::Compile(v8_str("obj.y = 42"))->Run();
1266
1267 // Indexed and named query.
1268 Script::Compile(v8_str("0 in obj"))->Run();
1269 Script::Compile(v8_str("'x' in obj"))->Run();
1270
1271 // Indexed and named deleter.
1272 Script::Compile(v8_str("delete obj[0]"))->Run();
1273 Script::Compile(v8_str("delete obj.x"))->Run();
1274
1275 // Enumerators.
1276 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1277}
1278
1279
1280static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1281 const AccessorInfo& info) {
1282 ApiTestFuzzer::Fuzz();
1283 if (v8_str("pre")->Equals(key)) {
1284 return v8_str("PrePropertyHandler: pre");
1285 }
1286 return v8::Handle<String>();
1287}
1288
1289
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001290static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1291 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001292 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001293 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001294 }
1295
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001296 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001297}
1298
1299
1300THREADED_TEST(PrePropertyHandler) {
1301 v8::HandleScope scope;
1302 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1303 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1304 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001305 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001306 LocalContext env(NULL, desc->InstanceTemplate());
1307 Script::Compile(v8_str(
1308 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1309 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1310 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1311 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1312 CHECK_EQ(v8_str("Object: on"), result_on);
1313 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1314 CHECK(result_post.IsEmpty());
1315}
1316
1317
1318THREADED_TEST(UndefinedIsNotEnumerable) {
1319 v8::HandleScope scope;
1320 LocalContext env;
1321 v8::Handle<Value> result = Script::Compile(v8_str(
1322 "this.propertyIsEnumerable(undefined)"))->Run();
1323 CHECK(result->IsFalse());
1324}
1325
1326
1327v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001328static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001329
1330
1331static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1332 ApiTestFuzzer::Fuzz();
1333 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1334 if (depth == kTargetRecursionDepth) return v8::Undefined();
1335 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1336 return call_recursively_script->Run();
1337}
1338
1339
1340static v8::Handle<Value> CallFunctionRecursivelyCall(
1341 const v8::Arguments& args) {
1342 ApiTestFuzzer::Fuzz();
1343 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1344 if (depth == kTargetRecursionDepth) {
1345 printf("[depth = %d]\n", depth);
1346 return v8::Undefined();
1347 }
1348 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1349 v8::Handle<Value> function =
1350 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001351 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001352}
1353
1354
1355THREADED_TEST(DeepCrossLanguageRecursion) {
1356 v8::HandleScope scope;
1357 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1358 global->Set(v8_str("callScriptRecursively"),
1359 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1360 global->Set(v8_str("callFunctionRecursively"),
1361 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1362 LocalContext env(NULL, global);
1363
1364 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1365 call_recursively_script = v8_compile("callScriptRecursively()");
1366 v8::Handle<Value> result = call_recursively_script->Run();
1367 call_recursively_script = v8::Handle<Script>();
1368
1369 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1370 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1371}
1372
1373
1374static v8::Handle<Value>
1375 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1376 ApiTestFuzzer::Fuzz();
1377 return v8::ThrowException(key);
1378}
1379
1380
1381static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1382 Local<Value>,
1383 const AccessorInfo&) {
1384 v8::ThrowException(key);
1385 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1386}
1387
1388
1389THREADED_TEST(CallbackExceptionRegression) {
1390 v8::HandleScope scope;
1391 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1392 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1393 ThrowingPropertyHandlerSet);
1394 LocalContext env;
1395 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1396 v8::Handle<Value> otto = Script::Compile(v8_str(
1397 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1398 CHECK_EQ(v8_str("otto"), otto);
1399 v8::Handle<Value> netto = Script::Compile(v8_str(
1400 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1401 CHECK_EQ(v8_str("netto"), netto);
1402}
1403
1404
Steve Blocka7e24c12009-10-30 11:49:00 +00001405THREADED_TEST(FunctionPrototype) {
1406 v8::HandleScope scope;
1407 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1408 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1409 LocalContext env;
1410 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1411 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1412 CHECK_EQ(script->Run()->Int32Value(), 321);
1413}
1414
1415
1416THREADED_TEST(InternalFields) {
1417 v8::HandleScope scope;
1418 LocalContext env;
1419
1420 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1421 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1422 instance_templ->SetInternalFieldCount(1);
1423 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1424 CHECK_EQ(1, obj->InternalFieldCount());
1425 CHECK(obj->GetInternalField(0)->IsUndefined());
1426 obj->SetInternalField(0, v8_num(17));
1427 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1428}
1429
1430
Steve Block6ded16b2010-05-10 14:33:55 +01001431THREADED_TEST(GlobalObjectInternalFields) {
1432 v8::HandleScope scope;
1433 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1434 global_template->SetInternalFieldCount(1);
1435 LocalContext env(NULL, global_template);
1436 v8::Handle<v8::Object> global_proxy = env->Global();
1437 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1438 CHECK_EQ(1, global->InternalFieldCount());
1439 CHECK(global->GetInternalField(0)->IsUndefined());
1440 global->SetInternalField(0, v8_num(17));
1441 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1442}
1443
1444
Steve Blocka7e24c12009-10-30 11:49:00 +00001445THREADED_TEST(InternalFieldsNativePointers) {
1446 v8::HandleScope scope;
1447 LocalContext env;
1448
1449 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1450 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1451 instance_templ->SetInternalFieldCount(1);
1452 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1453 CHECK_EQ(1, obj->InternalFieldCount());
1454 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1455
1456 char* data = new char[100];
1457
1458 void* aligned = data;
1459 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1460 void* unaligned = data + 1;
1461 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1462
1463 // Check reading and writing aligned pointers.
1464 obj->SetPointerInInternalField(0, aligned);
1465 i::Heap::CollectAllGarbage(false);
1466 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1467
1468 // Check reading and writing unaligned pointers.
1469 obj->SetPointerInInternalField(0, unaligned);
1470 i::Heap::CollectAllGarbage(false);
1471 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1472
1473 delete[] data;
1474}
1475
1476
Steve Block3ce2e202009-11-05 08:53:23 +00001477THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1478 v8::HandleScope scope;
1479 LocalContext env;
1480
1481 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1482 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1483 instance_templ->SetInternalFieldCount(1);
1484 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1485 CHECK_EQ(1, obj->InternalFieldCount());
1486 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1487
1488 char* data = new char[100];
1489
1490 void* aligned = data;
1491 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1492 void* unaligned = data + 1;
1493 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1494
1495 obj->SetPointerInInternalField(0, aligned);
1496 i::Heap::CollectAllGarbage(false);
1497 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1498
1499 obj->SetPointerInInternalField(0, unaligned);
1500 i::Heap::CollectAllGarbage(false);
1501 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1502
1503 obj->SetInternalField(0, v8::External::Wrap(aligned));
1504 i::Heap::CollectAllGarbage(false);
1505 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1506
1507 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1508 i::Heap::CollectAllGarbage(false);
1509 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1510
1511 delete[] data;
1512}
1513
1514
Steve Blocka7e24c12009-10-30 11:49:00 +00001515THREADED_TEST(IdentityHash) {
1516 v8::HandleScope scope;
1517 LocalContext env;
1518
1519 // Ensure that the test starts with an fresh heap to test whether the hash
1520 // code is based on the address.
1521 i::Heap::CollectAllGarbage(false);
1522 Local<v8::Object> obj = v8::Object::New();
1523 int hash = obj->GetIdentityHash();
1524 int hash1 = obj->GetIdentityHash();
1525 CHECK_EQ(hash, hash1);
1526 int hash2 = v8::Object::New()->GetIdentityHash();
1527 // Since the identity hash is essentially a random number two consecutive
1528 // objects should not be assigned the same hash code. If the test below fails
1529 // the random number generator should be evaluated.
1530 CHECK_NE(hash, hash2);
1531 i::Heap::CollectAllGarbage(false);
1532 int hash3 = v8::Object::New()->GetIdentityHash();
1533 // Make sure that the identity hash is not based on the initial address of
1534 // the object alone. If the test below fails the random number generator
1535 // should be evaluated.
1536 CHECK_NE(hash, hash3);
1537 int hash4 = obj->GetIdentityHash();
1538 CHECK_EQ(hash, hash4);
1539}
1540
1541
1542THREADED_TEST(HiddenProperties) {
1543 v8::HandleScope scope;
1544 LocalContext env;
1545
1546 v8::Local<v8::Object> obj = v8::Object::New();
1547 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1548 v8::Local<v8::String> empty = v8_str("");
1549 v8::Local<v8::String> prop_name = v8_str("prop_name");
1550
1551 i::Heap::CollectAllGarbage(false);
1552
1553 // Make sure delete of a non-existent hidden value works
1554 CHECK(obj->DeleteHiddenValue(key));
1555
1556 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1557 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1558 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1559 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1560
1561 i::Heap::CollectAllGarbage(false);
1562
1563 // Make sure we do not find the hidden property.
1564 CHECK(!obj->Has(empty));
1565 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1566 CHECK(obj->Get(empty)->IsUndefined());
1567 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1568 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1569 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1570 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1571
1572 i::Heap::CollectAllGarbage(false);
1573
1574 // Add another property and delete it afterwards to force the object in
1575 // slow case.
1576 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1577 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1578 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1579 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1580 CHECK(obj->Delete(prop_name));
1581 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1582
1583 i::Heap::CollectAllGarbage(false);
1584
1585 CHECK(obj->DeleteHiddenValue(key));
1586 CHECK(obj->GetHiddenValue(key).IsEmpty());
1587}
1588
1589
Steve Blockd0582a62009-12-15 09:54:21 +00001590static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001591static v8::Handle<Value> InterceptorForHiddenProperties(
1592 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001593 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001594 return v8::Handle<Value>();
1595}
1596
1597
1598THREADED_TEST(HiddenPropertiesWithInterceptors) {
1599 v8::HandleScope scope;
1600 LocalContext context;
1601
Steve Blockd0582a62009-12-15 09:54:21 +00001602 interceptor_for_hidden_properties_called = false;
1603
Steve Blocka7e24c12009-10-30 11:49:00 +00001604 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1605
1606 // Associate an interceptor with an object and start setting hidden values.
1607 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1608 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1609 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1610 Local<v8::Function> function = fun_templ->GetFunction();
1611 Local<v8::Object> obj = function->NewInstance();
1612 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1613 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001614 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001615}
1616
1617
1618THREADED_TEST(External) {
1619 v8::HandleScope scope;
1620 int x = 3;
1621 Local<v8::External> ext = v8::External::New(&x);
1622 LocalContext env;
1623 env->Global()->Set(v8_str("ext"), ext);
1624 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001625 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001626 int* ptr = static_cast<int*>(reext->Value());
1627 CHECK_EQ(x, 3);
1628 *ptr = 10;
1629 CHECK_EQ(x, 10);
1630
1631 // Make sure unaligned pointers are wrapped properly.
1632 char* data = i::StrDup("0123456789");
1633 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1634 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1635 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1636 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1637
1638 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1639 CHECK_EQ('0', *char_ptr);
1640 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1641 CHECK_EQ('1', *char_ptr);
1642 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1643 CHECK_EQ('2', *char_ptr);
1644 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1645 CHECK_EQ('3', *char_ptr);
1646 i::DeleteArray(data);
1647}
1648
1649
1650THREADED_TEST(GlobalHandle) {
1651 v8::Persistent<String> global;
1652 {
1653 v8::HandleScope scope;
1654 Local<String> str = v8_str("str");
1655 global = v8::Persistent<String>::New(str);
1656 }
1657 CHECK_EQ(global->Length(), 3);
1658 global.Dispose();
1659}
1660
1661
1662THREADED_TEST(ScriptException) {
1663 v8::HandleScope scope;
1664 LocalContext env;
1665 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1666 v8::TryCatch try_catch;
1667 Local<Value> result = script->Run();
1668 CHECK(result.IsEmpty());
1669 CHECK(try_catch.HasCaught());
1670 String::AsciiValue exception_value(try_catch.Exception());
1671 CHECK_EQ(*exception_value, "panama!");
1672}
1673
1674
1675bool message_received;
1676
1677
1678static void check_message(v8::Handle<v8::Message> message,
1679 v8::Handle<Value> data) {
1680 CHECK_EQ(5.76, data->NumberValue());
1681 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1682 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1683 message_received = true;
1684}
1685
1686
1687THREADED_TEST(MessageHandlerData) {
1688 message_received = false;
1689 v8::HandleScope scope;
1690 CHECK(!message_received);
1691 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1692 LocalContext context;
1693 v8::ScriptOrigin origin =
1694 v8::ScriptOrigin(v8_str("6.75"));
1695 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1696 &origin);
1697 script->SetData(v8_str("7.56"));
1698 script->Run();
1699 CHECK(message_received);
1700 // clear out the message listener
1701 v8::V8::RemoveMessageListeners(check_message);
1702}
1703
1704
1705THREADED_TEST(GetSetProperty) {
1706 v8::HandleScope scope;
1707 LocalContext context;
1708 context->Global()->Set(v8_str("foo"), v8_num(14));
1709 context->Global()->Set(v8_str("12"), v8_num(92));
1710 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1711 context->Global()->Set(v8_num(13), v8_num(56));
1712 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1713 CHECK_EQ(14, foo->Int32Value());
1714 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1715 CHECK_EQ(92, twelve->Int32Value());
1716 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1717 CHECK_EQ(32, sixteen->Int32Value());
1718 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1719 CHECK_EQ(56, thirteen->Int32Value());
1720 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1721 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1722 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1723 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1724 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1725 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1726 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1727 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1728 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1729}
1730
1731
1732THREADED_TEST(PropertyAttributes) {
1733 v8::HandleScope scope;
1734 LocalContext context;
1735 // read-only
1736 Local<String> prop = v8_str("read_only");
1737 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1738 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1739 Script::Compile(v8_str("read_only = 9"))->Run();
1740 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1741 context->Global()->Set(prop, v8_num(10));
1742 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1743 // dont-delete
1744 prop = v8_str("dont_delete");
1745 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1746 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1747 Script::Compile(v8_str("delete dont_delete"))->Run();
1748 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1749}
1750
1751
1752THREADED_TEST(Array) {
1753 v8::HandleScope scope;
1754 LocalContext context;
1755 Local<v8::Array> array = v8::Array::New();
1756 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001757 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001758 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001759 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001760 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001761 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001762 CHECK_EQ(3, array->Length());
1763 CHECK(!array->Has(0));
1764 CHECK(!array->Has(1));
1765 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001766 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001767 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001768 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001769 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001770 CHECK_EQ(1, arr->Get(0)->Int32Value());
1771 CHECK_EQ(2, arr->Get(1)->Int32Value());
1772 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001773}
1774
1775
1776v8::Handle<Value> HandleF(const v8::Arguments& args) {
1777 v8::HandleScope scope;
1778 ApiTestFuzzer::Fuzz();
1779 Local<v8::Array> result = v8::Array::New(args.Length());
1780 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001781 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001782 return scope.Close(result);
1783}
1784
1785
1786THREADED_TEST(Vector) {
1787 v8::HandleScope scope;
1788 Local<ObjectTemplate> global = ObjectTemplate::New();
1789 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1790 LocalContext context(0, global);
1791
1792 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001793 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001794 CHECK_EQ(0, a0->Length());
1795
1796 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001797 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001798 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001799 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001800
1801 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001802 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001803 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001804 CHECK_EQ(12, a2->Get(0)->Int32Value());
1805 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001806
1807 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001808 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001809 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001810 CHECK_EQ(14, a3->Get(0)->Int32Value());
1811 CHECK_EQ(15, a3->Get(1)->Int32Value());
1812 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001813
1814 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001815 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001816 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001817 CHECK_EQ(17, a4->Get(0)->Int32Value());
1818 CHECK_EQ(18, a4->Get(1)->Int32Value());
1819 CHECK_EQ(19, a4->Get(2)->Int32Value());
1820 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001821}
1822
1823
1824THREADED_TEST(FunctionCall) {
1825 v8::HandleScope scope;
1826 LocalContext context;
1827 CompileRun(
1828 "function Foo() {"
1829 " var result = [];"
1830 " for (var i = 0; i < arguments.length; i++) {"
1831 " result.push(arguments[i]);"
1832 " }"
1833 " return result;"
1834 "}");
1835 Local<Function> Foo =
1836 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1837
1838 v8::Handle<Value>* args0 = NULL;
1839 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1840 CHECK_EQ(0, a0->Length());
1841
1842 v8::Handle<Value> args1[] = { v8_num(1.1) };
1843 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1844 CHECK_EQ(1, a1->Length());
1845 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1846
1847 v8::Handle<Value> args2[] = { v8_num(2.2),
1848 v8_num(3.3) };
1849 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1850 CHECK_EQ(2, a2->Length());
1851 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1852 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1853
1854 v8::Handle<Value> args3[] = { v8_num(4.4),
1855 v8_num(5.5),
1856 v8_num(6.6) };
1857 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1858 CHECK_EQ(3, a3->Length());
1859 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1860 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1861 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1862
1863 v8::Handle<Value> args4[] = { v8_num(7.7),
1864 v8_num(8.8),
1865 v8_num(9.9),
1866 v8_num(10.11) };
1867 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1868 CHECK_EQ(4, a4->Length());
1869 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1870 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1871 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1872 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1873}
1874
1875
1876static const char* js_code_causing_out_of_memory =
1877 "var a = new Array(); while(true) a.push(a);";
1878
1879
1880// These tests run for a long time and prevent us from running tests
1881// that come after them so they cannot run in parallel.
1882TEST(OutOfMemory) {
1883 // It's not possible to read a snapshot into a heap with different dimensions.
1884 if (v8::internal::Snapshot::IsEnabled()) return;
1885 // Set heap limits.
1886 static const int K = 1024;
1887 v8::ResourceConstraints constraints;
1888 constraints.set_max_young_space_size(256 * K);
1889 constraints.set_max_old_space_size(4 * K * K);
1890 v8::SetResourceConstraints(&constraints);
1891
1892 // Execute a script that causes out of memory.
1893 v8::HandleScope scope;
1894 LocalContext context;
1895 v8::V8::IgnoreOutOfMemoryException();
1896 Local<Script> script =
1897 Script::Compile(String::New(js_code_causing_out_of_memory));
1898 Local<Value> result = script->Run();
1899
1900 // Check for out of memory state.
1901 CHECK(result.IsEmpty());
1902 CHECK(context->HasOutOfMemoryException());
1903}
1904
1905
1906v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1907 ApiTestFuzzer::Fuzz();
1908
1909 v8::HandleScope scope;
1910 LocalContext context;
1911 Local<Script> script =
1912 Script::Compile(String::New(js_code_causing_out_of_memory));
1913 Local<Value> result = script->Run();
1914
1915 // Check for out of memory state.
1916 CHECK(result.IsEmpty());
1917 CHECK(context->HasOutOfMemoryException());
1918
1919 return result;
1920}
1921
1922
1923TEST(OutOfMemoryNested) {
1924 // It's not possible to read a snapshot into a heap with different dimensions.
1925 if (v8::internal::Snapshot::IsEnabled()) return;
1926 // Set heap limits.
1927 static const int K = 1024;
1928 v8::ResourceConstraints constraints;
1929 constraints.set_max_young_space_size(256 * K);
1930 constraints.set_max_old_space_size(4 * K * K);
1931 v8::SetResourceConstraints(&constraints);
1932
1933 v8::HandleScope scope;
1934 Local<ObjectTemplate> templ = ObjectTemplate::New();
1935 templ->Set(v8_str("ProvokeOutOfMemory"),
1936 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1937 LocalContext context(0, templ);
1938 v8::V8::IgnoreOutOfMemoryException();
1939 Local<Value> result = CompileRun(
1940 "var thrown = false;"
1941 "try {"
1942 " ProvokeOutOfMemory();"
1943 "} catch (e) {"
1944 " thrown = true;"
1945 "}");
1946 // Check for out of memory state.
1947 CHECK(result.IsEmpty());
1948 CHECK(context->HasOutOfMemoryException());
1949}
1950
1951
1952TEST(HugeConsStringOutOfMemory) {
1953 // It's not possible to read a snapshot into a heap with different dimensions.
1954 if (v8::internal::Snapshot::IsEnabled()) return;
1955 v8::HandleScope scope;
1956 LocalContext context;
1957 // Set heap limits.
1958 static const int K = 1024;
1959 v8::ResourceConstraints constraints;
1960 constraints.set_max_young_space_size(256 * K);
1961 constraints.set_max_old_space_size(2 * K * K);
1962 v8::SetResourceConstraints(&constraints);
1963
1964 // Execute a script that causes out of memory.
1965 v8::V8::IgnoreOutOfMemoryException();
1966
1967 // Build huge string. This should fail with out of memory exception.
1968 Local<Value> result = CompileRun(
1969 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00001970 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00001971
1972 // Check for out of memory state.
1973 CHECK(result.IsEmpty());
1974 CHECK(context->HasOutOfMemoryException());
1975}
1976
1977
1978THREADED_TEST(ConstructCall) {
1979 v8::HandleScope scope;
1980 LocalContext context;
1981 CompileRun(
1982 "function Foo() {"
1983 " var result = [];"
1984 " for (var i = 0; i < arguments.length; i++) {"
1985 " result.push(arguments[i]);"
1986 " }"
1987 " return result;"
1988 "}");
1989 Local<Function> Foo =
1990 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1991
1992 v8::Handle<Value>* args0 = NULL;
1993 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
1994 CHECK_EQ(0, a0->Length());
1995
1996 v8::Handle<Value> args1[] = { v8_num(1.1) };
1997 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
1998 CHECK_EQ(1, a1->Length());
1999 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2000
2001 v8::Handle<Value> args2[] = { v8_num(2.2),
2002 v8_num(3.3) };
2003 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2004 CHECK_EQ(2, a2->Length());
2005 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2006 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2007
2008 v8::Handle<Value> args3[] = { v8_num(4.4),
2009 v8_num(5.5),
2010 v8_num(6.6) };
2011 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2012 CHECK_EQ(3, a3->Length());
2013 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2014 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2015 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2016
2017 v8::Handle<Value> args4[] = { v8_num(7.7),
2018 v8_num(8.8),
2019 v8_num(9.9),
2020 v8_num(10.11) };
2021 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2022 CHECK_EQ(4, a4->Length());
2023 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2024 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2025 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2026 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2027}
2028
2029
2030static void CheckUncle(v8::TryCatch* try_catch) {
2031 CHECK(try_catch->HasCaught());
2032 String::AsciiValue str_value(try_catch->Exception());
2033 CHECK_EQ(*str_value, "uncle?");
2034 try_catch->Reset();
2035}
2036
2037
Steve Block6ded16b2010-05-10 14:33:55 +01002038THREADED_TEST(ConversionNumber) {
2039 v8::HandleScope scope;
2040 LocalContext env;
2041 // Very large number.
2042 CompileRun("var obj = Math.pow(2,32) * 1237;");
2043 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2044 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2045 CHECK_EQ(0, obj->ToInt32()->Value());
2046 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2047 // Large number.
2048 CompileRun("var obj = -1234567890123;");
2049 obj = env->Global()->Get(v8_str("obj"));
2050 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2051 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2052 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2053 // Small positive integer.
2054 CompileRun("var obj = 42;");
2055 obj = env->Global()->Get(v8_str("obj"));
2056 CHECK_EQ(42.0, obj->ToNumber()->Value());
2057 CHECK_EQ(42, obj->ToInt32()->Value());
2058 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2059 // Negative integer.
2060 CompileRun("var obj = -37;");
2061 obj = env->Global()->Get(v8_str("obj"));
2062 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2063 CHECK_EQ(-37, obj->ToInt32()->Value());
2064 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2065 // Positive non-int32 integer.
2066 CompileRun("var obj = 0x81234567;");
2067 obj = env->Global()->Get(v8_str("obj"));
2068 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2069 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2070 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2071 // Fraction.
2072 CompileRun("var obj = 42.3;");
2073 obj = env->Global()->Get(v8_str("obj"));
2074 CHECK_EQ(42.3, obj->ToNumber()->Value());
2075 CHECK_EQ(42, obj->ToInt32()->Value());
2076 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2077 // Large negative fraction.
2078 CompileRun("var obj = -5726623061.75;");
2079 obj = env->Global()->Get(v8_str("obj"));
2080 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2081 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2082 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2083}
2084
2085
2086THREADED_TEST(isNumberType) {
2087 v8::HandleScope scope;
2088 LocalContext env;
2089 // Very large number.
2090 CompileRun("var obj = Math.pow(2,32) * 1237;");
2091 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2092 CHECK(!obj->IsInt32());
2093 CHECK(!obj->IsUint32());
2094 // Large negative number.
2095 CompileRun("var obj = -1234567890123;");
2096 obj = env->Global()->Get(v8_str("obj"));
2097 CHECK(!obj->IsInt32());
2098 CHECK(!obj->IsUint32());
2099 // Small positive integer.
2100 CompileRun("var obj = 42;");
2101 obj = env->Global()->Get(v8_str("obj"));
2102 CHECK(obj->IsInt32());
2103 CHECK(obj->IsUint32());
2104 // Negative integer.
2105 CompileRun("var obj = -37;");
2106 obj = env->Global()->Get(v8_str("obj"));
2107 CHECK(obj->IsInt32());
2108 CHECK(!obj->IsUint32());
2109 // Positive non-int32 integer.
2110 CompileRun("var obj = 0x81234567;");
2111 obj = env->Global()->Get(v8_str("obj"));
2112 CHECK(!obj->IsInt32());
2113 CHECK(obj->IsUint32());
2114 // Fraction.
2115 CompileRun("var obj = 42.3;");
2116 obj = env->Global()->Get(v8_str("obj"));
2117 CHECK(!obj->IsInt32());
2118 CHECK(!obj->IsUint32());
2119 // Large negative fraction.
2120 CompileRun("var obj = -5726623061.75;");
2121 obj = env->Global()->Get(v8_str("obj"));
2122 CHECK(!obj->IsInt32());
2123 CHECK(!obj->IsUint32());
2124}
2125
2126
Steve Blocka7e24c12009-10-30 11:49:00 +00002127THREADED_TEST(ConversionException) {
2128 v8::HandleScope scope;
2129 LocalContext env;
2130 CompileRun(
2131 "function TestClass() { };"
2132 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2133 "var obj = new TestClass();");
2134 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2135
2136 v8::TryCatch try_catch;
2137
2138 Local<Value> to_string_result = obj->ToString();
2139 CHECK(to_string_result.IsEmpty());
2140 CheckUncle(&try_catch);
2141
2142 Local<Value> to_number_result = obj->ToNumber();
2143 CHECK(to_number_result.IsEmpty());
2144 CheckUncle(&try_catch);
2145
2146 Local<Value> to_integer_result = obj->ToInteger();
2147 CHECK(to_integer_result.IsEmpty());
2148 CheckUncle(&try_catch);
2149
2150 Local<Value> to_uint32_result = obj->ToUint32();
2151 CHECK(to_uint32_result.IsEmpty());
2152 CheckUncle(&try_catch);
2153
2154 Local<Value> to_int32_result = obj->ToInt32();
2155 CHECK(to_int32_result.IsEmpty());
2156 CheckUncle(&try_catch);
2157
2158 Local<Value> to_object_result = v8::Undefined()->ToObject();
2159 CHECK(to_object_result.IsEmpty());
2160 CHECK(try_catch.HasCaught());
2161 try_catch.Reset();
2162
2163 int32_t int32_value = obj->Int32Value();
2164 CHECK_EQ(0, int32_value);
2165 CheckUncle(&try_catch);
2166
2167 uint32_t uint32_value = obj->Uint32Value();
2168 CHECK_EQ(0, uint32_value);
2169 CheckUncle(&try_catch);
2170
2171 double number_value = obj->NumberValue();
2172 CHECK_NE(0, IsNaN(number_value));
2173 CheckUncle(&try_catch);
2174
2175 int64_t integer_value = obj->IntegerValue();
2176 CHECK_EQ(0.0, static_cast<double>(integer_value));
2177 CheckUncle(&try_catch);
2178}
2179
2180
2181v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2182 ApiTestFuzzer::Fuzz();
2183 return v8::ThrowException(v8_str("konto"));
2184}
2185
2186
2187v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2188 if (args.Length() < 1) return v8::Boolean::New(false);
2189 v8::HandleScope scope;
2190 v8::TryCatch try_catch;
2191 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2192 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2193 return v8::Boolean::New(try_catch.HasCaught());
2194}
2195
2196
2197THREADED_TEST(APICatch) {
2198 v8::HandleScope scope;
2199 Local<ObjectTemplate> templ = ObjectTemplate::New();
2200 templ->Set(v8_str("ThrowFromC"),
2201 v8::FunctionTemplate::New(ThrowFromC));
2202 LocalContext context(0, templ);
2203 CompileRun(
2204 "var thrown = false;"
2205 "try {"
2206 " ThrowFromC();"
2207 "} catch (e) {"
2208 " thrown = true;"
2209 "}");
2210 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2211 CHECK(thrown->BooleanValue());
2212}
2213
2214
2215THREADED_TEST(APIThrowTryCatch) {
2216 v8::HandleScope scope;
2217 Local<ObjectTemplate> templ = ObjectTemplate::New();
2218 templ->Set(v8_str("ThrowFromC"),
2219 v8::FunctionTemplate::New(ThrowFromC));
2220 LocalContext context(0, templ);
2221 v8::TryCatch try_catch;
2222 CompileRun("ThrowFromC();");
2223 CHECK(try_catch.HasCaught());
2224}
2225
2226
2227// Test that a try-finally block doesn't shadow a try-catch block
2228// when setting up an external handler.
2229//
2230// BUG(271): Some of the exception propagation does not work on the
2231// ARM simulator because the simulator separates the C++ stack and the
2232// JS stack. This test therefore fails on the simulator. The test is
2233// not threaded to allow the threading tests to run on the simulator.
2234TEST(TryCatchInTryFinally) {
2235 v8::HandleScope scope;
2236 Local<ObjectTemplate> templ = ObjectTemplate::New();
2237 templ->Set(v8_str("CCatcher"),
2238 v8::FunctionTemplate::New(CCatcher));
2239 LocalContext context(0, templ);
2240 Local<Value> result = CompileRun("try {"
2241 " try {"
2242 " CCatcher('throw 7;');"
2243 " } finally {"
2244 " }"
2245 "} catch (e) {"
2246 "}");
2247 CHECK(result->IsTrue());
2248}
2249
2250
2251static void receive_message(v8::Handle<v8::Message> message,
2252 v8::Handle<v8::Value> data) {
2253 message->Get();
2254 message_received = true;
2255}
2256
2257
2258TEST(APIThrowMessage) {
2259 message_received = false;
2260 v8::HandleScope scope;
2261 v8::V8::AddMessageListener(receive_message);
2262 Local<ObjectTemplate> templ = ObjectTemplate::New();
2263 templ->Set(v8_str("ThrowFromC"),
2264 v8::FunctionTemplate::New(ThrowFromC));
2265 LocalContext context(0, templ);
2266 CompileRun("ThrowFromC();");
2267 CHECK(message_received);
2268 v8::V8::RemoveMessageListeners(check_message);
2269}
2270
2271
2272TEST(APIThrowMessageAndVerboseTryCatch) {
2273 message_received = false;
2274 v8::HandleScope scope;
2275 v8::V8::AddMessageListener(receive_message);
2276 Local<ObjectTemplate> templ = ObjectTemplate::New();
2277 templ->Set(v8_str("ThrowFromC"),
2278 v8::FunctionTemplate::New(ThrowFromC));
2279 LocalContext context(0, templ);
2280 v8::TryCatch try_catch;
2281 try_catch.SetVerbose(true);
2282 Local<Value> result = CompileRun("ThrowFromC();");
2283 CHECK(try_catch.HasCaught());
2284 CHECK(result.IsEmpty());
2285 CHECK(message_received);
2286 v8::V8::RemoveMessageListeners(check_message);
2287}
2288
2289
2290THREADED_TEST(ExternalScriptException) {
2291 v8::HandleScope scope;
2292 Local<ObjectTemplate> templ = ObjectTemplate::New();
2293 templ->Set(v8_str("ThrowFromC"),
2294 v8::FunctionTemplate::New(ThrowFromC));
2295 LocalContext context(0, templ);
2296
2297 v8::TryCatch try_catch;
2298 Local<Script> script
2299 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2300 Local<Value> result = script->Run();
2301 CHECK(result.IsEmpty());
2302 CHECK(try_catch.HasCaught());
2303 String::AsciiValue exception_value(try_catch.Exception());
2304 CHECK_EQ("konto", *exception_value);
2305}
2306
2307
2308
2309v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2310 ApiTestFuzzer::Fuzz();
2311 CHECK_EQ(4, args.Length());
2312 int count = args[0]->Int32Value();
2313 int cInterval = args[2]->Int32Value();
2314 if (count == 0) {
2315 return v8::ThrowException(v8_str("FromC"));
2316 } else {
2317 Local<v8::Object> global = Context::GetCurrent()->Global();
2318 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2319 v8::Handle<Value> argv[] = { v8_num(count - 1),
2320 args[1],
2321 args[2],
2322 args[3] };
2323 if (count % cInterval == 0) {
2324 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002325 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002326 int expected = args[3]->Int32Value();
2327 if (try_catch.HasCaught()) {
2328 CHECK_EQ(expected, count);
2329 CHECK(result.IsEmpty());
2330 CHECK(!i::Top::has_scheduled_exception());
2331 } else {
2332 CHECK_NE(expected, count);
2333 }
2334 return result;
2335 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002336 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002337 }
2338 }
2339}
2340
2341
2342v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2343 ApiTestFuzzer::Fuzz();
2344 CHECK_EQ(3, args.Length());
2345 bool equality = args[0]->BooleanValue();
2346 int count = args[1]->Int32Value();
2347 int expected = args[2]->Int32Value();
2348 if (equality) {
2349 CHECK_EQ(count, expected);
2350 } else {
2351 CHECK_NE(count, expected);
2352 }
2353 return v8::Undefined();
2354}
2355
2356
2357THREADED_TEST(EvalInTryFinally) {
2358 v8::HandleScope scope;
2359 LocalContext context;
2360 v8::TryCatch try_catch;
2361 CompileRun("(function() {"
2362 " try {"
2363 " eval('asldkf (*&^&*^');"
2364 " } finally {"
2365 " return;"
2366 " }"
2367 "})()");
2368 CHECK(!try_catch.HasCaught());
2369}
2370
2371
2372// This test works by making a stack of alternating JavaScript and C
2373// activations. These activations set up exception handlers with regular
2374// intervals, one interval for C activations and another for JavaScript
2375// activations. When enough activations have been created an exception is
2376// thrown and we check that the right activation catches the exception and that
2377// no other activations do. The right activation is always the topmost one with
2378// a handler, regardless of whether it is in JavaScript or C.
2379//
2380// The notation used to describe a test case looks like this:
2381//
2382// *JS[4] *C[3] @JS[2] C[1] JS[0]
2383//
2384// Each entry is an activation, either JS or C. The index is the count at that
2385// level. Stars identify activations with exception handlers, the @ identifies
2386// the exception handler that should catch the exception.
2387//
2388// BUG(271): Some of the exception propagation does not work on the
2389// ARM simulator because the simulator separates the C++ stack and the
2390// JS stack. This test therefore fails on the simulator. The test is
2391// not threaded to allow the threading tests to run on the simulator.
2392TEST(ExceptionOrder) {
2393 v8::HandleScope scope;
2394 Local<ObjectTemplate> templ = ObjectTemplate::New();
2395 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2396 templ->Set(v8_str("CThrowCountDown"),
2397 v8::FunctionTemplate::New(CThrowCountDown));
2398 LocalContext context(0, templ);
2399 CompileRun(
2400 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2401 " if (count == 0) throw 'FromJS';"
2402 " if (count % jsInterval == 0) {"
2403 " try {"
2404 " var value = CThrowCountDown(count - 1,"
2405 " jsInterval,"
2406 " cInterval,"
2407 " expected);"
2408 " check(false, count, expected);"
2409 " return value;"
2410 " } catch (e) {"
2411 " check(true, count, expected);"
2412 " }"
2413 " } else {"
2414 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2415 " }"
2416 "}");
2417 Local<Function> fun =
2418 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2419
2420 const int argc = 4;
2421 // count jsInterval cInterval expected
2422
2423 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2424 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2425 fun->Call(fun, argc, a0);
2426
2427 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2428 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2429 fun->Call(fun, argc, a1);
2430
2431 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2432 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2433 fun->Call(fun, argc, a2);
2434
2435 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2436 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2437 fun->Call(fun, argc, a3);
2438
2439 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2440 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2441 fun->Call(fun, argc, a4);
2442
2443 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2444 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2445 fun->Call(fun, argc, a5);
2446}
2447
2448
2449v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2450 ApiTestFuzzer::Fuzz();
2451 CHECK_EQ(1, args.Length());
2452 return v8::ThrowException(args[0]);
2453}
2454
2455
2456THREADED_TEST(ThrowValues) {
2457 v8::HandleScope scope;
2458 Local<ObjectTemplate> templ = ObjectTemplate::New();
2459 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2460 LocalContext context(0, templ);
2461 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2462 "function Run(obj) {"
2463 " try {"
2464 " Throw(obj);"
2465 " } catch (e) {"
2466 " return e;"
2467 " }"
2468 " return 'no exception';"
2469 "}"
2470 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2471 CHECK_EQ(5, result->Length());
2472 CHECK(result->Get(v8::Integer::New(0))->IsString());
2473 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2474 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2475 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2476 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2477 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2478 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2479}
2480
2481
2482THREADED_TEST(CatchZero) {
2483 v8::HandleScope scope;
2484 LocalContext context;
2485 v8::TryCatch try_catch;
2486 CHECK(!try_catch.HasCaught());
2487 Script::Compile(v8_str("throw 10"))->Run();
2488 CHECK(try_catch.HasCaught());
2489 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2490 try_catch.Reset();
2491 CHECK(!try_catch.HasCaught());
2492 Script::Compile(v8_str("throw 0"))->Run();
2493 CHECK(try_catch.HasCaught());
2494 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2495}
2496
2497
2498THREADED_TEST(CatchExceptionFromWith) {
2499 v8::HandleScope scope;
2500 LocalContext context;
2501 v8::TryCatch try_catch;
2502 CHECK(!try_catch.HasCaught());
2503 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2504 CHECK(try_catch.HasCaught());
2505}
2506
2507
2508THREADED_TEST(Equality) {
2509 v8::HandleScope scope;
2510 LocalContext context;
2511 // Check that equality works at all before relying on CHECK_EQ
2512 CHECK(v8_str("a")->Equals(v8_str("a")));
2513 CHECK(!v8_str("a")->Equals(v8_str("b")));
2514
2515 CHECK_EQ(v8_str("a"), v8_str("a"));
2516 CHECK_NE(v8_str("a"), v8_str("b"));
2517 CHECK_EQ(v8_num(1), v8_num(1));
2518 CHECK_EQ(v8_num(1.00), v8_num(1));
2519 CHECK_NE(v8_num(1), v8_num(2));
2520
2521 // Assume String is not symbol.
2522 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2523 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2524 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2525 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2526 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2527 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2528 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2529 CHECK(!not_a_number->StrictEquals(not_a_number));
2530 CHECK(v8::False()->StrictEquals(v8::False()));
2531 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2532
2533 v8::Handle<v8::Object> obj = v8::Object::New();
2534 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2535 CHECK(alias->StrictEquals(obj));
2536 alias.Dispose();
2537}
2538
2539
2540THREADED_TEST(MultiRun) {
2541 v8::HandleScope scope;
2542 LocalContext context;
2543 Local<Script> script = Script::Compile(v8_str("x"));
2544 for (int i = 0; i < 10; i++)
2545 script->Run();
2546}
2547
2548
2549static v8::Handle<Value> GetXValue(Local<String> name,
2550 const AccessorInfo& info) {
2551 ApiTestFuzzer::Fuzz();
2552 CHECK_EQ(info.Data(), v8_str("donut"));
2553 CHECK_EQ(name, v8_str("x"));
2554 return name;
2555}
2556
2557
2558THREADED_TEST(SimplePropertyRead) {
2559 v8::HandleScope scope;
2560 Local<ObjectTemplate> templ = ObjectTemplate::New();
2561 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2562 LocalContext context;
2563 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2564 Local<Script> script = Script::Compile(v8_str("obj.x"));
2565 for (int i = 0; i < 10; i++) {
2566 Local<Value> result = script->Run();
2567 CHECK_EQ(result, v8_str("x"));
2568 }
2569}
2570
Andrei Popescu31002712010-02-23 13:46:05 +00002571THREADED_TEST(DefinePropertyOnAPIAccessor) {
2572 v8::HandleScope scope;
2573 Local<ObjectTemplate> templ = ObjectTemplate::New();
2574 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2575 LocalContext context;
2576 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2577
2578 // Uses getOwnPropertyDescriptor to check the configurable status
2579 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002580 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002581 "obj, 'x');"
2582 "prop.configurable;"));
2583 Local<Value> result = script_desc->Run();
2584 CHECK_EQ(result->BooleanValue(), true);
2585
2586 // Redefine get - but still configurable
2587 Local<Script> script_define
2588 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2589 " configurable: true };"
2590 "Object.defineProperty(obj, 'x', desc);"
2591 "obj.x"));
2592 result = script_define->Run();
2593 CHECK_EQ(result, v8_num(42));
2594
2595 // Check that the accessor is still configurable
2596 result = script_desc->Run();
2597 CHECK_EQ(result->BooleanValue(), true);
2598
2599 // Redefine to a non-configurable
2600 script_define
2601 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2602 " configurable: false };"
2603 "Object.defineProperty(obj, 'x', desc);"
2604 "obj.x"));
2605 result = script_define->Run();
2606 CHECK_EQ(result, v8_num(43));
2607 result = script_desc->Run();
2608 CHECK_EQ(result->BooleanValue(), false);
2609
2610 // Make sure that it is not possible to redefine again
2611 v8::TryCatch try_catch;
2612 result = script_define->Run();
2613 CHECK(try_catch.HasCaught());
2614 String::AsciiValue exception_value(try_catch.Exception());
2615 CHECK_EQ(*exception_value,
2616 "TypeError: Cannot redefine property: defineProperty");
2617}
2618
2619THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2620 v8::HandleScope scope;
2621 Local<ObjectTemplate> templ = ObjectTemplate::New();
2622 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2623 LocalContext context;
2624 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2625
2626 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2627 "Object.getOwnPropertyDescriptor( "
2628 "obj, 'x');"
2629 "prop.configurable;"));
2630 Local<Value> result = script_desc->Run();
2631 CHECK_EQ(result->BooleanValue(), true);
2632
2633 Local<Script> script_define =
2634 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2635 " configurable: true };"
2636 "Object.defineProperty(obj, 'x', desc);"
2637 "obj.x"));
2638 result = script_define->Run();
2639 CHECK_EQ(result, v8_num(42));
2640
2641
2642 result = script_desc->Run();
2643 CHECK_EQ(result->BooleanValue(), true);
2644
2645
2646 script_define =
2647 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2648 " configurable: false };"
2649 "Object.defineProperty(obj, 'x', desc);"
2650 "obj.x"));
2651 result = script_define->Run();
2652 CHECK_EQ(result, v8_num(43));
2653 result = script_desc->Run();
2654
2655 CHECK_EQ(result->BooleanValue(), false);
2656
2657 v8::TryCatch try_catch;
2658 result = script_define->Run();
2659 CHECK(try_catch.HasCaught());
2660 String::AsciiValue exception_value(try_catch.Exception());
2661 CHECK_EQ(*exception_value,
2662 "TypeError: Cannot redefine property: defineProperty");
2663}
2664
2665
Leon Clarkef7060e22010-06-03 12:02:55 +01002666static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2667 char const* name) {
2668 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2669}
Andrei Popescu31002712010-02-23 13:46:05 +00002670
2671
Leon Clarkef7060e22010-06-03 12:02:55 +01002672THREADED_TEST(DefineAPIAccessorOnObject) {
2673 v8::HandleScope scope;
2674 Local<ObjectTemplate> templ = ObjectTemplate::New();
2675 LocalContext context;
2676
2677 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2678 CompileRun("var obj2 = {};");
2679
2680 CHECK(CompileRun("obj1.x")->IsUndefined());
2681 CHECK(CompileRun("obj2.x")->IsUndefined());
2682
2683 CHECK(GetGlobalProperty(&context, "obj1")->
2684 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2685
2686 ExpectString("obj1.x", "x");
2687 CHECK(CompileRun("obj2.x")->IsUndefined());
2688
2689 CHECK(GetGlobalProperty(&context, "obj2")->
2690 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2691
2692 ExpectString("obj1.x", "x");
2693 ExpectString("obj2.x", "x");
2694
2695 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2696 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2697
2698 CompileRun("Object.defineProperty(obj1, 'x',"
2699 "{ get: function() { return 'y'; }, configurable: true })");
2700
2701 ExpectString("obj1.x", "y");
2702 ExpectString("obj2.x", "x");
2703
2704 CompileRun("Object.defineProperty(obj2, 'x',"
2705 "{ get: function() { return 'y'; }, configurable: true })");
2706
2707 ExpectString("obj1.x", "y");
2708 ExpectString("obj2.x", "y");
2709
2710 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2711 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2712
2713 CHECK(GetGlobalProperty(&context, "obj1")->
2714 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2715 CHECK(GetGlobalProperty(&context, "obj2")->
2716 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2717
2718 ExpectString("obj1.x", "x");
2719 ExpectString("obj2.x", "x");
2720
2721 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2722 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2723
2724 // Define getters/setters, but now make them not configurable.
2725 CompileRun("Object.defineProperty(obj1, 'x',"
2726 "{ get: function() { return 'z'; }, configurable: false })");
2727 CompileRun("Object.defineProperty(obj2, 'x',"
2728 "{ get: function() { return 'z'; }, configurable: false })");
2729
2730 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2731 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2732
2733 ExpectString("obj1.x", "z");
2734 ExpectString("obj2.x", "z");
2735
2736 CHECK(!GetGlobalProperty(&context, "obj1")->
2737 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2738 CHECK(!GetGlobalProperty(&context, "obj2")->
2739 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2740
2741 ExpectString("obj1.x", "z");
2742 ExpectString("obj2.x", "z");
2743}
2744
2745
2746THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2747 v8::HandleScope scope;
2748 Local<ObjectTemplate> templ = ObjectTemplate::New();
2749 LocalContext context;
2750
2751 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2752 CompileRun("var obj2 = {};");
2753
2754 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2755 v8_str("x"),
2756 GetXValue, NULL,
2757 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2758 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2759 v8_str("x"),
2760 GetXValue, NULL,
2761 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2762
2763 ExpectString("obj1.x", "x");
2764 ExpectString("obj2.x", "x");
2765
2766 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2767 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2768
2769 CHECK(!GetGlobalProperty(&context, "obj1")->
2770 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2771 CHECK(!GetGlobalProperty(&context, "obj2")->
2772 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2773
2774 {
2775 v8::TryCatch try_catch;
2776 CompileRun("Object.defineProperty(obj1, 'x',"
2777 "{get: function() { return 'func'; }})");
2778 CHECK(try_catch.HasCaught());
2779 String::AsciiValue exception_value(try_catch.Exception());
2780 CHECK_EQ(*exception_value,
2781 "TypeError: Cannot redefine property: defineProperty");
2782 }
2783 {
2784 v8::TryCatch try_catch;
2785 CompileRun("Object.defineProperty(obj2, 'x',"
2786 "{get: function() { return 'func'; }})");
2787 CHECK(try_catch.HasCaught());
2788 String::AsciiValue exception_value(try_catch.Exception());
2789 CHECK_EQ(*exception_value,
2790 "TypeError: Cannot redefine property: defineProperty");
2791 }
2792}
2793
2794
2795static v8::Handle<Value> Get239Value(Local<String> name,
2796 const AccessorInfo& info) {
2797 ApiTestFuzzer::Fuzz();
2798 CHECK_EQ(info.Data(), v8_str("donut"));
2799 CHECK_EQ(name, v8_str("239"));
2800 return name;
2801}
2802
2803
2804THREADED_TEST(ElementAPIAccessor) {
2805 v8::HandleScope scope;
2806 Local<ObjectTemplate> templ = ObjectTemplate::New();
2807 LocalContext context;
2808
2809 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2810 CompileRun("var obj2 = {};");
2811
2812 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2813 v8_str("239"),
2814 Get239Value, NULL,
2815 v8_str("donut")));
2816 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2817 v8_str("239"),
2818 Get239Value, NULL,
2819 v8_str("donut")));
2820
2821 ExpectString("obj1[239]", "239");
2822 ExpectString("obj2[239]", "239");
2823 ExpectString("obj1['239']", "239");
2824 ExpectString("obj2['239']", "239");
2825}
2826
Steve Blocka7e24c12009-10-30 11:49:00 +00002827
2828v8::Persistent<Value> xValue;
2829
2830
2831static void SetXValue(Local<String> name,
2832 Local<Value> value,
2833 const AccessorInfo& info) {
2834 CHECK_EQ(value, v8_num(4));
2835 CHECK_EQ(info.Data(), v8_str("donut"));
2836 CHECK_EQ(name, v8_str("x"));
2837 CHECK(xValue.IsEmpty());
2838 xValue = v8::Persistent<Value>::New(value);
2839}
2840
2841
2842THREADED_TEST(SimplePropertyWrite) {
2843 v8::HandleScope scope;
2844 Local<ObjectTemplate> templ = ObjectTemplate::New();
2845 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2846 LocalContext context;
2847 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2848 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2849 for (int i = 0; i < 10; i++) {
2850 CHECK(xValue.IsEmpty());
2851 script->Run();
2852 CHECK_EQ(v8_num(4), xValue);
2853 xValue.Dispose();
2854 xValue = v8::Persistent<Value>();
2855 }
2856}
2857
2858
2859static v8::Handle<Value> XPropertyGetter(Local<String> property,
2860 const AccessorInfo& info) {
2861 ApiTestFuzzer::Fuzz();
2862 CHECK(info.Data()->IsUndefined());
2863 return property;
2864}
2865
2866
2867THREADED_TEST(NamedInterceptorPropertyRead) {
2868 v8::HandleScope scope;
2869 Local<ObjectTemplate> templ = ObjectTemplate::New();
2870 templ->SetNamedPropertyHandler(XPropertyGetter);
2871 LocalContext context;
2872 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2873 Local<Script> script = Script::Compile(v8_str("obj.x"));
2874 for (int i = 0; i < 10; i++) {
2875 Local<Value> result = script->Run();
2876 CHECK_EQ(result, v8_str("x"));
2877 }
2878}
2879
2880
Steve Block6ded16b2010-05-10 14:33:55 +01002881THREADED_TEST(NamedInterceptorDictionaryIC) {
2882 v8::HandleScope scope;
2883 Local<ObjectTemplate> templ = ObjectTemplate::New();
2884 templ->SetNamedPropertyHandler(XPropertyGetter);
2885 LocalContext context;
2886 // Create an object with a named interceptor.
2887 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2888 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2889 for (int i = 0; i < 10; i++) {
2890 Local<Value> result = script->Run();
2891 CHECK_EQ(result, v8_str("x"));
2892 }
2893 // Create a slow case object and a function accessing a property in
2894 // that slow case object (with dictionary probing in generated
2895 // code). Then force object with a named interceptor into slow-case,
2896 // pass it to the function, and check that the interceptor is called
2897 // instead of accessing the local property.
2898 Local<Value> result =
2899 CompileRun("function get_x(o) { return o.x; };"
2900 "var obj = { x : 42, y : 0 };"
2901 "delete obj.y;"
2902 "for (var i = 0; i < 10; i++) get_x(obj);"
2903 "interceptor_obj.x = 42;"
2904 "interceptor_obj.y = 10;"
2905 "delete interceptor_obj.y;"
2906 "get_x(interceptor_obj)");
2907 CHECK_EQ(result, v8_str("x"));
2908}
2909
2910
Andrei Popescu402d9372010-02-26 13:31:12 +00002911static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2912 const AccessorInfo& info) {
2913 // Set x on the prototype object and do not handle the get request.
2914 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002915 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002916 return v8::Handle<Value>();
2917}
2918
2919
2920// This is a regression test for http://crbug.com/20104. Map
2921// transitions should not interfere with post interceptor lookup.
2922THREADED_TEST(NamedInterceptorMapTransitionRead) {
2923 v8::HandleScope scope;
2924 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
2925 Local<v8::ObjectTemplate> instance_template
2926 = function_template->InstanceTemplate();
2927 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
2928 LocalContext context;
2929 context->Global()->Set(v8_str("F"), function_template->GetFunction());
2930 // Create an instance of F and introduce a map transition for x.
2931 CompileRun("var o = new F(); o.x = 23;");
2932 // Create an instance of F and invoke the getter. The result should be 23.
2933 Local<Value> result = CompileRun("o = new F(); o.x");
2934 CHECK_EQ(result->Int32Value(), 23);
2935}
2936
2937
Steve Blocka7e24c12009-10-30 11:49:00 +00002938static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2939 const AccessorInfo& info) {
2940 ApiTestFuzzer::Fuzz();
2941 if (index == 37) {
2942 return v8::Handle<Value>(v8_num(625));
2943 }
2944 return v8::Handle<Value>();
2945}
2946
2947
2948static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2949 Local<Value> value,
2950 const AccessorInfo& info) {
2951 ApiTestFuzzer::Fuzz();
2952 if (index == 39) {
2953 return value;
2954 }
2955 return v8::Handle<Value>();
2956}
2957
2958
2959THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2960 v8::HandleScope scope;
2961 Local<ObjectTemplate> templ = ObjectTemplate::New();
2962 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2963 IndexedPropertySetter);
2964 LocalContext context;
2965 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2966 Local<Script> getter_script = Script::Compile(v8_str(
2967 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2968 Local<Script> setter_script = Script::Compile(v8_str(
2969 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2970 "obj[17] = 23;"
2971 "obj.foo;"));
2972 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
2973 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
2974 "obj[39] = 47;"
2975 "obj.foo;")); // This setter should not run, due to the interceptor.
2976 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
2977 "obj[37];"));
2978 Local<Value> result = getter_script->Run();
2979 CHECK_EQ(v8_num(5), result);
2980 result = setter_script->Run();
2981 CHECK_EQ(v8_num(23), result);
2982 result = interceptor_setter_script->Run();
2983 CHECK_EQ(v8_num(23), result);
2984 result = interceptor_getter_script->Run();
2985 CHECK_EQ(v8_num(625), result);
2986}
2987
2988
Leon Clarked91b9f72010-01-27 17:25:45 +00002989static v8::Handle<Value> IdentityIndexedPropertyGetter(
2990 uint32_t index,
2991 const AccessorInfo& info) {
2992 return v8::Integer::New(index);
2993}
2994
2995
2996THREADED_TEST(IndexedInterceptorWithNoSetter) {
2997 v8::HandleScope scope;
2998 Local<ObjectTemplate> templ = ObjectTemplate::New();
2999 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3000
3001 LocalContext context;
3002 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3003
3004 const char* code =
3005 "try {"
3006 " obj[0] = 239;"
3007 " for (var i = 0; i < 100; i++) {"
3008 " var v = obj[0];"
3009 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3010 " }"
3011 " 'PASSED'"
3012 "} catch(e) {"
3013 " e"
3014 "}";
3015 ExpectString(code, "PASSED");
3016}
3017
3018
Andrei Popescu402d9372010-02-26 13:31:12 +00003019THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3020 v8::HandleScope scope;
3021 Local<ObjectTemplate> templ = ObjectTemplate::New();
3022 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3023
3024 LocalContext context;
3025 Local<v8::Object> obj = templ->NewInstance();
3026 obj->TurnOnAccessCheck();
3027 context->Global()->Set(v8_str("obj"), obj);
3028
3029 const char* code =
3030 "try {"
3031 " for (var i = 0; i < 100; i++) {"
3032 " var v = obj[0];"
3033 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3034 " }"
3035 " 'PASSED'"
3036 "} catch(e) {"
3037 " e"
3038 "}";
3039 ExpectString(code, "PASSED");
3040}
3041
3042
3043THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3044 i::FLAG_allow_natives_syntax = true;
3045 v8::HandleScope scope;
3046 Local<ObjectTemplate> templ = ObjectTemplate::New();
3047 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3048
3049 LocalContext context;
3050 Local<v8::Object> obj = templ->NewInstance();
3051 context->Global()->Set(v8_str("obj"), obj);
3052
3053 const char* code =
3054 "try {"
3055 " for (var i = 0; i < 100; i++) {"
3056 " var expected = i;"
3057 " if (i == 5) {"
3058 " %EnableAccessChecks(obj);"
3059 " expected = undefined;"
3060 " }"
3061 " var v = obj[i];"
3062 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3063 " if (i == 5) %DisableAccessChecks(obj);"
3064 " }"
3065 " 'PASSED'"
3066 "} catch(e) {"
3067 " e"
3068 "}";
3069 ExpectString(code, "PASSED");
3070}
3071
3072
3073THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3074 v8::HandleScope scope;
3075 Local<ObjectTemplate> templ = ObjectTemplate::New();
3076 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3077
3078 LocalContext context;
3079 Local<v8::Object> obj = templ->NewInstance();
3080 context->Global()->Set(v8_str("obj"), obj);
3081
3082 const char* code =
3083 "try {"
3084 " for (var i = 0; i < 100; i++) {"
3085 " var v = obj[i];"
3086 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3087 " }"
3088 " 'PASSED'"
3089 "} catch(e) {"
3090 " e"
3091 "}";
3092 ExpectString(code, "PASSED");
3093}
3094
3095
3096THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3097 v8::HandleScope scope;
3098 Local<ObjectTemplate> templ = ObjectTemplate::New();
3099 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3100
3101 LocalContext context;
3102 Local<v8::Object> obj = templ->NewInstance();
3103 context->Global()->Set(v8_str("obj"), obj);
3104
3105 const char* code =
3106 "try {"
3107 " for (var i = 0; i < 100; i++) {"
3108 " var expected = i;"
3109 " if (i == 50) {"
3110 " i = 'foobar';"
3111 " expected = undefined;"
3112 " }"
3113 " var v = obj[i];"
3114 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3115 " }"
3116 " 'PASSED'"
3117 "} catch(e) {"
3118 " e"
3119 "}";
3120 ExpectString(code, "PASSED");
3121}
3122
3123
3124THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3125 v8::HandleScope scope;
3126 Local<ObjectTemplate> templ = ObjectTemplate::New();
3127 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3128
3129 LocalContext context;
3130 Local<v8::Object> obj = templ->NewInstance();
3131 context->Global()->Set(v8_str("obj"), obj);
3132
3133 const char* code =
3134 "var original = obj;"
3135 "try {"
3136 " for (var i = 0; i < 100; i++) {"
3137 " var expected = i;"
3138 " if (i == 50) {"
3139 " obj = {50: 'foobar'};"
3140 " expected = 'foobar';"
3141 " }"
3142 " var v = obj[i];"
3143 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3144 " if (i == 50) obj = original;"
3145 " }"
3146 " 'PASSED'"
3147 "} catch(e) {"
3148 " e"
3149 "}";
3150 ExpectString(code, "PASSED");
3151}
3152
3153
3154THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3155 v8::HandleScope scope;
3156 Local<ObjectTemplate> templ = ObjectTemplate::New();
3157 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3158
3159 LocalContext context;
3160 Local<v8::Object> obj = templ->NewInstance();
3161 context->Global()->Set(v8_str("obj"), obj);
3162
3163 const char* code =
3164 "var original = obj;"
3165 "try {"
3166 " for (var i = 0; i < 100; i++) {"
3167 " var expected = i;"
3168 " if (i == 5) {"
3169 " obj = 239;"
3170 " expected = undefined;"
3171 " }"
3172 " var v = obj[i];"
3173 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3174 " if (i == 5) obj = original;"
3175 " }"
3176 " 'PASSED'"
3177 "} catch(e) {"
3178 " e"
3179 "}";
3180 ExpectString(code, "PASSED");
3181}
3182
3183
3184THREADED_TEST(IndexedInterceptorOnProto) {
3185 v8::HandleScope scope;
3186 Local<ObjectTemplate> templ = ObjectTemplate::New();
3187 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3188
3189 LocalContext context;
3190 Local<v8::Object> obj = templ->NewInstance();
3191 context->Global()->Set(v8_str("obj"), obj);
3192
3193 const char* code =
3194 "var o = {__proto__: obj};"
3195 "try {"
3196 " for (var i = 0; i < 100; i++) {"
3197 " var v = o[i];"
3198 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3199 " }"
3200 " 'PASSED'"
3201 "} catch(e) {"
3202 " e"
3203 "}";
3204 ExpectString(code, "PASSED");
3205}
3206
3207
Steve Blocka7e24c12009-10-30 11:49:00 +00003208THREADED_TEST(MultiContexts) {
3209 v8::HandleScope scope;
3210 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3211 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3212
3213 Local<String> password = v8_str("Password");
3214
3215 // Create an environment
3216 LocalContext context0(0, templ);
3217 context0->SetSecurityToken(password);
3218 v8::Handle<v8::Object> global0 = context0->Global();
3219 global0->Set(v8_str("custom"), v8_num(1234));
3220 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3221
3222 // Create an independent environment
3223 LocalContext context1(0, templ);
3224 context1->SetSecurityToken(password);
3225 v8::Handle<v8::Object> global1 = context1->Global();
3226 global1->Set(v8_str("custom"), v8_num(1234));
3227 CHECK_NE(global0, global1);
3228 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3229 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3230
3231 // Now create a new context with the old global
3232 LocalContext context2(0, templ, global1);
3233 context2->SetSecurityToken(password);
3234 v8::Handle<v8::Object> global2 = context2->Global();
3235 CHECK_EQ(global1, global2);
3236 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3237 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3238}
3239
3240
3241THREADED_TEST(FunctionPrototypeAcrossContexts) {
3242 // Make sure that functions created by cloning boilerplates cannot
3243 // communicate through their __proto__ field.
3244
3245 v8::HandleScope scope;
3246
3247 LocalContext env0;
3248 v8::Handle<v8::Object> global0 =
3249 env0->Global();
3250 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003251 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003252 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003253 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003254 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003255 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003256 proto0->Set(v8_str("custom"), v8_num(1234));
3257
3258 LocalContext env1;
3259 v8::Handle<v8::Object> global1 =
3260 env1->Global();
3261 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003262 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003263 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003264 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003265 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003266 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003267 CHECK(!proto1->Has(v8_str("custom")));
3268}
3269
3270
3271THREADED_TEST(Regress892105) {
3272 // Make sure that object and array literals created by cloning
3273 // boilerplates cannot communicate through their __proto__
3274 // field. This is rather difficult to check, but we try to add stuff
3275 // to Object.prototype and Array.prototype and create a new
3276 // environment. This should succeed.
3277
3278 v8::HandleScope scope;
3279
3280 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3281 "Array.prototype.arr = 4567;"
3282 "8901");
3283
3284 LocalContext env0;
3285 Local<Script> script0 = Script::Compile(source);
3286 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3287
3288 LocalContext env1;
3289 Local<Script> script1 = Script::Compile(source);
3290 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3291}
3292
3293
Steve Blocka7e24c12009-10-30 11:49:00 +00003294THREADED_TEST(UndetectableObject) {
3295 v8::HandleScope scope;
3296 LocalContext env;
3297
3298 Local<v8::FunctionTemplate> desc =
3299 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3300 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3301
3302 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3303 env->Global()->Set(v8_str("undetectable"), obj);
3304
3305 ExpectString("undetectable.toString()", "[object Object]");
3306 ExpectString("typeof undetectable", "undefined");
3307 ExpectString("typeof(undetectable)", "undefined");
3308 ExpectBoolean("typeof undetectable == 'undefined'", true);
3309 ExpectBoolean("typeof undetectable == 'object'", false);
3310 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3311 ExpectBoolean("!undetectable", true);
3312
3313 ExpectObject("true&&undetectable", obj);
3314 ExpectBoolean("false&&undetectable", false);
3315 ExpectBoolean("true||undetectable", true);
3316 ExpectObject("false||undetectable", obj);
3317
3318 ExpectObject("undetectable&&true", obj);
3319 ExpectObject("undetectable&&false", obj);
3320 ExpectBoolean("undetectable||true", true);
3321 ExpectBoolean("undetectable||false", false);
3322
3323 ExpectBoolean("undetectable==null", true);
3324 ExpectBoolean("null==undetectable", true);
3325 ExpectBoolean("undetectable==undefined", true);
3326 ExpectBoolean("undefined==undetectable", true);
3327 ExpectBoolean("undetectable==undetectable", true);
3328
3329
3330 ExpectBoolean("undetectable===null", false);
3331 ExpectBoolean("null===undetectable", false);
3332 ExpectBoolean("undetectable===undefined", false);
3333 ExpectBoolean("undefined===undetectable", false);
3334 ExpectBoolean("undetectable===undetectable", true);
3335}
3336
3337
3338THREADED_TEST(UndetectableString) {
3339 v8::HandleScope scope;
3340 LocalContext env;
3341
3342 Local<String> obj = String::NewUndetectable("foo");
3343 env->Global()->Set(v8_str("undetectable"), obj);
3344
3345 ExpectString("undetectable", "foo");
3346 ExpectString("typeof undetectable", "undefined");
3347 ExpectString("typeof(undetectable)", "undefined");
3348 ExpectBoolean("typeof undetectable == 'undefined'", true);
3349 ExpectBoolean("typeof undetectable == 'string'", false);
3350 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3351 ExpectBoolean("!undetectable", true);
3352
3353 ExpectObject("true&&undetectable", obj);
3354 ExpectBoolean("false&&undetectable", false);
3355 ExpectBoolean("true||undetectable", true);
3356 ExpectObject("false||undetectable", obj);
3357
3358 ExpectObject("undetectable&&true", obj);
3359 ExpectObject("undetectable&&false", obj);
3360 ExpectBoolean("undetectable||true", true);
3361 ExpectBoolean("undetectable||false", false);
3362
3363 ExpectBoolean("undetectable==null", true);
3364 ExpectBoolean("null==undetectable", true);
3365 ExpectBoolean("undetectable==undefined", true);
3366 ExpectBoolean("undefined==undetectable", true);
3367 ExpectBoolean("undetectable==undetectable", true);
3368
3369
3370 ExpectBoolean("undetectable===null", false);
3371 ExpectBoolean("null===undetectable", false);
3372 ExpectBoolean("undetectable===undefined", false);
3373 ExpectBoolean("undefined===undetectable", false);
3374 ExpectBoolean("undetectable===undetectable", true);
3375}
3376
3377
3378template <typename T> static void USE(T) { }
3379
3380
3381// This test is not intended to be run, just type checked.
3382static void PersistentHandles() {
3383 USE(PersistentHandles);
3384 Local<String> str = v8_str("foo");
3385 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3386 USE(p_str);
3387 Local<Script> scr = Script::Compile(v8_str(""));
3388 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3389 USE(p_scr);
3390 Local<ObjectTemplate> templ = ObjectTemplate::New();
3391 v8::Persistent<ObjectTemplate> p_templ =
3392 v8::Persistent<ObjectTemplate>::New(templ);
3393 USE(p_templ);
3394}
3395
3396
3397static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3398 ApiTestFuzzer::Fuzz();
3399 return v8::Undefined();
3400}
3401
3402
3403THREADED_TEST(GlobalObjectTemplate) {
3404 v8::HandleScope handle_scope;
3405 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3406 global_template->Set(v8_str("JSNI_Log"),
3407 v8::FunctionTemplate::New(HandleLogDelegator));
3408 v8::Persistent<Context> context = Context::New(0, global_template);
3409 Context::Scope context_scope(context);
3410 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3411 context.Dispose();
3412}
3413
3414
3415static const char* kSimpleExtensionSource =
3416 "function Foo() {"
3417 " return 4;"
3418 "}";
3419
3420
3421THREADED_TEST(SimpleExtensions) {
3422 v8::HandleScope handle_scope;
3423 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3424 const char* extension_names[] = { "simpletest" };
3425 v8::ExtensionConfiguration extensions(1, extension_names);
3426 v8::Handle<Context> context = Context::New(&extensions);
3427 Context::Scope lock(context);
3428 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3429 CHECK_EQ(result, v8::Integer::New(4));
3430}
3431
3432
3433static const char* kEvalExtensionSource1 =
3434 "function UseEval1() {"
3435 " var x = 42;"
3436 " return eval('x');"
3437 "}";
3438
3439
3440static const char* kEvalExtensionSource2 =
3441 "(function() {"
3442 " var x = 42;"
3443 " function e() {"
3444 " return eval('x');"
3445 " }"
3446 " this.UseEval2 = e;"
3447 "})()";
3448
3449
3450THREADED_TEST(UseEvalFromExtension) {
3451 v8::HandleScope handle_scope;
3452 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3453 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3454 const char* extension_names[] = { "evaltest1", "evaltest2" };
3455 v8::ExtensionConfiguration extensions(2, extension_names);
3456 v8::Handle<Context> context = Context::New(&extensions);
3457 Context::Scope lock(context);
3458 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3459 CHECK_EQ(result, v8::Integer::New(42));
3460 result = Script::Compile(v8_str("UseEval2()"))->Run();
3461 CHECK_EQ(result, v8::Integer::New(42));
3462}
3463
3464
3465static const char* kWithExtensionSource1 =
3466 "function UseWith1() {"
3467 " var x = 42;"
3468 " with({x:87}) { return x; }"
3469 "}";
3470
3471
3472
3473static const char* kWithExtensionSource2 =
3474 "(function() {"
3475 " var x = 42;"
3476 " function e() {"
3477 " with ({x:87}) { return x; }"
3478 " }"
3479 " this.UseWith2 = e;"
3480 "})()";
3481
3482
3483THREADED_TEST(UseWithFromExtension) {
3484 v8::HandleScope handle_scope;
3485 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3486 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3487 const char* extension_names[] = { "withtest1", "withtest2" };
3488 v8::ExtensionConfiguration extensions(2, extension_names);
3489 v8::Handle<Context> context = Context::New(&extensions);
3490 Context::Scope lock(context);
3491 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3492 CHECK_EQ(result, v8::Integer::New(87));
3493 result = Script::Compile(v8_str("UseWith2()"))->Run();
3494 CHECK_EQ(result, v8::Integer::New(87));
3495}
3496
3497
3498THREADED_TEST(AutoExtensions) {
3499 v8::HandleScope handle_scope;
3500 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3501 extension->set_auto_enable(true);
3502 v8::RegisterExtension(extension);
3503 v8::Handle<Context> context = Context::New();
3504 Context::Scope lock(context);
3505 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3506 CHECK_EQ(result, v8::Integer::New(4));
3507}
3508
3509
Steve Blockd0582a62009-12-15 09:54:21 +00003510static const char* kSyntaxErrorInExtensionSource =
3511 "[";
3512
3513
3514// Test that a syntax error in an extension does not cause a fatal
3515// error but results in an empty context.
3516THREADED_TEST(SyntaxErrorExtensions) {
3517 v8::HandleScope handle_scope;
3518 v8::RegisterExtension(new Extension("syntaxerror",
3519 kSyntaxErrorInExtensionSource));
3520 const char* extension_names[] = { "syntaxerror" };
3521 v8::ExtensionConfiguration extensions(1, extension_names);
3522 v8::Handle<Context> context = Context::New(&extensions);
3523 CHECK(context.IsEmpty());
3524}
3525
3526
3527static const char* kExceptionInExtensionSource =
3528 "throw 42";
3529
3530
3531// Test that an exception when installing an extension does not cause
3532// a fatal error but results in an empty context.
3533THREADED_TEST(ExceptionExtensions) {
3534 v8::HandleScope handle_scope;
3535 v8::RegisterExtension(new Extension("exception",
3536 kExceptionInExtensionSource));
3537 const char* extension_names[] = { "exception" };
3538 v8::ExtensionConfiguration extensions(1, extension_names);
3539 v8::Handle<Context> context = Context::New(&extensions);
3540 CHECK(context.IsEmpty());
3541}
3542
3543
Steve Blocka7e24c12009-10-30 11:49:00 +00003544static void CheckDependencies(const char* name, const char* expected) {
3545 v8::HandleScope handle_scope;
3546 v8::ExtensionConfiguration config(1, &name);
3547 LocalContext context(&config);
3548 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3549}
3550
3551
3552/*
3553 * Configuration:
3554 *
3555 * /-- B <--\
3556 * A <- -- D <-- E
3557 * \-- C <--/
3558 */
3559THREADED_TEST(ExtensionDependency) {
3560 static const char* kEDeps[] = { "D" };
3561 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3562 static const char* kDDeps[] = { "B", "C" };
3563 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3564 static const char* kBCDeps[] = { "A" };
3565 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3566 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3567 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3568 CheckDependencies("A", "undefinedA");
3569 CheckDependencies("B", "undefinedAB");
3570 CheckDependencies("C", "undefinedAC");
3571 CheckDependencies("D", "undefinedABCD");
3572 CheckDependencies("E", "undefinedABCDE");
3573 v8::HandleScope handle_scope;
3574 static const char* exts[2] = { "C", "E" };
3575 v8::ExtensionConfiguration config(2, exts);
3576 LocalContext context(&config);
3577 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3578}
3579
3580
3581static const char* kExtensionTestScript =
3582 "native function A();"
3583 "native function B();"
3584 "native function C();"
3585 "function Foo(i) {"
3586 " if (i == 0) return A();"
3587 " if (i == 1) return B();"
3588 " if (i == 2) return C();"
3589 "}";
3590
3591
3592static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3593 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003594 if (args.IsConstructCall()) {
3595 args.This()->Set(v8_str("data"), args.Data());
3596 return v8::Null();
3597 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003598 return args.Data();
3599}
3600
3601
3602class FunctionExtension : public Extension {
3603 public:
3604 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3605 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3606 v8::Handle<String> name);
3607};
3608
3609
3610static int lookup_count = 0;
3611v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3612 v8::Handle<String> name) {
3613 lookup_count++;
3614 if (name->Equals(v8_str("A"))) {
3615 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3616 } else if (name->Equals(v8_str("B"))) {
3617 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3618 } else if (name->Equals(v8_str("C"))) {
3619 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3620 } else {
3621 return v8::Handle<v8::FunctionTemplate>();
3622 }
3623}
3624
3625
3626THREADED_TEST(FunctionLookup) {
3627 v8::RegisterExtension(new FunctionExtension());
3628 v8::HandleScope handle_scope;
3629 static const char* exts[1] = { "functiontest" };
3630 v8::ExtensionConfiguration config(1, exts);
3631 LocalContext context(&config);
3632 CHECK_EQ(3, lookup_count);
3633 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3634 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3635 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3636}
3637
3638
Leon Clarkee46be812010-01-19 14:06:41 +00003639THREADED_TEST(NativeFunctionConstructCall) {
3640 v8::RegisterExtension(new FunctionExtension());
3641 v8::HandleScope handle_scope;
3642 static const char* exts[1] = { "functiontest" };
3643 v8::ExtensionConfiguration config(1, exts);
3644 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003645 for (int i = 0; i < 10; i++) {
3646 // Run a few times to ensure that allocation of objects doesn't
3647 // change behavior of a constructor function.
3648 CHECK_EQ(v8::Integer::New(8),
3649 Script::Compile(v8_str("(new A()).data"))->Run());
3650 CHECK_EQ(v8::Integer::New(7),
3651 Script::Compile(v8_str("(new B()).data"))->Run());
3652 CHECK_EQ(v8::Integer::New(6),
3653 Script::Compile(v8_str("(new C()).data"))->Run());
3654 }
Leon Clarkee46be812010-01-19 14:06:41 +00003655}
3656
3657
Steve Blocka7e24c12009-10-30 11:49:00 +00003658static const char* last_location;
3659static const char* last_message;
3660void StoringErrorCallback(const char* location, const char* message) {
3661 if (last_location == NULL) {
3662 last_location = location;
3663 last_message = message;
3664 }
3665}
3666
3667
3668// ErrorReporting creates a circular extensions configuration and
3669// tests that the fatal error handler gets called. This renders V8
3670// unusable and therefore this test cannot be run in parallel.
3671TEST(ErrorReporting) {
3672 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3673 static const char* aDeps[] = { "B" };
3674 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3675 static const char* bDeps[] = { "A" };
3676 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3677 last_location = NULL;
3678 v8::ExtensionConfiguration config(1, bDeps);
3679 v8::Handle<Context> context = Context::New(&config);
3680 CHECK(context.IsEmpty());
3681 CHECK_NE(last_location, NULL);
3682}
3683
3684
3685static const char* js_code_causing_huge_string_flattening =
3686 "var str = 'X';"
3687 "for (var i = 0; i < 30; i++) {"
3688 " str = str + str;"
3689 "}"
3690 "str.match(/X/);";
3691
3692
3693void OOMCallback(const char* location, const char* message) {
3694 exit(0);
3695}
3696
3697
3698TEST(RegexpOutOfMemory) {
3699 // Execute a script that causes out of memory when flattening a string.
3700 v8::HandleScope scope;
3701 v8::V8::SetFatalErrorHandler(OOMCallback);
3702 LocalContext context;
3703 Local<Script> script =
3704 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3705 last_location = NULL;
3706 Local<Value> result = script->Run();
3707
3708 CHECK(false); // Should not return.
3709}
3710
3711
3712static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3713 v8::Handle<Value> data) {
3714 CHECK_EQ(v8::Undefined(), data);
3715 CHECK(message->GetScriptResourceName()->IsUndefined());
3716 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3717 message->GetLineNumber();
3718 message->GetSourceLine();
3719}
3720
3721
3722THREADED_TEST(ErrorWithMissingScriptInfo) {
3723 v8::HandleScope scope;
3724 LocalContext context;
3725 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3726 Script::Compile(v8_str("throw Error()"))->Run();
3727 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3728}
3729
3730
3731int global_index = 0;
3732
3733class Snorkel {
3734 public:
3735 Snorkel() { index_ = global_index++; }
3736 int index_;
3737};
3738
3739class Whammy {
3740 public:
3741 Whammy() {
3742 cursor_ = 0;
3743 }
3744 ~Whammy() {
3745 script_.Dispose();
3746 }
3747 v8::Handle<Script> getScript() {
3748 if (script_.IsEmpty())
3749 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3750 return Local<Script>(*script_);
3751 }
3752
3753 public:
3754 static const int kObjectCount = 256;
3755 int cursor_;
3756 v8::Persistent<v8::Object> objects_[kObjectCount];
3757 v8::Persistent<Script> script_;
3758};
3759
3760static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3761 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3762 delete snorkel;
3763 obj.ClearWeak();
3764}
3765
3766v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3767 const AccessorInfo& info) {
3768 Whammy* whammy =
3769 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3770
3771 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3772
3773 v8::Handle<v8::Object> obj = v8::Object::New();
3774 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3775 if (!prev.IsEmpty()) {
3776 prev->Set(v8_str("next"), obj);
3777 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3778 whammy->objects_[whammy->cursor_].Clear();
3779 }
3780 whammy->objects_[whammy->cursor_] = global;
3781 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3782 return whammy->getScript()->Run();
3783}
3784
3785THREADED_TEST(WeakReference) {
3786 v8::HandleScope handle_scope;
3787 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
3788 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3789 0, 0, 0, 0,
3790 v8::External::New(new Whammy()));
3791 const char* extension_list[] = { "v8/gc" };
3792 v8::ExtensionConfiguration extensions(1, extension_list);
3793 v8::Persistent<Context> context = Context::New(&extensions);
3794 Context::Scope context_scope(context);
3795
3796 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3797 context->Global()->Set(v8_str("whammy"), interceptor);
3798 const char* code =
3799 "var last;"
3800 "for (var i = 0; i < 10000; i++) {"
3801 " var obj = whammy.length;"
3802 " if (last) last.next = obj;"
3803 " last = obj;"
3804 "}"
3805 "gc();"
3806 "4";
3807 v8::Handle<Value> result = CompileRun(code);
3808 CHECK_EQ(4.0, result->NumberValue());
3809
3810 context.Dispose();
3811}
3812
3813
Steve Blockd0582a62009-12-15 09:54:21 +00003814static bool in_scavenge = false;
3815static int last = -1;
3816
3817static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3818 CHECK_EQ(-1, last);
3819 last = 0;
3820 obj.Dispose();
3821 obj.Clear();
3822 in_scavenge = true;
3823 i::Heap::PerformScavenge();
3824 in_scavenge = false;
3825 *(reinterpret_cast<bool*>(data)) = true;
3826}
3827
3828static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3829 void* data) {
3830 CHECK_EQ(0, last);
3831 last = 1;
3832 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3833 obj.Dispose();
3834 obj.Clear();
3835}
3836
3837THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3838 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3839 // Calling callbacks from scavenges is unsafe as objects held by those
3840 // handlers might have become strongly reachable, but scavenge doesn't
3841 // check that.
3842 v8::Persistent<Context> context = Context::New();
3843 Context::Scope context_scope(context);
3844
3845 v8::Persistent<v8::Object> object_a;
3846 v8::Persistent<v8::Object> object_b;
3847
3848 {
3849 v8::HandleScope handle_scope;
3850 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
3851 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
3852 }
3853
3854 bool object_a_disposed = false;
3855 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
3856 bool released_in_scavenge = false;
3857 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
3858
3859 while (!object_a_disposed) {
3860 i::Heap::CollectAllGarbage(false);
3861 }
3862 CHECK(!released_in_scavenge);
3863}
3864
3865
Steve Blocka7e24c12009-10-30 11:49:00 +00003866v8::Handle<Function> args_fun;
3867
3868
3869static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3870 ApiTestFuzzer::Fuzz();
3871 CHECK_EQ(args_fun, args.Callee());
3872 CHECK_EQ(3, args.Length());
3873 CHECK_EQ(v8::Integer::New(1), args[0]);
3874 CHECK_EQ(v8::Integer::New(2), args[1]);
3875 CHECK_EQ(v8::Integer::New(3), args[2]);
3876 CHECK_EQ(v8::Undefined(), args[3]);
3877 v8::HandleScope scope;
3878 i::Heap::CollectAllGarbage(false);
3879 return v8::Undefined();
3880}
3881
3882
3883THREADED_TEST(Arguments) {
3884 v8::HandleScope scope;
3885 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3886 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3887 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01003888 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003889 v8_compile("f(1, 2, 3)")->Run();
3890}
3891
3892
Steve Blocka7e24c12009-10-30 11:49:00 +00003893static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3894 const AccessorInfo&) {
3895 return v8::Handle<Value>();
3896}
3897
3898
3899static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3900 const AccessorInfo&) {
3901 return v8::Handle<Value>();
3902}
3903
3904
3905static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3906 const AccessorInfo&) {
3907 if (!name->Equals(v8_str("foo"))) {
3908 return v8::Handle<v8::Boolean>(); // not intercepted
3909 }
3910
3911 return v8::False(); // intercepted, and don't delete the property
3912}
3913
3914
3915static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
3916 if (index != 2) {
3917 return v8::Handle<v8::Boolean>(); // not intercepted
3918 }
3919
3920 return v8::False(); // intercepted, and don't delete the property
3921}
3922
3923
3924THREADED_TEST(Deleter) {
3925 v8::HandleScope scope;
3926 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3927 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
3928 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
3929 LocalContext context;
3930 context->Global()->Set(v8_str("k"), obj->NewInstance());
3931 CompileRun(
3932 "k.foo = 'foo';"
3933 "k.bar = 'bar';"
3934 "k[2] = 2;"
3935 "k[4] = 4;");
3936 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
3937 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
3938
3939 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
3940 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
3941
3942 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
3943 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
3944
3945 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
3946 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
3947}
3948
3949
3950static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
3951 ApiTestFuzzer::Fuzz();
3952 if (name->Equals(v8_str("foo")) ||
3953 name->Equals(v8_str("bar")) ||
3954 name->Equals(v8_str("baz"))) {
3955 return v8::Undefined();
3956 }
3957 return v8::Handle<Value>();
3958}
3959
3960
3961static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
3962 ApiTestFuzzer::Fuzz();
3963 if (index == 0 || index == 1) return v8::Undefined();
3964 return v8::Handle<Value>();
3965}
3966
3967
3968static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
3969 ApiTestFuzzer::Fuzz();
3970 v8::Handle<v8::Array> result = v8::Array::New(3);
3971 result->Set(v8::Integer::New(0), v8_str("foo"));
3972 result->Set(v8::Integer::New(1), v8_str("bar"));
3973 result->Set(v8::Integer::New(2), v8_str("baz"));
3974 return result;
3975}
3976
3977
3978static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
3979 ApiTestFuzzer::Fuzz();
3980 v8::Handle<v8::Array> result = v8::Array::New(2);
3981 result->Set(v8::Integer::New(0), v8_str("0"));
3982 result->Set(v8::Integer::New(1), v8_str("1"));
3983 return result;
3984}
3985
3986
3987THREADED_TEST(Enumerators) {
3988 v8::HandleScope scope;
3989 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3990 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
3991 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
3992 LocalContext context;
3993 context->Global()->Set(v8_str("k"), obj->NewInstance());
3994 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
3995 "k[10] = 0;"
3996 "k.a = 0;"
3997 "k[5] = 0;"
3998 "k.b = 0;"
3999 "k[4294967295] = 0;"
4000 "k.c = 0;"
4001 "k[4294967296] = 0;"
4002 "k.d = 0;"
4003 "k[140000] = 0;"
4004 "k.e = 0;"
4005 "k[30000000000] = 0;"
4006 "k.f = 0;"
4007 "var result = [];"
4008 "for (var prop in k) {"
4009 " result.push(prop);"
4010 "}"
4011 "result"));
4012 // Check that we get all the property names returned including the
4013 // ones from the enumerators in the right order: indexed properties
4014 // in numerical order, indexed interceptor properties, named
4015 // properties in insertion order, named interceptor properties.
4016 // This order is not mandated by the spec, so this test is just
4017 // documenting our behavior.
4018 CHECK_EQ(17, result->Length());
4019 // Indexed properties in numerical order.
4020 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4021 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4022 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4023 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4024 // Indexed interceptor properties in the order they are returned
4025 // from the enumerator interceptor.
4026 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4027 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4028 // Named properties in insertion order.
4029 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4030 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4031 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4032 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4033 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4034 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4035 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4036 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4037 // Named interceptor properties.
4038 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4039 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4040 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4041}
4042
4043
4044int p_getter_count;
4045int p_getter_count2;
4046
4047
4048static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4049 ApiTestFuzzer::Fuzz();
4050 p_getter_count++;
4051 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4052 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4053 if (name->Equals(v8_str("p1"))) {
4054 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4055 } else if (name->Equals(v8_str("p2"))) {
4056 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4057 } else if (name->Equals(v8_str("p3"))) {
4058 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4059 } else if (name->Equals(v8_str("p4"))) {
4060 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4061 }
4062 return v8::Undefined();
4063}
4064
4065
4066static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4067 ApiTestFuzzer::Fuzz();
4068 LocalContext context;
4069 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4070 CompileRun(
4071 "o1.__proto__ = { };"
4072 "var o2 = { __proto__: o1 };"
4073 "var o3 = { __proto__: o2 };"
4074 "var o4 = { __proto__: o3 };"
4075 "for (var i = 0; i < 10; i++) o4.p4;"
4076 "for (var i = 0; i < 10; i++) o3.p3;"
4077 "for (var i = 0; i < 10; i++) o2.p2;"
4078 "for (var i = 0; i < 10; i++) o1.p1;");
4079}
4080
4081
4082static v8::Handle<Value> PGetter2(Local<String> name,
4083 const AccessorInfo& info) {
4084 ApiTestFuzzer::Fuzz();
4085 p_getter_count2++;
4086 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4087 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4088 if (name->Equals(v8_str("p1"))) {
4089 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4090 } else if (name->Equals(v8_str("p2"))) {
4091 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4092 } else if (name->Equals(v8_str("p3"))) {
4093 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4094 } else if (name->Equals(v8_str("p4"))) {
4095 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4096 }
4097 return v8::Undefined();
4098}
4099
4100
4101THREADED_TEST(GetterHolders) {
4102 v8::HandleScope scope;
4103 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4104 obj->SetAccessor(v8_str("p1"), PGetter);
4105 obj->SetAccessor(v8_str("p2"), PGetter);
4106 obj->SetAccessor(v8_str("p3"), PGetter);
4107 obj->SetAccessor(v8_str("p4"), PGetter);
4108 p_getter_count = 0;
4109 RunHolderTest(obj);
4110 CHECK_EQ(40, p_getter_count);
4111}
4112
4113
4114THREADED_TEST(PreInterceptorHolders) {
4115 v8::HandleScope scope;
4116 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4117 obj->SetNamedPropertyHandler(PGetter2);
4118 p_getter_count2 = 0;
4119 RunHolderTest(obj);
4120 CHECK_EQ(40, p_getter_count2);
4121}
4122
4123
4124THREADED_TEST(ObjectInstantiation) {
4125 v8::HandleScope scope;
4126 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4127 templ->SetAccessor(v8_str("t"), PGetter2);
4128 LocalContext context;
4129 context->Global()->Set(v8_str("o"), templ->NewInstance());
4130 for (int i = 0; i < 100; i++) {
4131 v8::HandleScope inner_scope;
4132 v8::Handle<v8::Object> obj = templ->NewInstance();
4133 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4134 context->Global()->Set(v8_str("o2"), obj);
4135 v8::Handle<Value> value =
4136 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4137 CHECK_EQ(v8::True(), value);
4138 context->Global()->Set(v8_str("o"), obj);
4139 }
4140}
4141
4142
4143THREADED_TEST(StringWrite) {
4144 v8::HandleScope scope;
4145 v8::Handle<String> str = v8_str("abcde");
4146
4147 char buf[100];
4148 int len;
4149
4150 memset(buf, 0x1, sizeof(buf));
4151 len = str->WriteAscii(buf);
4152 CHECK_EQ(len, 5);
4153 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4154
4155 memset(buf, 0x1, sizeof(buf));
4156 len = str->WriteAscii(buf, 0, 4);
4157 CHECK_EQ(len, 4);
4158 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4159
4160 memset(buf, 0x1, sizeof(buf));
4161 len = str->WriteAscii(buf, 0, 5);
4162 CHECK_EQ(len, 5);
4163 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4164
4165 memset(buf, 0x1, sizeof(buf));
4166 len = str->WriteAscii(buf, 0, 6);
4167 CHECK_EQ(len, 5);
4168 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4169
4170 memset(buf, 0x1, sizeof(buf));
4171 len = str->WriteAscii(buf, 4, -1);
4172 CHECK_EQ(len, 1);
4173 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4174
4175 memset(buf, 0x1, sizeof(buf));
4176 len = str->WriteAscii(buf, 4, 6);
4177 CHECK_EQ(len, 1);
4178 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4179
4180 memset(buf, 0x1, sizeof(buf));
4181 len = str->WriteAscii(buf, 4, 1);
4182 CHECK_EQ(len, 1);
4183 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4184}
4185
4186
4187THREADED_TEST(ToArrayIndex) {
4188 v8::HandleScope scope;
4189 LocalContext context;
4190
4191 v8::Handle<String> str = v8_str("42");
4192 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4193 CHECK(!index.IsEmpty());
4194 CHECK_EQ(42.0, index->Uint32Value());
4195 str = v8_str("42asdf");
4196 index = str->ToArrayIndex();
4197 CHECK(index.IsEmpty());
4198 str = v8_str("-42");
4199 index = str->ToArrayIndex();
4200 CHECK(index.IsEmpty());
4201 str = v8_str("4294967295");
4202 index = str->ToArrayIndex();
4203 CHECK(!index.IsEmpty());
4204 CHECK_EQ(4294967295.0, index->Uint32Value());
4205 v8::Handle<v8::Number> num = v8::Number::New(1);
4206 index = num->ToArrayIndex();
4207 CHECK(!index.IsEmpty());
4208 CHECK_EQ(1.0, index->Uint32Value());
4209 num = v8::Number::New(-1);
4210 index = num->ToArrayIndex();
4211 CHECK(index.IsEmpty());
4212 v8::Handle<v8::Object> obj = v8::Object::New();
4213 index = obj->ToArrayIndex();
4214 CHECK(index.IsEmpty());
4215}
4216
4217
4218THREADED_TEST(ErrorConstruction) {
4219 v8::HandleScope scope;
4220 LocalContext context;
4221
4222 v8::Handle<String> foo = v8_str("foo");
4223 v8::Handle<String> message = v8_str("message");
4224 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4225 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004226 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4227 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004228 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4229 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004230 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004231 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4232 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004233 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004234 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4235 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004236 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004237 v8::Handle<Value> error = v8::Exception::Error(foo);
4238 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004239 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004240}
4241
4242
4243static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4244 ApiTestFuzzer::Fuzz();
4245 return v8_num(10);
4246}
4247
4248
4249static void YSetter(Local<String> name,
4250 Local<Value> value,
4251 const AccessorInfo& info) {
4252 if (info.This()->Has(name)) {
4253 info.This()->Delete(name);
4254 }
4255 info.This()->Set(name, value);
4256}
4257
4258
4259THREADED_TEST(DeleteAccessor) {
4260 v8::HandleScope scope;
4261 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4262 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4263 LocalContext context;
4264 v8::Handle<v8::Object> holder = obj->NewInstance();
4265 context->Global()->Set(v8_str("holder"), holder);
4266 v8::Handle<Value> result = CompileRun(
4267 "holder.y = 11; holder.y = 12; holder.y");
4268 CHECK_EQ(12, result->Uint32Value());
4269}
4270
4271
4272THREADED_TEST(TypeSwitch) {
4273 v8::HandleScope scope;
4274 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4275 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4276 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4277 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4278 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4279 LocalContext context;
4280 v8::Handle<v8::Object> obj0 = v8::Object::New();
4281 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4282 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4283 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4284 for (int i = 0; i < 10; i++) {
4285 CHECK_EQ(0, type_switch->match(obj0));
4286 CHECK_EQ(1, type_switch->match(obj1));
4287 CHECK_EQ(2, type_switch->match(obj2));
4288 CHECK_EQ(3, type_switch->match(obj3));
4289 CHECK_EQ(3, type_switch->match(obj3));
4290 CHECK_EQ(2, type_switch->match(obj2));
4291 CHECK_EQ(1, type_switch->match(obj1));
4292 CHECK_EQ(0, type_switch->match(obj0));
4293 }
4294}
4295
4296
4297// For use within the TestSecurityHandler() test.
4298static bool g_security_callback_result = false;
4299static bool NamedSecurityTestCallback(Local<v8::Object> global,
4300 Local<Value> name,
4301 v8::AccessType type,
4302 Local<Value> data) {
4303 // Always allow read access.
4304 if (type == v8::ACCESS_GET)
4305 return true;
4306
4307 // Sometimes allow other access.
4308 return g_security_callback_result;
4309}
4310
4311
4312static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4313 uint32_t key,
4314 v8::AccessType type,
4315 Local<Value> data) {
4316 // Always allow read access.
4317 if (type == v8::ACCESS_GET)
4318 return true;
4319
4320 // Sometimes allow other access.
4321 return g_security_callback_result;
4322}
4323
4324
4325static int trouble_nesting = 0;
4326static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4327 ApiTestFuzzer::Fuzz();
4328 trouble_nesting++;
4329
4330 // Call a JS function that throws an uncaught exception.
4331 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4332 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4333 arg_this->Get(v8_str("trouble_callee")) :
4334 arg_this->Get(v8_str("trouble_caller"));
4335 CHECK(trouble_callee->IsFunction());
4336 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4337}
4338
4339
4340static int report_count = 0;
4341static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4342 v8::Handle<Value>) {
4343 report_count++;
4344}
4345
4346
4347// Counts uncaught exceptions, but other tests running in parallel
4348// also have uncaught exceptions.
4349TEST(ApiUncaughtException) {
4350 report_count = 0;
4351 v8::HandleScope scope;
4352 LocalContext env;
4353 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4354
4355 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4356 v8::Local<v8::Object> global = env->Global();
4357 global->Set(v8_str("trouble"), fun->GetFunction());
4358
4359 Script::Compile(v8_str("function trouble_callee() {"
4360 " var x = null;"
4361 " return x.foo;"
4362 "};"
4363 "function trouble_caller() {"
4364 " trouble();"
4365 "};"))->Run();
4366 Local<Value> trouble = global->Get(v8_str("trouble"));
4367 CHECK(trouble->IsFunction());
4368 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4369 CHECK(trouble_callee->IsFunction());
4370 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4371 CHECK(trouble_caller->IsFunction());
4372 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4373 CHECK_EQ(1, report_count);
4374 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4375}
4376
Leon Clarke4515c472010-02-03 11:58:03 +00004377static const char* script_resource_name = "ExceptionInNativeScript.js";
4378static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4379 v8::Handle<Value>) {
4380 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4381 CHECK(!name_val.IsEmpty() && name_val->IsString());
4382 v8::String::AsciiValue name(message->GetScriptResourceName());
4383 CHECK_EQ(script_resource_name, *name);
4384 CHECK_EQ(3, message->GetLineNumber());
4385 v8::String::AsciiValue source_line(message->GetSourceLine());
4386 CHECK_EQ(" new o.foo();", *source_line);
4387}
4388
4389TEST(ExceptionInNativeScript) {
4390 v8::HandleScope scope;
4391 LocalContext env;
4392 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4393
4394 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4395 v8::Local<v8::Object> global = env->Global();
4396 global->Set(v8_str("trouble"), fun->GetFunction());
4397
4398 Script::Compile(v8_str("function trouble() {\n"
4399 " var o = {};\n"
4400 " new o.foo();\n"
4401 "};"), v8::String::New(script_resource_name))->Run();
4402 Local<Value> trouble = global->Get(v8_str("trouble"));
4403 CHECK(trouble->IsFunction());
4404 Function::Cast(*trouble)->Call(global, 0, NULL);
4405 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4406}
4407
Steve Blocka7e24c12009-10-30 11:49:00 +00004408
4409TEST(CompilationErrorUsingTryCatchHandler) {
4410 v8::HandleScope scope;
4411 LocalContext env;
4412 v8::TryCatch try_catch;
4413 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4414 CHECK_NE(NULL, *try_catch.Exception());
4415 CHECK(try_catch.HasCaught());
4416}
4417
4418
4419TEST(TryCatchFinallyUsingTryCatchHandler) {
4420 v8::HandleScope scope;
4421 LocalContext env;
4422 v8::TryCatch try_catch;
4423 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4424 CHECK(!try_catch.HasCaught());
4425 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4426 CHECK(try_catch.HasCaught());
4427 try_catch.Reset();
4428 Script::Compile(v8_str("(function() {"
4429 "try { throw ''; } finally { return; }"
4430 "})()"))->Run();
4431 CHECK(!try_catch.HasCaught());
4432 Script::Compile(v8_str("(function()"
4433 " { try { throw ''; } finally { throw 0; }"
4434 "})()"))->Run();
4435 CHECK(try_catch.HasCaught());
4436}
4437
4438
4439// SecurityHandler can't be run twice
4440TEST(SecurityHandler) {
4441 v8::HandleScope scope0;
4442 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4443 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4444 IndexedSecurityTestCallback);
4445 // Create an environment
4446 v8::Persistent<Context> context0 =
4447 Context::New(NULL, global_template);
4448 context0->Enter();
4449
4450 v8::Handle<v8::Object> global0 = context0->Global();
4451 v8::Handle<Script> script0 = v8_compile("foo = 111");
4452 script0->Run();
4453 global0->Set(v8_str("0"), v8_num(999));
4454 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4455 CHECK_EQ(111, foo0->Int32Value());
4456 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4457 CHECK_EQ(999, z0->Int32Value());
4458
4459 // Create another environment, should fail security checks.
4460 v8::HandleScope scope1;
4461
4462 v8::Persistent<Context> context1 =
4463 Context::New(NULL, global_template);
4464 context1->Enter();
4465
4466 v8::Handle<v8::Object> global1 = context1->Global();
4467 global1->Set(v8_str("othercontext"), global0);
4468 // This set will fail the security check.
4469 v8::Handle<Script> script1 =
4470 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4471 script1->Run();
4472 // This read will pass the security check.
4473 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4474 CHECK_EQ(111, foo1->Int32Value());
4475 // This read will pass the security check.
4476 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4477 CHECK_EQ(999, z1->Int32Value());
4478
4479 // Create another environment, should pass security checks.
4480 { g_security_callback_result = true; // allow security handler to pass.
4481 v8::HandleScope scope2;
4482 LocalContext context2;
4483 v8::Handle<v8::Object> global2 = context2->Global();
4484 global2->Set(v8_str("othercontext"), global0);
4485 v8::Handle<Script> script2 =
4486 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4487 script2->Run();
4488 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4489 CHECK_EQ(333, foo2->Int32Value());
4490 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4491 CHECK_EQ(888, z2->Int32Value());
4492 }
4493
4494 context1->Exit();
4495 context1.Dispose();
4496
4497 context0->Exit();
4498 context0.Dispose();
4499}
4500
4501
4502THREADED_TEST(SecurityChecks) {
4503 v8::HandleScope handle_scope;
4504 LocalContext env1;
4505 v8::Persistent<Context> env2 = Context::New();
4506
4507 Local<Value> foo = v8_str("foo");
4508 Local<Value> bar = v8_str("bar");
4509
4510 // Set to the same domain.
4511 env1->SetSecurityToken(foo);
4512
4513 // Create a function in env1.
4514 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4515 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4516 CHECK(spy->IsFunction());
4517
4518 // Create another function accessing global objects.
4519 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4520 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4521 CHECK(spy2->IsFunction());
4522
4523 // Switch to env2 in the same domain and invoke spy on env2.
4524 {
4525 env2->SetSecurityToken(foo);
4526 // Enter env2
4527 Context::Scope scope_env2(env2);
4528 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4529 CHECK(result->IsFunction());
4530 }
4531
4532 {
4533 env2->SetSecurityToken(bar);
4534 Context::Scope scope_env2(env2);
4535
4536 // Call cross_domain_call, it should throw an exception
4537 v8::TryCatch try_catch;
4538 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4539 CHECK(try_catch.HasCaught());
4540 }
4541
4542 env2.Dispose();
4543}
4544
4545
4546// Regression test case for issue 1183439.
4547THREADED_TEST(SecurityChecksForPrototypeChain) {
4548 v8::HandleScope scope;
4549 LocalContext current;
4550 v8::Persistent<Context> other = Context::New();
4551
4552 // Change context to be able to get to the Object function in the
4553 // other context without hitting the security checks.
4554 v8::Local<Value> other_object;
4555 { Context::Scope scope(other);
4556 other_object = other->Global()->Get(v8_str("Object"));
4557 other->Global()->Set(v8_num(42), v8_num(87));
4558 }
4559
4560 current->Global()->Set(v8_str("other"), other->Global());
4561 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4562
4563 // Make sure the security check fails here and we get an undefined
4564 // result instead of getting the Object function. Repeat in a loop
4565 // to make sure to exercise the IC code.
4566 v8::Local<Script> access_other0 = v8_compile("other.Object");
4567 v8::Local<Script> access_other1 = v8_compile("other[42]");
4568 for (int i = 0; i < 5; i++) {
4569 CHECK(!access_other0->Run()->Equals(other_object));
4570 CHECK(access_other0->Run()->IsUndefined());
4571 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4572 CHECK(access_other1->Run()->IsUndefined());
4573 }
4574
4575 // Create an object that has 'other' in its prototype chain and make
4576 // sure we cannot access the Object function indirectly through
4577 // that. Repeat in a loop to make sure to exercise the IC code.
4578 v8_compile("function F() { };"
4579 "F.prototype = other;"
4580 "var f = new F();")->Run();
4581 v8::Local<Script> access_f0 = v8_compile("f.Object");
4582 v8::Local<Script> access_f1 = v8_compile("f[42]");
4583 for (int j = 0; j < 5; j++) {
4584 CHECK(!access_f0->Run()->Equals(other_object));
4585 CHECK(access_f0->Run()->IsUndefined());
4586 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4587 CHECK(access_f1->Run()->IsUndefined());
4588 }
4589
4590 // Now it gets hairy: Set the prototype for the other global object
4591 // to be the current global object. The prototype chain for 'f' now
4592 // goes through 'other' but ends up in the current global object.
4593 { Context::Scope scope(other);
4594 other->Global()->Set(v8_str("__proto__"), current->Global());
4595 }
4596 // Set a named and an index property on the current global
4597 // object. To force the lookup to go through the other global object,
4598 // the properties must not exist in the other global object.
4599 current->Global()->Set(v8_str("foo"), v8_num(100));
4600 current->Global()->Set(v8_num(99), v8_num(101));
4601 // Try to read the properties from f and make sure that the access
4602 // gets stopped by the security checks on the other global object.
4603 Local<Script> access_f2 = v8_compile("f.foo");
4604 Local<Script> access_f3 = v8_compile("f[99]");
4605 for (int k = 0; k < 5; k++) {
4606 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4607 CHECK(access_f2->Run()->IsUndefined());
4608 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4609 CHECK(access_f3->Run()->IsUndefined());
4610 }
4611 other.Dispose();
4612}
4613
4614
4615THREADED_TEST(CrossDomainDelete) {
4616 v8::HandleScope handle_scope;
4617 LocalContext env1;
4618 v8::Persistent<Context> env2 = Context::New();
4619
4620 Local<Value> foo = v8_str("foo");
4621 Local<Value> bar = v8_str("bar");
4622
4623 // Set to the same domain.
4624 env1->SetSecurityToken(foo);
4625 env2->SetSecurityToken(foo);
4626
4627 env1->Global()->Set(v8_str("prop"), v8_num(3));
4628 env2->Global()->Set(v8_str("env1"), env1->Global());
4629
4630 // Change env2 to a different domain and delete env1.prop.
4631 env2->SetSecurityToken(bar);
4632 {
4633 Context::Scope scope_env2(env2);
4634 Local<Value> result =
4635 Script::Compile(v8_str("delete env1.prop"))->Run();
4636 CHECK(result->IsFalse());
4637 }
4638
4639 // Check that env1.prop still exists.
4640 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4641 CHECK(v->IsNumber());
4642 CHECK_EQ(3, v->Int32Value());
4643
4644 env2.Dispose();
4645}
4646
4647
4648THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4649 v8::HandleScope handle_scope;
4650 LocalContext env1;
4651 v8::Persistent<Context> env2 = Context::New();
4652
4653 Local<Value> foo = v8_str("foo");
4654 Local<Value> bar = v8_str("bar");
4655
4656 // Set to the same domain.
4657 env1->SetSecurityToken(foo);
4658 env2->SetSecurityToken(foo);
4659
4660 env1->Global()->Set(v8_str("prop"), v8_num(3));
4661 env2->Global()->Set(v8_str("env1"), env1->Global());
4662
4663 // env1.prop is enumerable in env2.
4664 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4665 {
4666 Context::Scope scope_env2(env2);
4667 Local<Value> result = Script::Compile(test)->Run();
4668 CHECK(result->IsTrue());
4669 }
4670
4671 // Change env2 to a different domain and test again.
4672 env2->SetSecurityToken(bar);
4673 {
4674 Context::Scope scope_env2(env2);
4675 Local<Value> result = Script::Compile(test)->Run();
4676 CHECK(result->IsFalse());
4677 }
4678
4679 env2.Dispose();
4680}
4681
4682
4683THREADED_TEST(CrossDomainForIn) {
4684 v8::HandleScope handle_scope;
4685 LocalContext env1;
4686 v8::Persistent<Context> env2 = Context::New();
4687
4688 Local<Value> foo = v8_str("foo");
4689 Local<Value> bar = v8_str("bar");
4690
4691 // Set to the same domain.
4692 env1->SetSecurityToken(foo);
4693 env2->SetSecurityToken(foo);
4694
4695 env1->Global()->Set(v8_str("prop"), v8_num(3));
4696 env2->Global()->Set(v8_str("env1"), env1->Global());
4697
4698 // Change env2 to a different domain and set env1's global object
4699 // as the __proto__ of an object in env2 and enumerate properties
4700 // in for-in. It shouldn't enumerate properties on env1's global
4701 // object.
4702 env2->SetSecurityToken(bar);
4703 {
4704 Context::Scope scope_env2(env2);
4705 Local<Value> result =
4706 CompileRun("(function(){var obj = {'__proto__':env1};"
4707 "for (var p in obj)"
4708 " if (p == 'prop') return false;"
4709 "return true;})()");
4710 CHECK(result->IsTrue());
4711 }
4712 env2.Dispose();
4713}
4714
4715
4716TEST(ContextDetachGlobal) {
4717 v8::HandleScope handle_scope;
4718 LocalContext env1;
4719 v8::Persistent<Context> env2 = Context::New();
4720
4721 Local<v8::Object> global1 = env1->Global();
4722
4723 Local<Value> foo = v8_str("foo");
4724
4725 // Set to the same domain.
4726 env1->SetSecurityToken(foo);
4727 env2->SetSecurityToken(foo);
4728
4729 // Enter env2
4730 env2->Enter();
4731
Andrei Popescu74b3c142010-03-29 12:03:09 +01004732 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004733 Local<v8::Object> global2 = env2->Global();
4734 global2->Set(v8_str("prop"), v8::Integer::New(1));
4735 CompileRun("function getProp() {return prop;}");
4736
4737 env1->Global()->Set(v8_str("getProp"),
4738 global2->Get(v8_str("getProp")));
4739
Andrei Popescu74b3c142010-03-29 12:03:09 +01004740 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004741 env2->Exit();
4742 env2->DetachGlobal();
4743 // env2 has a new global object.
4744 CHECK(!env2->Global()->Equals(global2));
4745
4746 v8::Persistent<Context> env3 =
4747 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4748 env3->SetSecurityToken(v8_str("bar"));
4749 env3->Enter();
4750
4751 Local<v8::Object> global3 = env3->Global();
4752 CHECK_EQ(global2, global3);
4753 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4754 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4755 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4756 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4757 env3->Exit();
4758
4759 // Call getProp in env1, and it should return the value 1
4760 {
4761 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4762 CHECK(get_prop->IsFunction());
4763 v8::TryCatch try_catch;
4764 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4765 CHECK(!try_catch.HasCaught());
4766 CHECK_EQ(1, r->Int32Value());
4767 }
4768
4769 // Check that env3 is not accessible from env1
4770 {
4771 Local<Value> r = global3->Get(v8_str("prop2"));
4772 CHECK(r->IsUndefined());
4773 }
4774
4775 env2.Dispose();
4776 env3.Dispose();
4777}
4778
4779
Andrei Popescu74b3c142010-03-29 12:03:09 +01004780TEST(DetachAndReattachGlobal) {
4781 v8::HandleScope scope;
4782 LocalContext env1;
4783
4784 // Create second environment.
4785 v8::Persistent<Context> env2 = Context::New();
4786
4787 Local<Value> foo = v8_str("foo");
4788
4789 // Set same security token for env1 and env2.
4790 env1->SetSecurityToken(foo);
4791 env2->SetSecurityToken(foo);
4792
4793 // Create a property on the global object in env2.
4794 {
4795 v8::Context::Scope scope(env2);
4796 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4797 }
4798
4799 // Create a reference to env2 global from env1 global.
4800 env1->Global()->Set(v8_str("other"), env2->Global());
4801
4802 // Check that we have access to other.p in env2 from env1.
4803 Local<Value> result = CompileRun("other.p");
4804 CHECK(result->IsInt32());
4805 CHECK_EQ(42, result->Int32Value());
4806
4807 // Hold on to global from env2 and detach global from env2.
4808 Local<v8::Object> global2 = env2->Global();
4809 env2->DetachGlobal();
4810
4811 // Check that the global has been detached. No other.p property can
4812 // be found.
4813 result = CompileRun("other.p");
4814 CHECK(result->IsUndefined());
4815
4816 // Reuse global2 for env3.
4817 v8::Persistent<Context> env3 =
4818 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4819 CHECK_EQ(global2, env3->Global());
4820
4821 // Start by using the same security token for env3 as for env1 and env2.
4822 env3->SetSecurityToken(foo);
4823
4824 // Create a property on the global object in env3.
4825 {
4826 v8::Context::Scope scope(env3);
4827 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4828 }
4829
4830 // Check that other.p is now the property in env3 and that we have access.
4831 result = CompileRun("other.p");
4832 CHECK(result->IsInt32());
4833 CHECK_EQ(24, result->Int32Value());
4834
4835 // Change security token for env3 to something different from env1 and env2.
4836 env3->SetSecurityToken(v8_str("bar"));
4837
4838 // Check that we do not have access to other.p in env1. |other| is now
4839 // the global object for env3 which has a different security token,
4840 // so access should be blocked.
4841 result = CompileRun("other.p");
4842 CHECK(result->IsUndefined());
4843
4844 // Detach the global for env3 and reattach it to env2.
4845 env3->DetachGlobal();
4846 env2->ReattachGlobal(global2);
4847
4848 // Check that we have access to other.p again in env1. |other| is now
4849 // the global object for env2 which has the same security token as env1.
4850 result = CompileRun("other.p");
4851 CHECK(result->IsInt32());
4852 CHECK_EQ(42, result->Int32Value());
4853
4854 env2.Dispose();
4855 env3.Dispose();
4856}
4857
4858
Steve Blocka7e24c12009-10-30 11:49:00 +00004859static bool NamedAccessBlocker(Local<v8::Object> global,
4860 Local<Value> name,
4861 v8::AccessType type,
4862 Local<Value> data) {
4863 return Context::GetCurrent()->Global()->Equals(global);
4864}
4865
4866
4867static bool IndexedAccessBlocker(Local<v8::Object> global,
4868 uint32_t key,
4869 v8::AccessType type,
4870 Local<Value> data) {
4871 return Context::GetCurrent()->Global()->Equals(global);
4872}
4873
4874
4875static int g_echo_value = -1;
4876static v8::Handle<Value> EchoGetter(Local<String> name,
4877 const AccessorInfo& info) {
4878 return v8_num(g_echo_value);
4879}
4880
4881
4882static void EchoSetter(Local<String> name,
4883 Local<Value> value,
4884 const AccessorInfo&) {
4885 if (value->IsNumber())
4886 g_echo_value = value->Int32Value();
4887}
4888
4889
4890static v8::Handle<Value> UnreachableGetter(Local<String> name,
4891 const AccessorInfo& info) {
4892 CHECK(false); // This function should not be called..
4893 return v8::Undefined();
4894}
4895
4896
4897static void UnreachableSetter(Local<String>, Local<Value>,
4898 const AccessorInfo&) {
4899 CHECK(false); // This function should nto be called.
4900}
4901
4902
4903THREADED_TEST(AccessControl) {
4904 v8::HandleScope handle_scope;
4905 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4906
4907 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4908 IndexedAccessBlocker);
4909
4910 // Add an accessor accessible by cross-domain JS code.
4911 global_template->SetAccessor(
4912 v8_str("accessible_prop"),
4913 EchoGetter, EchoSetter,
4914 v8::Handle<Value>(),
4915 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4916
4917 // Add an accessor that is not accessible by cross-domain JS code.
4918 global_template->SetAccessor(v8_str("blocked_prop"),
4919 UnreachableGetter, UnreachableSetter,
4920 v8::Handle<Value>(),
4921 v8::DEFAULT);
4922
4923 // Create an environment
4924 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4925 context0->Enter();
4926
4927 v8::Handle<v8::Object> global0 = context0->Global();
4928
4929 v8::HandleScope scope1;
4930
4931 v8::Persistent<Context> context1 = Context::New();
4932 context1->Enter();
4933
4934 v8::Handle<v8::Object> global1 = context1->Global();
4935 global1->Set(v8_str("other"), global0);
4936
4937 v8::Handle<Value> value;
4938
4939 // Access blocked property
4940 value = v8_compile("other.blocked_prop = 1")->Run();
4941 value = v8_compile("other.blocked_prop")->Run();
4942 CHECK(value->IsUndefined());
4943
4944 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4945 CHECK(value->IsFalse());
4946
4947 // Access accessible property
4948 value = v8_compile("other.accessible_prop = 3")->Run();
4949 CHECK(value->IsNumber());
4950 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00004951 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00004952
4953 value = v8_compile("other.accessible_prop")->Run();
4954 CHECK(value->IsNumber());
4955 CHECK_EQ(3, value->Int32Value());
4956
4957 value =
4958 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
4959 CHECK(value->IsTrue());
4960
4961 // Enumeration doesn't enumerate accessors from inaccessible objects in
4962 // the prototype chain even if the accessors are in themselves accessible.
4963 Local<Value> result =
4964 CompileRun("(function(){var obj = {'__proto__':other};"
4965 "for (var p in obj)"
4966 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
4967 " return false;"
4968 " }"
4969 "return true;})()");
4970 CHECK(result->IsTrue());
4971
4972 context1->Exit();
4973 context0->Exit();
4974 context1.Dispose();
4975 context0.Dispose();
4976}
4977
4978
Leon Clarke4515c472010-02-03 11:58:03 +00004979static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
4980 Local<Value> name,
4981 v8::AccessType type,
4982 Local<Value> data) {
4983 return false;
4984}
4985
4986
4987static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
4988 uint32_t key,
4989 v8::AccessType type,
4990 Local<Value> data) {
4991 return false;
4992}
4993
4994
4995THREADED_TEST(AccessControlGetOwnPropertyNames) {
4996 v8::HandleScope handle_scope;
4997 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
4998
4999 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5000 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5001 GetOwnPropertyNamesIndexedBlocker);
5002
5003 // Create an environment
5004 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5005 context0->Enter();
5006
5007 v8::Handle<v8::Object> global0 = context0->Global();
5008
5009 v8::HandleScope scope1;
5010
5011 v8::Persistent<Context> context1 = Context::New();
5012 context1->Enter();
5013
5014 v8::Handle<v8::Object> global1 = context1->Global();
5015 global1->Set(v8_str("other"), global0);
5016 global1->Set(v8_str("object"), obj_template->NewInstance());
5017
5018 v8::Handle<Value> value;
5019
5020 // Attempt to get the property names of the other global object and
5021 // of an object that requires access checks. Accessing the other
5022 // global object should be blocked by access checks on the global
5023 // proxy object. Accessing the object that requires access checks
5024 // is blocked by the access checks on the object itself.
5025 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5026 CHECK(value->IsTrue());
5027
5028 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5029 CHECK(value->IsTrue());
5030
5031 context1->Exit();
5032 context0->Exit();
5033 context1.Dispose();
5034 context0.Dispose();
5035}
5036
5037
Steve Blocka7e24c12009-10-30 11:49:00 +00005038static v8::Handle<Value> ConstTenGetter(Local<String> name,
5039 const AccessorInfo& info) {
5040 return v8_num(10);
5041}
5042
5043
5044THREADED_TEST(CrossDomainAccessors) {
5045 v8::HandleScope handle_scope;
5046
5047 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5048
5049 v8::Handle<v8::ObjectTemplate> global_template =
5050 func_template->InstanceTemplate();
5051
5052 v8::Handle<v8::ObjectTemplate> proto_template =
5053 func_template->PrototypeTemplate();
5054
5055 // Add an accessor to proto that's accessible by cross-domain JS code.
5056 proto_template->SetAccessor(v8_str("accessible"),
5057 ConstTenGetter, 0,
5058 v8::Handle<Value>(),
5059 v8::ALL_CAN_READ);
5060
5061 // Add an accessor that is not accessible by cross-domain JS code.
5062 global_template->SetAccessor(v8_str("unreachable"),
5063 UnreachableGetter, 0,
5064 v8::Handle<Value>(),
5065 v8::DEFAULT);
5066
5067 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5068 context0->Enter();
5069
5070 Local<v8::Object> global = context0->Global();
5071 // Add a normal property that shadows 'accessible'
5072 global->Set(v8_str("accessible"), v8_num(11));
5073
5074 // Enter a new context.
5075 v8::HandleScope scope1;
5076 v8::Persistent<Context> context1 = Context::New();
5077 context1->Enter();
5078
5079 v8::Handle<v8::Object> global1 = context1->Global();
5080 global1->Set(v8_str("other"), global);
5081
5082 // Should return 10, instead of 11
5083 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5084 CHECK(value->IsNumber());
5085 CHECK_EQ(10, value->Int32Value());
5086
5087 value = v8_compile("other.unreachable")->Run();
5088 CHECK(value->IsUndefined());
5089
5090 context1->Exit();
5091 context0->Exit();
5092 context1.Dispose();
5093 context0.Dispose();
5094}
5095
5096
5097static int named_access_count = 0;
5098static int indexed_access_count = 0;
5099
5100static bool NamedAccessCounter(Local<v8::Object> global,
5101 Local<Value> name,
5102 v8::AccessType type,
5103 Local<Value> data) {
5104 named_access_count++;
5105 return true;
5106}
5107
5108
5109static bool IndexedAccessCounter(Local<v8::Object> global,
5110 uint32_t key,
5111 v8::AccessType type,
5112 Local<Value> data) {
5113 indexed_access_count++;
5114 return true;
5115}
5116
5117
5118// This one is too easily disturbed by other tests.
5119TEST(AccessControlIC) {
5120 named_access_count = 0;
5121 indexed_access_count = 0;
5122
5123 v8::HandleScope handle_scope;
5124
5125 // Create an environment.
5126 v8::Persistent<Context> context0 = Context::New();
5127 context0->Enter();
5128
5129 // Create an object that requires access-check functions to be
5130 // called for cross-domain access.
5131 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5132 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5133 IndexedAccessCounter);
5134 Local<v8::Object> object = object_template->NewInstance();
5135
5136 v8::HandleScope scope1;
5137
5138 // Create another environment.
5139 v8::Persistent<Context> context1 = Context::New();
5140 context1->Enter();
5141
5142 // Make easy access to the object from the other environment.
5143 v8::Handle<v8::Object> global1 = context1->Global();
5144 global1->Set(v8_str("obj"), object);
5145
5146 v8::Handle<Value> value;
5147
5148 // Check that the named access-control function is called every time.
5149 CompileRun("function testProp(obj) {"
5150 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5151 " for (var j = 0; j < 10; j++) obj.prop;"
5152 " return obj.prop"
5153 "}");
5154 value = CompileRun("testProp(obj)");
5155 CHECK(value->IsNumber());
5156 CHECK_EQ(1, value->Int32Value());
5157 CHECK_EQ(21, named_access_count);
5158
5159 // Check that the named access-control function is called every time.
5160 CompileRun("var p = 'prop';"
5161 "function testKeyed(obj) {"
5162 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5163 " for (var j = 0; j < 10; j++) obj[p];"
5164 " return obj[p];"
5165 "}");
5166 // Use obj which requires access checks. No inline caching is used
5167 // in that case.
5168 value = CompileRun("testKeyed(obj)");
5169 CHECK(value->IsNumber());
5170 CHECK_EQ(1, value->Int32Value());
5171 CHECK_EQ(42, named_access_count);
5172 // Force the inline caches into generic state and try again.
5173 CompileRun("testKeyed({ a: 0 })");
5174 CompileRun("testKeyed({ b: 0 })");
5175 value = CompileRun("testKeyed(obj)");
5176 CHECK(value->IsNumber());
5177 CHECK_EQ(1, value->Int32Value());
5178 CHECK_EQ(63, named_access_count);
5179
5180 // Check that the indexed access-control function is called every time.
5181 CompileRun("function testIndexed(obj) {"
5182 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5183 " for (var j = 0; j < 10; j++) obj[0];"
5184 " return obj[0]"
5185 "}");
5186 value = CompileRun("testIndexed(obj)");
5187 CHECK(value->IsNumber());
5188 CHECK_EQ(1, value->Int32Value());
5189 CHECK_EQ(21, indexed_access_count);
5190 // Force the inline caches into generic state.
5191 CompileRun("testIndexed(new Array(1))");
5192 // Test that the indexed access check is called.
5193 value = CompileRun("testIndexed(obj)");
5194 CHECK(value->IsNumber());
5195 CHECK_EQ(1, value->Int32Value());
5196 CHECK_EQ(42, indexed_access_count);
5197
5198 // Check that the named access check is called when invoking
5199 // functions on an object that requires access checks.
5200 CompileRun("obj.f = function() {}");
5201 CompileRun("function testCallNormal(obj) {"
5202 " for (var i = 0; i < 10; i++) obj.f();"
5203 "}");
5204 CompileRun("testCallNormal(obj)");
5205 CHECK_EQ(74, named_access_count);
5206
5207 // Force obj into slow case.
5208 value = CompileRun("delete obj.prop");
5209 CHECK(value->BooleanValue());
5210 // Force inline caches into dictionary probing mode.
5211 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5212 // Test that the named access check is called.
5213 value = CompileRun("testProp(obj);");
5214 CHECK(value->IsNumber());
5215 CHECK_EQ(1, value->Int32Value());
5216 CHECK_EQ(96, named_access_count);
5217
5218 // Force the call inline cache into dictionary probing mode.
5219 CompileRun("o.f = function() {}; testCallNormal(o)");
5220 // Test that the named access check is still called for each
5221 // invocation of the function.
5222 value = CompileRun("testCallNormal(obj)");
5223 CHECK_EQ(106, named_access_count);
5224
5225 context1->Exit();
5226 context0->Exit();
5227 context1.Dispose();
5228 context0.Dispose();
5229}
5230
5231
5232static bool NamedAccessFlatten(Local<v8::Object> global,
5233 Local<Value> name,
5234 v8::AccessType type,
5235 Local<Value> data) {
5236 char buf[100];
5237 int len;
5238
5239 CHECK(name->IsString());
5240
5241 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005242 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005243 CHECK_EQ(4, len);
5244
5245 uint16_t buf2[100];
5246
5247 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005248 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005249 CHECK_EQ(4, len);
5250
5251 return true;
5252}
5253
5254
5255static bool IndexedAccessFlatten(Local<v8::Object> global,
5256 uint32_t key,
5257 v8::AccessType type,
5258 Local<Value> data) {
5259 return true;
5260}
5261
5262
5263// Regression test. In access checks, operations that may cause
5264// garbage collection are not allowed. It used to be the case that
5265// using the Write operation on a string could cause a garbage
5266// collection due to flattening of the string. This is no longer the
5267// case.
5268THREADED_TEST(AccessControlFlatten) {
5269 named_access_count = 0;
5270 indexed_access_count = 0;
5271
5272 v8::HandleScope handle_scope;
5273
5274 // Create an environment.
5275 v8::Persistent<Context> context0 = Context::New();
5276 context0->Enter();
5277
5278 // Create an object that requires access-check functions to be
5279 // called for cross-domain access.
5280 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5281 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5282 IndexedAccessFlatten);
5283 Local<v8::Object> object = object_template->NewInstance();
5284
5285 v8::HandleScope scope1;
5286
5287 // Create another environment.
5288 v8::Persistent<Context> context1 = Context::New();
5289 context1->Enter();
5290
5291 // Make easy access to the object from the other environment.
5292 v8::Handle<v8::Object> global1 = context1->Global();
5293 global1->Set(v8_str("obj"), object);
5294
5295 v8::Handle<Value> value;
5296
5297 value = v8_compile("var p = 'as' + 'df';")->Run();
5298 value = v8_compile("obj[p];")->Run();
5299
5300 context1->Exit();
5301 context0->Exit();
5302 context1.Dispose();
5303 context0.Dispose();
5304}
5305
5306
5307static v8::Handle<Value> AccessControlNamedGetter(
5308 Local<String>, const AccessorInfo&) {
5309 return v8::Integer::New(42);
5310}
5311
5312
5313static v8::Handle<Value> AccessControlNamedSetter(
5314 Local<String>, Local<Value> value, const AccessorInfo&) {
5315 return value;
5316}
5317
5318
5319static v8::Handle<Value> AccessControlIndexedGetter(
5320 uint32_t index,
5321 const AccessorInfo& info) {
5322 return v8_num(42);
5323}
5324
5325
5326static v8::Handle<Value> AccessControlIndexedSetter(
5327 uint32_t, Local<Value> value, const AccessorInfo&) {
5328 return value;
5329}
5330
5331
5332THREADED_TEST(AccessControlInterceptorIC) {
5333 named_access_count = 0;
5334 indexed_access_count = 0;
5335
5336 v8::HandleScope handle_scope;
5337
5338 // Create an environment.
5339 v8::Persistent<Context> context0 = Context::New();
5340 context0->Enter();
5341
5342 // Create an object that requires access-check functions to be
5343 // called for cross-domain access. The object also has interceptors
5344 // interceptor.
5345 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5346 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5347 IndexedAccessCounter);
5348 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5349 AccessControlNamedSetter);
5350 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5351 AccessControlIndexedSetter);
5352 Local<v8::Object> object = object_template->NewInstance();
5353
5354 v8::HandleScope scope1;
5355
5356 // Create another environment.
5357 v8::Persistent<Context> context1 = Context::New();
5358 context1->Enter();
5359
5360 // Make easy access to the object from the other environment.
5361 v8::Handle<v8::Object> global1 = context1->Global();
5362 global1->Set(v8_str("obj"), object);
5363
5364 v8::Handle<Value> value;
5365
5366 // Check that the named access-control function is called every time
5367 // eventhough there is an interceptor on the object.
5368 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5369 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5370 "obj.x")->Run();
5371 CHECK(value->IsNumber());
5372 CHECK_EQ(42, value->Int32Value());
5373 CHECK_EQ(21, named_access_count);
5374
5375 value = v8_compile("var p = 'x';")->Run();
5376 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5377 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5378 "obj[p]")->Run();
5379 CHECK(value->IsNumber());
5380 CHECK_EQ(42, value->Int32Value());
5381 CHECK_EQ(42, named_access_count);
5382
5383 // Check that the indexed access-control function is called every
5384 // time eventhough there is an interceptor on the object.
5385 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5386 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5387 "obj[0]")->Run();
5388 CHECK(value->IsNumber());
5389 CHECK_EQ(42, value->Int32Value());
5390 CHECK_EQ(21, indexed_access_count);
5391
5392 context1->Exit();
5393 context0->Exit();
5394 context1.Dispose();
5395 context0.Dispose();
5396}
5397
5398
5399THREADED_TEST(Version) {
5400 v8::V8::GetVersion();
5401}
5402
5403
5404static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5405 ApiTestFuzzer::Fuzz();
5406 return v8_num(12);
5407}
5408
5409
5410THREADED_TEST(InstanceProperties) {
5411 v8::HandleScope handle_scope;
5412 LocalContext context;
5413
5414 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5415 Local<ObjectTemplate> instance = t->InstanceTemplate();
5416
5417 instance->Set(v8_str("x"), v8_num(42));
5418 instance->Set(v8_str("f"),
5419 v8::FunctionTemplate::New(InstanceFunctionCallback));
5420
5421 Local<Value> o = t->GetFunction()->NewInstance();
5422
5423 context->Global()->Set(v8_str("i"), o);
5424 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5425 CHECK_EQ(42, value->Int32Value());
5426
5427 value = Script::Compile(v8_str("i.f()"))->Run();
5428 CHECK_EQ(12, value->Int32Value());
5429}
5430
5431
5432static v8::Handle<Value>
5433GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5434 ApiTestFuzzer::Fuzz();
5435 return v8::Handle<Value>();
5436}
5437
5438
5439THREADED_TEST(GlobalObjectInstanceProperties) {
5440 v8::HandleScope handle_scope;
5441
5442 Local<Value> global_object;
5443
5444 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5445 t->InstanceTemplate()->SetNamedPropertyHandler(
5446 GlobalObjectInstancePropertiesGet);
5447 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5448 instance_template->Set(v8_str("x"), v8_num(42));
5449 instance_template->Set(v8_str("f"),
5450 v8::FunctionTemplate::New(InstanceFunctionCallback));
5451
5452 {
5453 LocalContext env(NULL, instance_template);
5454 // Hold on to the global object so it can be used again in another
5455 // environment initialization.
5456 global_object = env->Global();
5457
5458 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5459 CHECK_EQ(42, value->Int32Value());
5460 value = Script::Compile(v8_str("f()"))->Run();
5461 CHECK_EQ(12, value->Int32Value());
5462 }
5463
5464 {
5465 // Create new environment reusing the global object.
5466 LocalContext env(NULL, instance_template, global_object);
5467 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5468 CHECK_EQ(42, value->Int32Value());
5469 value = Script::Compile(v8_str("f()"))->Run();
5470 CHECK_EQ(12, value->Int32Value());
5471 }
5472}
5473
5474
5475static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5476 ApiTestFuzzer::Fuzz();
5477 return v8_num(42);
5478}
5479
5480
5481static int shadow_y;
5482static int shadow_y_setter_call_count;
5483static int shadow_y_getter_call_count;
5484
5485
5486static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5487 shadow_y_setter_call_count++;
5488 shadow_y = 42;
5489}
5490
5491
5492static v8::Handle<Value> ShadowYGetter(Local<String> name,
5493 const AccessorInfo& info) {
5494 ApiTestFuzzer::Fuzz();
5495 shadow_y_getter_call_count++;
5496 return v8_num(shadow_y);
5497}
5498
5499
5500static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5501 const AccessorInfo& info) {
5502 return v8::Handle<Value>();
5503}
5504
5505
5506static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5507 const AccessorInfo&) {
5508 return v8::Handle<Value>();
5509}
5510
5511
5512THREADED_TEST(ShadowObject) {
5513 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5514 v8::HandleScope handle_scope;
5515
5516 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5517 LocalContext context(NULL, global_template);
5518
5519 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5520 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5521 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5522 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5523 Local<ObjectTemplate> instance = t->InstanceTemplate();
5524
5525 // Only allow calls of f on instances of t.
5526 Local<v8::Signature> signature = v8::Signature::New(t);
5527 proto->Set(v8_str("f"),
5528 v8::FunctionTemplate::New(ShadowFunctionCallback,
5529 Local<Value>(),
5530 signature));
5531 proto->Set(v8_str("x"), v8_num(12));
5532
5533 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5534
5535 Local<Value> o = t->GetFunction()->NewInstance();
5536 context->Global()->Set(v8_str("__proto__"), o);
5537
5538 Local<Value> value =
5539 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5540 CHECK(value->IsBoolean());
5541 CHECK(!value->BooleanValue());
5542
5543 value = Script::Compile(v8_str("x"))->Run();
5544 CHECK_EQ(12, value->Int32Value());
5545
5546 value = Script::Compile(v8_str("f()"))->Run();
5547 CHECK_EQ(42, value->Int32Value());
5548
5549 Script::Compile(v8_str("y = 42"))->Run();
5550 CHECK_EQ(1, shadow_y_setter_call_count);
5551 value = Script::Compile(v8_str("y"))->Run();
5552 CHECK_EQ(1, shadow_y_getter_call_count);
5553 CHECK_EQ(42, value->Int32Value());
5554}
5555
5556
5557THREADED_TEST(HiddenPrototype) {
5558 v8::HandleScope handle_scope;
5559 LocalContext context;
5560
5561 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5562 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5563 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5564 t1->SetHiddenPrototype(true);
5565 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5566 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5567 t2->SetHiddenPrototype(true);
5568 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5569 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5570 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5571
5572 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5573 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5574 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5575 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5576
5577 // Setting the prototype on an object skips hidden prototypes.
5578 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5579 o0->Set(v8_str("__proto__"), o1);
5580 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5581 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5582 o0->Set(v8_str("__proto__"), o2);
5583 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5584 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5585 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5586 o0->Set(v8_str("__proto__"), o3);
5587 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5588 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5589 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5590 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5591
5592 // Getting the prototype of o0 should get the first visible one
5593 // which is o3. Therefore, z should not be defined on the prototype
5594 // object.
5595 Local<Value> proto = o0->Get(v8_str("__proto__"));
5596 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005597 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005598}
5599
5600
Andrei Popescu402d9372010-02-26 13:31:12 +00005601THREADED_TEST(SetPrototype) {
5602 v8::HandleScope handle_scope;
5603 LocalContext context;
5604
5605 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5606 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5607 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5608 t1->SetHiddenPrototype(true);
5609 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5610 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5611 t2->SetHiddenPrototype(true);
5612 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5613 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5614 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5615
5616 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5617 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5618 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5619 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5620
5621 // Setting the prototype on an object does not skip hidden prototypes.
5622 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5623 CHECK(o0->SetPrototype(o1));
5624 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5625 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5626 CHECK(o1->SetPrototype(o2));
5627 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5628 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5629 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5630 CHECK(o2->SetPrototype(o3));
5631 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5632 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5633 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5634 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5635
5636 // Getting the prototype of o0 should get the first visible one
5637 // which is o3. Therefore, z should not be defined on the prototype
5638 // object.
5639 Local<Value> proto = o0->Get(v8_str("__proto__"));
5640 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005641 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005642
5643 // However, Object::GetPrototype ignores hidden prototype.
5644 Local<Value> proto0 = o0->GetPrototype();
5645 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005646 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005647
5648 Local<Value> proto1 = o1->GetPrototype();
5649 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005650 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005651
5652 Local<Value> proto2 = o2->GetPrototype();
5653 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005654 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005655}
5656
5657
5658THREADED_TEST(SetPrototypeThrows) {
5659 v8::HandleScope handle_scope;
5660 LocalContext context;
5661
5662 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5663
5664 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5665 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5666
5667 CHECK(o0->SetPrototype(o1));
5668 // If setting the prototype leads to the cycle, SetPrototype should
5669 // return false and keep VM in sane state.
5670 v8::TryCatch try_catch;
5671 CHECK(!o1->SetPrototype(o0));
5672 CHECK(!try_catch.HasCaught());
5673 ASSERT(!i::Top::has_pending_exception());
5674
5675 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5676}
5677
5678
Steve Blocka7e24c12009-10-30 11:49:00 +00005679THREADED_TEST(GetterSetterExceptions) {
5680 v8::HandleScope handle_scope;
5681 LocalContext context;
5682 CompileRun(
5683 "function Foo() { };"
5684 "function Throw() { throw 5; };"
5685 "var x = { };"
5686 "x.__defineSetter__('set', Throw);"
5687 "x.__defineGetter__('get', Throw);");
5688 Local<v8::Object> x =
5689 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5690 v8::TryCatch try_catch;
5691 x->Set(v8_str("set"), v8::Integer::New(8));
5692 x->Get(v8_str("get"));
5693 x->Set(v8_str("set"), v8::Integer::New(8));
5694 x->Get(v8_str("get"));
5695 x->Set(v8_str("set"), v8::Integer::New(8));
5696 x->Get(v8_str("get"));
5697 x->Set(v8_str("set"), v8::Integer::New(8));
5698 x->Get(v8_str("get"));
5699}
5700
5701
5702THREADED_TEST(Constructor) {
5703 v8::HandleScope handle_scope;
5704 LocalContext context;
5705 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5706 templ->SetClassName(v8_str("Fun"));
5707 Local<Function> cons = templ->GetFunction();
5708 context->Global()->Set(v8_str("Fun"), cons);
5709 Local<v8::Object> inst = cons->NewInstance();
5710 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5711 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5712 CHECK(value->BooleanValue());
5713}
5714
5715THREADED_TEST(FunctionDescriptorException) {
5716 v8::HandleScope handle_scope;
5717 LocalContext context;
5718 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5719 templ->SetClassName(v8_str("Fun"));
5720 Local<Function> cons = templ->GetFunction();
5721 context->Global()->Set(v8_str("Fun"), cons);
5722 Local<Value> value = CompileRun(
5723 "function test() {"
5724 " try {"
5725 " (new Fun()).blah()"
5726 " } catch (e) {"
5727 " var str = String(e);"
5728 " if (str.indexOf('TypeError') == -1) return 1;"
5729 " if (str.indexOf('[object Fun]') != -1) return 2;"
5730 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5731 " return 0;"
5732 " }"
5733 " return 4;"
5734 "}"
5735 "test();");
5736 CHECK_EQ(0, value->Int32Value());
5737}
5738
5739
5740THREADED_TEST(EvalAliasedDynamic) {
5741 v8::HandleScope scope;
5742 LocalContext current;
5743
5744 // Tests where aliased eval can only be resolved dynamically.
5745 Local<Script> script =
5746 Script::Compile(v8_str("function f(x) { "
5747 " var foo = 2;"
5748 " with (x) { return eval('foo'); }"
5749 "}"
5750 "foo = 0;"
5751 "result1 = f(new Object());"
5752 "result2 = f(this);"
5753 "var x = new Object();"
5754 "x.eval = function(x) { return 1; };"
5755 "result3 = f(x);"));
5756 script->Run();
5757 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5758 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5759 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5760
5761 v8::TryCatch try_catch;
5762 script =
5763 Script::Compile(v8_str("function f(x) { "
5764 " var bar = 2;"
5765 " with (x) { return eval('bar'); }"
5766 "}"
5767 "f(this)"));
5768 script->Run();
5769 CHECK(try_catch.HasCaught());
5770 try_catch.Reset();
5771}
5772
5773
5774THREADED_TEST(CrossEval) {
5775 v8::HandleScope scope;
5776 LocalContext other;
5777 LocalContext current;
5778
5779 Local<String> token = v8_str("<security token>");
5780 other->SetSecurityToken(token);
5781 current->SetSecurityToken(token);
5782
5783 // Setup reference from current to other.
5784 current->Global()->Set(v8_str("other"), other->Global());
5785
5786 // Check that new variables are introduced in other context.
5787 Local<Script> script =
5788 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5789 script->Run();
5790 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5791 CHECK_EQ(1234, foo->Int32Value());
5792 CHECK(!current->Global()->Has(v8_str("foo")));
5793
5794 // Check that writing to non-existing properties introduces them in
5795 // the other context.
5796 script =
5797 Script::Compile(v8_str("other.eval('na = 1234')"));
5798 script->Run();
5799 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5800 CHECK(!current->Global()->Has(v8_str("na")));
5801
5802 // Check that global variables in current context are not visible in other
5803 // context.
5804 v8::TryCatch try_catch;
5805 script =
5806 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5807 Local<Value> result = script->Run();
5808 CHECK(try_catch.HasCaught());
5809 try_catch.Reset();
5810
5811 // Check that local variables in current context are not visible in other
5812 // context.
5813 script =
5814 Script::Compile(v8_str("(function() { "
5815 " var baz = 87;"
5816 " return other.eval('baz');"
5817 "})();"));
5818 result = script->Run();
5819 CHECK(try_catch.HasCaught());
5820 try_catch.Reset();
5821
5822 // Check that global variables in the other environment are visible
5823 // when evaluting code.
5824 other->Global()->Set(v8_str("bis"), v8_num(1234));
5825 script = Script::Compile(v8_str("other.eval('bis')"));
5826 CHECK_EQ(1234, script->Run()->Int32Value());
5827 CHECK(!try_catch.HasCaught());
5828
5829 // Check that the 'this' pointer points to the global object evaluating
5830 // code.
5831 other->Global()->Set(v8_str("t"), other->Global());
5832 script = Script::Compile(v8_str("other.eval('this == t')"));
5833 result = script->Run();
5834 CHECK(result->IsTrue());
5835 CHECK(!try_catch.HasCaught());
5836
5837 // Check that variables introduced in with-statement are not visible in
5838 // other context.
5839 script =
5840 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
5841 result = script->Run();
5842 CHECK(try_catch.HasCaught());
5843 try_catch.Reset();
5844
5845 // Check that you cannot use 'eval.call' with another object than the
5846 // current global object.
5847 script =
5848 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
5849 result = script->Run();
5850 CHECK(try_catch.HasCaught());
5851}
5852
5853
5854// Test that calling eval in a context which has been detached from
5855// its global throws an exception. This behavior is consistent with
5856// other JavaScript implementations.
5857THREADED_TEST(EvalInDetachedGlobal) {
5858 v8::HandleScope scope;
5859
5860 v8::Persistent<Context> context0 = Context::New();
5861 v8::Persistent<Context> context1 = Context::New();
5862
5863 // Setup function in context0 that uses eval from context0.
5864 context0->Enter();
5865 v8::Handle<v8::Value> fun =
5866 CompileRun("var x = 42;"
5867 "(function() {"
5868 " var e = eval;"
5869 " return function(s) { return e(s); }"
5870 "})()");
5871 context0->Exit();
5872
5873 // Put the function into context1 and call it before and after
5874 // detaching the global. Before detaching, the call succeeds and
5875 // after detaching and exception is thrown.
5876 context1->Enter();
5877 context1->Global()->Set(v8_str("fun"), fun);
5878 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
5879 CHECK_EQ(42, x_value->Int32Value());
5880 context0->DetachGlobal();
5881 v8::TryCatch catcher;
5882 x_value = CompileRun("fun('x')");
5883 CHECK(x_value.IsEmpty());
5884 CHECK(catcher.HasCaught());
5885 context1->Exit();
5886
5887 context1.Dispose();
5888 context0.Dispose();
5889}
5890
5891
5892THREADED_TEST(CrossLazyLoad) {
5893 v8::HandleScope scope;
5894 LocalContext other;
5895 LocalContext current;
5896
5897 Local<String> token = v8_str("<security token>");
5898 other->SetSecurityToken(token);
5899 current->SetSecurityToken(token);
5900
5901 // Setup reference from current to other.
5902 current->Global()->Set(v8_str("other"), other->Global());
5903
5904 // Trigger lazy loading in other context.
5905 Local<Script> script =
5906 Script::Compile(v8_str("other.eval('new Date(42)')"));
5907 Local<Value> value = script->Run();
5908 CHECK_EQ(42.0, value->NumberValue());
5909}
5910
5911
5912static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00005913 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00005914 if (args.IsConstructCall()) {
5915 if (args[0]->IsInt32()) {
5916 return v8_num(-args[0]->Int32Value());
5917 }
5918 }
5919
5920 return args[0];
5921}
5922
5923
5924// Test that a call handler can be set for objects which will allow
5925// non-function objects created through the API to be called as
5926// functions.
5927THREADED_TEST(CallAsFunction) {
5928 v8::HandleScope scope;
5929 LocalContext context;
5930
5931 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5932 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5933 instance_template->SetCallAsFunctionHandler(call_as_function);
5934 Local<v8::Object> instance = t->GetFunction()->NewInstance();
5935 context->Global()->Set(v8_str("obj"), instance);
5936 v8::TryCatch try_catch;
5937 Local<Value> value;
5938 CHECK(!try_catch.HasCaught());
5939
5940 value = CompileRun("obj(42)");
5941 CHECK(!try_catch.HasCaught());
5942 CHECK_EQ(42, value->Int32Value());
5943
5944 value = CompileRun("(function(o){return o(49)})(obj)");
5945 CHECK(!try_catch.HasCaught());
5946 CHECK_EQ(49, value->Int32Value());
5947
5948 // test special case of call as function
5949 value = CompileRun("[obj]['0'](45)");
5950 CHECK(!try_catch.HasCaught());
5951 CHECK_EQ(45, value->Int32Value());
5952
5953 value = CompileRun("obj.call = Function.prototype.call;"
5954 "obj.call(null, 87)");
5955 CHECK(!try_catch.HasCaught());
5956 CHECK_EQ(87, value->Int32Value());
5957
5958 // Regression tests for bug #1116356: Calling call through call/apply
5959 // must work for non-function receivers.
5960 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
5961 value = CompileRun(apply_99);
5962 CHECK(!try_catch.HasCaught());
5963 CHECK_EQ(99, value->Int32Value());
5964
5965 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
5966 value = CompileRun(call_17);
5967 CHECK(!try_catch.HasCaught());
5968 CHECK_EQ(17, value->Int32Value());
5969
5970 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00005971 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00005972 value = CompileRun("new obj(43)");
5973 CHECK(!try_catch.HasCaught());
5974 CHECK_EQ(-43, value->Int32Value());
5975}
5976
5977
5978static int CountHandles() {
5979 return v8::HandleScope::NumberOfHandles();
5980}
5981
5982
5983static int Recurse(int depth, int iterations) {
5984 v8::HandleScope scope;
5985 if (depth == 0) return CountHandles();
5986 for (int i = 0; i < iterations; i++) {
5987 Local<v8::Number> n = v8::Integer::New(42);
5988 }
5989 return Recurse(depth - 1, iterations);
5990}
5991
5992
5993THREADED_TEST(HandleIteration) {
5994 static const int kIterations = 500;
5995 static const int kNesting = 200;
5996 CHECK_EQ(0, CountHandles());
5997 {
5998 v8::HandleScope scope1;
5999 CHECK_EQ(0, CountHandles());
6000 for (int i = 0; i < kIterations; i++) {
6001 Local<v8::Number> n = v8::Integer::New(42);
6002 CHECK_EQ(i + 1, CountHandles());
6003 }
6004
6005 CHECK_EQ(kIterations, CountHandles());
6006 {
6007 v8::HandleScope scope2;
6008 for (int j = 0; j < kIterations; j++) {
6009 Local<v8::Number> n = v8::Integer::New(42);
6010 CHECK_EQ(j + 1 + kIterations, CountHandles());
6011 }
6012 }
6013 CHECK_EQ(kIterations, CountHandles());
6014 }
6015 CHECK_EQ(0, CountHandles());
6016 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6017}
6018
6019
6020static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6021 Local<String> name,
6022 const AccessorInfo& info) {
6023 ApiTestFuzzer::Fuzz();
6024 return v8::Handle<Value>();
6025}
6026
6027
6028THREADED_TEST(InterceptorHasOwnProperty) {
6029 v8::HandleScope scope;
6030 LocalContext context;
6031 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6032 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6033 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6034 Local<Function> function = fun_templ->GetFunction();
6035 context->Global()->Set(v8_str("constructor"), function);
6036 v8::Handle<Value> value = CompileRun(
6037 "var o = new constructor();"
6038 "o.hasOwnProperty('ostehaps');");
6039 CHECK_EQ(false, value->BooleanValue());
6040 value = CompileRun(
6041 "o.ostehaps = 42;"
6042 "o.hasOwnProperty('ostehaps');");
6043 CHECK_EQ(true, value->BooleanValue());
6044 value = CompileRun(
6045 "var p = new constructor();"
6046 "p.hasOwnProperty('ostehaps');");
6047 CHECK_EQ(false, value->BooleanValue());
6048}
6049
6050
6051static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6052 Local<String> name,
6053 const AccessorInfo& info) {
6054 ApiTestFuzzer::Fuzz();
6055 i::Heap::CollectAllGarbage(false);
6056 return v8::Handle<Value>();
6057}
6058
6059
6060THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6061 v8::HandleScope scope;
6062 LocalContext context;
6063 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6064 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6065 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6066 Local<Function> function = fun_templ->GetFunction();
6067 context->Global()->Set(v8_str("constructor"), function);
6068 // Let's first make some stuff so we can be sure to get a good GC.
6069 CompileRun(
6070 "function makestr(size) {"
6071 " switch (size) {"
6072 " case 1: return 'f';"
6073 " case 2: return 'fo';"
6074 " case 3: return 'foo';"
6075 " }"
6076 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6077 "}"
6078 "var x = makestr(12345);"
6079 "x = makestr(31415);"
6080 "x = makestr(23456);");
6081 v8::Handle<Value> value = CompileRun(
6082 "var o = new constructor();"
6083 "o.__proto__ = new String(x);"
6084 "o.hasOwnProperty('ostehaps');");
6085 CHECK_EQ(false, value->BooleanValue());
6086}
6087
6088
6089typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6090 const AccessorInfo& info);
6091
6092
6093static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6094 const char* source,
6095 int expected) {
6096 v8::HandleScope scope;
6097 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6098 templ->SetNamedPropertyHandler(getter);
6099 LocalContext context;
6100 context->Global()->Set(v8_str("o"), templ->NewInstance());
6101 v8::Handle<Value> value = CompileRun(source);
6102 CHECK_EQ(expected, value->Int32Value());
6103}
6104
6105
6106static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6107 const AccessorInfo& info) {
6108 ApiTestFuzzer::Fuzz();
6109 CHECK(v8_str("x")->Equals(name));
6110 return v8::Integer::New(42);
6111}
6112
6113
6114// This test should hit the load IC for the interceptor case.
6115THREADED_TEST(InterceptorLoadIC) {
6116 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6117 "var result = 0;"
6118 "for (var i = 0; i < 1000; i++) {"
6119 " result = o.x;"
6120 "}",
6121 42);
6122}
6123
6124
6125// Below go several tests which verify that JITing for various
6126// configurations of interceptor and explicit fields works fine
6127// (those cases are special cased to get better performance).
6128
6129static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6130 const AccessorInfo& info) {
6131 ApiTestFuzzer::Fuzz();
6132 return v8_str("x")->Equals(name)
6133 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6134}
6135
6136
6137THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6138 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6139 "var result = 0;"
6140 "o.y = 239;"
6141 "for (var i = 0; i < 1000; i++) {"
6142 " result = o.y;"
6143 "}",
6144 239);
6145}
6146
6147
6148THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6149 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6150 "var result = 0;"
6151 "o.__proto__ = { 'y': 239 };"
6152 "for (var i = 0; i < 1000; i++) {"
6153 " result = o.y + o.x;"
6154 "}",
6155 239 + 42);
6156}
6157
6158
6159THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6160 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6161 "var result = 0;"
6162 "o.__proto__.y = 239;"
6163 "for (var i = 0; i < 1000; i++) {"
6164 " result = o.y + o.x;"
6165 "}",
6166 239 + 42);
6167}
6168
6169
6170THREADED_TEST(InterceptorLoadICUndefined) {
6171 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6172 "var result = 0;"
6173 "for (var i = 0; i < 1000; i++) {"
6174 " result = (o.y == undefined) ? 239 : 42;"
6175 "}",
6176 239);
6177}
6178
6179
6180THREADED_TEST(InterceptorLoadICWithOverride) {
6181 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6182 "fst = new Object(); fst.__proto__ = o;"
6183 "snd = new Object(); snd.__proto__ = fst;"
6184 "var result1 = 0;"
6185 "for (var i = 0; i < 1000; i++) {"
6186 " result1 = snd.x;"
6187 "}"
6188 "fst.x = 239;"
6189 "var result = 0;"
6190 "for (var i = 0; i < 1000; i++) {"
6191 " result = snd.x;"
6192 "}"
6193 "result + result1",
6194 239 + 42);
6195}
6196
6197
6198// Test the case when we stored field into
6199// a stub, but interceptor produced value on its own.
6200THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6201 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6202 "proto = new Object();"
6203 "o.__proto__ = proto;"
6204 "proto.x = 239;"
6205 "for (var i = 0; i < 1000; i++) {"
6206 " o.x;"
6207 // Now it should be ICed and keep a reference to x defined on proto
6208 "}"
6209 "var result = 0;"
6210 "for (var i = 0; i < 1000; i++) {"
6211 " result += o.x;"
6212 "}"
6213 "result;",
6214 42 * 1000);
6215}
6216
6217
6218// Test the case when we stored field into
6219// a stub, but it got invalidated later on.
6220THREADED_TEST(InterceptorLoadICInvalidatedField) {
6221 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6222 "proto1 = new Object();"
6223 "proto2 = new Object();"
6224 "o.__proto__ = proto1;"
6225 "proto1.__proto__ = proto2;"
6226 "proto2.y = 239;"
6227 "for (var i = 0; i < 1000; i++) {"
6228 " o.y;"
6229 // Now it should be ICed and keep a reference to y defined on proto2
6230 "}"
6231 "proto1.y = 42;"
6232 "var result = 0;"
6233 "for (var i = 0; i < 1000; i++) {"
6234 " result += o.y;"
6235 "}"
6236 "result;",
6237 42 * 1000);
6238}
6239
6240
Steve Block6ded16b2010-05-10 14:33:55 +01006241static int interceptor_load_not_handled_calls = 0;
6242static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6243 const AccessorInfo& info) {
6244 ++interceptor_load_not_handled_calls;
6245 return v8::Handle<v8::Value>();
6246}
6247
6248
6249// Test how post-interceptor lookups are done in the non-cacheable
6250// case: the interceptor should not be invoked during this lookup.
6251THREADED_TEST(InterceptorLoadICPostInterceptor) {
6252 interceptor_load_not_handled_calls = 0;
6253 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6254 "receiver = new Object();"
6255 "receiver.__proto__ = o;"
6256 "proto = new Object();"
6257 "/* Make proto a slow-case object. */"
6258 "for (var i = 0; i < 1000; i++) {"
6259 " proto[\"xxxxxxxx\" + i] = [];"
6260 "}"
6261 "proto.x = 17;"
6262 "o.__proto__ = proto;"
6263 "var result = 0;"
6264 "for (var i = 0; i < 1000; i++) {"
6265 " result += receiver.x;"
6266 "}"
6267 "result;",
6268 17 * 1000);
6269 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6270}
6271
6272
Steve Blocka7e24c12009-10-30 11:49:00 +00006273// Test the case when we stored field into
6274// a stub, but it got invalidated later on due to override on
6275// global object which is between interceptor and fields' holders.
6276THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6277 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6278 "o.__proto__ = this;" // set a global to be a proto of o.
6279 "this.__proto__.y = 239;"
6280 "for (var i = 0; i < 10; i++) {"
6281 " if (o.y != 239) throw 'oops: ' + o.y;"
6282 // Now it should be ICed and keep a reference to y defined on field_holder.
6283 "}"
6284 "this.y = 42;" // Assign on a global.
6285 "var result = 0;"
6286 "for (var i = 0; i < 10; i++) {"
6287 " result += o.y;"
6288 "}"
6289 "result;",
6290 42 * 10);
6291}
6292
6293
6294static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
6295 ApiTestFuzzer::Fuzz();
6296 return v8_num(239);
6297}
6298
6299
6300static void SetOnThis(Local<String> name,
6301 Local<Value> value,
6302 const AccessorInfo& info) {
6303 info.This()->ForceSet(name, value);
6304}
6305
6306
6307THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6308 v8::HandleScope scope;
6309 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6310 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6311 templ->SetAccessor(v8_str("y"), Return239);
6312 LocalContext context;
6313 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006314
6315 // Check the case when receiver and interceptor's holder
6316 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006317 v8::Handle<Value> value = CompileRun(
6318 "var result = 0;"
6319 "for (var i = 0; i < 7; i++) {"
6320 " result = o.y;"
6321 "}");
6322 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006323
6324 // Check the case when interceptor's holder is in proto chain
6325 // of receiver.
6326 value = CompileRun(
6327 "r = { __proto__: o };"
6328 "var result = 0;"
6329 "for (var i = 0; i < 7; i++) {"
6330 " result = r.y;"
6331 "}");
6332 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006333}
6334
6335
6336THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6337 v8::HandleScope scope;
6338 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6339 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6340 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6341 templ_p->SetAccessor(v8_str("y"), Return239);
6342
6343 LocalContext context;
6344 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6345 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6346
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006347 // Check the case when receiver and interceptor's holder
6348 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006349 v8::Handle<Value> value = CompileRun(
6350 "o.__proto__ = p;"
6351 "var result = 0;"
6352 "for (var i = 0; i < 7; i++) {"
6353 " result = o.x + o.y;"
6354 "}");
6355 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006356
6357 // Check the case when interceptor's holder is in proto chain
6358 // of receiver.
6359 value = CompileRun(
6360 "r = { __proto__: o };"
6361 "var result = 0;"
6362 "for (var i = 0; i < 7; i++) {"
6363 " result = r.x + r.y;"
6364 "}");
6365 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006366}
6367
6368
6369THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6370 v8::HandleScope scope;
6371 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6372 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6373 templ->SetAccessor(v8_str("y"), Return239);
6374
6375 LocalContext context;
6376 context->Global()->Set(v8_str("o"), templ->NewInstance());
6377
6378 v8::Handle<Value> value = CompileRun(
6379 "fst = new Object(); fst.__proto__ = o;"
6380 "snd = new Object(); snd.__proto__ = fst;"
6381 "var result1 = 0;"
6382 "for (var i = 0; i < 7; i++) {"
6383 " result1 = snd.x;"
6384 "}"
6385 "fst.x = 239;"
6386 "var result = 0;"
6387 "for (var i = 0; i < 7; i++) {"
6388 " result = snd.x;"
6389 "}"
6390 "result + result1");
6391 CHECK_EQ(239 + 42, value->Int32Value());
6392}
6393
6394
6395// Test the case when we stored callback into
6396// a stub, but interceptor produced value on its own.
6397THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6398 v8::HandleScope scope;
6399 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6400 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6401 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6402 templ_p->SetAccessor(v8_str("y"), Return239);
6403
6404 LocalContext context;
6405 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6406 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6407
6408 v8::Handle<Value> value = CompileRun(
6409 "o.__proto__ = p;"
6410 "for (var i = 0; i < 7; i++) {"
6411 " o.x;"
6412 // Now it should be ICed and keep a reference to x defined on p
6413 "}"
6414 "var result = 0;"
6415 "for (var i = 0; i < 7; i++) {"
6416 " result += o.x;"
6417 "}"
6418 "result");
6419 CHECK_EQ(42 * 7, value->Int32Value());
6420}
6421
6422
6423// Test the case when we stored callback into
6424// a stub, but it got invalidated later on.
6425THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6426 v8::HandleScope scope;
6427 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6428 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6429 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6430 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6431
6432 LocalContext context;
6433 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6434 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6435
6436 v8::Handle<Value> value = CompileRun(
6437 "inbetween = new Object();"
6438 "o.__proto__ = inbetween;"
6439 "inbetween.__proto__ = p;"
6440 "for (var i = 0; i < 10; i++) {"
6441 " o.y;"
6442 // Now it should be ICed and keep a reference to y defined on p
6443 "}"
6444 "inbetween.y = 42;"
6445 "var result = 0;"
6446 "for (var i = 0; i < 10; i++) {"
6447 " result += o.y;"
6448 "}"
6449 "result");
6450 CHECK_EQ(42 * 10, value->Int32Value());
6451}
6452
6453
6454// Test the case when we stored callback into
6455// a stub, but it got invalidated later on due to override on
6456// global object which is between interceptor and callbacks' holders.
6457THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6458 v8::HandleScope scope;
6459 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6460 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6461 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6462 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6463
6464 LocalContext context;
6465 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6466 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6467
6468 v8::Handle<Value> value = CompileRun(
6469 "o.__proto__ = this;"
6470 "this.__proto__ = p;"
6471 "for (var i = 0; i < 10; i++) {"
6472 " if (o.y != 239) throw 'oops: ' + o.y;"
6473 // Now it should be ICed and keep a reference to y defined on p
6474 "}"
6475 "this.y = 42;"
6476 "var result = 0;"
6477 "for (var i = 0; i < 10; i++) {"
6478 " result += o.y;"
6479 "}"
6480 "result");
6481 CHECK_EQ(42 * 10, value->Int32Value());
6482}
6483
6484
6485static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6486 const AccessorInfo& info) {
6487 ApiTestFuzzer::Fuzz();
6488 CHECK(v8_str("x")->Equals(name));
6489 return v8::Integer::New(0);
6490}
6491
6492
6493THREADED_TEST(InterceptorReturningZero) {
6494 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6495 "o.x == undefined ? 1 : 0",
6496 0);
6497}
6498
6499
6500static v8::Handle<Value> InterceptorStoreICSetter(
6501 Local<String> key, Local<Value> value, const AccessorInfo&) {
6502 CHECK(v8_str("x")->Equals(key));
6503 CHECK_EQ(42, value->Int32Value());
6504 return value;
6505}
6506
6507
6508// This test should hit the store IC for the interceptor case.
6509THREADED_TEST(InterceptorStoreIC) {
6510 v8::HandleScope scope;
6511 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6512 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6513 InterceptorStoreICSetter);
6514 LocalContext context;
6515 context->Global()->Set(v8_str("o"), templ->NewInstance());
6516 v8::Handle<Value> value = CompileRun(
6517 "for (var i = 0; i < 1000; i++) {"
6518 " o.x = 42;"
6519 "}");
6520}
6521
6522
6523THREADED_TEST(InterceptorStoreICWithNoSetter) {
6524 v8::HandleScope scope;
6525 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6526 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6527 LocalContext context;
6528 context->Global()->Set(v8_str("o"), templ->NewInstance());
6529 v8::Handle<Value> value = CompileRun(
6530 "for (var i = 0; i < 1000; i++) {"
6531 " o.y = 239;"
6532 "}"
6533 "42 + o.y");
6534 CHECK_EQ(239 + 42, value->Int32Value());
6535}
6536
6537
6538
6539
6540v8::Handle<Value> call_ic_function;
6541v8::Handle<Value> call_ic_function2;
6542v8::Handle<Value> call_ic_function3;
6543
6544static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6545 const AccessorInfo& info) {
6546 ApiTestFuzzer::Fuzz();
6547 CHECK(v8_str("x")->Equals(name));
6548 return call_ic_function;
6549}
6550
6551
6552// This test should hit the call IC for the interceptor case.
6553THREADED_TEST(InterceptorCallIC) {
6554 v8::HandleScope scope;
6555 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6556 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6557 LocalContext context;
6558 context->Global()->Set(v8_str("o"), templ->NewInstance());
6559 call_ic_function =
6560 v8_compile("function f(x) { return x + 1; }; f")->Run();
6561 v8::Handle<Value> value = CompileRun(
6562 "var result = 0;"
6563 "for (var i = 0; i < 1000; i++) {"
6564 " result = o.x(41);"
6565 "}");
6566 CHECK_EQ(42, value->Int32Value());
6567}
6568
6569
6570// This test checks that if interceptor doesn't provide
6571// a value, we can fetch regular value.
6572THREADED_TEST(InterceptorCallICSeesOthers) {
6573 v8::HandleScope scope;
6574 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6575 templ->SetNamedPropertyHandler(NoBlockGetterX);
6576 LocalContext context;
6577 context->Global()->Set(v8_str("o"), templ->NewInstance());
6578 v8::Handle<Value> value = CompileRun(
6579 "o.x = function f(x) { return x + 1; };"
6580 "var result = 0;"
6581 "for (var i = 0; i < 7; i++) {"
6582 " result = o.x(41);"
6583 "}");
6584 CHECK_EQ(42, value->Int32Value());
6585}
6586
6587
6588static v8::Handle<Value> call_ic_function4;
6589static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6590 const AccessorInfo& info) {
6591 ApiTestFuzzer::Fuzz();
6592 CHECK(v8_str("x")->Equals(name));
6593 return call_ic_function4;
6594}
6595
6596
6597// This test checks that if interceptor provides a function,
6598// even if we cached shadowed variant, interceptor's function
6599// is invoked
6600THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6601 v8::HandleScope scope;
6602 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6603 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6604 LocalContext context;
6605 context->Global()->Set(v8_str("o"), templ->NewInstance());
6606 call_ic_function4 =
6607 v8_compile("function f(x) { return x - 1; }; f")->Run();
6608 v8::Handle<Value> value = CompileRun(
6609 "o.__proto__.x = function(x) { return x + 1; };"
6610 "var result = 0;"
6611 "for (var i = 0; i < 1000; i++) {"
6612 " result = o.x(42);"
6613 "}");
6614 CHECK_EQ(41, value->Int32Value());
6615}
6616
6617
6618// Test the case when we stored cacheable lookup into
6619// a stub, but it got invalidated later on
6620THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6621 v8::HandleScope scope;
6622 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6623 templ->SetNamedPropertyHandler(NoBlockGetterX);
6624 LocalContext context;
6625 context->Global()->Set(v8_str("o"), templ->NewInstance());
6626 v8::Handle<Value> value = CompileRun(
6627 "proto1 = new Object();"
6628 "proto2 = new Object();"
6629 "o.__proto__ = proto1;"
6630 "proto1.__proto__ = proto2;"
6631 "proto2.y = function(x) { return x + 1; };"
6632 // Invoke it many times to compile a stub
6633 "for (var i = 0; i < 7; i++) {"
6634 " o.y(42);"
6635 "}"
6636 "proto1.y = function(x) { return x - 1; };"
6637 "var result = 0;"
6638 "for (var i = 0; i < 7; i++) {"
6639 " result += o.y(42);"
6640 "}");
6641 CHECK_EQ(41 * 7, value->Int32Value());
6642}
6643
6644
6645static v8::Handle<Value> call_ic_function5;
6646static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6647 const AccessorInfo& info) {
6648 ApiTestFuzzer::Fuzz();
6649 if (v8_str("x")->Equals(name))
6650 return call_ic_function5;
6651 else
6652 return Local<Value>();
6653}
6654
6655
6656// This test checks that if interceptor doesn't provide a function,
6657// cached constant function is used
6658THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6659 v8::HandleScope scope;
6660 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6661 templ->SetNamedPropertyHandler(NoBlockGetterX);
6662 LocalContext context;
6663 context->Global()->Set(v8_str("o"), templ->NewInstance());
6664 v8::Handle<Value> value = CompileRun(
6665 "function inc(x) { return x + 1; };"
6666 "inc(1);"
6667 "o.x = inc;"
6668 "var result = 0;"
6669 "for (var i = 0; i < 1000; i++) {"
6670 " result = o.x(42);"
6671 "}");
6672 CHECK_EQ(43, value->Int32Value());
6673}
6674
6675
6676// This test checks that if interceptor provides a function,
6677// even if we cached constant function, interceptor's function
6678// is invoked
6679THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6680 v8::HandleScope scope;
6681 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6682 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6683 LocalContext context;
6684 context->Global()->Set(v8_str("o"), templ->NewInstance());
6685 call_ic_function5 =
6686 v8_compile("function f(x) { return x - 1; }; f")->Run();
6687 v8::Handle<Value> value = CompileRun(
6688 "function inc(x) { return x + 1; };"
6689 "inc(1);"
6690 "o.x = inc;"
6691 "var result = 0;"
6692 "for (var i = 0; i < 1000; i++) {"
6693 " result = o.x(42);"
6694 "}");
6695 CHECK_EQ(41, value->Int32Value());
6696}
6697
6698
6699// Test the case when we stored constant function into
6700// a stub, but it got invalidated later on
6701THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6702 v8::HandleScope scope;
6703 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6704 templ->SetNamedPropertyHandler(NoBlockGetterX);
6705 LocalContext context;
6706 context->Global()->Set(v8_str("o"), templ->NewInstance());
6707 v8::Handle<Value> value = CompileRun(
6708 "function inc(x) { return x + 1; };"
6709 "inc(1);"
6710 "proto1 = new Object();"
6711 "proto2 = new Object();"
6712 "o.__proto__ = proto1;"
6713 "proto1.__proto__ = proto2;"
6714 "proto2.y = inc;"
6715 // Invoke it many times to compile a stub
6716 "for (var i = 0; i < 7; i++) {"
6717 " o.y(42);"
6718 "}"
6719 "proto1.y = function(x) { return x - 1; };"
6720 "var result = 0;"
6721 "for (var i = 0; i < 7; i++) {"
6722 " result += o.y(42);"
6723 "}");
6724 CHECK_EQ(41 * 7, value->Int32Value());
6725}
6726
6727
6728// Test the case when we stored constant function into
6729// a stub, but it got invalidated later on due to override on
6730// global object which is between interceptor and constant function' holders.
6731THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6732 v8::HandleScope scope;
6733 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6734 templ->SetNamedPropertyHandler(NoBlockGetterX);
6735 LocalContext context;
6736 context->Global()->Set(v8_str("o"), templ->NewInstance());
6737 v8::Handle<Value> value = CompileRun(
6738 "function inc(x) { return x + 1; };"
6739 "inc(1);"
6740 "o.__proto__ = this;"
6741 "this.__proto__.y = inc;"
6742 // Invoke it many times to compile a stub
6743 "for (var i = 0; i < 7; i++) {"
6744 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6745 "}"
6746 "this.y = function(x) { return x - 1; };"
6747 "var result = 0;"
6748 "for (var i = 0; i < 7; i++) {"
6749 " result += o.y(42);"
6750 "}");
6751 CHECK_EQ(41 * 7, value->Int32Value());
6752}
6753
6754
Leon Clarke4515c472010-02-03 11:58:03 +00006755// Test the case when actual function to call sits on global object.
6756THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6757 v8::HandleScope scope;
6758 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6759 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6760
6761 LocalContext context;
6762 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6763
6764 v8::Handle<Value> value = CompileRun(
6765 "try {"
6766 " o.__proto__ = this;"
6767 " for (var i = 0; i < 10; i++) {"
6768 " var v = o.parseFloat('239');"
6769 " if (v != 239) throw v;"
6770 // Now it should be ICed and keep a reference to parseFloat.
6771 " }"
6772 " var result = 0;"
6773 " for (var i = 0; i < 10; i++) {"
6774 " result += o.parseFloat('239');"
6775 " }"
6776 " result"
6777 "} catch(e) {"
6778 " e"
6779 "};");
6780 CHECK_EQ(239 * 10, value->Int32Value());
6781}
6782
Andrei Popescu402d9372010-02-26 13:31:12 +00006783static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6784 const AccessorInfo& info) {
6785 ApiTestFuzzer::Fuzz();
6786 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6787 ++(*call_count);
6788 if ((*call_count) % 20 == 0) {
6789 v8::internal::Heap::CollectAllGarbage(true);
6790 }
6791 return v8::Handle<Value>();
6792}
6793
6794static v8::Handle<Value> FastApiCallback_TrivialSignature(
6795 const v8::Arguments& args) {
6796 ApiTestFuzzer::Fuzz();
6797 CHECK_EQ(args.This(), args.Holder());
6798 CHECK(args.Data()->Equals(v8_str("method_data")));
6799 return v8::Integer::New(args[0]->Int32Value() + 1);
6800}
6801
6802static v8::Handle<Value> FastApiCallback_SimpleSignature(
6803 const v8::Arguments& args) {
6804 ApiTestFuzzer::Fuzz();
6805 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6806 CHECK(args.Data()->Equals(v8_str("method_data")));
6807 // Note, we're using HasRealNamedProperty instead of Has to avoid
6808 // invoking the interceptor again.
6809 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6810 return v8::Integer::New(args[0]->Int32Value() + 1);
6811}
6812
6813// Helper to maximize the odds of object moving.
6814static void GenerateSomeGarbage() {
6815 CompileRun(
6816 "var garbage;"
6817 "for (var i = 0; i < 1000; i++) {"
6818 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6819 "}"
6820 "garbage = undefined;");
6821}
6822
6823THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
6824 int interceptor_call_count = 0;
6825 v8::HandleScope scope;
6826 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6827 v8::Handle<v8::FunctionTemplate> method_templ =
6828 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6829 v8_str("method_data"),
6830 v8::Handle<v8::Signature>());
6831 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6832 proto_templ->Set(v8_str("method"), method_templ);
6833 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6834 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6835 NULL, NULL, NULL, NULL,
6836 v8::External::Wrap(&interceptor_call_count));
6837 LocalContext context;
6838 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6839 GenerateSomeGarbage();
6840 context->Global()->Set(v8_str("o"), fun->NewInstance());
6841 v8::Handle<Value> value = CompileRun(
6842 "var result = 0;"
6843 "for (var i = 0; i < 100; i++) {"
6844 " result = o.method(41);"
6845 "}");
6846 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6847 CHECK_EQ(100, interceptor_call_count);
6848}
6849
6850THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
6851 int interceptor_call_count = 0;
6852 v8::HandleScope scope;
6853 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6854 v8::Handle<v8::FunctionTemplate> method_templ =
6855 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6856 v8_str("method_data"),
6857 v8::Signature::New(fun_templ));
6858 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6859 proto_templ->Set(v8_str("method"), method_templ);
6860 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6861 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6862 NULL, NULL, NULL, NULL,
6863 v8::External::Wrap(&interceptor_call_count));
6864 LocalContext context;
6865 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6866 GenerateSomeGarbage();
6867 context->Global()->Set(v8_str("o"), fun->NewInstance());
6868 v8::Handle<Value> value = CompileRun(
6869 "o.foo = 17;"
6870 "var receiver = {};"
6871 "receiver.__proto__ = o;"
6872 "var result = 0;"
6873 "for (var i = 0; i < 100; i++) {"
6874 " result = receiver.method(41);"
6875 "}");
6876 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6877 CHECK_EQ(100, interceptor_call_count);
6878}
6879
6880THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
6881 int interceptor_call_count = 0;
6882 v8::HandleScope scope;
6883 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6884 v8::Handle<v8::FunctionTemplate> method_templ =
6885 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6886 v8_str("method_data"),
6887 v8::Signature::New(fun_templ));
6888 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6889 proto_templ->Set(v8_str("method"), method_templ);
6890 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6891 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6892 NULL, NULL, NULL, NULL,
6893 v8::External::Wrap(&interceptor_call_count));
6894 LocalContext context;
6895 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6896 GenerateSomeGarbage();
6897 context->Global()->Set(v8_str("o"), fun->NewInstance());
6898 v8::Handle<Value> value = CompileRun(
6899 "o.foo = 17;"
6900 "var receiver = {};"
6901 "receiver.__proto__ = o;"
6902 "var result = 0;"
6903 "var saved_result = 0;"
6904 "for (var i = 0; i < 100; i++) {"
6905 " result = receiver.method(41);"
6906 " if (i == 50) {"
6907 " saved_result = result;"
6908 " receiver = {method: function(x) { return x - 1 }};"
6909 " }"
6910 "}");
6911 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6912 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6913 CHECK_GE(interceptor_call_count, 50);
6914}
6915
6916THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
6917 int interceptor_call_count = 0;
6918 v8::HandleScope scope;
6919 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6920 v8::Handle<v8::FunctionTemplate> method_templ =
6921 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6922 v8_str("method_data"),
6923 v8::Signature::New(fun_templ));
6924 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6925 proto_templ->Set(v8_str("method"), method_templ);
6926 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6927 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6928 NULL, NULL, NULL, NULL,
6929 v8::External::Wrap(&interceptor_call_count));
6930 LocalContext context;
6931 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6932 GenerateSomeGarbage();
6933 context->Global()->Set(v8_str("o"), fun->NewInstance());
6934 v8::Handle<Value> value = CompileRun(
6935 "o.foo = 17;"
6936 "var receiver = {};"
6937 "receiver.__proto__ = o;"
6938 "var result = 0;"
6939 "var saved_result = 0;"
6940 "for (var i = 0; i < 100; i++) {"
6941 " result = receiver.method(41);"
6942 " if (i == 50) {"
6943 " saved_result = result;"
6944 " o.method = function(x) { return x - 1 };"
6945 " }"
6946 "}");
6947 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6948 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6949 CHECK_GE(interceptor_call_count, 50);
6950}
6951
Steve Block6ded16b2010-05-10 14:33:55 +01006952THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
6953 int interceptor_call_count = 0;
6954 v8::HandleScope scope;
6955 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6956 v8::Handle<v8::FunctionTemplate> method_templ =
6957 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6958 v8_str("method_data"),
6959 v8::Signature::New(fun_templ));
6960 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6961 proto_templ->Set(v8_str("method"), method_templ);
6962 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6963 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6964 NULL, NULL, NULL, NULL,
6965 v8::External::Wrap(&interceptor_call_count));
6966 LocalContext context;
6967 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6968 GenerateSomeGarbage();
6969 context->Global()->Set(v8_str("o"), fun->NewInstance());
6970 v8::TryCatch try_catch;
6971 v8::Handle<Value> value = CompileRun(
6972 "o.foo = 17;"
6973 "var receiver = {};"
6974 "receiver.__proto__ = o;"
6975 "var result = 0;"
6976 "var saved_result = 0;"
6977 "for (var i = 0; i < 100; i++) {"
6978 " result = receiver.method(41);"
6979 " if (i == 50) {"
6980 " saved_result = result;"
6981 " receiver = 333;"
6982 " }"
6983 "}");
6984 CHECK(try_catch.HasCaught());
6985 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
6986 try_catch.Exception()->ToString());
6987 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6988 CHECK_GE(interceptor_call_count, 50);
6989}
6990
Andrei Popescu402d9372010-02-26 13:31:12 +00006991THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
6992 int interceptor_call_count = 0;
6993 v8::HandleScope scope;
6994 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6995 v8::Handle<v8::FunctionTemplate> method_templ =
6996 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6997 v8_str("method_data"),
6998 v8::Signature::New(fun_templ));
6999 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7000 proto_templ->Set(v8_str("method"), method_templ);
7001 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7002 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7003 NULL, NULL, NULL, NULL,
7004 v8::External::Wrap(&interceptor_call_count));
7005 LocalContext context;
7006 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7007 GenerateSomeGarbage();
7008 context->Global()->Set(v8_str("o"), fun->NewInstance());
7009 v8::TryCatch try_catch;
7010 v8::Handle<Value> value = CompileRun(
7011 "o.foo = 17;"
7012 "var receiver = {};"
7013 "receiver.__proto__ = o;"
7014 "var result = 0;"
7015 "var saved_result = 0;"
7016 "for (var i = 0; i < 100; i++) {"
7017 " result = receiver.method(41);"
7018 " if (i == 50) {"
7019 " saved_result = result;"
7020 " receiver = {method: receiver.method};"
7021 " }"
7022 "}");
7023 CHECK(try_catch.HasCaught());
7024 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7025 try_catch.Exception()->ToString());
7026 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7027 CHECK_GE(interceptor_call_count, 50);
7028}
7029
7030THREADED_TEST(CallICFastApi_TrivialSignature) {
7031 v8::HandleScope scope;
7032 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7033 v8::Handle<v8::FunctionTemplate> method_templ =
7034 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7035 v8_str("method_data"),
7036 v8::Handle<v8::Signature>());
7037 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7038 proto_templ->Set(v8_str("method"), method_templ);
7039 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7040 LocalContext context;
7041 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7042 GenerateSomeGarbage();
7043 context->Global()->Set(v8_str("o"), fun->NewInstance());
7044 v8::Handle<Value> value = CompileRun(
7045 "var result = 0;"
7046 "for (var i = 0; i < 100; i++) {"
7047 " result = o.method(41);"
7048 "}");
7049
7050 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7051}
7052
7053THREADED_TEST(CallICFastApi_SimpleSignature) {
7054 v8::HandleScope scope;
7055 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7056 v8::Handle<v8::FunctionTemplate> method_templ =
7057 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7058 v8_str("method_data"),
7059 v8::Signature::New(fun_templ));
7060 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7061 proto_templ->Set(v8_str("method"), method_templ);
7062 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7063 LocalContext context;
7064 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7065 GenerateSomeGarbage();
7066 context->Global()->Set(v8_str("o"), fun->NewInstance());
7067 v8::Handle<Value> value = CompileRun(
7068 "o.foo = 17;"
7069 "var receiver = {};"
7070 "receiver.__proto__ = o;"
7071 "var result = 0;"
7072 "for (var i = 0; i < 100; i++) {"
7073 " result = receiver.method(41);"
7074 "}");
7075
7076 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7077}
7078
Steve Block6ded16b2010-05-10 14:33:55 +01007079THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007080 v8::HandleScope scope;
7081 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7082 v8::Handle<v8::FunctionTemplate> method_templ =
7083 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7084 v8_str("method_data"),
7085 v8::Signature::New(fun_templ));
7086 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7087 proto_templ->Set(v8_str("method"), method_templ);
7088 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7089 LocalContext context;
7090 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7091 GenerateSomeGarbage();
7092 context->Global()->Set(v8_str("o"), fun->NewInstance());
7093 v8::Handle<Value> value = CompileRun(
7094 "o.foo = 17;"
7095 "var receiver = {};"
7096 "receiver.__proto__ = o;"
7097 "var result = 0;"
7098 "var saved_result = 0;"
7099 "for (var i = 0; i < 100; i++) {"
7100 " result = receiver.method(41);"
7101 " if (i == 50) {"
7102 " saved_result = result;"
7103 " receiver = {method: function(x) { return x - 1 }};"
7104 " }"
7105 "}");
7106 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7107 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7108}
7109
Steve Block6ded16b2010-05-10 14:33:55 +01007110THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7111 v8::HandleScope scope;
7112 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7113 v8::Handle<v8::FunctionTemplate> method_templ =
7114 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7115 v8_str("method_data"),
7116 v8::Signature::New(fun_templ));
7117 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7118 proto_templ->Set(v8_str("method"), method_templ);
7119 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7120 LocalContext context;
7121 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7122 GenerateSomeGarbage();
7123 context->Global()->Set(v8_str("o"), fun->NewInstance());
7124 v8::TryCatch try_catch;
7125 v8::Handle<Value> value = CompileRun(
7126 "o.foo = 17;"
7127 "var receiver = {};"
7128 "receiver.__proto__ = o;"
7129 "var result = 0;"
7130 "var saved_result = 0;"
7131 "for (var i = 0; i < 100; i++) {"
7132 " result = receiver.method(41);"
7133 " if (i == 50) {"
7134 " saved_result = result;"
7135 " receiver = 333;"
7136 " }"
7137 "}");
7138 CHECK(try_catch.HasCaught());
7139 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7140 try_catch.Exception()->ToString());
7141 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7142}
7143
Leon Clarke4515c472010-02-03 11:58:03 +00007144
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007145v8::Handle<Value> keyed_call_ic_function;
7146
7147static v8::Handle<Value> InterceptorKeyedCallICGetter(
7148 Local<String> name, const AccessorInfo& info) {
7149 ApiTestFuzzer::Fuzz();
7150 if (v8_str("x")->Equals(name)) {
7151 return keyed_call_ic_function;
7152 }
7153 return v8::Handle<Value>();
7154}
7155
7156
7157// Test the case when we stored cacheable lookup into
7158// a stub, but the function name changed (to another cacheable function).
7159THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7160 v8::HandleScope scope;
7161 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7162 templ->SetNamedPropertyHandler(NoBlockGetterX);
7163 LocalContext context;
7164 context->Global()->Set(v8_str("o"), templ->NewInstance());
7165 v8::Handle<Value> value = CompileRun(
7166 "proto = new Object();"
7167 "proto.y = function(x) { return x + 1; };"
7168 "proto.z = function(x) { return x - 1; };"
7169 "o.__proto__ = proto;"
7170 "var result = 0;"
7171 "var method = 'y';"
7172 "for (var i = 0; i < 10; i++) {"
7173 " if (i == 5) { method = 'z'; };"
7174 " result += o[method](41);"
7175 "}");
7176 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7177}
7178
7179
7180// Test the case when we stored cacheable lookup into
7181// a stub, but the function name changed (and the new function is present
7182// both before and after the interceptor in the prototype chain).
7183THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7184 v8::HandleScope scope;
7185 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7186 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7187 LocalContext context;
7188 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7189 keyed_call_ic_function =
7190 v8_compile("function f(x) { return x - 1; }; f")->Run();
7191 v8::Handle<Value> value = CompileRun(
7192 "o = new Object();"
7193 "proto2 = new Object();"
7194 "o.y = function(x) { return x + 1; };"
7195 "proto2.y = function(x) { return x + 2; };"
7196 "o.__proto__ = proto1;"
7197 "proto1.__proto__ = proto2;"
7198 "var result = 0;"
7199 "var method = 'x';"
7200 "for (var i = 0; i < 10; i++) {"
7201 " if (i == 5) { method = 'y'; };"
7202 " result += o[method](41);"
7203 "}");
7204 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7205}
7206
7207
7208// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7209// on the global object.
7210THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7211 v8::HandleScope scope;
7212 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7213 templ->SetNamedPropertyHandler(NoBlockGetterX);
7214 LocalContext context;
7215 context->Global()->Set(v8_str("o"), templ->NewInstance());
7216 v8::Handle<Value> value = CompileRun(
7217 "function inc(x) { return x + 1; };"
7218 "inc(1);"
7219 "function dec(x) { return x - 1; };"
7220 "dec(1);"
7221 "o.__proto__ = this;"
7222 "this.__proto__.x = inc;"
7223 "this.__proto__.y = dec;"
7224 "var result = 0;"
7225 "var method = 'x';"
7226 "for (var i = 0; i < 10; i++) {"
7227 " if (i == 5) { method = 'y'; };"
7228 " result += o[method](41);"
7229 "}");
7230 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7231}
7232
7233
7234// Test the case when actual function to call sits on global object.
7235THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7236 v8::HandleScope scope;
7237 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7238 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7239 LocalContext context;
7240 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7241
7242 v8::Handle<Value> value = CompileRun(
7243 "function len(x) { return x.length; };"
7244 "o.__proto__ = this;"
7245 "var m = 'parseFloat';"
7246 "var result = 0;"
7247 "for (var i = 0; i < 10; i++) {"
7248 " if (i == 5) {"
7249 " m = 'len';"
7250 " saved_result = result;"
7251 " };"
7252 " result = o[m]('239');"
7253 "}");
7254 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7255 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7256}
7257
7258// Test the map transition before the interceptor.
7259THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7260 v8::HandleScope scope;
7261 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7262 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7263 LocalContext context;
7264 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7265
7266 v8::Handle<Value> value = CompileRun(
7267 "var o = new Object();"
7268 "o.__proto__ = proto;"
7269 "o.method = function(x) { return x + 1; };"
7270 "var m = 'method';"
7271 "var result = 0;"
7272 "for (var i = 0; i < 10; i++) {"
7273 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7274 " result += o[m](41);"
7275 "}");
7276 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7277}
7278
7279
7280// Test the map transition after the interceptor.
7281THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7282 v8::HandleScope scope;
7283 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7284 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7285 LocalContext context;
7286 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7287
7288 v8::Handle<Value> value = CompileRun(
7289 "var proto = new Object();"
7290 "o.__proto__ = proto;"
7291 "proto.method = function(x) { return x + 1; };"
7292 "var m = 'method';"
7293 "var result = 0;"
7294 "for (var i = 0; i < 10; i++) {"
7295 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7296 " result += o[m](41);"
7297 "}");
7298 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7299}
7300
7301
Steve Blocka7e24c12009-10-30 11:49:00 +00007302static int interceptor_call_count = 0;
7303
7304static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7305 const AccessorInfo& info) {
7306 ApiTestFuzzer::Fuzz();
7307 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7308 return call_ic_function2;
7309 }
7310 return v8::Handle<Value>();
7311}
7312
7313
7314// This test should hit load and call ICs for the interceptor case.
7315// Once in a while, the interceptor will reply that a property was not
7316// found in which case we should get a reference error.
7317THREADED_TEST(InterceptorICReferenceErrors) {
7318 v8::HandleScope scope;
7319 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7320 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7321 LocalContext context(0, templ, v8::Handle<Value>());
7322 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7323 v8::Handle<Value> value = CompileRun(
7324 "function f() {"
7325 " for (var i = 0; i < 1000; i++) {"
7326 " try { x; } catch(e) { return true; }"
7327 " }"
7328 " return false;"
7329 "};"
7330 "f();");
7331 CHECK_EQ(true, value->BooleanValue());
7332 interceptor_call_count = 0;
7333 value = CompileRun(
7334 "function g() {"
7335 " for (var i = 0; i < 1000; i++) {"
7336 " try { x(42); } catch(e) { return true; }"
7337 " }"
7338 " return false;"
7339 "};"
7340 "g();");
7341 CHECK_EQ(true, value->BooleanValue());
7342}
7343
7344
7345static int interceptor_ic_exception_get_count = 0;
7346
7347static v8::Handle<Value> InterceptorICExceptionGetter(
7348 Local<String> name,
7349 const AccessorInfo& info) {
7350 ApiTestFuzzer::Fuzz();
7351 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7352 return call_ic_function3;
7353 }
7354 if (interceptor_ic_exception_get_count == 20) {
7355 return v8::ThrowException(v8_num(42));
7356 }
7357 // Do not handle get for properties other than x.
7358 return v8::Handle<Value>();
7359}
7360
7361// Test interceptor load/call IC where the interceptor throws an
7362// exception once in a while.
7363THREADED_TEST(InterceptorICGetterExceptions) {
7364 interceptor_ic_exception_get_count = 0;
7365 v8::HandleScope scope;
7366 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7367 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7368 LocalContext context(0, templ, v8::Handle<Value>());
7369 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7370 v8::Handle<Value> value = CompileRun(
7371 "function f() {"
7372 " for (var i = 0; i < 100; i++) {"
7373 " try { x; } catch(e) { return true; }"
7374 " }"
7375 " return false;"
7376 "};"
7377 "f();");
7378 CHECK_EQ(true, value->BooleanValue());
7379 interceptor_ic_exception_get_count = 0;
7380 value = CompileRun(
7381 "function f() {"
7382 " for (var i = 0; i < 100; i++) {"
7383 " try { x(42); } catch(e) { return true; }"
7384 " }"
7385 " return false;"
7386 "};"
7387 "f();");
7388 CHECK_EQ(true, value->BooleanValue());
7389}
7390
7391
7392static int interceptor_ic_exception_set_count = 0;
7393
7394static v8::Handle<Value> InterceptorICExceptionSetter(
7395 Local<String> key, Local<Value> value, const AccessorInfo&) {
7396 ApiTestFuzzer::Fuzz();
7397 if (++interceptor_ic_exception_set_count > 20) {
7398 return v8::ThrowException(v8_num(42));
7399 }
7400 // Do not actually handle setting.
7401 return v8::Handle<Value>();
7402}
7403
7404// Test interceptor store IC where the interceptor throws an exception
7405// once in a while.
7406THREADED_TEST(InterceptorICSetterExceptions) {
7407 interceptor_ic_exception_set_count = 0;
7408 v8::HandleScope scope;
7409 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7410 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7411 LocalContext context(0, templ, v8::Handle<Value>());
7412 v8::Handle<Value> value = CompileRun(
7413 "function f() {"
7414 " for (var i = 0; i < 100; i++) {"
7415 " try { x = 42; } catch(e) { return true; }"
7416 " }"
7417 " return false;"
7418 "};"
7419 "f();");
7420 CHECK_EQ(true, value->BooleanValue());
7421}
7422
7423
7424// Test that we ignore null interceptors.
7425THREADED_TEST(NullNamedInterceptor) {
7426 v8::HandleScope scope;
7427 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7428 templ->SetNamedPropertyHandler(0);
7429 LocalContext context;
7430 templ->Set("x", v8_num(42));
7431 v8::Handle<v8::Object> obj = templ->NewInstance();
7432 context->Global()->Set(v8_str("obj"), obj);
7433 v8::Handle<Value> value = CompileRun("obj.x");
7434 CHECK(value->IsInt32());
7435 CHECK_EQ(42, value->Int32Value());
7436}
7437
7438
7439// Test that we ignore null interceptors.
7440THREADED_TEST(NullIndexedInterceptor) {
7441 v8::HandleScope scope;
7442 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7443 templ->SetIndexedPropertyHandler(0);
7444 LocalContext context;
7445 templ->Set("42", v8_num(42));
7446 v8::Handle<v8::Object> obj = templ->NewInstance();
7447 context->Global()->Set(v8_str("obj"), obj);
7448 v8::Handle<Value> value = CompileRun("obj[42]");
7449 CHECK(value->IsInt32());
7450 CHECK_EQ(42, value->Int32Value());
7451}
7452
7453
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007454THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7455 v8::HandleScope scope;
7456 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7457 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7458 LocalContext env;
7459 env->Global()->Set(v8_str("obj"),
7460 templ->GetFunction()->NewInstance());
7461 ExpectTrue("obj.x === 42");
7462 ExpectTrue("!obj.propertyIsEnumerable('x')");
7463}
7464
7465
Steve Blocka7e24c12009-10-30 11:49:00 +00007466static v8::Handle<Value> ParentGetter(Local<String> name,
7467 const AccessorInfo& info) {
7468 ApiTestFuzzer::Fuzz();
7469 return v8_num(1);
7470}
7471
7472
7473static v8::Handle<Value> ChildGetter(Local<String> name,
7474 const AccessorInfo& info) {
7475 ApiTestFuzzer::Fuzz();
7476 return v8_num(42);
7477}
7478
7479
7480THREADED_TEST(Overriding) {
7481 v8::HandleScope scope;
7482 LocalContext context;
7483
7484 // Parent template.
7485 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7486 Local<ObjectTemplate> parent_instance_templ =
7487 parent_templ->InstanceTemplate();
7488 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7489
7490 // Template that inherits from the parent template.
7491 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7492 Local<ObjectTemplate> child_instance_templ =
7493 child_templ->InstanceTemplate();
7494 child_templ->Inherit(parent_templ);
7495 // Override 'f'. The child version of 'f' should get called for child
7496 // instances.
7497 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7498 // Add 'g' twice. The 'g' added last should get called for instances.
7499 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7500 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7501
7502 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7503 // so 'h' can be shadowed on the instance object.
7504 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7505 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7506 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7507
7508 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7509 // but the attribute does not have effect because it is duplicated with
7510 // NULL setter.
7511 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7512 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7513
7514
7515
7516 // Instantiate the child template.
7517 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7518
7519 // Check that the child function overrides the parent one.
7520 context->Global()->Set(v8_str("o"), instance);
7521 Local<Value> value = v8_compile("o.f")->Run();
7522 // Check that the 'g' that was added last is hit.
7523 CHECK_EQ(42, value->Int32Value());
7524 value = v8_compile("o.g")->Run();
7525 CHECK_EQ(42, value->Int32Value());
7526
7527 // Check 'h' can be shadowed.
7528 value = v8_compile("o.h = 3; o.h")->Run();
7529 CHECK_EQ(3, value->Int32Value());
7530
7531 // Check 'i' is cannot be shadowed or changed.
7532 value = v8_compile("o.i = 3; o.i")->Run();
7533 CHECK_EQ(42, value->Int32Value());
7534}
7535
7536
7537static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7538 ApiTestFuzzer::Fuzz();
7539 if (args.IsConstructCall()) {
7540 return v8::Boolean::New(true);
7541 }
7542 return v8::Boolean::New(false);
7543}
7544
7545
7546THREADED_TEST(IsConstructCall) {
7547 v8::HandleScope scope;
7548
7549 // Function template with call handler.
7550 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7551 templ->SetCallHandler(IsConstructHandler);
7552
7553 LocalContext context;
7554
7555 context->Global()->Set(v8_str("f"), templ->GetFunction());
7556 Local<Value> value = v8_compile("f()")->Run();
7557 CHECK(!value->BooleanValue());
7558 value = v8_compile("new f()")->Run();
7559 CHECK(value->BooleanValue());
7560}
7561
7562
7563THREADED_TEST(ObjectProtoToString) {
7564 v8::HandleScope scope;
7565 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7566 templ->SetClassName(v8_str("MyClass"));
7567
7568 LocalContext context;
7569
7570 Local<String> customized_tostring = v8_str("customized toString");
7571
7572 // Replace Object.prototype.toString
7573 v8_compile("Object.prototype.toString = function() {"
7574 " return 'customized toString';"
7575 "}")->Run();
7576
7577 // Normal ToString call should call replaced Object.prototype.toString
7578 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7579 Local<String> value = instance->ToString();
7580 CHECK(value->IsString() && value->Equals(customized_tostring));
7581
7582 // ObjectProtoToString should not call replace toString function.
7583 value = instance->ObjectProtoToString();
7584 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7585
7586 // Check global
7587 value = context->Global()->ObjectProtoToString();
7588 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7589
7590 // Check ordinary object
7591 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007592 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007593 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7594}
7595
7596
7597bool ApiTestFuzzer::fuzzing_ = false;
7598v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_=
7599 v8::internal::OS::CreateSemaphore(0);
7600int ApiTestFuzzer::active_tests_;
7601int ApiTestFuzzer::tests_being_run_;
7602int ApiTestFuzzer::current_;
7603
7604
7605// We are in a callback and want to switch to another thread (if we
7606// are currently running the thread fuzzing test).
7607void ApiTestFuzzer::Fuzz() {
7608 if (!fuzzing_) return;
7609 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7610 test->ContextSwitch();
7611}
7612
7613
7614// Let the next thread go. Since it is also waiting on the V8 lock it may
7615// not start immediately.
7616bool ApiTestFuzzer::NextThread() {
7617 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007618 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007619 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007620 if (kLogThreading)
7621 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007622 return false;
7623 }
Steve Blockd0582a62009-12-15 09:54:21 +00007624 if (kLogThreading) {
7625 printf("Switch from %s to %s\n",
7626 test_name,
7627 RegisterThreadedTest::nth(test_position)->name());
7628 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007629 current_ = test_position;
7630 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7631 return true;
7632}
7633
7634
7635void ApiTestFuzzer::Run() {
7636 // When it is our turn...
7637 gate_->Wait();
7638 {
7639 // ... get the V8 lock and start running the test.
7640 v8::Locker locker;
7641 CallTest();
7642 }
7643 // This test finished.
7644 active_ = false;
7645 active_tests_--;
7646 // If it was the last then signal that fact.
7647 if (active_tests_ == 0) {
7648 all_tests_done_->Signal();
7649 } else {
7650 // Otherwise select a new test and start that.
7651 NextThread();
7652 }
7653}
7654
7655
7656static unsigned linear_congruential_generator;
7657
7658
7659void ApiTestFuzzer::Setup(PartOfTest part) {
7660 linear_congruential_generator = i::FLAG_testing_prng_seed;
7661 fuzzing_ = true;
7662 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7663 int end = (part == FIRST_PART)
7664 ? (RegisterThreadedTest::count() >> 1)
7665 : RegisterThreadedTest::count();
7666 active_tests_ = tests_being_run_ = end - start;
7667 for (int i = 0; i < tests_being_run_; i++) {
7668 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7669 }
7670 for (int i = 0; i < active_tests_; i++) {
7671 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7672 }
7673}
7674
7675
7676static void CallTestNumber(int test_number) {
7677 (RegisterThreadedTest::nth(test_number)->callback())();
7678}
7679
7680
7681void ApiTestFuzzer::RunAllTests() {
7682 // Set off the first test.
7683 current_ = -1;
7684 NextThread();
7685 // Wait till they are all done.
7686 all_tests_done_->Wait();
7687}
7688
7689
7690int ApiTestFuzzer::GetNextTestNumber() {
7691 int next_test;
7692 do {
7693 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7694 linear_congruential_generator *= 1664525u;
7695 linear_congruential_generator += 1013904223u;
7696 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7697 return next_test;
7698}
7699
7700
7701void ApiTestFuzzer::ContextSwitch() {
7702 // If the new thread is the same as the current thread there is nothing to do.
7703 if (NextThread()) {
7704 // Now it can start.
7705 v8::Unlocker unlocker;
7706 // Wait till someone starts us again.
7707 gate_->Wait();
7708 // And we're off.
7709 }
7710}
7711
7712
7713void ApiTestFuzzer::TearDown() {
7714 fuzzing_ = false;
7715 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7716 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7717 if (fuzzer != NULL) fuzzer->Join();
7718 }
7719}
7720
7721
7722// Lets not be needlessly self-referential.
7723TEST(Threading) {
7724 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7725 ApiTestFuzzer::RunAllTests();
7726 ApiTestFuzzer::TearDown();
7727}
7728
7729TEST(Threading2) {
7730 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7731 ApiTestFuzzer::RunAllTests();
7732 ApiTestFuzzer::TearDown();
7733}
7734
7735
7736void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007737 if (kLogThreading)
7738 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007739 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007740 if (kLogThreading)
7741 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007742}
7743
7744
7745static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7746 CHECK(v8::Locker::IsLocked());
7747 ApiTestFuzzer::Fuzz();
7748 v8::Unlocker unlocker;
7749 const char* code = "throw 7;";
7750 {
7751 v8::Locker nested_locker;
7752 v8::HandleScope scope;
7753 v8::Handle<Value> exception;
7754 { v8::TryCatch try_catch;
7755 v8::Handle<Value> value = CompileRun(code);
7756 CHECK(value.IsEmpty());
7757 CHECK(try_catch.HasCaught());
7758 // Make sure to wrap the exception in a new handle because
7759 // the handle returned from the TryCatch is destroyed
7760 // when the TryCatch is destroyed.
7761 exception = Local<Value>::New(try_catch.Exception());
7762 }
7763 return v8::ThrowException(exception);
7764 }
7765}
7766
7767
7768static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7769 CHECK(v8::Locker::IsLocked());
7770 ApiTestFuzzer::Fuzz();
7771 v8::Unlocker unlocker;
7772 const char* code = "throw 7;";
7773 {
7774 v8::Locker nested_locker;
7775 v8::HandleScope scope;
7776 v8::Handle<Value> value = CompileRun(code);
7777 CHECK(value.IsEmpty());
7778 return v8_str("foo");
7779 }
7780}
7781
7782
7783// These are locking tests that don't need to be run again
7784// as part of the locking aggregation tests.
7785TEST(NestedLockers) {
7786 v8::Locker locker;
7787 CHECK(v8::Locker::IsLocked());
7788 v8::HandleScope scope;
7789 LocalContext env;
7790 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7791 Local<Function> fun = fun_templ->GetFunction();
7792 env->Global()->Set(v8_str("throw_in_js"), fun);
7793 Local<Script> script = v8_compile("(function () {"
7794 " try {"
7795 " throw_in_js();"
7796 " return 42;"
7797 " } catch (e) {"
7798 " return e * 13;"
7799 " }"
7800 "})();");
7801 CHECK_EQ(91, script->Run()->Int32Value());
7802}
7803
7804
7805// These are locking tests that don't need to be run again
7806// as part of the locking aggregation tests.
7807TEST(NestedLockersNoTryCatch) {
7808 v8::Locker locker;
7809 v8::HandleScope scope;
7810 LocalContext env;
7811 Local<v8::FunctionTemplate> fun_templ =
7812 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7813 Local<Function> fun = fun_templ->GetFunction();
7814 env->Global()->Set(v8_str("throw_in_js"), fun);
7815 Local<Script> script = v8_compile("(function () {"
7816 " try {"
7817 " throw_in_js();"
7818 " return 42;"
7819 " } catch (e) {"
7820 " return e * 13;"
7821 " }"
7822 "})();");
7823 CHECK_EQ(91, script->Run()->Int32Value());
7824}
7825
7826
7827THREADED_TEST(RecursiveLocking) {
7828 v8::Locker locker;
7829 {
7830 v8::Locker locker2;
7831 CHECK(v8::Locker::IsLocked());
7832 }
7833}
7834
7835
7836static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
7837 ApiTestFuzzer::Fuzz();
7838 v8::Unlocker unlocker;
7839 return v8::Undefined();
7840}
7841
7842
7843THREADED_TEST(LockUnlockLock) {
7844 {
7845 v8::Locker locker;
7846 v8::HandleScope scope;
7847 LocalContext env;
7848 Local<v8::FunctionTemplate> fun_templ =
7849 v8::FunctionTemplate::New(UnlockForAMoment);
7850 Local<Function> fun = fun_templ->GetFunction();
7851 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7852 Local<Script> script = v8_compile("(function () {"
7853 " unlock_for_a_moment();"
7854 " return 42;"
7855 "})();");
7856 CHECK_EQ(42, script->Run()->Int32Value());
7857 }
7858 {
7859 v8::Locker locker;
7860 v8::HandleScope scope;
7861 LocalContext env;
7862 Local<v8::FunctionTemplate> fun_templ =
7863 v8::FunctionTemplate::New(UnlockForAMoment);
7864 Local<Function> fun = fun_templ->GetFunction();
7865 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7866 Local<Script> script = v8_compile("(function () {"
7867 " unlock_for_a_moment();"
7868 " return 42;"
7869 "})();");
7870 CHECK_EQ(42, script->Run()->Int32Value());
7871 }
7872}
7873
7874
Leon Clarked91b9f72010-01-27 17:25:45 +00007875static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00007876 int count = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +00007877 v8::internal::HeapIterator it;
7878 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
7879 if (object->IsJSGlobalObject()) count++;
7880 return count;
7881}
7882
7883
7884static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00007885 // We need to collect all garbage twice to be sure that everything
7886 // has been collected. This is because inline caches are cleared in
7887 // the first garbage collection but some of the maps have already
7888 // been marked at that point. Therefore some of the maps are not
7889 // collected until the second garbage collection.
7890 v8::internal::Heap::CollectAllGarbage(false);
7891 v8::internal::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00007892 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00007893#ifdef DEBUG
7894 if (count > 0) v8::internal::Heap::TracePathToGlobal();
7895#endif
7896 return count;
7897}
7898
7899
7900TEST(DontLeakGlobalObjects) {
7901 // Regression test for issues 1139850 and 1174891.
7902
7903 v8::V8::Initialize();
7904
7905 int count = GetSurvivingGlobalObjectsCount();
7906
7907 for (int i = 0; i < 5; i++) {
7908 { v8::HandleScope scope;
7909 LocalContext context;
7910 }
7911 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7912
7913 { v8::HandleScope scope;
7914 LocalContext context;
7915 v8_compile("Date")->Run();
7916 }
7917 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7918
7919 { v8::HandleScope scope;
7920 LocalContext context;
7921 v8_compile("/aaa/")->Run();
7922 }
7923 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7924
7925 { v8::HandleScope scope;
7926 const char* extension_list[] = { "v8/gc" };
7927 v8::ExtensionConfiguration extensions(1, extension_list);
7928 LocalContext context(&extensions);
7929 v8_compile("gc();")->Run();
7930 }
7931 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7932 }
7933}
7934
7935
7936v8::Persistent<v8::Object> some_object;
7937v8::Persistent<v8::Object> bad_handle;
7938
7939void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
7940 v8::HandleScope scope;
7941 bad_handle = v8::Persistent<v8::Object>::New(some_object);
7942}
7943
7944
7945THREADED_TEST(NewPersistentHandleFromWeakCallback) {
7946 LocalContext context;
7947
7948 v8::Persistent<v8::Object> handle1, handle2;
7949 {
7950 v8::HandleScope scope;
7951 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
7952 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7953 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7954 }
7955 // Note: order is implementation dependent alas: currently
7956 // global handle nodes are processed by PostGarbageCollectionProcessing
7957 // in reverse allocation order, so if second allocated handle is deleted,
7958 // weak callback of the first handle would be able to 'reallocate' it.
7959 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
7960 handle2.Dispose();
7961 i::Heap::CollectAllGarbage(false);
7962}
7963
7964
7965v8::Persistent<v8::Object> to_be_disposed;
7966
7967void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
7968 to_be_disposed.Dispose();
7969 i::Heap::CollectAllGarbage(false);
7970}
7971
7972
7973THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
7974 LocalContext context;
7975
7976 v8::Persistent<v8::Object> handle1, handle2;
7977 {
7978 v8::HandleScope scope;
7979 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7980 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7981 }
7982 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
7983 to_be_disposed = handle2;
7984 i::Heap::CollectAllGarbage(false);
7985}
7986
Steve Blockd0582a62009-12-15 09:54:21 +00007987void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
7988 handle.Dispose();
7989}
7990
7991void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
7992 v8::HandleScope scope;
7993 v8::Persistent<v8::Object>::New(v8::Object::New());
7994}
7995
7996
7997THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
7998 LocalContext context;
7999
8000 v8::Persistent<v8::Object> handle1, handle2, handle3;
8001 {
8002 v8::HandleScope scope;
8003 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8004 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8005 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8006 }
8007 handle2.MakeWeak(NULL, DisposingCallback);
8008 handle3.MakeWeak(NULL, HandleCreatingCallback);
8009 i::Heap::CollectAllGarbage(false);
8010}
8011
Steve Blocka7e24c12009-10-30 11:49:00 +00008012
8013THREADED_TEST(CheckForCrossContextObjectLiterals) {
8014 v8::V8::Initialize();
8015
8016 const int nof = 2;
8017 const char* sources[nof] = {
8018 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8019 "Object()"
8020 };
8021
8022 for (int i = 0; i < nof; i++) {
8023 const char* source = sources[i];
8024 { v8::HandleScope scope;
8025 LocalContext context;
8026 CompileRun(source);
8027 }
8028 { v8::HandleScope scope;
8029 LocalContext context;
8030 CompileRun(source);
8031 }
8032 }
8033}
8034
8035
8036static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8037 v8::HandleScope inner;
8038 env->Enter();
8039 v8::Handle<Value> three = v8_num(3);
8040 v8::Handle<Value> value = inner.Close(three);
8041 env->Exit();
8042 return value;
8043}
8044
8045
8046THREADED_TEST(NestedHandleScopeAndContexts) {
8047 v8::HandleScope outer;
8048 v8::Persistent<Context> env = Context::New();
8049 env->Enter();
8050 v8::Handle<Value> value = NestedScope(env);
8051 v8::Handle<String> str = value->ToString();
8052 env->Exit();
8053 env.Dispose();
8054}
8055
8056
8057THREADED_TEST(ExternalAllocatedMemory) {
8058 v8::HandleScope outer;
8059 v8::Persistent<Context> env = Context::New();
8060 const int kSize = 1024*1024;
8061 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8062 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8063}
8064
8065
8066THREADED_TEST(DisposeEnteredContext) {
8067 v8::HandleScope scope;
8068 LocalContext outer;
8069 { v8::Persistent<v8::Context> inner = v8::Context::New();
8070 inner->Enter();
8071 inner.Dispose();
8072 inner.Clear();
8073 inner->Exit();
8074 }
8075}
8076
8077
8078// Regression test for issue 54, object templates with internal fields
8079// but no accessors or interceptors did not get their internal field
8080// count set on instances.
8081THREADED_TEST(Regress54) {
8082 v8::HandleScope outer;
8083 LocalContext context;
8084 static v8::Persistent<v8::ObjectTemplate> templ;
8085 if (templ.IsEmpty()) {
8086 v8::HandleScope inner;
8087 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8088 local->SetInternalFieldCount(1);
8089 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8090 }
8091 v8::Handle<v8::Object> result = templ->NewInstance();
8092 CHECK_EQ(1, result->InternalFieldCount());
8093}
8094
8095
8096// If part of the threaded tests, this test makes ThreadingTest fail
8097// on mac.
8098TEST(CatchStackOverflow) {
8099 v8::HandleScope scope;
8100 LocalContext context;
8101 v8::TryCatch try_catch;
8102 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8103 "function f() {"
8104 " return f();"
8105 "}"
8106 ""
8107 "f();"));
8108 v8::Handle<v8::Value> result = script->Run();
8109 CHECK(result.IsEmpty());
8110}
8111
8112
8113static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8114 const char* resource_name,
8115 int line_offset) {
8116 v8::HandleScope scope;
8117 v8::TryCatch try_catch;
8118 v8::Handle<v8::Value> result = script->Run();
8119 CHECK(result.IsEmpty());
8120 CHECK(try_catch.HasCaught());
8121 v8::Handle<v8::Message> message = try_catch.Message();
8122 CHECK(!message.IsEmpty());
8123 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8124 CHECK_EQ(91, message->GetStartPosition());
8125 CHECK_EQ(92, message->GetEndPosition());
8126 CHECK_EQ(2, message->GetStartColumn());
8127 CHECK_EQ(3, message->GetEndColumn());
8128 v8::String::AsciiValue line(message->GetSourceLine());
8129 CHECK_EQ(" throw 'nirk';", *line);
8130 v8::String::AsciiValue name(message->GetScriptResourceName());
8131 CHECK_EQ(resource_name, *name);
8132}
8133
8134
8135THREADED_TEST(TryCatchSourceInfo) {
8136 v8::HandleScope scope;
8137 LocalContext context;
8138 v8::Handle<v8::String> source = v8::String::New(
8139 "function Foo() {\n"
8140 " return Bar();\n"
8141 "}\n"
8142 "\n"
8143 "function Bar() {\n"
8144 " return Baz();\n"
8145 "}\n"
8146 "\n"
8147 "function Baz() {\n"
8148 " throw 'nirk';\n"
8149 "}\n"
8150 "\n"
8151 "Foo();\n");
8152
8153 const char* resource_name;
8154 v8::Handle<v8::Script> script;
8155 resource_name = "test.js";
8156 script = v8::Script::Compile(source, v8::String::New(resource_name));
8157 CheckTryCatchSourceInfo(script, resource_name, 0);
8158
8159 resource_name = "test1.js";
8160 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8161 script = v8::Script::Compile(source, &origin1);
8162 CheckTryCatchSourceInfo(script, resource_name, 0);
8163
8164 resource_name = "test2.js";
8165 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8166 script = v8::Script::Compile(source, &origin2);
8167 CheckTryCatchSourceInfo(script, resource_name, 7);
8168}
8169
8170
8171THREADED_TEST(CompilationCache) {
8172 v8::HandleScope scope;
8173 LocalContext context;
8174 v8::Handle<v8::String> source0 = v8::String::New("1234");
8175 v8::Handle<v8::String> source1 = v8::String::New("1234");
8176 v8::Handle<v8::Script> script0 =
8177 v8::Script::Compile(source0, v8::String::New("test.js"));
8178 v8::Handle<v8::Script> script1 =
8179 v8::Script::Compile(source1, v8::String::New("test.js"));
8180 v8::Handle<v8::Script> script2 =
8181 v8::Script::Compile(source0); // different origin
8182 CHECK_EQ(1234, script0->Run()->Int32Value());
8183 CHECK_EQ(1234, script1->Run()->Int32Value());
8184 CHECK_EQ(1234, script2->Run()->Int32Value());
8185}
8186
8187
8188static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8189 ApiTestFuzzer::Fuzz();
8190 return v8_num(42);
8191}
8192
8193
8194THREADED_TEST(CallbackFunctionName) {
8195 v8::HandleScope scope;
8196 LocalContext context;
8197 Local<ObjectTemplate> t = ObjectTemplate::New();
8198 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8199 context->Global()->Set(v8_str("obj"), t->NewInstance());
8200 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8201 CHECK(value->IsString());
8202 v8::String::AsciiValue name(value);
8203 CHECK_EQ("asdf", *name);
8204}
8205
8206
8207THREADED_TEST(DateAccess) {
8208 v8::HandleScope scope;
8209 LocalContext context;
8210 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8211 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008212 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008213}
8214
8215
8216void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008217 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008218 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8219 CHECK_EQ(elmc, props->Length());
8220 for (int i = 0; i < elmc; i++) {
8221 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8222 CHECK_EQ(elmv[i], *elm);
8223 }
8224}
8225
8226
8227THREADED_TEST(PropertyEnumeration) {
8228 v8::HandleScope scope;
8229 LocalContext context;
8230 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8231 "var result = [];"
8232 "result[0] = {};"
8233 "result[1] = {a: 1, b: 2};"
8234 "result[2] = [1, 2, 3];"
8235 "var proto = {x: 1, y: 2, z: 3};"
8236 "var x = { __proto__: proto, w: 0, z: 1 };"
8237 "result[3] = x;"
8238 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008239 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008240 CHECK_EQ(4, elms->Length());
8241 int elmc0 = 0;
8242 const char** elmv0 = NULL;
8243 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8244 int elmc1 = 2;
8245 const char* elmv1[] = {"a", "b"};
8246 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8247 int elmc2 = 3;
8248 const char* elmv2[] = {"0", "1", "2"};
8249 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8250 int elmc3 = 4;
8251 const char* elmv3[] = {"w", "z", "x", "y"};
8252 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8253}
8254
8255
Steve Blocka7e24c12009-10-30 11:49:00 +00008256static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8257 Local<Value> name,
8258 v8::AccessType type,
8259 Local<Value> data) {
8260 return type != v8::ACCESS_SET;
8261}
8262
8263
8264static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8265 uint32_t key,
8266 v8::AccessType type,
8267 Local<Value> data) {
8268 return type != v8::ACCESS_SET;
8269}
8270
8271
8272THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8273 v8::HandleScope scope;
8274 LocalContext context;
8275 Local<ObjectTemplate> templ = ObjectTemplate::New();
8276 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8277 IndexedSetAccessBlocker);
8278 templ->Set(v8_str("x"), v8::True());
8279 Local<v8::Object> instance = templ->NewInstance();
8280 context->Global()->Set(v8_str("obj"), instance);
8281 Local<Value> value = CompileRun("obj.x");
8282 CHECK(value->BooleanValue());
8283}
8284
8285
8286static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8287 Local<Value> name,
8288 v8::AccessType type,
8289 Local<Value> data) {
8290 return false;
8291}
8292
8293
8294static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8295 uint32_t key,
8296 v8::AccessType type,
8297 Local<Value> data) {
8298 return false;
8299}
8300
8301
8302
8303THREADED_TEST(AccessChecksReenabledCorrectly) {
8304 v8::HandleScope scope;
8305 LocalContext context;
8306 Local<ObjectTemplate> templ = ObjectTemplate::New();
8307 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8308 IndexedGetAccessBlocker);
8309 templ->Set(v8_str("a"), v8_str("a"));
8310 // Add more than 8 (see kMaxFastProperties) properties
8311 // so that the constructor will force copying map.
8312 // Cannot sprintf, gcc complains unsafety.
8313 char buf[4];
8314 for (char i = '0'; i <= '9' ; i++) {
8315 buf[0] = i;
8316 for (char j = '0'; j <= '9'; j++) {
8317 buf[1] = j;
8318 for (char k = '0'; k <= '9'; k++) {
8319 buf[2] = k;
8320 buf[3] = 0;
8321 templ->Set(v8_str(buf), v8::Number::New(k));
8322 }
8323 }
8324 }
8325
8326 Local<v8::Object> instance_1 = templ->NewInstance();
8327 context->Global()->Set(v8_str("obj_1"), instance_1);
8328
8329 Local<Value> value_1 = CompileRun("obj_1.a");
8330 CHECK(value_1->IsUndefined());
8331
8332 Local<v8::Object> instance_2 = templ->NewInstance();
8333 context->Global()->Set(v8_str("obj_2"), instance_2);
8334
8335 Local<Value> value_2 = CompileRun("obj_2.a");
8336 CHECK(value_2->IsUndefined());
8337}
8338
8339
8340// This tests that access check information remains on the global
8341// object template when creating contexts.
8342THREADED_TEST(AccessControlRepeatedContextCreation) {
8343 v8::HandleScope handle_scope;
8344 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8345 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8346 IndexedSetAccessBlocker);
8347 i::Handle<i::ObjectTemplateInfo> internal_template =
8348 v8::Utils::OpenHandle(*global_template);
8349 CHECK(!internal_template->constructor()->IsUndefined());
8350 i::Handle<i::FunctionTemplateInfo> constructor(
8351 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8352 CHECK(!constructor->access_check_info()->IsUndefined());
8353 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8354 CHECK(!constructor->access_check_info()->IsUndefined());
8355}
8356
8357
8358THREADED_TEST(TurnOnAccessCheck) {
8359 v8::HandleScope handle_scope;
8360
8361 // Create an environment with access check to the global object disabled by
8362 // default.
8363 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8364 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8365 IndexedGetAccessBlocker,
8366 v8::Handle<v8::Value>(),
8367 false);
8368 v8::Persistent<Context> context = Context::New(NULL, global_template);
8369 Context::Scope context_scope(context);
8370
8371 // Set up a property and a number of functions.
8372 context->Global()->Set(v8_str("a"), v8_num(1));
8373 CompileRun("function f1() {return a;}"
8374 "function f2() {return a;}"
8375 "function g1() {return h();}"
8376 "function g2() {return h();}"
8377 "function h() {return 1;}");
8378 Local<Function> f1 =
8379 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8380 Local<Function> f2 =
8381 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8382 Local<Function> g1 =
8383 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8384 Local<Function> g2 =
8385 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8386 Local<Function> h =
8387 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8388
8389 // Get the global object.
8390 v8::Handle<v8::Object> global = context->Global();
8391
8392 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8393 // uses the runtime system to retreive property a whereas f2 uses global load
8394 // inline cache.
8395 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8396 for (int i = 0; i < 4; i++) {
8397 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8398 }
8399
8400 // Same for g1 and g2.
8401 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8402 for (int i = 0; i < 4; i++) {
8403 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8404 }
8405
8406 // Detach the global and turn on access check.
8407 context->DetachGlobal();
8408 context->Global()->TurnOnAccessCheck();
8409
8410 // Failing access check to property get results in undefined.
8411 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8412 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8413
8414 // Failing access check to function call results in exception.
8415 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8416 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8417
8418 // No failing access check when just returning a constant.
8419 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8420}
8421
8422
8423// This test verifies that pre-compilation (aka preparsing) can be called
8424// without initializing the whole VM. Thus we cannot run this test in a
8425// multi-threaded setup.
8426TEST(PreCompile) {
8427 // TODO(155): This test would break without the initialization of V8. This is
8428 // a workaround for now to make this test not fail.
8429 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008430 const char* script = "function foo(a) { return a+1; }";
8431 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008432 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008433 CHECK_NE(sd->Length(), 0);
8434 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008435 CHECK(!sd->HasError());
8436 delete sd;
8437}
8438
8439
8440TEST(PreCompileWithError) {
8441 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008442 const char* script = "function foo(a) { return 1 * * 2; }";
8443 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008444 v8::ScriptData::PreCompile(script, i::StrLength(script));
8445 CHECK(sd->HasError());
8446 delete sd;
8447}
8448
8449
8450TEST(Regress31661) {
8451 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008452 const char* script = " The Definintive Guide";
8453 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008454 v8::ScriptData::PreCompile(script, i::StrLength(script));
8455 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008456 delete sd;
8457}
8458
8459
Leon Clarkef7060e22010-06-03 12:02:55 +01008460// Tests that ScriptData can be serialized and deserialized.
8461TEST(PreCompileSerialization) {
8462 v8::V8::Initialize();
8463 const char* script = "function foo(a) { return a+1; }";
8464 v8::ScriptData* sd =
8465 v8::ScriptData::PreCompile(script, i::StrLength(script));
8466
8467 // Serialize.
8468 int serialized_data_length = sd->Length();
8469 char* serialized_data = i::NewArray<char>(serialized_data_length);
8470 memcpy(serialized_data, sd->Data(), serialized_data_length);
8471
8472 // Deserialize.
8473 v8::ScriptData* deserialized_sd =
8474 v8::ScriptData::New(serialized_data, serialized_data_length);
8475
8476 // Verify that the original is the same as the deserialized.
8477 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8478 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8479 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8480
8481 delete sd;
8482 delete deserialized_sd;
8483}
8484
8485
8486// Attempts to deserialize bad data.
8487TEST(PreCompileDeserializationError) {
8488 v8::V8::Initialize();
8489 const char* data = "DONT CARE";
8490 int invalid_size = 3;
8491 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8492
8493 CHECK_EQ(0, sd->Length());
8494
8495 delete sd;
8496}
8497
8498
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008499// Verifies that the Handle<String> and const char* versions of the API produce
8500// the same results (at least for one trivial case).
8501TEST(PreCompileAPIVariationsAreSame) {
8502 v8::V8::Initialize();
8503 v8::HandleScope scope;
8504
8505 const char* cstring = "function foo(a) { return a+1; }";
8506 v8::ScriptData* sd_from_cstring =
8507 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8508
8509 TestAsciiResource* resource = new TestAsciiResource(cstring);
8510 v8::ScriptData* sd_from_istring = v8::ScriptData::PreCompile(
8511 v8::String::NewExternal(resource));
8512
8513 CHECK_EQ(sd_from_cstring->Length(), sd_from_istring->Length());
8514 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8515 sd_from_istring->Data(),
8516 sd_from_cstring->Length()));
8517
8518 delete sd_from_cstring;
8519 delete sd_from_istring;
8520}
8521
8522
Steve Blocka7e24c12009-10-30 11:49:00 +00008523// This tests that we do not allow dictionary load/call inline caches
8524// to use functions that have not yet been compiled. The potential
8525// problem of loading a function that has not yet been compiled can
8526// arise because we share code between contexts via the compilation
8527// cache.
8528THREADED_TEST(DictionaryICLoadedFunction) {
8529 v8::HandleScope scope;
8530 // Test LoadIC.
8531 for (int i = 0; i < 2; i++) {
8532 LocalContext context;
8533 context->Global()->Set(v8_str("tmp"), v8::True());
8534 context->Global()->Delete(v8_str("tmp"));
8535 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8536 }
8537 // Test CallIC.
8538 for (int i = 0; i < 2; i++) {
8539 LocalContext context;
8540 context->Global()->Set(v8_str("tmp"), v8::True());
8541 context->Global()->Delete(v8_str("tmp"));
8542 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8543 }
8544}
8545
8546
8547// Test that cross-context new calls use the context of the callee to
8548// create the new JavaScript object.
8549THREADED_TEST(CrossContextNew) {
8550 v8::HandleScope scope;
8551 v8::Persistent<Context> context0 = Context::New();
8552 v8::Persistent<Context> context1 = Context::New();
8553
8554 // Allow cross-domain access.
8555 Local<String> token = v8_str("<security token>");
8556 context0->SetSecurityToken(token);
8557 context1->SetSecurityToken(token);
8558
8559 // Set an 'x' property on the Object prototype and define a
8560 // constructor function in context0.
8561 context0->Enter();
8562 CompileRun("Object.prototype.x = 42; function C() {};");
8563 context0->Exit();
8564
8565 // Call the constructor function from context0 and check that the
8566 // result has the 'x' property.
8567 context1->Enter();
8568 context1->Global()->Set(v8_str("other"), context0->Global());
8569 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8570 CHECK(value->IsInt32());
8571 CHECK_EQ(42, value->Int32Value());
8572 context1->Exit();
8573
8574 // Dispose the contexts to allow them to be garbage collected.
8575 context0.Dispose();
8576 context1.Dispose();
8577}
8578
8579
8580class RegExpInterruptTest {
8581 public:
8582 RegExpInterruptTest() : block_(NULL) {}
8583 ~RegExpInterruptTest() { delete block_; }
8584 void RunTest() {
8585 block_ = i::OS::CreateSemaphore(0);
8586 gc_count_ = 0;
8587 gc_during_regexp_ = 0;
8588 regexp_success_ = false;
8589 gc_success_ = false;
8590 GCThread gc_thread(this);
8591 gc_thread.Start();
8592 v8::Locker::StartPreemption(1);
8593
8594 LongRunningRegExp();
8595 {
8596 v8::Unlocker unlock;
8597 gc_thread.Join();
8598 }
8599 v8::Locker::StopPreemption();
8600 CHECK(regexp_success_);
8601 CHECK(gc_success_);
8602 }
8603 private:
8604 // Number of garbage collections required.
8605 static const int kRequiredGCs = 5;
8606
8607 class GCThread : public i::Thread {
8608 public:
8609 explicit GCThread(RegExpInterruptTest* test)
8610 : test_(test) {}
8611 virtual void Run() {
8612 test_->CollectGarbage();
8613 }
8614 private:
8615 RegExpInterruptTest* test_;
8616 };
8617
8618 void CollectGarbage() {
8619 block_->Wait();
8620 while (gc_during_regexp_ < kRequiredGCs) {
8621 {
8622 v8::Locker lock;
8623 // TODO(lrn): Perhaps create some garbage before collecting.
8624 i::Heap::CollectAllGarbage(false);
8625 gc_count_++;
8626 }
8627 i::OS::Sleep(1);
8628 }
8629 gc_success_ = true;
8630 }
8631
8632 void LongRunningRegExp() {
8633 block_->Signal(); // Enable garbage collection thread on next preemption.
8634 int rounds = 0;
8635 while (gc_during_regexp_ < kRequiredGCs) {
8636 int gc_before = gc_count_;
8637 {
8638 // Match 15-30 "a"'s against 14 and a "b".
8639 const char* c_source =
8640 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8641 ".exec('aaaaaaaaaaaaaaab') === null";
8642 Local<String> source = String::New(c_source);
8643 Local<Script> script = Script::Compile(source);
8644 Local<Value> result = script->Run();
8645 if (!result->BooleanValue()) {
8646 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8647 return;
8648 }
8649 }
8650 {
8651 // Match 15-30 "a"'s against 15 and a "b".
8652 const char* c_source =
8653 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8654 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8655 Local<String> source = String::New(c_source);
8656 Local<Script> script = Script::Compile(source);
8657 Local<Value> result = script->Run();
8658 if (!result->BooleanValue()) {
8659 gc_during_regexp_ = kRequiredGCs;
8660 return;
8661 }
8662 }
8663 int gc_after = gc_count_;
8664 gc_during_regexp_ += gc_after - gc_before;
8665 rounds++;
8666 i::OS::Sleep(1);
8667 }
8668 regexp_success_ = true;
8669 }
8670
8671 i::Semaphore* block_;
8672 int gc_count_;
8673 int gc_during_regexp_;
8674 bool regexp_success_;
8675 bool gc_success_;
8676};
8677
8678
8679// Test that a regular expression execution can be interrupted and
8680// survive a garbage collection.
8681TEST(RegExpInterruption) {
8682 v8::Locker lock;
8683 v8::V8::Initialize();
8684 v8::HandleScope scope;
8685 Local<Context> local_env;
8686 {
8687 LocalContext env;
8688 local_env = env.local();
8689 }
8690
8691 // Local context should still be live.
8692 CHECK(!local_env.IsEmpty());
8693 local_env->Enter();
8694
8695 // Should complete without problems.
8696 RegExpInterruptTest().RunTest();
8697
8698 local_env->Exit();
8699}
8700
8701
8702class ApplyInterruptTest {
8703 public:
8704 ApplyInterruptTest() : block_(NULL) {}
8705 ~ApplyInterruptTest() { delete block_; }
8706 void RunTest() {
8707 block_ = i::OS::CreateSemaphore(0);
8708 gc_count_ = 0;
8709 gc_during_apply_ = 0;
8710 apply_success_ = false;
8711 gc_success_ = false;
8712 GCThread gc_thread(this);
8713 gc_thread.Start();
8714 v8::Locker::StartPreemption(1);
8715
8716 LongRunningApply();
8717 {
8718 v8::Unlocker unlock;
8719 gc_thread.Join();
8720 }
8721 v8::Locker::StopPreemption();
8722 CHECK(apply_success_);
8723 CHECK(gc_success_);
8724 }
8725 private:
8726 // Number of garbage collections required.
8727 static const int kRequiredGCs = 2;
8728
8729 class GCThread : public i::Thread {
8730 public:
8731 explicit GCThread(ApplyInterruptTest* test)
8732 : test_(test) {}
8733 virtual void Run() {
8734 test_->CollectGarbage();
8735 }
8736 private:
8737 ApplyInterruptTest* test_;
8738 };
8739
8740 void CollectGarbage() {
8741 block_->Wait();
8742 while (gc_during_apply_ < kRequiredGCs) {
8743 {
8744 v8::Locker lock;
8745 i::Heap::CollectAllGarbage(false);
8746 gc_count_++;
8747 }
8748 i::OS::Sleep(1);
8749 }
8750 gc_success_ = true;
8751 }
8752
8753 void LongRunningApply() {
8754 block_->Signal();
8755 int rounds = 0;
8756 while (gc_during_apply_ < kRequiredGCs) {
8757 int gc_before = gc_count_;
8758 {
8759 const char* c_source =
8760 "function do_very_little(bar) {"
8761 " this.foo = bar;"
8762 "}"
8763 "for (var i = 0; i < 100000; i++) {"
8764 " do_very_little.apply(this, ['bar']);"
8765 "}";
8766 Local<String> source = String::New(c_source);
8767 Local<Script> script = Script::Compile(source);
8768 Local<Value> result = script->Run();
8769 // Check that no exception was thrown.
8770 CHECK(!result.IsEmpty());
8771 }
8772 int gc_after = gc_count_;
8773 gc_during_apply_ += gc_after - gc_before;
8774 rounds++;
8775 }
8776 apply_success_ = true;
8777 }
8778
8779 i::Semaphore* block_;
8780 int gc_count_;
8781 int gc_during_apply_;
8782 bool apply_success_;
8783 bool gc_success_;
8784};
8785
8786
8787// Test that nothing bad happens if we get a preemption just when we were
8788// about to do an apply().
8789TEST(ApplyInterruption) {
8790 v8::Locker lock;
8791 v8::V8::Initialize();
8792 v8::HandleScope scope;
8793 Local<Context> local_env;
8794 {
8795 LocalContext env;
8796 local_env = env.local();
8797 }
8798
8799 // Local context should still be live.
8800 CHECK(!local_env.IsEmpty());
8801 local_env->Enter();
8802
8803 // Should complete without problems.
8804 ApplyInterruptTest().RunTest();
8805
8806 local_env->Exit();
8807}
8808
8809
8810// Verify that we can clone an object
8811TEST(ObjectClone) {
8812 v8::HandleScope scope;
8813 LocalContext env;
8814
8815 const char* sample =
8816 "var rv = {};" \
8817 "rv.alpha = 'hello';" \
8818 "rv.beta = 123;" \
8819 "rv;";
8820
8821 // Create an object, verify basics.
8822 Local<Value> val = CompileRun(sample);
8823 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01008824 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008825 obj->Set(v8_str("gamma"), v8_str("cloneme"));
8826
8827 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
8828 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8829 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
8830
8831 // Clone it.
8832 Local<v8::Object> clone = obj->Clone();
8833 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
8834 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
8835 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
8836
8837 // Set a property on the clone, verify each object.
8838 clone->Set(v8_str("beta"), v8::Integer::New(456));
8839 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8840 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
8841}
8842
8843
8844class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
8845 public:
8846 explicit AsciiVectorResource(i::Vector<const char> vector)
8847 : data_(vector) {}
8848 virtual ~AsciiVectorResource() {}
8849 virtual size_t length() const { return data_.length(); }
8850 virtual const char* data() const { return data_.start(); }
8851 private:
8852 i::Vector<const char> data_;
8853};
8854
8855
8856class UC16VectorResource : public v8::String::ExternalStringResource {
8857 public:
8858 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
8859 : data_(vector) {}
8860 virtual ~UC16VectorResource() {}
8861 virtual size_t length() const { return data_.length(); }
8862 virtual const i::uc16* data() const { return data_.start(); }
8863 private:
8864 i::Vector<const i::uc16> data_;
8865};
8866
8867
8868static void MorphAString(i::String* string,
8869 AsciiVectorResource* ascii_resource,
8870 UC16VectorResource* uc16_resource) {
8871 CHECK(i::StringShape(string).IsExternal());
8872 if (string->IsAsciiRepresentation()) {
8873 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008874 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008875 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00008876 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008877 i::ExternalTwoByteString* morphed =
8878 i::ExternalTwoByteString::cast(string);
8879 morphed->set_resource(uc16_resource);
8880 } else {
8881 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008882 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008883 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00008884 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008885 i::ExternalAsciiString* morphed =
8886 i::ExternalAsciiString::cast(string);
8887 morphed->set_resource(ascii_resource);
8888 }
8889}
8890
8891
8892// Test that we can still flatten a string if the components it is built up
8893// from have been turned into 16 bit strings in the mean time.
8894THREADED_TEST(MorphCompositeStringTest) {
8895 const char* c_string = "Now is the time for all good men"
8896 " to come to the aid of the party";
8897 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
8898 {
8899 v8::HandleScope scope;
8900 LocalContext env;
8901 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008902 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008903 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008904 i::Vector<const uint16_t>(two_byte_string,
8905 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008906
8907 Local<String> lhs(v8::Utils::ToLocal(
8908 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8909 Local<String> rhs(v8::Utils::ToLocal(
8910 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8911
8912 env->Global()->Set(v8_str("lhs"), lhs);
8913 env->Global()->Set(v8_str("rhs"), rhs);
8914
8915 CompileRun(
8916 "var cons = lhs + rhs;"
8917 "var slice = lhs.substring(1, lhs.length - 1);"
8918 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
8919
8920 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
8921 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
8922
8923 // Now do some stuff to make sure the strings are flattened, etc.
8924 CompileRun(
8925 "/[^a-z]/.test(cons);"
8926 "/[^a-z]/.test(slice);"
8927 "/[^a-z]/.test(slice_on_cons);");
8928 const char* expected_cons =
8929 "Now is the time for all good men to come to the aid of the party"
8930 "Now is the time for all good men to come to the aid of the party";
8931 const char* expected_slice =
8932 "ow is the time for all good men to come to the aid of the part";
8933 const char* expected_slice_on_cons =
8934 "ow is the time for all good men to come to the aid of the party"
8935 "Now is the time for all good men to come to the aid of the part";
8936 CHECK_EQ(String::New(expected_cons),
8937 env->Global()->Get(v8_str("cons")));
8938 CHECK_EQ(String::New(expected_slice),
8939 env->Global()->Get(v8_str("slice")));
8940 CHECK_EQ(String::New(expected_slice_on_cons),
8941 env->Global()->Get(v8_str("slice_on_cons")));
8942 }
8943}
8944
8945
8946TEST(CompileExternalTwoByteSource) {
8947 v8::HandleScope scope;
8948 LocalContext context;
8949
8950 // This is a very short list of sources, which currently is to check for a
8951 // regression caused by r2703.
8952 const char* ascii_sources[] = {
8953 "0.5",
8954 "-0.5", // This mainly testes PushBack in the Scanner.
8955 "--0.5", // This mainly testes PushBack in the Scanner.
8956 NULL
8957 };
8958
8959 // Compile the sources as external two byte strings.
8960 for (int i = 0; ascii_sources[i] != NULL; i++) {
8961 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
8962 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008963 i::Vector<const uint16_t>(two_byte_string,
8964 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00008965 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
8966 v8::Script::Compile(source);
8967 }
8968}
8969
8970
8971class RegExpStringModificationTest {
8972 public:
8973 RegExpStringModificationTest()
8974 : block_(i::OS::CreateSemaphore(0)),
8975 morphs_(0),
8976 morphs_during_regexp_(0),
8977 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
8978 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
8979 ~RegExpStringModificationTest() { delete block_; }
8980 void RunTest() {
8981 regexp_success_ = false;
8982 morph_success_ = false;
8983
8984 // Initialize the contents of two_byte_content_ to be a uc16 representation
8985 // of "aaaaaaaaaaaaaab".
8986 for (int i = 0; i < 14; i++) {
8987 two_byte_content_[i] = 'a';
8988 }
8989 two_byte_content_[14] = 'b';
8990
8991 // Create the input string for the regexp - the one we are going to change
8992 // properties of.
8993 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
8994
8995 // Inject the input as a global variable.
8996 i::Handle<i::String> input_name =
8997 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
8998 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
8999
9000
9001 MorphThread morph_thread(this);
9002 morph_thread.Start();
9003 v8::Locker::StartPreemption(1);
9004 LongRunningRegExp();
9005 {
9006 v8::Unlocker unlock;
9007 morph_thread.Join();
9008 }
9009 v8::Locker::StopPreemption();
9010 CHECK(regexp_success_);
9011 CHECK(morph_success_);
9012 }
9013 private:
9014
9015 // Number of string modifications required.
9016 static const int kRequiredModifications = 5;
9017 static const int kMaxModifications = 100;
9018
9019 class MorphThread : public i::Thread {
9020 public:
9021 explicit MorphThread(RegExpStringModificationTest* test)
9022 : test_(test) {}
9023 virtual void Run() {
9024 test_->MorphString();
9025 }
9026 private:
9027 RegExpStringModificationTest* test_;
9028 };
9029
9030 void MorphString() {
9031 block_->Wait();
9032 while (morphs_during_regexp_ < kRequiredModifications &&
9033 morphs_ < kMaxModifications) {
9034 {
9035 v8::Locker lock;
9036 // Swap string between ascii and two-byte representation.
9037 i::String* string = *input_;
9038 MorphAString(string, &ascii_resource_, &uc16_resource_);
9039 morphs_++;
9040 }
9041 i::OS::Sleep(1);
9042 }
9043 morph_success_ = true;
9044 }
9045
9046 void LongRunningRegExp() {
9047 block_->Signal(); // Enable morphing thread on next preemption.
9048 while (morphs_during_regexp_ < kRequiredModifications &&
9049 morphs_ < kMaxModifications) {
9050 int morphs_before = morphs_;
9051 {
9052 // Match 15-30 "a"'s against 14 and a "b".
9053 const char* c_source =
9054 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9055 ".exec(input) === null";
9056 Local<String> source = String::New(c_source);
9057 Local<Script> script = Script::Compile(source);
9058 Local<Value> result = script->Run();
9059 CHECK(result->IsTrue());
9060 }
9061 int morphs_after = morphs_;
9062 morphs_during_regexp_ += morphs_after - morphs_before;
9063 }
9064 regexp_success_ = true;
9065 }
9066
9067 i::uc16 two_byte_content_[15];
9068 i::Semaphore* block_;
9069 int morphs_;
9070 int morphs_during_regexp_;
9071 bool regexp_success_;
9072 bool morph_success_;
9073 i::Handle<i::String> input_;
9074 AsciiVectorResource ascii_resource_;
9075 UC16VectorResource uc16_resource_;
9076};
9077
9078
9079// Test that a regular expression execution can be interrupted and
9080// the string changed without failing.
9081TEST(RegExpStringModification) {
9082 v8::Locker lock;
9083 v8::V8::Initialize();
9084 v8::HandleScope scope;
9085 Local<Context> local_env;
9086 {
9087 LocalContext env;
9088 local_env = env.local();
9089 }
9090
9091 // Local context should still be live.
9092 CHECK(!local_env.IsEmpty());
9093 local_env->Enter();
9094
9095 // Should complete without problems.
9096 RegExpStringModificationTest().RunTest();
9097
9098 local_env->Exit();
9099}
9100
9101
9102// Test that we can set a property on the global object even if there
9103// is a read-only property in the prototype chain.
9104TEST(ReadOnlyPropertyInGlobalProto) {
9105 v8::HandleScope scope;
9106 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9107 LocalContext context(0, templ);
9108 v8::Handle<v8::Object> global = context->Global();
9109 v8::Handle<v8::Object> global_proto =
9110 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9111 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9112 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9113 // Check without 'eval' or 'with'.
9114 v8::Handle<v8::Value> res =
9115 CompileRun("function f() { x = 42; return x; }; f()");
9116 // Check with 'eval'.
9117 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9118 CHECK_EQ(v8::Integer::New(42), res);
9119 // Check with 'with'.
9120 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9121 CHECK_EQ(v8::Integer::New(42), res);
9122}
9123
9124static int force_set_set_count = 0;
9125static int force_set_get_count = 0;
9126bool pass_on_get = false;
9127
9128static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9129 const v8::AccessorInfo& info) {
9130 force_set_get_count++;
9131 if (pass_on_get) {
9132 return v8::Handle<v8::Value>();
9133 } else {
9134 return v8::Int32::New(3);
9135 }
9136}
9137
9138static void ForceSetSetter(v8::Local<v8::String> name,
9139 v8::Local<v8::Value> value,
9140 const v8::AccessorInfo& info) {
9141 force_set_set_count++;
9142}
9143
9144static v8::Handle<v8::Value> ForceSetInterceptSetter(
9145 v8::Local<v8::String> name,
9146 v8::Local<v8::Value> value,
9147 const v8::AccessorInfo& info) {
9148 force_set_set_count++;
9149 return v8::Undefined();
9150}
9151
9152TEST(ForceSet) {
9153 force_set_get_count = 0;
9154 force_set_set_count = 0;
9155 pass_on_get = false;
9156
9157 v8::HandleScope scope;
9158 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9159 v8::Handle<v8::String> access_property = v8::String::New("a");
9160 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9161 LocalContext context(NULL, templ);
9162 v8::Handle<v8::Object> global = context->Global();
9163
9164 // Ordinary properties
9165 v8::Handle<v8::String> simple_property = v8::String::New("p");
9166 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9167 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9168 // This should fail because the property is read-only
9169 global->Set(simple_property, v8::Int32::New(5));
9170 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9171 // This should succeed even though the property is read-only
9172 global->ForceSet(simple_property, v8::Int32::New(6));
9173 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9174
9175 // Accessors
9176 CHECK_EQ(0, force_set_set_count);
9177 CHECK_EQ(0, force_set_get_count);
9178 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9179 // CHECK_EQ the property shouldn't override it, just call the setter
9180 // which in this case does nothing.
9181 global->Set(access_property, v8::Int32::New(7));
9182 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9183 CHECK_EQ(1, force_set_set_count);
9184 CHECK_EQ(2, force_set_get_count);
9185 // Forcing the property to be set should override the accessor without
9186 // calling it
9187 global->ForceSet(access_property, v8::Int32::New(8));
9188 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9189 CHECK_EQ(1, force_set_set_count);
9190 CHECK_EQ(2, force_set_get_count);
9191}
9192
9193TEST(ForceSetWithInterceptor) {
9194 force_set_get_count = 0;
9195 force_set_set_count = 0;
9196 pass_on_get = false;
9197
9198 v8::HandleScope scope;
9199 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9200 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9201 LocalContext context(NULL, templ);
9202 v8::Handle<v8::Object> global = context->Global();
9203
9204 v8::Handle<v8::String> some_property = v8::String::New("a");
9205 CHECK_EQ(0, force_set_set_count);
9206 CHECK_EQ(0, force_set_get_count);
9207 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9208 // Setting the property shouldn't override it, just call the setter
9209 // which in this case does nothing.
9210 global->Set(some_property, v8::Int32::New(7));
9211 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9212 CHECK_EQ(1, force_set_set_count);
9213 CHECK_EQ(2, force_set_get_count);
9214 // Getting the property when the interceptor returns an empty handle
9215 // should yield undefined, since the property isn't present on the
9216 // object itself yet.
9217 pass_on_get = true;
9218 CHECK(global->Get(some_property)->IsUndefined());
9219 CHECK_EQ(1, force_set_set_count);
9220 CHECK_EQ(3, force_set_get_count);
9221 // Forcing the property to be set should cause the value to be
9222 // set locally without calling the interceptor.
9223 global->ForceSet(some_property, v8::Int32::New(8));
9224 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9225 CHECK_EQ(1, force_set_set_count);
9226 CHECK_EQ(4, force_set_get_count);
9227 // Reenabling the interceptor should cause it to take precedence over
9228 // the property
9229 pass_on_get = false;
9230 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9231 CHECK_EQ(1, force_set_set_count);
9232 CHECK_EQ(5, force_set_get_count);
9233 // The interceptor should also work for other properties
9234 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9235 CHECK_EQ(1, force_set_set_count);
9236 CHECK_EQ(6, force_set_get_count);
9237}
9238
9239
9240THREADED_TEST(ForceDelete) {
9241 v8::HandleScope scope;
9242 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9243 LocalContext context(NULL, templ);
9244 v8::Handle<v8::Object> global = context->Global();
9245
9246 // Ordinary properties
9247 v8::Handle<v8::String> simple_property = v8::String::New("p");
9248 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9249 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9250 // This should fail because the property is dont-delete.
9251 CHECK(!global->Delete(simple_property));
9252 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9253 // This should succeed even though the property is dont-delete.
9254 CHECK(global->ForceDelete(simple_property));
9255 CHECK(global->Get(simple_property)->IsUndefined());
9256}
9257
9258
9259static int force_delete_interceptor_count = 0;
9260static bool pass_on_delete = false;
9261
9262
9263static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9264 v8::Local<v8::String> name,
9265 const v8::AccessorInfo& info) {
9266 force_delete_interceptor_count++;
9267 if (pass_on_delete) {
9268 return v8::Handle<v8::Boolean>();
9269 } else {
9270 return v8::True();
9271 }
9272}
9273
9274
9275THREADED_TEST(ForceDeleteWithInterceptor) {
9276 force_delete_interceptor_count = 0;
9277 pass_on_delete = false;
9278
9279 v8::HandleScope scope;
9280 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9281 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9282 LocalContext context(NULL, templ);
9283 v8::Handle<v8::Object> global = context->Global();
9284
9285 v8::Handle<v8::String> some_property = v8::String::New("a");
9286 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9287
9288 // Deleting a property should get intercepted and nothing should
9289 // happen.
9290 CHECK_EQ(0, force_delete_interceptor_count);
9291 CHECK(global->Delete(some_property));
9292 CHECK_EQ(1, force_delete_interceptor_count);
9293 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9294 // Deleting the property when the interceptor returns an empty
9295 // handle should not delete the property since it is DontDelete.
9296 pass_on_delete = true;
9297 CHECK(!global->Delete(some_property));
9298 CHECK_EQ(2, force_delete_interceptor_count);
9299 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9300 // Forcing the property to be deleted should delete the value
9301 // without calling the interceptor.
9302 CHECK(global->ForceDelete(some_property));
9303 CHECK(global->Get(some_property)->IsUndefined());
9304 CHECK_EQ(2, force_delete_interceptor_count);
9305}
9306
9307
9308// Make sure that forcing a delete invalidates any IC stubs, so we
9309// don't read the hole value.
9310THREADED_TEST(ForceDeleteIC) {
9311 v8::HandleScope scope;
9312 LocalContext context;
9313 // Create a DontDelete variable on the global object.
9314 CompileRun("this.__proto__ = { foo: 'horse' };"
9315 "var foo = 'fish';"
9316 "function f() { return foo.length; }");
9317 // Initialize the IC for foo in f.
9318 CompileRun("for (var i = 0; i < 4; i++) f();");
9319 // Make sure the value of foo is correct before the deletion.
9320 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9321 // Force the deletion of foo.
9322 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9323 // Make sure the value for foo is read from the prototype, and that
9324 // we don't get in trouble with reading the deleted cell value
9325 // sentinel.
9326 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9327}
9328
9329
9330v8::Persistent<Context> calling_context0;
9331v8::Persistent<Context> calling_context1;
9332v8::Persistent<Context> calling_context2;
9333
9334
9335// Check that the call to the callback is initiated in
9336// calling_context2, the directly calling context is calling_context1
9337// and the callback itself is in calling_context0.
9338static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9339 ApiTestFuzzer::Fuzz();
9340 CHECK(Context::GetCurrent() == calling_context0);
9341 CHECK(Context::GetCalling() == calling_context1);
9342 CHECK(Context::GetEntered() == calling_context2);
9343 return v8::Integer::New(42);
9344}
9345
9346
9347THREADED_TEST(GetCallingContext) {
9348 v8::HandleScope scope;
9349
9350 calling_context0 = Context::New();
9351 calling_context1 = Context::New();
9352 calling_context2 = Context::New();
9353
9354 // Allow cross-domain access.
9355 Local<String> token = v8_str("<security token>");
9356 calling_context0->SetSecurityToken(token);
9357 calling_context1->SetSecurityToken(token);
9358 calling_context2->SetSecurityToken(token);
9359
9360 // Create an object with a C++ callback in context0.
9361 calling_context0->Enter();
9362 Local<v8::FunctionTemplate> callback_templ =
9363 v8::FunctionTemplate::New(GetCallingContextCallback);
9364 calling_context0->Global()->Set(v8_str("callback"),
9365 callback_templ->GetFunction());
9366 calling_context0->Exit();
9367
9368 // Expose context0 in context1 and setup a function that calls the
9369 // callback function.
9370 calling_context1->Enter();
9371 calling_context1->Global()->Set(v8_str("context0"),
9372 calling_context0->Global());
9373 CompileRun("function f() { context0.callback() }");
9374 calling_context1->Exit();
9375
9376 // Expose context1 in context2 and call the callback function in
9377 // context0 indirectly through f in context1.
9378 calling_context2->Enter();
9379 calling_context2->Global()->Set(v8_str("context1"),
9380 calling_context1->Global());
9381 CompileRun("context1.f()");
9382 calling_context2->Exit();
9383
9384 // Dispose the contexts to allow them to be garbage collected.
9385 calling_context0.Dispose();
9386 calling_context1.Dispose();
9387 calling_context2.Dispose();
9388 calling_context0.Clear();
9389 calling_context1.Clear();
9390 calling_context2.Clear();
9391}
9392
9393
9394// Check that a variable declaration with no explicit initialization
9395// value does not shadow an existing property in the prototype chain.
9396//
9397// This is consistent with Firefox and Safari.
9398//
9399// See http://crbug.com/12548.
9400THREADED_TEST(InitGlobalVarInProtoChain) {
9401 v8::HandleScope scope;
9402 LocalContext context;
9403 // Introduce a variable in the prototype chain.
9404 CompileRun("__proto__.x = 42");
9405 v8::Handle<v8::Value> result = CompileRun("var x; x");
9406 CHECK(!result->IsUndefined());
9407 CHECK_EQ(42, result->Int32Value());
9408}
9409
9410
9411// Regression test for issue 398.
9412// If a function is added to an object, creating a constant function
9413// field, and the result is cloned, replacing the constant function on the
9414// original should not affect the clone.
9415// See http://code.google.com/p/v8/issues/detail?id=398
9416THREADED_TEST(ReplaceConstantFunction) {
9417 v8::HandleScope scope;
9418 LocalContext context;
9419 v8::Handle<v8::Object> obj = v8::Object::New();
9420 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9421 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9422 obj->Set(foo_string, func_templ->GetFunction());
9423 v8::Handle<v8::Object> obj_clone = obj->Clone();
9424 obj_clone->Set(foo_string, v8::String::New("Hello"));
9425 CHECK(!obj->Get(foo_string)->IsUndefined());
9426}
9427
9428
9429// Regression test for http://crbug.com/16276.
9430THREADED_TEST(Regress16276) {
9431 v8::HandleScope scope;
9432 LocalContext context;
9433 // Force the IC in f to be a dictionary load IC.
9434 CompileRun("function f(obj) { return obj.x; }\n"
9435 "var obj = { x: { foo: 42 }, y: 87 };\n"
9436 "var x = obj.x;\n"
9437 "delete obj.y;\n"
9438 "for (var i = 0; i < 5; i++) f(obj);");
9439 // Detach the global object to make 'this' refer directly to the
9440 // global object (not the proxy), and make sure that the dictionary
9441 // load IC doesn't mess up loading directly from the global object.
9442 context->DetachGlobal();
9443 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9444}
9445
9446
9447THREADED_TEST(PixelArray) {
9448 v8::HandleScope scope;
9449 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009450 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009451 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9452 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9453 pixel_data);
9454 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9455 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009456 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009457 }
9458 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9459 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009460 CHECK_EQ(i % 256, pixels->get(i));
9461 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009462 }
9463
9464 v8::Handle<v8::Object> obj = v8::Object::New();
9465 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9466 // Set the elements to be the pixels.
9467 // jsobj->set_elements(*pixels);
9468 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9469 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9470 obj->Set(v8_str("field"), v8::Int32::New(1503));
9471 context->Global()->Set(v8_str("pixels"), obj);
9472 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9473 CHECK_EQ(1503, result->Int32Value());
9474 result = CompileRun("pixels[1]");
9475 CHECK_EQ(1, result->Int32Value());
9476
9477 result = CompileRun("var sum = 0;"
9478 "for (var i = 0; i < 8; i++) {"
9479 " sum += pixels[i] = pixels[i] = -i;"
9480 "}"
9481 "sum;");
9482 CHECK_EQ(-28, result->Int32Value());
9483
9484 result = CompileRun("var sum = 0;"
9485 "for (var i = 0; i < 8; i++) {"
9486 " sum += pixels[i] = pixels[i] = 0;"
9487 "}"
9488 "sum;");
9489 CHECK_EQ(0, result->Int32Value());
9490
9491 result = CompileRun("var sum = 0;"
9492 "for (var i = 0; i < 8; i++) {"
9493 " sum += pixels[i] = pixels[i] = 255;"
9494 "}"
9495 "sum;");
9496 CHECK_EQ(8 * 255, result->Int32Value());
9497
9498 result = CompileRun("var sum = 0;"
9499 "for (var i = 0; i < 8; i++) {"
9500 " sum += pixels[i] = pixels[i] = 256 + i;"
9501 "}"
9502 "sum;");
9503 CHECK_EQ(2076, result->Int32Value());
9504
9505 result = CompileRun("var sum = 0;"
9506 "for (var i = 0; i < 8; i++) {"
9507 " sum += pixels[i] = pixels[i] = i;"
9508 "}"
9509 "sum;");
9510 CHECK_EQ(28, result->Int32Value());
9511
9512 result = CompileRun("var sum = 0;"
9513 "for (var i = 0; i < 8; i++) {"
9514 " sum += pixels[i];"
9515 "}"
9516 "sum;");
9517 CHECK_EQ(28, result->Int32Value());
9518
9519 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9520 i::SetElement(jsobj, 1, value);
9521 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9522 *value.location() = i::Smi::FromInt(256);
9523 i::SetElement(jsobj, 1, value);
9524 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9525 *value.location() = i::Smi::FromInt(-1);
9526 i::SetElement(jsobj, 1, value);
9527 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9528
9529 result = CompileRun("for (var i = 0; i < 8; i++) {"
9530 " pixels[i] = (i * 65) - 109;"
9531 "}"
9532 "pixels[1] + pixels[6];");
9533 CHECK_EQ(255, result->Int32Value());
9534 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9535 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9536 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9537 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9538 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9539 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9540 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9541 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9542 result = CompileRun("var sum = 0;"
9543 "for (var i = 0; i < 8; i++) {"
9544 " sum += pixels[i];"
9545 "}"
9546 "sum;");
9547 CHECK_EQ(984, result->Int32Value());
9548
9549 result = CompileRun("for (var i = 0; i < 8; i++) {"
9550 " pixels[i] = (i * 1.1);"
9551 "}"
9552 "pixels[1] + pixels[6];");
9553 CHECK_EQ(8, result->Int32Value());
9554 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9555 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9556 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9557 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9558 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9559 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9560 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9561 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9562
9563 result = CompileRun("for (var i = 0; i < 8; i++) {"
9564 " pixels[7] = undefined;"
9565 "}"
9566 "pixels[7];");
9567 CHECK_EQ(0, result->Int32Value());
9568 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9569
9570 result = CompileRun("for (var i = 0; i < 8; i++) {"
9571 " pixels[6] = '2.3';"
9572 "}"
9573 "pixels[6];");
9574 CHECK_EQ(2, result->Int32Value());
9575 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9576
9577 result = CompileRun("for (var i = 0; i < 8; i++) {"
9578 " pixels[5] = NaN;"
9579 "}"
9580 "pixels[5];");
9581 CHECK_EQ(0, result->Int32Value());
9582 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9583
9584 result = CompileRun("for (var i = 0; i < 8; i++) {"
9585 " pixels[8] = Infinity;"
9586 "}"
9587 "pixels[8];");
9588 CHECK_EQ(255, result->Int32Value());
9589 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9590
9591 result = CompileRun("for (var i = 0; i < 8; i++) {"
9592 " pixels[9] = -Infinity;"
9593 "}"
9594 "pixels[9];");
9595 CHECK_EQ(0, result->Int32Value());
9596 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9597
9598 result = CompileRun("pixels[3] = 33;"
9599 "delete pixels[3];"
9600 "pixels[3];");
9601 CHECK_EQ(33, result->Int32Value());
9602
9603 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9604 "pixels[2] = 12; pixels[3] = 13;"
9605 "pixels.__defineGetter__('2',"
9606 "function() { return 120; });"
9607 "pixels[2];");
9608 CHECK_EQ(12, result->Int32Value());
9609
9610 result = CompileRun("var js_array = new Array(40);"
9611 "js_array[0] = 77;"
9612 "js_array;");
9613 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9614
9615 result = CompileRun("pixels[1] = 23;"
9616 "pixels.__proto__ = [];"
9617 "js_array.__proto__ = pixels;"
9618 "js_array.concat(pixels);");
9619 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9620 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9621
9622 result = CompileRun("pixels[1] = 23;");
9623 CHECK_EQ(23, result->Int32Value());
9624
Steve Blockd0582a62009-12-15 09:54:21 +00009625 // Test for index greater than 255. Regression test for:
9626 // http://code.google.com/p/chromium/issues/detail?id=26337.
9627 result = CompileRun("pixels[256] = 255;");
9628 CHECK_EQ(255, result->Int32Value());
9629 result = CompileRun("var i = 0;"
9630 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9631 "i");
9632 CHECK_EQ(255, result->Int32Value());
9633
Steve Blocka7e24c12009-10-30 11:49:00 +00009634 free(pixel_data);
9635}
9636
9637
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009638THREADED_TEST(PixelArrayInfo) {
9639 v8::HandleScope scope;
9640 LocalContext context;
9641 for (int size = 0; size < 100; size += 10) {
9642 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9643 v8::Handle<v8::Object> obj = v8::Object::New();
9644 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9645 CHECK(obj->HasIndexedPropertiesInPixelData());
9646 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9647 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9648 free(pixel_data);
9649 }
9650}
9651
9652
9653static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9654 switch (array_type) {
9655 case v8::kExternalByteArray:
9656 case v8::kExternalUnsignedByteArray:
9657 return 1;
9658 break;
9659 case v8::kExternalShortArray:
9660 case v8::kExternalUnsignedShortArray:
9661 return 2;
9662 break;
9663 case v8::kExternalIntArray:
9664 case v8::kExternalUnsignedIntArray:
9665 case v8::kExternalFloatArray:
9666 return 4;
9667 break;
9668 default:
9669 UNREACHABLE();
9670 return -1;
9671 }
9672 UNREACHABLE();
9673 return -1;
9674}
9675
9676
Steve Block3ce2e202009-11-05 08:53:23 +00009677template <class ExternalArrayClass, class ElementType>
9678static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9679 int64_t low,
9680 int64_t high) {
9681 v8::HandleScope scope;
9682 LocalContext context;
9683 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009684 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +00009685 ElementType* array_data =
9686 static_cast<ElementType*>(malloc(kElementCount * element_size));
9687 i::Handle<ExternalArrayClass> array =
9688 i::Handle<ExternalArrayClass>::cast(
9689 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9690 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9691 for (int i = 0; i < kElementCount; i++) {
9692 array->set(i, static_cast<ElementType>(i));
9693 }
9694 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9695 for (int i = 0; i < kElementCount; i++) {
9696 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9697 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9698 }
9699
9700 v8::Handle<v8::Object> obj = v8::Object::New();
9701 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9702 // Set the elements to be the external array.
9703 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9704 array_type,
9705 kElementCount);
9706 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9707 obj->Set(v8_str("field"), v8::Int32::New(1503));
9708 context->Global()->Set(v8_str("ext_array"), obj);
9709 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9710 CHECK_EQ(1503, result->Int32Value());
9711 result = CompileRun("ext_array[1]");
9712 CHECK_EQ(1, result->Int32Value());
9713
9714 // Check pass through of assigned smis
9715 result = CompileRun("var sum = 0;"
9716 "for (var i = 0; i < 8; i++) {"
9717 " sum += ext_array[i] = ext_array[i] = -i;"
9718 "}"
9719 "sum;");
9720 CHECK_EQ(-28, result->Int32Value());
9721
9722 // Check assigned smis
9723 result = CompileRun("for (var i = 0; i < 8; i++) {"
9724 " ext_array[i] = i;"
9725 "}"
9726 "var sum = 0;"
9727 "for (var i = 0; i < 8; i++) {"
9728 " sum += ext_array[i];"
9729 "}"
9730 "sum;");
9731 CHECK_EQ(28, result->Int32Value());
9732
9733 // Check assigned smis in reverse order
9734 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9735 " ext_array[i] = i;"
9736 "}"
9737 "var sum = 0;"
9738 "for (var i = 0; i < 8; i++) {"
9739 " sum += ext_array[i];"
9740 "}"
9741 "sum;");
9742 CHECK_EQ(28, result->Int32Value());
9743
9744 // Check pass through of assigned HeapNumbers
9745 result = CompileRun("var sum = 0;"
9746 "for (var i = 0; i < 16; i+=2) {"
9747 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9748 "}"
9749 "sum;");
9750 CHECK_EQ(-28, result->Int32Value());
9751
9752 // Check assigned HeapNumbers
9753 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9754 " ext_array[i] = (i * 0.5);"
9755 "}"
9756 "var sum = 0;"
9757 "for (var i = 0; i < 16; i+=2) {"
9758 " sum += ext_array[i];"
9759 "}"
9760 "sum;");
9761 CHECK_EQ(28, result->Int32Value());
9762
9763 // Check assigned HeapNumbers in reverse order
9764 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9765 " ext_array[i] = (i * 0.5);"
9766 "}"
9767 "var sum = 0;"
9768 "for (var i = 0; i < 16; i+=2) {"
9769 " sum += ext_array[i];"
9770 "}"
9771 "sum;");
9772 CHECK_EQ(28, result->Int32Value());
9773
9774 i::ScopedVector<char> test_buf(1024);
9775
9776 // Check legal boundary conditions.
9777 // The repeated loads and stores ensure the ICs are exercised.
9778 const char* boundary_program =
9779 "var res = 0;"
9780 "for (var i = 0; i < 16; i++) {"
9781 " ext_array[i] = %lld;"
9782 " if (i > 8) {"
9783 " res = ext_array[i];"
9784 " }"
9785 "}"
9786 "res;";
9787 i::OS::SNPrintF(test_buf,
9788 boundary_program,
9789 low);
9790 result = CompileRun(test_buf.start());
9791 CHECK_EQ(low, result->IntegerValue());
9792
9793 i::OS::SNPrintF(test_buf,
9794 boundary_program,
9795 high);
9796 result = CompileRun(test_buf.start());
9797 CHECK_EQ(high, result->IntegerValue());
9798
9799 // Check misprediction of type in IC.
9800 result = CompileRun("var tmp_array = ext_array;"
9801 "var sum = 0;"
9802 "for (var i = 0; i < 8; i++) {"
9803 " tmp_array[i] = i;"
9804 " sum += tmp_array[i];"
9805 " if (i == 4) {"
9806 " tmp_array = {};"
9807 " }"
9808 "}"
9809 "sum;");
9810 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9811 CHECK_EQ(28, result->Int32Value());
9812
9813 // Make sure out-of-range loads do not throw.
9814 i::OS::SNPrintF(test_buf,
9815 "var caught_exception = false;"
9816 "try {"
9817 " ext_array[%d];"
9818 "} catch (e) {"
9819 " caught_exception = true;"
9820 "}"
9821 "caught_exception;",
9822 kElementCount);
9823 result = CompileRun(test_buf.start());
9824 CHECK_EQ(false, result->BooleanValue());
9825
9826 // Make sure out-of-range stores do not throw.
9827 i::OS::SNPrintF(test_buf,
9828 "var caught_exception = false;"
9829 "try {"
9830 " ext_array[%d] = 1;"
9831 "} catch (e) {"
9832 " caught_exception = true;"
9833 "}"
9834 "caught_exception;",
9835 kElementCount);
9836 result = CompileRun(test_buf.start());
9837 CHECK_EQ(false, result->BooleanValue());
9838
9839 // Check other boundary conditions, values and operations.
9840 result = CompileRun("for (var i = 0; i < 8; i++) {"
9841 " ext_array[7] = undefined;"
9842 "}"
9843 "ext_array[7];");
9844 CHECK_EQ(0, result->Int32Value());
9845 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
9846
9847 result = CompileRun("for (var i = 0; i < 8; i++) {"
9848 " ext_array[6] = '2.3';"
9849 "}"
9850 "ext_array[6];");
9851 CHECK_EQ(2, result->Int32Value());
9852 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
9853
9854 if (array_type != v8::kExternalFloatArray) {
9855 // Though the specification doesn't state it, be explicit about
9856 // converting NaNs and +/-Infinity to zero.
9857 result = CompileRun("for (var i = 0; i < 8; i++) {"
9858 " ext_array[i] = 5;"
9859 "}"
9860 "for (var i = 0; i < 8; i++) {"
9861 " ext_array[i] = NaN;"
9862 "}"
9863 "ext_array[5];");
9864 CHECK_EQ(0, result->Int32Value());
9865 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9866
9867 result = CompileRun("for (var i = 0; i < 8; i++) {"
9868 " ext_array[i] = 5;"
9869 "}"
9870 "for (var i = 0; i < 8; i++) {"
9871 " ext_array[i] = Infinity;"
9872 "}"
9873 "ext_array[5];");
9874 CHECK_EQ(0, result->Int32Value());
9875 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9876
9877 result = CompileRun("for (var i = 0; i < 8; i++) {"
9878 " ext_array[i] = 5;"
9879 "}"
9880 "for (var i = 0; i < 8; i++) {"
9881 " ext_array[i] = -Infinity;"
9882 "}"
9883 "ext_array[5];");
9884 CHECK_EQ(0, result->Int32Value());
9885 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9886 }
9887
9888 result = CompileRun("ext_array[3] = 33;"
9889 "delete ext_array[3];"
9890 "ext_array[3];");
9891 CHECK_EQ(33, result->Int32Value());
9892
9893 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
9894 "ext_array[2] = 12; ext_array[3] = 13;"
9895 "ext_array.__defineGetter__('2',"
9896 "function() { return 120; });"
9897 "ext_array[2];");
9898 CHECK_EQ(12, result->Int32Value());
9899
9900 result = CompileRun("var js_array = new Array(40);"
9901 "js_array[0] = 77;"
9902 "js_array;");
9903 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9904
9905 result = CompileRun("ext_array[1] = 23;"
9906 "ext_array.__proto__ = [];"
9907 "js_array.__proto__ = ext_array;"
9908 "js_array.concat(ext_array);");
9909 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9910 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9911
9912 result = CompileRun("ext_array[1] = 23;");
9913 CHECK_EQ(23, result->Int32Value());
9914
Steve Blockd0582a62009-12-15 09:54:21 +00009915 // Test more complex manipulations which cause eax to contain values
9916 // that won't be completely overwritten by loads from the arrays.
9917 // This catches bugs in the instructions used for the KeyedLoadIC
9918 // for byte and word types.
9919 {
9920 const int kXSize = 300;
9921 const int kYSize = 300;
9922 const int kLargeElementCount = kXSize * kYSize * 4;
9923 ElementType* large_array_data =
9924 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
9925 i::Handle<ExternalArrayClass> large_array =
9926 i::Handle<ExternalArrayClass>::cast(
9927 i::Factory::NewExternalArray(kLargeElementCount,
9928 array_type,
9929 array_data));
9930 v8::Handle<v8::Object> large_obj = v8::Object::New();
9931 // Set the elements to be the external array.
9932 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
9933 array_type,
9934 kLargeElementCount);
9935 context->Global()->Set(v8_str("large_array"), large_obj);
9936 // Initialize contents of a few rows.
9937 for (int x = 0; x < 300; x++) {
9938 int row = 0;
9939 int offset = row * 300 * 4;
9940 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9941 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9942 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9943 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9944 row = 150;
9945 offset = row * 300 * 4;
9946 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9947 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9948 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9949 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9950 row = 298;
9951 offset = row * 300 * 4;
9952 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9953 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9954 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9955 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9956 }
9957 // The goal of the code below is to make "offset" large enough
9958 // that the computation of the index (which goes into eax) has
9959 // high bits set which will not be overwritten by a byte or short
9960 // load.
9961 result = CompileRun("var failed = false;"
9962 "var offset = 0;"
9963 "for (var i = 0; i < 300; i++) {"
9964 " if (large_array[4 * i] != 127 ||"
9965 " large_array[4 * i + 1] != 0 ||"
9966 " large_array[4 * i + 2] != 0 ||"
9967 " large_array[4 * i + 3] != 127) {"
9968 " failed = true;"
9969 " }"
9970 "}"
9971 "offset = 150 * 300 * 4;"
9972 "for (var i = 0; i < 300; i++) {"
9973 " if (large_array[offset + 4 * i] != 127 ||"
9974 " large_array[offset + 4 * i + 1] != 0 ||"
9975 " large_array[offset + 4 * i + 2] != 0 ||"
9976 " large_array[offset + 4 * i + 3] != 127) {"
9977 " failed = true;"
9978 " }"
9979 "}"
9980 "offset = 298 * 300 * 4;"
9981 "for (var i = 0; i < 300; i++) {"
9982 " if (large_array[offset + 4 * i] != 127 ||"
9983 " large_array[offset + 4 * i + 1] != 0 ||"
9984 " large_array[offset + 4 * i + 2] != 0 ||"
9985 " large_array[offset + 4 * i + 3] != 127) {"
9986 " failed = true;"
9987 " }"
9988 "}"
9989 "!failed;");
9990 CHECK_EQ(true, result->BooleanValue());
9991 free(large_array_data);
9992 }
9993
Steve Block3ce2e202009-11-05 08:53:23 +00009994 free(array_data);
9995}
9996
9997
9998THREADED_TEST(ExternalByteArray) {
9999 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>(
10000 v8::kExternalByteArray,
10001 -128,
10002 127);
10003}
10004
10005
10006THREADED_TEST(ExternalUnsignedByteArray) {
10007 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>(
10008 v8::kExternalUnsignedByteArray,
10009 0,
10010 255);
10011}
10012
10013
10014THREADED_TEST(ExternalShortArray) {
10015 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>(
10016 v8::kExternalShortArray,
10017 -32768,
10018 32767);
10019}
10020
10021
10022THREADED_TEST(ExternalUnsignedShortArray) {
10023 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>(
10024 v8::kExternalUnsignedShortArray,
10025 0,
10026 65535);
10027}
10028
10029
10030THREADED_TEST(ExternalIntArray) {
10031 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>(
10032 v8::kExternalIntArray,
10033 INT_MIN, // -2147483648
10034 INT_MAX); // 2147483647
10035}
10036
10037
10038THREADED_TEST(ExternalUnsignedIntArray) {
10039 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>(
10040 v8::kExternalUnsignedIntArray,
10041 0,
10042 UINT_MAX); // 4294967295
10043}
10044
10045
10046THREADED_TEST(ExternalFloatArray) {
10047 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>(
10048 v8::kExternalFloatArray,
10049 -500,
10050 500);
10051}
10052
10053
10054THREADED_TEST(ExternalArrays) {
10055 TestExternalByteArray();
10056 TestExternalUnsignedByteArray();
10057 TestExternalShortArray();
10058 TestExternalUnsignedShortArray();
10059 TestExternalIntArray();
10060 TestExternalUnsignedIntArray();
10061 TestExternalFloatArray();
10062}
10063
10064
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010065void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10066 v8::HandleScope scope;
10067 LocalContext context;
10068 for (int size = 0; size < 100; size += 10) {
10069 int element_size = ExternalArrayElementSize(array_type);
10070 void* external_data = malloc(size * element_size);
10071 v8::Handle<v8::Object> obj = v8::Object::New();
10072 obj->SetIndexedPropertiesToExternalArrayData(
10073 external_data, array_type, size);
10074 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10075 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10076 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10077 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10078 free(external_data);
10079 }
10080}
10081
10082
10083THREADED_TEST(ExternalArrayInfo) {
10084 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10085 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10086 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10087 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10088 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10089 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10090 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10091}
10092
10093
Steve Blocka7e24c12009-10-30 11:49:00 +000010094THREADED_TEST(ScriptContextDependence) {
10095 v8::HandleScope scope;
10096 LocalContext c1;
10097 const char *source = "foo";
10098 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10099 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10100 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10101 CHECK_EQ(dep->Run()->Int32Value(), 100);
10102 CHECK_EQ(indep->Run()->Int32Value(), 100);
10103 LocalContext c2;
10104 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10105 CHECK_EQ(dep->Run()->Int32Value(), 100);
10106 CHECK_EQ(indep->Run()->Int32Value(), 101);
10107}
10108
10109
10110THREADED_TEST(StackTrace) {
10111 v8::HandleScope scope;
10112 LocalContext context;
10113 v8::TryCatch try_catch;
10114 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10115 v8::Handle<v8::String> src = v8::String::New(source);
10116 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10117 v8::Script::New(src, origin)->Run();
10118 CHECK(try_catch.HasCaught());
10119 v8::String::Utf8Value stack(try_catch.StackTrace());
10120 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10121}
10122
10123
Kristian Monsen25f61362010-05-21 11:50:48 +010010124// Checks that a StackFrame has certain expected values.
10125void checkStackFrame(const char* expected_script_name,
10126 const char* expected_func_name, int expected_line_number,
10127 int expected_column, bool is_eval, bool is_constructor,
10128 v8::Handle<v8::StackFrame> frame) {
10129 v8::HandleScope scope;
10130 v8::String::Utf8Value func_name(frame->GetFunctionName());
10131 v8::String::Utf8Value script_name(frame->GetScriptName());
10132 if (*script_name == NULL) {
10133 // The situation where there is no associated script, like for evals.
10134 CHECK(expected_script_name == NULL);
10135 } else {
10136 CHECK(strstr(*script_name, expected_script_name) != NULL);
10137 }
10138 CHECK(strstr(*func_name, expected_func_name) != NULL);
10139 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10140 CHECK_EQ(expected_column, frame->GetColumn());
10141 CHECK_EQ(is_eval, frame->IsEval());
10142 CHECK_EQ(is_constructor, frame->IsConstructor());
10143}
10144
10145
10146v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10147 v8::HandleScope scope;
10148 const char* origin = "capture-stack-trace-test";
10149 const int kOverviewTest = 1;
10150 const int kDetailedTest = 2;
10151
10152 ASSERT(args.Length() == 1);
10153
10154 int testGroup = args[0]->Int32Value();
10155 if (testGroup == kOverviewTest) {
10156 v8::Handle<v8::StackTrace> stackTrace =
10157 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10158 CHECK_EQ(4, stackTrace->GetFrameCount());
10159 checkStackFrame(origin, "bar", 2, 10, false, false,
10160 stackTrace->GetFrame(0));
10161 checkStackFrame(origin, "foo", 6, 3, false, false,
10162 stackTrace->GetFrame(1));
10163 checkStackFrame(NULL, "", 1, 1, false, false,
10164 stackTrace->GetFrame(2));
10165 // The last frame is an anonymous function that has the initial call.
10166 checkStackFrame(origin, "", 8, 7, false, false,
10167 stackTrace->GetFrame(3));
10168
10169 CHECK(stackTrace->AsArray()->IsArray());
10170 } else if (testGroup == kDetailedTest) {
10171 v8::Handle<v8::StackTrace> stackTrace =
10172 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10173 CHECK_EQ(4, stackTrace->GetFrameCount());
10174 checkStackFrame(origin, "bat", 4, 22, false, false,
10175 stackTrace->GetFrame(0));
10176 checkStackFrame(origin, "baz", 8, 3, false, true,
10177 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010178#ifdef ENABLE_DEBUGGER_SUPPORT
10179 bool is_eval = true;
10180#else // ENABLE_DEBUGGER_SUPPORT
10181 bool is_eval = false;
10182#endif // ENABLE_DEBUGGER_SUPPORT
10183
10184 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010185 stackTrace->GetFrame(2));
10186 // The last frame is an anonymous function that has the initial call to foo.
10187 checkStackFrame(origin, "", 10, 1, false, false,
10188 stackTrace->GetFrame(3));
10189
10190 CHECK(stackTrace->AsArray()->IsArray());
10191 }
10192 return v8::Undefined();
10193}
10194
10195
10196// Tests the C++ StackTrace API.
10197THREADED_TEST(CaptureStackTrace) {
10198 v8::HandleScope scope;
10199 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10200 Local<ObjectTemplate> templ = ObjectTemplate::New();
10201 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10202 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10203 LocalContext context(0, templ);
10204
10205 // Test getting OVERVIEW information. Should ignore information that is not
10206 // script name, function name, line number, and column offset.
10207 const char *overview_source =
10208 "function bar() {\n"
10209 " var y; AnalyzeStackInNativeCode(1);\n"
10210 "}\n"
10211 "function foo() {\n"
10212 "\n"
10213 " bar();\n"
10214 "}\n"
10215 "var x;eval('new foo();');";
10216 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10217 v8::Handle<Value> overview_result =
10218 v8::Script::New(overview_src, origin)->Run();
10219 ASSERT(!overview_result.IsEmpty());
10220 ASSERT(overview_result->IsObject());
10221
10222 // Test getting DETAILED information.
10223 const char *detailed_source =
10224 "function bat() {AnalyzeStackInNativeCode(2);\n"
10225 "}\n"
10226 "\n"
10227 "function baz() {\n"
10228 " bat();\n"
10229 "}\n"
10230 "eval('new baz();');";
10231 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10232 // Make the script using a non-zero line and column offset.
10233 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10234 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10235 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10236 v8::Handle<v8::Script> detailed_script(
10237 v8::Script::New(detailed_src, &detailed_origin));
10238 v8::Handle<Value> detailed_result = detailed_script->Run();
10239 ASSERT(!detailed_result.IsEmpty());
10240 ASSERT(detailed_result->IsObject());
10241}
10242
10243
Steve Block3ce2e202009-11-05 08:53:23 +000010244// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010245THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010246 bool rv = false;
10247 for (int i = 0; i < 100; i++) {
10248 rv = v8::V8::IdleNotification();
10249 if (rv)
10250 break;
10251 }
10252 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010253}
10254
10255
10256static uint32_t* stack_limit;
10257
10258static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10259 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10260 return v8::Undefined();
10261}
10262
10263
10264// Uses the address of a local variable to determine the stack top now.
10265// Given a size, returns an address that is that far from the current
10266// top of stack.
10267static uint32_t* ComputeStackLimit(uint32_t size) {
10268 uint32_t* answer = &size - (size / sizeof(size));
10269 // If the size is very large and the stack is very near the bottom of
10270 // memory then the calculation above may wrap around and give an address
10271 // that is above the (downwards-growing) stack. In that case we return
10272 // a very low address.
10273 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10274 return answer;
10275}
10276
10277
10278TEST(SetResourceConstraints) {
10279 static const int K = 1024;
10280 uint32_t* set_limit = ComputeStackLimit(128 * K);
10281
10282 // Set stack limit.
10283 v8::ResourceConstraints constraints;
10284 constraints.set_stack_limit(set_limit);
10285 CHECK(v8::SetResourceConstraints(&constraints));
10286
10287 // Execute a script.
10288 v8::HandleScope scope;
10289 LocalContext env;
10290 Local<v8::FunctionTemplate> fun_templ =
10291 v8::FunctionTemplate::New(GetStackLimitCallback);
10292 Local<Function> fun = fun_templ->GetFunction();
10293 env->Global()->Set(v8_str("get_stack_limit"), fun);
10294 CompileRun("get_stack_limit();");
10295
10296 CHECK(stack_limit == set_limit);
10297}
10298
10299
10300TEST(SetResourceConstraintsInThread) {
10301 uint32_t* set_limit;
10302 {
10303 v8::Locker locker;
10304 static const int K = 1024;
10305 set_limit = ComputeStackLimit(128 * K);
10306
10307 // Set stack limit.
10308 v8::ResourceConstraints constraints;
10309 constraints.set_stack_limit(set_limit);
10310 CHECK(v8::SetResourceConstraints(&constraints));
10311
10312 // Execute a script.
10313 v8::HandleScope scope;
10314 LocalContext env;
10315 Local<v8::FunctionTemplate> fun_templ =
10316 v8::FunctionTemplate::New(GetStackLimitCallback);
10317 Local<Function> fun = fun_templ->GetFunction();
10318 env->Global()->Set(v8_str("get_stack_limit"), fun);
10319 CompileRun("get_stack_limit();");
10320
10321 CHECK(stack_limit == set_limit);
10322 }
10323 {
10324 v8::Locker locker;
10325 CHECK(stack_limit == set_limit);
10326 }
10327}
Steve Block3ce2e202009-11-05 08:53:23 +000010328
10329
10330THREADED_TEST(GetHeapStatistics) {
10331 v8::HandleScope scope;
10332 LocalContext c1;
10333 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010334 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10335 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010336 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010337 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10338 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10339}
10340
10341
10342static double DoubleFromBits(uint64_t value) {
10343 double target;
10344#ifdef BIG_ENDIAN_FLOATING_POINT
10345 const int kIntSize = 4;
10346 // Somebody swapped the lower and higher half of doubles.
10347 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10348 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10349#else
10350 memcpy(&target, &value, sizeof(target));
10351#endif
10352 return target;
10353}
10354
10355
10356static uint64_t DoubleToBits(double value) {
10357 uint64_t target;
10358#ifdef BIG_ENDIAN_FLOATING_POINT
10359 const int kIntSize = 4;
10360 // Somebody swapped the lower and higher half of doubles.
10361 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10362 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10363#else
10364 memcpy(&target, &value, sizeof(target));
10365#endif
10366 return target;
10367}
10368
10369
10370static double DoubleToDateTime(double input) {
10371 double date_limit = 864e13;
10372 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10373 return i::OS::nan_value();
10374 }
10375 return (input < 0) ? -(floor(-input)) : floor(input);
10376}
10377
10378// We don't have a consistent way to write 64-bit constants syntactically, so we
10379// split them into two 32-bit constants and combine them programmatically.
10380static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10381 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10382}
10383
10384
10385THREADED_TEST(QuietSignalingNaNs) {
10386 v8::HandleScope scope;
10387 LocalContext context;
10388 v8::TryCatch try_catch;
10389
10390 // Special double values.
10391 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10392 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10393 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10394 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10395 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10396 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10397 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10398
10399 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10400 // on either side of the epoch.
10401 double date_limit = 864e13;
10402
10403 double test_values[] = {
10404 snan,
10405 qnan,
10406 infinity,
10407 max_normal,
10408 date_limit + 1,
10409 date_limit,
10410 min_normal,
10411 max_denormal,
10412 min_denormal,
10413 0,
10414 -0,
10415 -min_denormal,
10416 -max_denormal,
10417 -min_normal,
10418 -date_limit,
10419 -date_limit - 1,
10420 -max_normal,
10421 -infinity,
10422 -qnan,
10423 -snan
10424 };
10425 int num_test_values = 20;
10426
10427 for (int i = 0; i < num_test_values; i++) {
10428 double test_value = test_values[i];
10429
10430 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10431 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10432 double stored_number = number->NumberValue();
10433 if (!IsNaN(test_value)) {
10434 CHECK_EQ(test_value, stored_number);
10435 } else {
10436 uint64_t stored_bits = DoubleToBits(stored_number);
10437 // Check if quiet nan (bits 51..62 all set).
10438 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10439 }
10440
10441 // Check that Date::New preserves non-NaNs in the date range and
10442 // quiets SNaNs.
10443 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10444 double expected_stored_date = DoubleToDateTime(test_value);
10445 double stored_date = date->NumberValue();
10446 if (!IsNaN(expected_stored_date)) {
10447 CHECK_EQ(expected_stored_date, stored_date);
10448 } else {
10449 uint64_t stored_bits = DoubleToBits(stored_date);
10450 // Check if quiet nan (bits 51..62 all set).
10451 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10452 }
10453 }
10454}
10455
10456
10457static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10458 v8::HandleScope scope;
10459 v8::TryCatch tc;
10460 v8::Handle<v8::String> str = args[0]->ToString();
10461 if (tc.HasCaught())
10462 return tc.ReThrow();
10463 return v8::Undefined();
10464}
10465
10466
10467// Test that an exception can be propagated down through a spaghetti
10468// stack using ReThrow.
10469THREADED_TEST(SpaghettiStackReThrow) {
10470 v8::HandleScope scope;
10471 LocalContext context;
10472 context->Global()->Set(
10473 v8::String::New("s"),
10474 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10475 v8::TryCatch try_catch;
10476 CompileRun(
10477 "var i = 0;"
10478 "var o = {"
10479 " toString: function () {"
10480 " if (i == 10) {"
10481 " throw 'Hey!';"
10482 " } else {"
10483 " i++;"
10484 " return s(o);"
10485 " }"
10486 " }"
10487 "};"
10488 "s(o);");
10489 CHECK(try_catch.HasCaught());
10490 v8::String::Utf8Value value(try_catch.Exception());
10491 CHECK_EQ(0, strcmp(*value, "Hey!"));
10492}
10493
10494
Steve Blockd0582a62009-12-15 09:54:21 +000010495TEST(Regress528) {
10496 v8::V8::Initialize();
10497
10498 v8::HandleScope scope;
10499 v8::Persistent<Context> context;
10500 v8::Persistent<Context> other_context;
10501 int gc_count;
10502
10503 // Create a context used to keep the code from aging in the compilation
10504 // cache.
10505 other_context = Context::New();
10506
10507 // Context-dependent context data creates reference from the compilation
10508 // cache to the global object.
10509 const char* source_simple = "1";
10510 context = Context::New();
10511 {
10512 v8::HandleScope scope;
10513
10514 context->Enter();
10515 Local<v8::String> obj = v8::String::New("");
10516 context->SetData(obj);
10517 CompileRun(source_simple);
10518 context->Exit();
10519 }
10520 context.Dispose();
10521 for (gc_count = 1; gc_count < 10; gc_count++) {
10522 other_context->Enter();
10523 CompileRun(source_simple);
10524 other_context->Exit();
10525 v8::internal::Heap::CollectAllGarbage(false);
10526 if (GetGlobalObjectsCount() == 1) break;
10527 }
10528 CHECK_GE(2, gc_count);
10529 CHECK_EQ(1, GetGlobalObjectsCount());
10530
10531 // Eval in a function creates reference from the compilation cache to the
10532 // global object.
10533 const char* source_eval = "function f(){eval('1')}; f()";
10534 context = Context::New();
10535 {
10536 v8::HandleScope scope;
10537
10538 context->Enter();
10539 CompileRun(source_eval);
10540 context->Exit();
10541 }
10542 context.Dispose();
10543 for (gc_count = 1; gc_count < 10; gc_count++) {
10544 other_context->Enter();
10545 CompileRun(source_eval);
10546 other_context->Exit();
10547 v8::internal::Heap::CollectAllGarbage(false);
10548 if (GetGlobalObjectsCount() == 1) break;
10549 }
10550 CHECK_GE(2, gc_count);
10551 CHECK_EQ(1, GetGlobalObjectsCount());
10552
10553 // Looking up the line number for an exception creates reference from the
10554 // compilation cache to the global object.
10555 const char* source_exception = "function f(){throw 1;} f()";
10556 context = Context::New();
10557 {
10558 v8::HandleScope scope;
10559
10560 context->Enter();
10561 v8::TryCatch try_catch;
10562 CompileRun(source_exception);
10563 CHECK(try_catch.HasCaught());
10564 v8::Handle<v8::Message> message = try_catch.Message();
10565 CHECK(!message.IsEmpty());
10566 CHECK_EQ(1, message->GetLineNumber());
10567 context->Exit();
10568 }
10569 context.Dispose();
10570 for (gc_count = 1; gc_count < 10; gc_count++) {
10571 other_context->Enter();
10572 CompileRun(source_exception);
10573 other_context->Exit();
10574 v8::internal::Heap::CollectAllGarbage(false);
10575 if (GetGlobalObjectsCount() == 1) break;
10576 }
10577 CHECK_GE(2, gc_count);
10578 CHECK_EQ(1, GetGlobalObjectsCount());
10579
10580 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010581}
Andrei Popescu402d9372010-02-26 13:31:12 +000010582
10583
10584THREADED_TEST(ScriptOrigin) {
10585 v8::HandleScope scope;
10586 LocalContext env;
10587 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10588 v8::Handle<v8::String> script = v8::String::New(
10589 "function f() {}\n\nfunction g() {}");
10590 v8::Script::Compile(script, &origin)->Run();
10591 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10592 env->Global()->Get(v8::String::New("f")));
10593 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10594 env->Global()->Get(v8::String::New("g")));
10595
10596 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10597 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10598 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10599
10600 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10601 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10602 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10603}
10604
10605
10606THREADED_TEST(ScriptLineNumber) {
10607 v8::HandleScope scope;
10608 LocalContext env;
10609 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10610 v8::Handle<v8::String> script = v8::String::New(
10611 "function f() {}\n\nfunction g() {}");
10612 v8::Script::Compile(script, &origin)->Run();
10613 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10614 env->Global()->Get(v8::String::New("f")));
10615 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10616 env->Global()->Get(v8::String::New("g")));
10617 CHECK_EQ(0, f->GetScriptLineNumber());
10618 CHECK_EQ(2, g->GetScriptLineNumber());
10619}
10620
10621
10622static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10623 const AccessorInfo& info) {
10624 return v8_num(42);
10625}
10626
10627
10628static void SetterWhichSetsYOnThisTo23(Local<String> name,
10629 Local<Value> value,
10630 const AccessorInfo& info) {
10631 info.This()->Set(v8_str("y"), v8_num(23));
10632}
10633
10634
Steve Block6ded16b2010-05-10 14:33:55 +010010635TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010636 v8::HandleScope scope;
10637 Local<ObjectTemplate> templ = ObjectTemplate::New();
10638 templ->SetAccessor(v8_str("x"),
10639 GetterWhichReturns42,
10640 SetterWhichSetsYOnThisTo23);
10641 LocalContext context;
10642 context->Global()->Set(v8_str("P"), templ->NewInstance());
10643 CompileRun("function C1() {"
10644 " this.x = 23;"
10645 "};"
10646 "C1.prototype = P;"
10647 "function C2() {"
10648 " this.x = 23"
10649 "};"
10650 "C2.prototype = { };"
10651 "C2.prototype.__proto__ = P;");
10652
10653 v8::Local<v8::Script> script;
10654 script = v8::Script::Compile(v8_str("new C1();"));
10655 for (int i = 0; i < 10; i++) {
10656 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10657 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10658 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10659 }
10660
10661 script = v8::Script::Compile(v8_str("new C2();"));
10662 for (int i = 0; i < 10; i++) {
10663 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10664 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10665 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10666 }
10667}
10668
10669
10670static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10671 Local<String> name, const AccessorInfo& info) {
10672 return v8_num(42);
10673}
10674
10675
10676static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10677 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10678 if (name->Equals(v8_str("x"))) {
10679 info.This()->Set(v8_str("y"), v8_num(23));
10680 }
10681 return v8::Handle<Value>();
10682}
10683
10684
10685THREADED_TEST(InterceptorOnConstructorPrototype) {
10686 v8::HandleScope scope;
10687 Local<ObjectTemplate> templ = ObjectTemplate::New();
10688 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10689 NamedPropertySetterWhichSetsYOnThisTo23);
10690 LocalContext context;
10691 context->Global()->Set(v8_str("P"), templ->NewInstance());
10692 CompileRun("function C1() {"
10693 " this.x = 23;"
10694 "};"
10695 "C1.prototype = P;"
10696 "function C2() {"
10697 " this.x = 23"
10698 "};"
10699 "C2.prototype = { };"
10700 "C2.prototype.__proto__ = P;");
10701
10702 v8::Local<v8::Script> script;
10703 script = v8::Script::Compile(v8_str("new C1();"));
10704 for (int i = 0; i < 10; i++) {
10705 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10706 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10707 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10708 }
10709
10710 script = v8::Script::Compile(v8_str("new C2();"));
10711 for (int i = 0; i < 10; i++) {
10712 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10713 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10714 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10715 }
10716}
Steve Block6ded16b2010-05-10 14:33:55 +010010717
10718
10719TEST(Bug618) {
10720 const char* source = "function C1() {"
10721 " this.x = 23;"
10722 "};"
10723 "C1.prototype = P;";
10724
10725 v8::HandleScope scope;
10726 LocalContext context;
10727 v8::Local<v8::Script> script;
10728
10729 // Use a simple object as prototype.
10730 v8::Local<v8::Object> prototype = v8::Object::New();
10731 prototype->Set(v8_str("y"), v8_num(42));
10732 context->Global()->Set(v8_str("P"), prototype);
10733
10734 // This compile will add the code to the compilation cache.
10735 CompileRun(source);
10736
10737 script = v8::Script::Compile(v8_str("new C1();"));
10738 for (int i = 0; i < 10; i++) {
10739 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10740 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10741 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10742 }
10743
10744 // Use an API object with accessors as prototype.
10745 Local<ObjectTemplate> templ = ObjectTemplate::New();
10746 templ->SetAccessor(v8_str("x"),
10747 GetterWhichReturns42,
10748 SetterWhichSetsYOnThisTo23);
10749 context->Global()->Set(v8_str("P"), templ->NewInstance());
10750
10751 // This compile will get the code from the compilation cache.
10752 CompileRun(source);
10753
10754 script = v8::Script::Compile(v8_str("new C1();"));
10755 for (int i = 0; i < 10; i++) {
10756 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10757 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10758 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10759 }
10760}
10761
10762int prologue_call_count = 0;
10763int epilogue_call_count = 0;
10764int prologue_call_count_second = 0;
10765int epilogue_call_count_second = 0;
10766
10767void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
10768 ++prologue_call_count;
10769}
10770
10771void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
10772 ++epilogue_call_count;
10773}
10774
10775void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10776 ++prologue_call_count_second;
10777}
10778
10779void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10780 ++epilogue_call_count_second;
10781}
10782
10783TEST(GCCallbacks) {
10784 LocalContext context;
10785
10786 v8::V8::AddGCPrologueCallback(PrologueCallback);
10787 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
10788 CHECK_EQ(0, prologue_call_count);
10789 CHECK_EQ(0, epilogue_call_count);
10790 i::Heap::CollectAllGarbage(false);
10791 CHECK_EQ(1, prologue_call_count);
10792 CHECK_EQ(1, epilogue_call_count);
10793 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
10794 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
10795 i::Heap::CollectAllGarbage(false);
10796 CHECK_EQ(2, prologue_call_count);
10797 CHECK_EQ(2, epilogue_call_count);
10798 CHECK_EQ(1, prologue_call_count_second);
10799 CHECK_EQ(1, epilogue_call_count_second);
10800 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
10801 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
10802 i::Heap::CollectAllGarbage(false);
10803 CHECK_EQ(2, prologue_call_count);
10804 CHECK_EQ(2, epilogue_call_count);
10805 CHECK_EQ(2, prologue_call_count_second);
10806 CHECK_EQ(2, epilogue_call_count_second);
10807 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
10808 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
10809 i::Heap::CollectAllGarbage(false);
10810 CHECK_EQ(2, prologue_call_count);
10811 CHECK_EQ(2, epilogue_call_count);
10812 CHECK_EQ(2, prologue_call_count_second);
10813 CHECK_EQ(2, epilogue_call_count_second);
10814}
Kristian Monsen25f61362010-05-21 11:50:48 +010010815
10816
10817THREADED_TEST(AddToJSFunctionResultCache) {
10818 i::FLAG_allow_natives_syntax = true;
10819 v8::HandleScope scope;
10820
10821 LocalContext context;
10822
10823 const char* code =
10824 "(function() {"
10825 " var key0 = 'a';"
10826 " var key1 = 'b';"
10827 " var r0 = %_GetFromCache(0, key0);"
10828 " var r1 = %_GetFromCache(0, key1);"
10829 " var r0_ = %_GetFromCache(0, key0);"
10830 " if (r0 !== r0_)"
10831 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
10832 " var r1_ = %_GetFromCache(0, key1);"
10833 " if (r1 !== r1_)"
10834 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
10835 " return 'PASSED';"
10836 "})()";
10837 v8::internal::Heap::ClearJSFunctionResultCaches();
10838 ExpectString(code, "PASSED");
10839}
10840
10841
10842static const int k0CacheSize = 16;
10843
10844THREADED_TEST(FillJSFunctionResultCache) {
10845 i::FLAG_allow_natives_syntax = true;
10846 v8::HandleScope scope;
10847
10848 LocalContext context;
10849
10850 const char* code =
10851 "(function() {"
10852 " var k = 'a';"
10853 " var r = %_GetFromCache(0, k);"
10854 " for (var i = 0; i < 16; i++) {"
10855 " %_GetFromCache(0, 'a' + i);"
10856 " };"
10857 " if (r === %_GetFromCache(0, k))"
10858 " return 'FAILED: k0CacheSize is too small';"
10859 " return 'PASSED';"
10860 "})()";
10861 v8::internal::Heap::ClearJSFunctionResultCaches();
10862 ExpectString(code, "PASSED");
10863}
10864
10865
10866THREADED_TEST(RoundRobinGetFromCache) {
10867 i::FLAG_allow_natives_syntax = true;
10868 v8::HandleScope scope;
10869
10870 LocalContext context;
10871
10872 const char* code =
10873 "(function() {"
10874 " var keys = [];"
10875 " for (var i = 0; i < 16; i++) keys.push(i);"
10876 " var values = [];"
10877 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
10878 " for (var i = 0; i < 16; i++) {"
10879 " var v = %_GetFromCache(0, keys[i]);"
10880 " if (v !== values[i])"
10881 " return 'Wrong value for ' + "
10882 " keys[i] + ': ' + v + ' vs. ' + values[i];"
10883 " };"
10884 " return 'PASSED';"
10885 "})()";
10886 v8::internal::Heap::ClearJSFunctionResultCaches();
10887 ExpectString(code, "PASSED");
10888}
10889
10890
10891THREADED_TEST(ReverseGetFromCache) {
10892 i::FLAG_allow_natives_syntax = true;
10893 v8::HandleScope scope;
10894
10895 LocalContext context;
10896
10897 const char* code =
10898 "(function() {"
10899 " var keys = [];"
10900 " for (var i = 0; i < 16; i++) keys.push(i);"
10901 " var values = [];"
10902 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
10903 " for (var i = 15; i >= 16; i--) {"
10904 " var v = %_GetFromCache(0, keys[i]);"
10905 " if (v !== values[i])"
10906 " return 'Wrong value for ' + "
10907 " keys[i] + ': ' + v + ' vs. ' + values[i];"
10908 " };"
10909 " return 'PASSED';"
10910 "})()";
10911 v8::internal::Heap::ClearJSFunctionResultCaches();
10912 ExpectString(code, "PASSED");
10913}
10914
10915
10916THREADED_TEST(TestEviction) {
10917 i::FLAG_allow_natives_syntax = true;
10918 v8::HandleScope scope;
10919
10920 LocalContext context;
10921
10922 const char* code =
10923 "(function() {"
10924 " for (var i = 0; i < 2*16; i++) {"
10925 " %_GetFromCache(0, 'a' + i);"
10926 " };"
10927 " return 'PASSED';"
10928 "})()";
10929 v8::internal::Heap::ClearJSFunctionResultCaches();
10930 ExpectString(code, "PASSED");
10931}