blob: 912312531723150aeaae9161309a6e399d02c8d3 [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
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010030#define USE_NEW_QUERY_CALLBACKS
31
Steve Blocka7e24c12009-10-30 11:49:00 +000032#include "v8.h"
33
34#include "api.h"
35#include "compilation-cache.h"
36#include "execution.h"
37#include "snapshot.h"
38#include "platform.h"
39#include "top.h"
Steve Block3ce2e202009-11-05 08:53:23 +000040#include "utils.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000041#include "cctest.h"
42
Andrei Popescu31002712010-02-23 13:46:05 +000043static const bool kLogThreading = true;
Steve Blockd0582a62009-12-15 09:54:21 +000044
Steve Blocka7e24c12009-10-30 11:49:00 +000045static bool IsNaN(double x) {
46#ifdef WIN32
47 return _isnan(x);
48#else
49 return isnan(x);
50#endif
51}
52
53using ::v8::ObjectTemplate;
54using ::v8::Value;
55using ::v8::Context;
56using ::v8::Local;
57using ::v8::String;
58using ::v8::Script;
59using ::v8::Function;
60using ::v8::AccessorInfo;
61using ::v8::Extension;
62
63namespace i = ::v8::internal;
64
Steve Blocka7e24c12009-10-30 11:49:00 +000065
Leon Clarked91b9f72010-01-27 17:25:45 +000066static void ExpectString(const char* code, const char* expected) {
67 Local<Value> result = CompileRun(code);
68 CHECK(result->IsString());
69 String::AsciiValue ascii(result);
70 CHECK_EQ(expected, *ascii);
71}
72
73
74static void ExpectBoolean(const char* code, bool expected) {
75 Local<Value> result = CompileRun(code);
76 CHECK(result->IsBoolean());
77 CHECK_EQ(expected, result->BooleanValue());
78}
79
80
Leon Clarkef7060e22010-06-03 12:02:55 +010081static void ExpectTrue(const char* code) {
82 ExpectBoolean(code, true);
83}
84
85
Leon Clarked91b9f72010-01-27 17:25:45 +000086static void ExpectObject(const char* code, Local<Value> expected) {
87 Local<Value> result = CompileRun(code);
88 CHECK(result->Equals(expected));
89}
90
91
Steve Blocka7e24c12009-10-30 11:49:00 +000092static int signature_callback_count;
93static v8::Handle<Value> IncrementingSignatureCallback(
94 const v8::Arguments& args) {
95 ApiTestFuzzer::Fuzz();
96 signature_callback_count++;
97 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
98 for (int i = 0; i < args.Length(); i++)
99 result->Set(v8::Integer::New(i), args[i]);
100 return result;
101}
102
103
104static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
105 ApiTestFuzzer::Fuzz();
106 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
107 for (int i = 0; i < args.Length(); i++) {
108 result->Set(v8::Integer::New(i), args[i]);
109 }
110 return result;
111}
112
113
114THREADED_TEST(Handles) {
115 v8::HandleScope scope;
116 Local<Context> local_env;
117 {
118 LocalContext env;
119 local_env = env.local();
120 }
121
122 // Local context should still be live.
123 CHECK(!local_env.IsEmpty());
124 local_env->Enter();
125
126 v8::Handle<v8::Primitive> undef = v8::Undefined();
127 CHECK(!undef.IsEmpty());
128 CHECK(undef->IsUndefined());
129
130 const char* c_source = "1 + 2 + 3";
131 Local<String> source = String::New(c_source);
132 Local<Script> script = Script::Compile(source);
133 CHECK_EQ(6, script->Run()->Int32Value());
134
135 local_env->Exit();
136}
137
138
Steve Blocka7e24c12009-10-30 11:49:00 +0000139THREADED_TEST(ReceiverSignature) {
140 v8::HandleScope scope;
141 LocalContext env;
142 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
143 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
144 fun->PrototypeTemplate()->Set(
145 v8_str("m"),
146 v8::FunctionTemplate::New(IncrementingSignatureCallback,
147 v8::Handle<Value>(),
148 sig));
149 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
150 signature_callback_count = 0;
151 CompileRun(
152 "var o = new Fun();"
153 "o.m();");
154 CHECK_EQ(1, signature_callback_count);
155 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
156 sub_fun->Inherit(fun);
157 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
158 CompileRun(
159 "var o = new SubFun();"
160 "o.m();");
161 CHECK_EQ(2, signature_callback_count);
162
163 v8::TryCatch try_catch;
164 CompileRun(
165 "var o = { };"
166 "o.m = Fun.prototype.m;"
167 "o.m();");
168 CHECK_EQ(2, signature_callback_count);
169 CHECK(try_catch.HasCaught());
170 try_catch.Reset();
171 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
172 sub_fun->Inherit(fun);
173 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
174 CompileRun(
175 "var o = new UnrelFun();"
176 "o.m = Fun.prototype.m;"
177 "o.m();");
178 CHECK_EQ(2, signature_callback_count);
179 CHECK(try_catch.HasCaught());
180}
181
182
183
184
185THREADED_TEST(ArgumentSignature) {
186 v8::HandleScope scope;
187 LocalContext env;
188 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
189 cons->SetClassName(v8_str("Cons"));
190 v8::Handle<v8::Signature> sig =
191 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
192 v8::Handle<v8::FunctionTemplate> fun =
193 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
194 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
195 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
196
197 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
198 CHECK(value1->IsTrue());
199
200 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
201 CHECK(value2->IsTrue());
202
203 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
204 CHECK(value3->IsTrue());
205
206 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
207 cons1->SetClassName(v8_str("Cons1"));
208 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
209 cons2->SetClassName(v8_str("Cons2"));
210 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
211 cons3->SetClassName(v8_str("Cons3"));
212
213 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
214 v8::Handle<v8::Signature> wsig =
215 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
216 v8::Handle<v8::FunctionTemplate> fun2 =
217 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
218
219 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
220 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
221 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
222 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
223 v8::Handle<Value> value4 = CompileRun(
224 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
225 "'[object Cons1],[object Cons2],[object Cons3]'");
226 CHECK(value4->IsTrue());
227
228 v8::Handle<Value> value5 = CompileRun(
229 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
230 CHECK(value5->IsTrue());
231
232 v8::Handle<Value> value6 = CompileRun(
233 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
234 CHECK(value6->IsTrue());
235
236 v8::Handle<Value> value7 = CompileRun(
237 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
238 "'[object Cons1],[object Cons2],[object Cons3],d';");
239 CHECK(value7->IsTrue());
240
241 v8::Handle<Value> value8 = CompileRun(
242 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
243 CHECK(value8->IsTrue());
244}
245
246
247THREADED_TEST(HulIgennem) {
248 v8::HandleScope scope;
249 LocalContext env;
250 v8::Handle<v8::Primitive> undef = v8::Undefined();
251 Local<String> undef_str = undef->ToString();
252 char* value = i::NewArray<char>(undef_str->Length() + 1);
253 undef_str->WriteAscii(value);
254 CHECK_EQ(0, strcmp(value, "undefined"));
255 i::DeleteArray(value);
256}
257
258
259THREADED_TEST(Access) {
260 v8::HandleScope scope;
261 LocalContext env;
262 Local<v8::Object> obj = v8::Object::New();
263 Local<Value> foo_before = obj->Get(v8_str("foo"));
264 CHECK(foo_before->IsUndefined());
265 Local<String> bar_str = v8_str("bar");
266 obj->Set(v8_str("foo"), bar_str);
267 Local<Value> foo_after = obj->Get(v8_str("foo"));
268 CHECK(!foo_after->IsUndefined());
269 CHECK(foo_after->IsString());
270 CHECK_EQ(bar_str, foo_after);
271}
272
273
Steve Block6ded16b2010-05-10 14:33:55 +0100274THREADED_TEST(AccessElement) {
275 v8::HandleScope scope;
276 LocalContext env;
277 Local<v8::Object> obj = v8::Object::New();
278 Local<Value> before = obj->Get(1);
279 CHECK(before->IsUndefined());
280 Local<String> bar_str = v8_str("bar");
281 obj->Set(1, bar_str);
282 Local<Value> after = obj->Get(1);
283 CHECK(!after->IsUndefined());
284 CHECK(after->IsString());
285 CHECK_EQ(bar_str, after);
286
287 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
288 CHECK_EQ(v8_str("a"), value->Get(0));
289 CHECK_EQ(v8_str("b"), value->Get(1));
290}
291
292
Steve Blocka7e24c12009-10-30 11:49:00 +0000293THREADED_TEST(Script) {
294 v8::HandleScope scope;
295 LocalContext env;
296 const char* c_source = "1 + 2 + 3";
297 Local<String> source = String::New(c_source);
298 Local<Script> script = Script::Compile(source);
299 CHECK_EQ(6, script->Run()->Int32Value());
300}
301
302
303static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000304 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000305 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000306 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000307 return converted;
308}
309
310
311class TestResource: public String::ExternalStringResource {
312 public:
313 static int dispose_count;
314
315 explicit TestResource(uint16_t* data)
316 : data_(data), length_(0) {
317 while (data[length_]) ++length_;
318 }
319
320 ~TestResource() {
321 i::DeleteArray(data_);
322 ++dispose_count;
323 }
324
325 const uint16_t* data() const {
326 return data_;
327 }
328
329 size_t length() const {
330 return length_;
331 }
332 private:
333 uint16_t* data_;
334 size_t length_;
335};
336
337
338int TestResource::dispose_count = 0;
339
340
341class TestAsciiResource: public String::ExternalAsciiStringResource {
342 public:
343 static int dispose_count;
344
345 explicit TestAsciiResource(const char* data)
346 : data_(data),
347 length_(strlen(data)) { }
348
349 ~TestAsciiResource() {
350 i::DeleteArray(data_);
351 ++dispose_count;
352 }
353
354 const char* data() const {
355 return data_;
356 }
357
358 size_t length() const {
359 return length_;
360 }
361 private:
362 const char* data_;
363 size_t length_;
364};
365
366
367int TestAsciiResource::dispose_count = 0;
368
369
370THREADED_TEST(ScriptUsingStringResource) {
371 TestResource::dispose_count = 0;
372 const char* c_source = "1 + 2 * 3";
373 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
374 {
375 v8::HandleScope scope;
376 LocalContext env;
377 TestResource* resource = new TestResource(two_byte_source);
378 Local<String> source = String::NewExternal(resource);
379 Local<Script> script = Script::Compile(source);
380 Local<Value> value = script->Run();
381 CHECK(value->IsNumber());
382 CHECK_EQ(7, value->Int32Value());
383 CHECK(source->IsExternal());
384 CHECK_EQ(resource,
385 static_cast<TestResource*>(source->GetExternalStringResource()));
386 v8::internal::Heap::CollectAllGarbage(false);
387 CHECK_EQ(0, TestResource::dispose_count);
388 }
389 v8::internal::CompilationCache::Clear();
390 v8::internal::Heap::CollectAllGarbage(false);
391 CHECK_EQ(1, TestResource::dispose_count);
392}
393
394
395THREADED_TEST(ScriptUsingAsciiStringResource) {
396 TestAsciiResource::dispose_count = 0;
397 const char* c_source = "1 + 2 * 3";
398 {
399 v8::HandleScope scope;
400 LocalContext env;
401 Local<String> source =
402 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
403 Local<Script> script = Script::Compile(source);
404 Local<Value> value = script->Run();
405 CHECK(value->IsNumber());
406 CHECK_EQ(7, value->Int32Value());
407 v8::internal::Heap::CollectAllGarbage(false);
408 CHECK_EQ(0, TestAsciiResource::dispose_count);
409 }
410 v8::internal::CompilationCache::Clear();
411 v8::internal::Heap::CollectAllGarbage(false);
412 CHECK_EQ(1, TestAsciiResource::dispose_count);
413}
414
415
416THREADED_TEST(ScriptMakingExternalString) {
417 TestResource::dispose_count = 0;
418 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
419 {
420 v8::HandleScope scope;
421 LocalContext env;
422 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000423 // Trigger GCs so that the newly allocated string moves to old gen.
424 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
425 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000426 bool success = source->MakeExternal(new TestResource(two_byte_source));
427 CHECK(success);
428 Local<Script> script = Script::Compile(source);
429 Local<Value> value = script->Run();
430 CHECK(value->IsNumber());
431 CHECK_EQ(7, value->Int32Value());
432 v8::internal::Heap::CollectAllGarbage(false);
433 CHECK_EQ(0, TestResource::dispose_count);
434 }
435 v8::internal::CompilationCache::Clear();
436 v8::internal::Heap::CollectAllGarbage(false);
437 CHECK_EQ(1, TestResource::dispose_count);
438}
439
440
441THREADED_TEST(ScriptMakingExternalAsciiString) {
442 TestAsciiResource::dispose_count = 0;
443 const char* c_source = "1 + 2 * 3";
444 {
445 v8::HandleScope scope;
446 LocalContext env;
447 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000448 // Trigger GCs so that the newly allocated string moves to old gen.
449 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
450 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000451 bool success = source->MakeExternal(
452 new TestAsciiResource(i::StrDup(c_source)));
453 CHECK(success);
454 Local<Script> script = Script::Compile(source);
455 Local<Value> value = script->Run();
456 CHECK(value->IsNumber());
457 CHECK_EQ(7, value->Int32Value());
458 v8::internal::Heap::CollectAllGarbage(false);
459 CHECK_EQ(0, TestAsciiResource::dispose_count);
460 }
461 v8::internal::CompilationCache::Clear();
462 v8::internal::Heap::CollectAllGarbage(false);
463 CHECK_EQ(1, TestAsciiResource::dispose_count);
464}
465
466
Andrei Popescu402d9372010-02-26 13:31:12 +0000467TEST(MakingExternalStringConditions) {
468 v8::HandleScope scope;
469 LocalContext env;
470
471 // Free some space in the new space so that we can check freshness.
472 i::Heap::CollectGarbage(0, i::NEW_SPACE);
473 i::Heap::CollectGarbage(0, i::NEW_SPACE);
474
475 Local<String> small_string = String::New(AsciiToTwoByteString("small"));
476 // We should refuse to externalize newly created small string.
477 CHECK(!small_string->CanMakeExternal());
478 // Trigger GCs so that the newly allocated string moves to old gen.
479 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
480 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
481 // Old space strings should be accepted.
482 CHECK(small_string->CanMakeExternal());
483
484 small_string = String::New(AsciiToTwoByteString("small 2"));
485 // We should refuse externalizing newly created small string.
486 CHECK(!small_string->CanMakeExternal());
487 for (int i = 0; i < 100; i++) {
488 String::Value value(small_string);
489 }
490 // Frequently used strings should be accepted.
491 CHECK(small_string->CanMakeExternal());
492
493 const int buf_size = 10 * 1024;
494 char* buf = i::NewArray<char>(buf_size);
495 memset(buf, 'a', buf_size);
496 buf[buf_size - 1] = '\0';
497 Local<String> large_string = String::New(AsciiToTwoByteString(buf));
498 i::DeleteArray(buf);
499 // Large strings should be immediately accepted.
500 CHECK(large_string->CanMakeExternal());
501}
502
503
504TEST(MakingExternalAsciiStringConditions) {
505 v8::HandleScope scope;
506 LocalContext env;
507
508 // Free some space in the new space so that we can check freshness.
509 i::Heap::CollectGarbage(0, i::NEW_SPACE);
510 i::Heap::CollectGarbage(0, i::NEW_SPACE);
511
512 Local<String> small_string = String::New("small");
513 // We should refuse to externalize newly created small string.
514 CHECK(!small_string->CanMakeExternal());
515 // Trigger GCs so that the newly allocated string moves to old gen.
516 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
517 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
518 // Old space strings should be accepted.
519 CHECK(small_string->CanMakeExternal());
520
521 small_string = String::New("small 2");
522 // We should refuse externalizing newly created small string.
523 CHECK(!small_string->CanMakeExternal());
524 for (int i = 0; i < 100; i++) {
525 String::Value value(small_string);
526 }
527 // Frequently used strings should be accepted.
528 CHECK(small_string->CanMakeExternal());
529
530 const int buf_size = 10 * 1024;
531 char* buf = i::NewArray<char>(buf_size);
532 memset(buf, 'a', buf_size);
533 buf[buf_size - 1] = '\0';
534 Local<String> large_string = String::New(buf);
535 i::DeleteArray(buf);
536 // Large strings should be immediately accepted.
537 CHECK(large_string->CanMakeExternal());
538}
539
540
Steve Blocka7e24c12009-10-30 11:49:00 +0000541THREADED_TEST(UsingExternalString) {
542 {
543 v8::HandleScope scope;
544 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
545 Local<String> string =
546 String::NewExternal(new TestResource(two_byte_string));
547 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
548 // Trigger GCs so that the newly allocated string moves to old gen.
549 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
550 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
551 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
552 CHECK(isymbol->IsSymbol());
553 }
554 i::Heap::CollectAllGarbage(false);
555 i::Heap::CollectAllGarbage(false);
556}
557
558
559THREADED_TEST(UsingExternalAsciiString) {
560 {
561 v8::HandleScope scope;
562 const char* one_byte_string = "test string";
563 Local<String> string = String::NewExternal(
564 new TestAsciiResource(i::StrDup(one_byte_string)));
565 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
566 // Trigger GCs so that the newly allocated string moves to old gen.
567 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
568 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
569 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
570 CHECK(isymbol->IsSymbol());
571 }
572 i::Heap::CollectAllGarbage(false);
573 i::Heap::CollectAllGarbage(false);
574}
575
576
Leon Clarkee46be812010-01-19 14:06:41 +0000577THREADED_TEST(ScavengeExternalString) {
578 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100579 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000580 {
581 v8::HandleScope scope;
582 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
583 Local<String> string =
584 String::NewExternal(new TestResource(two_byte_string));
585 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
586 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100587 in_new_space = i::Heap::InNewSpace(*istring);
588 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000589 CHECK_EQ(0, TestResource::dispose_count);
590 }
Steve Block6ded16b2010-05-10 14:33:55 +0100591 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000592 CHECK_EQ(1, TestResource::dispose_count);
593}
594
595
596THREADED_TEST(ScavengeExternalAsciiString) {
597 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100598 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000599 {
600 v8::HandleScope scope;
601 const char* one_byte_string = "test string";
602 Local<String> string = String::NewExternal(
603 new TestAsciiResource(i::StrDup(one_byte_string)));
604 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
605 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100606 in_new_space = i::Heap::InNewSpace(*istring);
607 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000608 CHECK_EQ(0, TestAsciiResource::dispose_count);
609 }
Steve Block6ded16b2010-05-10 14:33:55 +0100610 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000611 CHECK_EQ(1, TestAsciiResource::dispose_count);
612}
613
614
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100615class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
616 public:
617 static int dispose_calls;
618
619 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
620 : TestAsciiResource(data),
621 dispose_(dispose) { }
622
623 void Dispose() {
624 ++dispose_calls;
625 if (dispose_) delete this;
626 }
627 private:
628 bool dispose_;
629};
630
631
632int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
633
634
635TEST(ExternalStringWithDisposeHandling) {
636 const char* c_source = "1 + 2 * 3";
637
638 // Use a stack allocated external string resource allocated object.
639 TestAsciiResource::dispose_count = 0;
640 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
641 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
642 {
643 v8::HandleScope scope;
644 LocalContext env;
645 Local<String> source = String::NewExternal(&res_stack);
646 Local<Script> script = Script::Compile(source);
647 Local<Value> value = script->Run();
648 CHECK(value->IsNumber());
649 CHECK_EQ(7, value->Int32Value());
650 v8::internal::Heap::CollectAllGarbage(false);
651 CHECK_EQ(0, TestAsciiResource::dispose_count);
652 }
653 v8::internal::CompilationCache::Clear();
654 v8::internal::Heap::CollectAllGarbage(false);
655 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
656 CHECK_EQ(0, TestAsciiResource::dispose_count);
657
658 // Use a heap allocated external string resource allocated object.
659 TestAsciiResource::dispose_count = 0;
660 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
661 TestAsciiResource* res_heap =
662 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
663 {
664 v8::HandleScope scope;
665 LocalContext env;
666 Local<String> source = String::NewExternal(res_heap);
667 Local<Script> script = Script::Compile(source);
668 Local<Value> value = script->Run();
669 CHECK(value->IsNumber());
670 CHECK_EQ(7, value->Int32Value());
671 v8::internal::Heap::CollectAllGarbage(false);
672 CHECK_EQ(0, TestAsciiResource::dispose_count);
673 }
674 v8::internal::CompilationCache::Clear();
675 v8::internal::Heap::CollectAllGarbage(false);
676 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
677 CHECK_EQ(1, TestAsciiResource::dispose_count);
678}
679
680
Steve Block3ce2e202009-11-05 08:53:23 +0000681THREADED_TEST(StringConcat) {
682 {
683 v8::HandleScope scope;
684 LocalContext env;
685 const char* one_byte_string_1 = "function a_times_t";
686 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
687 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
688 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
689 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
690 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
691 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
692 Local<String> left = v8_str(one_byte_string_1);
693 Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1));
694 Local<String> source = String::Concat(left, right);
695 right = String::NewExternal(
696 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
697 source = String::Concat(source, right);
698 right = String::NewExternal(
699 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
700 source = String::Concat(source, right);
701 right = v8_str(one_byte_string_2);
702 source = String::Concat(source, right);
703 right = String::New(AsciiToTwoByteString(two_byte_string_2));
704 source = String::Concat(source, right);
705 right = String::NewExternal(
706 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
707 source = String::Concat(source, right);
708 Local<Script> script = Script::Compile(source);
709 Local<Value> value = script->Run();
710 CHECK(value->IsNumber());
711 CHECK_EQ(68, value->Int32Value());
712 }
713 v8::internal::CompilationCache::Clear();
714 i::Heap::CollectAllGarbage(false);
715 i::Heap::CollectAllGarbage(false);
716}
717
718
Steve Blocka7e24c12009-10-30 11:49:00 +0000719THREADED_TEST(GlobalProperties) {
720 v8::HandleScope scope;
721 LocalContext env;
722 v8::Handle<v8::Object> global = env->Global();
723 global->Set(v8_str("pi"), v8_num(3.1415926));
724 Local<Value> pi = global->Get(v8_str("pi"));
725 CHECK_EQ(3.1415926, pi->NumberValue());
726}
727
728
729static v8::Handle<Value> handle_call(const v8::Arguments& args) {
730 ApiTestFuzzer::Fuzz();
731 return v8_num(102);
732}
733
734
735static v8::Handle<Value> construct_call(const v8::Arguments& args) {
736 ApiTestFuzzer::Fuzz();
737 args.This()->Set(v8_str("x"), v8_num(1));
738 args.This()->Set(v8_str("y"), v8_num(2));
739 return args.This();
740}
741
742THREADED_TEST(FunctionTemplate) {
743 v8::HandleScope scope;
744 LocalContext env;
745 {
746 Local<v8::FunctionTemplate> fun_templ =
747 v8::FunctionTemplate::New(handle_call);
748 Local<Function> fun = fun_templ->GetFunction();
749 env->Global()->Set(v8_str("obj"), fun);
750 Local<Script> script = v8_compile("obj()");
751 CHECK_EQ(102, script->Run()->Int32Value());
752 }
753 // Use SetCallHandler to initialize a function template, should work like the
754 // previous one.
755 {
756 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
757 fun_templ->SetCallHandler(handle_call);
758 Local<Function> fun = fun_templ->GetFunction();
759 env->Global()->Set(v8_str("obj"), fun);
760 Local<Script> script = v8_compile("obj()");
761 CHECK_EQ(102, script->Run()->Int32Value());
762 }
763 // Test constructor calls.
764 {
765 Local<v8::FunctionTemplate> fun_templ =
766 v8::FunctionTemplate::New(construct_call);
767 fun_templ->SetClassName(v8_str("funky"));
768 Local<Function> fun = fun_templ->GetFunction();
769 env->Global()->Set(v8_str("obj"), fun);
770 Local<Script> script = v8_compile("var s = new obj(); s.x");
771 CHECK_EQ(1, script->Run()->Int32Value());
772
773 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
774 CHECK_EQ(v8_str("[object funky]"), result);
775 }
776}
777
778
779THREADED_TEST(FindInstanceInPrototypeChain) {
780 v8::HandleScope scope;
781 LocalContext env;
782
783 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
784 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
785 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
786 derived->Inherit(base);
787
788 Local<v8::Function> base_function = base->GetFunction();
789 Local<v8::Function> derived_function = derived->GetFunction();
790 Local<v8::Function> other_function = other->GetFunction();
791
792 Local<v8::Object> base_instance = base_function->NewInstance();
793 Local<v8::Object> derived_instance = derived_function->NewInstance();
794 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
795 Local<v8::Object> other_instance = other_function->NewInstance();
796 derived_instance2->Set(v8_str("__proto__"), derived_instance);
797 other_instance->Set(v8_str("__proto__"), derived_instance2);
798
799 // base_instance is only an instance of base.
800 CHECK_EQ(base_instance,
801 base_instance->FindInstanceInPrototypeChain(base));
802 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
803 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
804
805 // derived_instance is an instance of base and derived.
806 CHECK_EQ(derived_instance,
807 derived_instance->FindInstanceInPrototypeChain(base));
808 CHECK_EQ(derived_instance,
809 derived_instance->FindInstanceInPrototypeChain(derived));
810 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
811
812 // other_instance is an instance of other and its immediate
813 // prototype derived_instance2 is an instance of base and derived.
814 // Note, derived_instance is an instance of base and derived too,
815 // but it comes after derived_instance2 in the prototype chain of
816 // other_instance.
817 CHECK_EQ(derived_instance2,
818 other_instance->FindInstanceInPrototypeChain(base));
819 CHECK_EQ(derived_instance2,
820 other_instance->FindInstanceInPrototypeChain(derived));
821 CHECK_EQ(other_instance,
822 other_instance->FindInstanceInPrototypeChain(other));
823}
824
825
Steve Block3ce2e202009-11-05 08:53:23 +0000826THREADED_TEST(TinyInteger) {
827 v8::HandleScope scope;
828 LocalContext env;
829 int32_t value = 239;
830 Local<v8::Integer> value_obj = v8::Integer::New(value);
831 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
832}
833
834
835THREADED_TEST(BigSmiInteger) {
836 v8::HandleScope scope;
837 LocalContext env;
838 int32_t value = i::Smi::kMaxValue;
839 // We cannot add one to a Smi::kMaxValue without wrapping.
840 if (i::kSmiValueSize < 32) {
841 CHECK(i::Smi::IsValid(value));
842 CHECK(!i::Smi::IsValid(value + 1));
843 Local<v8::Integer> value_obj = v8::Integer::New(value);
844 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
845 }
846}
847
848
849THREADED_TEST(BigInteger) {
850 v8::HandleScope scope;
851 LocalContext env;
852 // We cannot add one to a Smi::kMaxValue without wrapping.
853 if (i::kSmiValueSize < 32) {
854 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
855 // The code will not be run in that case, due to the "if" guard.
856 int32_t value =
857 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
858 CHECK(value > i::Smi::kMaxValue);
859 CHECK(!i::Smi::IsValid(value));
860 Local<v8::Integer> value_obj = v8::Integer::New(value);
861 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
862 }
863}
864
865
866THREADED_TEST(TinyUnsignedInteger) {
867 v8::HandleScope scope;
868 LocalContext env;
869 uint32_t value = 239;
870 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
871 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
872}
873
874
875THREADED_TEST(BigUnsignedSmiInteger) {
876 v8::HandleScope scope;
877 LocalContext env;
878 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
879 CHECK(i::Smi::IsValid(value));
880 CHECK(!i::Smi::IsValid(value + 1));
881 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
882 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
883}
884
885
886THREADED_TEST(BigUnsignedInteger) {
887 v8::HandleScope scope;
888 LocalContext env;
889 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
890 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
891 CHECK(!i::Smi::IsValid(value));
892 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
893 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
894}
895
896
897THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
898 v8::HandleScope scope;
899 LocalContext env;
900 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
901 uint32_t value = INT32_MAX_AS_UINT + 1;
902 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
903 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
904 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
905}
906
907
Steve Blocka7e24c12009-10-30 11:49:00 +0000908THREADED_TEST(Number) {
909 v8::HandleScope scope;
910 LocalContext env;
911 double PI = 3.1415926;
912 Local<v8::Number> pi_obj = v8::Number::New(PI);
913 CHECK_EQ(PI, pi_obj->NumberValue());
914}
915
916
917THREADED_TEST(ToNumber) {
918 v8::HandleScope scope;
919 LocalContext env;
920 Local<String> str = v8_str("3.1415926");
921 CHECK_EQ(3.1415926, str->NumberValue());
922 v8::Handle<v8::Boolean> t = v8::True();
923 CHECK_EQ(1.0, t->NumberValue());
924 v8::Handle<v8::Boolean> f = v8::False();
925 CHECK_EQ(0.0, f->NumberValue());
926}
927
928
929THREADED_TEST(Date) {
930 v8::HandleScope scope;
931 LocalContext env;
932 double PI = 3.1415926;
933 Local<Value> date_obj = v8::Date::New(PI);
934 CHECK_EQ(3.0, date_obj->NumberValue());
935}
936
937
938THREADED_TEST(Boolean) {
939 v8::HandleScope scope;
940 LocalContext env;
941 v8::Handle<v8::Boolean> t = v8::True();
942 CHECK(t->Value());
943 v8::Handle<v8::Boolean> f = v8::False();
944 CHECK(!f->Value());
945 v8::Handle<v8::Primitive> u = v8::Undefined();
946 CHECK(!u->BooleanValue());
947 v8::Handle<v8::Primitive> n = v8::Null();
948 CHECK(!n->BooleanValue());
949 v8::Handle<String> str1 = v8_str("");
950 CHECK(!str1->BooleanValue());
951 v8::Handle<String> str2 = v8_str("x");
952 CHECK(str2->BooleanValue());
953 CHECK(!v8::Number::New(0)->BooleanValue());
954 CHECK(v8::Number::New(-1)->BooleanValue());
955 CHECK(v8::Number::New(1)->BooleanValue());
956 CHECK(v8::Number::New(42)->BooleanValue());
957 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
958}
959
960
961static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
962 ApiTestFuzzer::Fuzz();
963 return v8_num(13.4);
964}
965
966
967static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
968 ApiTestFuzzer::Fuzz();
969 return v8_num(876);
970}
971
972
973THREADED_TEST(GlobalPrototype) {
974 v8::HandleScope scope;
975 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
976 func_templ->PrototypeTemplate()->Set(
977 "dummy",
978 v8::FunctionTemplate::New(DummyCallHandler));
979 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
980 templ->Set("x", v8_num(200));
981 templ->SetAccessor(v8_str("m"), GetM);
982 LocalContext env(0, templ);
983 v8::Handle<v8::Object> obj = env->Global();
984 v8::Handle<Script> script = v8_compile("dummy()");
985 v8::Handle<Value> result = script->Run();
986 CHECK_EQ(13.4, result->NumberValue());
987 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
988 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
989}
990
991
Steve Blocka7e24c12009-10-30 11:49:00 +0000992THREADED_TEST(ObjectTemplate) {
993 v8::HandleScope scope;
994 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
995 templ1->Set("x", v8_num(10));
996 templ1->Set("y", v8_num(13));
997 LocalContext env;
998 Local<v8::Object> instance1 = templ1->NewInstance();
999 env->Global()->Set(v8_str("p"), instance1);
1000 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1001 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1002 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1003 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1004 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1005 templ2->Set("a", v8_num(12));
1006 templ2->Set("b", templ1);
1007 Local<v8::Object> instance2 = templ2->NewInstance();
1008 env->Global()->Set(v8_str("q"), instance2);
1009 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1010 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1011 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1012 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1013}
1014
1015
1016static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1017 ApiTestFuzzer::Fuzz();
1018 return v8_num(17.2);
1019}
1020
1021
1022static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1023 ApiTestFuzzer::Fuzz();
1024 return v8_num(15.2);
1025}
1026
1027
1028THREADED_TEST(DescriptorInheritance) {
1029 v8::HandleScope scope;
1030 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1031 super->PrototypeTemplate()->Set("flabby",
1032 v8::FunctionTemplate::New(GetFlabby));
1033 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1034
1035 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1036
1037 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1038 base1->Inherit(super);
1039 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1040
1041 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1042 base2->Inherit(super);
1043 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1044
1045 LocalContext env;
1046
1047 env->Global()->Set(v8_str("s"), super->GetFunction());
1048 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1049 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1050
1051 // Checks right __proto__ chain.
1052 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1053 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1054
1055 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1056
1057 // Instance accessor should not be visible on function object or its prototype
1058 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1059 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1060 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1061
1062 env->Global()->Set(v8_str("obj"),
1063 base1->GetFunction()->NewInstance());
1064 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1065 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1066 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1067 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1068 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1069
1070 env->Global()->Set(v8_str("obj2"),
1071 base2->GetFunction()->NewInstance());
1072 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1073 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1074 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1075 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1076 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1077
1078 // base1 and base2 cannot cross reference to each's prototype
1079 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1080 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1081}
1082
1083
1084int echo_named_call_count;
1085
1086
1087static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1088 const AccessorInfo& info) {
1089 ApiTestFuzzer::Fuzz();
1090 CHECK_EQ(v8_str("data"), info.Data());
1091 echo_named_call_count++;
1092 return name;
1093}
1094
1095
1096THREADED_TEST(NamedPropertyHandlerGetter) {
1097 echo_named_call_count = 0;
1098 v8::HandleScope scope;
1099 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1100 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1101 0, 0, 0, 0,
1102 v8_str("data"));
1103 LocalContext env;
1104 env->Global()->Set(v8_str("obj"),
1105 templ->GetFunction()->NewInstance());
1106 CHECK_EQ(echo_named_call_count, 0);
1107 v8_compile("obj.x")->Run();
1108 CHECK_EQ(echo_named_call_count, 1);
1109 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1110 v8::Handle<Value> str = CompileRun(code);
1111 String::AsciiValue value(str);
1112 CHECK_EQ(*value, "oddlepoddle");
1113 // Check default behavior
1114 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1115 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1116 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1117}
1118
1119
1120int echo_indexed_call_count = 0;
1121
1122
1123static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1124 const AccessorInfo& info) {
1125 ApiTestFuzzer::Fuzz();
1126 CHECK_EQ(v8_num(637), info.Data());
1127 echo_indexed_call_count++;
1128 return v8_num(index);
1129}
1130
1131
1132THREADED_TEST(IndexedPropertyHandlerGetter) {
1133 v8::HandleScope scope;
1134 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1135 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1136 0, 0, 0, 0,
1137 v8_num(637));
1138 LocalContext env;
1139 env->Global()->Set(v8_str("obj"),
1140 templ->GetFunction()->NewInstance());
1141 Local<Script> script = v8_compile("obj[900]");
1142 CHECK_EQ(script->Run()->Int32Value(), 900);
1143}
1144
1145
1146v8::Handle<v8::Object> bottom;
1147
1148static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1149 uint32_t index,
1150 const AccessorInfo& info) {
1151 ApiTestFuzzer::Fuzz();
1152 CHECK(info.This()->Equals(bottom));
1153 return v8::Handle<Value>();
1154}
1155
1156static v8::Handle<Value> CheckThisNamedPropertyHandler(
1157 Local<String> name,
1158 const AccessorInfo& info) {
1159 ApiTestFuzzer::Fuzz();
1160 CHECK(info.This()->Equals(bottom));
1161 return v8::Handle<Value>();
1162}
1163
1164
1165v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1166 Local<Value> value,
1167 const AccessorInfo& info) {
1168 ApiTestFuzzer::Fuzz();
1169 CHECK(info.This()->Equals(bottom));
1170 return v8::Handle<Value>();
1171}
1172
1173
1174v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1175 Local<Value> value,
1176 const AccessorInfo& info) {
1177 ApiTestFuzzer::Fuzz();
1178 CHECK(info.This()->Equals(bottom));
1179 return v8::Handle<Value>();
1180}
1181
1182v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery(
1183 uint32_t index,
1184 const AccessorInfo& info) {
1185 ApiTestFuzzer::Fuzz();
1186 CHECK(info.This()->Equals(bottom));
1187 return v8::Handle<v8::Boolean>();
1188}
1189
1190
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001191v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001192 const AccessorInfo& info) {
1193 ApiTestFuzzer::Fuzz();
1194 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001195 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001196}
1197
1198
1199v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1200 uint32_t index,
1201 const AccessorInfo& info) {
1202 ApiTestFuzzer::Fuzz();
1203 CHECK(info.This()->Equals(bottom));
1204 return v8::Handle<v8::Boolean>();
1205}
1206
1207
1208v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1209 Local<String> property,
1210 const AccessorInfo& info) {
1211 ApiTestFuzzer::Fuzz();
1212 CHECK(info.This()->Equals(bottom));
1213 return v8::Handle<v8::Boolean>();
1214}
1215
1216
1217v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1218 const AccessorInfo& info) {
1219 ApiTestFuzzer::Fuzz();
1220 CHECK(info.This()->Equals(bottom));
1221 return v8::Handle<v8::Array>();
1222}
1223
1224
1225v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1226 const AccessorInfo& info) {
1227 ApiTestFuzzer::Fuzz();
1228 CHECK(info.This()->Equals(bottom));
1229 return v8::Handle<v8::Array>();
1230}
1231
1232
1233THREADED_TEST(PropertyHandlerInPrototype) {
1234 v8::HandleScope scope;
1235 LocalContext env;
1236
1237 // Set up a prototype chain with three interceptors.
1238 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1239 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1240 CheckThisIndexedPropertyHandler,
1241 CheckThisIndexedPropertySetter,
1242 CheckThisIndexedPropertyQuery,
1243 CheckThisIndexedPropertyDeleter,
1244 CheckThisIndexedPropertyEnumerator);
1245
1246 templ->InstanceTemplate()->SetNamedPropertyHandler(
1247 CheckThisNamedPropertyHandler,
1248 CheckThisNamedPropertySetter,
1249 CheckThisNamedPropertyQuery,
1250 CheckThisNamedPropertyDeleter,
1251 CheckThisNamedPropertyEnumerator);
1252
1253 bottom = templ->GetFunction()->NewInstance();
1254 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1255 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1256
1257 bottom->Set(v8_str("__proto__"), middle);
1258 middle->Set(v8_str("__proto__"), top);
1259 env->Global()->Set(v8_str("obj"), bottom);
1260
1261 // Indexed and named get.
1262 Script::Compile(v8_str("obj[0]"))->Run();
1263 Script::Compile(v8_str("obj.x"))->Run();
1264
1265 // Indexed and named set.
1266 Script::Compile(v8_str("obj[1] = 42"))->Run();
1267 Script::Compile(v8_str("obj.y = 42"))->Run();
1268
1269 // Indexed and named query.
1270 Script::Compile(v8_str("0 in obj"))->Run();
1271 Script::Compile(v8_str("'x' in obj"))->Run();
1272
1273 // Indexed and named deleter.
1274 Script::Compile(v8_str("delete obj[0]"))->Run();
1275 Script::Compile(v8_str("delete obj.x"))->Run();
1276
1277 // Enumerators.
1278 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1279}
1280
1281
1282static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1283 const AccessorInfo& info) {
1284 ApiTestFuzzer::Fuzz();
1285 if (v8_str("pre")->Equals(key)) {
1286 return v8_str("PrePropertyHandler: pre");
1287 }
1288 return v8::Handle<String>();
1289}
1290
1291
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001292static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1293 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001294 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001295 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001296 }
1297
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001298 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001299}
1300
1301
1302THREADED_TEST(PrePropertyHandler) {
1303 v8::HandleScope scope;
1304 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1305 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1306 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001307 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001308 LocalContext env(NULL, desc->InstanceTemplate());
1309 Script::Compile(v8_str(
1310 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1311 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1312 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1313 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1314 CHECK_EQ(v8_str("Object: on"), result_on);
1315 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1316 CHECK(result_post.IsEmpty());
1317}
1318
1319
1320THREADED_TEST(UndefinedIsNotEnumerable) {
1321 v8::HandleScope scope;
1322 LocalContext env;
1323 v8::Handle<Value> result = Script::Compile(v8_str(
1324 "this.propertyIsEnumerable(undefined)"))->Run();
1325 CHECK(result->IsFalse());
1326}
1327
1328
1329v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001330static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001331
1332
1333static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1334 ApiTestFuzzer::Fuzz();
1335 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1336 if (depth == kTargetRecursionDepth) return v8::Undefined();
1337 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1338 return call_recursively_script->Run();
1339}
1340
1341
1342static v8::Handle<Value> CallFunctionRecursivelyCall(
1343 const v8::Arguments& args) {
1344 ApiTestFuzzer::Fuzz();
1345 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1346 if (depth == kTargetRecursionDepth) {
1347 printf("[depth = %d]\n", depth);
1348 return v8::Undefined();
1349 }
1350 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1351 v8::Handle<Value> function =
1352 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001353 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001354}
1355
1356
1357THREADED_TEST(DeepCrossLanguageRecursion) {
1358 v8::HandleScope scope;
1359 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1360 global->Set(v8_str("callScriptRecursively"),
1361 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1362 global->Set(v8_str("callFunctionRecursively"),
1363 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1364 LocalContext env(NULL, global);
1365
1366 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1367 call_recursively_script = v8_compile("callScriptRecursively()");
1368 v8::Handle<Value> result = call_recursively_script->Run();
1369 call_recursively_script = v8::Handle<Script>();
1370
1371 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1372 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1373}
1374
1375
1376static v8::Handle<Value>
1377 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1378 ApiTestFuzzer::Fuzz();
1379 return v8::ThrowException(key);
1380}
1381
1382
1383static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1384 Local<Value>,
1385 const AccessorInfo&) {
1386 v8::ThrowException(key);
1387 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1388}
1389
1390
1391THREADED_TEST(CallbackExceptionRegression) {
1392 v8::HandleScope scope;
1393 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1394 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1395 ThrowingPropertyHandlerSet);
1396 LocalContext env;
1397 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1398 v8::Handle<Value> otto = Script::Compile(v8_str(
1399 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1400 CHECK_EQ(v8_str("otto"), otto);
1401 v8::Handle<Value> netto = Script::Compile(v8_str(
1402 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1403 CHECK_EQ(v8_str("netto"), netto);
1404}
1405
1406
Steve Blocka7e24c12009-10-30 11:49:00 +00001407THREADED_TEST(FunctionPrototype) {
1408 v8::HandleScope scope;
1409 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1410 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1411 LocalContext env;
1412 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1413 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1414 CHECK_EQ(script->Run()->Int32Value(), 321);
1415}
1416
1417
1418THREADED_TEST(InternalFields) {
1419 v8::HandleScope scope;
1420 LocalContext env;
1421
1422 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1423 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1424 instance_templ->SetInternalFieldCount(1);
1425 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1426 CHECK_EQ(1, obj->InternalFieldCount());
1427 CHECK(obj->GetInternalField(0)->IsUndefined());
1428 obj->SetInternalField(0, v8_num(17));
1429 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1430}
1431
1432
Steve Block6ded16b2010-05-10 14:33:55 +01001433THREADED_TEST(GlobalObjectInternalFields) {
1434 v8::HandleScope scope;
1435 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1436 global_template->SetInternalFieldCount(1);
1437 LocalContext env(NULL, global_template);
1438 v8::Handle<v8::Object> global_proxy = env->Global();
1439 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1440 CHECK_EQ(1, global->InternalFieldCount());
1441 CHECK(global->GetInternalField(0)->IsUndefined());
1442 global->SetInternalField(0, v8_num(17));
1443 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1444}
1445
1446
Steve Blocka7e24c12009-10-30 11:49:00 +00001447THREADED_TEST(InternalFieldsNativePointers) {
1448 v8::HandleScope scope;
1449 LocalContext env;
1450
1451 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1452 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1453 instance_templ->SetInternalFieldCount(1);
1454 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1455 CHECK_EQ(1, obj->InternalFieldCount());
1456 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1457
1458 char* data = new char[100];
1459
1460 void* aligned = data;
1461 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1462 void* unaligned = data + 1;
1463 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1464
1465 // Check reading and writing aligned pointers.
1466 obj->SetPointerInInternalField(0, aligned);
1467 i::Heap::CollectAllGarbage(false);
1468 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1469
1470 // Check reading and writing unaligned pointers.
1471 obj->SetPointerInInternalField(0, unaligned);
1472 i::Heap::CollectAllGarbage(false);
1473 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1474
1475 delete[] data;
1476}
1477
1478
Steve Block3ce2e202009-11-05 08:53:23 +00001479THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1480 v8::HandleScope scope;
1481 LocalContext env;
1482
1483 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1484 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1485 instance_templ->SetInternalFieldCount(1);
1486 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1487 CHECK_EQ(1, obj->InternalFieldCount());
1488 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1489
1490 char* data = new char[100];
1491
1492 void* aligned = data;
1493 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1494 void* unaligned = data + 1;
1495 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1496
1497 obj->SetPointerInInternalField(0, aligned);
1498 i::Heap::CollectAllGarbage(false);
1499 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1500
1501 obj->SetPointerInInternalField(0, unaligned);
1502 i::Heap::CollectAllGarbage(false);
1503 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1504
1505 obj->SetInternalField(0, v8::External::Wrap(aligned));
1506 i::Heap::CollectAllGarbage(false);
1507 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1508
1509 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1510 i::Heap::CollectAllGarbage(false);
1511 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1512
1513 delete[] data;
1514}
1515
1516
Steve Blocka7e24c12009-10-30 11:49:00 +00001517THREADED_TEST(IdentityHash) {
1518 v8::HandleScope scope;
1519 LocalContext env;
1520
1521 // Ensure that the test starts with an fresh heap to test whether the hash
1522 // code is based on the address.
1523 i::Heap::CollectAllGarbage(false);
1524 Local<v8::Object> obj = v8::Object::New();
1525 int hash = obj->GetIdentityHash();
1526 int hash1 = obj->GetIdentityHash();
1527 CHECK_EQ(hash, hash1);
1528 int hash2 = v8::Object::New()->GetIdentityHash();
1529 // Since the identity hash is essentially a random number two consecutive
1530 // objects should not be assigned the same hash code. If the test below fails
1531 // the random number generator should be evaluated.
1532 CHECK_NE(hash, hash2);
1533 i::Heap::CollectAllGarbage(false);
1534 int hash3 = v8::Object::New()->GetIdentityHash();
1535 // Make sure that the identity hash is not based on the initial address of
1536 // the object alone. If the test below fails the random number generator
1537 // should be evaluated.
1538 CHECK_NE(hash, hash3);
1539 int hash4 = obj->GetIdentityHash();
1540 CHECK_EQ(hash, hash4);
1541}
1542
1543
1544THREADED_TEST(HiddenProperties) {
1545 v8::HandleScope scope;
1546 LocalContext env;
1547
1548 v8::Local<v8::Object> obj = v8::Object::New();
1549 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1550 v8::Local<v8::String> empty = v8_str("");
1551 v8::Local<v8::String> prop_name = v8_str("prop_name");
1552
1553 i::Heap::CollectAllGarbage(false);
1554
1555 // Make sure delete of a non-existent hidden value works
1556 CHECK(obj->DeleteHiddenValue(key));
1557
1558 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1559 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1560 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1561 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1562
1563 i::Heap::CollectAllGarbage(false);
1564
1565 // Make sure we do not find the hidden property.
1566 CHECK(!obj->Has(empty));
1567 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1568 CHECK(obj->Get(empty)->IsUndefined());
1569 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1570 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1571 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1572 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1573
1574 i::Heap::CollectAllGarbage(false);
1575
1576 // Add another property and delete it afterwards to force the object in
1577 // slow case.
1578 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1579 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1580 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1581 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1582 CHECK(obj->Delete(prop_name));
1583 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1584
1585 i::Heap::CollectAllGarbage(false);
1586
1587 CHECK(obj->DeleteHiddenValue(key));
1588 CHECK(obj->GetHiddenValue(key).IsEmpty());
1589}
1590
1591
Steve Blockd0582a62009-12-15 09:54:21 +00001592static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001593static v8::Handle<Value> InterceptorForHiddenProperties(
1594 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001595 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001596 return v8::Handle<Value>();
1597}
1598
1599
1600THREADED_TEST(HiddenPropertiesWithInterceptors) {
1601 v8::HandleScope scope;
1602 LocalContext context;
1603
Steve Blockd0582a62009-12-15 09:54:21 +00001604 interceptor_for_hidden_properties_called = false;
1605
Steve Blocka7e24c12009-10-30 11:49:00 +00001606 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1607
1608 // Associate an interceptor with an object and start setting hidden values.
1609 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1610 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1611 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1612 Local<v8::Function> function = fun_templ->GetFunction();
1613 Local<v8::Object> obj = function->NewInstance();
1614 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1615 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001616 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001617}
1618
1619
1620THREADED_TEST(External) {
1621 v8::HandleScope scope;
1622 int x = 3;
1623 Local<v8::External> ext = v8::External::New(&x);
1624 LocalContext env;
1625 env->Global()->Set(v8_str("ext"), ext);
1626 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001627 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001628 int* ptr = static_cast<int*>(reext->Value());
1629 CHECK_EQ(x, 3);
1630 *ptr = 10;
1631 CHECK_EQ(x, 10);
1632
1633 // Make sure unaligned pointers are wrapped properly.
1634 char* data = i::StrDup("0123456789");
1635 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1636 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1637 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1638 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1639
1640 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1641 CHECK_EQ('0', *char_ptr);
1642 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1643 CHECK_EQ('1', *char_ptr);
1644 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1645 CHECK_EQ('2', *char_ptr);
1646 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1647 CHECK_EQ('3', *char_ptr);
1648 i::DeleteArray(data);
1649}
1650
1651
1652THREADED_TEST(GlobalHandle) {
1653 v8::Persistent<String> global;
1654 {
1655 v8::HandleScope scope;
1656 Local<String> str = v8_str("str");
1657 global = v8::Persistent<String>::New(str);
1658 }
1659 CHECK_EQ(global->Length(), 3);
1660 global.Dispose();
1661}
1662
1663
1664THREADED_TEST(ScriptException) {
1665 v8::HandleScope scope;
1666 LocalContext env;
1667 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1668 v8::TryCatch try_catch;
1669 Local<Value> result = script->Run();
1670 CHECK(result.IsEmpty());
1671 CHECK(try_catch.HasCaught());
1672 String::AsciiValue exception_value(try_catch.Exception());
1673 CHECK_EQ(*exception_value, "panama!");
1674}
1675
1676
1677bool message_received;
1678
1679
1680static void check_message(v8::Handle<v8::Message> message,
1681 v8::Handle<Value> data) {
1682 CHECK_EQ(5.76, data->NumberValue());
1683 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1684 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1685 message_received = true;
1686}
1687
1688
1689THREADED_TEST(MessageHandlerData) {
1690 message_received = false;
1691 v8::HandleScope scope;
1692 CHECK(!message_received);
1693 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1694 LocalContext context;
1695 v8::ScriptOrigin origin =
1696 v8::ScriptOrigin(v8_str("6.75"));
1697 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1698 &origin);
1699 script->SetData(v8_str("7.56"));
1700 script->Run();
1701 CHECK(message_received);
1702 // clear out the message listener
1703 v8::V8::RemoveMessageListeners(check_message);
1704}
1705
1706
1707THREADED_TEST(GetSetProperty) {
1708 v8::HandleScope scope;
1709 LocalContext context;
1710 context->Global()->Set(v8_str("foo"), v8_num(14));
1711 context->Global()->Set(v8_str("12"), v8_num(92));
1712 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1713 context->Global()->Set(v8_num(13), v8_num(56));
1714 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1715 CHECK_EQ(14, foo->Int32Value());
1716 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1717 CHECK_EQ(92, twelve->Int32Value());
1718 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1719 CHECK_EQ(32, sixteen->Int32Value());
1720 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1721 CHECK_EQ(56, thirteen->Int32Value());
1722 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1723 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1724 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1725 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1726 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1727 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1728 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1729 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1730 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1731}
1732
1733
1734THREADED_TEST(PropertyAttributes) {
1735 v8::HandleScope scope;
1736 LocalContext context;
1737 // read-only
1738 Local<String> prop = v8_str("read_only");
1739 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1740 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1741 Script::Compile(v8_str("read_only = 9"))->Run();
1742 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1743 context->Global()->Set(prop, v8_num(10));
1744 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1745 // dont-delete
1746 prop = v8_str("dont_delete");
1747 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1748 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1749 Script::Compile(v8_str("delete dont_delete"))->Run();
1750 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1751}
1752
1753
1754THREADED_TEST(Array) {
1755 v8::HandleScope scope;
1756 LocalContext context;
1757 Local<v8::Array> array = v8::Array::New();
1758 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001759 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001760 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001761 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001762 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001763 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001764 CHECK_EQ(3, array->Length());
1765 CHECK(!array->Has(0));
1766 CHECK(!array->Has(1));
1767 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001768 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001769 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001770 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001771 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001772 CHECK_EQ(1, arr->Get(0)->Int32Value());
1773 CHECK_EQ(2, arr->Get(1)->Int32Value());
1774 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001775}
1776
1777
1778v8::Handle<Value> HandleF(const v8::Arguments& args) {
1779 v8::HandleScope scope;
1780 ApiTestFuzzer::Fuzz();
1781 Local<v8::Array> result = v8::Array::New(args.Length());
1782 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001783 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001784 return scope.Close(result);
1785}
1786
1787
1788THREADED_TEST(Vector) {
1789 v8::HandleScope scope;
1790 Local<ObjectTemplate> global = ObjectTemplate::New();
1791 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1792 LocalContext context(0, global);
1793
1794 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001795 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001796 CHECK_EQ(0, a0->Length());
1797
1798 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001799 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001800 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001801 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001802
1803 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001804 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001805 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001806 CHECK_EQ(12, a2->Get(0)->Int32Value());
1807 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001808
1809 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001810 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001811 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001812 CHECK_EQ(14, a3->Get(0)->Int32Value());
1813 CHECK_EQ(15, a3->Get(1)->Int32Value());
1814 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001815
1816 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001817 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001818 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001819 CHECK_EQ(17, a4->Get(0)->Int32Value());
1820 CHECK_EQ(18, a4->Get(1)->Int32Value());
1821 CHECK_EQ(19, a4->Get(2)->Int32Value());
1822 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001823}
1824
1825
1826THREADED_TEST(FunctionCall) {
1827 v8::HandleScope scope;
1828 LocalContext context;
1829 CompileRun(
1830 "function Foo() {"
1831 " var result = [];"
1832 " for (var i = 0; i < arguments.length; i++) {"
1833 " result.push(arguments[i]);"
1834 " }"
1835 " return result;"
1836 "}");
1837 Local<Function> Foo =
1838 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1839
1840 v8::Handle<Value>* args0 = NULL;
1841 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1842 CHECK_EQ(0, a0->Length());
1843
1844 v8::Handle<Value> args1[] = { v8_num(1.1) };
1845 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1846 CHECK_EQ(1, a1->Length());
1847 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1848
1849 v8::Handle<Value> args2[] = { v8_num(2.2),
1850 v8_num(3.3) };
1851 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1852 CHECK_EQ(2, a2->Length());
1853 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1854 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1855
1856 v8::Handle<Value> args3[] = { v8_num(4.4),
1857 v8_num(5.5),
1858 v8_num(6.6) };
1859 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1860 CHECK_EQ(3, a3->Length());
1861 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1862 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1863 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1864
1865 v8::Handle<Value> args4[] = { v8_num(7.7),
1866 v8_num(8.8),
1867 v8_num(9.9),
1868 v8_num(10.11) };
1869 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1870 CHECK_EQ(4, a4->Length());
1871 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1872 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1873 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1874 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1875}
1876
1877
1878static const char* js_code_causing_out_of_memory =
1879 "var a = new Array(); while(true) a.push(a);";
1880
1881
1882// These tests run for a long time and prevent us from running tests
1883// that come after them so they cannot run in parallel.
1884TEST(OutOfMemory) {
1885 // It's not possible to read a snapshot into a heap with different dimensions.
1886 if (v8::internal::Snapshot::IsEnabled()) return;
1887 // Set heap limits.
1888 static const int K = 1024;
1889 v8::ResourceConstraints constraints;
1890 constraints.set_max_young_space_size(256 * K);
1891 constraints.set_max_old_space_size(4 * K * K);
1892 v8::SetResourceConstraints(&constraints);
1893
1894 // Execute a script that causes out of memory.
1895 v8::HandleScope scope;
1896 LocalContext context;
1897 v8::V8::IgnoreOutOfMemoryException();
1898 Local<Script> script =
1899 Script::Compile(String::New(js_code_causing_out_of_memory));
1900 Local<Value> result = script->Run();
1901
1902 // Check for out of memory state.
1903 CHECK(result.IsEmpty());
1904 CHECK(context->HasOutOfMemoryException());
1905}
1906
1907
1908v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1909 ApiTestFuzzer::Fuzz();
1910
1911 v8::HandleScope scope;
1912 LocalContext context;
1913 Local<Script> script =
1914 Script::Compile(String::New(js_code_causing_out_of_memory));
1915 Local<Value> result = script->Run();
1916
1917 // Check for out of memory state.
1918 CHECK(result.IsEmpty());
1919 CHECK(context->HasOutOfMemoryException());
1920
1921 return result;
1922}
1923
1924
1925TEST(OutOfMemoryNested) {
1926 // It's not possible to read a snapshot into a heap with different dimensions.
1927 if (v8::internal::Snapshot::IsEnabled()) return;
1928 // Set heap limits.
1929 static const int K = 1024;
1930 v8::ResourceConstraints constraints;
1931 constraints.set_max_young_space_size(256 * K);
1932 constraints.set_max_old_space_size(4 * K * K);
1933 v8::SetResourceConstraints(&constraints);
1934
1935 v8::HandleScope scope;
1936 Local<ObjectTemplate> templ = ObjectTemplate::New();
1937 templ->Set(v8_str("ProvokeOutOfMemory"),
1938 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1939 LocalContext context(0, templ);
1940 v8::V8::IgnoreOutOfMemoryException();
1941 Local<Value> result = CompileRun(
1942 "var thrown = false;"
1943 "try {"
1944 " ProvokeOutOfMemory();"
1945 "} catch (e) {"
1946 " thrown = true;"
1947 "}");
1948 // Check for out of memory state.
1949 CHECK(result.IsEmpty());
1950 CHECK(context->HasOutOfMemoryException());
1951}
1952
1953
1954TEST(HugeConsStringOutOfMemory) {
1955 // It's not possible to read a snapshot into a heap with different dimensions.
1956 if (v8::internal::Snapshot::IsEnabled()) return;
1957 v8::HandleScope scope;
1958 LocalContext context;
1959 // Set heap limits.
1960 static const int K = 1024;
1961 v8::ResourceConstraints constraints;
1962 constraints.set_max_young_space_size(256 * K);
1963 constraints.set_max_old_space_size(2 * K * K);
1964 v8::SetResourceConstraints(&constraints);
1965
1966 // Execute a script that causes out of memory.
1967 v8::V8::IgnoreOutOfMemoryException();
1968
1969 // Build huge string. This should fail with out of memory exception.
1970 Local<Value> result = CompileRun(
1971 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00001972 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00001973
1974 // Check for out of memory state.
1975 CHECK(result.IsEmpty());
1976 CHECK(context->HasOutOfMemoryException());
1977}
1978
1979
1980THREADED_TEST(ConstructCall) {
1981 v8::HandleScope scope;
1982 LocalContext context;
1983 CompileRun(
1984 "function Foo() {"
1985 " var result = [];"
1986 " for (var i = 0; i < arguments.length; i++) {"
1987 " result.push(arguments[i]);"
1988 " }"
1989 " return result;"
1990 "}");
1991 Local<Function> Foo =
1992 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1993
1994 v8::Handle<Value>* args0 = NULL;
1995 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
1996 CHECK_EQ(0, a0->Length());
1997
1998 v8::Handle<Value> args1[] = { v8_num(1.1) };
1999 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2000 CHECK_EQ(1, a1->Length());
2001 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2002
2003 v8::Handle<Value> args2[] = { v8_num(2.2),
2004 v8_num(3.3) };
2005 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2006 CHECK_EQ(2, a2->Length());
2007 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2008 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2009
2010 v8::Handle<Value> args3[] = { v8_num(4.4),
2011 v8_num(5.5),
2012 v8_num(6.6) };
2013 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2014 CHECK_EQ(3, a3->Length());
2015 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2016 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2017 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2018
2019 v8::Handle<Value> args4[] = { v8_num(7.7),
2020 v8_num(8.8),
2021 v8_num(9.9),
2022 v8_num(10.11) };
2023 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2024 CHECK_EQ(4, a4->Length());
2025 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2026 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2027 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2028 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2029}
2030
2031
2032static void CheckUncle(v8::TryCatch* try_catch) {
2033 CHECK(try_catch->HasCaught());
2034 String::AsciiValue str_value(try_catch->Exception());
2035 CHECK_EQ(*str_value, "uncle?");
2036 try_catch->Reset();
2037}
2038
2039
Steve Block6ded16b2010-05-10 14:33:55 +01002040THREADED_TEST(ConversionNumber) {
2041 v8::HandleScope scope;
2042 LocalContext env;
2043 // Very large number.
2044 CompileRun("var obj = Math.pow(2,32) * 1237;");
2045 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2046 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2047 CHECK_EQ(0, obj->ToInt32()->Value());
2048 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2049 // Large number.
2050 CompileRun("var obj = -1234567890123;");
2051 obj = env->Global()->Get(v8_str("obj"));
2052 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2053 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2054 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2055 // Small positive integer.
2056 CompileRun("var obj = 42;");
2057 obj = env->Global()->Get(v8_str("obj"));
2058 CHECK_EQ(42.0, obj->ToNumber()->Value());
2059 CHECK_EQ(42, obj->ToInt32()->Value());
2060 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2061 // Negative integer.
2062 CompileRun("var obj = -37;");
2063 obj = env->Global()->Get(v8_str("obj"));
2064 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2065 CHECK_EQ(-37, obj->ToInt32()->Value());
2066 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2067 // Positive non-int32 integer.
2068 CompileRun("var obj = 0x81234567;");
2069 obj = env->Global()->Get(v8_str("obj"));
2070 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2071 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2072 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2073 // Fraction.
2074 CompileRun("var obj = 42.3;");
2075 obj = env->Global()->Get(v8_str("obj"));
2076 CHECK_EQ(42.3, obj->ToNumber()->Value());
2077 CHECK_EQ(42, obj->ToInt32()->Value());
2078 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2079 // Large negative fraction.
2080 CompileRun("var obj = -5726623061.75;");
2081 obj = env->Global()->Get(v8_str("obj"));
2082 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2083 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2084 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2085}
2086
2087
2088THREADED_TEST(isNumberType) {
2089 v8::HandleScope scope;
2090 LocalContext env;
2091 // Very large number.
2092 CompileRun("var obj = Math.pow(2,32) * 1237;");
2093 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2094 CHECK(!obj->IsInt32());
2095 CHECK(!obj->IsUint32());
2096 // Large negative number.
2097 CompileRun("var obj = -1234567890123;");
2098 obj = env->Global()->Get(v8_str("obj"));
2099 CHECK(!obj->IsInt32());
2100 CHECK(!obj->IsUint32());
2101 // Small positive integer.
2102 CompileRun("var obj = 42;");
2103 obj = env->Global()->Get(v8_str("obj"));
2104 CHECK(obj->IsInt32());
2105 CHECK(obj->IsUint32());
2106 // Negative integer.
2107 CompileRun("var obj = -37;");
2108 obj = env->Global()->Get(v8_str("obj"));
2109 CHECK(obj->IsInt32());
2110 CHECK(!obj->IsUint32());
2111 // Positive non-int32 integer.
2112 CompileRun("var obj = 0x81234567;");
2113 obj = env->Global()->Get(v8_str("obj"));
2114 CHECK(!obj->IsInt32());
2115 CHECK(obj->IsUint32());
2116 // Fraction.
2117 CompileRun("var obj = 42.3;");
2118 obj = env->Global()->Get(v8_str("obj"));
2119 CHECK(!obj->IsInt32());
2120 CHECK(!obj->IsUint32());
2121 // Large negative fraction.
2122 CompileRun("var obj = -5726623061.75;");
2123 obj = env->Global()->Get(v8_str("obj"));
2124 CHECK(!obj->IsInt32());
2125 CHECK(!obj->IsUint32());
2126}
2127
2128
Steve Blocka7e24c12009-10-30 11:49:00 +00002129THREADED_TEST(ConversionException) {
2130 v8::HandleScope scope;
2131 LocalContext env;
2132 CompileRun(
2133 "function TestClass() { };"
2134 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2135 "var obj = new TestClass();");
2136 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2137
2138 v8::TryCatch try_catch;
2139
2140 Local<Value> to_string_result = obj->ToString();
2141 CHECK(to_string_result.IsEmpty());
2142 CheckUncle(&try_catch);
2143
2144 Local<Value> to_number_result = obj->ToNumber();
2145 CHECK(to_number_result.IsEmpty());
2146 CheckUncle(&try_catch);
2147
2148 Local<Value> to_integer_result = obj->ToInteger();
2149 CHECK(to_integer_result.IsEmpty());
2150 CheckUncle(&try_catch);
2151
2152 Local<Value> to_uint32_result = obj->ToUint32();
2153 CHECK(to_uint32_result.IsEmpty());
2154 CheckUncle(&try_catch);
2155
2156 Local<Value> to_int32_result = obj->ToInt32();
2157 CHECK(to_int32_result.IsEmpty());
2158 CheckUncle(&try_catch);
2159
2160 Local<Value> to_object_result = v8::Undefined()->ToObject();
2161 CHECK(to_object_result.IsEmpty());
2162 CHECK(try_catch.HasCaught());
2163 try_catch.Reset();
2164
2165 int32_t int32_value = obj->Int32Value();
2166 CHECK_EQ(0, int32_value);
2167 CheckUncle(&try_catch);
2168
2169 uint32_t uint32_value = obj->Uint32Value();
2170 CHECK_EQ(0, uint32_value);
2171 CheckUncle(&try_catch);
2172
2173 double number_value = obj->NumberValue();
2174 CHECK_NE(0, IsNaN(number_value));
2175 CheckUncle(&try_catch);
2176
2177 int64_t integer_value = obj->IntegerValue();
2178 CHECK_EQ(0.0, static_cast<double>(integer_value));
2179 CheckUncle(&try_catch);
2180}
2181
2182
2183v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2184 ApiTestFuzzer::Fuzz();
2185 return v8::ThrowException(v8_str("konto"));
2186}
2187
2188
2189v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2190 if (args.Length() < 1) return v8::Boolean::New(false);
2191 v8::HandleScope scope;
2192 v8::TryCatch try_catch;
2193 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2194 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2195 return v8::Boolean::New(try_catch.HasCaught());
2196}
2197
2198
2199THREADED_TEST(APICatch) {
2200 v8::HandleScope scope;
2201 Local<ObjectTemplate> templ = ObjectTemplate::New();
2202 templ->Set(v8_str("ThrowFromC"),
2203 v8::FunctionTemplate::New(ThrowFromC));
2204 LocalContext context(0, templ);
2205 CompileRun(
2206 "var thrown = false;"
2207 "try {"
2208 " ThrowFromC();"
2209 "} catch (e) {"
2210 " thrown = true;"
2211 "}");
2212 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2213 CHECK(thrown->BooleanValue());
2214}
2215
2216
2217THREADED_TEST(APIThrowTryCatch) {
2218 v8::HandleScope scope;
2219 Local<ObjectTemplate> templ = ObjectTemplate::New();
2220 templ->Set(v8_str("ThrowFromC"),
2221 v8::FunctionTemplate::New(ThrowFromC));
2222 LocalContext context(0, templ);
2223 v8::TryCatch try_catch;
2224 CompileRun("ThrowFromC();");
2225 CHECK(try_catch.HasCaught());
2226}
2227
2228
2229// Test that a try-finally block doesn't shadow a try-catch block
2230// when setting up an external handler.
2231//
2232// BUG(271): Some of the exception propagation does not work on the
2233// ARM simulator because the simulator separates the C++ stack and the
2234// JS stack. This test therefore fails on the simulator. The test is
2235// not threaded to allow the threading tests to run on the simulator.
2236TEST(TryCatchInTryFinally) {
2237 v8::HandleScope scope;
2238 Local<ObjectTemplate> templ = ObjectTemplate::New();
2239 templ->Set(v8_str("CCatcher"),
2240 v8::FunctionTemplate::New(CCatcher));
2241 LocalContext context(0, templ);
2242 Local<Value> result = CompileRun("try {"
2243 " try {"
2244 " CCatcher('throw 7;');"
2245 " } finally {"
2246 " }"
2247 "} catch (e) {"
2248 "}");
2249 CHECK(result->IsTrue());
2250}
2251
2252
2253static void receive_message(v8::Handle<v8::Message> message,
2254 v8::Handle<v8::Value> data) {
2255 message->Get();
2256 message_received = true;
2257}
2258
2259
2260TEST(APIThrowMessage) {
2261 message_received = false;
2262 v8::HandleScope scope;
2263 v8::V8::AddMessageListener(receive_message);
2264 Local<ObjectTemplate> templ = ObjectTemplate::New();
2265 templ->Set(v8_str("ThrowFromC"),
2266 v8::FunctionTemplate::New(ThrowFromC));
2267 LocalContext context(0, templ);
2268 CompileRun("ThrowFromC();");
2269 CHECK(message_received);
2270 v8::V8::RemoveMessageListeners(check_message);
2271}
2272
2273
2274TEST(APIThrowMessageAndVerboseTryCatch) {
2275 message_received = false;
2276 v8::HandleScope scope;
2277 v8::V8::AddMessageListener(receive_message);
2278 Local<ObjectTemplate> templ = ObjectTemplate::New();
2279 templ->Set(v8_str("ThrowFromC"),
2280 v8::FunctionTemplate::New(ThrowFromC));
2281 LocalContext context(0, templ);
2282 v8::TryCatch try_catch;
2283 try_catch.SetVerbose(true);
2284 Local<Value> result = CompileRun("ThrowFromC();");
2285 CHECK(try_catch.HasCaught());
2286 CHECK(result.IsEmpty());
2287 CHECK(message_received);
2288 v8::V8::RemoveMessageListeners(check_message);
2289}
2290
2291
2292THREADED_TEST(ExternalScriptException) {
2293 v8::HandleScope scope;
2294 Local<ObjectTemplate> templ = ObjectTemplate::New();
2295 templ->Set(v8_str("ThrowFromC"),
2296 v8::FunctionTemplate::New(ThrowFromC));
2297 LocalContext context(0, templ);
2298
2299 v8::TryCatch try_catch;
2300 Local<Script> script
2301 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2302 Local<Value> result = script->Run();
2303 CHECK(result.IsEmpty());
2304 CHECK(try_catch.HasCaught());
2305 String::AsciiValue exception_value(try_catch.Exception());
2306 CHECK_EQ("konto", *exception_value);
2307}
2308
2309
2310
2311v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2312 ApiTestFuzzer::Fuzz();
2313 CHECK_EQ(4, args.Length());
2314 int count = args[0]->Int32Value();
2315 int cInterval = args[2]->Int32Value();
2316 if (count == 0) {
2317 return v8::ThrowException(v8_str("FromC"));
2318 } else {
2319 Local<v8::Object> global = Context::GetCurrent()->Global();
2320 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2321 v8::Handle<Value> argv[] = { v8_num(count - 1),
2322 args[1],
2323 args[2],
2324 args[3] };
2325 if (count % cInterval == 0) {
2326 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002327 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002328 int expected = args[3]->Int32Value();
2329 if (try_catch.HasCaught()) {
2330 CHECK_EQ(expected, count);
2331 CHECK(result.IsEmpty());
2332 CHECK(!i::Top::has_scheduled_exception());
2333 } else {
2334 CHECK_NE(expected, count);
2335 }
2336 return result;
2337 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002338 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002339 }
2340 }
2341}
2342
2343
2344v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2345 ApiTestFuzzer::Fuzz();
2346 CHECK_EQ(3, args.Length());
2347 bool equality = args[0]->BooleanValue();
2348 int count = args[1]->Int32Value();
2349 int expected = args[2]->Int32Value();
2350 if (equality) {
2351 CHECK_EQ(count, expected);
2352 } else {
2353 CHECK_NE(count, expected);
2354 }
2355 return v8::Undefined();
2356}
2357
2358
2359THREADED_TEST(EvalInTryFinally) {
2360 v8::HandleScope scope;
2361 LocalContext context;
2362 v8::TryCatch try_catch;
2363 CompileRun("(function() {"
2364 " try {"
2365 " eval('asldkf (*&^&*^');"
2366 " } finally {"
2367 " return;"
2368 " }"
2369 "})()");
2370 CHECK(!try_catch.HasCaught());
2371}
2372
2373
2374// This test works by making a stack of alternating JavaScript and C
2375// activations. These activations set up exception handlers with regular
2376// intervals, one interval for C activations and another for JavaScript
2377// activations. When enough activations have been created an exception is
2378// thrown and we check that the right activation catches the exception and that
2379// no other activations do. The right activation is always the topmost one with
2380// a handler, regardless of whether it is in JavaScript or C.
2381//
2382// The notation used to describe a test case looks like this:
2383//
2384// *JS[4] *C[3] @JS[2] C[1] JS[0]
2385//
2386// Each entry is an activation, either JS or C. The index is the count at that
2387// level. Stars identify activations with exception handlers, the @ identifies
2388// the exception handler that should catch the exception.
2389//
2390// BUG(271): Some of the exception propagation does not work on the
2391// ARM simulator because the simulator separates the C++ stack and the
2392// JS stack. This test therefore fails on the simulator. The test is
2393// not threaded to allow the threading tests to run on the simulator.
2394TEST(ExceptionOrder) {
2395 v8::HandleScope scope;
2396 Local<ObjectTemplate> templ = ObjectTemplate::New();
2397 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2398 templ->Set(v8_str("CThrowCountDown"),
2399 v8::FunctionTemplate::New(CThrowCountDown));
2400 LocalContext context(0, templ);
2401 CompileRun(
2402 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2403 " if (count == 0) throw 'FromJS';"
2404 " if (count % jsInterval == 0) {"
2405 " try {"
2406 " var value = CThrowCountDown(count - 1,"
2407 " jsInterval,"
2408 " cInterval,"
2409 " expected);"
2410 " check(false, count, expected);"
2411 " return value;"
2412 " } catch (e) {"
2413 " check(true, count, expected);"
2414 " }"
2415 " } else {"
2416 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2417 " }"
2418 "}");
2419 Local<Function> fun =
2420 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2421
2422 const int argc = 4;
2423 // count jsInterval cInterval expected
2424
2425 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2426 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2427 fun->Call(fun, argc, a0);
2428
2429 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2430 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2431 fun->Call(fun, argc, a1);
2432
2433 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2434 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2435 fun->Call(fun, argc, a2);
2436
2437 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2438 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2439 fun->Call(fun, argc, a3);
2440
2441 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2442 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2443 fun->Call(fun, argc, a4);
2444
2445 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2446 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2447 fun->Call(fun, argc, a5);
2448}
2449
2450
2451v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2452 ApiTestFuzzer::Fuzz();
2453 CHECK_EQ(1, args.Length());
2454 return v8::ThrowException(args[0]);
2455}
2456
2457
2458THREADED_TEST(ThrowValues) {
2459 v8::HandleScope scope;
2460 Local<ObjectTemplate> templ = ObjectTemplate::New();
2461 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2462 LocalContext context(0, templ);
2463 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2464 "function Run(obj) {"
2465 " try {"
2466 " Throw(obj);"
2467 " } catch (e) {"
2468 " return e;"
2469 " }"
2470 " return 'no exception';"
2471 "}"
2472 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2473 CHECK_EQ(5, result->Length());
2474 CHECK(result->Get(v8::Integer::New(0))->IsString());
2475 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2476 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2477 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2478 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2479 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2480 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2481}
2482
2483
2484THREADED_TEST(CatchZero) {
2485 v8::HandleScope scope;
2486 LocalContext context;
2487 v8::TryCatch try_catch;
2488 CHECK(!try_catch.HasCaught());
2489 Script::Compile(v8_str("throw 10"))->Run();
2490 CHECK(try_catch.HasCaught());
2491 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2492 try_catch.Reset();
2493 CHECK(!try_catch.HasCaught());
2494 Script::Compile(v8_str("throw 0"))->Run();
2495 CHECK(try_catch.HasCaught());
2496 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2497}
2498
2499
2500THREADED_TEST(CatchExceptionFromWith) {
2501 v8::HandleScope scope;
2502 LocalContext context;
2503 v8::TryCatch try_catch;
2504 CHECK(!try_catch.HasCaught());
2505 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2506 CHECK(try_catch.HasCaught());
2507}
2508
2509
2510THREADED_TEST(Equality) {
2511 v8::HandleScope scope;
2512 LocalContext context;
2513 // Check that equality works at all before relying on CHECK_EQ
2514 CHECK(v8_str("a")->Equals(v8_str("a")));
2515 CHECK(!v8_str("a")->Equals(v8_str("b")));
2516
2517 CHECK_EQ(v8_str("a"), v8_str("a"));
2518 CHECK_NE(v8_str("a"), v8_str("b"));
2519 CHECK_EQ(v8_num(1), v8_num(1));
2520 CHECK_EQ(v8_num(1.00), v8_num(1));
2521 CHECK_NE(v8_num(1), v8_num(2));
2522
2523 // Assume String is not symbol.
2524 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2525 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2526 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2527 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2528 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2529 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2530 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2531 CHECK(!not_a_number->StrictEquals(not_a_number));
2532 CHECK(v8::False()->StrictEquals(v8::False()));
2533 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2534
2535 v8::Handle<v8::Object> obj = v8::Object::New();
2536 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2537 CHECK(alias->StrictEquals(obj));
2538 alias.Dispose();
2539}
2540
2541
2542THREADED_TEST(MultiRun) {
2543 v8::HandleScope scope;
2544 LocalContext context;
2545 Local<Script> script = Script::Compile(v8_str("x"));
2546 for (int i = 0; i < 10; i++)
2547 script->Run();
2548}
2549
2550
2551static v8::Handle<Value> GetXValue(Local<String> name,
2552 const AccessorInfo& info) {
2553 ApiTestFuzzer::Fuzz();
2554 CHECK_EQ(info.Data(), v8_str("donut"));
2555 CHECK_EQ(name, v8_str("x"));
2556 return name;
2557}
2558
2559
2560THREADED_TEST(SimplePropertyRead) {
2561 v8::HandleScope scope;
2562 Local<ObjectTemplate> templ = ObjectTemplate::New();
2563 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2564 LocalContext context;
2565 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2566 Local<Script> script = Script::Compile(v8_str("obj.x"));
2567 for (int i = 0; i < 10; i++) {
2568 Local<Value> result = script->Run();
2569 CHECK_EQ(result, v8_str("x"));
2570 }
2571}
2572
Andrei Popescu31002712010-02-23 13:46:05 +00002573THREADED_TEST(DefinePropertyOnAPIAccessor) {
2574 v8::HandleScope scope;
2575 Local<ObjectTemplate> templ = ObjectTemplate::New();
2576 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2577 LocalContext context;
2578 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2579
2580 // Uses getOwnPropertyDescriptor to check the configurable status
2581 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002582 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002583 "obj, 'x');"
2584 "prop.configurable;"));
2585 Local<Value> result = script_desc->Run();
2586 CHECK_EQ(result->BooleanValue(), true);
2587
2588 // Redefine get - but still configurable
2589 Local<Script> script_define
2590 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2591 " configurable: true };"
2592 "Object.defineProperty(obj, 'x', desc);"
2593 "obj.x"));
2594 result = script_define->Run();
2595 CHECK_EQ(result, v8_num(42));
2596
2597 // Check that the accessor is still configurable
2598 result = script_desc->Run();
2599 CHECK_EQ(result->BooleanValue(), true);
2600
2601 // Redefine to a non-configurable
2602 script_define
2603 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2604 " configurable: false };"
2605 "Object.defineProperty(obj, 'x', desc);"
2606 "obj.x"));
2607 result = script_define->Run();
2608 CHECK_EQ(result, v8_num(43));
2609 result = script_desc->Run();
2610 CHECK_EQ(result->BooleanValue(), false);
2611
2612 // Make sure that it is not possible to redefine again
2613 v8::TryCatch try_catch;
2614 result = script_define->Run();
2615 CHECK(try_catch.HasCaught());
2616 String::AsciiValue exception_value(try_catch.Exception());
2617 CHECK_EQ(*exception_value,
2618 "TypeError: Cannot redefine property: defineProperty");
2619}
2620
2621THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2622 v8::HandleScope scope;
2623 Local<ObjectTemplate> templ = ObjectTemplate::New();
2624 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2625 LocalContext context;
2626 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2627
2628 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2629 "Object.getOwnPropertyDescriptor( "
2630 "obj, 'x');"
2631 "prop.configurable;"));
2632 Local<Value> result = script_desc->Run();
2633 CHECK_EQ(result->BooleanValue(), true);
2634
2635 Local<Script> script_define =
2636 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2637 " configurable: true };"
2638 "Object.defineProperty(obj, 'x', desc);"
2639 "obj.x"));
2640 result = script_define->Run();
2641 CHECK_EQ(result, v8_num(42));
2642
2643
2644 result = script_desc->Run();
2645 CHECK_EQ(result->BooleanValue(), true);
2646
2647
2648 script_define =
2649 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2650 " configurable: false };"
2651 "Object.defineProperty(obj, 'x', desc);"
2652 "obj.x"));
2653 result = script_define->Run();
2654 CHECK_EQ(result, v8_num(43));
2655 result = script_desc->Run();
2656
2657 CHECK_EQ(result->BooleanValue(), false);
2658
2659 v8::TryCatch try_catch;
2660 result = script_define->Run();
2661 CHECK(try_catch.HasCaught());
2662 String::AsciiValue exception_value(try_catch.Exception());
2663 CHECK_EQ(*exception_value,
2664 "TypeError: Cannot redefine property: defineProperty");
2665}
2666
2667
Leon Clarkef7060e22010-06-03 12:02:55 +01002668static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2669 char const* name) {
2670 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2671}
Andrei Popescu31002712010-02-23 13:46:05 +00002672
2673
Leon Clarkef7060e22010-06-03 12:02:55 +01002674THREADED_TEST(DefineAPIAccessorOnObject) {
2675 v8::HandleScope scope;
2676 Local<ObjectTemplate> templ = ObjectTemplate::New();
2677 LocalContext context;
2678
2679 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2680 CompileRun("var obj2 = {};");
2681
2682 CHECK(CompileRun("obj1.x")->IsUndefined());
2683 CHECK(CompileRun("obj2.x")->IsUndefined());
2684
2685 CHECK(GetGlobalProperty(&context, "obj1")->
2686 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2687
2688 ExpectString("obj1.x", "x");
2689 CHECK(CompileRun("obj2.x")->IsUndefined());
2690
2691 CHECK(GetGlobalProperty(&context, "obj2")->
2692 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2693
2694 ExpectString("obj1.x", "x");
2695 ExpectString("obj2.x", "x");
2696
2697 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2698 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2699
2700 CompileRun("Object.defineProperty(obj1, 'x',"
2701 "{ get: function() { return 'y'; }, configurable: true })");
2702
2703 ExpectString("obj1.x", "y");
2704 ExpectString("obj2.x", "x");
2705
2706 CompileRun("Object.defineProperty(obj2, 'x',"
2707 "{ get: function() { return 'y'; }, configurable: true })");
2708
2709 ExpectString("obj1.x", "y");
2710 ExpectString("obj2.x", "y");
2711
2712 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2713 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2714
2715 CHECK(GetGlobalProperty(&context, "obj1")->
2716 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2717 CHECK(GetGlobalProperty(&context, "obj2")->
2718 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2719
2720 ExpectString("obj1.x", "x");
2721 ExpectString("obj2.x", "x");
2722
2723 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2724 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2725
2726 // Define getters/setters, but now make them not configurable.
2727 CompileRun("Object.defineProperty(obj1, 'x',"
2728 "{ get: function() { return 'z'; }, configurable: false })");
2729 CompileRun("Object.defineProperty(obj2, 'x',"
2730 "{ get: function() { return 'z'; }, configurable: false })");
2731
2732 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2733 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2734
2735 ExpectString("obj1.x", "z");
2736 ExpectString("obj2.x", "z");
2737
2738 CHECK(!GetGlobalProperty(&context, "obj1")->
2739 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2740 CHECK(!GetGlobalProperty(&context, "obj2")->
2741 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2742
2743 ExpectString("obj1.x", "z");
2744 ExpectString("obj2.x", "z");
2745}
2746
2747
2748THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2749 v8::HandleScope scope;
2750 Local<ObjectTemplate> templ = ObjectTemplate::New();
2751 LocalContext context;
2752
2753 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2754 CompileRun("var obj2 = {};");
2755
2756 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2757 v8_str("x"),
2758 GetXValue, NULL,
2759 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2760 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2761 v8_str("x"),
2762 GetXValue, NULL,
2763 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2764
2765 ExpectString("obj1.x", "x");
2766 ExpectString("obj2.x", "x");
2767
2768 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2769 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2770
2771 CHECK(!GetGlobalProperty(&context, "obj1")->
2772 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2773 CHECK(!GetGlobalProperty(&context, "obj2")->
2774 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2775
2776 {
2777 v8::TryCatch try_catch;
2778 CompileRun("Object.defineProperty(obj1, 'x',"
2779 "{get: function() { return 'func'; }})");
2780 CHECK(try_catch.HasCaught());
2781 String::AsciiValue exception_value(try_catch.Exception());
2782 CHECK_EQ(*exception_value,
2783 "TypeError: Cannot redefine property: defineProperty");
2784 }
2785 {
2786 v8::TryCatch try_catch;
2787 CompileRun("Object.defineProperty(obj2, 'x',"
2788 "{get: function() { return 'func'; }})");
2789 CHECK(try_catch.HasCaught());
2790 String::AsciiValue exception_value(try_catch.Exception());
2791 CHECK_EQ(*exception_value,
2792 "TypeError: Cannot redefine property: defineProperty");
2793 }
2794}
2795
2796
2797static v8::Handle<Value> Get239Value(Local<String> name,
2798 const AccessorInfo& info) {
2799 ApiTestFuzzer::Fuzz();
2800 CHECK_EQ(info.Data(), v8_str("donut"));
2801 CHECK_EQ(name, v8_str("239"));
2802 return name;
2803}
2804
2805
2806THREADED_TEST(ElementAPIAccessor) {
2807 v8::HandleScope scope;
2808 Local<ObjectTemplate> templ = ObjectTemplate::New();
2809 LocalContext context;
2810
2811 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2812 CompileRun("var obj2 = {};");
2813
2814 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2815 v8_str("239"),
2816 Get239Value, NULL,
2817 v8_str("donut")));
2818 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2819 v8_str("239"),
2820 Get239Value, NULL,
2821 v8_str("donut")));
2822
2823 ExpectString("obj1[239]", "239");
2824 ExpectString("obj2[239]", "239");
2825 ExpectString("obj1['239']", "239");
2826 ExpectString("obj2['239']", "239");
2827}
2828
Steve Blocka7e24c12009-10-30 11:49:00 +00002829
2830v8::Persistent<Value> xValue;
2831
2832
2833static void SetXValue(Local<String> name,
2834 Local<Value> value,
2835 const AccessorInfo& info) {
2836 CHECK_EQ(value, v8_num(4));
2837 CHECK_EQ(info.Data(), v8_str("donut"));
2838 CHECK_EQ(name, v8_str("x"));
2839 CHECK(xValue.IsEmpty());
2840 xValue = v8::Persistent<Value>::New(value);
2841}
2842
2843
2844THREADED_TEST(SimplePropertyWrite) {
2845 v8::HandleScope scope;
2846 Local<ObjectTemplate> templ = ObjectTemplate::New();
2847 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2848 LocalContext context;
2849 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2850 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2851 for (int i = 0; i < 10; i++) {
2852 CHECK(xValue.IsEmpty());
2853 script->Run();
2854 CHECK_EQ(v8_num(4), xValue);
2855 xValue.Dispose();
2856 xValue = v8::Persistent<Value>();
2857 }
2858}
2859
2860
2861static v8::Handle<Value> XPropertyGetter(Local<String> property,
2862 const AccessorInfo& info) {
2863 ApiTestFuzzer::Fuzz();
2864 CHECK(info.Data()->IsUndefined());
2865 return property;
2866}
2867
2868
2869THREADED_TEST(NamedInterceptorPropertyRead) {
2870 v8::HandleScope scope;
2871 Local<ObjectTemplate> templ = ObjectTemplate::New();
2872 templ->SetNamedPropertyHandler(XPropertyGetter);
2873 LocalContext context;
2874 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2875 Local<Script> script = Script::Compile(v8_str("obj.x"));
2876 for (int i = 0; i < 10; i++) {
2877 Local<Value> result = script->Run();
2878 CHECK_EQ(result, v8_str("x"));
2879 }
2880}
2881
2882
Steve Block6ded16b2010-05-10 14:33:55 +01002883THREADED_TEST(NamedInterceptorDictionaryIC) {
2884 v8::HandleScope scope;
2885 Local<ObjectTemplate> templ = ObjectTemplate::New();
2886 templ->SetNamedPropertyHandler(XPropertyGetter);
2887 LocalContext context;
2888 // Create an object with a named interceptor.
2889 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2890 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2891 for (int i = 0; i < 10; i++) {
2892 Local<Value> result = script->Run();
2893 CHECK_EQ(result, v8_str("x"));
2894 }
2895 // Create a slow case object and a function accessing a property in
2896 // that slow case object (with dictionary probing in generated
2897 // code). Then force object with a named interceptor into slow-case,
2898 // pass it to the function, and check that the interceptor is called
2899 // instead of accessing the local property.
2900 Local<Value> result =
2901 CompileRun("function get_x(o) { return o.x; };"
2902 "var obj = { x : 42, y : 0 };"
2903 "delete obj.y;"
2904 "for (var i = 0; i < 10; i++) get_x(obj);"
2905 "interceptor_obj.x = 42;"
2906 "interceptor_obj.y = 10;"
2907 "delete interceptor_obj.y;"
2908 "get_x(interceptor_obj)");
2909 CHECK_EQ(result, v8_str("x"));
2910}
2911
2912
Andrei Popescu402d9372010-02-26 13:31:12 +00002913static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2914 const AccessorInfo& info) {
2915 // Set x on the prototype object and do not handle the get request.
2916 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002917 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002918 return v8::Handle<Value>();
2919}
2920
2921
2922// This is a regression test for http://crbug.com/20104. Map
2923// transitions should not interfere with post interceptor lookup.
2924THREADED_TEST(NamedInterceptorMapTransitionRead) {
2925 v8::HandleScope scope;
2926 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
2927 Local<v8::ObjectTemplate> instance_template
2928 = function_template->InstanceTemplate();
2929 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
2930 LocalContext context;
2931 context->Global()->Set(v8_str("F"), function_template->GetFunction());
2932 // Create an instance of F and introduce a map transition for x.
2933 CompileRun("var o = new F(); o.x = 23;");
2934 // Create an instance of F and invoke the getter. The result should be 23.
2935 Local<Value> result = CompileRun("o = new F(); o.x");
2936 CHECK_EQ(result->Int32Value(), 23);
2937}
2938
2939
Steve Blocka7e24c12009-10-30 11:49:00 +00002940static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2941 const AccessorInfo& info) {
2942 ApiTestFuzzer::Fuzz();
2943 if (index == 37) {
2944 return v8::Handle<Value>(v8_num(625));
2945 }
2946 return v8::Handle<Value>();
2947}
2948
2949
2950static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2951 Local<Value> value,
2952 const AccessorInfo& info) {
2953 ApiTestFuzzer::Fuzz();
2954 if (index == 39) {
2955 return value;
2956 }
2957 return v8::Handle<Value>();
2958}
2959
2960
2961THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2962 v8::HandleScope scope;
2963 Local<ObjectTemplate> templ = ObjectTemplate::New();
2964 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2965 IndexedPropertySetter);
2966 LocalContext context;
2967 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2968 Local<Script> getter_script = Script::Compile(v8_str(
2969 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2970 Local<Script> setter_script = Script::Compile(v8_str(
2971 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2972 "obj[17] = 23;"
2973 "obj.foo;"));
2974 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
2975 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
2976 "obj[39] = 47;"
2977 "obj.foo;")); // This setter should not run, due to the interceptor.
2978 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
2979 "obj[37];"));
2980 Local<Value> result = getter_script->Run();
2981 CHECK_EQ(v8_num(5), result);
2982 result = setter_script->Run();
2983 CHECK_EQ(v8_num(23), result);
2984 result = interceptor_setter_script->Run();
2985 CHECK_EQ(v8_num(23), result);
2986 result = interceptor_getter_script->Run();
2987 CHECK_EQ(v8_num(625), result);
2988}
2989
2990
Leon Clarked91b9f72010-01-27 17:25:45 +00002991static v8::Handle<Value> IdentityIndexedPropertyGetter(
2992 uint32_t index,
2993 const AccessorInfo& info) {
2994 return v8::Integer::New(index);
2995}
2996
2997
2998THREADED_TEST(IndexedInterceptorWithNoSetter) {
2999 v8::HandleScope scope;
3000 Local<ObjectTemplate> templ = ObjectTemplate::New();
3001 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3002
3003 LocalContext context;
3004 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3005
3006 const char* code =
3007 "try {"
3008 " obj[0] = 239;"
3009 " for (var i = 0; i < 100; i++) {"
3010 " var v = obj[0];"
3011 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3012 " }"
3013 " 'PASSED'"
3014 "} catch(e) {"
3015 " e"
3016 "}";
3017 ExpectString(code, "PASSED");
3018}
3019
3020
Andrei Popescu402d9372010-02-26 13:31:12 +00003021THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3022 v8::HandleScope scope;
3023 Local<ObjectTemplate> templ = ObjectTemplate::New();
3024 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3025
3026 LocalContext context;
3027 Local<v8::Object> obj = templ->NewInstance();
3028 obj->TurnOnAccessCheck();
3029 context->Global()->Set(v8_str("obj"), obj);
3030
3031 const char* code =
3032 "try {"
3033 " for (var i = 0; i < 100; i++) {"
3034 " var v = obj[0];"
3035 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3036 " }"
3037 " 'PASSED'"
3038 "} catch(e) {"
3039 " e"
3040 "}";
3041 ExpectString(code, "PASSED");
3042}
3043
3044
3045THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3046 i::FLAG_allow_natives_syntax = true;
3047 v8::HandleScope scope;
3048 Local<ObjectTemplate> templ = ObjectTemplate::New();
3049 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3050
3051 LocalContext context;
3052 Local<v8::Object> obj = templ->NewInstance();
3053 context->Global()->Set(v8_str("obj"), obj);
3054
3055 const char* code =
3056 "try {"
3057 " for (var i = 0; i < 100; i++) {"
3058 " var expected = i;"
3059 " if (i == 5) {"
3060 " %EnableAccessChecks(obj);"
3061 " expected = undefined;"
3062 " }"
3063 " var v = obj[i];"
3064 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3065 " if (i == 5) %DisableAccessChecks(obj);"
3066 " }"
3067 " 'PASSED'"
3068 "} catch(e) {"
3069 " e"
3070 "}";
3071 ExpectString(code, "PASSED");
3072}
3073
3074
3075THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3076 v8::HandleScope scope;
3077 Local<ObjectTemplate> templ = ObjectTemplate::New();
3078 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3079
3080 LocalContext context;
3081 Local<v8::Object> obj = templ->NewInstance();
3082 context->Global()->Set(v8_str("obj"), obj);
3083
3084 const char* code =
3085 "try {"
3086 " for (var i = 0; i < 100; i++) {"
3087 " var v = obj[i];"
3088 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3089 " }"
3090 " 'PASSED'"
3091 "} catch(e) {"
3092 " e"
3093 "}";
3094 ExpectString(code, "PASSED");
3095}
3096
3097
3098THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3099 v8::HandleScope scope;
3100 Local<ObjectTemplate> templ = ObjectTemplate::New();
3101 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3102
3103 LocalContext context;
3104 Local<v8::Object> obj = templ->NewInstance();
3105 context->Global()->Set(v8_str("obj"), obj);
3106
3107 const char* code =
3108 "try {"
3109 " for (var i = 0; i < 100; i++) {"
3110 " var expected = i;"
3111 " if (i == 50) {"
3112 " i = 'foobar';"
3113 " expected = undefined;"
3114 " }"
3115 " var v = obj[i];"
3116 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3117 " }"
3118 " 'PASSED'"
3119 "} catch(e) {"
3120 " e"
3121 "}";
3122 ExpectString(code, "PASSED");
3123}
3124
3125
3126THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3127 v8::HandleScope scope;
3128 Local<ObjectTemplate> templ = ObjectTemplate::New();
3129 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3130
3131 LocalContext context;
3132 Local<v8::Object> obj = templ->NewInstance();
3133 context->Global()->Set(v8_str("obj"), obj);
3134
3135 const char* code =
3136 "var original = obj;"
3137 "try {"
3138 " for (var i = 0; i < 100; i++) {"
3139 " var expected = i;"
3140 " if (i == 50) {"
3141 " obj = {50: 'foobar'};"
3142 " expected = 'foobar';"
3143 " }"
3144 " var v = obj[i];"
3145 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3146 " if (i == 50) obj = original;"
3147 " }"
3148 " 'PASSED'"
3149 "} catch(e) {"
3150 " e"
3151 "}";
3152 ExpectString(code, "PASSED");
3153}
3154
3155
3156THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3157 v8::HandleScope scope;
3158 Local<ObjectTemplate> templ = ObjectTemplate::New();
3159 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3160
3161 LocalContext context;
3162 Local<v8::Object> obj = templ->NewInstance();
3163 context->Global()->Set(v8_str("obj"), obj);
3164
3165 const char* code =
3166 "var original = obj;"
3167 "try {"
3168 " for (var i = 0; i < 100; i++) {"
3169 " var expected = i;"
3170 " if (i == 5) {"
3171 " obj = 239;"
3172 " expected = undefined;"
3173 " }"
3174 " var v = obj[i];"
3175 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3176 " if (i == 5) obj = original;"
3177 " }"
3178 " 'PASSED'"
3179 "} catch(e) {"
3180 " e"
3181 "}";
3182 ExpectString(code, "PASSED");
3183}
3184
3185
3186THREADED_TEST(IndexedInterceptorOnProto) {
3187 v8::HandleScope scope;
3188 Local<ObjectTemplate> templ = ObjectTemplate::New();
3189 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3190
3191 LocalContext context;
3192 Local<v8::Object> obj = templ->NewInstance();
3193 context->Global()->Set(v8_str("obj"), obj);
3194
3195 const char* code =
3196 "var o = {__proto__: obj};"
3197 "try {"
3198 " for (var i = 0; i < 100; i++) {"
3199 " var v = o[i];"
3200 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3201 " }"
3202 " 'PASSED'"
3203 "} catch(e) {"
3204 " e"
3205 "}";
3206 ExpectString(code, "PASSED");
3207}
3208
3209
Steve Blocka7e24c12009-10-30 11:49:00 +00003210THREADED_TEST(MultiContexts) {
3211 v8::HandleScope scope;
3212 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3213 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3214
3215 Local<String> password = v8_str("Password");
3216
3217 // Create an environment
3218 LocalContext context0(0, templ);
3219 context0->SetSecurityToken(password);
3220 v8::Handle<v8::Object> global0 = context0->Global();
3221 global0->Set(v8_str("custom"), v8_num(1234));
3222 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3223
3224 // Create an independent environment
3225 LocalContext context1(0, templ);
3226 context1->SetSecurityToken(password);
3227 v8::Handle<v8::Object> global1 = context1->Global();
3228 global1->Set(v8_str("custom"), v8_num(1234));
3229 CHECK_NE(global0, global1);
3230 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3231 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3232
3233 // Now create a new context with the old global
3234 LocalContext context2(0, templ, global1);
3235 context2->SetSecurityToken(password);
3236 v8::Handle<v8::Object> global2 = context2->Global();
3237 CHECK_EQ(global1, global2);
3238 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3239 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3240}
3241
3242
3243THREADED_TEST(FunctionPrototypeAcrossContexts) {
3244 // Make sure that functions created by cloning boilerplates cannot
3245 // communicate through their __proto__ field.
3246
3247 v8::HandleScope scope;
3248
3249 LocalContext env0;
3250 v8::Handle<v8::Object> global0 =
3251 env0->Global();
3252 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003253 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003254 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003255 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003256 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003257 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003258 proto0->Set(v8_str("custom"), v8_num(1234));
3259
3260 LocalContext env1;
3261 v8::Handle<v8::Object> global1 =
3262 env1->Global();
3263 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003264 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003265 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003266 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003267 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003268 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003269 CHECK(!proto1->Has(v8_str("custom")));
3270}
3271
3272
3273THREADED_TEST(Regress892105) {
3274 // Make sure that object and array literals created by cloning
3275 // boilerplates cannot communicate through their __proto__
3276 // field. This is rather difficult to check, but we try to add stuff
3277 // to Object.prototype and Array.prototype and create a new
3278 // environment. This should succeed.
3279
3280 v8::HandleScope scope;
3281
3282 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3283 "Array.prototype.arr = 4567;"
3284 "8901");
3285
3286 LocalContext env0;
3287 Local<Script> script0 = Script::Compile(source);
3288 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3289
3290 LocalContext env1;
3291 Local<Script> script1 = Script::Compile(source);
3292 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3293}
3294
3295
Steve Blocka7e24c12009-10-30 11:49:00 +00003296THREADED_TEST(UndetectableObject) {
3297 v8::HandleScope scope;
3298 LocalContext env;
3299
3300 Local<v8::FunctionTemplate> desc =
3301 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3302 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3303
3304 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3305 env->Global()->Set(v8_str("undetectable"), obj);
3306
3307 ExpectString("undetectable.toString()", "[object Object]");
3308 ExpectString("typeof undetectable", "undefined");
3309 ExpectString("typeof(undetectable)", "undefined");
3310 ExpectBoolean("typeof undetectable == 'undefined'", true);
3311 ExpectBoolean("typeof undetectable == 'object'", false);
3312 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3313 ExpectBoolean("!undetectable", true);
3314
3315 ExpectObject("true&&undetectable", obj);
3316 ExpectBoolean("false&&undetectable", false);
3317 ExpectBoolean("true||undetectable", true);
3318 ExpectObject("false||undetectable", obj);
3319
3320 ExpectObject("undetectable&&true", obj);
3321 ExpectObject("undetectable&&false", obj);
3322 ExpectBoolean("undetectable||true", true);
3323 ExpectBoolean("undetectable||false", false);
3324
3325 ExpectBoolean("undetectable==null", true);
3326 ExpectBoolean("null==undetectable", true);
3327 ExpectBoolean("undetectable==undefined", true);
3328 ExpectBoolean("undefined==undetectable", true);
3329 ExpectBoolean("undetectable==undetectable", true);
3330
3331
3332 ExpectBoolean("undetectable===null", false);
3333 ExpectBoolean("null===undetectable", false);
3334 ExpectBoolean("undetectable===undefined", false);
3335 ExpectBoolean("undefined===undetectable", false);
3336 ExpectBoolean("undetectable===undetectable", true);
3337}
3338
3339
3340THREADED_TEST(UndetectableString) {
3341 v8::HandleScope scope;
3342 LocalContext env;
3343
3344 Local<String> obj = String::NewUndetectable("foo");
3345 env->Global()->Set(v8_str("undetectable"), obj);
3346
3347 ExpectString("undetectable", "foo");
3348 ExpectString("typeof undetectable", "undefined");
3349 ExpectString("typeof(undetectable)", "undefined");
3350 ExpectBoolean("typeof undetectable == 'undefined'", true);
3351 ExpectBoolean("typeof undetectable == 'string'", false);
3352 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3353 ExpectBoolean("!undetectable", true);
3354
3355 ExpectObject("true&&undetectable", obj);
3356 ExpectBoolean("false&&undetectable", false);
3357 ExpectBoolean("true||undetectable", true);
3358 ExpectObject("false||undetectable", obj);
3359
3360 ExpectObject("undetectable&&true", obj);
3361 ExpectObject("undetectable&&false", obj);
3362 ExpectBoolean("undetectable||true", true);
3363 ExpectBoolean("undetectable||false", false);
3364
3365 ExpectBoolean("undetectable==null", true);
3366 ExpectBoolean("null==undetectable", true);
3367 ExpectBoolean("undetectable==undefined", true);
3368 ExpectBoolean("undefined==undetectable", true);
3369 ExpectBoolean("undetectable==undetectable", true);
3370
3371
3372 ExpectBoolean("undetectable===null", false);
3373 ExpectBoolean("null===undetectable", false);
3374 ExpectBoolean("undetectable===undefined", false);
3375 ExpectBoolean("undefined===undetectable", false);
3376 ExpectBoolean("undetectable===undetectable", true);
3377}
3378
3379
3380template <typename T> static void USE(T) { }
3381
3382
3383// This test is not intended to be run, just type checked.
3384static void PersistentHandles() {
3385 USE(PersistentHandles);
3386 Local<String> str = v8_str("foo");
3387 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3388 USE(p_str);
3389 Local<Script> scr = Script::Compile(v8_str(""));
3390 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3391 USE(p_scr);
3392 Local<ObjectTemplate> templ = ObjectTemplate::New();
3393 v8::Persistent<ObjectTemplate> p_templ =
3394 v8::Persistent<ObjectTemplate>::New(templ);
3395 USE(p_templ);
3396}
3397
3398
3399static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3400 ApiTestFuzzer::Fuzz();
3401 return v8::Undefined();
3402}
3403
3404
3405THREADED_TEST(GlobalObjectTemplate) {
3406 v8::HandleScope handle_scope;
3407 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3408 global_template->Set(v8_str("JSNI_Log"),
3409 v8::FunctionTemplate::New(HandleLogDelegator));
3410 v8::Persistent<Context> context = Context::New(0, global_template);
3411 Context::Scope context_scope(context);
3412 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3413 context.Dispose();
3414}
3415
3416
3417static const char* kSimpleExtensionSource =
3418 "function Foo() {"
3419 " return 4;"
3420 "}";
3421
3422
3423THREADED_TEST(SimpleExtensions) {
3424 v8::HandleScope handle_scope;
3425 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3426 const char* extension_names[] = { "simpletest" };
3427 v8::ExtensionConfiguration extensions(1, extension_names);
3428 v8::Handle<Context> context = Context::New(&extensions);
3429 Context::Scope lock(context);
3430 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3431 CHECK_EQ(result, v8::Integer::New(4));
3432}
3433
3434
3435static const char* kEvalExtensionSource1 =
3436 "function UseEval1() {"
3437 " var x = 42;"
3438 " return eval('x');"
3439 "}";
3440
3441
3442static const char* kEvalExtensionSource2 =
3443 "(function() {"
3444 " var x = 42;"
3445 " function e() {"
3446 " return eval('x');"
3447 " }"
3448 " this.UseEval2 = e;"
3449 "})()";
3450
3451
3452THREADED_TEST(UseEvalFromExtension) {
3453 v8::HandleScope handle_scope;
3454 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3455 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3456 const char* extension_names[] = { "evaltest1", "evaltest2" };
3457 v8::ExtensionConfiguration extensions(2, extension_names);
3458 v8::Handle<Context> context = Context::New(&extensions);
3459 Context::Scope lock(context);
3460 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3461 CHECK_EQ(result, v8::Integer::New(42));
3462 result = Script::Compile(v8_str("UseEval2()"))->Run();
3463 CHECK_EQ(result, v8::Integer::New(42));
3464}
3465
3466
3467static const char* kWithExtensionSource1 =
3468 "function UseWith1() {"
3469 " var x = 42;"
3470 " with({x:87}) { return x; }"
3471 "}";
3472
3473
3474
3475static const char* kWithExtensionSource2 =
3476 "(function() {"
3477 " var x = 42;"
3478 " function e() {"
3479 " with ({x:87}) { return x; }"
3480 " }"
3481 " this.UseWith2 = e;"
3482 "})()";
3483
3484
3485THREADED_TEST(UseWithFromExtension) {
3486 v8::HandleScope handle_scope;
3487 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3488 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3489 const char* extension_names[] = { "withtest1", "withtest2" };
3490 v8::ExtensionConfiguration extensions(2, extension_names);
3491 v8::Handle<Context> context = Context::New(&extensions);
3492 Context::Scope lock(context);
3493 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3494 CHECK_EQ(result, v8::Integer::New(87));
3495 result = Script::Compile(v8_str("UseWith2()"))->Run();
3496 CHECK_EQ(result, v8::Integer::New(87));
3497}
3498
3499
3500THREADED_TEST(AutoExtensions) {
3501 v8::HandleScope handle_scope;
3502 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3503 extension->set_auto_enable(true);
3504 v8::RegisterExtension(extension);
3505 v8::Handle<Context> context = Context::New();
3506 Context::Scope lock(context);
3507 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3508 CHECK_EQ(result, v8::Integer::New(4));
3509}
3510
3511
Steve Blockd0582a62009-12-15 09:54:21 +00003512static const char* kSyntaxErrorInExtensionSource =
3513 "[";
3514
3515
3516// Test that a syntax error in an extension does not cause a fatal
3517// error but results in an empty context.
3518THREADED_TEST(SyntaxErrorExtensions) {
3519 v8::HandleScope handle_scope;
3520 v8::RegisterExtension(new Extension("syntaxerror",
3521 kSyntaxErrorInExtensionSource));
3522 const char* extension_names[] = { "syntaxerror" };
3523 v8::ExtensionConfiguration extensions(1, extension_names);
3524 v8::Handle<Context> context = Context::New(&extensions);
3525 CHECK(context.IsEmpty());
3526}
3527
3528
3529static const char* kExceptionInExtensionSource =
3530 "throw 42";
3531
3532
3533// Test that an exception when installing an extension does not cause
3534// a fatal error but results in an empty context.
3535THREADED_TEST(ExceptionExtensions) {
3536 v8::HandleScope handle_scope;
3537 v8::RegisterExtension(new Extension("exception",
3538 kExceptionInExtensionSource));
3539 const char* extension_names[] = { "exception" };
3540 v8::ExtensionConfiguration extensions(1, extension_names);
3541 v8::Handle<Context> context = Context::New(&extensions);
3542 CHECK(context.IsEmpty());
3543}
3544
3545
Steve Blocka7e24c12009-10-30 11:49:00 +00003546static void CheckDependencies(const char* name, const char* expected) {
3547 v8::HandleScope handle_scope;
3548 v8::ExtensionConfiguration config(1, &name);
3549 LocalContext context(&config);
3550 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3551}
3552
3553
3554/*
3555 * Configuration:
3556 *
3557 * /-- B <--\
3558 * A <- -- D <-- E
3559 * \-- C <--/
3560 */
3561THREADED_TEST(ExtensionDependency) {
3562 static const char* kEDeps[] = { "D" };
3563 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3564 static const char* kDDeps[] = { "B", "C" };
3565 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3566 static const char* kBCDeps[] = { "A" };
3567 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3568 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3569 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3570 CheckDependencies("A", "undefinedA");
3571 CheckDependencies("B", "undefinedAB");
3572 CheckDependencies("C", "undefinedAC");
3573 CheckDependencies("D", "undefinedABCD");
3574 CheckDependencies("E", "undefinedABCDE");
3575 v8::HandleScope handle_scope;
3576 static const char* exts[2] = { "C", "E" };
3577 v8::ExtensionConfiguration config(2, exts);
3578 LocalContext context(&config);
3579 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3580}
3581
3582
3583static const char* kExtensionTestScript =
3584 "native function A();"
3585 "native function B();"
3586 "native function C();"
3587 "function Foo(i) {"
3588 " if (i == 0) return A();"
3589 " if (i == 1) return B();"
3590 " if (i == 2) return C();"
3591 "}";
3592
3593
3594static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3595 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003596 if (args.IsConstructCall()) {
3597 args.This()->Set(v8_str("data"), args.Data());
3598 return v8::Null();
3599 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003600 return args.Data();
3601}
3602
3603
3604class FunctionExtension : public Extension {
3605 public:
3606 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3607 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3608 v8::Handle<String> name);
3609};
3610
3611
3612static int lookup_count = 0;
3613v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3614 v8::Handle<String> name) {
3615 lookup_count++;
3616 if (name->Equals(v8_str("A"))) {
3617 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3618 } else if (name->Equals(v8_str("B"))) {
3619 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3620 } else if (name->Equals(v8_str("C"))) {
3621 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3622 } else {
3623 return v8::Handle<v8::FunctionTemplate>();
3624 }
3625}
3626
3627
3628THREADED_TEST(FunctionLookup) {
3629 v8::RegisterExtension(new FunctionExtension());
3630 v8::HandleScope handle_scope;
3631 static const char* exts[1] = { "functiontest" };
3632 v8::ExtensionConfiguration config(1, exts);
3633 LocalContext context(&config);
3634 CHECK_EQ(3, lookup_count);
3635 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3636 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3637 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3638}
3639
3640
Leon Clarkee46be812010-01-19 14:06:41 +00003641THREADED_TEST(NativeFunctionConstructCall) {
3642 v8::RegisterExtension(new FunctionExtension());
3643 v8::HandleScope handle_scope;
3644 static const char* exts[1] = { "functiontest" };
3645 v8::ExtensionConfiguration config(1, exts);
3646 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003647 for (int i = 0; i < 10; i++) {
3648 // Run a few times to ensure that allocation of objects doesn't
3649 // change behavior of a constructor function.
3650 CHECK_EQ(v8::Integer::New(8),
3651 Script::Compile(v8_str("(new A()).data"))->Run());
3652 CHECK_EQ(v8::Integer::New(7),
3653 Script::Compile(v8_str("(new B()).data"))->Run());
3654 CHECK_EQ(v8::Integer::New(6),
3655 Script::Compile(v8_str("(new C()).data"))->Run());
3656 }
Leon Clarkee46be812010-01-19 14:06:41 +00003657}
3658
3659
Steve Blocka7e24c12009-10-30 11:49:00 +00003660static const char* last_location;
3661static const char* last_message;
3662void StoringErrorCallback(const char* location, const char* message) {
3663 if (last_location == NULL) {
3664 last_location = location;
3665 last_message = message;
3666 }
3667}
3668
3669
3670// ErrorReporting creates a circular extensions configuration and
3671// tests that the fatal error handler gets called. This renders V8
3672// unusable and therefore this test cannot be run in parallel.
3673TEST(ErrorReporting) {
3674 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3675 static const char* aDeps[] = { "B" };
3676 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3677 static const char* bDeps[] = { "A" };
3678 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3679 last_location = NULL;
3680 v8::ExtensionConfiguration config(1, bDeps);
3681 v8::Handle<Context> context = Context::New(&config);
3682 CHECK(context.IsEmpty());
3683 CHECK_NE(last_location, NULL);
3684}
3685
3686
3687static const char* js_code_causing_huge_string_flattening =
3688 "var str = 'X';"
3689 "for (var i = 0; i < 30; i++) {"
3690 " str = str + str;"
3691 "}"
3692 "str.match(/X/);";
3693
3694
3695void OOMCallback(const char* location, const char* message) {
3696 exit(0);
3697}
3698
3699
3700TEST(RegexpOutOfMemory) {
3701 // Execute a script that causes out of memory when flattening a string.
3702 v8::HandleScope scope;
3703 v8::V8::SetFatalErrorHandler(OOMCallback);
3704 LocalContext context;
3705 Local<Script> script =
3706 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3707 last_location = NULL;
3708 Local<Value> result = script->Run();
3709
3710 CHECK(false); // Should not return.
3711}
3712
3713
3714static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3715 v8::Handle<Value> data) {
3716 CHECK_EQ(v8::Undefined(), data);
3717 CHECK(message->GetScriptResourceName()->IsUndefined());
3718 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3719 message->GetLineNumber();
3720 message->GetSourceLine();
3721}
3722
3723
3724THREADED_TEST(ErrorWithMissingScriptInfo) {
3725 v8::HandleScope scope;
3726 LocalContext context;
3727 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3728 Script::Compile(v8_str("throw Error()"))->Run();
3729 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3730}
3731
3732
3733int global_index = 0;
3734
3735class Snorkel {
3736 public:
3737 Snorkel() { index_ = global_index++; }
3738 int index_;
3739};
3740
3741class Whammy {
3742 public:
3743 Whammy() {
3744 cursor_ = 0;
3745 }
3746 ~Whammy() {
3747 script_.Dispose();
3748 }
3749 v8::Handle<Script> getScript() {
3750 if (script_.IsEmpty())
3751 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3752 return Local<Script>(*script_);
3753 }
3754
3755 public:
3756 static const int kObjectCount = 256;
3757 int cursor_;
3758 v8::Persistent<v8::Object> objects_[kObjectCount];
3759 v8::Persistent<Script> script_;
3760};
3761
3762static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3763 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3764 delete snorkel;
3765 obj.ClearWeak();
3766}
3767
3768v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3769 const AccessorInfo& info) {
3770 Whammy* whammy =
3771 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3772
3773 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3774
3775 v8::Handle<v8::Object> obj = v8::Object::New();
3776 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3777 if (!prev.IsEmpty()) {
3778 prev->Set(v8_str("next"), obj);
3779 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3780 whammy->objects_[whammy->cursor_].Clear();
3781 }
3782 whammy->objects_[whammy->cursor_] = global;
3783 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3784 return whammy->getScript()->Run();
3785}
3786
3787THREADED_TEST(WeakReference) {
3788 v8::HandleScope handle_scope;
3789 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
3790 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3791 0, 0, 0, 0,
3792 v8::External::New(new Whammy()));
3793 const char* extension_list[] = { "v8/gc" };
3794 v8::ExtensionConfiguration extensions(1, extension_list);
3795 v8::Persistent<Context> context = Context::New(&extensions);
3796 Context::Scope context_scope(context);
3797
3798 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3799 context->Global()->Set(v8_str("whammy"), interceptor);
3800 const char* code =
3801 "var last;"
3802 "for (var i = 0; i < 10000; i++) {"
3803 " var obj = whammy.length;"
3804 " if (last) last.next = obj;"
3805 " last = obj;"
3806 "}"
3807 "gc();"
3808 "4";
3809 v8::Handle<Value> result = CompileRun(code);
3810 CHECK_EQ(4.0, result->NumberValue());
3811
3812 context.Dispose();
3813}
3814
3815
Steve Blockd0582a62009-12-15 09:54:21 +00003816static bool in_scavenge = false;
3817static int last = -1;
3818
3819static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3820 CHECK_EQ(-1, last);
3821 last = 0;
3822 obj.Dispose();
3823 obj.Clear();
3824 in_scavenge = true;
3825 i::Heap::PerformScavenge();
3826 in_scavenge = false;
3827 *(reinterpret_cast<bool*>(data)) = true;
3828}
3829
3830static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3831 void* data) {
3832 CHECK_EQ(0, last);
3833 last = 1;
3834 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3835 obj.Dispose();
3836 obj.Clear();
3837}
3838
3839THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3840 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3841 // Calling callbacks from scavenges is unsafe as objects held by those
3842 // handlers might have become strongly reachable, but scavenge doesn't
3843 // check that.
3844 v8::Persistent<Context> context = Context::New();
3845 Context::Scope context_scope(context);
3846
3847 v8::Persistent<v8::Object> object_a;
3848 v8::Persistent<v8::Object> object_b;
3849
3850 {
3851 v8::HandleScope handle_scope;
3852 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
3853 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
3854 }
3855
3856 bool object_a_disposed = false;
3857 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
3858 bool released_in_scavenge = false;
3859 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
3860
3861 while (!object_a_disposed) {
3862 i::Heap::CollectAllGarbage(false);
3863 }
3864 CHECK(!released_in_scavenge);
3865}
3866
3867
Steve Blocka7e24c12009-10-30 11:49:00 +00003868v8::Handle<Function> args_fun;
3869
3870
3871static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3872 ApiTestFuzzer::Fuzz();
3873 CHECK_EQ(args_fun, args.Callee());
3874 CHECK_EQ(3, args.Length());
3875 CHECK_EQ(v8::Integer::New(1), args[0]);
3876 CHECK_EQ(v8::Integer::New(2), args[1]);
3877 CHECK_EQ(v8::Integer::New(3), args[2]);
3878 CHECK_EQ(v8::Undefined(), args[3]);
3879 v8::HandleScope scope;
3880 i::Heap::CollectAllGarbage(false);
3881 return v8::Undefined();
3882}
3883
3884
3885THREADED_TEST(Arguments) {
3886 v8::HandleScope scope;
3887 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3888 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3889 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01003890 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003891 v8_compile("f(1, 2, 3)")->Run();
3892}
3893
3894
Steve Blocka7e24c12009-10-30 11:49:00 +00003895static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3896 const AccessorInfo&) {
3897 return v8::Handle<Value>();
3898}
3899
3900
3901static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3902 const AccessorInfo&) {
3903 return v8::Handle<Value>();
3904}
3905
3906
3907static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3908 const AccessorInfo&) {
3909 if (!name->Equals(v8_str("foo"))) {
3910 return v8::Handle<v8::Boolean>(); // not intercepted
3911 }
3912
3913 return v8::False(); // intercepted, and don't delete the property
3914}
3915
3916
3917static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
3918 if (index != 2) {
3919 return v8::Handle<v8::Boolean>(); // not intercepted
3920 }
3921
3922 return v8::False(); // intercepted, and don't delete the property
3923}
3924
3925
3926THREADED_TEST(Deleter) {
3927 v8::HandleScope scope;
3928 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3929 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
3930 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
3931 LocalContext context;
3932 context->Global()->Set(v8_str("k"), obj->NewInstance());
3933 CompileRun(
3934 "k.foo = 'foo';"
3935 "k.bar = 'bar';"
3936 "k[2] = 2;"
3937 "k[4] = 4;");
3938 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
3939 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
3940
3941 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
3942 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
3943
3944 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
3945 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
3946
3947 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
3948 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
3949}
3950
3951
3952static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
3953 ApiTestFuzzer::Fuzz();
3954 if (name->Equals(v8_str("foo")) ||
3955 name->Equals(v8_str("bar")) ||
3956 name->Equals(v8_str("baz"))) {
3957 return v8::Undefined();
3958 }
3959 return v8::Handle<Value>();
3960}
3961
3962
3963static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
3964 ApiTestFuzzer::Fuzz();
3965 if (index == 0 || index == 1) return v8::Undefined();
3966 return v8::Handle<Value>();
3967}
3968
3969
3970static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
3971 ApiTestFuzzer::Fuzz();
3972 v8::Handle<v8::Array> result = v8::Array::New(3);
3973 result->Set(v8::Integer::New(0), v8_str("foo"));
3974 result->Set(v8::Integer::New(1), v8_str("bar"));
3975 result->Set(v8::Integer::New(2), v8_str("baz"));
3976 return result;
3977}
3978
3979
3980static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
3981 ApiTestFuzzer::Fuzz();
3982 v8::Handle<v8::Array> result = v8::Array::New(2);
3983 result->Set(v8::Integer::New(0), v8_str("0"));
3984 result->Set(v8::Integer::New(1), v8_str("1"));
3985 return result;
3986}
3987
3988
3989THREADED_TEST(Enumerators) {
3990 v8::HandleScope scope;
3991 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3992 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
3993 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
3994 LocalContext context;
3995 context->Global()->Set(v8_str("k"), obj->NewInstance());
3996 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
3997 "k[10] = 0;"
3998 "k.a = 0;"
3999 "k[5] = 0;"
4000 "k.b = 0;"
4001 "k[4294967295] = 0;"
4002 "k.c = 0;"
4003 "k[4294967296] = 0;"
4004 "k.d = 0;"
4005 "k[140000] = 0;"
4006 "k.e = 0;"
4007 "k[30000000000] = 0;"
4008 "k.f = 0;"
4009 "var result = [];"
4010 "for (var prop in k) {"
4011 " result.push(prop);"
4012 "}"
4013 "result"));
4014 // Check that we get all the property names returned including the
4015 // ones from the enumerators in the right order: indexed properties
4016 // in numerical order, indexed interceptor properties, named
4017 // properties in insertion order, named interceptor properties.
4018 // This order is not mandated by the spec, so this test is just
4019 // documenting our behavior.
4020 CHECK_EQ(17, result->Length());
4021 // Indexed properties in numerical order.
4022 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4023 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4024 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4025 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4026 // Indexed interceptor properties in the order they are returned
4027 // from the enumerator interceptor.
4028 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4029 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4030 // Named properties in insertion order.
4031 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4032 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4033 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4034 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4035 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4036 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4037 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4038 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4039 // Named interceptor properties.
4040 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4041 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4042 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4043}
4044
4045
4046int p_getter_count;
4047int p_getter_count2;
4048
4049
4050static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4051 ApiTestFuzzer::Fuzz();
4052 p_getter_count++;
4053 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4054 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4055 if (name->Equals(v8_str("p1"))) {
4056 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4057 } else if (name->Equals(v8_str("p2"))) {
4058 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4059 } else if (name->Equals(v8_str("p3"))) {
4060 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4061 } else if (name->Equals(v8_str("p4"))) {
4062 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4063 }
4064 return v8::Undefined();
4065}
4066
4067
4068static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4069 ApiTestFuzzer::Fuzz();
4070 LocalContext context;
4071 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4072 CompileRun(
4073 "o1.__proto__ = { };"
4074 "var o2 = { __proto__: o1 };"
4075 "var o3 = { __proto__: o2 };"
4076 "var o4 = { __proto__: o3 };"
4077 "for (var i = 0; i < 10; i++) o4.p4;"
4078 "for (var i = 0; i < 10; i++) o3.p3;"
4079 "for (var i = 0; i < 10; i++) o2.p2;"
4080 "for (var i = 0; i < 10; i++) o1.p1;");
4081}
4082
4083
4084static v8::Handle<Value> PGetter2(Local<String> name,
4085 const AccessorInfo& info) {
4086 ApiTestFuzzer::Fuzz();
4087 p_getter_count2++;
4088 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4089 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4090 if (name->Equals(v8_str("p1"))) {
4091 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4092 } else if (name->Equals(v8_str("p2"))) {
4093 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4094 } else if (name->Equals(v8_str("p3"))) {
4095 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4096 } else if (name->Equals(v8_str("p4"))) {
4097 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4098 }
4099 return v8::Undefined();
4100}
4101
4102
4103THREADED_TEST(GetterHolders) {
4104 v8::HandleScope scope;
4105 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4106 obj->SetAccessor(v8_str("p1"), PGetter);
4107 obj->SetAccessor(v8_str("p2"), PGetter);
4108 obj->SetAccessor(v8_str("p3"), PGetter);
4109 obj->SetAccessor(v8_str("p4"), PGetter);
4110 p_getter_count = 0;
4111 RunHolderTest(obj);
4112 CHECK_EQ(40, p_getter_count);
4113}
4114
4115
4116THREADED_TEST(PreInterceptorHolders) {
4117 v8::HandleScope scope;
4118 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4119 obj->SetNamedPropertyHandler(PGetter2);
4120 p_getter_count2 = 0;
4121 RunHolderTest(obj);
4122 CHECK_EQ(40, p_getter_count2);
4123}
4124
4125
4126THREADED_TEST(ObjectInstantiation) {
4127 v8::HandleScope scope;
4128 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4129 templ->SetAccessor(v8_str("t"), PGetter2);
4130 LocalContext context;
4131 context->Global()->Set(v8_str("o"), templ->NewInstance());
4132 for (int i = 0; i < 100; i++) {
4133 v8::HandleScope inner_scope;
4134 v8::Handle<v8::Object> obj = templ->NewInstance();
4135 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4136 context->Global()->Set(v8_str("o2"), obj);
4137 v8::Handle<Value> value =
4138 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4139 CHECK_EQ(v8::True(), value);
4140 context->Global()->Set(v8_str("o"), obj);
4141 }
4142}
4143
4144
4145THREADED_TEST(StringWrite) {
4146 v8::HandleScope scope;
4147 v8::Handle<String> str = v8_str("abcde");
4148
4149 char buf[100];
4150 int len;
4151
4152 memset(buf, 0x1, sizeof(buf));
4153 len = str->WriteAscii(buf);
4154 CHECK_EQ(len, 5);
4155 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4156
4157 memset(buf, 0x1, sizeof(buf));
4158 len = str->WriteAscii(buf, 0, 4);
4159 CHECK_EQ(len, 4);
4160 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4161
4162 memset(buf, 0x1, sizeof(buf));
4163 len = str->WriteAscii(buf, 0, 5);
4164 CHECK_EQ(len, 5);
4165 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4166
4167 memset(buf, 0x1, sizeof(buf));
4168 len = str->WriteAscii(buf, 0, 6);
4169 CHECK_EQ(len, 5);
4170 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4171
4172 memset(buf, 0x1, sizeof(buf));
4173 len = str->WriteAscii(buf, 4, -1);
4174 CHECK_EQ(len, 1);
4175 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4176
4177 memset(buf, 0x1, sizeof(buf));
4178 len = str->WriteAscii(buf, 4, 6);
4179 CHECK_EQ(len, 1);
4180 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4181
4182 memset(buf, 0x1, sizeof(buf));
4183 len = str->WriteAscii(buf, 4, 1);
4184 CHECK_EQ(len, 1);
4185 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4186}
4187
4188
4189THREADED_TEST(ToArrayIndex) {
4190 v8::HandleScope scope;
4191 LocalContext context;
4192
4193 v8::Handle<String> str = v8_str("42");
4194 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4195 CHECK(!index.IsEmpty());
4196 CHECK_EQ(42.0, index->Uint32Value());
4197 str = v8_str("42asdf");
4198 index = str->ToArrayIndex();
4199 CHECK(index.IsEmpty());
4200 str = v8_str("-42");
4201 index = str->ToArrayIndex();
4202 CHECK(index.IsEmpty());
4203 str = v8_str("4294967295");
4204 index = str->ToArrayIndex();
4205 CHECK(!index.IsEmpty());
4206 CHECK_EQ(4294967295.0, index->Uint32Value());
4207 v8::Handle<v8::Number> num = v8::Number::New(1);
4208 index = num->ToArrayIndex();
4209 CHECK(!index.IsEmpty());
4210 CHECK_EQ(1.0, index->Uint32Value());
4211 num = v8::Number::New(-1);
4212 index = num->ToArrayIndex();
4213 CHECK(index.IsEmpty());
4214 v8::Handle<v8::Object> obj = v8::Object::New();
4215 index = obj->ToArrayIndex();
4216 CHECK(index.IsEmpty());
4217}
4218
4219
4220THREADED_TEST(ErrorConstruction) {
4221 v8::HandleScope scope;
4222 LocalContext context;
4223
4224 v8::Handle<String> foo = v8_str("foo");
4225 v8::Handle<String> message = v8_str("message");
4226 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4227 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004228 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4229 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004230 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4231 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004232 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004233 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4234 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004235 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004236 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4237 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004238 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004239 v8::Handle<Value> error = v8::Exception::Error(foo);
4240 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004241 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004242}
4243
4244
4245static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4246 ApiTestFuzzer::Fuzz();
4247 return v8_num(10);
4248}
4249
4250
4251static void YSetter(Local<String> name,
4252 Local<Value> value,
4253 const AccessorInfo& info) {
4254 if (info.This()->Has(name)) {
4255 info.This()->Delete(name);
4256 }
4257 info.This()->Set(name, value);
4258}
4259
4260
4261THREADED_TEST(DeleteAccessor) {
4262 v8::HandleScope scope;
4263 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4264 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4265 LocalContext context;
4266 v8::Handle<v8::Object> holder = obj->NewInstance();
4267 context->Global()->Set(v8_str("holder"), holder);
4268 v8::Handle<Value> result = CompileRun(
4269 "holder.y = 11; holder.y = 12; holder.y");
4270 CHECK_EQ(12, result->Uint32Value());
4271}
4272
4273
4274THREADED_TEST(TypeSwitch) {
4275 v8::HandleScope scope;
4276 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4277 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4278 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4279 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4280 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4281 LocalContext context;
4282 v8::Handle<v8::Object> obj0 = v8::Object::New();
4283 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4284 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4285 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4286 for (int i = 0; i < 10; i++) {
4287 CHECK_EQ(0, type_switch->match(obj0));
4288 CHECK_EQ(1, type_switch->match(obj1));
4289 CHECK_EQ(2, type_switch->match(obj2));
4290 CHECK_EQ(3, type_switch->match(obj3));
4291 CHECK_EQ(3, type_switch->match(obj3));
4292 CHECK_EQ(2, type_switch->match(obj2));
4293 CHECK_EQ(1, type_switch->match(obj1));
4294 CHECK_EQ(0, type_switch->match(obj0));
4295 }
4296}
4297
4298
4299// For use within the TestSecurityHandler() test.
4300static bool g_security_callback_result = false;
4301static bool NamedSecurityTestCallback(Local<v8::Object> global,
4302 Local<Value> name,
4303 v8::AccessType type,
4304 Local<Value> data) {
4305 // Always allow read access.
4306 if (type == v8::ACCESS_GET)
4307 return true;
4308
4309 // Sometimes allow other access.
4310 return g_security_callback_result;
4311}
4312
4313
4314static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4315 uint32_t key,
4316 v8::AccessType type,
4317 Local<Value> data) {
4318 // Always allow read access.
4319 if (type == v8::ACCESS_GET)
4320 return true;
4321
4322 // Sometimes allow other access.
4323 return g_security_callback_result;
4324}
4325
4326
4327static int trouble_nesting = 0;
4328static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4329 ApiTestFuzzer::Fuzz();
4330 trouble_nesting++;
4331
4332 // Call a JS function that throws an uncaught exception.
4333 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4334 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4335 arg_this->Get(v8_str("trouble_callee")) :
4336 arg_this->Get(v8_str("trouble_caller"));
4337 CHECK(trouble_callee->IsFunction());
4338 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4339}
4340
4341
4342static int report_count = 0;
4343static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4344 v8::Handle<Value>) {
4345 report_count++;
4346}
4347
4348
4349// Counts uncaught exceptions, but other tests running in parallel
4350// also have uncaught exceptions.
4351TEST(ApiUncaughtException) {
4352 report_count = 0;
4353 v8::HandleScope scope;
4354 LocalContext env;
4355 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4356
4357 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4358 v8::Local<v8::Object> global = env->Global();
4359 global->Set(v8_str("trouble"), fun->GetFunction());
4360
4361 Script::Compile(v8_str("function trouble_callee() {"
4362 " var x = null;"
4363 " return x.foo;"
4364 "};"
4365 "function trouble_caller() {"
4366 " trouble();"
4367 "};"))->Run();
4368 Local<Value> trouble = global->Get(v8_str("trouble"));
4369 CHECK(trouble->IsFunction());
4370 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4371 CHECK(trouble_callee->IsFunction());
4372 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4373 CHECK(trouble_caller->IsFunction());
4374 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4375 CHECK_EQ(1, report_count);
4376 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4377}
4378
Leon Clarke4515c472010-02-03 11:58:03 +00004379static const char* script_resource_name = "ExceptionInNativeScript.js";
4380static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4381 v8::Handle<Value>) {
4382 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4383 CHECK(!name_val.IsEmpty() && name_val->IsString());
4384 v8::String::AsciiValue name(message->GetScriptResourceName());
4385 CHECK_EQ(script_resource_name, *name);
4386 CHECK_EQ(3, message->GetLineNumber());
4387 v8::String::AsciiValue source_line(message->GetSourceLine());
4388 CHECK_EQ(" new o.foo();", *source_line);
4389}
4390
4391TEST(ExceptionInNativeScript) {
4392 v8::HandleScope scope;
4393 LocalContext env;
4394 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4395
4396 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4397 v8::Local<v8::Object> global = env->Global();
4398 global->Set(v8_str("trouble"), fun->GetFunction());
4399
4400 Script::Compile(v8_str("function trouble() {\n"
4401 " var o = {};\n"
4402 " new o.foo();\n"
4403 "};"), v8::String::New(script_resource_name))->Run();
4404 Local<Value> trouble = global->Get(v8_str("trouble"));
4405 CHECK(trouble->IsFunction());
4406 Function::Cast(*trouble)->Call(global, 0, NULL);
4407 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4408}
4409
Steve Blocka7e24c12009-10-30 11:49:00 +00004410
4411TEST(CompilationErrorUsingTryCatchHandler) {
4412 v8::HandleScope scope;
4413 LocalContext env;
4414 v8::TryCatch try_catch;
4415 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4416 CHECK_NE(NULL, *try_catch.Exception());
4417 CHECK(try_catch.HasCaught());
4418}
4419
4420
4421TEST(TryCatchFinallyUsingTryCatchHandler) {
4422 v8::HandleScope scope;
4423 LocalContext env;
4424 v8::TryCatch try_catch;
4425 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4426 CHECK(!try_catch.HasCaught());
4427 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4428 CHECK(try_catch.HasCaught());
4429 try_catch.Reset();
4430 Script::Compile(v8_str("(function() {"
4431 "try { throw ''; } finally { return; }"
4432 "})()"))->Run();
4433 CHECK(!try_catch.HasCaught());
4434 Script::Compile(v8_str("(function()"
4435 " { try { throw ''; } finally { throw 0; }"
4436 "})()"))->Run();
4437 CHECK(try_catch.HasCaught());
4438}
4439
4440
4441// SecurityHandler can't be run twice
4442TEST(SecurityHandler) {
4443 v8::HandleScope scope0;
4444 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4445 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4446 IndexedSecurityTestCallback);
4447 // Create an environment
4448 v8::Persistent<Context> context0 =
4449 Context::New(NULL, global_template);
4450 context0->Enter();
4451
4452 v8::Handle<v8::Object> global0 = context0->Global();
4453 v8::Handle<Script> script0 = v8_compile("foo = 111");
4454 script0->Run();
4455 global0->Set(v8_str("0"), v8_num(999));
4456 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4457 CHECK_EQ(111, foo0->Int32Value());
4458 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4459 CHECK_EQ(999, z0->Int32Value());
4460
4461 // Create another environment, should fail security checks.
4462 v8::HandleScope scope1;
4463
4464 v8::Persistent<Context> context1 =
4465 Context::New(NULL, global_template);
4466 context1->Enter();
4467
4468 v8::Handle<v8::Object> global1 = context1->Global();
4469 global1->Set(v8_str("othercontext"), global0);
4470 // This set will fail the security check.
4471 v8::Handle<Script> script1 =
4472 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4473 script1->Run();
4474 // This read will pass the security check.
4475 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4476 CHECK_EQ(111, foo1->Int32Value());
4477 // This read will pass the security check.
4478 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4479 CHECK_EQ(999, z1->Int32Value());
4480
4481 // Create another environment, should pass security checks.
4482 { g_security_callback_result = true; // allow security handler to pass.
4483 v8::HandleScope scope2;
4484 LocalContext context2;
4485 v8::Handle<v8::Object> global2 = context2->Global();
4486 global2->Set(v8_str("othercontext"), global0);
4487 v8::Handle<Script> script2 =
4488 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4489 script2->Run();
4490 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4491 CHECK_EQ(333, foo2->Int32Value());
4492 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4493 CHECK_EQ(888, z2->Int32Value());
4494 }
4495
4496 context1->Exit();
4497 context1.Dispose();
4498
4499 context0->Exit();
4500 context0.Dispose();
4501}
4502
4503
4504THREADED_TEST(SecurityChecks) {
4505 v8::HandleScope handle_scope;
4506 LocalContext env1;
4507 v8::Persistent<Context> env2 = Context::New();
4508
4509 Local<Value> foo = v8_str("foo");
4510 Local<Value> bar = v8_str("bar");
4511
4512 // Set to the same domain.
4513 env1->SetSecurityToken(foo);
4514
4515 // Create a function in env1.
4516 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4517 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4518 CHECK(spy->IsFunction());
4519
4520 // Create another function accessing global objects.
4521 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4522 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4523 CHECK(spy2->IsFunction());
4524
4525 // Switch to env2 in the same domain and invoke spy on env2.
4526 {
4527 env2->SetSecurityToken(foo);
4528 // Enter env2
4529 Context::Scope scope_env2(env2);
4530 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4531 CHECK(result->IsFunction());
4532 }
4533
4534 {
4535 env2->SetSecurityToken(bar);
4536 Context::Scope scope_env2(env2);
4537
4538 // Call cross_domain_call, it should throw an exception
4539 v8::TryCatch try_catch;
4540 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4541 CHECK(try_catch.HasCaught());
4542 }
4543
4544 env2.Dispose();
4545}
4546
4547
4548// Regression test case for issue 1183439.
4549THREADED_TEST(SecurityChecksForPrototypeChain) {
4550 v8::HandleScope scope;
4551 LocalContext current;
4552 v8::Persistent<Context> other = Context::New();
4553
4554 // Change context to be able to get to the Object function in the
4555 // other context without hitting the security checks.
4556 v8::Local<Value> other_object;
4557 { Context::Scope scope(other);
4558 other_object = other->Global()->Get(v8_str("Object"));
4559 other->Global()->Set(v8_num(42), v8_num(87));
4560 }
4561
4562 current->Global()->Set(v8_str("other"), other->Global());
4563 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4564
4565 // Make sure the security check fails here and we get an undefined
4566 // result instead of getting the Object function. Repeat in a loop
4567 // to make sure to exercise the IC code.
4568 v8::Local<Script> access_other0 = v8_compile("other.Object");
4569 v8::Local<Script> access_other1 = v8_compile("other[42]");
4570 for (int i = 0; i < 5; i++) {
4571 CHECK(!access_other0->Run()->Equals(other_object));
4572 CHECK(access_other0->Run()->IsUndefined());
4573 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4574 CHECK(access_other1->Run()->IsUndefined());
4575 }
4576
4577 // Create an object that has 'other' in its prototype chain and make
4578 // sure we cannot access the Object function indirectly through
4579 // that. Repeat in a loop to make sure to exercise the IC code.
4580 v8_compile("function F() { };"
4581 "F.prototype = other;"
4582 "var f = new F();")->Run();
4583 v8::Local<Script> access_f0 = v8_compile("f.Object");
4584 v8::Local<Script> access_f1 = v8_compile("f[42]");
4585 for (int j = 0; j < 5; j++) {
4586 CHECK(!access_f0->Run()->Equals(other_object));
4587 CHECK(access_f0->Run()->IsUndefined());
4588 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4589 CHECK(access_f1->Run()->IsUndefined());
4590 }
4591
4592 // Now it gets hairy: Set the prototype for the other global object
4593 // to be the current global object. The prototype chain for 'f' now
4594 // goes through 'other' but ends up in the current global object.
4595 { Context::Scope scope(other);
4596 other->Global()->Set(v8_str("__proto__"), current->Global());
4597 }
4598 // Set a named and an index property on the current global
4599 // object. To force the lookup to go through the other global object,
4600 // the properties must not exist in the other global object.
4601 current->Global()->Set(v8_str("foo"), v8_num(100));
4602 current->Global()->Set(v8_num(99), v8_num(101));
4603 // Try to read the properties from f and make sure that the access
4604 // gets stopped by the security checks on the other global object.
4605 Local<Script> access_f2 = v8_compile("f.foo");
4606 Local<Script> access_f3 = v8_compile("f[99]");
4607 for (int k = 0; k < 5; k++) {
4608 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4609 CHECK(access_f2->Run()->IsUndefined());
4610 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4611 CHECK(access_f3->Run()->IsUndefined());
4612 }
4613 other.Dispose();
4614}
4615
4616
4617THREADED_TEST(CrossDomainDelete) {
4618 v8::HandleScope handle_scope;
4619 LocalContext env1;
4620 v8::Persistent<Context> env2 = Context::New();
4621
4622 Local<Value> foo = v8_str("foo");
4623 Local<Value> bar = v8_str("bar");
4624
4625 // Set to the same domain.
4626 env1->SetSecurityToken(foo);
4627 env2->SetSecurityToken(foo);
4628
4629 env1->Global()->Set(v8_str("prop"), v8_num(3));
4630 env2->Global()->Set(v8_str("env1"), env1->Global());
4631
4632 // Change env2 to a different domain and delete env1.prop.
4633 env2->SetSecurityToken(bar);
4634 {
4635 Context::Scope scope_env2(env2);
4636 Local<Value> result =
4637 Script::Compile(v8_str("delete env1.prop"))->Run();
4638 CHECK(result->IsFalse());
4639 }
4640
4641 // Check that env1.prop still exists.
4642 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4643 CHECK(v->IsNumber());
4644 CHECK_EQ(3, v->Int32Value());
4645
4646 env2.Dispose();
4647}
4648
4649
4650THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4651 v8::HandleScope handle_scope;
4652 LocalContext env1;
4653 v8::Persistent<Context> env2 = Context::New();
4654
4655 Local<Value> foo = v8_str("foo");
4656 Local<Value> bar = v8_str("bar");
4657
4658 // Set to the same domain.
4659 env1->SetSecurityToken(foo);
4660 env2->SetSecurityToken(foo);
4661
4662 env1->Global()->Set(v8_str("prop"), v8_num(3));
4663 env2->Global()->Set(v8_str("env1"), env1->Global());
4664
4665 // env1.prop is enumerable in env2.
4666 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4667 {
4668 Context::Scope scope_env2(env2);
4669 Local<Value> result = Script::Compile(test)->Run();
4670 CHECK(result->IsTrue());
4671 }
4672
4673 // Change env2 to a different domain and test again.
4674 env2->SetSecurityToken(bar);
4675 {
4676 Context::Scope scope_env2(env2);
4677 Local<Value> result = Script::Compile(test)->Run();
4678 CHECK(result->IsFalse());
4679 }
4680
4681 env2.Dispose();
4682}
4683
4684
4685THREADED_TEST(CrossDomainForIn) {
4686 v8::HandleScope handle_scope;
4687 LocalContext env1;
4688 v8::Persistent<Context> env2 = Context::New();
4689
4690 Local<Value> foo = v8_str("foo");
4691 Local<Value> bar = v8_str("bar");
4692
4693 // Set to the same domain.
4694 env1->SetSecurityToken(foo);
4695 env2->SetSecurityToken(foo);
4696
4697 env1->Global()->Set(v8_str("prop"), v8_num(3));
4698 env2->Global()->Set(v8_str("env1"), env1->Global());
4699
4700 // Change env2 to a different domain and set env1's global object
4701 // as the __proto__ of an object in env2 and enumerate properties
4702 // in for-in. It shouldn't enumerate properties on env1's global
4703 // object.
4704 env2->SetSecurityToken(bar);
4705 {
4706 Context::Scope scope_env2(env2);
4707 Local<Value> result =
4708 CompileRun("(function(){var obj = {'__proto__':env1};"
4709 "for (var p in obj)"
4710 " if (p == 'prop') return false;"
4711 "return true;})()");
4712 CHECK(result->IsTrue());
4713 }
4714 env2.Dispose();
4715}
4716
4717
4718TEST(ContextDetachGlobal) {
4719 v8::HandleScope handle_scope;
4720 LocalContext env1;
4721 v8::Persistent<Context> env2 = Context::New();
4722
4723 Local<v8::Object> global1 = env1->Global();
4724
4725 Local<Value> foo = v8_str("foo");
4726
4727 // Set to the same domain.
4728 env1->SetSecurityToken(foo);
4729 env2->SetSecurityToken(foo);
4730
4731 // Enter env2
4732 env2->Enter();
4733
Andrei Popescu74b3c142010-03-29 12:03:09 +01004734 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004735 Local<v8::Object> global2 = env2->Global();
4736 global2->Set(v8_str("prop"), v8::Integer::New(1));
4737 CompileRun("function getProp() {return prop;}");
4738
4739 env1->Global()->Set(v8_str("getProp"),
4740 global2->Get(v8_str("getProp")));
4741
Andrei Popescu74b3c142010-03-29 12:03:09 +01004742 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004743 env2->Exit();
4744 env2->DetachGlobal();
4745 // env2 has a new global object.
4746 CHECK(!env2->Global()->Equals(global2));
4747
4748 v8::Persistent<Context> env3 =
4749 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4750 env3->SetSecurityToken(v8_str("bar"));
4751 env3->Enter();
4752
4753 Local<v8::Object> global3 = env3->Global();
4754 CHECK_EQ(global2, global3);
4755 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4756 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4757 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4758 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4759 env3->Exit();
4760
4761 // Call getProp in env1, and it should return the value 1
4762 {
4763 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4764 CHECK(get_prop->IsFunction());
4765 v8::TryCatch try_catch;
4766 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4767 CHECK(!try_catch.HasCaught());
4768 CHECK_EQ(1, r->Int32Value());
4769 }
4770
4771 // Check that env3 is not accessible from env1
4772 {
4773 Local<Value> r = global3->Get(v8_str("prop2"));
4774 CHECK(r->IsUndefined());
4775 }
4776
4777 env2.Dispose();
4778 env3.Dispose();
4779}
4780
4781
Andrei Popescu74b3c142010-03-29 12:03:09 +01004782TEST(DetachAndReattachGlobal) {
4783 v8::HandleScope scope;
4784 LocalContext env1;
4785
4786 // Create second environment.
4787 v8::Persistent<Context> env2 = Context::New();
4788
4789 Local<Value> foo = v8_str("foo");
4790
4791 // Set same security token for env1 and env2.
4792 env1->SetSecurityToken(foo);
4793 env2->SetSecurityToken(foo);
4794
4795 // Create a property on the global object in env2.
4796 {
4797 v8::Context::Scope scope(env2);
4798 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4799 }
4800
4801 // Create a reference to env2 global from env1 global.
4802 env1->Global()->Set(v8_str("other"), env2->Global());
4803
4804 // Check that we have access to other.p in env2 from env1.
4805 Local<Value> result = CompileRun("other.p");
4806 CHECK(result->IsInt32());
4807 CHECK_EQ(42, result->Int32Value());
4808
4809 // Hold on to global from env2 and detach global from env2.
4810 Local<v8::Object> global2 = env2->Global();
4811 env2->DetachGlobal();
4812
4813 // Check that the global has been detached. No other.p property can
4814 // be found.
4815 result = CompileRun("other.p");
4816 CHECK(result->IsUndefined());
4817
4818 // Reuse global2 for env3.
4819 v8::Persistent<Context> env3 =
4820 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4821 CHECK_EQ(global2, env3->Global());
4822
4823 // Start by using the same security token for env3 as for env1 and env2.
4824 env3->SetSecurityToken(foo);
4825
4826 // Create a property on the global object in env3.
4827 {
4828 v8::Context::Scope scope(env3);
4829 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4830 }
4831
4832 // Check that other.p is now the property in env3 and that we have access.
4833 result = CompileRun("other.p");
4834 CHECK(result->IsInt32());
4835 CHECK_EQ(24, result->Int32Value());
4836
4837 // Change security token for env3 to something different from env1 and env2.
4838 env3->SetSecurityToken(v8_str("bar"));
4839
4840 // Check that we do not have access to other.p in env1. |other| is now
4841 // the global object for env3 which has a different security token,
4842 // so access should be blocked.
4843 result = CompileRun("other.p");
4844 CHECK(result->IsUndefined());
4845
4846 // Detach the global for env3 and reattach it to env2.
4847 env3->DetachGlobal();
4848 env2->ReattachGlobal(global2);
4849
4850 // Check that we have access to other.p again in env1. |other| is now
4851 // the global object for env2 which has the same security token as env1.
4852 result = CompileRun("other.p");
4853 CHECK(result->IsInt32());
4854 CHECK_EQ(42, result->Int32Value());
4855
4856 env2.Dispose();
4857 env3.Dispose();
4858}
4859
4860
Steve Blocka7e24c12009-10-30 11:49:00 +00004861static bool NamedAccessBlocker(Local<v8::Object> global,
4862 Local<Value> name,
4863 v8::AccessType type,
4864 Local<Value> data) {
4865 return Context::GetCurrent()->Global()->Equals(global);
4866}
4867
4868
4869static bool IndexedAccessBlocker(Local<v8::Object> global,
4870 uint32_t key,
4871 v8::AccessType type,
4872 Local<Value> data) {
4873 return Context::GetCurrent()->Global()->Equals(global);
4874}
4875
4876
4877static int g_echo_value = -1;
4878static v8::Handle<Value> EchoGetter(Local<String> name,
4879 const AccessorInfo& info) {
4880 return v8_num(g_echo_value);
4881}
4882
4883
4884static void EchoSetter(Local<String> name,
4885 Local<Value> value,
4886 const AccessorInfo&) {
4887 if (value->IsNumber())
4888 g_echo_value = value->Int32Value();
4889}
4890
4891
4892static v8::Handle<Value> UnreachableGetter(Local<String> name,
4893 const AccessorInfo& info) {
4894 CHECK(false); // This function should not be called..
4895 return v8::Undefined();
4896}
4897
4898
4899static void UnreachableSetter(Local<String>, Local<Value>,
4900 const AccessorInfo&) {
4901 CHECK(false); // This function should nto be called.
4902}
4903
4904
4905THREADED_TEST(AccessControl) {
4906 v8::HandleScope handle_scope;
4907 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4908
4909 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4910 IndexedAccessBlocker);
4911
4912 // Add an accessor accessible by cross-domain JS code.
4913 global_template->SetAccessor(
4914 v8_str("accessible_prop"),
4915 EchoGetter, EchoSetter,
4916 v8::Handle<Value>(),
4917 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4918
4919 // Add an accessor that is not accessible by cross-domain JS code.
4920 global_template->SetAccessor(v8_str("blocked_prop"),
4921 UnreachableGetter, UnreachableSetter,
4922 v8::Handle<Value>(),
4923 v8::DEFAULT);
4924
4925 // Create an environment
4926 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4927 context0->Enter();
4928
4929 v8::Handle<v8::Object> global0 = context0->Global();
4930
4931 v8::HandleScope scope1;
4932
4933 v8::Persistent<Context> context1 = Context::New();
4934 context1->Enter();
4935
4936 v8::Handle<v8::Object> global1 = context1->Global();
4937 global1->Set(v8_str("other"), global0);
4938
4939 v8::Handle<Value> value;
4940
4941 // Access blocked property
4942 value = v8_compile("other.blocked_prop = 1")->Run();
4943 value = v8_compile("other.blocked_prop")->Run();
4944 CHECK(value->IsUndefined());
4945
4946 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4947 CHECK(value->IsFalse());
4948
4949 // Access accessible property
4950 value = v8_compile("other.accessible_prop = 3")->Run();
4951 CHECK(value->IsNumber());
4952 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00004953 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00004954
4955 value = v8_compile("other.accessible_prop")->Run();
4956 CHECK(value->IsNumber());
4957 CHECK_EQ(3, value->Int32Value());
4958
4959 value =
4960 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
4961 CHECK(value->IsTrue());
4962
4963 // Enumeration doesn't enumerate accessors from inaccessible objects in
4964 // the prototype chain even if the accessors are in themselves accessible.
4965 Local<Value> result =
4966 CompileRun("(function(){var obj = {'__proto__':other};"
4967 "for (var p in obj)"
4968 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
4969 " return false;"
4970 " }"
4971 "return true;})()");
4972 CHECK(result->IsTrue());
4973
4974 context1->Exit();
4975 context0->Exit();
4976 context1.Dispose();
4977 context0.Dispose();
4978}
4979
4980
Leon Clarke4515c472010-02-03 11:58:03 +00004981static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
4982 Local<Value> name,
4983 v8::AccessType type,
4984 Local<Value> data) {
4985 return false;
4986}
4987
4988
4989static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
4990 uint32_t key,
4991 v8::AccessType type,
4992 Local<Value> data) {
4993 return false;
4994}
4995
4996
4997THREADED_TEST(AccessControlGetOwnPropertyNames) {
4998 v8::HandleScope handle_scope;
4999 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5000
5001 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5002 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5003 GetOwnPropertyNamesIndexedBlocker);
5004
5005 // Create an environment
5006 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5007 context0->Enter();
5008
5009 v8::Handle<v8::Object> global0 = context0->Global();
5010
5011 v8::HandleScope scope1;
5012
5013 v8::Persistent<Context> context1 = Context::New();
5014 context1->Enter();
5015
5016 v8::Handle<v8::Object> global1 = context1->Global();
5017 global1->Set(v8_str("other"), global0);
5018 global1->Set(v8_str("object"), obj_template->NewInstance());
5019
5020 v8::Handle<Value> value;
5021
5022 // Attempt to get the property names of the other global object and
5023 // of an object that requires access checks. Accessing the other
5024 // global object should be blocked by access checks on the global
5025 // proxy object. Accessing the object that requires access checks
5026 // is blocked by the access checks on the object itself.
5027 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5028 CHECK(value->IsTrue());
5029
5030 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5031 CHECK(value->IsTrue());
5032
5033 context1->Exit();
5034 context0->Exit();
5035 context1.Dispose();
5036 context0.Dispose();
5037}
5038
5039
Steve Blocka7e24c12009-10-30 11:49:00 +00005040static v8::Handle<Value> ConstTenGetter(Local<String> name,
5041 const AccessorInfo& info) {
5042 return v8_num(10);
5043}
5044
5045
5046THREADED_TEST(CrossDomainAccessors) {
5047 v8::HandleScope handle_scope;
5048
5049 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5050
5051 v8::Handle<v8::ObjectTemplate> global_template =
5052 func_template->InstanceTemplate();
5053
5054 v8::Handle<v8::ObjectTemplate> proto_template =
5055 func_template->PrototypeTemplate();
5056
5057 // Add an accessor to proto that's accessible by cross-domain JS code.
5058 proto_template->SetAccessor(v8_str("accessible"),
5059 ConstTenGetter, 0,
5060 v8::Handle<Value>(),
5061 v8::ALL_CAN_READ);
5062
5063 // Add an accessor that is not accessible by cross-domain JS code.
5064 global_template->SetAccessor(v8_str("unreachable"),
5065 UnreachableGetter, 0,
5066 v8::Handle<Value>(),
5067 v8::DEFAULT);
5068
5069 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5070 context0->Enter();
5071
5072 Local<v8::Object> global = context0->Global();
5073 // Add a normal property that shadows 'accessible'
5074 global->Set(v8_str("accessible"), v8_num(11));
5075
5076 // Enter a new context.
5077 v8::HandleScope scope1;
5078 v8::Persistent<Context> context1 = Context::New();
5079 context1->Enter();
5080
5081 v8::Handle<v8::Object> global1 = context1->Global();
5082 global1->Set(v8_str("other"), global);
5083
5084 // Should return 10, instead of 11
5085 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5086 CHECK(value->IsNumber());
5087 CHECK_EQ(10, value->Int32Value());
5088
5089 value = v8_compile("other.unreachable")->Run();
5090 CHECK(value->IsUndefined());
5091
5092 context1->Exit();
5093 context0->Exit();
5094 context1.Dispose();
5095 context0.Dispose();
5096}
5097
5098
5099static int named_access_count = 0;
5100static int indexed_access_count = 0;
5101
5102static bool NamedAccessCounter(Local<v8::Object> global,
5103 Local<Value> name,
5104 v8::AccessType type,
5105 Local<Value> data) {
5106 named_access_count++;
5107 return true;
5108}
5109
5110
5111static bool IndexedAccessCounter(Local<v8::Object> global,
5112 uint32_t key,
5113 v8::AccessType type,
5114 Local<Value> data) {
5115 indexed_access_count++;
5116 return true;
5117}
5118
5119
5120// This one is too easily disturbed by other tests.
5121TEST(AccessControlIC) {
5122 named_access_count = 0;
5123 indexed_access_count = 0;
5124
5125 v8::HandleScope handle_scope;
5126
5127 // Create an environment.
5128 v8::Persistent<Context> context0 = Context::New();
5129 context0->Enter();
5130
5131 // Create an object that requires access-check functions to be
5132 // called for cross-domain access.
5133 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5134 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5135 IndexedAccessCounter);
5136 Local<v8::Object> object = object_template->NewInstance();
5137
5138 v8::HandleScope scope1;
5139
5140 // Create another environment.
5141 v8::Persistent<Context> context1 = Context::New();
5142 context1->Enter();
5143
5144 // Make easy access to the object from the other environment.
5145 v8::Handle<v8::Object> global1 = context1->Global();
5146 global1->Set(v8_str("obj"), object);
5147
5148 v8::Handle<Value> value;
5149
5150 // Check that the named access-control function is called every time.
5151 CompileRun("function testProp(obj) {"
5152 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5153 " for (var j = 0; j < 10; j++) obj.prop;"
5154 " return obj.prop"
5155 "}");
5156 value = CompileRun("testProp(obj)");
5157 CHECK(value->IsNumber());
5158 CHECK_EQ(1, value->Int32Value());
5159 CHECK_EQ(21, named_access_count);
5160
5161 // Check that the named access-control function is called every time.
5162 CompileRun("var p = 'prop';"
5163 "function testKeyed(obj) {"
5164 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5165 " for (var j = 0; j < 10; j++) obj[p];"
5166 " return obj[p];"
5167 "}");
5168 // Use obj which requires access checks. No inline caching is used
5169 // in that case.
5170 value = CompileRun("testKeyed(obj)");
5171 CHECK(value->IsNumber());
5172 CHECK_EQ(1, value->Int32Value());
5173 CHECK_EQ(42, named_access_count);
5174 // Force the inline caches into generic state and try again.
5175 CompileRun("testKeyed({ a: 0 })");
5176 CompileRun("testKeyed({ b: 0 })");
5177 value = CompileRun("testKeyed(obj)");
5178 CHECK(value->IsNumber());
5179 CHECK_EQ(1, value->Int32Value());
5180 CHECK_EQ(63, named_access_count);
5181
5182 // Check that the indexed access-control function is called every time.
5183 CompileRun("function testIndexed(obj) {"
5184 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5185 " for (var j = 0; j < 10; j++) obj[0];"
5186 " return obj[0]"
5187 "}");
5188 value = CompileRun("testIndexed(obj)");
5189 CHECK(value->IsNumber());
5190 CHECK_EQ(1, value->Int32Value());
5191 CHECK_EQ(21, indexed_access_count);
5192 // Force the inline caches into generic state.
5193 CompileRun("testIndexed(new Array(1))");
5194 // Test that the indexed access check is called.
5195 value = CompileRun("testIndexed(obj)");
5196 CHECK(value->IsNumber());
5197 CHECK_EQ(1, value->Int32Value());
5198 CHECK_EQ(42, indexed_access_count);
5199
5200 // Check that the named access check is called when invoking
5201 // functions on an object that requires access checks.
5202 CompileRun("obj.f = function() {}");
5203 CompileRun("function testCallNormal(obj) {"
5204 " for (var i = 0; i < 10; i++) obj.f();"
5205 "}");
5206 CompileRun("testCallNormal(obj)");
5207 CHECK_EQ(74, named_access_count);
5208
5209 // Force obj into slow case.
5210 value = CompileRun("delete obj.prop");
5211 CHECK(value->BooleanValue());
5212 // Force inline caches into dictionary probing mode.
5213 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5214 // Test that the named access check is called.
5215 value = CompileRun("testProp(obj);");
5216 CHECK(value->IsNumber());
5217 CHECK_EQ(1, value->Int32Value());
5218 CHECK_EQ(96, named_access_count);
5219
5220 // Force the call inline cache into dictionary probing mode.
5221 CompileRun("o.f = function() {}; testCallNormal(o)");
5222 // Test that the named access check is still called for each
5223 // invocation of the function.
5224 value = CompileRun("testCallNormal(obj)");
5225 CHECK_EQ(106, named_access_count);
5226
5227 context1->Exit();
5228 context0->Exit();
5229 context1.Dispose();
5230 context0.Dispose();
5231}
5232
5233
5234static bool NamedAccessFlatten(Local<v8::Object> global,
5235 Local<Value> name,
5236 v8::AccessType type,
5237 Local<Value> data) {
5238 char buf[100];
5239 int len;
5240
5241 CHECK(name->IsString());
5242
5243 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005244 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005245 CHECK_EQ(4, len);
5246
5247 uint16_t buf2[100];
5248
5249 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005250 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005251 CHECK_EQ(4, len);
5252
5253 return true;
5254}
5255
5256
5257static bool IndexedAccessFlatten(Local<v8::Object> global,
5258 uint32_t key,
5259 v8::AccessType type,
5260 Local<Value> data) {
5261 return true;
5262}
5263
5264
5265// Regression test. In access checks, operations that may cause
5266// garbage collection are not allowed. It used to be the case that
5267// using the Write operation on a string could cause a garbage
5268// collection due to flattening of the string. This is no longer the
5269// case.
5270THREADED_TEST(AccessControlFlatten) {
5271 named_access_count = 0;
5272 indexed_access_count = 0;
5273
5274 v8::HandleScope handle_scope;
5275
5276 // Create an environment.
5277 v8::Persistent<Context> context0 = Context::New();
5278 context0->Enter();
5279
5280 // Create an object that requires access-check functions to be
5281 // called for cross-domain access.
5282 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5283 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5284 IndexedAccessFlatten);
5285 Local<v8::Object> object = object_template->NewInstance();
5286
5287 v8::HandleScope scope1;
5288
5289 // Create another environment.
5290 v8::Persistent<Context> context1 = Context::New();
5291 context1->Enter();
5292
5293 // Make easy access to the object from the other environment.
5294 v8::Handle<v8::Object> global1 = context1->Global();
5295 global1->Set(v8_str("obj"), object);
5296
5297 v8::Handle<Value> value;
5298
5299 value = v8_compile("var p = 'as' + 'df';")->Run();
5300 value = v8_compile("obj[p];")->Run();
5301
5302 context1->Exit();
5303 context0->Exit();
5304 context1.Dispose();
5305 context0.Dispose();
5306}
5307
5308
5309static v8::Handle<Value> AccessControlNamedGetter(
5310 Local<String>, const AccessorInfo&) {
5311 return v8::Integer::New(42);
5312}
5313
5314
5315static v8::Handle<Value> AccessControlNamedSetter(
5316 Local<String>, Local<Value> value, const AccessorInfo&) {
5317 return value;
5318}
5319
5320
5321static v8::Handle<Value> AccessControlIndexedGetter(
5322 uint32_t index,
5323 const AccessorInfo& info) {
5324 return v8_num(42);
5325}
5326
5327
5328static v8::Handle<Value> AccessControlIndexedSetter(
5329 uint32_t, Local<Value> value, const AccessorInfo&) {
5330 return value;
5331}
5332
5333
5334THREADED_TEST(AccessControlInterceptorIC) {
5335 named_access_count = 0;
5336 indexed_access_count = 0;
5337
5338 v8::HandleScope handle_scope;
5339
5340 // Create an environment.
5341 v8::Persistent<Context> context0 = Context::New();
5342 context0->Enter();
5343
5344 // Create an object that requires access-check functions to be
5345 // called for cross-domain access. The object also has interceptors
5346 // interceptor.
5347 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5348 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5349 IndexedAccessCounter);
5350 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5351 AccessControlNamedSetter);
5352 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5353 AccessControlIndexedSetter);
5354 Local<v8::Object> object = object_template->NewInstance();
5355
5356 v8::HandleScope scope1;
5357
5358 // Create another environment.
5359 v8::Persistent<Context> context1 = Context::New();
5360 context1->Enter();
5361
5362 // Make easy access to the object from the other environment.
5363 v8::Handle<v8::Object> global1 = context1->Global();
5364 global1->Set(v8_str("obj"), object);
5365
5366 v8::Handle<Value> value;
5367
5368 // Check that the named access-control function is called every time
5369 // eventhough there is an interceptor on the object.
5370 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5371 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5372 "obj.x")->Run();
5373 CHECK(value->IsNumber());
5374 CHECK_EQ(42, value->Int32Value());
5375 CHECK_EQ(21, named_access_count);
5376
5377 value = v8_compile("var p = 'x';")->Run();
5378 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5379 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5380 "obj[p]")->Run();
5381 CHECK(value->IsNumber());
5382 CHECK_EQ(42, value->Int32Value());
5383 CHECK_EQ(42, named_access_count);
5384
5385 // Check that the indexed access-control function is called every
5386 // time eventhough there is an interceptor on the object.
5387 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5388 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5389 "obj[0]")->Run();
5390 CHECK(value->IsNumber());
5391 CHECK_EQ(42, value->Int32Value());
5392 CHECK_EQ(21, indexed_access_count);
5393
5394 context1->Exit();
5395 context0->Exit();
5396 context1.Dispose();
5397 context0.Dispose();
5398}
5399
5400
5401THREADED_TEST(Version) {
5402 v8::V8::GetVersion();
5403}
5404
5405
5406static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5407 ApiTestFuzzer::Fuzz();
5408 return v8_num(12);
5409}
5410
5411
5412THREADED_TEST(InstanceProperties) {
5413 v8::HandleScope handle_scope;
5414 LocalContext context;
5415
5416 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5417 Local<ObjectTemplate> instance = t->InstanceTemplate();
5418
5419 instance->Set(v8_str("x"), v8_num(42));
5420 instance->Set(v8_str("f"),
5421 v8::FunctionTemplate::New(InstanceFunctionCallback));
5422
5423 Local<Value> o = t->GetFunction()->NewInstance();
5424
5425 context->Global()->Set(v8_str("i"), o);
5426 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5427 CHECK_EQ(42, value->Int32Value());
5428
5429 value = Script::Compile(v8_str("i.f()"))->Run();
5430 CHECK_EQ(12, value->Int32Value());
5431}
5432
5433
5434static v8::Handle<Value>
5435GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5436 ApiTestFuzzer::Fuzz();
5437 return v8::Handle<Value>();
5438}
5439
5440
5441THREADED_TEST(GlobalObjectInstanceProperties) {
5442 v8::HandleScope handle_scope;
5443
5444 Local<Value> global_object;
5445
5446 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5447 t->InstanceTemplate()->SetNamedPropertyHandler(
5448 GlobalObjectInstancePropertiesGet);
5449 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5450 instance_template->Set(v8_str("x"), v8_num(42));
5451 instance_template->Set(v8_str("f"),
5452 v8::FunctionTemplate::New(InstanceFunctionCallback));
5453
5454 {
5455 LocalContext env(NULL, instance_template);
5456 // Hold on to the global object so it can be used again in another
5457 // environment initialization.
5458 global_object = env->Global();
5459
5460 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5461 CHECK_EQ(42, value->Int32Value());
5462 value = Script::Compile(v8_str("f()"))->Run();
5463 CHECK_EQ(12, value->Int32Value());
5464 }
5465
5466 {
5467 // Create new environment reusing the global object.
5468 LocalContext env(NULL, instance_template, global_object);
5469 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5470 CHECK_EQ(42, value->Int32Value());
5471 value = Script::Compile(v8_str("f()"))->Run();
5472 CHECK_EQ(12, value->Int32Value());
5473 }
5474}
5475
5476
5477static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5478 ApiTestFuzzer::Fuzz();
5479 return v8_num(42);
5480}
5481
5482
5483static int shadow_y;
5484static int shadow_y_setter_call_count;
5485static int shadow_y_getter_call_count;
5486
5487
5488static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5489 shadow_y_setter_call_count++;
5490 shadow_y = 42;
5491}
5492
5493
5494static v8::Handle<Value> ShadowYGetter(Local<String> name,
5495 const AccessorInfo& info) {
5496 ApiTestFuzzer::Fuzz();
5497 shadow_y_getter_call_count++;
5498 return v8_num(shadow_y);
5499}
5500
5501
5502static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5503 const AccessorInfo& info) {
5504 return v8::Handle<Value>();
5505}
5506
5507
5508static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5509 const AccessorInfo&) {
5510 return v8::Handle<Value>();
5511}
5512
5513
5514THREADED_TEST(ShadowObject) {
5515 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5516 v8::HandleScope handle_scope;
5517
5518 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5519 LocalContext context(NULL, global_template);
5520
5521 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5522 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5523 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5524 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5525 Local<ObjectTemplate> instance = t->InstanceTemplate();
5526
5527 // Only allow calls of f on instances of t.
5528 Local<v8::Signature> signature = v8::Signature::New(t);
5529 proto->Set(v8_str("f"),
5530 v8::FunctionTemplate::New(ShadowFunctionCallback,
5531 Local<Value>(),
5532 signature));
5533 proto->Set(v8_str("x"), v8_num(12));
5534
5535 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5536
5537 Local<Value> o = t->GetFunction()->NewInstance();
5538 context->Global()->Set(v8_str("__proto__"), o);
5539
5540 Local<Value> value =
5541 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5542 CHECK(value->IsBoolean());
5543 CHECK(!value->BooleanValue());
5544
5545 value = Script::Compile(v8_str("x"))->Run();
5546 CHECK_EQ(12, value->Int32Value());
5547
5548 value = Script::Compile(v8_str("f()"))->Run();
5549 CHECK_EQ(42, value->Int32Value());
5550
5551 Script::Compile(v8_str("y = 42"))->Run();
5552 CHECK_EQ(1, shadow_y_setter_call_count);
5553 value = Script::Compile(v8_str("y"))->Run();
5554 CHECK_EQ(1, shadow_y_getter_call_count);
5555 CHECK_EQ(42, value->Int32Value());
5556}
5557
5558
5559THREADED_TEST(HiddenPrototype) {
5560 v8::HandleScope handle_scope;
5561 LocalContext context;
5562
5563 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5564 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5565 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5566 t1->SetHiddenPrototype(true);
5567 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5568 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5569 t2->SetHiddenPrototype(true);
5570 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5571 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5572 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5573
5574 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5575 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5576 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5577 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5578
5579 // Setting the prototype on an object skips hidden prototypes.
5580 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5581 o0->Set(v8_str("__proto__"), o1);
5582 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5583 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5584 o0->Set(v8_str("__proto__"), o2);
5585 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5586 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5587 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5588 o0->Set(v8_str("__proto__"), o3);
5589 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5590 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5591 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5592 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5593
5594 // Getting the prototype of o0 should get the first visible one
5595 // which is o3. Therefore, z should not be defined on the prototype
5596 // object.
5597 Local<Value> proto = o0->Get(v8_str("__proto__"));
5598 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005599 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005600}
5601
5602
Andrei Popescu402d9372010-02-26 13:31:12 +00005603THREADED_TEST(SetPrototype) {
5604 v8::HandleScope handle_scope;
5605 LocalContext context;
5606
5607 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5608 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5609 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5610 t1->SetHiddenPrototype(true);
5611 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5612 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5613 t2->SetHiddenPrototype(true);
5614 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5615 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5616 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5617
5618 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5619 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5620 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5621 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5622
5623 // Setting the prototype on an object does not skip hidden prototypes.
5624 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5625 CHECK(o0->SetPrototype(o1));
5626 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5627 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5628 CHECK(o1->SetPrototype(o2));
5629 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5630 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5631 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5632 CHECK(o2->SetPrototype(o3));
5633 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5634 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5635 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5636 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5637
5638 // Getting the prototype of o0 should get the first visible one
5639 // which is o3. Therefore, z should not be defined on the prototype
5640 // object.
5641 Local<Value> proto = o0->Get(v8_str("__proto__"));
5642 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005643 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005644
5645 // However, Object::GetPrototype ignores hidden prototype.
5646 Local<Value> proto0 = o0->GetPrototype();
5647 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005648 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005649
5650 Local<Value> proto1 = o1->GetPrototype();
5651 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005652 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005653
5654 Local<Value> proto2 = o2->GetPrototype();
5655 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005656 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005657}
5658
5659
5660THREADED_TEST(SetPrototypeThrows) {
5661 v8::HandleScope handle_scope;
5662 LocalContext context;
5663
5664 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5665
5666 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5667 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5668
5669 CHECK(o0->SetPrototype(o1));
5670 // If setting the prototype leads to the cycle, SetPrototype should
5671 // return false and keep VM in sane state.
5672 v8::TryCatch try_catch;
5673 CHECK(!o1->SetPrototype(o0));
5674 CHECK(!try_catch.HasCaught());
5675 ASSERT(!i::Top::has_pending_exception());
5676
5677 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5678}
5679
5680
Steve Blocka7e24c12009-10-30 11:49:00 +00005681THREADED_TEST(GetterSetterExceptions) {
5682 v8::HandleScope handle_scope;
5683 LocalContext context;
5684 CompileRun(
5685 "function Foo() { };"
5686 "function Throw() { throw 5; };"
5687 "var x = { };"
5688 "x.__defineSetter__('set', Throw);"
5689 "x.__defineGetter__('get', Throw);");
5690 Local<v8::Object> x =
5691 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5692 v8::TryCatch try_catch;
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 x->Set(v8_str("set"), v8::Integer::New(8));
5700 x->Get(v8_str("get"));
5701}
5702
5703
5704THREADED_TEST(Constructor) {
5705 v8::HandleScope handle_scope;
5706 LocalContext context;
5707 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5708 templ->SetClassName(v8_str("Fun"));
5709 Local<Function> cons = templ->GetFunction();
5710 context->Global()->Set(v8_str("Fun"), cons);
5711 Local<v8::Object> inst = cons->NewInstance();
5712 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5713 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5714 CHECK(value->BooleanValue());
5715}
5716
5717THREADED_TEST(FunctionDescriptorException) {
5718 v8::HandleScope handle_scope;
5719 LocalContext context;
5720 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5721 templ->SetClassName(v8_str("Fun"));
5722 Local<Function> cons = templ->GetFunction();
5723 context->Global()->Set(v8_str("Fun"), cons);
5724 Local<Value> value = CompileRun(
5725 "function test() {"
5726 " try {"
5727 " (new Fun()).blah()"
5728 " } catch (e) {"
5729 " var str = String(e);"
5730 " if (str.indexOf('TypeError') == -1) return 1;"
5731 " if (str.indexOf('[object Fun]') != -1) return 2;"
5732 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5733 " return 0;"
5734 " }"
5735 " return 4;"
5736 "}"
5737 "test();");
5738 CHECK_EQ(0, value->Int32Value());
5739}
5740
5741
5742THREADED_TEST(EvalAliasedDynamic) {
5743 v8::HandleScope scope;
5744 LocalContext current;
5745
5746 // Tests where aliased eval can only be resolved dynamically.
5747 Local<Script> script =
5748 Script::Compile(v8_str("function f(x) { "
5749 " var foo = 2;"
5750 " with (x) { return eval('foo'); }"
5751 "}"
5752 "foo = 0;"
5753 "result1 = f(new Object());"
5754 "result2 = f(this);"
5755 "var x = new Object();"
5756 "x.eval = function(x) { return 1; };"
5757 "result3 = f(x);"));
5758 script->Run();
5759 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5760 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5761 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5762
5763 v8::TryCatch try_catch;
5764 script =
5765 Script::Compile(v8_str("function f(x) { "
5766 " var bar = 2;"
5767 " with (x) { return eval('bar'); }"
5768 "}"
5769 "f(this)"));
5770 script->Run();
5771 CHECK(try_catch.HasCaught());
5772 try_catch.Reset();
5773}
5774
5775
5776THREADED_TEST(CrossEval) {
5777 v8::HandleScope scope;
5778 LocalContext other;
5779 LocalContext current;
5780
5781 Local<String> token = v8_str("<security token>");
5782 other->SetSecurityToken(token);
5783 current->SetSecurityToken(token);
5784
5785 // Setup reference from current to other.
5786 current->Global()->Set(v8_str("other"), other->Global());
5787
5788 // Check that new variables are introduced in other context.
5789 Local<Script> script =
5790 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5791 script->Run();
5792 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5793 CHECK_EQ(1234, foo->Int32Value());
5794 CHECK(!current->Global()->Has(v8_str("foo")));
5795
5796 // Check that writing to non-existing properties introduces them in
5797 // the other context.
5798 script =
5799 Script::Compile(v8_str("other.eval('na = 1234')"));
5800 script->Run();
5801 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5802 CHECK(!current->Global()->Has(v8_str("na")));
5803
5804 // Check that global variables in current context are not visible in other
5805 // context.
5806 v8::TryCatch try_catch;
5807 script =
5808 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5809 Local<Value> result = script->Run();
5810 CHECK(try_catch.HasCaught());
5811 try_catch.Reset();
5812
5813 // Check that local variables in current context are not visible in other
5814 // context.
5815 script =
5816 Script::Compile(v8_str("(function() { "
5817 " var baz = 87;"
5818 " return other.eval('baz');"
5819 "})();"));
5820 result = script->Run();
5821 CHECK(try_catch.HasCaught());
5822 try_catch.Reset();
5823
5824 // Check that global variables in the other environment are visible
5825 // when evaluting code.
5826 other->Global()->Set(v8_str("bis"), v8_num(1234));
5827 script = Script::Compile(v8_str("other.eval('bis')"));
5828 CHECK_EQ(1234, script->Run()->Int32Value());
5829 CHECK(!try_catch.HasCaught());
5830
5831 // Check that the 'this' pointer points to the global object evaluating
5832 // code.
5833 other->Global()->Set(v8_str("t"), other->Global());
5834 script = Script::Compile(v8_str("other.eval('this == t')"));
5835 result = script->Run();
5836 CHECK(result->IsTrue());
5837 CHECK(!try_catch.HasCaught());
5838
5839 // Check that variables introduced in with-statement are not visible in
5840 // other context.
5841 script =
5842 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
5843 result = script->Run();
5844 CHECK(try_catch.HasCaught());
5845 try_catch.Reset();
5846
5847 // Check that you cannot use 'eval.call' with another object than the
5848 // current global object.
5849 script =
5850 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
5851 result = script->Run();
5852 CHECK(try_catch.HasCaught());
5853}
5854
5855
5856// Test that calling eval in a context which has been detached from
5857// its global throws an exception. This behavior is consistent with
5858// other JavaScript implementations.
5859THREADED_TEST(EvalInDetachedGlobal) {
5860 v8::HandleScope scope;
5861
5862 v8::Persistent<Context> context0 = Context::New();
5863 v8::Persistent<Context> context1 = Context::New();
5864
5865 // Setup function in context0 that uses eval from context0.
5866 context0->Enter();
5867 v8::Handle<v8::Value> fun =
5868 CompileRun("var x = 42;"
5869 "(function() {"
5870 " var e = eval;"
5871 " return function(s) { return e(s); }"
5872 "})()");
5873 context0->Exit();
5874
5875 // Put the function into context1 and call it before and after
5876 // detaching the global. Before detaching, the call succeeds and
5877 // after detaching and exception is thrown.
5878 context1->Enter();
5879 context1->Global()->Set(v8_str("fun"), fun);
5880 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
5881 CHECK_EQ(42, x_value->Int32Value());
5882 context0->DetachGlobal();
5883 v8::TryCatch catcher;
5884 x_value = CompileRun("fun('x')");
5885 CHECK(x_value.IsEmpty());
5886 CHECK(catcher.HasCaught());
5887 context1->Exit();
5888
5889 context1.Dispose();
5890 context0.Dispose();
5891}
5892
5893
5894THREADED_TEST(CrossLazyLoad) {
5895 v8::HandleScope scope;
5896 LocalContext other;
5897 LocalContext current;
5898
5899 Local<String> token = v8_str("<security token>");
5900 other->SetSecurityToken(token);
5901 current->SetSecurityToken(token);
5902
5903 // Setup reference from current to other.
5904 current->Global()->Set(v8_str("other"), other->Global());
5905
5906 // Trigger lazy loading in other context.
5907 Local<Script> script =
5908 Script::Compile(v8_str("other.eval('new Date(42)')"));
5909 Local<Value> value = script->Run();
5910 CHECK_EQ(42.0, value->NumberValue());
5911}
5912
5913
5914static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00005915 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00005916 if (args.IsConstructCall()) {
5917 if (args[0]->IsInt32()) {
5918 return v8_num(-args[0]->Int32Value());
5919 }
5920 }
5921
5922 return args[0];
5923}
5924
5925
5926// Test that a call handler can be set for objects which will allow
5927// non-function objects created through the API to be called as
5928// functions.
5929THREADED_TEST(CallAsFunction) {
5930 v8::HandleScope scope;
5931 LocalContext context;
5932
5933 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5934 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5935 instance_template->SetCallAsFunctionHandler(call_as_function);
5936 Local<v8::Object> instance = t->GetFunction()->NewInstance();
5937 context->Global()->Set(v8_str("obj"), instance);
5938 v8::TryCatch try_catch;
5939 Local<Value> value;
5940 CHECK(!try_catch.HasCaught());
5941
5942 value = CompileRun("obj(42)");
5943 CHECK(!try_catch.HasCaught());
5944 CHECK_EQ(42, value->Int32Value());
5945
5946 value = CompileRun("(function(o){return o(49)})(obj)");
5947 CHECK(!try_catch.HasCaught());
5948 CHECK_EQ(49, value->Int32Value());
5949
5950 // test special case of call as function
5951 value = CompileRun("[obj]['0'](45)");
5952 CHECK(!try_catch.HasCaught());
5953 CHECK_EQ(45, value->Int32Value());
5954
5955 value = CompileRun("obj.call = Function.prototype.call;"
5956 "obj.call(null, 87)");
5957 CHECK(!try_catch.HasCaught());
5958 CHECK_EQ(87, value->Int32Value());
5959
5960 // Regression tests for bug #1116356: Calling call through call/apply
5961 // must work for non-function receivers.
5962 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
5963 value = CompileRun(apply_99);
5964 CHECK(!try_catch.HasCaught());
5965 CHECK_EQ(99, value->Int32Value());
5966
5967 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
5968 value = CompileRun(call_17);
5969 CHECK(!try_catch.HasCaught());
5970 CHECK_EQ(17, value->Int32Value());
5971
5972 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00005973 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00005974 value = CompileRun("new obj(43)");
5975 CHECK(!try_catch.HasCaught());
5976 CHECK_EQ(-43, value->Int32Value());
5977}
5978
5979
5980static int CountHandles() {
5981 return v8::HandleScope::NumberOfHandles();
5982}
5983
5984
5985static int Recurse(int depth, int iterations) {
5986 v8::HandleScope scope;
5987 if (depth == 0) return CountHandles();
5988 for (int i = 0; i < iterations; i++) {
5989 Local<v8::Number> n = v8::Integer::New(42);
5990 }
5991 return Recurse(depth - 1, iterations);
5992}
5993
5994
5995THREADED_TEST(HandleIteration) {
5996 static const int kIterations = 500;
5997 static const int kNesting = 200;
5998 CHECK_EQ(0, CountHandles());
5999 {
6000 v8::HandleScope scope1;
6001 CHECK_EQ(0, CountHandles());
6002 for (int i = 0; i < kIterations; i++) {
6003 Local<v8::Number> n = v8::Integer::New(42);
6004 CHECK_EQ(i + 1, CountHandles());
6005 }
6006
6007 CHECK_EQ(kIterations, CountHandles());
6008 {
6009 v8::HandleScope scope2;
6010 for (int j = 0; j < kIterations; j++) {
6011 Local<v8::Number> n = v8::Integer::New(42);
6012 CHECK_EQ(j + 1 + kIterations, CountHandles());
6013 }
6014 }
6015 CHECK_EQ(kIterations, CountHandles());
6016 }
6017 CHECK_EQ(0, CountHandles());
6018 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6019}
6020
6021
6022static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6023 Local<String> name,
6024 const AccessorInfo& info) {
6025 ApiTestFuzzer::Fuzz();
6026 return v8::Handle<Value>();
6027}
6028
6029
6030THREADED_TEST(InterceptorHasOwnProperty) {
6031 v8::HandleScope scope;
6032 LocalContext context;
6033 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6034 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6035 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6036 Local<Function> function = fun_templ->GetFunction();
6037 context->Global()->Set(v8_str("constructor"), function);
6038 v8::Handle<Value> value = CompileRun(
6039 "var o = new constructor();"
6040 "o.hasOwnProperty('ostehaps');");
6041 CHECK_EQ(false, value->BooleanValue());
6042 value = CompileRun(
6043 "o.ostehaps = 42;"
6044 "o.hasOwnProperty('ostehaps');");
6045 CHECK_EQ(true, value->BooleanValue());
6046 value = CompileRun(
6047 "var p = new constructor();"
6048 "p.hasOwnProperty('ostehaps');");
6049 CHECK_EQ(false, value->BooleanValue());
6050}
6051
6052
6053static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6054 Local<String> name,
6055 const AccessorInfo& info) {
6056 ApiTestFuzzer::Fuzz();
6057 i::Heap::CollectAllGarbage(false);
6058 return v8::Handle<Value>();
6059}
6060
6061
6062THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6063 v8::HandleScope scope;
6064 LocalContext context;
6065 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6066 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6067 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6068 Local<Function> function = fun_templ->GetFunction();
6069 context->Global()->Set(v8_str("constructor"), function);
6070 // Let's first make some stuff so we can be sure to get a good GC.
6071 CompileRun(
6072 "function makestr(size) {"
6073 " switch (size) {"
6074 " case 1: return 'f';"
6075 " case 2: return 'fo';"
6076 " case 3: return 'foo';"
6077 " }"
6078 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6079 "}"
6080 "var x = makestr(12345);"
6081 "x = makestr(31415);"
6082 "x = makestr(23456);");
6083 v8::Handle<Value> value = CompileRun(
6084 "var o = new constructor();"
6085 "o.__proto__ = new String(x);"
6086 "o.hasOwnProperty('ostehaps');");
6087 CHECK_EQ(false, value->BooleanValue());
6088}
6089
6090
6091typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6092 const AccessorInfo& info);
6093
6094
6095static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6096 const char* source,
6097 int expected) {
6098 v8::HandleScope scope;
6099 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6100 templ->SetNamedPropertyHandler(getter);
6101 LocalContext context;
6102 context->Global()->Set(v8_str("o"), templ->NewInstance());
6103 v8::Handle<Value> value = CompileRun(source);
6104 CHECK_EQ(expected, value->Int32Value());
6105}
6106
6107
6108static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6109 const AccessorInfo& info) {
6110 ApiTestFuzzer::Fuzz();
6111 CHECK(v8_str("x")->Equals(name));
6112 return v8::Integer::New(42);
6113}
6114
6115
6116// This test should hit the load IC for the interceptor case.
6117THREADED_TEST(InterceptorLoadIC) {
6118 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6119 "var result = 0;"
6120 "for (var i = 0; i < 1000; i++) {"
6121 " result = o.x;"
6122 "}",
6123 42);
6124}
6125
6126
6127// Below go several tests which verify that JITing for various
6128// configurations of interceptor and explicit fields works fine
6129// (those cases are special cased to get better performance).
6130
6131static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6132 const AccessorInfo& info) {
6133 ApiTestFuzzer::Fuzz();
6134 return v8_str("x")->Equals(name)
6135 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6136}
6137
6138
6139THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6140 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6141 "var result = 0;"
6142 "o.y = 239;"
6143 "for (var i = 0; i < 1000; i++) {"
6144 " result = o.y;"
6145 "}",
6146 239);
6147}
6148
6149
6150THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6151 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6152 "var result = 0;"
6153 "o.__proto__ = { 'y': 239 };"
6154 "for (var i = 0; i < 1000; i++) {"
6155 " result = o.y + o.x;"
6156 "}",
6157 239 + 42);
6158}
6159
6160
6161THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6162 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6163 "var result = 0;"
6164 "o.__proto__.y = 239;"
6165 "for (var i = 0; i < 1000; i++) {"
6166 " result = o.y + o.x;"
6167 "}",
6168 239 + 42);
6169}
6170
6171
6172THREADED_TEST(InterceptorLoadICUndefined) {
6173 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6174 "var result = 0;"
6175 "for (var i = 0; i < 1000; i++) {"
6176 " result = (o.y == undefined) ? 239 : 42;"
6177 "}",
6178 239);
6179}
6180
6181
6182THREADED_TEST(InterceptorLoadICWithOverride) {
6183 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6184 "fst = new Object(); fst.__proto__ = o;"
6185 "snd = new Object(); snd.__proto__ = fst;"
6186 "var result1 = 0;"
6187 "for (var i = 0; i < 1000; i++) {"
6188 " result1 = snd.x;"
6189 "}"
6190 "fst.x = 239;"
6191 "var result = 0;"
6192 "for (var i = 0; i < 1000; i++) {"
6193 " result = snd.x;"
6194 "}"
6195 "result + result1",
6196 239 + 42);
6197}
6198
6199
6200// Test the case when we stored field into
6201// a stub, but interceptor produced value on its own.
6202THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6203 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6204 "proto = new Object();"
6205 "o.__proto__ = proto;"
6206 "proto.x = 239;"
6207 "for (var i = 0; i < 1000; i++) {"
6208 " o.x;"
6209 // Now it should be ICed and keep a reference to x defined on proto
6210 "}"
6211 "var result = 0;"
6212 "for (var i = 0; i < 1000; i++) {"
6213 " result += o.x;"
6214 "}"
6215 "result;",
6216 42 * 1000);
6217}
6218
6219
6220// Test the case when we stored field into
6221// a stub, but it got invalidated later on.
6222THREADED_TEST(InterceptorLoadICInvalidatedField) {
6223 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6224 "proto1 = new Object();"
6225 "proto2 = new Object();"
6226 "o.__proto__ = proto1;"
6227 "proto1.__proto__ = proto2;"
6228 "proto2.y = 239;"
6229 "for (var i = 0; i < 1000; i++) {"
6230 " o.y;"
6231 // Now it should be ICed and keep a reference to y defined on proto2
6232 "}"
6233 "proto1.y = 42;"
6234 "var result = 0;"
6235 "for (var i = 0; i < 1000; i++) {"
6236 " result += o.y;"
6237 "}"
6238 "result;",
6239 42 * 1000);
6240}
6241
6242
Steve Block6ded16b2010-05-10 14:33:55 +01006243static int interceptor_load_not_handled_calls = 0;
6244static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6245 const AccessorInfo& info) {
6246 ++interceptor_load_not_handled_calls;
6247 return v8::Handle<v8::Value>();
6248}
6249
6250
6251// Test how post-interceptor lookups are done in the non-cacheable
6252// case: the interceptor should not be invoked during this lookup.
6253THREADED_TEST(InterceptorLoadICPostInterceptor) {
6254 interceptor_load_not_handled_calls = 0;
6255 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6256 "receiver = new Object();"
6257 "receiver.__proto__ = o;"
6258 "proto = new Object();"
6259 "/* Make proto a slow-case object. */"
6260 "for (var i = 0; i < 1000; i++) {"
6261 " proto[\"xxxxxxxx\" + i] = [];"
6262 "}"
6263 "proto.x = 17;"
6264 "o.__proto__ = proto;"
6265 "var result = 0;"
6266 "for (var i = 0; i < 1000; i++) {"
6267 " result += receiver.x;"
6268 "}"
6269 "result;",
6270 17 * 1000);
6271 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6272}
6273
6274
Steve Blocka7e24c12009-10-30 11:49:00 +00006275// Test the case when we stored field into
6276// a stub, but it got invalidated later on due to override on
6277// global object which is between interceptor and fields' holders.
6278THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6279 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6280 "o.__proto__ = this;" // set a global to be a proto of o.
6281 "this.__proto__.y = 239;"
6282 "for (var i = 0; i < 10; i++) {"
6283 " if (o.y != 239) throw 'oops: ' + o.y;"
6284 // Now it should be ICed and keep a reference to y defined on field_holder.
6285 "}"
6286 "this.y = 42;" // Assign on a global.
6287 "var result = 0;"
6288 "for (var i = 0; i < 10; i++) {"
6289 " result += o.y;"
6290 "}"
6291 "result;",
6292 42 * 10);
6293}
6294
6295
6296static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
6297 ApiTestFuzzer::Fuzz();
6298 return v8_num(239);
6299}
6300
6301
6302static void SetOnThis(Local<String> name,
6303 Local<Value> value,
6304 const AccessorInfo& info) {
6305 info.This()->ForceSet(name, value);
6306}
6307
6308
6309THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6310 v8::HandleScope scope;
6311 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6312 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6313 templ->SetAccessor(v8_str("y"), Return239);
6314 LocalContext context;
6315 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006316
6317 // Check the case when receiver and interceptor's holder
6318 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006319 v8::Handle<Value> value = CompileRun(
6320 "var result = 0;"
6321 "for (var i = 0; i < 7; i++) {"
6322 " result = o.y;"
6323 "}");
6324 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006325
6326 // Check the case when interceptor's holder is in proto chain
6327 // of receiver.
6328 value = CompileRun(
6329 "r = { __proto__: o };"
6330 "var result = 0;"
6331 "for (var i = 0; i < 7; i++) {"
6332 " result = r.y;"
6333 "}");
6334 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006335}
6336
6337
6338THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6339 v8::HandleScope scope;
6340 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6341 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6342 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6343 templ_p->SetAccessor(v8_str("y"), Return239);
6344
6345 LocalContext context;
6346 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6347 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6348
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006349 // Check the case when receiver and interceptor's holder
6350 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006351 v8::Handle<Value> value = CompileRun(
6352 "o.__proto__ = p;"
6353 "var result = 0;"
6354 "for (var i = 0; i < 7; i++) {"
6355 " result = o.x + o.y;"
6356 "}");
6357 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006358
6359 // Check the case when interceptor's holder is in proto chain
6360 // of receiver.
6361 value = CompileRun(
6362 "r = { __proto__: o };"
6363 "var result = 0;"
6364 "for (var i = 0; i < 7; i++) {"
6365 " result = r.x + r.y;"
6366 "}");
6367 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006368}
6369
6370
6371THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6372 v8::HandleScope scope;
6373 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6374 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6375 templ->SetAccessor(v8_str("y"), Return239);
6376
6377 LocalContext context;
6378 context->Global()->Set(v8_str("o"), templ->NewInstance());
6379
6380 v8::Handle<Value> value = CompileRun(
6381 "fst = new Object(); fst.__proto__ = o;"
6382 "snd = new Object(); snd.__proto__ = fst;"
6383 "var result1 = 0;"
6384 "for (var i = 0; i < 7; i++) {"
6385 " result1 = snd.x;"
6386 "}"
6387 "fst.x = 239;"
6388 "var result = 0;"
6389 "for (var i = 0; i < 7; i++) {"
6390 " result = snd.x;"
6391 "}"
6392 "result + result1");
6393 CHECK_EQ(239 + 42, value->Int32Value());
6394}
6395
6396
6397// Test the case when we stored callback into
6398// a stub, but interceptor produced value on its own.
6399THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6400 v8::HandleScope scope;
6401 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6402 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6403 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6404 templ_p->SetAccessor(v8_str("y"), Return239);
6405
6406 LocalContext context;
6407 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6408 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6409
6410 v8::Handle<Value> value = CompileRun(
6411 "o.__proto__ = p;"
6412 "for (var i = 0; i < 7; i++) {"
6413 " o.x;"
6414 // Now it should be ICed and keep a reference to x defined on p
6415 "}"
6416 "var result = 0;"
6417 "for (var i = 0; i < 7; i++) {"
6418 " result += o.x;"
6419 "}"
6420 "result");
6421 CHECK_EQ(42 * 7, value->Int32Value());
6422}
6423
6424
6425// Test the case when we stored callback into
6426// a stub, but it got invalidated later on.
6427THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6428 v8::HandleScope scope;
6429 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6430 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6431 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6432 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6433
6434 LocalContext context;
6435 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6436 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6437
6438 v8::Handle<Value> value = CompileRun(
6439 "inbetween = new Object();"
6440 "o.__proto__ = inbetween;"
6441 "inbetween.__proto__ = p;"
6442 "for (var i = 0; i < 10; i++) {"
6443 " o.y;"
6444 // Now it should be ICed and keep a reference to y defined on p
6445 "}"
6446 "inbetween.y = 42;"
6447 "var result = 0;"
6448 "for (var i = 0; i < 10; i++) {"
6449 " result += o.y;"
6450 "}"
6451 "result");
6452 CHECK_EQ(42 * 10, value->Int32Value());
6453}
6454
6455
6456// Test the case when we stored callback into
6457// a stub, but it got invalidated later on due to override on
6458// global object which is between interceptor and callbacks' holders.
6459THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6460 v8::HandleScope scope;
6461 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6462 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6463 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6464 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6465
6466 LocalContext context;
6467 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6468 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6469
6470 v8::Handle<Value> value = CompileRun(
6471 "o.__proto__ = this;"
6472 "this.__proto__ = p;"
6473 "for (var i = 0; i < 10; i++) {"
6474 " if (o.y != 239) throw 'oops: ' + o.y;"
6475 // Now it should be ICed and keep a reference to y defined on p
6476 "}"
6477 "this.y = 42;"
6478 "var result = 0;"
6479 "for (var i = 0; i < 10; i++) {"
6480 " result += o.y;"
6481 "}"
6482 "result");
6483 CHECK_EQ(42 * 10, value->Int32Value());
6484}
6485
6486
6487static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6488 const AccessorInfo& info) {
6489 ApiTestFuzzer::Fuzz();
6490 CHECK(v8_str("x")->Equals(name));
6491 return v8::Integer::New(0);
6492}
6493
6494
6495THREADED_TEST(InterceptorReturningZero) {
6496 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6497 "o.x == undefined ? 1 : 0",
6498 0);
6499}
6500
6501
6502static v8::Handle<Value> InterceptorStoreICSetter(
6503 Local<String> key, Local<Value> value, const AccessorInfo&) {
6504 CHECK(v8_str("x")->Equals(key));
6505 CHECK_EQ(42, value->Int32Value());
6506 return value;
6507}
6508
6509
6510// This test should hit the store IC for the interceptor case.
6511THREADED_TEST(InterceptorStoreIC) {
6512 v8::HandleScope scope;
6513 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6514 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6515 InterceptorStoreICSetter);
6516 LocalContext context;
6517 context->Global()->Set(v8_str("o"), templ->NewInstance());
6518 v8::Handle<Value> value = CompileRun(
6519 "for (var i = 0; i < 1000; i++) {"
6520 " o.x = 42;"
6521 "}");
6522}
6523
6524
6525THREADED_TEST(InterceptorStoreICWithNoSetter) {
6526 v8::HandleScope scope;
6527 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6528 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6529 LocalContext context;
6530 context->Global()->Set(v8_str("o"), templ->NewInstance());
6531 v8::Handle<Value> value = CompileRun(
6532 "for (var i = 0; i < 1000; i++) {"
6533 " o.y = 239;"
6534 "}"
6535 "42 + o.y");
6536 CHECK_EQ(239 + 42, value->Int32Value());
6537}
6538
6539
6540
6541
6542v8::Handle<Value> call_ic_function;
6543v8::Handle<Value> call_ic_function2;
6544v8::Handle<Value> call_ic_function3;
6545
6546static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6547 const AccessorInfo& info) {
6548 ApiTestFuzzer::Fuzz();
6549 CHECK(v8_str("x")->Equals(name));
6550 return call_ic_function;
6551}
6552
6553
6554// This test should hit the call IC for the interceptor case.
6555THREADED_TEST(InterceptorCallIC) {
6556 v8::HandleScope scope;
6557 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6558 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6559 LocalContext context;
6560 context->Global()->Set(v8_str("o"), templ->NewInstance());
6561 call_ic_function =
6562 v8_compile("function f(x) { return x + 1; }; f")->Run();
6563 v8::Handle<Value> value = CompileRun(
6564 "var result = 0;"
6565 "for (var i = 0; i < 1000; i++) {"
6566 " result = o.x(41);"
6567 "}");
6568 CHECK_EQ(42, value->Int32Value());
6569}
6570
6571
6572// This test checks that if interceptor doesn't provide
6573// a value, we can fetch regular value.
6574THREADED_TEST(InterceptorCallICSeesOthers) {
6575 v8::HandleScope scope;
6576 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6577 templ->SetNamedPropertyHandler(NoBlockGetterX);
6578 LocalContext context;
6579 context->Global()->Set(v8_str("o"), templ->NewInstance());
6580 v8::Handle<Value> value = CompileRun(
6581 "o.x = function f(x) { return x + 1; };"
6582 "var result = 0;"
6583 "for (var i = 0; i < 7; i++) {"
6584 " result = o.x(41);"
6585 "}");
6586 CHECK_EQ(42, value->Int32Value());
6587}
6588
6589
6590static v8::Handle<Value> call_ic_function4;
6591static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6592 const AccessorInfo& info) {
6593 ApiTestFuzzer::Fuzz();
6594 CHECK(v8_str("x")->Equals(name));
6595 return call_ic_function4;
6596}
6597
6598
6599// This test checks that if interceptor provides a function,
6600// even if we cached shadowed variant, interceptor's function
6601// is invoked
6602THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6603 v8::HandleScope scope;
6604 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6605 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6606 LocalContext context;
6607 context->Global()->Set(v8_str("o"), templ->NewInstance());
6608 call_ic_function4 =
6609 v8_compile("function f(x) { return x - 1; }; f")->Run();
6610 v8::Handle<Value> value = CompileRun(
6611 "o.__proto__.x = function(x) { return x + 1; };"
6612 "var result = 0;"
6613 "for (var i = 0; i < 1000; i++) {"
6614 " result = o.x(42);"
6615 "}");
6616 CHECK_EQ(41, value->Int32Value());
6617}
6618
6619
6620// Test the case when we stored cacheable lookup into
6621// a stub, but it got invalidated later on
6622THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6623 v8::HandleScope scope;
6624 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6625 templ->SetNamedPropertyHandler(NoBlockGetterX);
6626 LocalContext context;
6627 context->Global()->Set(v8_str("o"), templ->NewInstance());
6628 v8::Handle<Value> value = CompileRun(
6629 "proto1 = new Object();"
6630 "proto2 = new Object();"
6631 "o.__proto__ = proto1;"
6632 "proto1.__proto__ = proto2;"
6633 "proto2.y = function(x) { return x + 1; };"
6634 // Invoke it many times to compile a stub
6635 "for (var i = 0; i < 7; i++) {"
6636 " o.y(42);"
6637 "}"
6638 "proto1.y = function(x) { return x - 1; };"
6639 "var result = 0;"
6640 "for (var i = 0; i < 7; i++) {"
6641 " result += o.y(42);"
6642 "}");
6643 CHECK_EQ(41 * 7, value->Int32Value());
6644}
6645
6646
6647static v8::Handle<Value> call_ic_function5;
6648static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6649 const AccessorInfo& info) {
6650 ApiTestFuzzer::Fuzz();
6651 if (v8_str("x")->Equals(name))
6652 return call_ic_function5;
6653 else
6654 return Local<Value>();
6655}
6656
6657
6658// This test checks that if interceptor doesn't provide a function,
6659// cached constant function is used
6660THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6661 v8::HandleScope scope;
6662 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6663 templ->SetNamedPropertyHandler(NoBlockGetterX);
6664 LocalContext context;
6665 context->Global()->Set(v8_str("o"), templ->NewInstance());
6666 v8::Handle<Value> value = CompileRun(
6667 "function inc(x) { return x + 1; };"
6668 "inc(1);"
6669 "o.x = inc;"
6670 "var result = 0;"
6671 "for (var i = 0; i < 1000; i++) {"
6672 " result = o.x(42);"
6673 "}");
6674 CHECK_EQ(43, value->Int32Value());
6675}
6676
6677
6678// This test checks that if interceptor provides a function,
6679// even if we cached constant function, interceptor's function
6680// is invoked
6681THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6682 v8::HandleScope scope;
6683 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6684 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6685 LocalContext context;
6686 context->Global()->Set(v8_str("o"), templ->NewInstance());
6687 call_ic_function5 =
6688 v8_compile("function f(x) { return x - 1; }; f")->Run();
6689 v8::Handle<Value> value = CompileRun(
6690 "function inc(x) { return x + 1; };"
6691 "inc(1);"
6692 "o.x = inc;"
6693 "var result = 0;"
6694 "for (var i = 0; i < 1000; i++) {"
6695 " result = o.x(42);"
6696 "}");
6697 CHECK_EQ(41, value->Int32Value());
6698}
6699
6700
6701// Test the case when we stored constant function into
6702// a stub, but it got invalidated later on
6703THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6704 v8::HandleScope scope;
6705 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6706 templ->SetNamedPropertyHandler(NoBlockGetterX);
6707 LocalContext context;
6708 context->Global()->Set(v8_str("o"), templ->NewInstance());
6709 v8::Handle<Value> value = CompileRun(
6710 "function inc(x) { return x + 1; };"
6711 "inc(1);"
6712 "proto1 = new Object();"
6713 "proto2 = new Object();"
6714 "o.__proto__ = proto1;"
6715 "proto1.__proto__ = proto2;"
6716 "proto2.y = inc;"
6717 // Invoke it many times to compile a stub
6718 "for (var i = 0; i < 7; i++) {"
6719 " o.y(42);"
6720 "}"
6721 "proto1.y = function(x) { return x - 1; };"
6722 "var result = 0;"
6723 "for (var i = 0; i < 7; i++) {"
6724 " result += o.y(42);"
6725 "}");
6726 CHECK_EQ(41 * 7, value->Int32Value());
6727}
6728
6729
6730// Test the case when we stored constant function into
6731// a stub, but it got invalidated later on due to override on
6732// global object which is between interceptor and constant function' holders.
6733THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6734 v8::HandleScope scope;
6735 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6736 templ->SetNamedPropertyHandler(NoBlockGetterX);
6737 LocalContext context;
6738 context->Global()->Set(v8_str("o"), templ->NewInstance());
6739 v8::Handle<Value> value = CompileRun(
6740 "function inc(x) { return x + 1; };"
6741 "inc(1);"
6742 "o.__proto__ = this;"
6743 "this.__proto__.y = inc;"
6744 // Invoke it many times to compile a stub
6745 "for (var i = 0; i < 7; i++) {"
6746 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6747 "}"
6748 "this.y = function(x) { return x - 1; };"
6749 "var result = 0;"
6750 "for (var i = 0; i < 7; i++) {"
6751 " result += o.y(42);"
6752 "}");
6753 CHECK_EQ(41 * 7, value->Int32Value());
6754}
6755
6756
Leon Clarke4515c472010-02-03 11:58:03 +00006757// Test the case when actual function to call sits on global object.
6758THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6759 v8::HandleScope scope;
6760 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6761 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6762
6763 LocalContext context;
6764 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6765
6766 v8::Handle<Value> value = CompileRun(
6767 "try {"
6768 " o.__proto__ = this;"
6769 " for (var i = 0; i < 10; i++) {"
6770 " var v = o.parseFloat('239');"
6771 " if (v != 239) throw v;"
6772 // Now it should be ICed and keep a reference to parseFloat.
6773 " }"
6774 " var result = 0;"
6775 " for (var i = 0; i < 10; i++) {"
6776 " result += o.parseFloat('239');"
6777 " }"
6778 " result"
6779 "} catch(e) {"
6780 " e"
6781 "};");
6782 CHECK_EQ(239 * 10, value->Int32Value());
6783}
6784
Andrei Popescu402d9372010-02-26 13:31:12 +00006785static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6786 const AccessorInfo& info) {
6787 ApiTestFuzzer::Fuzz();
6788 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6789 ++(*call_count);
6790 if ((*call_count) % 20 == 0) {
6791 v8::internal::Heap::CollectAllGarbage(true);
6792 }
6793 return v8::Handle<Value>();
6794}
6795
6796static v8::Handle<Value> FastApiCallback_TrivialSignature(
6797 const v8::Arguments& args) {
6798 ApiTestFuzzer::Fuzz();
6799 CHECK_EQ(args.This(), args.Holder());
6800 CHECK(args.Data()->Equals(v8_str("method_data")));
6801 return v8::Integer::New(args[0]->Int32Value() + 1);
6802}
6803
6804static v8::Handle<Value> FastApiCallback_SimpleSignature(
6805 const v8::Arguments& args) {
6806 ApiTestFuzzer::Fuzz();
6807 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6808 CHECK(args.Data()->Equals(v8_str("method_data")));
6809 // Note, we're using HasRealNamedProperty instead of Has to avoid
6810 // invoking the interceptor again.
6811 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6812 return v8::Integer::New(args[0]->Int32Value() + 1);
6813}
6814
6815// Helper to maximize the odds of object moving.
6816static void GenerateSomeGarbage() {
6817 CompileRun(
6818 "var garbage;"
6819 "for (var i = 0; i < 1000; i++) {"
6820 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6821 "}"
6822 "garbage = undefined;");
6823}
6824
6825THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
6826 int interceptor_call_count = 0;
6827 v8::HandleScope scope;
6828 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6829 v8::Handle<v8::FunctionTemplate> method_templ =
6830 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6831 v8_str("method_data"),
6832 v8::Handle<v8::Signature>());
6833 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6834 proto_templ->Set(v8_str("method"), method_templ);
6835 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6836 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6837 NULL, NULL, NULL, NULL,
6838 v8::External::Wrap(&interceptor_call_count));
6839 LocalContext context;
6840 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6841 GenerateSomeGarbage();
6842 context->Global()->Set(v8_str("o"), fun->NewInstance());
6843 v8::Handle<Value> value = CompileRun(
6844 "var result = 0;"
6845 "for (var i = 0; i < 100; i++) {"
6846 " result = o.method(41);"
6847 "}");
6848 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6849 CHECK_EQ(100, interceptor_call_count);
6850}
6851
6852THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
6853 int interceptor_call_count = 0;
6854 v8::HandleScope scope;
6855 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6856 v8::Handle<v8::FunctionTemplate> method_templ =
6857 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6858 v8_str("method_data"),
6859 v8::Signature::New(fun_templ));
6860 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6861 proto_templ->Set(v8_str("method"), method_templ);
6862 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6863 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6864 NULL, NULL, NULL, NULL,
6865 v8::External::Wrap(&interceptor_call_count));
6866 LocalContext context;
6867 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6868 GenerateSomeGarbage();
6869 context->Global()->Set(v8_str("o"), fun->NewInstance());
6870 v8::Handle<Value> value = CompileRun(
6871 "o.foo = 17;"
6872 "var receiver = {};"
6873 "receiver.__proto__ = o;"
6874 "var result = 0;"
6875 "for (var i = 0; i < 100; i++) {"
6876 " result = receiver.method(41);"
6877 "}");
6878 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6879 CHECK_EQ(100, interceptor_call_count);
6880}
6881
6882THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
6883 int interceptor_call_count = 0;
6884 v8::HandleScope scope;
6885 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6886 v8::Handle<v8::FunctionTemplate> method_templ =
6887 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6888 v8_str("method_data"),
6889 v8::Signature::New(fun_templ));
6890 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6891 proto_templ->Set(v8_str("method"), method_templ);
6892 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6893 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6894 NULL, NULL, NULL, NULL,
6895 v8::External::Wrap(&interceptor_call_count));
6896 LocalContext context;
6897 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6898 GenerateSomeGarbage();
6899 context->Global()->Set(v8_str("o"), fun->NewInstance());
6900 v8::Handle<Value> value = CompileRun(
6901 "o.foo = 17;"
6902 "var receiver = {};"
6903 "receiver.__proto__ = o;"
6904 "var result = 0;"
6905 "var saved_result = 0;"
6906 "for (var i = 0; i < 100; i++) {"
6907 " result = receiver.method(41);"
6908 " if (i == 50) {"
6909 " saved_result = result;"
6910 " receiver = {method: function(x) { return x - 1 }};"
6911 " }"
6912 "}");
6913 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6914 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6915 CHECK_GE(interceptor_call_count, 50);
6916}
6917
6918THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
6919 int interceptor_call_count = 0;
6920 v8::HandleScope scope;
6921 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6922 v8::Handle<v8::FunctionTemplate> method_templ =
6923 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6924 v8_str("method_data"),
6925 v8::Signature::New(fun_templ));
6926 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6927 proto_templ->Set(v8_str("method"), method_templ);
6928 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6929 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6930 NULL, NULL, NULL, NULL,
6931 v8::External::Wrap(&interceptor_call_count));
6932 LocalContext context;
6933 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6934 GenerateSomeGarbage();
6935 context->Global()->Set(v8_str("o"), fun->NewInstance());
6936 v8::Handle<Value> value = CompileRun(
6937 "o.foo = 17;"
6938 "var receiver = {};"
6939 "receiver.__proto__ = o;"
6940 "var result = 0;"
6941 "var saved_result = 0;"
6942 "for (var i = 0; i < 100; i++) {"
6943 " result = receiver.method(41);"
6944 " if (i == 50) {"
6945 " saved_result = result;"
6946 " o.method = function(x) { return x - 1 };"
6947 " }"
6948 "}");
6949 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6950 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6951 CHECK_GE(interceptor_call_count, 50);
6952}
6953
Steve Block6ded16b2010-05-10 14:33:55 +01006954THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
6955 int interceptor_call_count = 0;
6956 v8::HandleScope scope;
6957 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6958 v8::Handle<v8::FunctionTemplate> method_templ =
6959 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6960 v8_str("method_data"),
6961 v8::Signature::New(fun_templ));
6962 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6963 proto_templ->Set(v8_str("method"), method_templ);
6964 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6965 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6966 NULL, NULL, NULL, NULL,
6967 v8::External::Wrap(&interceptor_call_count));
6968 LocalContext context;
6969 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6970 GenerateSomeGarbage();
6971 context->Global()->Set(v8_str("o"), fun->NewInstance());
6972 v8::TryCatch try_catch;
6973 v8::Handle<Value> value = CompileRun(
6974 "o.foo = 17;"
6975 "var receiver = {};"
6976 "receiver.__proto__ = o;"
6977 "var result = 0;"
6978 "var saved_result = 0;"
6979 "for (var i = 0; i < 100; i++) {"
6980 " result = receiver.method(41);"
6981 " if (i == 50) {"
6982 " saved_result = result;"
6983 " receiver = 333;"
6984 " }"
6985 "}");
6986 CHECK(try_catch.HasCaught());
6987 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
6988 try_catch.Exception()->ToString());
6989 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6990 CHECK_GE(interceptor_call_count, 50);
6991}
6992
Andrei Popescu402d9372010-02-26 13:31:12 +00006993THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
6994 int interceptor_call_count = 0;
6995 v8::HandleScope scope;
6996 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6997 v8::Handle<v8::FunctionTemplate> method_templ =
6998 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6999 v8_str("method_data"),
7000 v8::Signature::New(fun_templ));
7001 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7002 proto_templ->Set(v8_str("method"), method_templ);
7003 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7004 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7005 NULL, NULL, NULL, NULL,
7006 v8::External::Wrap(&interceptor_call_count));
7007 LocalContext context;
7008 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7009 GenerateSomeGarbage();
7010 context->Global()->Set(v8_str("o"), fun->NewInstance());
7011 v8::TryCatch try_catch;
7012 v8::Handle<Value> value = CompileRun(
7013 "o.foo = 17;"
7014 "var receiver = {};"
7015 "receiver.__proto__ = o;"
7016 "var result = 0;"
7017 "var saved_result = 0;"
7018 "for (var i = 0; i < 100; i++) {"
7019 " result = receiver.method(41);"
7020 " if (i == 50) {"
7021 " saved_result = result;"
7022 " receiver = {method: receiver.method};"
7023 " }"
7024 "}");
7025 CHECK(try_catch.HasCaught());
7026 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7027 try_catch.Exception()->ToString());
7028 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7029 CHECK_GE(interceptor_call_count, 50);
7030}
7031
7032THREADED_TEST(CallICFastApi_TrivialSignature) {
7033 v8::HandleScope scope;
7034 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7035 v8::Handle<v8::FunctionTemplate> method_templ =
7036 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7037 v8_str("method_data"),
7038 v8::Handle<v8::Signature>());
7039 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7040 proto_templ->Set(v8_str("method"), method_templ);
7041 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7042 LocalContext context;
7043 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7044 GenerateSomeGarbage();
7045 context->Global()->Set(v8_str("o"), fun->NewInstance());
7046 v8::Handle<Value> value = CompileRun(
7047 "var result = 0;"
7048 "for (var i = 0; i < 100; i++) {"
7049 " result = o.method(41);"
7050 "}");
7051
7052 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7053}
7054
7055THREADED_TEST(CallICFastApi_SimpleSignature) {
7056 v8::HandleScope scope;
7057 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7058 v8::Handle<v8::FunctionTemplate> method_templ =
7059 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7060 v8_str("method_data"),
7061 v8::Signature::New(fun_templ));
7062 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7063 proto_templ->Set(v8_str("method"), method_templ);
7064 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7065 LocalContext context;
7066 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7067 GenerateSomeGarbage();
7068 context->Global()->Set(v8_str("o"), fun->NewInstance());
7069 v8::Handle<Value> value = CompileRun(
7070 "o.foo = 17;"
7071 "var receiver = {};"
7072 "receiver.__proto__ = o;"
7073 "var result = 0;"
7074 "for (var i = 0; i < 100; i++) {"
7075 " result = receiver.method(41);"
7076 "}");
7077
7078 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7079}
7080
Steve Block6ded16b2010-05-10 14:33:55 +01007081THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007082 v8::HandleScope scope;
7083 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7084 v8::Handle<v8::FunctionTemplate> method_templ =
7085 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7086 v8_str("method_data"),
7087 v8::Signature::New(fun_templ));
7088 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7089 proto_templ->Set(v8_str("method"), method_templ);
7090 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7091 LocalContext context;
7092 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7093 GenerateSomeGarbage();
7094 context->Global()->Set(v8_str("o"), fun->NewInstance());
7095 v8::Handle<Value> value = CompileRun(
7096 "o.foo = 17;"
7097 "var receiver = {};"
7098 "receiver.__proto__ = o;"
7099 "var result = 0;"
7100 "var saved_result = 0;"
7101 "for (var i = 0; i < 100; i++) {"
7102 " result = receiver.method(41);"
7103 " if (i == 50) {"
7104 " saved_result = result;"
7105 " receiver = {method: function(x) { return x - 1 }};"
7106 " }"
7107 "}");
7108 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7109 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7110}
7111
Steve Block6ded16b2010-05-10 14:33:55 +01007112THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7113 v8::HandleScope scope;
7114 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7115 v8::Handle<v8::FunctionTemplate> method_templ =
7116 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7117 v8_str("method_data"),
7118 v8::Signature::New(fun_templ));
7119 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7120 proto_templ->Set(v8_str("method"), method_templ);
7121 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7122 LocalContext context;
7123 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7124 GenerateSomeGarbage();
7125 context->Global()->Set(v8_str("o"), fun->NewInstance());
7126 v8::TryCatch try_catch;
7127 v8::Handle<Value> value = CompileRun(
7128 "o.foo = 17;"
7129 "var receiver = {};"
7130 "receiver.__proto__ = o;"
7131 "var result = 0;"
7132 "var saved_result = 0;"
7133 "for (var i = 0; i < 100; i++) {"
7134 " result = receiver.method(41);"
7135 " if (i == 50) {"
7136 " saved_result = result;"
7137 " receiver = 333;"
7138 " }"
7139 "}");
7140 CHECK(try_catch.HasCaught());
7141 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7142 try_catch.Exception()->ToString());
7143 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7144}
7145
Leon Clarke4515c472010-02-03 11:58:03 +00007146
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007147v8::Handle<Value> keyed_call_ic_function;
7148
7149static v8::Handle<Value> InterceptorKeyedCallICGetter(
7150 Local<String> name, const AccessorInfo& info) {
7151 ApiTestFuzzer::Fuzz();
7152 if (v8_str("x")->Equals(name)) {
7153 return keyed_call_ic_function;
7154 }
7155 return v8::Handle<Value>();
7156}
7157
7158
7159// Test the case when we stored cacheable lookup into
7160// a stub, but the function name changed (to another cacheable function).
7161THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7162 v8::HandleScope scope;
7163 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7164 templ->SetNamedPropertyHandler(NoBlockGetterX);
7165 LocalContext context;
7166 context->Global()->Set(v8_str("o"), templ->NewInstance());
7167 v8::Handle<Value> value = CompileRun(
7168 "proto = new Object();"
7169 "proto.y = function(x) { return x + 1; };"
7170 "proto.z = function(x) { return x - 1; };"
7171 "o.__proto__ = proto;"
7172 "var result = 0;"
7173 "var method = 'y';"
7174 "for (var i = 0; i < 10; i++) {"
7175 " if (i == 5) { method = 'z'; };"
7176 " result += o[method](41);"
7177 "}");
7178 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7179}
7180
7181
7182// Test the case when we stored cacheable lookup into
7183// a stub, but the function name changed (and the new function is present
7184// both before and after the interceptor in the prototype chain).
7185THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7186 v8::HandleScope scope;
7187 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7188 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7189 LocalContext context;
7190 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7191 keyed_call_ic_function =
7192 v8_compile("function f(x) { return x - 1; }; f")->Run();
7193 v8::Handle<Value> value = CompileRun(
7194 "o = new Object();"
7195 "proto2 = new Object();"
7196 "o.y = function(x) { return x + 1; };"
7197 "proto2.y = function(x) { return x + 2; };"
7198 "o.__proto__ = proto1;"
7199 "proto1.__proto__ = proto2;"
7200 "var result = 0;"
7201 "var method = 'x';"
7202 "for (var i = 0; i < 10; i++) {"
7203 " if (i == 5) { method = 'y'; };"
7204 " result += o[method](41);"
7205 "}");
7206 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7207}
7208
7209
7210// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7211// on the global object.
7212THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7213 v8::HandleScope scope;
7214 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7215 templ->SetNamedPropertyHandler(NoBlockGetterX);
7216 LocalContext context;
7217 context->Global()->Set(v8_str("o"), templ->NewInstance());
7218 v8::Handle<Value> value = CompileRun(
7219 "function inc(x) { return x + 1; };"
7220 "inc(1);"
7221 "function dec(x) { return x - 1; };"
7222 "dec(1);"
7223 "o.__proto__ = this;"
7224 "this.__proto__.x = inc;"
7225 "this.__proto__.y = dec;"
7226 "var result = 0;"
7227 "var method = 'x';"
7228 "for (var i = 0; i < 10; i++) {"
7229 " if (i == 5) { method = 'y'; };"
7230 " result += o[method](41);"
7231 "}");
7232 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7233}
7234
7235
7236// Test the case when actual function to call sits on global object.
7237THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7238 v8::HandleScope scope;
7239 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7240 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7241 LocalContext context;
7242 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7243
7244 v8::Handle<Value> value = CompileRun(
7245 "function len(x) { return x.length; };"
7246 "o.__proto__ = this;"
7247 "var m = 'parseFloat';"
7248 "var result = 0;"
7249 "for (var i = 0; i < 10; i++) {"
7250 " if (i == 5) {"
7251 " m = 'len';"
7252 " saved_result = result;"
7253 " };"
7254 " result = o[m]('239');"
7255 "}");
7256 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7257 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7258}
7259
7260// Test the map transition before the interceptor.
7261THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7262 v8::HandleScope scope;
7263 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7264 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7265 LocalContext context;
7266 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7267
7268 v8::Handle<Value> value = CompileRun(
7269 "var o = new Object();"
7270 "o.__proto__ = proto;"
7271 "o.method = function(x) { return x + 1; };"
7272 "var m = 'method';"
7273 "var result = 0;"
7274 "for (var i = 0; i < 10; i++) {"
7275 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7276 " result += o[m](41);"
7277 "}");
7278 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7279}
7280
7281
7282// Test the map transition after the interceptor.
7283THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7284 v8::HandleScope scope;
7285 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7286 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7287 LocalContext context;
7288 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7289
7290 v8::Handle<Value> value = CompileRun(
7291 "var proto = new Object();"
7292 "o.__proto__ = proto;"
7293 "proto.method = function(x) { return x + 1; };"
7294 "var m = 'method';"
7295 "var result = 0;"
7296 "for (var i = 0; i < 10; i++) {"
7297 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7298 " result += o[m](41);"
7299 "}");
7300 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7301}
7302
7303
Steve Blocka7e24c12009-10-30 11:49:00 +00007304static int interceptor_call_count = 0;
7305
7306static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7307 const AccessorInfo& info) {
7308 ApiTestFuzzer::Fuzz();
7309 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7310 return call_ic_function2;
7311 }
7312 return v8::Handle<Value>();
7313}
7314
7315
7316// This test should hit load and call ICs for the interceptor case.
7317// Once in a while, the interceptor will reply that a property was not
7318// found in which case we should get a reference error.
7319THREADED_TEST(InterceptorICReferenceErrors) {
7320 v8::HandleScope scope;
7321 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7322 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7323 LocalContext context(0, templ, v8::Handle<Value>());
7324 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7325 v8::Handle<Value> value = CompileRun(
7326 "function f() {"
7327 " for (var i = 0; i < 1000; i++) {"
7328 " try { x; } catch(e) { return true; }"
7329 " }"
7330 " return false;"
7331 "};"
7332 "f();");
7333 CHECK_EQ(true, value->BooleanValue());
7334 interceptor_call_count = 0;
7335 value = CompileRun(
7336 "function g() {"
7337 " for (var i = 0; i < 1000; i++) {"
7338 " try { x(42); } catch(e) { return true; }"
7339 " }"
7340 " return false;"
7341 "};"
7342 "g();");
7343 CHECK_EQ(true, value->BooleanValue());
7344}
7345
7346
7347static int interceptor_ic_exception_get_count = 0;
7348
7349static v8::Handle<Value> InterceptorICExceptionGetter(
7350 Local<String> name,
7351 const AccessorInfo& info) {
7352 ApiTestFuzzer::Fuzz();
7353 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7354 return call_ic_function3;
7355 }
7356 if (interceptor_ic_exception_get_count == 20) {
7357 return v8::ThrowException(v8_num(42));
7358 }
7359 // Do not handle get for properties other than x.
7360 return v8::Handle<Value>();
7361}
7362
7363// Test interceptor load/call IC where the interceptor throws an
7364// exception once in a while.
7365THREADED_TEST(InterceptorICGetterExceptions) {
7366 interceptor_ic_exception_get_count = 0;
7367 v8::HandleScope scope;
7368 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7369 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7370 LocalContext context(0, templ, v8::Handle<Value>());
7371 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7372 v8::Handle<Value> value = CompileRun(
7373 "function f() {"
7374 " for (var i = 0; i < 100; i++) {"
7375 " try { x; } catch(e) { return true; }"
7376 " }"
7377 " return false;"
7378 "};"
7379 "f();");
7380 CHECK_EQ(true, value->BooleanValue());
7381 interceptor_ic_exception_get_count = 0;
7382 value = CompileRun(
7383 "function f() {"
7384 " for (var i = 0; i < 100; i++) {"
7385 " try { x(42); } catch(e) { return true; }"
7386 " }"
7387 " return false;"
7388 "};"
7389 "f();");
7390 CHECK_EQ(true, value->BooleanValue());
7391}
7392
7393
7394static int interceptor_ic_exception_set_count = 0;
7395
7396static v8::Handle<Value> InterceptorICExceptionSetter(
7397 Local<String> key, Local<Value> value, const AccessorInfo&) {
7398 ApiTestFuzzer::Fuzz();
7399 if (++interceptor_ic_exception_set_count > 20) {
7400 return v8::ThrowException(v8_num(42));
7401 }
7402 // Do not actually handle setting.
7403 return v8::Handle<Value>();
7404}
7405
7406// Test interceptor store IC where the interceptor throws an exception
7407// once in a while.
7408THREADED_TEST(InterceptorICSetterExceptions) {
7409 interceptor_ic_exception_set_count = 0;
7410 v8::HandleScope scope;
7411 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7412 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7413 LocalContext context(0, templ, v8::Handle<Value>());
7414 v8::Handle<Value> value = CompileRun(
7415 "function f() {"
7416 " for (var i = 0; i < 100; i++) {"
7417 " try { x = 42; } catch(e) { return true; }"
7418 " }"
7419 " return false;"
7420 "};"
7421 "f();");
7422 CHECK_EQ(true, value->BooleanValue());
7423}
7424
7425
7426// Test that we ignore null interceptors.
7427THREADED_TEST(NullNamedInterceptor) {
7428 v8::HandleScope scope;
7429 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7430 templ->SetNamedPropertyHandler(0);
7431 LocalContext context;
7432 templ->Set("x", v8_num(42));
7433 v8::Handle<v8::Object> obj = templ->NewInstance();
7434 context->Global()->Set(v8_str("obj"), obj);
7435 v8::Handle<Value> value = CompileRun("obj.x");
7436 CHECK(value->IsInt32());
7437 CHECK_EQ(42, value->Int32Value());
7438}
7439
7440
7441// Test that we ignore null interceptors.
7442THREADED_TEST(NullIndexedInterceptor) {
7443 v8::HandleScope scope;
7444 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7445 templ->SetIndexedPropertyHandler(0);
7446 LocalContext context;
7447 templ->Set("42", v8_num(42));
7448 v8::Handle<v8::Object> obj = templ->NewInstance();
7449 context->Global()->Set(v8_str("obj"), obj);
7450 v8::Handle<Value> value = CompileRun("obj[42]");
7451 CHECK(value->IsInt32());
7452 CHECK_EQ(42, value->Int32Value());
7453}
7454
7455
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007456THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7457 v8::HandleScope scope;
7458 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7459 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7460 LocalContext env;
7461 env->Global()->Set(v8_str("obj"),
7462 templ->GetFunction()->NewInstance());
7463 ExpectTrue("obj.x === 42");
7464 ExpectTrue("!obj.propertyIsEnumerable('x')");
7465}
7466
7467
Steve Blocka7e24c12009-10-30 11:49:00 +00007468static v8::Handle<Value> ParentGetter(Local<String> name,
7469 const AccessorInfo& info) {
7470 ApiTestFuzzer::Fuzz();
7471 return v8_num(1);
7472}
7473
7474
7475static v8::Handle<Value> ChildGetter(Local<String> name,
7476 const AccessorInfo& info) {
7477 ApiTestFuzzer::Fuzz();
7478 return v8_num(42);
7479}
7480
7481
7482THREADED_TEST(Overriding) {
7483 v8::HandleScope scope;
7484 LocalContext context;
7485
7486 // Parent template.
7487 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7488 Local<ObjectTemplate> parent_instance_templ =
7489 parent_templ->InstanceTemplate();
7490 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7491
7492 // Template that inherits from the parent template.
7493 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7494 Local<ObjectTemplate> child_instance_templ =
7495 child_templ->InstanceTemplate();
7496 child_templ->Inherit(parent_templ);
7497 // Override 'f'. The child version of 'f' should get called for child
7498 // instances.
7499 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7500 // Add 'g' twice. The 'g' added last should get called for instances.
7501 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7502 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7503
7504 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7505 // so 'h' can be shadowed on the instance object.
7506 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7507 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7508 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7509
7510 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7511 // but the attribute does not have effect because it is duplicated with
7512 // NULL setter.
7513 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7514 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7515
7516
7517
7518 // Instantiate the child template.
7519 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7520
7521 // Check that the child function overrides the parent one.
7522 context->Global()->Set(v8_str("o"), instance);
7523 Local<Value> value = v8_compile("o.f")->Run();
7524 // Check that the 'g' that was added last is hit.
7525 CHECK_EQ(42, value->Int32Value());
7526 value = v8_compile("o.g")->Run();
7527 CHECK_EQ(42, value->Int32Value());
7528
7529 // Check 'h' can be shadowed.
7530 value = v8_compile("o.h = 3; o.h")->Run();
7531 CHECK_EQ(3, value->Int32Value());
7532
7533 // Check 'i' is cannot be shadowed or changed.
7534 value = v8_compile("o.i = 3; o.i")->Run();
7535 CHECK_EQ(42, value->Int32Value());
7536}
7537
7538
7539static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7540 ApiTestFuzzer::Fuzz();
7541 if (args.IsConstructCall()) {
7542 return v8::Boolean::New(true);
7543 }
7544 return v8::Boolean::New(false);
7545}
7546
7547
7548THREADED_TEST(IsConstructCall) {
7549 v8::HandleScope scope;
7550
7551 // Function template with call handler.
7552 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7553 templ->SetCallHandler(IsConstructHandler);
7554
7555 LocalContext context;
7556
7557 context->Global()->Set(v8_str("f"), templ->GetFunction());
7558 Local<Value> value = v8_compile("f()")->Run();
7559 CHECK(!value->BooleanValue());
7560 value = v8_compile("new f()")->Run();
7561 CHECK(value->BooleanValue());
7562}
7563
7564
7565THREADED_TEST(ObjectProtoToString) {
7566 v8::HandleScope scope;
7567 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7568 templ->SetClassName(v8_str("MyClass"));
7569
7570 LocalContext context;
7571
7572 Local<String> customized_tostring = v8_str("customized toString");
7573
7574 // Replace Object.prototype.toString
7575 v8_compile("Object.prototype.toString = function() {"
7576 " return 'customized toString';"
7577 "}")->Run();
7578
7579 // Normal ToString call should call replaced Object.prototype.toString
7580 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7581 Local<String> value = instance->ToString();
7582 CHECK(value->IsString() && value->Equals(customized_tostring));
7583
7584 // ObjectProtoToString should not call replace toString function.
7585 value = instance->ObjectProtoToString();
7586 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7587
7588 // Check global
7589 value = context->Global()->ObjectProtoToString();
7590 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7591
7592 // Check ordinary object
7593 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007594 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007595 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7596}
7597
7598
7599bool ApiTestFuzzer::fuzzing_ = false;
7600v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_=
7601 v8::internal::OS::CreateSemaphore(0);
7602int ApiTestFuzzer::active_tests_;
7603int ApiTestFuzzer::tests_being_run_;
7604int ApiTestFuzzer::current_;
7605
7606
7607// We are in a callback and want to switch to another thread (if we
7608// are currently running the thread fuzzing test).
7609void ApiTestFuzzer::Fuzz() {
7610 if (!fuzzing_) return;
7611 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7612 test->ContextSwitch();
7613}
7614
7615
7616// Let the next thread go. Since it is also waiting on the V8 lock it may
7617// not start immediately.
7618bool ApiTestFuzzer::NextThread() {
7619 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007620 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007621 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007622 if (kLogThreading)
7623 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007624 return false;
7625 }
Steve Blockd0582a62009-12-15 09:54:21 +00007626 if (kLogThreading) {
7627 printf("Switch from %s to %s\n",
7628 test_name,
7629 RegisterThreadedTest::nth(test_position)->name());
7630 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007631 current_ = test_position;
7632 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7633 return true;
7634}
7635
7636
7637void ApiTestFuzzer::Run() {
7638 // When it is our turn...
7639 gate_->Wait();
7640 {
7641 // ... get the V8 lock and start running the test.
7642 v8::Locker locker;
7643 CallTest();
7644 }
7645 // This test finished.
7646 active_ = false;
7647 active_tests_--;
7648 // If it was the last then signal that fact.
7649 if (active_tests_ == 0) {
7650 all_tests_done_->Signal();
7651 } else {
7652 // Otherwise select a new test and start that.
7653 NextThread();
7654 }
7655}
7656
7657
7658static unsigned linear_congruential_generator;
7659
7660
7661void ApiTestFuzzer::Setup(PartOfTest part) {
7662 linear_congruential_generator = i::FLAG_testing_prng_seed;
7663 fuzzing_ = true;
7664 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7665 int end = (part == FIRST_PART)
7666 ? (RegisterThreadedTest::count() >> 1)
7667 : RegisterThreadedTest::count();
7668 active_tests_ = tests_being_run_ = end - start;
7669 for (int i = 0; i < tests_being_run_; i++) {
7670 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7671 }
7672 for (int i = 0; i < active_tests_; i++) {
7673 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7674 }
7675}
7676
7677
7678static void CallTestNumber(int test_number) {
7679 (RegisterThreadedTest::nth(test_number)->callback())();
7680}
7681
7682
7683void ApiTestFuzzer::RunAllTests() {
7684 // Set off the first test.
7685 current_ = -1;
7686 NextThread();
7687 // Wait till they are all done.
7688 all_tests_done_->Wait();
7689}
7690
7691
7692int ApiTestFuzzer::GetNextTestNumber() {
7693 int next_test;
7694 do {
7695 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7696 linear_congruential_generator *= 1664525u;
7697 linear_congruential_generator += 1013904223u;
7698 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7699 return next_test;
7700}
7701
7702
7703void ApiTestFuzzer::ContextSwitch() {
7704 // If the new thread is the same as the current thread there is nothing to do.
7705 if (NextThread()) {
7706 // Now it can start.
7707 v8::Unlocker unlocker;
7708 // Wait till someone starts us again.
7709 gate_->Wait();
7710 // And we're off.
7711 }
7712}
7713
7714
7715void ApiTestFuzzer::TearDown() {
7716 fuzzing_ = false;
7717 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7718 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7719 if (fuzzer != NULL) fuzzer->Join();
7720 }
7721}
7722
7723
7724// Lets not be needlessly self-referential.
7725TEST(Threading) {
7726 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7727 ApiTestFuzzer::RunAllTests();
7728 ApiTestFuzzer::TearDown();
7729}
7730
7731TEST(Threading2) {
7732 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7733 ApiTestFuzzer::RunAllTests();
7734 ApiTestFuzzer::TearDown();
7735}
7736
7737
7738void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007739 if (kLogThreading)
7740 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007741 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007742 if (kLogThreading)
7743 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007744}
7745
7746
7747static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7748 CHECK(v8::Locker::IsLocked());
7749 ApiTestFuzzer::Fuzz();
7750 v8::Unlocker unlocker;
7751 const char* code = "throw 7;";
7752 {
7753 v8::Locker nested_locker;
7754 v8::HandleScope scope;
7755 v8::Handle<Value> exception;
7756 { v8::TryCatch try_catch;
7757 v8::Handle<Value> value = CompileRun(code);
7758 CHECK(value.IsEmpty());
7759 CHECK(try_catch.HasCaught());
7760 // Make sure to wrap the exception in a new handle because
7761 // the handle returned from the TryCatch is destroyed
7762 // when the TryCatch is destroyed.
7763 exception = Local<Value>::New(try_catch.Exception());
7764 }
7765 return v8::ThrowException(exception);
7766 }
7767}
7768
7769
7770static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7771 CHECK(v8::Locker::IsLocked());
7772 ApiTestFuzzer::Fuzz();
7773 v8::Unlocker unlocker;
7774 const char* code = "throw 7;";
7775 {
7776 v8::Locker nested_locker;
7777 v8::HandleScope scope;
7778 v8::Handle<Value> value = CompileRun(code);
7779 CHECK(value.IsEmpty());
7780 return v8_str("foo");
7781 }
7782}
7783
7784
7785// These are locking tests that don't need to be run again
7786// as part of the locking aggregation tests.
7787TEST(NestedLockers) {
7788 v8::Locker locker;
7789 CHECK(v8::Locker::IsLocked());
7790 v8::HandleScope scope;
7791 LocalContext env;
7792 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7793 Local<Function> fun = fun_templ->GetFunction();
7794 env->Global()->Set(v8_str("throw_in_js"), fun);
7795 Local<Script> script = v8_compile("(function () {"
7796 " try {"
7797 " throw_in_js();"
7798 " return 42;"
7799 " } catch (e) {"
7800 " return e * 13;"
7801 " }"
7802 "})();");
7803 CHECK_EQ(91, script->Run()->Int32Value());
7804}
7805
7806
7807// These are locking tests that don't need to be run again
7808// as part of the locking aggregation tests.
7809TEST(NestedLockersNoTryCatch) {
7810 v8::Locker locker;
7811 v8::HandleScope scope;
7812 LocalContext env;
7813 Local<v8::FunctionTemplate> fun_templ =
7814 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7815 Local<Function> fun = fun_templ->GetFunction();
7816 env->Global()->Set(v8_str("throw_in_js"), fun);
7817 Local<Script> script = v8_compile("(function () {"
7818 " try {"
7819 " throw_in_js();"
7820 " return 42;"
7821 " } catch (e) {"
7822 " return e * 13;"
7823 " }"
7824 "})();");
7825 CHECK_EQ(91, script->Run()->Int32Value());
7826}
7827
7828
7829THREADED_TEST(RecursiveLocking) {
7830 v8::Locker locker;
7831 {
7832 v8::Locker locker2;
7833 CHECK(v8::Locker::IsLocked());
7834 }
7835}
7836
7837
7838static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
7839 ApiTestFuzzer::Fuzz();
7840 v8::Unlocker unlocker;
7841 return v8::Undefined();
7842}
7843
7844
7845THREADED_TEST(LockUnlockLock) {
7846 {
7847 v8::Locker locker;
7848 v8::HandleScope scope;
7849 LocalContext env;
7850 Local<v8::FunctionTemplate> fun_templ =
7851 v8::FunctionTemplate::New(UnlockForAMoment);
7852 Local<Function> fun = fun_templ->GetFunction();
7853 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7854 Local<Script> script = v8_compile("(function () {"
7855 " unlock_for_a_moment();"
7856 " return 42;"
7857 "})();");
7858 CHECK_EQ(42, script->Run()->Int32Value());
7859 }
7860 {
7861 v8::Locker locker;
7862 v8::HandleScope scope;
7863 LocalContext env;
7864 Local<v8::FunctionTemplate> fun_templ =
7865 v8::FunctionTemplate::New(UnlockForAMoment);
7866 Local<Function> fun = fun_templ->GetFunction();
7867 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7868 Local<Script> script = v8_compile("(function () {"
7869 " unlock_for_a_moment();"
7870 " return 42;"
7871 "})();");
7872 CHECK_EQ(42, script->Run()->Int32Value());
7873 }
7874}
7875
7876
Leon Clarked91b9f72010-01-27 17:25:45 +00007877static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00007878 int count = 0;
Leon Clarked91b9f72010-01-27 17:25:45 +00007879 v8::internal::HeapIterator it;
7880 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
7881 if (object->IsJSGlobalObject()) count++;
7882 return count;
7883}
7884
7885
7886static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00007887 // We need to collect all garbage twice to be sure that everything
7888 // has been collected. This is because inline caches are cleared in
7889 // the first garbage collection but some of the maps have already
7890 // been marked at that point. Therefore some of the maps are not
7891 // collected until the second garbage collection.
7892 v8::internal::Heap::CollectAllGarbage(false);
7893 v8::internal::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00007894 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00007895#ifdef DEBUG
7896 if (count > 0) v8::internal::Heap::TracePathToGlobal();
7897#endif
7898 return count;
7899}
7900
7901
7902TEST(DontLeakGlobalObjects) {
7903 // Regression test for issues 1139850 and 1174891.
7904
7905 v8::V8::Initialize();
7906
7907 int count = GetSurvivingGlobalObjectsCount();
7908
7909 for (int i = 0; i < 5; i++) {
7910 { v8::HandleScope scope;
7911 LocalContext context;
7912 }
7913 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7914
7915 { v8::HandleScope scope;
7916 LocalContext context;
7917 v8_compile("Date")->Run();
7918 }
7919 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7920
7921 { v8::HandleScope scope;
7922 LocalContext context;
7923 v8_compile("/aaa/")->Run();
7924 }
7925 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7926
7927 { v8::HandleScope scope;
7928 const char* extension_list[] = { "v8/gc" };
7929 v8::ExtensionConfiguration extensions(1, extension_list);
7930 LocalContext context(&extensions);
7931 v8_compile("gc();")->Run();
7932 }
7933 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7934 }
7935}
7936
7937
7938v8::Persistent<v8::Object> some_object;
7939v8::Persistent<v8::Object> bad_handle;
7940
7941void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
7942 v8::HandleScope scope;
7943 bad_handle = v8::Persistent<v8::Object>::New(some_object);
7944}
7945
7946
7947THREADED_TEST(NewPersistentHandleFromWeakCallback) {
7948 LocalContext context;
7949
7950 v8::Persistent<v8::Object> handle1, handle2;
7951 {
7952 v8::HandleScope scope;
7953 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
7954 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7955 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7956 }
7957 // Note: order is implementation dependent alas: currently
7958 // global handle nodes are processed by PostGarbageCollectionProcessing
7959 // in reverse allocation order, so if second allocated handle is deleted,
7960 // weak callback of the first handle would be able to 'reallocate' it.
7961 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
7962 handle2.Dispose();
7963 i::Heap::CollectAllGarbage(false);
7964}
7965
7966
7967v8::Persistent<v8::Object> to_be_disposed;
7968
7969void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
7970 to_be_disposed.Dispose();
7971 i::Heap::CollectAllGarbage(false);
7972}
7973
7974
7975THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
7976 LocalContext context;
7977
7978 v8::Persistent<v8::Object> handle1, handle2;
7979 {
7980 v8::HandleScope scope;
7981 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
7982 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
7983 }
7984 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
7985 to_be_disposed = handle2;
7986 i::Heap::CollectAllGarbage(false);
7987}
7988
Steve Blockd0582a62009-12-15 09:54:21 +00007989void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
7990 handle.Dispose();
7991}
7992
7993void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
7994 v8::HandleScope scope;
7995 v8::Persistent<v8::Object>::New(v8::Object::New());
7996}
7997
7998
7999THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8000 LocalContext context;
8001
8002 v8::Persistent<v8::Object> handle1, handle2, handle3;
8003 {
8004 v8::HandleScope scope;
8005 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8006 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8007 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8008 }
8009 handle2.MakeWeak(NULL, DisposingCallback);
8010 handle3.MakeWeak(NULL, HandleCreatingCallback);
8011 i::Heap::CollectAllGarbage(false);
8012}
8013
Steve Blocka7e24c12009-10-30 11:49:00 +00008014
8015THREADED_TEST(CheckForCrossContextObjectLiterals) {
8016 v8::V8::Initialize();
8017
8018 const int nof = 2;
8019 const char* sources[nof] = {
8020 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8021 "Object()"
8022 };
8023
8024 for (int i = 0; i < nof; i++) {
8025 const char* source = sources[i];
8026 { v8::HandleScope scope;
8027 LocalContext context;
8028 CompileRun(source);
8029 }
8030 { v8::HandleScope scope;
8031 LocalContext context;
8032 CompileRun(source);
8033 }
8034 }
8035}
8036
8037
8038static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8039 v8::HandleScope inner;
8040 env->Enter();
8041 v8::Handle<Value> three = v8_num(3);
8042 v8::Handle<Value> value = inner.Close(three);
8043 env->Exit();
8044 return value;
8045}
8046
8047
8048THREADED_TEST(NestedHandleScopeAndContexts) {
8049 v8::HandleScope outer;
8050 v8::Persistent<Context> env = Context::New();
8051 env->Enter();
8052 v8::Handle<Value> value = NestedScope(env);
8053 v8::Handle<String> str = value->ToString();
8054 env->Exit();
8055 env.Dispose();
8056}
8057
8058
8059THREADED_TEST(ExternalAllocatedMemory) {
8060 v8::HandleScope outer;
8061 v8::Persistent<Context> env = Context::New();
8062 const int kSize = 1024*1024;
8063 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8064 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8065}
8066
8067
8068THREADED_TEST(DisposeEnteredContext) {
8069 v8::HandleScope scope;
8070 LocalContext outer;
8071 { v8::Persistent<v8::Context> inner = v8::Context::New();
8072 inner->Enter();
8073 inner.Dispose();
8074 inner.Clear();
8075 inner->Exit();
8076 }
8077}
8078
8079
8080// Regression test for issue 54, object templates with internal fields
8081// but no accessors or interceptors did not get their internal field
8082// count set on instances.
8083THREADED_TEST(Regress54) {
8084 v8::HandleScope outer;
8085 LocalContext context;
8086 static v8::Persistent<v8::ObjectTemplate> templ;
8087 if (templ.IsEmpty()) {
8088 v8::HandleScope inner;
8089 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8090 local->SetInternalFieldCount(1);
8091 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8092 }
8093 v8::Handle<v8::Object> result = templ->NewInstance();
8094 CHECK_EQ(1, result->InternalFieldCount());
8095}
8096
8097
8098// If part of the threaded tests, this test makes ThreadingTest fail
8099// on mac.
8100TEST(CatchStackOverflow) {
8101 v8::HandleScope scope;
8102 LocalContext context;
8103 v8::TryCatch try_catch;
8104 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8105 "function f() {"
8106 " return f();"
8107 "}"
8108 ""
8109 "f();"));
8110 v8::Handle<v8::Value> result = script->Run();
8111 CHECK(result.IsEmpty());
8112}
8113
8114
8115static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8116 const char* resource_name,
8117 int line_offset) {
8118 v8::HandleScope scope;
8119 v8::TryCatch try_catch;
8120 v8::Handle<v8::Value> result = script->Run();
8121 CHECK(result.IsEmpty());
8122 CHECK(try_catch.HasCaught());
8123 v8::Handle<v8::Message> message = try_catch.Message();
8124 CHECK(!message.IsEmpty());
8125 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8126 CHECK_EQ(91, message->GetStartPosition());
8127 CHECK_EQ(92, message->GetEndPosition());
8128 CHECK_EQ(2, message->GetStartColumn());
8129 CHECK_EQ(3, message->GetEndColumn());
8130 v8::String::AsciiValue line(message->GetSourceLine());
8131 CHECK_EQ(" throw 'nirk';", *line);
8132 v8::String::AsciiValue name(message->GetScriptResourceName());
8133 CHECK_EQ(resource_name, *name);
8134}
8135
8136
8137THREADED_TEST(TryCatchSourceInfo) {
8138 v8::HandleScope scope;
8139 LocalContext context;
8140 v8::Handle<v8::String> source = v8::String::New(
8141 "function Foo() {\n"
8142 " return Bar();\n"
8143 "}\n"
8144 "\n"
8145 "function Bar() {\n"
8146 " return Baz();\n"
8147 "}\n"
8148 "\n"
8149 "function Baz() {\n"
8150 " throw 'nirk';\n"
8151 "}\n"
8152 "\n"
8153 "Foo();\n");
8154
8155 const char* resource_name;
8156 v8::Handle<v8::Script> script;
8157 resource_name = "test.js";
8158 script = v8::Script::Compile(source, v8::String::New(resource_name));
8159 CheckTryCatchSourceInfo(script, resource_name, 0);
8160
8161 resource_name = "test1.js";
8162 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8163 script = v8::Script::Compile(source, &origin1);
8164 CheckTryCatchSourceInfo(script, resource_name, 0);
8165
8166 resource_name = "test2.js";
8167 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8168 script = v8::Script::Compile(source, &origin2);
8169 CheckTryCatchSourceInfo(script, resource_name, 7);
8170}
8171
8172
8173THREADED_TEST(CompilationCache) {
8174 v8::HandleScope scope;
8175 LocalContext context;
8176 v8::Handle<v8::String> source0 = v8::String::New("1234");
8177 v8::Handle<v8::String> source1 = v8::String::New("1234");
8178 v8::Handle<v8::Script> script0 =
8179 v8::Script::Compile(source0, v8::String::New("test.js"));
8180 v8::Handle<v8::Script> script1 =
8181 v8::Script::Compile(source1, v8::String::New("test.js"));
8182 v8::Handle<v8::Script> script2 =
8183 v8::Script::Compile(source0); // different origin
8184 CHECK_EQ(1234, script0->Run()->Int32Value());
8185 CHECK_EQ(1234, script1->Run()->Int32Value());
8186 CHECK_EQ(1234, script2->Run()->Int32Value());
8187}
8188
8189
8190static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8191 ApiTestFuzzer::Fuzz();
8192 return v8_num(42);
8193}
8194
8195
8196THREADED_TEST(CallbackFunctionName) {
8197 v8::HandleScope scope;
8198 LocalContext context;
8199 Local<ObjectTemplate> t = ObjectTemplate::New();
8200 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8201 context->Global()->Set(v8_str("obj"), t->NewInstance());
8202 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8203 CHECK(value->IsString());
8204 v8::String::AsciiValue name(value);
8205 CHECK_EQ("asdf", *name);
8206}
8207
8208
8209THREADED_TEST(DateAccess) {
8210 v8::HandleScope scope;
8211 LocalContext context;
8212 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8213 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008214 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008215}
8216
8217
8218void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008219 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008220 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8221 CHECK_EQ(elmc, props->Length());
8222 for (int i = 0; i < elmc; i++) {
8223 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8224 CHECK_EQ(elmv[i], *elm);
8225 }
8226}
8227
8228
8229THREADED_TEST(PropertyEnumeration) {
8230 v8::HandleScope scope;
8231 LocalContext context;
8232 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8233 "var result = [];"
8234 "result[0] = {};"
8235 "result[1] = {a: 1, b: 2};"
8236 "result[2] = [1, 2, 3];"
8237 "var proto = {x: 1, y: 2, z: 3};"
8238 "var x = { __proto__: proto, w: 0, z: 1 };"
8239 "result[3] = x;"
8240 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008241 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008242 CHECK_EQ(4, elms->Length());
8243 int elmc0 = 0;
8244 const char** elmv0 = NULL;
8245 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8246 int elmc1 = 2;
8247 const char* elmv1[] = {"a", "b"};
8248 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8249 int elmc2 = 3;
8250 const char* elmv2[] = {"0", "1", "2"};
8251 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8252 int elmc3 = 4;
8253 const char* elmv3[] = {"w", "z", "x", "y"};
8254 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8255}
8256
8257
Steve Blocka7e24c12009-10-30 11:49:00 +00008258static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8259 Local<Value> name,
8260 v8::AccessType type,
8261 Local<Value> data) {
8262 return type != v8::ACCESS_SET;
8263}
8264
8265
8266static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8267 uint32_t key,
8268 v8::AccessType type,
8269 Local<Value> data) {
8270 return type != v8::ACCESS_SET;
8271}
8272
8273
8274THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8275 v8::HandleScope scope;
8276 LocalContext context;
8277 Local<ObjectTemplate> templ = ObjectTemplate::New();
8278 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8279 IndexedSetAccessBlocker);
8280 templ->Set(v8_str("x"), v8::True());
8281 Local<v8::Object> instance = templ->NewInstance();
8282 context->Global()->Set(v8_str("obj"), instance);
8283 Local<Value> value = CompileRun("obj.x");
8284 CHECK(value->BooleanValue());
8285}
8286
8287
8288static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8289 Local<Value> name,
8290 v8::AccessType type,
8291 Local<Value> data) {
8292 return false;
8293}
8294
8295
8296static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8297 uint32_t key,
8298 v8::AccessType type,
8299 Local<Value> data) {
8300 return false;
8301}
8302
8303
8304
8305THREADED_TEST(AccessChecksReenabledCorrectly) {
8306 v8::HandleScope scope;
8307 LocalContext context;
8308 Local<ObjectTemplate> templ = ObjectTemplate::New();
8309 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8310 IndexedGetAccessBlocker);
8311 templ->Set(v8_str("a"), v8_str("a"));
8312 // Add more than 8 (see kMaxFastProperties) properties
8313 // so that the constructor will force copying map.
8314 // Cannot sprintf, gcc complains unsafety.
8315 char buf[4];
8316 for (char i = '0'; i <= '9' ; i++) {
8317 buf[0] = i;
8318 for (char j = '0'; j <= '9'; j++) {
8319 buf[1] = j;
8320 for (char k = '0'; k <= '9'; k++) {
8321 buf[2] = k;
8322 buf[3] = 0;
8323 templ->Set(v8_str(buf), v8::Number::New(k));
8324 }
8325 }
8326 }
8327
8328 Local<v8::Object> instance_1 = templ->NewInstance();
8329 context->Global()->Set(v8_str("obj_1"), instance_1);
8330
8331 Local<Value> value_1 = CompileRun("obj_1.a");
8332 CHECK(value_1->IsUndefined());
8333
8334 Local<v8::Object> instance_2 = templ->NewInstance();
8335 context->Global()->Set(v8_str("obj_2"), instance_2);
8336
8337 Local<Value> value_2 = CompileRun("obj_2.a");
8338 CHECK(value_2->IsUndefined());
8339}
8340
8341
8342// This tests that access check information remains on the global
8343// object template when creating contexts.
8344THREADED_TEST(AccessControlRepeatedContextCreation) {
8345 v8::HandleScope handle_scope;
8346 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8347 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8348 IndexedSetAccessBlocker);
8349 i::Handle<i::ObjectTemplateInfo> internal_template =
8350 v8::Utils::OpenHandle(*global_template);
8351 CHECK(!internal_template->constructor()->IsUndefined());
8352 i::Handle<i::FunctionTemplateInfo> constructor(
8353 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8354 CHECK(!constructor->access_check_info()->IsUndefined());
8355 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8356 CHECK(!constructor->access_check_info()->IsUndefined());
8357}
8358
8359
8360THREADED_TEST(TurnOnAccessCheck) {
8361 v8::HandleScope handle_scope;
8362
8363 // Create an environment with access check to the global object disabled by
8364 // default.
8365 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8366 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8367 IndexedGetAccessBlocker,
8368 v8::Handle<v8::Value>(),
8369 false);
8370 v8::Persistent<Context> context = Context::New(NULL, global_template);
8371 Context::Scope context_scope(context);
8372
8373 // Set up a property and a number of functions.
8374 context->Global()->Set(v8_str("a"), v8_num(1));
8375 CompileRun("function f1() {return a;}"
8376 "function f2() {return a;}"
8377 "function g1() {return h();}"
8378 "function g2() {return h();}"
8379 "function h() {return 1;}");
8380 Local<Function> f1 =
8381 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8382 Local<Function> f2 =
8383 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8384 Local<Function> g1 =
8385 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8386 Local<Function> g2 =
8387 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8388 Local<Function> h =
8389 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8390
8391 // Get the global object.
8392 v8::Handle<v8::Object> global = context->Global();
8393
8394 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8395 // uses the runtime system to retreive property a whereas f2 uses global load
8396 // inline cache.
8397 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8398 for (int i = 0; i < 4; i++) {
8399 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8400 }
8401
8402 // Same for g1 and g2.
8403 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8404 for (int i = 0; i < 4; i++) {
8405 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8406 }
8407
8408 // Detach the global and turn on access check.
8409 context->DetachGlobal();
8410 context->Global()->TurnOnAccessCheck();
8411
8412 // Failing access check to property get results in undefined.
8413 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8414 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8415
8416 // Failing access check to function call results in exception.
8417 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8418 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8419
8420 // No failing access check when just returning a constant.
8421 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8422}
8423
8424
8425// This test verifies that pre-compilation (aka preparsing) can be called
8426// without initializing the whole VM. Thus we cannot run this test in a
8427// multi-threaded setup.
8428TEST(PreCompile) {
8429 // TODO(155): This test would break without the initialization of V8. This is
8430 // a workaround for now to make this test not fail.
8431 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008432 const char* script = "function foo(a) { return a+1; }";
8433 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008434 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008435 CHECK_NE(sd->Length(), 0);
8436 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008437 CHECK(!sd->HasError());
8438 delete sd;
8439}
8440
8441
8442TEST(PreCompileWithError) {
8443 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008444 const char* script = "function foo(a) { return 1 * * 2; }";
8445 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008446 v8::ScriptData::PreCompile(script, i::StrLength(script));
8447 CHECK(sd->HasError());
8448 delete sd;
8449}
8450
8451
8452TEST(Regress31661) {
8453 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008454 const char* script = " The Definintive Guide";
8455 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008456 v8::ScriptData::PreCompile(script, i::StrLength(script));
8457 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008458 delete sd;
8459}
8460
8461
Leon Clarkef7060e22010-06-03 12:02:55 +01008462// Tests that ScriptData can be serialized and deserialized.
8463TEST(PreCompileSerialization) {
8464 v8::V8::Initialize();
8465 const char* script = "function foo(a) { return a+1; }";
8466 v8::ScriptData* sd =
8467 v8::ScriptData::PreCompile(script, i::StrLength(script));
8468
8469 // Serialize.
8470 int serialized_data_length = sd->Length();
8471 char* serialized_data = i::NewArray<char>(serialized_data_length);
8472 memcpy(serialized_data, sd->Data(), serialized_data_length);
8473
8474 // Deserialize.
8475 v8::ScriptData* deserialized_sd =
8476 v8::ScriptData::New(serialized_data, serialized_data_length);
8477
8478 // Verify that the original is the same as the deserialized.
8479 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8480 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8481 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8482
8483 delete sd;
8484 delete deserialized_sd;
8485}
8486
8487
8488// Attempts to deserialize bad data.
8489TEST(PreCompileDeserializationError) {
8490 v8::V8::Initialize();
8491 const char* data = "DONT CARE";
8492 int invalid_size = 3;
8493 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8494
8495 CHECK_EQ(0, sd->Length());
8496
8497 delete sd;
8498}
8499
8500
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008501// Verifies that the Handle<String> and const char* versions of the API produce
8502// the same results (at least for one trivial case).
8503TEST(PreCompileAPIVariationsAreSame) {
8504 v8::V8::Initialize();
8505 v8::HandleScope scope;
8506
8507 const char* cstring = "function foo(a) { return a+1; }";
8508 v8::ScriptData* sd_from_cstring =
8509 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8510
8511 TestAsciiResource* resource = new TestAsciiResource(cstring);
8512 v8::ScriptData* sd_from_istring = v8::ScriptData::PreCompile(
8513 v8::String::NewExternal(resource));
8514
8515 CHECK_EQ(sd_from_cstring->Length(), sd_from_istring->Length());
8516 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8517 sd_from_istring->Data(),
8518 sd_from_cstring->Length()));
8519
8520 delete sd_from_cstring;
8521 delete sd_from_istring;
8522}
8523
8524
Steve Blocka7e24c12009-10-30 11:49:00 +00008525// This tests that we do not allow dictionary load/call inline caches
8526// to use functions that have not yet been compiled. The potential
8527// problem of loading a function that has not yet been compiled can
8528// arise because we share code between contexts via the compilation
8529// cache.
8530THREADED_TEST(DictionaryICLoadedFunction) {
8531 v8::HandleScope scope;
8532 // Test LoadIC.
8533 for (int i = 0; i < 2; i++) {
8534 LocalContext context;
8535 context->Global()->Set(v8_str("tmp"), v8::True());
8536 context->Global()->Delete(v8_str("tmp"));
8537 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8538 }
8539 // Test CallIC.
8540 for (int i = 0; i < 2; i++) {
8541 LocalContext context;
8542 context->Global()->Set(v8_str("tmp"), v8::True());
8543 context->Global()->Delete(v8_str("tmp"));
8544 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8545 }
8546}
8547
8548
8549// Test that cross-context new calls use the context of the callee to
8550// create the new JavaScript object.
8551THREADED_TEST(CrossContextNew) {
8552 v8::HandleScope scope;
8553 v8::Persistent<Context> context0 = Context::New();
8554 v8::Persistent<Context> context1 = Context::New();
8555
8556 // Allow cross-domain access.
8557 Local<String> token = v8_str("<security token>");
8558 context0->SetSecurityToken(token);
8559 context1->SetSecurityToken(token);
8560
8561 // Set an 'x' property on the Object prototype and define a
8562 // constructor function in context0.
8563 context0->Enter();
8564 CompileRun("Object.prototype.x = 42; function C() {};");
8565 context0->Exit();
8566
8567 // Call the constructor function from context0 and check that the
8568 // result has the 'x' property.
8569 context1->Enter();
8570 context1->Global()->Set(v8_str("other"), context0->Global());
8571 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8572 CHECK(value->IsInt32());
8573 CHECK_EQ(42, value->Int32Value());
8574 context1->Exit();
8575
8576 // Dispose the contexts to allow them to be garbage collected.
8577 context0.Dispose();
8578 context1.Dispose();
8579}
8580
8581
8582class RegExpInterruptTest {
8583 public:
8584 RegExpInterruptTest() : block_(NULL) {}
8585 ~RegExpInterruptTest() { delete block_; }
8586 void RunTest() {
8587 block_ = i::OS::CreateSemaphore(0);
8588 gc_count_ = 0;
8589 gc_during_regexp_ = 0;
8590 regexp_success_ = false;
8591 gc_success_ = false;
8592 GCThread gc_thread(this);
8593 gc_thread.Start();
8594 v8::Locker::StartPreemption(1);
8595
8596 LongRunningRegExp();
8597 {
8598 v8::Unlocker unlock;
8599 gc_thread.Join();
8600 }
8601 v8::Locker::StopPreemption();
8602 CHECK(regexp_success_);
8603 CHECK(gc_success_);
8604 }
8605 private:
8606 // Number of garbage collections required.
8607 static const int kRequiredGCs = 5;
8608
8609 class GCThread : public i::Thread {
8610 public:
8611 explicit GCThread(RegExpInterruptTest* test)
8612 : test_(test) {}
8613 virtual void Run() {
8614 test_->CollectGarbage();
8615 }
8616 private:
8617 RegExpInterruptTest* test_;
8618 };
8619
8620 void CollectGarbage() {
8621 block_->Wait();
8622 while (gc_during_regexp_ < kRequiredGCs) {
8623 {
8624 v8::Locker lock;
8625 // TODO(lrn): Perhaps create some garbage before collecting.
8626 i::Heap::CollectAllGarbage(false);
8627 gc_count_++;
8628 }
8629 i::OS::Sleep(1);
8630 }
8631 gc_success_ = true;
8632 }
8633
8634 void LongRunningRegExp() {
8635 block_->Signal(); // Enable garbage collection thread on next preemption.
8636 int rounds = 0;
8637 while (gc_during_regexp_ < kRequiredGCs) {
8638 int gc_before = gc_count_;
8639 {
8640 // Match 15-30 "a"'s against 14 and a "b".
8641 const char* c_source =
8642 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8643 ".exec('aaaaaaaaaaaaaaab') === null";
8644 Local<String> source = String::New(c_source);
8645 Local<Script> script = Script::Compile(source);
8646 Local<Value> result = script->Run();
8647 if (!result->BooleanValue()) {
8648 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8649 return;
8650 }
8651 }
8652 {
8653 // Match 15-30 "a"'s against 15 and a "b".
8654 const char* c_source =
8655 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8656 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8657 Local<String> source = String::New(c_source);
8658 Local<Script> script = Script::Compile(source);
8659 Local<Value> result = script->Run();
8660 if (!result->BooleanValue()) {
8661 gc_during_regexp_ = kRequiredGCs;
8662 return;
8663 }
8664 }
8665 int gc_after = gc_count_;
8666 gc_during_regexp_ += gc_after - gc_before;
8667 rounds++;
8668 i::OS::Sleep(1);
8669 }
8670 regexp_success_ = true;
8671 }
8672
8673 i::Semaphore* block_;
8674 int gc_count_;
8675 int gc_during_regexp_;
8676 bool regexp_success_;
8677 bool gc_success_;
8678};
8679
8680
8681// Test that a regular expression execution can be interrupted and
8682// survive a garbage collection.
8683TEST(RegExpInterruption) {
8684 v8::Locker lock;
8685 v8::V8::Initialize();
8686 v8::HandleScope scope;
8687 Local<Context> local_env;
8688 {
8689 LocalContext env;
8690 local_env = env.local();
8691 }
8692
8693 // Local context should still be live.
8694 CHECK(!local_env.IsEmpty());
8695 local_env->Enter();
8696
8697 // Should complete without problems.
8698 RegExpInterruptTest().RunTest();
8699
8700 local_env->Exit();
8701}
8702
8703
8704class ApplyInterruptTest {
8705 public:
8706 ApplyInterruptTest() : block_(NULL) {}
8707 ~ApplyInterruptTest() { delete block_; }
8708 void RunTest() {
8709 block_ = i::OS::CreateSemaphore(0);
8710 gc_count_ = 0;
8711 gc_during_apply_ = 0;
8712 apply_success_ = false;
8713 gc_success_ = false;
8714 GCThread gc_thread(this);
8715 gc_thread.Start();
8716 v8::Locker::StartPreemption(1);
8717
8718 LongRunningApply();
8719 {
8720 v8::Unlocker unlock;
8721 gc_thread.Join();
8722 }
8723 v8::Locker::StopPreemption();
8724 CHECK(apply_success_);
8725 CHECK(gc_success_);
8726 }
8727 private:
8728 // Number of garbage collections required.
8729 static const int kRequiredGCs = 2;
8730
8731 class GCThread : public i::Thread {
8732 public:
8733 explicit GCThread(ApplyInterruptTest* test)
8734 : test_(test) {}
8735 virtual void Run() {
8736 test_->CollectGarbage();
8737 }
8738 private:
8739 ApplyInterruptTest* test_;
8740 };
8741
8742 void CollectGarbage() {
8743 block_->Wait();
8744 while (gc_during_apply_ < kRequiredGCs) {
8745 {
8746 v8::Locker lock;
8747 i::Heap::CollectAllGarbage(false);
8748 gc_count_++;
8749 }
8750 i::OS::Sleep(1);
8751 }
8752 gc_success_ = true;
8753 }
8754
8755 void LongRunningApply() {
8756 block_->Signal();
8757 int rounds = 0;
8758 while (gc_during_apply_ < kRequiredGCs) {
8759 int gc_before = gc_count_;
8760 {
8761 const char* c_source =
8762 "function do_very_little(bar) {"
8763 " this.foo = bar;"
8764 "}"
8765 "for (var i = 0; i < 100000; i++) {"
8766 " do_very_little.apply(this, ['bar']);"
8767 "}";
8768 Local<String> source = String::New(c_source);
8769 Local<Script> script = Script::Compile(source);
8770 Local<Value> result = script->Run();
8771 // Check that no exception was thrown.
8772 CHECK(!result.IsEmpty());
8773 }
8774 int gc_after = gc_count_;
8775 gc_during_apply_ += gc_after - gc_before;
8776 rounds++;
8777 }
8778 apply_success_ = true;
8779 }
8780
8781 i::Semaphore* block_;
8782 int gc_count_;
8783 int gc_during_apply_;
8784 bool apply_success_;
8785 bool gc_success_;
8786};
8787
8788
8789// Test that nothing bad happens if we get a preemption just when we were
8790// about to do an apply().
8791TEST(ApplyInterruption) {
8792 v8::Locker lock;
8793 v8::V8::Initialize();
8794 v8::HandleScope scope;
8795 Local<Context> local_env;
8796 {
8797 LocalContext env;
8798 local_env = env.local();
8799 }
8800
8801 // Local context should still be live.
8802 CHECK(!local_env.IsEmpty());
8803 local_env->Enter();
8804
8805 // Should complete without problems.
8806 ApplyInterruptTest().RunTest();
8807
8808 local_env->Exit();
8809}
8810
8811
8812// Verify that we can clone an object
8813TEST(ObjectClone) {
8814 v8::HandleScope scope;
8815 LocalContext env;
8816
8817 const char* sample =
8818 "var rv = {};" \
8819 "rv.alpha = 'hello';" \
8820 "rv.beta = 123;" \
8821 "rv;";
8822
8823 // Create an object, verify basics.
8824 Local<Value> val = CompileRun(sample);
8825 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01008826 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008827 obj->Set(v8_str("gamma"), v8_str("cloneme"));
8828
8829 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
8830 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8831 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
8832
8833 // Clone it.
8834 Local<v8::Object> clone = obj->Clone();
8835 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
8836 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
8837 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
8838
8839 // Set a property on the clone, verify each object.
8840 clone->Set(v8_str("beta"), v8::Integer::New(456));
8841 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8842 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
8843}
8844
8845
8846class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
8847 public:
8848 explicit AsciiVectorResource(i::Vector<const char> vector)
8849 : data_(vector) {}
8850 virtual ~AsciiVectorResource() {}
8851 virtual size_t length() const { return data_.length(); }
8852 virtual const char* data() const { return data_.start(); }
8853 private:
8854 i::Vector<const char> data_;
8855};
8856
8857
8858class UC16VectorResource : public v8::String::ExternalStringResource {
8859 public:
8860 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
8861 : data_(vector) {}
8862 virtual ~UC16VectorResource() {}
8863 virtual size_t length() const { return data_.length(); }
8864 virtual const i::uc16* data() const { return data_.start(); }
8865 private:
8866 i::Vector<const i::uc16> data_;
8867};
8868
8869
8870static void MorphAString(i::String* string,
8871 AsciiVectorResource* ascii_resource,
8872 UC16VectorResource* uc16_resource) {
8873 CHECK(i::StringShape(string).IsExternal());
8874 if (string->IsAsciiRepresentation()) {
8875 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008876 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008877 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00008878 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008879 i::ExternalTwoByteString* morphed =
8880 i::ExternalTwoByteString::cast(string);
8881 morphed->set_resource(uc16_resource);
8882 } else {
8883 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008884 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008885 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00008886 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008887 i::ExternalAsciiString* morphed =
8888 i::ExternalAsciiString::cast(string);
8889 morphed->set_resource(ascii_resource);
8890 }
8891}
8892
8893
8894// Test that we can still flatten a string if the components it is built up
8895// from have been turned into 16 bit strings in the mean time.
8896THREADED_TEST(MorphCompositeStringTest) {
8897 const char* c_string = "Now is the time for all good men"
8898 " to come to the aid of the party";
8899 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
8900 {
8901 v8::HandleScope scope;
8902 LocalContext env;
8903 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008904 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008905 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008906 i::Vector<const uint16_t>(two_byte_string,
8907 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008908
8909 Local<String> lhs(v8::Utils::ToLocal(
8910 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8911 Local<String> rhs(v8::Utils::ToLocal(
8912 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8913
8914 env->Global()->Set(v8_str("lhs"), lhs);
8915 env->Global()->Set(v8_str("rhs"), rhs);
8916
8917 CompileRun(
8918 "var cons = lhs + rhs;"
8919 "var slice = lhs.substring(1, lhs.length - 1);"
8920 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
8921
8922 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
8923 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
8924
8925 // Now do some stuff to make sure the strings are flattened, etc.
8926 CompileRun(
8927 "/[^a-z]/.test(cons);"
8928 "/[^a-z]/.test(slice);"
8929 "/[^a-z]/.test(slice_on_cons);");
8930 const char* expected_cons =
8931 "Now is the time for all good men to come to the aid of the party"
8932 "Now is the time for all good men to come to the aid of the party";
8933 const char* expected_slice =
8934 "ow is the time for all good men to come to the aid of the part";
8935 const char* expected_slice_on_cons =
8936 "ow is the time for all good men to come to the aid of the party"
8937 "Now is the time for all good men to come to the aid of the part";
8938 CHECK_EQ(String::New(expected_cons),
8939 env->Global()->Get(v8_str("cons")));
8940 CHECK_EQ(String::New(expected_slice),
8941 env->Global()->Get(v8_str("slice")));
8942 CHECK_EQ(String::New(expected_slice_on_cons),
8943 env->Global()->Get(v8_str("slice_on_cons")));
8944 }
8945}
8946
8947
8948TEST(CompileExternalTwoByteSource) {
8949 v8::HandleScope scope;
8950 LocalContext context;
8951
8952 // This is a very short list of sources, which currently is to check for a
8953 // regression caused by r2703.
8954 const char* ascii_sources[] = {
8955 "0.5",
8956 "-0.5", // This mainly testes PushBack in the Scanner.
8957 "--0.5", // This mainly testes PushBack in the Scanner.
8958 NULL
8959 };
8960
8961 // Compile the sources as external two byte strings.
8962 for (int i = 0; ascii_sources[i] != NULL; i++) {
8963 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
8964 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008965 i::Vector<const uint16_t>(two_byte_string,
8966 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00008967 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
8968 v8::Script::Compile(source);
8969 }
8970}
8971
8972
8973class RegExpStringModificationTest {
8974 public:
8975 RegExpStringModificationTest()
8976 : block_(i::OS::CreateSemaphore(0)),
8977 morphs_(0),
8978 morphs_during_regexp_(0),
8979 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
8980 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
8981 ~RegExpStringModificationTest() { delete block_; }
8982 void RunTest() {
8983 regexp_success_ = false;
8984 morph_success_ = false;
8985
8986 // Initialize the contents of two_byte_content_ to be a uc16 representation
8987 // of "aaaaaaaaaaaaaab".
8988 for (int i = 0; i < 14; i++) {
8989 two_byte_content_[i] = 'a';
8990 }
8991 two_byte_content_[14] = 'b';
8992
8993 // Create the input string for the regexp - the one we are going to change
8994 // properties of.
8995 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
8996
8997 // Inject the input as a global variable.
8998 i::Handle<i::String> input_name =
8999 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
9000 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
9001
9002
9003 MorphThread morph_thread(this);
9004 morph_thread.Start();
9005 v8::Locker::StartPreemption(1);
9006 LongRunningRegExp();
9007 {
9008 v8::Unlocker unlock;
9009 morph_thread.Join();
9010 }
9011 v8::Locker::StopPreemption();
9012 CHECK(regexp_success_);
9013 CHECK(morph_success_);
9014 }
9015 private:
9016
9017 // Number of string modifications required.
9018 static const int kRequiredModifications = 5;
9019 static const int kMaxModifications = 100;
9020
9021 class MorphThread : public i::Thread {
9022 public:
9023 explicit MorphThread(RegExpStringModificationTest* test)
9024 : test_(test) {}
9025 virtual void Run() {
9026 test_->MorphString();
9027 }
9028 private:
9029 RegExpStringModificationTest* test_;
9030 };
9031
9032 void MorphString() {
9033 block_->Wait();
9034 while (morphs_during_regexp_ < kRequiredModifications &&
9035 morphs_ < kMaxModifications) {
9036 {
9037 v8::Locker lock;
9038 // Swap string between ascii and two-byte representation.
9039 i::String* string = *input_;
9040 MorphAString(string, &ascii_resource_, &uc16_resource_);
9041 morphs_++;
9042 }
9043 i::OS::Sleep(1);
9044 }
9045 morph_success_ = true;
9046 }
9047
9048 void LongRunningRegExp() {
9049 block_->Signal(); // Enable morphing thread on next preemption.
9050 while (morphs_during_regexp_ < kRequiredModifications &&
9051 morphs_ < kMaxModifications) {
9052 int morphs_before = morphs_;
9053 {
9054 // Match 15-30 "a"'s against 14 and a "b".
9055 const char* c_source =
9056 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9057 ".exec(input) === null";
9058 Local<String> source = String::New(c_source);
9059 Local<Script> script = Script::Compile(source);
9060 Local<Value> result = script->Run();
9061 CHECK(result->IsTrue());
9062 }
9063 int morphs_after = morphs_;
9064 morphs_during_regexp_ += morphs_after - morphs_before;
9065 }
9066 regexp_success_ = true;
9067 }
9068
9069 i::uc16 two_byte_content_[15];
9070 i::Semaphore* block_;
9071 int morphs_;
9072 int morphs_during_regexp_;
9073 bool regexp_success_;
9074 bool morph_success_;
9075 i::Handle<i::String> input_;
9076 AsciiVectorResource ascii_resource_;
9077 UC16VectorResource uc16_resource_;
9078};
9079
9080
9081// Test that a regular expression execution can be interrupted and
9082// the string changed without failing.
9083TEST(RegExpStringModification) {
9084 v8::Locker lock;
9085 v8::V8::Initialize();
9086 v8::HandleScope scope;
9087 Local<Context> local_env;
9088 {
9089 LocalContext env;
9090 local_env = env.local();
9091 }
9092
9093 // Local context should still be live.
9094 CHECK(!local_env.IsEmpty());
9095 local_env->Enter();
9096
9097 // Should complete without problems.
9098 RegExpStringModificationTest().RunTest();
9099
9100 local_env->Exit();
9101}
9102
9103
9104// Test that we can set a property on the global object even if there
9105// is a read-only property in the prototype chain.
9106TEST(ReadOnlyPropertyInGlobalProto) {
9107 v8::HandleScope scope;
9108 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9109 LocalContext context(0, templ);
9110 v8::Handle<v8::Object> global = context->Global();
9111 v8::Handle<v8::Object> global_proto =
9112 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9113 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9114 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9115 // Check without 'eval' or 'with'.
9116 v8::Handle<v8::Value> res =
9117 CompileRun("function f() { x = 42; return x; }; f()");
9118 // Check with 'eval'.
9119 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9120 CHECK_EQ(v8::Integer::New(42), res);
9121 // Check with 'with'.
9122 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9123 CHECK_EQ(v8::Integer::New(42), res);
9124}
9125
9126static int force_set_set_count = 0;
9127static int force_set_get_count = 0;
9128bool pass_on_get = false;
9129
9130static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9131 const v8::AccessorInfo& info) {
9132 force_set_get_count++;
9133 if (pass_on_get) {
9134 return v8::Handle<v8::Value>();
9135 } else {
9136 return v8::Int32::New(3);
9137 }
9138}
9139
9140static void ForceSetSetter(v8::Local<v8::String> name,
9141 v8::Local<v8::Value> value,
9142 const v8::AccessorInfo& info) {
9143 force_set_set_count++;
9144}
9145
9146static v8::Handle<v8::Value> ForceSetInterceptSetter(
9147 v8::Local<v8::String> name,
9148 v8::Local<v8::Value> value,
9149 const v8::AccessorInfo& info) {
9150 force_set_set_count++;
9151 return v8::Undefined();
9152}
9153
9154TEST(ForceSet) {
9155 force_set_get_count = 0;
9156 force_set_set_count = 0;
9157 pass_on_get = false;
9158
9159 v8::HandleScope scope;
9160 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9161 v8::Handle<v8::String> access_property = v8::String::New("a");
9162 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9163 LocalContext context(NULL, templ);
9164 v8::Handle<v8::Object> global = context->Global();
9165
9166 // Ordinary properties
9167 v8::Handle<v8::String> simple_property = v8::String::New("p");
9168 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9169 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9170 // This should fail because the property is read-only
9171 global->Set(simple_property, v8::Int32::New(5));
9172 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9173 // This should succeed even though the property is read-only
9174 global->ForceSet(simple_property, v8::Int32::New(6));
9175 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9176
9177 // Accessors
9178 CHECK_EQ(0, force_set_set_count);
9179 CHECK_EQ(0, force_set_get_count);
9180 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9181 // CHECK_EQ the property shouldn't override it, just call the setter
9182 // which in this case does nothing.
9183 global->Set(access_property, v8::Int32::New(7));
9184 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9185 CHECK_EQ(1, force_set_set_count);
9186 CHECK_EQ(2, force_set_get_count);
9187 // Forcing the property to be set should override the accessor without
9188 // calling it
9189 global->ForceSet(access_property, v8::Int32::New(8));
9190 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9191 CHECK_EQ(1, force_set_set_count);
9192 CHECK_EQ(2, force_set_get_count);
9193}
9194
9195TEST(ForceSetWithInterceptor) {
9196 force_set_get_count = 0;
9197 force_set_set_count = 0;
9198 pass_on_get = false;
9199
9200 v8::HandleScope scope;
9201 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9202 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9203 LocalContext context(NULL, templ);
9204 v8::Handle<v8::Object> global = context->Global();
9205
9206 v8::Handle<v8::String> some_property = v8::String::New("a");
9207 CHECK_EQ(0, force_set_set_count);
9208 CHECK_EQ(0, force_set_get_count);
9209 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9210 // Setting the property shouldn't override it, just call the setter
9211 // which in this case does nothing.
9212 global->Set(some_property, v8::Int32::New(7));
9213 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9214 CHECK_EQ(1, force_set_set_count);
9215 CHECK_EQ(2, force_set_get_count);
9216 // Getting the property when the interceptor returns an empty handle
9217 // should yield undefined, since the property isn't present on the
9218 // object itself yet.
9219 pass_on_get = true;
9220 CHECK(global->Get(some_property)->IsUndefined());
9221 CHECK_EQ(1, force_set_set_count);
9222 CHECK_EQ(3, force_set_get_count);
9223 // Forcing the property to be set should cause the value to be
9224 // set locally without calling the interceptor.
9225 global->ForceSet(some_property, v8::Int32::New(8));
9226 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9227 CHECK_EQ(1, force_set_set_count);
9228 CHECK_EQ(4, force_set_get_count);
9229 // Reenabling the interceptor should cause it to take precedence over
9230 // the property
9231 pass_on_get = false;
9232 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9233 CHECK_EQ(1, force_set_set_count);
9234 CHECK_EQ(5, force_set_get_count);
9235 // The interceptor should also work for other properties
9236 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9237 CHECK_EQ(1, force_set_set_count);
9238 CHECK_EQ(6, force_set_get_count);
9239}
9240
9241
9242THREADED_TEST(ForceDelete) {
9243 v8::HandleScope scope;
9244 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9245 LocalContext context(NULL, templ);
9246 v8::Handle<v8::Object> global = context->Global();
9247
9248 // Ordinary properties
9249 v8::Handle<v8::String> simple_property = v8::String::New("p");
9250 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9251 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9252 // This should fail because the property is dont-delete.
9253 CHECK(!global->Delete(simple_property));
9254 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9255 // This should succeed even though the property is dont-delete.
9256 CHECK(global->ForceDelete(simple_property));
9257 CHECK(global->Get(simple_property)->IsUndefined());
9258}
9259
9260
9261static int force_delete_interceptor_count = 0;
9262static bool pass_on_delete = false;
9263
9264
9265static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9266 v8::Local<v8::String> name,
9267 const v8::AccessorInfo& info) {
9268 force_delete_interceptor_count++;
9269 if (pass_on_delete) {
9270 return v8::Handle<v8::Boolean>();
9271 } else {
9272 return v8::True();
9273 }
9274}
9275
9276
9277THREADED_TEST(ForceDeleteWithInterceptor) {
9278 force_delete_interceptor_count = 0;
9279 pass_on_delete = false;
9280
9281 v8::HandleScope scope;
9282 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9283 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9284 LocalContext context(NULL, templ);
9285 v8::Handle<v8::Object> global = context->Global();
9286
9287 v8::Handle<v8::String> some_property = v8::String::New("a");
9288 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9289
9290 // Deleting a property should get intercepted and nothing should
9291 // happen.
9292 CHECK_EQ(0, force_delete_interceptor_count);
9293 CHECK(global->Delete(some_property));
9294 CHECK_EQ(1, force_delete_interceptor_count);
9295 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9296 // Deleting the property when the interceptor returns an empty
9297 // handle should not delete the property since it is DontDelete.
9298 pass_on_delete = true;
9299 CHECK(!global->Delete(some_property));
9300 CHECK_EQ(2, force_delete_interceptor_count);
9301 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9302 // Forcing the property to be deleted should delete the value
9303 // without calling the interceptor.
9304 CHECK(global->ForceDelete(some_property));
9305 CHECK(global->Get(some_property)->IsUndefined());
9306 CHECK_EQ(2, force_delete_interceptor_count);
9307}
9308
9309
9310// Make sure that forcing a delete invalidates any IC stubs, so we
9311// don't read the hole value.
9312THREADED_TEST(ForceDeleteIC) {
9313 v8::HandleScope scope;
9314 LocalContext context;
9315 // Create a DontDelete variable on the global object.
9316 CompileRun("this.__proto__ = { foo: 'horse' };"
9317 "var foo = 'fish';"
9318 "function f() { return foo.length; }");
9319 // Initialize the IC for foo in f.
9320 CompileRun("for (var i = 0; i < 4; i++) f();");
9321 // Make sure the value of foo is correct before the deletion.
9322 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9323 // Force the deletion of foo.
9324 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9325 // Make sure the value for foo is read from the prototype, and that
9326 // we don't get in trouble with reading the deleted cell value
9327 // sentinel.
9328 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9329}
9330
9331
9332v8::Persistent<Context> calling_context0;
9333v8::Persistent<Context> calling_context1;
9334v8::Persistent<Context> calling_context2;
9335
9336
9337// Check that the call to the callback is initiated in
9338// calling_context2, the directly calling context is calling_context1
9339// and the callback itself is in calling_context0.
9340static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9341 ApiTestFuzzer::Fuzz();
9342 CHECK(Context::GetCurrent() == calling_context0);
9343 CHECK(Context::GetCalling() == calling_context1);
9344 CHECK(Context::GetEntered() == calling_context2);
9345 return v8::Integer::New(42);
9346}
9347
9348
9349THREADED_TEST(GetCallingContext) {
9350 v8::HandleScope scope;
9351
9352 calling_context0 = Context::New();
9353 calling_context1 = Context::New();
9354 calling_context2 = Context::New();
9355
9356 // Allow cross-domain access.
9357 Local<String> token = v8_str("<security token>");
9358 calling_context0->SetSecurityToken(token);
9359 calling_context1->SetSecurityToken(token);
9360 calling_context2->SetSecurityToken(token);
9361
9362 // Create an object with a C++ callback in context0.
9363 calling_context0->Enter();
9364 Local<v8::FunctionTemplate> callback_templ =
9365 v8::FunctionTemplate::New(GetCallingContextCallback);
9366 calling_context0->Global()->Set(v8_str("callback"),
9367 callback_templ->GetFunction());
9368 calling_context0->Exit();
9369
9370 // Expose context0 in context1 and setup a function that calls the
9371 // callback function.
9372 calling_context1->Enter();
9373 calling_context1->Global()->Set(v8_str("context0"),
9374 calling_context0->Global());
9375 CompileRun("function f() { context0.callback() }");
9376 calling_context1->Exit();
9377
9378 // Expose context1 in context2 and call the callback function in
9379 // context0 indirectly through f in context1.
9380 calling_context2->Enter();
9381 calling_context2->Global()->Set(v8_str("context1"),
9382 calling_context1->Global());
9383 CompileRun("context1.f()");
9384 calling_context2->Exit();
9385
9386 // Dispose the contexts to allow them to be garbage collected.
9387 calling_context0.Dispose();
9388 calling_context1.Dispose();
9389 calling_context2.Dispose();
9390 calling_context0.Clear();
9391 calling_context1.Clear();
9392 calling_context2.Clear();
9393}
9394
9395
9396// Check that a variable declaration with no explicit initialization
9397// value does not shadow an existing property in the prototype chain.
9398//
9399// This is consistent with Firefox and Safari.
9400//
9401// See http://crbug.com/12548.
9402THREADED_TEST(InitGlobalVarInProtoChain) {
9403 v8::HandleScope scope;
9404 LocalContext context;
9405 // Introduce a variable in the prototype chain.
9406 CompileRun("__proto__.x = 42");
9407 v8::Handle<v8::Value> result = CompileRun("var x; x");
9408 CHECK(!result->IsUndefined());
9409 CHECK_EQ(42, result->Int32Value());
9410}
9411
9412
9413// Regression test for issue 398.
9414// If a function is added to an object, creating a constant function
9415// field, and the result is cloned, replacing the constant function on the
9416// original should not affect the clone.
9417// See http://code.google.com/p/v8/issues/detail?id=398
9418THREADED_TEST(ReplaceConstantFunction) {
9419 v8::HandleScope scope;
9420 LocalContext context;
9421 v8::Handle<v8::Object> obj = v8::Object::New();
9422 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9423 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9424 obj->Set(foo_string, func_templ->GetFunction());
9425 v8::Handle<v8::Object> obj_clone = obj->Clone();
9426 obj_clone->Set(foo_string, v8::String::New("Hello"));
9427 CHECK(!obj->Get(foo_string)->IsUndefined());
9428}
9429
9430
9431// Regression test for http://crbug.com/16276.
9432THREADED_TEST(Regress16276) {
9433 v8::HandleScope scope;
9434 LocalContext context;
9435 // Force the IC in f to be a dictionary load IC.
9436 CompileRun("function f(obj) { return obj.x; }\n"
9437 "var obj = { x: { foo: 42 }, y: 87 };\n"
9438 "var x = obj.x;\n"
9439 "delete obj.y;\n"
9440 "for (var i = 0; i < 5; i++) f(obj);");
9441 // Detach the global object to make 'this' refer directly to the
9442 // global object (not the proxy), and make sure that the dictionary
9443 // load IC doesn't mess up loading directly from the global object.
9444 context->DetachGlobal();
9445 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9446}
9447
9448
9449THREADED_TEST(PixelArray) {
9450 v8::HandleScope scope;
9451 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009452 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009453 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9454 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9455 pixel_data);
9456 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9457 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009458 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009459 }
9460 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9461 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009462 CHECK_EQ(i % 256, pixels->get(i));
9463 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009464 }
9465
9466 v8::Handle<v8::Object> obj = v8::Object::New();
9467 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9468 // Set the elements to be the pixels.
9469 // jsobj->set_elements(*pixels);
9470 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9471 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9472 obj->Set(v8_str("field"), v8::Int32::New(1503));
9473 context->Global()->Set(v8_str("pixels"), obj);
9474 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9475 CHECK_EQ(1503, result->Int32Value());
9476 result = CompileRun("pixels[1]");
9477 CHECK_EQ(1, result->Int32Value());
9478
9479 result = CompileRun("var sum = 0;"
9480 "for (var i = 0; i < 8; i++) {"
9481 " sum += pixels[i] = pixels[i] = -i;"
9482 "}"
9483 "sum;");
9484 CHECK_EQ(-28, result->Int32Value());
9485
9486 result = CompileRun("var sum = 0;"
9487 "for (var i = 0; i < 8; i++) {"
9488 " sum += pixels[i] = pixels[i] = 0;"
9489 "}"
9490 "sum;");
9491 CHECK_EQ(0, result->Int32Value());
9492
9493 result = CompileRun("var sum = 0;"
9494 "for (var i = 0; i < 8; i++) {"
9495 " sum += pixels[i] = pixels[i] = 255;"
9496 "}"
9497 "sum;");
9498 CHECK_EQ(8 * 255, result->Int32Value());
9499
9500 result = CompileRun("var sum = 0;"
9501 "for (var i = 0; i < 8; i++) {"
9502 " sum += pixels[i] = pixels[i] = 256 + i;"
9503 "}"
9504 "sum;");
9505 CHECK_EQ(2076, result->Int32Value());
9506
9507 result = CompileRun("var sum = 0;"
9508 "for (var i = 0; i < 8; i++) {"
9509 " sum += pixels[i] = pixels[i] = i;"
9510 "}"
9511 "sum;");
9512 CHECK_EQ(28, result->Int32Value());
9513
9514 result = CompileRun("var sum = 0;"
9515 "for (var i = 0; i < 8; i++) {"
9516 " sum += pixels[i];"
9517 "}"
9518 "sum;");
9519 CHECK_EQ(28, result->Int32Value());
9520
9521 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9522 i::SetElement(jsobj, 1, value);
9523 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9524 *value.location() = i::Smi::FromInt(256);
9525 i::SetElement(jsobj, 1, value);
9526 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9527 *value.location() = i::Smi::FromInt(-1);
9528 i::SetElement(jsobj, 1, value);
9529 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9530
9531 result = CompileRun("for (var i = 0; i < 8; i++) {"
9532 " pixels[i] = (i * 65) - 109;"
9533 "}"
9534 "pixels[1] + pixels[6];");
9535 CHECK_EQ(255, result->Int32Value());
9536 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9537 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9538 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9539 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9540 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9541 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9542 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9543 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9544 result = CompileRun("var sum = 0;"
9545 "for (var i = 0; i < 8; i++) {"
9546 " sum += pixels[i];"
9547 "}"
9548 "sum;");
9549 CHECK_EQ(984, result->Int32Value());
9550
9551 result = CompileRun("for (var i = 0; i < 8; i++) {"
9552 " pixels[i] = (i * 1.1);"
9553 "}"
9554 "pixels[1] + pixels[6];");
9555 CHECK_EQ(8, result->Int32Value());
9556 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9557 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9558 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9559 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9560 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9561 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9562 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9563 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9564
9565 result = CompileRun("for (var i = 0; i < 8; i++) {"
9566 " pixels[7] = undefined;"
9567 "}"
9568 "pixels[7];");
9569 CHECK_EQ(0, result->Int32Value());
9570 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9571
9572 result = CompileRun("for (var i = 0; i < 8; i++) {"
9573 " pixels[6] = '2.3';"
9574 "}"
9575 "pixels[6];");
9576 CHECK_EQ(2, result->Int32Value());
9577 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9578
9579 result = CompileRun("for (var i = 0; i < 8; i++) {"
9580 " pixels[5] = NaN;"
9581 "}"
9582 "pixels[5];");
9583 CHECK_EQ(0, result->Int32Value());
9584 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9585
9586 result = CompileRun("for (var i = 0; i < 8; i++) {"
9587 " pixels[8] = Infinity;"
9588 "}"
9589 "pixels[8];");
9590 CHECK_EQ(255, result->Int32Value());
9591 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9592
9593 result = CompileRun("for (var i = 0; i < 8; i++) {"
9594 " pixels[9] = -Infinity;"
9595 "}"
9596 "pixels[9];");
9597 CHECK_EQ(0, result->Int32Value());
9598 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9599
9600 result = CompileRun("pixels[3] = 33;"
9601 "delete pixels[3];"
9602 "pixels[3];");
9603 CHECK_EQ(33, result->Int32Value());
9604
9605 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9606 "pixels[2] = 12; pixels[3] = 13;"
9607 "pixels.__defineGetter__('2',"
9608 "function() { return 120; });"
9609 "pixels[2];");
9610 CHECK_EQ(12, result->Int32Value());
9611
9612 result = CompileRun("var js_array = new Array(40);"
9613 "js_array[0] = 77;"
9614 "js_array;");
9615 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9616
9617 result = CompileRun("pixels[1] = 23;"
9618 "pixels.__proto__ = [];"
9619 "js_array.__proto__ = pixels;"
9620 "js_array.concat(pixels);");
9621 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9622 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9623
9624 result = CompileRun("pixels[1] = 23;");
9625 CHECK_EQ(23, result->Int32Value());
9626
Steve Blockd0582a62009-12-15 09:54:21 +00009627 // Test for index greater than 255. Regression test for:
9628 // http://code.google.com/p/chromium/issues/detail?id=26337.
9629 result = CompileRun("pixels[256] = 255;");
9630 CHECK_EQ(255, result->Int32Value());
9631 result = CompileRun("var i = 0;"
9632 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9633 "i");
9634 CHECK_EQ(255, result->Int32Value());
9635
Steve Blocka7e24c12009-10-30 11:49:00 +00009636 free(pixel_data);
9637}
9638
9639
Steve Block3ce2e202009-11-05 08:53:23 +00009640template <class ExternalArrayClass, class ElementType>
9641static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9642 int64_t low,
9643 int64_t high) {
9644 v8::HandleScope scope;
9645 LocalContext context;
9646 const int kElementCount = 40;
9647 int element_size = 0;
9648 switch (array_type) {
9649 case v8::kExternalByteArray:
9650 case v8::kExternalUnsignedByteArray:
9651 element_size = 1;
9652 break;
9653 case v8::kExternalShortArray:
9654 case v8::kExternalUnsignedShortArray:
9655 element_size = 2;
9656 break;
9657 case v8::kExternalIntArray:
9658 case v8::kExternalUnsignedIntArray:
9659 case v8::kExternalFloatArray:
9660 element_size = 4;
9661 break;
9662 default:
9663 UNREACHABLE();
9664 break;
9665 }
9666 ElementType* array_data =
9667 static_cast<ElementType*>(malloc(kElementCount * element_size));
9668 i::Handle<ExternalArrayClass> array =
9669 i::Handle<ExternalArrayClass>::cast(
9670 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9671 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9672 for (int i = 0; i < kElementCount; i++) {
9673 array->set(i, static_cast<ElementType>(i));
9674 }
9675 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9676 for (int i = 0; i < kElementCount; i++) {
9677 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9678 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9679 }
9680
9681 v8::Handle<v8::Object> obj = v8::Object::New();
9682 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9683 // Set the elements to be the external array.
9684 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9685 array_type,
9686 kElementCount);
9687 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9688 obj->Set(v8_str("field"), v8::Int32::New(1503));
9689 context->Global()->Set(v8_str("ext_array"), obj);
9690 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9691 CHECK_EQ(1503, result->Int32Value());
9692 result = CompileRun("ext_array[1]");
9693 CHECK_EQ(1, result->Int32Value());
9694
9695 // Check pass through of assigned smis
9696 result = CompileRun("var sum = 0;"
9697 "for (var i = 0; i < 8; i++) {"
9698 " sum += ext_array[i] = ext_array[i] = -i;"
9699 "}"
9700 "sum;");
9701 CHECK_EQ(-28, result->Int32Value());
9702
9703 // Check assigned smis
9704 result = CompileRun("for (var i = 0; i < 8; i++) {"
9705 " ext_array[i] = i;"
9706 "}"
9707 "var sum = 0;"
9708 "for (var i = 0; i < 8; i++) {"
9709 " sum += ext_array[i];"
9710 "}"
9711 "sum;");
9712 CHECK_EQ(28, result->Int32Value());
9713
9714 // Check assigned smis in reverse order
9715 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9716 " ext_array[i] = i;"
9717 "}"
9718 "var sum = 0;"
9719 "for (var i = 0; i < 8; i++) {"
9720 " sum += ext_array[i];"
9721 "}"
9722 "sum;");
9723 CHECK_EQ(28, result->Int32Value());
9724
9725 // Check pass through of assigned HeapNumbers
9726 result = CompileRun("var sum = 0;"
9727 "for (var i = 0; i < 16; i+=2) {"
9728 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9729 "}"
9730 "sum;");
9731 CHECK_EQ(-28, result->Int32Value());
9732
9733 // Check assigned HeapNumbers
9734 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9735 " ext_array[i] = (i * 0.5);"
9736 "}"
9737 "var sum = 0;"
9738 "for (var i = 0; i < 16; i+=2) {"
9739 " sum += ext_array[i];"
9740 "}"
9741 "sum;");
9742 CHECK_EQ(28, result->Int32Value());
9743
9744 // Check assigned HeapNumbers in reverse order
9745 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9746 " ext_array[i] = (i * 0.5);"
9747 "}"
9748 "var sum = 0;"
9749 "for (var i = 0; i < 16; i+=2) {"
9750 " sum += ext_array[i];"
9751 "}"
9752 "sum;");
9753 CHECK_EQ(28, result->Int32Value());
9754
9755 i::ScopedVector<char> test_buf(1024);
9756
9757 // Check legal boundary conditions.
9758 // The repeated loads and stores ensure the ICs are exercised.
9759 const char* boundary_program =
9760 "var res = 0;"
9761 "for (var i = 0; i < 16; i++) {"
9762 " ext_array[i] = %lld;"
9763 " if (i > 8) {"
9764 " res = ext_array[i];"
9765 " }"
9766 "}"
9767 "res;";
9768 i::OS::SNPrintF(test_buf,
9769 boundary_program,
9770 low);
9771 result = CompileRun(test_buf.start());
9772 CHECK_EQ(low, result->IntegerValue());
9773
9774 i::OS::SNPrintF(test_buf,
9775 boundary_program,
9776 high);
9777 result = CompileRun(test_buf.start());
9778 CHECK_EQ(high, result->IntegerValue());
9779
9780 // Check misprediction of type in IC.
9781 result = CompileRun("var tmp_array = ext_array;"
9782 "var sum = 0;"
9783 "for (var i = 0; i < 8; i++) {"
9784 " tmp_array[i] = i;"
9785 " sum += tmp_array[i];"
9786 " if (i == 4) {"
9787 " tmp_array = {};"
9788 " }"
9789 "}"
9790 "sum;");
9791 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9792 CHECK_EQ(28, result->Int32Value());
9793
9794 // Make sure out-of-range loads do not throw.
9795 i::OS::SNPrintF(test_buf,
9796 "var caught_exception = false;"
9797 "try {"
9798 " ext_array[%d];"
9799 "} catch (e) {"
9800 " caught_exception = true;"
9801 "}"
9802 "caught_exception;",
9803 kElementCount);
9804 result = CompileRun(test_buf.start());
9805 CHECK_EQ(false, result->BooleanValue());
9806
9807 // Make sure out-of-range stores do not throw.
9808 i::OS::SNPrintF(test_buf,
9809 "var caught_exception = false;"
9810 "try {"
9811 " ext_array[%d] = 1;"
9812 "} catch (e) {"
9813 " caught_exception = true;"
9814 "}"
9815 "caught_exception;",
9816 kElementCount);
9817 result = CompileRun(test_buf.start());
9818 CHECK_EQ(false, result->BooleanValue());
9819
9820 // Check other boundary conditions, values and operations.
9821 result = CompileRun("for (var i = 0; i < 8; i++) {"
9822 " ext_array[7] = undefined;"
9823 "}"
9824 "ext_array[7];");
9825 CHECK_EQ(0, result->Int32Value());
9826 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
9827
9828 result = CompileRun("for (var i = 0; i < 8; i++) {"
9829 " ext_array[6] = '2.3';"
9830 "}"
9831 "ext_array[6];");
9832 CHECK_EQ(2, result->Int32Value());
9833 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
9834
9835 if (array_type != v8::kExternalFloatArray) {
9836 // Though the specification doesn't state it, be explicit about
9837 // converting NaNs and +/-Infinity to zero.
9838 result = CompileRun("for (var i = 0; i < 8; i++) {"
9839 " ext_array[i] = 5;"
9840 "}"
9841 "for (var i = 0; i < 8; i++) {"
9842 " ext_array[i] = NaN;"
9843 "}"
9844 "ext_array[5];");
9845 CHECK_EQ(0, result->Int32Value());
9846 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9847
9848 result = CompileRun("for (var i = 0; i < 8; i++) {"
9849 " ext_array[i] = 5;"
9850 "}"
9851 "for (var i = 0; i < 8; i++) {"
9852 " ext_array[i] = Infinity;"
9853 "}"
9854 "ext_array[5];");
9855 CHECK_EQ(0, result->Int32Value());
9856 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9857
9858 result = CompileRun("for (var i = 0; i < 8; i++) {"
9859 " ext_array[i] = 5;"
9860 "}"
9861 "for (var i = 0; i < 8; i++) {"
9862 " ext_array[i] = -Infinity;"
9863 "}"
9864 "ext_array[5];");
9865 CHECK_EQ(0, result->Int32Value());
9866 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9867 }
9868
9869 result = CompileRun("ext_array[3] = 33;"
9870 "delete ext_array[3];"
9871 "ext_array[3];");
9872 CHECK_EQ(33, result->Int32Value());
9873
9874 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
9875 "ext_array[2] = 12; ext_array[3] = 13;"
9876 "ext_array.__defineGetter__('2',"
9877 "function() { return 120; });"
9878 "ext_array[2];");
9879 CHECK_EQ(12, result->Int32Value());
9880
9881 result = CompileRun("var js_array = new Array(40);"
9882 "js_array[0] = 77;"
9883 "js_array;");
9884 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9885
9886 result = CompileRun("ext_array[1] = 23;"
9887 "ext_array.__proto__ = [];"
9888 "js_array.__proto__ = ext_array;"
9889 "js_array.concat(ext_array);");
9890 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9891 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9892
9893 result = CompileRun("ext_array[1] = 23;");
9894 CHECK_EQ(23, result->Int32Value());
9895
Steve Blockd0582a62009-12-15 09:54:21 +00009896 // Test more complex manipulations which cause eax to contain values
9897 // that won't be completely overwritten by loads from the arrays.
9898 // This catches bugs in the instructions used for the KeyedLoadIC
9899 // for byte and word types.
9900 {
9901 const int kXSize = 300;
9902 const int kYSize = 300;
9903 const int kLargeElementCount = kXSize * kYSize * 4;
9904 ElementType* large_array_data =
9905 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
9906 i::Handle<ExternalArrayClass> large_array =
9907 i::Handle<ExternalArrayClass>::cast(
9908 i::Factory::NewExternalArray(kLargeElementCount,
9909 array_type,
9910 array_data));
9911 v8::Handle<v8::Object> large_obj = v8::Object::New();
9912 // Set the elements to be the external array.
9913 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
9914 array_type,
9915 kLargeElementCount);
9916 context->Global()->Set(v8_str("large_array"), large_obj);
9917 // Initialize contents of a few rows.
9918 for (int x = 0; x < 300; x++) {
9919 int row = 0;
9920 int offset = row * 300 * 4;
9921 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9922 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9923 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9924 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9925 row = 150;
9926 offset = row * 300 * 4;
9927 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9928 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9929 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9930 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9931 row = 298;
9932 offset = row * 300 * 4;
9933 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
9934 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
9935 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
9936 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
9937 }
9938 // The goal of the code below is to make "offset" large enough
9939 // that the computation of the index (which goes into eax) has
9940 // high bits set which will not be overwritten by a byte or short
9941 // load.
9942 result = CompileRun("var failed = false;"
9943 "var offset = 0;"
9944 "for (var i = 0; i < 300; i++) {"
9945 " if (large_array[4 * i] != 127 ||"
9946 " large_array[4 * i + 1] != 0 ||"
9947 " large_array[4 * i + 2] != 0 ||"
9948 " large_array[4 * i + 3] != 127) {"
9949 " failed = true;"
9950 " }"
9951 "}"
9952 "offset = 150 * 300 * 4;"
9953 "for (var i = 0; i < 300; i++) {"
9954 " if (large_array[offset + 4 * i] != 127 ||"
9955 " large_array[offset + 4 * i + 1] != 0 ||"
9956 " large_array[offset + 4 * i + 2] != 0 ||"
9957 " large_array[offset + 4 * i + 3] != 127) {"
9958 " failed = true;"
9959 " }"
9960 "}"
9961 "offset = 298 * 300 * 4;"
9962 "for (var i = 0; i < 300; i++) {"
9963 " if (large_array[offset + 4 * i] != 127 ||"
9964 " large_array[offset + 4 * i + 1] != 0 ||"
9965 " large_array[offset + 4 * i + 2] != 0 ||"
9966 " large_array[offset + 4 * i + 3] != 127) {"
9967 " failed = true;"
9968 " }"
9969 "}"
9970 "!failed;");
9971 CHECK_EQ(true, result->BooleanValue());
9972 free(large_array_data);
9973 }
9974
Steve Block3ce2e202009-11-05 08:53:23 +00009975 free(array_data);
9976}
9977
9978
9979THREADED_TEST(ExternalByteArray) {
9980 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>(
9981 v8::kExternalByteArray,
9982 -128,
9983 127);
9984}
9985
9986
9987THREADED_TEST(ExternalUnsignedByteArray) {
9988 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>(
9989 v8::kExternalUnsignedByteArray,
9990 0,
9991 255);
9992}
9993
9994
9995THREADED_TEST(ExternalShortArray) {
9996 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>(
9997 v8::kExternalShortArray,
9998 -32768,
9999 32767);
10000}
10001
10002
10003THREADED_TEST(ExternalUnsignedShortArray) {
10004 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>(
10005 v8::kExternalUnsignedShortArray,
10006 0,
10007 65535);
10008}
10009
10010
10011THREADED_TEST(ExternalIntArray) {
10012 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>(
10013 v8::kExternalIntArray,
10014 INT_MIN, // -2147483648
10015 INT_MAX); // 2147483647
10016}
10017
10018
10019THREADED_TEST(ExternalUnsignedIntArray) {
10020 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>(
10021 v8::kExternalUnsignedIntArray,
10022 0,
10023 UINT_MAX); // 4294967295
10024}
10025
10026
10027THREADED_TEST(ExternalFloatArray) {
10028 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>(
10029 v8::kExternalFloatArray,
10030 -500,
10031 500);
10032}
10033
10034
10035THREADED_TEST(ExternalArrays) {
10036 TestExternalByteArray();
10037 TestExternalUnsignedByteArray();
10038 TestExternalShortArray();
10039 TestExternalUnsignedShortArray();
10040 TestExternalIntArray();
10041 TestExternalUnsignedIntArray();
10042 TestExternalFloatArray();
10043}
10044
10045
Steve Blocka7e24c12009-10-30 11:49:00 +000010046THREADED_TEST(ScriptContextDependence) {
10047 v8::HandleScope scope;
10048 LocalContext c1;
10049 const char *source = "foo";
10050 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10051 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10052 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10053 CHECK_EQ(dep->Run()->Int32Value(), 100);
10054 CHECK_EQ(indep->Run()->Int32Value(), 100);
10055 LocalContext c2;
10056 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10057 CHECK_EQ(dep->Run()->Int32Value(), 100);
10058 CHECK_EQ(indep->Run()->Int32Value(), 101);
10059}
10060
10061
10062THREADED_TEST(StackTrace) {
10063 v8::HandleScope scope;
10064 LocalContext context;
10065 v8::TryCatch try_catch;
10066 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10067 v8::Handle<v8::String> src = v8::String::New(source);
10068 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10069 v8::Script::New(src, origin)->Run();
10070 CHECK(try_catch.HasCaught());
10071 v8::String::Utf8Value stack(try_catch.StackTrace());
10072 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10073}
10074
10075
Kristian Monsen25f61362010-05-21 11:50:48 +010010076// Checks that a StackFrame has certain expected values.
10077void checkStackFrame(const char* expected_script_name,
10078 const char* expected_func_name, int expected_line_number,
10079 int expected_column, bool is_eval, bool is_constructor,
10080 v8::Handle<v8::StackFrame> frame) {
10081 v8::HandleScope scope;
10082 v8::String::Utf8Value func_name(frame->GetFunctionName());
10083 v8::String::Utf8Value script_name(frame->GetScriptName());
10084 if (*script_name == NULL) {
10085 // The situation where there is no associated script, like for evals.
10086 CHECK(expected_script_name == NULL);
10087 } else {
10088 CHECK(strstr(*script_name, expected_script_name) != NULL);
10089 }
10090 CHECK(strstr(*func_name, expected_func_name) != NULL);
10091 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10092 CHECK_EQ(expected_column, frame->GetColumn());
10093 CHECK_EQ(is_eval, frame->IsEval());
10094 CHECK_EQ(is_constructor, frame->IsConstructor());
10095}
10096
10097
10098v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10099 v8::HandleScope scope;
10100 const char* origin = "capture-stack-trace-test";
10101 const int kOverviewTest = 1;
10102 const int kDetailedTest = 2;
10103
10104 ASSERT(args.Length() == 1);
10105
10106 int testGroup = args[0]->Int32Value();
10107 if (testGroup == kOverviewTest) {
10108 v8::Handle<v8::StackTrace> stackTrace =
10109 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10110 CHECK_EQ(4, stackTrace->GetFrameCount());
10111 checkStackFrame(origin, "bar", 2, 10, false, false,
10112 stackTrace->GetFrame(0));
10113 checkStackFrame(origin, "foo", 6, 3, false, false,
10114 stackTrace->GetFrame(1));
10115 checkStackFrame(NULL, "", 1, 1, false, false,
10116 stackTrace->GetFrame(2));
10117 // The last frame is an anonymous function that has the initial call.
10118 checkStackFrame(origin, "", 8, 7, false, false,
10119 stackTrace->GetFrame(3));
10120
10121 CHECK(stackTrace->AsArray()->IsArray());
10122 } else if (testGroup == kDetailedTest) {
10123 v8::Handle<v8::StackTrace> stackTrace =
10124 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10125 CHECK_EQ(4, stackTrace->GetFrameCount());
10126 checkStackFrame(origin, "bat", 4, 22, false, false,
10127 stackTrace->GetFrame(0));
10128 checkStackFrame(origin, "baz", 8, 3, false, true,
10129 stackTrace->GetFrame(1));
10130 checkStackFrame(NULL, "", 1, 1, true, false,
10131 stackTrace->GetFrame(2));
10132 // The last frame is an anonymous function that has the initial call to foo.
10133 checkStackFrame(origin, "", 10, 1, false, false,
10134 stackTrace->GetFrame(3));
10135
10136 CHECK(stackTrace->AsArray()->IsArray());
10137 }
10138 return v8::Undefined();
10139}
10140
10141
10142// Tests the C++ StackTrace API.
10143THREADED_TEST(CaptureStackTrace) {
10144 v8::HandleScope scope;
10145 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10146 Local<ObjectTemplate> templ = ObjectTemplate::New();
10147 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10148 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10149 LocalContext context(0, templ);
10150
10151 // Test getting OVERVIEW information. Should ignore information that is not
10152 // script name, function name, line number, and column offset.
10153 const char *overview_source =
10154 "function bar() {\n"
10155 " var y; AnalyzeStackInNativeCode(1);\n"
10156 "}\n"
10157 "function foo() {\n"
10158 "\n"
10159 " bar();\n"
10160 "}\n"
10161 "var x;eval('new foo();');";
10162 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10163 v8::Handle<Value> overview_result =
10164 v8::Script::New(overview_src, origin)->Run();
10165 ASSERT(!overview_result.IsEmpty());
10166 ASSERT(overview_result->IsObject());
10167
10168 // Test getting DETAILED information.
10169 const char *detailed_source =
10170 "function bat() {AnalyzeStackInNativeCode(2);\n"
10171 "}\n"
10172 "\n"
10173 "function baz() {\n"
10174 " bat();\n"
10175 "}\n"
10176 "eval('new baz();');";
10177 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10178 // Make the script using a non-zero line and column offset.
10179 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10180 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10181 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10182 v8::Handle<v8::Script> detailed_script(
10183 v8::Script::New(detailed_src, &detailed_origin));
10184 v8::Handle<Value> detailed_result = detailed_script->Run();
10185 ASSERT(!detailed_result.IsEmpty());
10186 ASSERT(detailed_result->IsObject());
10187}
10188
10189
Steve Block3ce2e202009-11-05 08:53:23 +000010190// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010191THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010192 bool rv = false;
10193 for (int i = 0; i < 100; i++) {
10194 rv = v8::V8::IdleNotification();
10195 if (rv)
10196 break;
10197 }
10198 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010199}
10200
10201
10202static uint32_t* stack_limit;
10203
10204static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10205 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10206 return v8::Undefined();
10207}
10208
10209
10210// Uses the address of a local variable to determine the stack top now.
10211// Given a size, returns an address that is that far from the current
10212// top of stack.
10213static uint32_t* ComputeStackLimit(uint32_t size) {
10214 uint32_t* answer = &size - (size / sizeof(size));
10215 // If the size is very large and the stack is very near the bottom of
10216 // memory then the calculation above may wrap around and give an address
10217 // that is above the (downwards-growing) stack. In that case we return
10218 // a very low address.
10219 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10220 return answer;
10221}
10222
10223
10224TEST(SetResourceConstraints) {
10225 static const int K = 1024;
10226 uint32_t* set_limit = ComputeStackLimit(128 * K);
10227
10228 // Set stack limit.
10229 v8::ResourceConstraints constraints;
10230 constraints.set_stack_limit(set_limit);
10231 CHECK(v8::SetResourceConstraints(&constraints));
10232
10233 // Execute a script.
10234 v8::HandleScope scope;
10235 LocalContext env;
10236 Local<v8::FunctionTemplate> fun_templ =
10237 v8::FunctionTemplate::New(GetStackLimitCallback);
10238 Local<Function> fun = fun_templ->GetFunction();
10239 env->Global()->Set(v8_str("get_stack_limit"), fun);
10240 CompileRun("get_stack_limit();");
10241
10242 CHECK(stack_limit == set_limit);
10243}
10244
10245
10246TEST(SetResourceConstraintsInThread) {
10247 uint32_t* set_limit;
10248 {
10249 v8::Locker locker;
10250 static const int K = 1024;
10251 set_limit = ComputeStackLimit(128 * K);
10252
10253 // Set stack limit.
10254 v8::ResourceConstraints constraints;
10255 constraints.set_stack_limit(set_limit);
10256 CHECK(v8::SetResourceConstraints(&constraints));
10257
10258 // Execute a script.
10259 v8::HandleScope scope;
10260 LocalContext env;
10261 Local<v8::FunctionTemplate> fun_templ =
10262 v8::FunctionTemplate::New(GetStackLimitCallback);
10263 Local<Function> fun = fun_templ->GetFunction();
10264 env->Global()->Set(v8_str("get_stack_limit"), fun);
10265 CompileRun("get_stack_limit();");
10266
10267 CHECK(stack_limit == set_limit);
10268 }
10269 {
10270 v8::Locker locker;
10271 CHECK(stack_limit == set_limit);
10272 }
10273}
Steve Block3ce2e202009-11-05 08:53:23 +000010274
10275
10276THREADED_TEST(GetHeapStatistics) {
10277 v8::HandleScope scope;
10278 LocalContext c1;
10279 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010280 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10281 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010282 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010283 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10284 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10285}
10286
10287
10288static double DoubleFromBits(uint64_t value) {
10289 double target;
10290#ifdef BIG_ENDIAN_FLOATING_POINT
10291 const int kIntSize = 4;
10292 // Somebody swapped the lower and higher half of doubles.
10293 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10294 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10295#else
10296 memcpy(&target, &value, sizeof(target));
10297#endif
10298 return target;
10299}
10300
10301
10302static uint64_t DoubleToBits(double value) {
10303 uint64_t target;
10304#ifdef BIG_ENDIAN_FLOATING_POINT
10305 const int kIntSize = 4;
10306 // Somebody swapped the lower and higher half of doubles.
10307 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10308 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10309#else
10310 memcpy(&target, &value, sizeof(target));
10311#endif
10312 return target;
10313}
10314
10315
10316static double DoubleToDateTime(double input) {
10317 double date_limit = 864e13;
10318 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10319 return i::OS::nan_value();
10320 }
10321 return (input < 0) ? -(floor(-input)) : floor(input);
10322}
10323
10324// We don't have a consistent way to write 64-bit constants syntactically, so we
10325// split them into two 32-bit constants and combine them programmatically.
10326static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10327 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10328}
10329
10330
10331THREADED_TEST(QuietSignalingNaNs) {
10332 v8::HandleScope scope;
10333 LocalContext context;
10334 v8::TryCatch try_catch;
10335
10336 // Special double values.
10337 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10338 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10339 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10340 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10341 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10342 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10343 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10344
10345 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10346 // on either side of the epoch.
10347 double date_limit = 864e13;
10348
10349 double test_values[] = {
10350 snan,
10351 qnan,
10352 infinity,
10353 max_normal,
10354 date_limit + 1,
10355 date_limit,
10356 min_normal,
10357 max_denormal,
10358 min_denormal,
10359 0,
10360 -0,
10361 -min_denormal,
10362 -max_denormal,
10363 -min_normal,
10364 -date_limit,
10365 -date_limit - 1,
10366 -max_normal,
10367 -infinity,
10368 -qnan,
10369 -snan
10370 };
10371 int num_test_values = 20;
10372
10373 for (int i = 0; i < num_test_values; i++) {
10374 double test_value = test_values[i];
10375
10376 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10377 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10378 double stored_number = number->NumberValue();
10379 if (!IsNaN(test_value)) {
10380 CHECK_EQ(test_value, stored_number);
10381 } else {
10382 uint64_t stored_bits = DoubleToBits(stored_number);
10383 // Check if quiet nan (bits 51..62 all set).
10384 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10385 }
10386
10387 // Check that Date::New preserves non-NaNs in the date range and
10388 // quiets SNaNs.
10389 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10390 double expected_stored_date = DoubleToDateTime(test_value);
10391 double stored_date = date->NumberValue();
10392 if (!IsNaN(expected_stored_date)) {
10393 CHECK_EQ(expected_stored_date, stored_date);
10394 } else {
10395 uint64_t stored_bits = DoubleToBits(stored_date);
10396 // Check if quiet nan (bits 51..62 all set).
10397 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10398 }
10399 }
10400}
10401
10402
10403static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10404 v8::HandleScope scope;
10405 v8::TryCatch tc;
10406 v8::Handle<v8::String> str = args[0]->ToString();
10407 if (tc.HasCaught())
10408 return tc.ReThrow();
10409 return v8::Undefined();
10410}
10411
10412
10413// Test that an exception can be propagated down through a spaghetti
10414// stack using ReThrow.
10415THREADED_TEST(SpaghettiStackReThrow) {
10416 v8::HandleScope scope;
10417 LocalContext context;
10418 context->Global()->Set(
10419 v8::String::New("s"),
10420 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10421 v8::TryCatch try_catch;
10422 CompileRun(
10423 "var i = 0;"
10424 "var o = {"
10425 " toString: function () {"
10426 " if (i == 10) {"
10427 " throw 'Hey!';"
10428 " } else {"
10429 " i++;"
10430 " return s(o);"
10431 " }"
10432 " }"
10433 "};"
10434 "s(o);");
10435 CHECK(try_catch.HasCaught());
10436 v8::String::Utf8Value value(try_catch.Exception());
10437 CHECK_EQ(0, strcmp(*value, "Hey!"));
10438}
10439
10440
Steve Blockd0582a62009-12-15 09:54:21 +000010441TEST(Regress528) {
10442 v8::V8::Initialize();
10443
10444 v8::HandleScope scope;
10445 v8::Persistent<Context> context;
10446 v8::Persistent<Context> other_context;
10447 int gc_count;
10448
10449 // Create a context used to keep the code from aging in the compilation
10450 // cache.
10451 other_context = Context::New();
10452
10453 // Context-dependent context data creates reference from the compilation
10454 // cache to the global object.
10455 const char* source_simple = "1";
10456 context = Context::New();
10457 {
10458 v8::HandleScope scope;
10459
10460 context->Enter();
10461 Local<v8::String> obj = v8::String::New("");
10462 context->SetData(obj);
10463 CompileRun(source_simple);
10464 context->Exit();
10465 }
10466 context.Dispose();
10467 for (gc_count = 1; gc_count < 10; gc_count++) {
10468 other_context->Enter();
10469 CompileRun(source_simple);
10470 other_context->Exit();
10471 v8::internal::Heap::CollectAllGarbage(false);
10472 if (GetGlobalObjectsCount() == 1) break;
10473 }
10474 CHECK_GE(2, gc_count);
10475 CHECK_EQ(1, GetGlobalObjectsCount());
10476
10477 // Eval in a function creates reference from the compilation cache to the
10478 // global object.
10479 const char* source_eval = "function f(){eval('1')}; f()";
10480 context = Context::New();
10481 {
10482 v8::HandleScope scope;
10483
10484 context->Enter();
10485 CompileRun(source_eval);
10486 context->Exit();
10487 }
10488 context.Dispose();
10489 for (gc_count = 1; gc_count < 10; gc_count++) {
10490 other_context->Enter();
10491 CompileRun(source_eval);
10492 other_context->Exit();
10493 v8::internal::Heap::CollectAllGarbage(false);
10494 if (GetGlobalObjectsCount() == 1) break;
10495 }
10496 CHECK_GE(2, gc_count);
10497 CHECK_EQ(1, GetGlobalObjectsCount());
10498
10499 // Looking up the line number for an exception creates reference from the
10500 // compilation cache to the global object.
10501 const char* source_exception = "function f(){throw 1;} f()";
10502 context = Context::New();
10503 {
10504 v8::HandleScope scope;
10505
10506 context->Enter();
10507 v8::TryCatch try_catch;
10508 CompileRun(source_exception);
10509 CHECK(try_catch.HasCaught());
10510 v8::Handle<v8::Message> message = try_catch.Message();
10511 CHECK(!message.IsEmpty());
10512 CHECK_EQ(1, message->GetLineNumber());
10513 context->Exit();
10514 }
10515 context.Dispose();
10516 for (gc_count = 1; gc_count < 10; gc_count++) {
10517 other_context->Enter();
10518 CompileRun(source_exception);
10519 other_context->Exit();
10520 v8::internal::Heap::CollectAllGarbage(false);
10521 if (GetGlobalObjectsCount() == 1) break;
10522 }
10523 CHECK_GE(2, gc_count);
10524 CHECK_EQ(1, GetGlobalObjectsCount());
10525
10526 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010527}
Andrei Popescu402d9372010-02-26 13:31:12 +000010528
10529
10530THREADED_TEST(ScriptOrigin) {
10531 v8::HandleScope scope;
10532 LocalContext env;
10533 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10534 v8::Handle<v8::String> script = v8::String::New(
10535 "function f() {}\n\nfunction g() {}");
10536 v8::Script::Compile(script, &origin)->Run();
10537 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10538 env->Global()->Get(v8::String::New("f")));
10539 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10540 env->Global()->Get(v8::String::New("g")));
10541
10542 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10543 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10544 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10545
10546 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10547 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10548 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10549}
10550
10551
10552THREADED_TEST(ScriptLineNumber) {
10553 v8::HandleScope scope;
10554 LocalContext env;
10555 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10556 v8::Handle<v8::String> script = v8::String::New(
10557 "function f() {}\n\nfunction g() {}");
10558 v8::Script::Compile(script, &origin)->Run();
10559 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10560 env->Global()->Get(v8::String::New("f")));
10561 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10562 env->Global()->Get(v8::String::New("g")));
10563 CHECK_EQ(0, f->GetScriptLineNumber());
10564 CHECK_EQ(2, g->GetScriptLineNumber());
10565}
10566
10567
10568static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10569 const AccessorInfo& info) {
10570 return v8_num(42);
10571}
10572
10573
10574static void SetterWhichSetsYOnThisTo23(Local<String> name,
10575 Local<Value> value,
10576 const AccessorInfo& info) {
10577 info.This()->Set(v8_str("y"), v8_num(23));
10578}
10579
10580
Steve Block6ded16b2010-05-10 14:33:55 +010010581TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010582 v8::HandleScope scope;
10583 Local<ObjectTemplate> templ = ObjectTemplate::New();
10584 templ->SetAccessor(v8_str("x"),
10585 GetterWhichReturns42,
10586 SetterWhichSetsYOnThisTo23);
10587 LocalContext context;
10588 context->Global()->Set(v8_str("P"), templ->NewInstance());
10589 CompileRun("function C1() {"
10590 " this.x = 23;"
10591 "};"
10592 "C1.prototype = P;"
10593 "function C2() {"
10594 " this.x = 23"
10595 "};"
10596 "C2.prototype = { };"
10597 "C2.prototype.__proto__ = P;");
10598
10599 v8::Local<v8::Script> script;
10600 script = v8::Script::Compile(v8_str("new C1();"));
10601 for (int i = 0; i < 10; i++) {
10602 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10603 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10604 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10605 }
10606
10607 script = v8::Script::Compile(v8_str("new C2();"));
10608 for (int i = 0; i < 10; i++) {
10609 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10610 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10611 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10612 }
10613}
10614
10615
10616static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10617 Local<String> name, const AccessorInfo& info) {
10618 return v8_num(42);
10619}
10620
10621
10622static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10623 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10624 if (name->Equals(v8_str("x"))) {
10625 info.This()->Set(v8_str("y"), v8_num(23));
10626 }
10627 return v8::Handle<Value>();
10628}
10629
10630
10631THREADED_TEST(InterceptorOnConstructorPrototype) {
10632 v8::HandleScope scope;
10633 Local<ObjectTemplate> templ = ObjectTemplate::New();
10634 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10635 NamedPropertySetterWhichSetsYOnThisTo23);
10636 LocalContext context;
10637 context->Global()->Set(v8_str("P"), templ->NewInstance());
10638 CompileRun("function C1() {"
10639 " this.x = 23;"
10640 "};"
10641 "C1.prototype = P;"
10642 "function C2() {"
10643 " this.x = 23"
10644 "};"
10645 "C2.prototype = { };"
10646 "C2.prototype.__proto__ = P;");
10647
10648 v8::Local<v8::Script> script;
10649 script = v8::Script::Compile(v8_str("new C1();"));
10650 for (int i = 0; i < 10; i++) {
10651 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10652 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10653 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10654 }
10655
10656 script = v8::Script::Compile(v8_str("new C2();"));
10657 for (int i = 0; i < 10; i++) {
10658 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10659 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10660 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10661 }
10662}
Steve Block6ded16b2010-05-10 14:33:55 +010010663
10664
10665TEST(Bug618) {
10666 const char* source = "function C1() {"
10667 " this.x = 23;"
10668 "};"
10669 "C1.prototype = P;";
10670
10671 v8::HandleScope scope;
10672 LocalContext context;
10673 v8::Local<v8::Script> script;
10674
10675 // Use a simple object as prototype.
10676 v8::Local<v8::Object> prototype = v8::Object::New();
10677 prototype->Set(v8_str("y"), v8_num(42));
10678 context->Global()->Set(v8_str("P"), prototype);
10679
10680 // This compile will add the code to the compilation cache.
10681 CompileRun(source);
10682
10683 script = v8::Script::Compile(v8_str("new C1();"));
10684 for (int i = 0; i < 10; i++) {
10685 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10686 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10687 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10688 }
10689
10690 // Use an API object with accessors as prototype.
10691 Local<ObjectTemplate> templ = ObjectTemplate::New();
10692 templ->SetAccessor(v8_str("x"),
10693 GetterWhichReturns42,
10694 SetterWhichSetsYOnThisTo23);
10695 context->Global()->Set(v8_str("P"), templ->NewInstance());
10696
10697 // This compile will get the code from the compilation cache.
10698 CompileRun(source);
10699
10700 script = v8::Script::Compile(v8_str("new C1();"));
10701 for (int i = 0; i < 10; i++) {
10702 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10703 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10704 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10705 }
10706}
10707
10708int prologue_call_count = 0;
10709int epilogue_call_count = 0;
10710int prologue_call_count_second = 0;
10711int epilogue_call_count_second = 0;
10712
10713void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
10714 ++prologue_call_count;
10715}
10716
10717void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
10718 ++epilogue_call_count;
10719}
10720
10721void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10722 ++prologue_call_count_second;
10723}
10724
10725void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10726 ++epilogue_call_count_second;
10727}
10728
10729TEST(GCCallbacks) {
10730 LocalContext context;
10731
10732 v8::V8::AddGCPrologueCallback(PrologueCallback);
10733 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
10734 CHECK_EQ(0, prologue_call_count);
10735 CHECK_EQ(0, epilogue_call_count);
10736 i::Heap::CollectAllGarbage(false);
10737 CHECK_EQ(1, prologue_call_count);
10738 CHECK_EQ(1, epilogue_call_count);
10739 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
10740 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
10741 i::Heap::CollectAllGarbage(false);
10742 CHECK_EQ(2, prologue_call_count);
10743 CHECK_EQ(2, epilogue_call_count);
10744 CHECK_EQ(1, prologue_call_count_second);
10745 CHECK_EQ(1, epilogue_call_count_second);
10746 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
10747 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
10748 i::Heap::CollectAllGarbage(false);
10749 CHECK_EQ(2, prologue_call_count);
10750 CHECK_EQ(2, epilogue_call_count);
10751 CHECK_EQ(2, prologue_call_count_second);
10752 CHECK_EQ(2, epilogue_call_count_second);
10753 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
10754 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
10755 i::Heap::CollectAllGarbage(false);
10756 CHECK_EQ(2, prologue_call_count);
10757 CHECK_EQ(2, epilogue_call_count);
10758 CHECK_EQ(2, prologue_call_count_second);
10759 CHECK_EQ(2, epilogue_call_count_second);
10760}
Kristian Monsen25f61362010-05-21 11:50:48 +010010761
10762
10763THREADED_TEST(AddToJSFunctionResultCache) {
10764 i::FLAG_allow_natives_syntax = true;
10765 v8::HandleScope scope;
10766
10767 LocalContext context;
10768
10769 const char* code =
10770 "(function() {"
10771 " var key0 = 'a';"
10772 " var key1 = 'b';"
10773 " var r0 = %_GetFromCache(0, key0);"
10774 " var r1 = %_GetFromCache(0, key1);"
10775 " var r0_ = %_GetFromCache(0, key0);"
10776 " if (r0 !== r0_)"
10777 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
10778 " var r1_ = %_GetFromCache(0, key1);"
10779 " if (r1 !== r1_)"
10780 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
10781 " return 'PASSED';"
10782 "})()";
10783 v8::internal::Heap::ClearJSFunctionResultCaches();
10784 ExpectString(code, "PASSED");
10785}
10786
10787
10788static const int k0CacheSize = 16;
10789
10790THREADED_TEST(FillJSFunctionResultCache) {
10791 i::FLAG_allow_natives_syntax = true;
10792 v8::HandleScope scope;
10793
10794 LocalContext context;
10795
10796 const char* code =
10797 "(function() {"
10798 " var k = 'a';"
10799 " var r = %_GetFromCache(0, k);"
10800 " for (var i = 0; i < 16; i++) {"
10801 " %_GetFromCache(0, 'a' + i);"
10802 " };"
10803 " if (r === %_GetFromCache(0, k))"
10804 " return 'FAILED: k0CacheSize is too small';"
10805 " return 'PASSED';"
10806 "})()";
10807 v8::internal::Heap::ClearJSFunctionResultCaches();
10808 ExpectString(code, "PASSED");
10809}
10810
10811
10812THREADED_TEST(RoundRobinGetFromCache) {
10813 i::FLAG_allow_natives_syntax = true;
10814 v8::HandleScope scope;
10815
10816 LocalContext context;
10817
10818 const char* code =
10819 "(function() {"
10820 " var keys = [];"
10821 " for (var i = 0; i < 16; i++) keys.push(i);"
10822 " var values = [];"
10823 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
10824 " for (var i = 0; i < 16; i++) {"
10825 " var v = %_GetFromCache(0, keys[i]);"
10826 " if (v !== values[i])"
10827 " return 'Wrong value for ' + "
10828 " keys[i] + ': ' + v + ' vs. ' + values[i];"
10829 " };"
10830 " return 'PASSED';"
10831 "})()";
10832 v8::internal::Heap::ClearJSFunctionResultCaches();
10833 ExpectString(code, "PASSED");
10834}
10835
10836
10837THREADED_TEST(ReverseGetFromCache) {
10838 i::FLAG_allow_natives_syntax = true;
10839 v8::HandleScope scope;
10840
10841 LocalContext context;
10842
10843 const char* code =
10844 "(function() {"
10845 " var keys = [];"
10846 " for (var i = 0; i < 16; i++) keys.push(i);"
10847 " var values = [];"
10848 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
10849 " for (var i = 15; i >= 16; i--) {"
10850 " var v = %_GetFromCache(0, keys[i]);"
10851 " if (v !== values[i])"
10852 " return 'Wrong value for ' + "
10853 " keys[i] + ': ' + v + ' vs. ' + values[i];"
10854 " };"
10855 " return 'PASSED';"
10856 "})()";
10857 v8::internal::Heap::ClearJSFunctionResultCaches();
10858 ExpectString(code, "PASSED");
10859}
10860
10861
10862THREADED_TEST(TestEviction) {
10863 i::FLAG_allow_natives_syntax = true;
10864 v8::HandleScope scope;
10865
10866 LocalContext context;
10867
10868 const char* code =
10869 "(function() {"
10870 " for (var i = 0; i < 2*16; i++) {"
10871 " %_GetFromCache(0, 'a' + i);"
10872 " };"
10873 " return 'PASSED';"
10874 "})()";
10875 v8::internal::Heap::ClearJSFunctionResultCaches();
10876 ExpectString(code, "PASSED");
10877}