blob: cd2641270ff11578eeeae8d9b147785da30b73c8 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2007-2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
Steve Block3ce2e202009-11-05 08:53:23 +000028#include <limits.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000029
30#include "v8.h"
31
32#include "api.h"
33#include "compilation-cache.h"
34#include "execution.h"
35#include "snapshot.h"
36#include "platform.h"
37#include "top.h"
Steve Block3ce2e202009-11-05 08:53:23 +000038#include "utils.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000039#include "cctest.h"
Iain Merrick9ac36c92010-09-13 15:29:50 +010040#include "parser.h"
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080041#include "unicode-inl.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000042
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
Steve Block8defd9f2010-07-08 12:39:36 +010063namespace i = ::i;
Steve Blocka7e24c12009-10-30 11:49:00 +000064
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
Iain Merrick75681382010-08-19 15:07:18 +010086static void ExpectFalse(const char* code) {
87 ExpectBoolean(code, false);
88}
89
90
Leon Clarked91b9f72010-01-27 17:25:45 +000091static void ExpectObject(const char* code, Local<Value> expected) {
92 Local<Value> result = CompileRun(code);
93 CHECK(result->Equals(expected));
94}
95
96
Iain Merrick75681382010-08-19 15:07:18 +010097static void ExpectUndefined(const char* code) {
98 Local<Value> result = CompileRun(code);
99 CHECK(result->IsUndefined());
100}
101
102
Steve Blocka7e24c12009-10-30 11:49:00 +0000103static int signature_callback_count;
104static v8::Handle<Value> IncrementingSignatureCallback(
105 const v8::Arguments& args) {
106 ApiTestFuzzer::Fuzz();
107 signature_callback_count++;
108 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
109 for (int i = 0; i < args.Length(); i++)
110 result->Set(v8::Integer::New(i), args[i]);
111 return result;
112}
113
114
115static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
116 ApiTestFuzzer::Fuzz();
117 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
118 for (int i = 0; i < args.Length(); i++) {
119 result->Set(v8::Integer::New(i), args[i]);
120 }
121 return result;
122}
123
124
125THREADED_TEST(Handles) {
126 v8::HandleScope scope;
127 Local<Context> local_env;
128 {
129 LocalContext env;
130 local_env = env.local();
131 }
132
133 // Local context should still be live.
134 CHECK(!local_env.IsEmpty());
135 local_env->Enter();
136
137 v8::Handle<v8::Primitive> undef = v8::Undefined();
138 CHECK(!undef.IsEmpty());
139 CHECK(undef->IsUndefined());
140
141 const char* c_source = "1 + 2 + 3";
142 Local<String> source = String::New(c_source);
143 Local<Script> script = Script::Compile(source);
144 CHECK_EQ(6, script->Run()->Int32Value());
145
146 local_env->Exit();
147}
148
149
Steve Blocka7e24c12009-10-30 11:49:00 +0000150THREADED_TEST(ReceiverSignature) {
151 v8::HandleScope scope;
152 LocalContext env;
153 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
154 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
155 fun->PrototypeTemplate()->Set(
156 v8_str("m"),
157 v8::FunctionTemplate::New(IncrementingSignatureCallback,
158 v8::Handle<Value>(),
159 sig));
160 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
161 signature_callback_count = 0;
162 CompileRun(
163 "var o = new Fun();"
164 "o.m();");
165 CHECK_EQ(1, signature_callback_count);
166 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
167 sub_fun->Inherit(fun);
168 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
169 CompileRun(
170 "var o = new SubFun();"
171 "o.m();");
172 CHECK_EQ(2, signature_callback_count);
173
174 v8::TryCatch try_catch;
175 CompileRun(
176 "var o = { };"
177 "o.m = Fun.prototype.m;"
178 "o.m();");
179 CHECK_EQ(2, signature_callback_count);
180 CHECK(try_catch.HasCaught());
181 try_catch.Reset();
182 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
183 sub_fun->Inherit(fun);
184 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
185 CompileRun(
186 "var o = new UnrelFun();"
187 "o.m = Fun.prototype.m;"
188 "o.m();");
189 CHECK_EQ(2, signature_callback_count);
190 CHECK(try_catch.HasCaught());
191}
192
193
194
195
196THREADED_TEST(ArgumentSignature) {
197 v8::HandleScope scope;
198 LocalContext env;
199 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
200 cons->SetClassName(v8_str("Cons"));
201 v8::Handle<v8::Signature> sig =
202 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
203 v8::Handle<v8::FunctionTemplate> fun =
204 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
205 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
206 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
207
208 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
209 CHECK(value1->IsTrue());
210
211 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
212 CHECK(value2->IsTrue());
213
214 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
215 CHECK(value3->IsTrue());
216
217 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
218 cons1->SetClassName(v8_str("Cons1"));
219 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
220 cons2->SetClassName(v8_str("Cons2"));
221 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
222 cons3->SetClassName(v8_str("Cons3"));
223
224 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
225 v8::Handle<v8::Signature> wsig =
226 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
227 v8::Handle<v8::FunctionTemplate> fun2 =
228 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
229
230 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
231 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
232 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
233 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
234 v8::Handle<Value> value4 = CompileRun(
235 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
236 "'[object Cons1],[object Cons2],[object Cons3]'");
237 CHECK(value4->IsTrue());
238
239 v8::Handle<Value> value5 = CompileRun(
240 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
241 CHECK(value5->IsTrue());
242
243 v8::Handle<Value> value6 = CompileRun(
244 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
245 CHECK(value6->IsTrue());
246
247 v8::Handle<Value> value7 = CompileRun(
248 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
249 "'[object Cons1],[object Cons2],[object Cons3],d';");
250 CHECK(value7->IsTrue());
251
252 v8::Handle<Value> value8 = CompileRun(
253 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
254 CHECK(value8->IsTrue());
255}
256
257
258THREADED_TEST(HulIgennem) {
259 v8::HandleScope scope;
260 LocalContext env;
261 v8::Handle<v8::Primitive> undef = v8::Undefined();
262 Local<String> undef_str = undef->ToString();
263 char* value = i::NewArray<char>(undef_str->Length() + 1);
264 undef_str->WriteAscii(value);
265 CHECK_EQ(0, strcmp(value, "undefined"));
266 i::DeleteArray(value);
267}
268
269
270THREADED_TEST(Access) {
271 v8::HandleScope scope;
272 LocalContext env;
273 Local<v8::Object> obj = v8::Object::New();
274 Local<Value> foo_before = obj->Get(v8_str("foo"));
275 CHECK(foo_before->IsUndefined());
276 Local<String> bar_str = v8_str("bar");
277 obj->Set(v8_str("foo"), bar_str);
278 Local<Value> foo_after = obj->Get(v8_str("foo"));
279 CHECK(!foo_after->IsUndefined());
280 CHECK(foo_after->IsString());
281 CHECK_EQ(bar_str, foo_after);
282}
283
284
Steve Block6ded16b2010-05-10 14:33:55 +0100285THREADED_TEST(AccessElement) {
286 v8::HandleScope scope;
287 LocalContext env;
288 Local<v8::Object> obj = v8::Object::New();
289 Local<Value> before = obj->Get(1);
290 CHECK(before->IsUndefined());
291 Local<String> bar_str = v8_str("bar");
292 obj->Set(1, bar_str);
293 Local<Value> after = obj->Get(1);
294 CHECK(!after->IsUndefined());
295 CHECK(after->IsString());
296 CHECK_EQ(bar_str, after);
297
298 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
299 CHECK_EQ(v8_str("a"), value->Get(0));
300 CHECK_EQ(v8_str("b"), value->Get(1));
301}
302
303
Steve Blocka7e24c12009-10-30 11:49:00 +0000304THREADED_TEST(Script) {
305 v8::HandleScope scope;
306 LocalContext env;
307 const char* c_source = "1 + 2 + 3";
308 Local<String> source = String::New(c_source);
309 Local<Script> script = Script::Compile(source);
310 CHECK_EQ(6, script->Run()->Int32Value());
311}
312
313
314static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000315 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000316 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000317 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000318 return converted;
319}
320
321
322class TestResource: public String::ExternalStringResource {
323 public:
324 static int dispose_count;
325
326 explicit TestResource(uint16_t* data)
327 : data_(data), length_(0) {
328 while (data[length_]) ++length_;
329 }
330
331 ~TestResource() {
332 i::DeleteArray(data_);
333 ++dispose_count;
334 }
335
336 const uint16_t* data() const {
337 return data_;
338 }
339
340 size_t length() const {
341 return length_;
342 }
343 private:
344 uint16_t* data_;
345 size_t length_;
346};
347
348
349int TestResource::dispose_count = 0;
350
351
352class TestAsciiResource: public String::ExternalAsciiStringResource {
353 public:
354 static int dispose_count;
355
356 explicit TestAsciiResource(const char* data)
357 : data_(data),
358 length_(strlen(data)) { }
359
360 ~TestAsciiResource() {
361 i::DeleteArray(data_);
362 ++dispose_count;
363 }
364
365 const char* data() const {
366 return data_;
367 }
368
369 size_t length() const {
370 return length_;
371 }
372 private:
373 const char* data_;
374 size_t length_;
375};
376
377
378int TestAsciiResource::dispose_count = 0;
379
380
381THREADED_TEST(ScriptUsingStringResource) {
382 TestResource::dispose_count = 0;
383 const char* c_source = "1 + 2 * 3";
384 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
385 {
386 v8::HandleScope scope;
387 LocalContext env;
388 TestResource* resource = new TestResource(two_byte_source);
389 Local<String> source = String::NewExternal(resource);
390 Local<Script> script = Script::Compile(source);
391 Local<Value> value = script->Run();
392 CHECK(value->IsNumber());
393 CHECK_EQ(7, value->Int32Value());
394 CHECK(source->IsExternal());
395 CHECK_EQ(resource,
396 static_cast<TestResource*>(source->GetExternalStringResource()));
Steve Block8defd9f2010-07-08 12:39:36 +0100397 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000398 CHECK_EQ(0, TestResource::dispose_count);
399 }
Steve Block8defd9f2010-07-08 12:39:36 +0100400 i::CompilationCache::Clear();
401 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000402 CHECK_EQ(1, TestResource::dispose_count);
403}
404
405
406THREADED_TEST(ScriptUsingAsciiStringResource) {
407 TestAsciiResource::dispose_count = 0;
408 const char* c_source = "1 + 2 * 3";
409 {
410 v8::HandleScope scope;
411 LocalContext env;
412 Local<String> source =
413 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
414 Local<Script> script = Script::Compile(source);
415 Local<Value> value = script->Run();
416 CHECK(value->IsNumber());
417 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100418 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000419 CHECK_EQ(0, TestAsciiResource::dispose_count);
420 }
Steve Block8defd9f2010-07-08 12:39:36 +0100421 i::CompilationCache::Clear();
422 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000423 CHECK_EQ(1, TestAsciiResource::dispose_count);
424}
425
426
427THREADED_TEST(ScriptMakingExternalString) {
428 TestResource::dispose_count = 0;
429 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
430 {
431 v8::HandleScope scope;
432 LocalContext env;
433 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000434 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100435 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
436 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000437 bool success = source->MakeExternal(new TestResource(two_byte_source));
438 CHECK(success);
439 Local<Script> script = Script::Compile(source);
440 Local<Value> value = script->Run();
441 CHECK(value->IsNumber());
442 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100443 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000444 CHECK_EQ(0, TestResource::dispose_count);
445 }
Steve Block8defd9f2010-07-08 12:39:36 +0100446 i::CompilationCache::Clear();
447 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000448 CHECK_EQ(1, TestResource::dispose_count);
449}
450
451
452THREADED_TEST(ScriptMakingExternalAsciiString) {
453 TestAsciiResource::dispose_count = 0;
454 const char* c_source = "1 + 2 * 3";
455 {
456 v8::HandleScope scope;
457 LocalContext env;
458 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000459 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100460 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
461 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000462 bool success = source->MakeExternal(
463 new TestAsciiResource(i::StrDup(c_source)));
464 CHECK(success);
465 Local<Script> script = Script::Compile(source);
466 Local<Value> value = script->Run();
467 CHECK(value->IsNumber());
468 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100469 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000470 CHECK_EQ(0, TestAsciiResource::dispose_count);
471 }
Steve Block8defd9f2010-07-08 12:39:36 +0100472 i::CompilationCache::Clear();
473 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000474 CHECK_EQ(1, TestAsciiResource::dispose_count);
475}
476
477
Andrei Popescu402d9372010-02-26 13:31:12 +0000478TEST(MakingExternalStringConditions) {
479 v8::HandleScope scope;
480 LocalContext env;
481
482 // Free some space in the new space so that we can check freshness.
Ben Murdochf87a2032010-10-22 12:50:53 +0100483 i::Heap::CollectGarbage(i::NEW_SPACE);
484 i::Heap::CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000485
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100486 uint16_t* two_byte_string = AsciiToTwoByteString("small");
487 Local<String> small_string = String::New(two_byte_string);
488 i::DeleteArray(two_byte_string);
489
Andrei Popescu402d9372010-02-26 13:31:12 +0000490 // We should refuse to externalize newly created small string.
491 CHECK(!small_string->CanMakeExternal());
492 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100493 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
494 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000495 // Old space strings should be accepted.
496 CHECK(small_string->CanMakeExternal());
497
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100498 two_byte_string = AsciiToTwoByteString("small 2");
499 small_string = String::New(two_byte_string);
500 i::DeleteArray(two_byte_string);
501
Andrei Popescu402d9372010-02-26 13:31:12 +0000502 // We should refuse externalizing newly created small string.
503 CHECK(!small_string->CanMakeExternal());
504 for (int i = 0; i < 100; i++) {
505 String::Value value(small_string);
506 }
507 // Frequently used strings should be accepted.
508 CHECK(small_string->CanMakeExternal());
509
510 const int buf_size = 10 * 1024;
511 char* buf = i::NewArray<char>(buf_size);
512 memset(buf, 'a', buf_size);
513 buf[buf_size - 1] = '\0';
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100514
515 two_byte_string = AsciiToTwoByteString(buf);
516 Local<String> large_string = String::New(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000517 i::DeleteArray(buf);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100518 i::DeleteArray(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000519 // Large strings should be immediately accepted.
520 CHECK(large_string->CanMakeExternal());
521}
522
523
524TEST(MakingExternalAsciiStringConditions) {
525 v8::HandleScope scope;
526 LocalContext env;
527
528 // Free some space in the new space so that we can check freshness.
Ben Murdochf87a2032010-10-22 12:50:53 +0100529 i::Heap::CollectGarbage(i::NEW_SPACE);
530 i::Heap::CollectGarbage(i::NEW_SPACE);
Andrei Popescu402d9372010-02-26 13:31:12 +0000531
532 Local<String> small_string = String::New("small");
533 // We should refuse to externalize newly created small string.
534 CHECK(!small_string->CanMakeExternal());
535 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100536 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
537 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Andrei Popescu402d9372010-02-26 13:31:12 +0000538 // Old space strings should be accepted.
539 CHECK(small_string->CanMakeExternal());
540
541 small_string = String::New("small 2");
542 // We should refuse externalizing newly created small string.
543 CHECK(!small_string->CanMakeExternal());
544 for (int i = 0; i < 100; i++) {
545 String::Value value(small_string);
546 }
547 // Frequently used strings should be accepted.
548 CHECK(small_string->CanMakeExternal());
549
550 const int buf_size = 10 * 1024;
551 char* buf = i::NewArray<char>(buf_size);
552 memset(buf, 'a', buf_size);
553 buf[buf_size - 1] = '\0';
554 Local<String> large_string = String::New(buf);
555 i::DeleteArray(buf);
556 // Large strings should be immediately accepted.
557 CHECK(large_string->CanMakeExternal());
558}
559
560
Steve Blocka7e24c12009-10-30 11:49:00 +0000561THREADED_TEST(UsingExternalString) {
562 {
563 v8::HandleScope scope;
564 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
565 Local<String> string =
566 String::NewExternal(new TestResource(two_byte_string));
567 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
568 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100569 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
570 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000571 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
572 CHECK(isymbol->IsSymbol());
573 }
574 i::Heap::CollectAllGarbage(false);
575 i::Heap::CollectAllGarbage(false);
576}
577
578
579THREADED_TEST(UsingExternalAsciiString) {
580 {
581 v8::HandleScope scope;
582 const char* one_byte_string = "test string";
583 Local<String> string = String::NewExternal(
584 new TestAsciiResource(i::StrDup(one_byte_string)));
585 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
586 // Trigger GCs so that the newly allocated string moves to old gen.
Ben Murdochf87a2032010-10-22 12:50:53 +0100587 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now
588 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000589 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
590 CHECK(isymbol->IsSymbol());
591 }
592 i::Heap::CollectAllGarbage(false);
593 i::Heap::CollectAllGarbage(false);
594}
595
596
Leon Clarkee46be812010-01-19 14:06:41 +0000597THREADED_TEST(ScavengeExternalString) {
598 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100599 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000600 {
601 v8::HandleScope scope;
602 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
603 Local<String> string =
604 String::NewExternal(new TestResource(two_byte_string));
605 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Ben Murdochf87a2032010-10-22 12:50:53 +0100606 i::Heap::CollectGarbage(i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100607 in_new_space = i::Heap::InNewSpace(*istring);
608 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000609 CHECK_EQ(0, TestResource::dispose_count);
610 }
Ben Murdochf87a2032010-10-22 12:50:53 +0100611 i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000612 CHECK_EQ(1, TestResource::dispose_count);
613}
614
615
616THREADED_TEST(ScavengeExternalAsciiString) {
617 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100618 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000619 {
620 v8::HandleScope scope;
621 const char* one_byte_string = "test string";
622 Local<String> string = String::NewExternal(
623 new TestAsciiResource(i::StrDup(one_byte_string)));
624 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
Ben Murdochf87a2032010-10-22 12:50:53 +0100625 i::Heap::CollectGarbage(i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100626 in_new_space = i::Heap::InNewSpace(*istring);
627 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000628 CHECK_EQ(0, TestAsciiResource::dispose_count);
629 }
Ben Murdochf87a2032010-10-22 12:50:53 +0100630 i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000631 CHECK_EQ(1, TestAsciiResource::dispose_count);
632}
633
634
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100635class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
636 public:
637 static int dispose_calls;
638
639 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
640 : TestAsciiResource(data),
641 dispose_(dispose) { }
642
643 void Dispose() {
644 ++dispose_calls;
645 if (dispose_) delete this;
646 }
647 private:
648 bool dispose_;
649};
650
651
652int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
653
654
655TEST(ExternalStringWithDisposeHandling) {
656 const char* c_source = "1 + 2 * 3";
657
658 // Use a stack allocated external string resource allocated object.
659 TestAsciiResource::dispose_count = 0;
660 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
661 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
662 {
663 v8::HandleScope scope;
664 LocalContext env;
665 Local<String> source = String::NewExternal(&res_stack);
666 Local<Script> script = Script::Compile(source);
667 Local<Value> value = script->Run();
668 CHECK(value->IsNumber());
669 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100670 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100671 CHECK_EQ(0, TestAsciiResource::dispose_count);
672 }
Steve Block8defd9f2010-07-08 12:39:36 +0100673 i::CompilationCache::Clear();
674 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100675 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
676 CHECK_EQ(0, TestAsciiResource::dispose_count);
677
678 // Use a heap allocated external string resource allocated object.
679 TestAsciiResource::dispose_count = 0;
680 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
681 TestAsciiResource* res_heap =
682 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
683 {
684 v8::HandleScope scope;
685 LocalContext env;
686 Local<String> source = String::NewExternal(res_heap);
687 Local<Script> script = Script::Compile(source);
688 Local<Value> value = script->Run();
689 CHECK(value->IsNumber());
690 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100691 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100692 CHECK_EQ(0, TestAsciiResource::dispose_count);
693 }
Steve Block8defd9f2010-07-08 12:39:36 +0100694 i::CompilationCache::Clear();
695 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100696 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
697 CHECK_EQ(1, TestAsciiResource::dispose_count);
698}
699
700
Steve Block3ce2e202009-11-05 08:53:23 +0000701THREADED_TEST(StringConcat) {
702 {
703 v8::HandleScope scope;
704 LocalContext env;
705 const char* one_byte_string_1 = "function a_times_t";
706 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
707 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
708 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
709 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
710 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
711 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
712 Local<String> left = v8_str(one_byte_string_1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100713
714 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
715 Local<String> right = String::New(two_byte_source);
716 i::DeleteArray(two_byte_source);
717
Steve Block3ce2e202009-11-05 08:53:23 +0000718 Local<String> source = String::Concat(left, right);
719 right = String::NewExternal(
720 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
721 source = String::Concat(source, right);
722 right = String::NewExternal(
723 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
724 source = String::Concat(source, right);
725 right = v8_str(one_byte_string_2);
726 source = String::Concat(source, right);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100727
728 two_byte_source = AsciiToTwoByteString(two_byte_string_2);
729 right = String::New(two_byte_source);
730 i::DeleteArray(two_byte_source);
731
Steve Block3ce2e202009-11-05 08:53:23 +0000732 source = String::Concat(source, right);
733 right = String::NewExternal(
734 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
735 source = String::Concat(source, right);
736 Local<Script> script = Script::Compile(source);
737 Local<Value> value = script->Run();
738 CHECK(value->IsNumber());
739 CHECK_EQ(68, value->Int32Value());
740 }
Steve Block8defd9f2010-07-08 12:39:36 +0100741 i::CompilationCache::Clear();
Steve Block3ce2e202009-11-05 08:53:23 +0000742 i::Heap::CollectAllGarbage(false);
743 i::Heap::CollectAllGarbage(false);
744}
745
746
Steve Blocka7e24c12009-10-30 11:49:00 +0000747THREADED_TEST(GlobalProperties) {
748 v8::HandleScope scope;
749 LocalContext env;
750 v8::Handle<v8::Object> global = env->Global();
751 global->Set(v8_str("pi"), v8_num(3.1415926));
752 Local<Value> pi = global->Get(v8_str("pi"));
753 CHECK_EQ(3.1415926, pi->NumberValue());
754}
755
756
757static v8::Handle<Value> handle_call(const v8::Arguments& args) {
758 ApiTestFuzzer::Fuzz();
759 return v8_num(102);
760}
761
762
763static v8::Handle<Value> construct_call(const v8::Arguments& args) {
764 ApiTestFuzzer::Fuzz();
765 args.This()->Set(v8_str("x"), v8_num(1));
766 args.This()->Set(v8_str("y"), v8_num(2));
767 return args.This();
768}
769
Ben Murdochf87a2032010-10-22 12:50:53 +0100770static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
771 ApiTestFuzzer::Fuzz();
772 return v8_num(239);
773}
774
775
Steve Blocka7e24c12009-10-30 11:49:00 +0000776THREADED_TEST(FunctionTemplate) {
777 v8::HandleScope scope;
778 LocalContext env;
779 {
780 Local<v8::FunctionTemplate> fun_templ =
781 v8::FunctionTemplate::New(handle_call);
782 Local<Function> fun = fun_templ->GetFunction();
783 env->Global()->Set(v8_str("obj"), fun);
784 Local<Script> script = v8_compile("obj()");
785 CHECK_EQ(102, script->Run()->Int32Value());
786 }
787 // Use SetCallHandler to initialize a function template, should work like the
788 // previous one.
789 {
790 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
791 fun_templ->SetCallHandler(handle_call);
792 Local<Function> fun = fun_templ->GetFunction();
793 env->Global()->Set(v8_str("obj"), fun);
794 Local<Script> script = v8_compile("obj()");
795 CHECK_EQ(102, script->Run()->Int32Value());
796 }
797 // Test constructor calls.
798 {
799 Local<v8::FunctionTemplate> fun_templ =
800 v8::FunctionTemplate::New(construct_call);
801 fun_templ->SetClassName(v8_str("funky"));
Ben Murdochf87a2032010-10-22 12:50:53 +0100802 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239);
Steve Blocka7e24c12009-10-30 11:49:00 +0000803 Local<Function> fun = fun_templ->GetFunction();
804 env->Global()->Set(v8_str("obj"), fun);
805 Local<Script> script = v8_compile("var s = new obj(); s.x");
806 CHECK_EQ(1, script->Run()->Int32Value());
807
808 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
809 CHECK_EQ(v8_str("[object funky]"), result);
Ben Murdochf87a2032010-10-22 12:50:53 +0100810
811 result = v8_compile("(new obj()).m")->Run();
812 CHECK_EQ(239, result->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000813 }
814}
815
816
Ben Murdochb8e0da22011-05-16 14:20:40 +0100817static void* expected_ptr;
818static v8::Handle<v8::Value> callback(const v8::Arguments& args) {
819 void* ptr = v8::External::Unwrap(args.Data());
820 CHECK_EQ(expected_ptr, ptr);
821 return v8::Boolean::New(true);
822}
823
824
825static void TestExternalPointerWrapping() {
826 v8::HandleScope scope;
827 LocalContext env;
828
829 v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr);
830
831 v8::Handle<v8::Object> obj = v8::Object::New();
832 obj->Set(v8_str("func"),
833 v8::FunctionTemplate::New(callback, data)->GetFunction());
834 env->Global()->Set(v8_str("obj"), obj);
835
836 CHECK(CompileRun(
837 "function foo() {\n"
838 " for (var i = 0; i < 13; i++) obj.func();\n"
839 "}\n"
840 "foo(), true")->BooleanValue());
841}
842
843
844THREADED_TEST(ExternalWrap) {
845 // Check heap allocated object.
846 int* ptr = new int;
847 expected_ptr = ptr;
848 TestExternalPointerWrapping();
849 delete ptr;
850
851 // Check stack allocated object.
852 int foo;
853 expected_ptr = &foo;
854 TestExternalPointerWrapping();
855
856 // Check not aligned addresses.
857 const int n = 100;
858 char* s = new char[n];
859 for (int i = 0; i < n; i++) {
860 expected_ptr = s + i;
861 TestExternalPointerWrapping();
862 }
863
864 delete[] s;
865
866 // Check several invalid addresses.
867 expected_ptr = reinterpret_cast<void*>(1);
868 TestExternalPointerWrapping();
869
870 expected_ptr = reinterpret_cast<void*>(0xdeadbeef);
871 TestExternalPointerWrapping();
872
873 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1);
874 TestExternalPointerWrapping();
875
876#if defined(V8_HOST_ARCH_X64)
Steve Block1e0659c2011-05-24 12:43:12 +0100877 // Check a value with a leading 1 bit in x64 Smi encoding.
878 expected_ptr = reinterpret_cast<void*>(0x400000000);
879 TestExternalPointerWrapping();
880
Ben Murdochb8e0da22011-05-16 14:20:40 +0100881 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef);
882 TestExternalPointerWrapping();
883
884 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1);
885 TestExternalPointerWrapping();
886#endif
887}
888
889
Steve Blocka7e24c12009-10-30 11:49:00 +0000890THREADED_TEST(FindInstanceInPrototypeChain) {
891 v8::HandleScope scope;
892 LocalContext env;
893
894 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
895 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
896 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
897 derived->Inherit(base);
898
899 Local<v8::Function> base_function = base->GetFunction();
900 Local<v8::Function> derived_function = derived->GetFunction();
901 Local<v8::Function> other_function = other->GetFunction();
902
903 Local<v8::Object> base_instance = base_function->NewInstance();
904 Local<v8::Object> derived_instance = derived_function->NewInstance();
905 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
906 Local<v8::Object> other_instance = other_function->NewInstance();
907 derived_instance2->Set(v8_str("__proto__"), derived_instance);
908 other_instance->Set(v8_str("__proto__"), derived_instance2);
909
910 // base_instance is only an instance of base.
911 CHECK_EQ(base_instance,
912 base_instance->FindInstanceInPrototypeChain(base));
913 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
914 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
915
916 // derived_instance is an instance of base and derived.
917 CHECK_EQ(derived_instance,
918 derived_instance->FindInstanceInPrototypeChain(base));
919 CHECK_EQ(derived_instance,
920 derived_instance->FindInstanceInPrototypeChain(derived));
921 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
922
923 // other_instance is an instance of other and its immediate
924 // prototype derived_instance2 is an instance of base and derived.
925 // Note, derived_instance is an instance of base and derived too,
926 // but it comes after derived_instance2 in the prototype chain of
927 // other_instance.
928 CHECK_EQ(derived_instance2,
929 other_instance->FindInstanceInPrototypeChain(base));
930 CHECK_EQ(derived_instance2,
931 other_instance->FindInstanceInPrototypeChain(derived));
932 CHECK_EQ(other_instance,
933 other_instance->FindInstanceInPrototypeChain(other));
934}
935
936
Steve Block3ce2e202009-11-05 08:53:23 +0000937THREADED_TEST(TinyInteger) {
938 v8::HandleScope scope;
939 LocalContext env;
940 int32_t value = 239;
941 Local<v8::Integer> value_obj = v8::Integer::New(value);
942 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
943}
944
945
946THREADED_TEST(BigSmiInteger) {
947 v8::HandleScope scope;
948 LocalContext env;
949 int32_t value = i::Smi::kMaxValue;
950 // We cannot add one to a Smi::kMaxValue without wrapping.
951 if (i::kSmiValueSize < 32) {
952 CHECK(i::Smi::IsValid(value));
953 CHECK(!i::Smi::IsValid(value + 1));
954 Local<v8::Integer> value_obj = v8::Integer::New(value);
955 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
956 }
957}
958
959
960THREADED_TEST(BigInteger) {
961 v8::HandleScope scope;
962 LocalContext env;
963 // We cannot add one to a Smi::kMaxValue without wrapping.
964 if (i::kSmiValueSize < 32) {
965 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
966 // The code will not be run in that case, due to the "if" guard.
967 int32_t value =
968 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
969 CHECK(value > i::Smi::kMaxValue);
970 CHECK(!i::Smi::IsValid(value));
971 Local<v8::Integer> value_obj = v8::Integer::New(value);
972 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
973 }
974}
975
976
977THREADED_TEST(TinyUnsignedInteger) {
978 v8::HandleScope scope;
979 LocalContext env;
980 uint32_t value = 239;
981 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
982 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
983}
984
985
986THREADED_TEST(BigUnsignedSmiInteger) {
987 v8::HandleScope scope;
988 LocalContext env;
989 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
990 CHECK(i::Smi::IsValid(value));
991 CHECK(!i::Smi::IsValid(value + 1));
992 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
993 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
994}
995
996
997THREADED_TEST(BigUnsignedInteger) {
998 v8::HandleScope scope;
999 LocalContext env;
1000 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
1001 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
1002 CHECK(!i::Smi::IsValid(value));
1003 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1004 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1005}
1006
1007
1008THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
1009 v8::HandleScope scope;
1010 LocalContext env;
1011 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
1012 uint32_t value = INT32_MAX_AS_UINT + 1;
1013 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
1014 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
1015 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
1016}
1017
1018
Steve Blocka7e24c12009-10-30 11:49:00 +00001019THREADED_TEST(Number) {
1020 v8::HandleScope scope;
1021 LocalContext env;
1022 double PI = 3.1415926;
1023 Local<v8::Number> pi_obj = v8::Number::New(PI);
1024 CHECK_EQ(PI, pi_obj->NumberValue());
1025}
1026
1027
1028THREADED_TEST(ToNumber) {
1029 v8::HandleScope scope;
1030 LocalContext env;
1031 Local<String> str = v8_str("3.1415926");
1032 CHECK_EQ(3.1415926, str->NumberValue());
1033 v8::Handle<v8::Boolean> t = v8::True();
1034 CHECK_EQ(1.0, t->NumberValue());
1035 v8::Handle<v8::Boolean> f = v8::False();
1036 CHECK_EQ(0.0, f->NumberValue());
1037}
1038
1039
1040THREADED_TEST(Date) {
1041 v8::HandleScope scope;
1042 LocalContext env;
1043 double PI = 3.1415926;
1044 Local<Value> date_obj = v8::Date::New(PI);
1045 CHECK_EQ(3.0, date_obj->NumberValue());
1046}
1047
1048
1049THREADED_TEST(Boolean) {
1050 v8::HandleScope scope;
1051 LocalContext env;
1052 v8::Handle<v8::Boolean> t = v8::True();
1053 CHECK(t->Value());
1054 v8::Handle<v8::Boolean> f = v8::False();
1055 CHECK(!f->Value());
1056 v8::Handle<v8::Primitive> u = v8::Undefined();
1057 CHECK(!u->BooleanValue());
1058 v8::Handle<v8::Primitive> n = v8::Null();
1059 CHECK(!n->BooleanValue());
1060 v8::Handle<String> str1 = v8_str("");
1061 CHECK(!str1->BooleanValue());
1062 v8::Handle<String> str2 = v8_str("x");
1063 CHECK(str2->BooleanValue());
1064 CHECK(!v8::Number::New(0)->BooleanValue());
1065 CHECK(v8::Number::New(-1)->BooleanValue());
1066 CHECK(v8::Number::New(1)->BooleanValue());
1067 CHECK(v8::Number::New(42)->BooleanValue());
1068 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
1069}
1070
1071
1072static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
1073 ApiTestFuzzer::Fuzz();
1074 return v8_num(13.4);
1075}
1076
1077
1078static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
1079 ApiTestFuzzer::Fuzz();
1080 return v8_num(876);
1081}
1082
1083
1084THREADED_TEST(GlobalPrototype) {
1085 v8::HandleScope scope;
1086 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
1087 func_templ->PrototypeTemplate()->Set(
1088 "dummy",
1089 v8::FunctionTemplate::New(DummyCallHandler));
1090 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
1091 templ->Set("x", v8_num(200));
1092 templ->SetAccessor(v8_str("m"), GetM);
1093 LocalContext env(0, templ);
1094 v8::Handle<v8::Object> obj = env->Global();
1095 v8::Handle<Script> script = v8_compile("dummy()");
1096 v8::Handle<Value> result = script->Run();
1097 CHECK_EQ(13.4, result->NumberValue());
1098 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1099 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1100}
1101
1102
Steve Blocka7e24c12009-10-30 11:49:00 +00001103THREADED_TEST(ObjectTemplate) {
1104 v8::HandleScope scope;
1105 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1106 templ1->Set("x", v8_num(10));
1107 templ1->Set("y", v8_num(13));
1108 LocalContext env;
1109 Local<v8::Object> instance1 = templ1->NewInstance();
1110 env->Global()->Set(v8_str("p"), instance1);
1111 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1112 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1113 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1114 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1115 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1116 templ2->Set("a", v8_num(12));
1117 templ2->Set("b", templ1);
1118 Local<v8::Object> instance2 = templ2->NewInstance();
1119 env->Global()->Set(v8_str("q"), instance2);
1120 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1121 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1122 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1123 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1124}
1125
1126
1127static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1128 ApiTestFuzzer::Fuzz();
1129 return v8_num(17.2);
1130}
1131
1132
1133static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1134 ApiTestFuzzer::Fuzz();
1135 return v8_num(15.2);
1136}
1137
1138
1139THREADED_TEST(DescriptorInheritance) {
1140 v8::HandleScope scope;
1141 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1142 super->PrototypeTemplate()->Set("flabby",
1143 v8::FunctionTemplate::New(GetFlabby));
1144 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1145
1146 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1147
1148 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1149 base1->Inherit(super);
1150 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1151
1152 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1153 base2->Inherit(super);
1154 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1155
1156 LocalContext env;
1157
1158 env->Global()->Set(v8_str("s"), super->GetFunction());
1159 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1160 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1161
1162 // Checks right __proto__ chain.
1163 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1164 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1165
1166 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1167
1168 // Instance accessor should not be visible on function object or its prototype
1169 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1170 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1171 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1172
1173 env->Global()->Set(v8_str("obj"),
1174 base1->GetFunction()->NewInstance());
1175 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1176 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1177 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1178 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1179 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1180
1181 env->Global()->Set(v8_str("obj2"),
1182 base2->GetFunction()->NewInstance());
1183 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1184 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1185 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1186 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1187 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1188
1189 // base1 and base2 cannot cross reference to each's prototype
1190 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1191 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1192}
1193
1194
1195int echo_named_call_count;
1196
1197
1198static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1199 const AccessorInfo& info) {
1200 ApiTestFuzzer::Fuzz();
1201 CHECK_EQ(v8_str("data"), info.Data());
1202 echo_named_call_count++;
1203 return name;
1204}
1205
1206
1207THREADED_TEST(NamedPropertyHandlerGetter) {
1208 echo_named_call_count = 0;
1209 v8::HandleScope scope;
1210 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1211 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1212 0, 0, 0, 0,
1213 v8_str("data"));
1214 LocalContext env;
1215 env->Global()->Set(v8_str("obj"),
1216 templ->GetFunction()->NewInstance());
1217 CHECK_EQ(echo_named_call_count, 0);
1218 v8_compile("obj.x")->Run();
1219 CHECK_EQ(echo_named_call_count, 1);
1220 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1221 v8::Handle<Value> str = CompileRun(code);
1222 String::AsciiValue value(str);
1223 CHECK_EQ(*value, "oddlepoddle");
1224 // Check default behavior
1225 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1226 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1227 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1228}
1229
1230
1231int echo_indexed_call_count = 0;
1232
1233
1234static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1235 const AccessorInfo& info) {
1236 ApiTestFuzzer::Fuzz();
1237 CHECK_EQ(v8_num(637), info.Data());
1238 echo_indexed_call_count++;
1239 return v8_num(index);
1240}
1241
1242
1243THREADED_TEST(IndexedPropertyHandlerGetter) {
1244 v8::HandleScope scope;
1245 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1246 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1247 0, 0, 0, 0,
1248 v8_num(637));
1249 LocalContext env;
1250 env->Global()->Set(v8_str("obj"),
1251 templ->GetFunction()->NewInstance());
1252 Local<Script> script = v8_compile("obj[900]");
1253 CHECK_EQ(script->Run()->Int32Value(), 900);
1254}
1255
1256
1257v8::Handle<v8::Object> bottom;
1258
1259static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1260 uint32_t index,
1261 const AccessorInfo& info) {
1262 ApiTestFuzzer::Fuzz();
1263 CHECK(info.This()->Equals(bottom));
1264 return v8::Handle<Value>();
1265}
1266
1267static v8::Handle<Value> CheckThisNamedPropertyHandler(
1268 Local<String> name,
1269 const AccessorInfo& info) {
1270 ApiTestFuzzer::Fuzz();
1271 CHECK(info.This()->Equals(bottom));
1272 return v8::Handle<Value>();
1273}
1274
1275
1276v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1277 Local<Value> value,
1278 const AccessorInfo& info) {
1279 ApiTestFuzzer::Fuzz();
1280 CHECK(info.This()->Equals(bottom));
1281 return v8::Handle<Value>();
1282}
1283
1284
1285v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1286 Local<Value> value,
1287 const AccessorInfo& info) {
1288 ApiTestFuzzer::Fuzz();
1289 CHECK(info.This()->Equals(bottom));
1290 return v8::Handle<Value>();
1291}
1292
Iain Merrick75681382010-08-19 15:07:18 +01001293v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery(
Steve Blocka7e24c12009-10-30 11:49:00 +00001294 uint32_t index,
1295 const AccessorInfo& info) {
1296 ApiTestFuzzer::Fuzz();
1297 CHECK(info.This()->Equals(bottom));
Iain Merrick75681382010-08-19 15:07:18 +01001298 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001299}
1300
1301
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001302v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001303 const AccessorInfo& info) {
1304 ApiTestFuzzer::Fuzz();
1305 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001306 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001307}
1308
1309
1310v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1311 uint32_t index,
1312 const AccessorInfo& info) {
1313 ApiTestFuzzer::Fuzz();
1314 CHECK(info.This()->Equals(bottom));
1315 return v8::Handle<v8::Boolean>();
1316}
1317
1318
1319v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1320 Local<String> property,
1321 const AccessorInfo& info) {
1322 ApiTestFuzzer::Fuzz();
1323 CHECK(info.This()->Equals(bottom));
1324 return v8::Handle<v8::Boolean>();
1325}
1326
1327
1328v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1329 const AccessorInfo& info) {
1330 ApiTestFuzzer::Fuzz();
1331 CHECK(info.This()->Equals(bottom));
1332 return v8::Handle<v8::Array>();
1333}
1334
1335
1336v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1337 const AccessorInfo& info) {
1338 ApiTestFuzzer::Fuzz();
1339 CHECK(info.This()->Equals(bottom));
1340 return v8::Handle<v8::Array>();
1341}
1342
1343
1344THREADED_TEST(PropertyHandlerInPrototype) {
1345 v8::HandleScope scope;
1346 LocalContext env;
1347
1348 // Set up a prototype chain with three interceptors.
1349 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1350 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1351 CheckThisIndexedPropertyHandler,
1352 CheckThisIndexedPropertySetter,
1353 CheckThisIndexedPropertyQuery,
1354 CheckThisIndexedPropertyDeleter,
1355 CheckThisIndexedPropertyEnumerator);
1356
1357 templ->InstanceTemplate()->SetNamedPropertyHandler(
1358 CheckThisNamedPropertyHandler,
1359 CheckThisNamedPropertySetter,
1360 CheckThisNamedPropertyQuery,
1361 CheckThisNamedPropertyDeleter,
1362 CheckThisNamedPropertyEnumerator);
1363
1364 bottom = templ->GetFunction()->NewInstance();
1365 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1366 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1367
1368 bottom->Set(v8_str("__proto__"), middle);
1369 middle->Set(v8_str("__proto__"), top);
1370 env->Global()->Set(v8_str("obj"), bottom);
1371
1372 // Indexed and named get.
1373 Script::Compile(v8_str("obj[0]"))->Run();
1374 Script::Compile(v8_str("obj.x"))->Run();
1375
1376 // Indexed and named set.
1377 Script::Compile(v8_str("obj[1] = 42"))->Run();
1378 Script::Compile(v8_str("obj.y = 42"))->Run();
1379
1380 // Indexed and named query.
1381 Script::Compile(v8_str("0 in obj"))->Run();
1382 Script::Compile(v8_str("'x' in obj"))->Run();
1383
1384 // Indexed and named deleter.
1385 Script::Compile(v8_str("delete obj[0]"))->Run();
1386 Script::Compile(v8_str("delete obj.x"))->Run();
1387
1388 // Enumerators.
1389 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1390}
1391
1392
1393static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1394 const AccessorInfo& info) {
1395 ApiTestFuzzer::Fuzz();
1396 if (v8_str("pre")->Equals(key)) {
1397 return v8_str("PrePropertyHandler: pre");
1398 }
1399 return v8::Handle<String>();
1400}
1401
1402
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001403static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1404 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001405 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001406 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001407 }
1408
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001409 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001410}
1411
1412
1413THREADED_TEST(PrePropertyHandler) {
1414 v8::HandleScope scope;
1415 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1416 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1417 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001418 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001419 LocalContext env(NULL, desc->InstanceTemplate());
1420 Script::Compile(v8_str(
1421 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1422 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1423 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1424 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1425 CHECK_EQ(v8_str("Object: on"), result_on);
1426 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1427 CHECK(result_post.IsEmpty());
1428}
1429
1430
1431THREADED_TEST(UndefinedIsNotEnumerable) {
1432 v8::HandleScope scope;
1433 LocalContext env;
1434 v8::Handle<Value> result = Script::Compile(v8_str(
1435 "this.propertyIsEnumerable(undefined)"))->Run();
1436 CHECK(result->IsFalse());
1437}
1438
1439
1440v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001441static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001442
1443
1444static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1445 ApiTestFuzzer::Fuzz();
1446 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1447 if (depth == kTargetRecursionDepth) return v8::Undefined();
1448 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1449 return call_recursively_script->Run();
1450}
1451
1452
1453static v8::Handle<Value> CallFunctionRecursivelyCall(
1454 const v8::Arguments& args) {
1455 ApiTestFuzzer::Fuzz();
1456 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1457 if (depth == kTargetRecursionDepth) {
1458 printf("[depth = %d]\n", depth);
1459 return v8::Undefined();
1460 }
1461 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1462 v8::Handle<Value> function =
1463 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001464 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001465}
1466
1467
1468THREADED_TEST(DeepCrossLanguageRecursion) {
1469 v8::HandleScope scope;
1470 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1471 global->Set(v8_str("callScriptRecursively"),
1472 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1473 global->Set(v8_str("callFunctionRecursively"),
1474 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1475 LocalContext env(NULL, global);
1476
1477 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1478 call_recursively_script = v8_compile("callScriptRecursively()");
1479 v8::Handle<Value> result = call_recursively_script->Run();
1480 call_recursively_script = v8::Handle<Script>();
1481
1482 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1483 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1484}
1485
1486
1487static v8::Handle<Value>
1488 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1489 ApiTestFuzzer::Fuzz();
1490 return v8::ThrowException(key);
1491}
1492
1493
1494static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1495 Local<Value>,
1496 const AccessorInfo&) {
1497 v8::ThrowException(key);
1498 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1499}
1500
1501
1502THREADED_TEST(CallbackExceptionRegression) {
1503 v8::HandleScope scope;
1504 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1505 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1506 ThrowingPropertyHandlerSet);
1507 LocalContext env;
1508 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1509 v8::Handle<Value> otto = Script::Compile(v8_str(
1510 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1511 CHECK_EQ(v8_str("otto"), otto);
1512 v8::Handle<Value> netto = Script::Compile(v8_str(
1513 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1514 CHECK_EQ(v8_str("netto"), netto);
1515}
1516
1517
Steve Blocka7e24c12009-10-30 11:49:00 +00001518THREADED_TEST(FunctionPrototype) {
1519 v8::HandleScope scope;
1520 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1521 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1522 LocalContext env;
1523 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1524 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1525 CHECK_EQ(script->Run()->Int32Value(), 321);
1526}
1527
1528
1529THREADED_TEST(InternalFields) {
1530 v8::HandleScope scope;
1531 LocalContext env;
1532
1533 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1534 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1535 instance_templ->SetInternalFieldCount(1);
1536 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1537 CHECK_EQ(1, obj->InternalFieldCount());
1538 CHECK(obj->GetInternalField(0)->IsUndefined());
1539 obj->SetInternalField(0, v8_num(17));
1540 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1541}
1542
1543
Steve Block6ded16b2010-05-10 14:33:55 +01001544THREADED_TEST(GlobalObjectInternalFields) {
1545 v8::HandleScope scope;
1546 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1547 global_template->SetInternalFieldCount(1);
1548 LocalContext env(NULL, global_template);
1549 v8::Handle<v8::Object> global_proxy = env->Global();
1550 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1551 CHECK_EQ(1, global->InternalFieldCount());
1552 CHECK(global->GetInternalField(0)->IsUndefined());
1553 global->SetInternalField(0, v8_num(17));
1554 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1555}
1556
1557
Steve Blocka7e24c12009-10-30 11:49:00 +00001558THREADED_TEST(InternalFieldsNativePointers) {
1559 v8::HandleScope scope;
1560 LocalContext env;
1561
1562 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1563 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1564 instance_templ->SetInternalFieldCount(1);
1565 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1566 CHECK_EQ(1, obj->InternalFieldCount());
1567 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1568
1569 char* data = new char[100];
1570
1571 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001572 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001573 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001574 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001575
1576 // Check reading and writing aligned pointers.
1577 obj->SetPointerInInternalField(0, aligned);
1578 i::Heap::CollectAllGarbage(false);
1579 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1580
1581 // Check reading and writing unaligned pointers.
1582 obj->SetPointerInInternalField(0, unaligned);
1583 i::Heap::CollectAllGarbage(false);
1584 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1585
1586 delete[] data;
1587}
1588
1589
Steve Block3ce2e202009-11-05 08:53:23 +00001590THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1591 v8::HandleScope scope;
1592 LocalContext env;
1593
1594 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1595 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1596 instance_templ->SetInternalFieldCount(1);
1597 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1598 CHECK_EQ(1, obj->InternalFieldCount());
1599 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1600
1601 char* data = new char[100];
1602
1603 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001604 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001605 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001606 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001607
1608 obj->SetPointerInInternalField(0, aligned);
1609 i::Heap::CollectAllGarbage(false);
1610 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1611
1612 obj->SetPointerInInternalField(0, unaligned);
1613 i::Heap::CollectAllGarbage(false);
1614 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1615
1616 obj->SetInternalField(0, v8::External::Wrap(aligned));
1617 i::Heap::CollectAllGarbage(false);
1618 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1619
1620 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1621 i::Heap::CollectAllGarbage(false);
1622 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1623
1624 delete[] data;
1625}
1626
1627
Steve Blocka7e24c12009-10-30 11:49:00 +00001628THREADED_TEST(IdentityHash) {
1629 v8::HandleScope scope;
1630 LocalContext env;
1631
1632 // Ensure that the test starts with an fresh heap to test whether the hash
1633 // code is based on the address.
1634 i::Heap::CollectAllGarbage(false);
1635 Local<v8::Object> obj = v8::Object::New();
1636 int hash = obj->GetIdentityHash();
1637 int hash1 = obj->GetIdentityHash();
1638 CHECK_EQ(hash, hash1);
1639 int hash2 = v8::Object::New()->GetIdentityHash();
1640 // Since the identity hash is essentially a random number two consecutive
1641 // objects should not be assigned the same hash code. If the test below fails
1642 // the random number generator should be evaluated.
1643 CHECK_NE(hash, hash2);
1644 i::Heap::CollectAllGarbage(false);
1645 int hash3 = v8::Object::New()->GetIdentityHash();
1646 // Make sure that the identity hash is not based on the initial address of
1647 // the object alone. If the test below fails the random number generator
1648 // should be evaluated.
1649 CHECK_NE(hash, hash3);
1650 int hash4 = obj->GetIdentityHash();
1651 CHECK_EQ(hash, hash4);
Steve Block1e0659c2011-05-24 12:43:12 +01001652
1653 // Check identity hashes behaviour in the presence of JS accessors.
1654 // Put a getter for 'v8::IdentityHash' on the Object's prototype:
1655 {
1656 CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n");
1657 Local<v8::Object> o1 = v8::Object::New();
1658 Local<v8::Object> o2 = v8::Object::New();
1659 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
1660 }
1661 {
1662 CompileRun(
1663 "function cnst() { return 42; };\n"
1664 "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n");
1665 Local<v8::Object> o1 = v8::Object::New();
1666 Local<v8::Object> o2 = v8::Object::New();
1667 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash());
1668 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001669}
1670
1671
1672THREADED_TEST(HiddenProperties) {
1673 v8::HandleScope scope;
1674 LocalContext env;
1675
1676 v8::Local<v8::Object> obj = v8::Object::New();
1677 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1678 v8::Local<v8::String> empty = v8_str("");
1679 v8::Local<v8::String> prop_name = v8_str("prop_name");
1680
1681 i::Heap::CollectAllGarbage(false);
1682
1683 // Make sure delete of a non-existent hidden value works
1684 CHECK(obj->DeleteHiddenValue(key));
1685
1686 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1687 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1688 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1689 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1690
1691 i::Heap::CollectAllGarbage(false);
1692
1693 // Make sure we do not find the hidden property.
1694 CHECK(!obj->Has(empty));
1695 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1696 CHECK(obj->Get(empty)->IsUndefined());
1697 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1698 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1699 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1700 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1701
1702 i::Heap::CollectAllGarbage(false);
1703
1704 // Add another property and delete it afterwards to force the object in
1705 // slow case.
1706 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1707 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1708 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1709 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1710 CHECK(obj->Delete(prop_name));
1711 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1712
1713 i::Heap::CollectAllGarbage(false);
1714
1715 CHECK(obj->DeleteHiddenValue(key));
1716 CHECK(obj->GetHiddenValue(key).IsEmpty());
1717}
1718
1719
Steve Blockd0582a62009-12-15 09:54:21 +00001720static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001721static v8::Handle<Value> InterceptorForHiddenProperties(
1722 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001723 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001724 return v8::Handle<Value>();
1725}
1726
1727
1728THREADED_TEST(HiddenPropertiesWithInterceptors) {
1729 v8::HandleScope scope;
1730 LocalContext context;
1731
Steve Blockd0582a62009-12-15 09:54:21 +00001732 interceptor_for_hidden_properties_called = false;
1733
Steve Blocka7e24c12009-10-30 11:49:00 +00001734 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1735
1736 // Associate an interceptor with an object and start setting hidden values.
1737 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1738 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1739 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1740 Local<v8::Function> function = fun_templ->GetFunction();
1741 Local<v8::Object> obj = function->NewInstance();
1742 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1743 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001744 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001745}
1746
1747
1748THREADED_TEST(External) {
1749 v8::HandleScope scope;
1750 int x = 3;
1751 Local<v8::External> ext = v8::External::New(&x);
1752 LocalContext env;
1753 env->Global()->Set(v8_str("ext"), ext);
1754 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001755 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001756 int* ptr = static_cast<int*>(reext->Value());
1757 CHECK_EQ(x, 3);
1758 *ptr = 10;
1759 CHECK_EQ(x, 10);
1760
1761 // Make sure unaligned pointers are wrapped properly.
1762 char* data = i::StrDup("0123456789");
1763 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1764 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1765 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1766 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1767
1768 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1769 CHECK_EQ('0', *char_ptr);
1770 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1771 CHECK_EQ('1', *char_ptr);
1772 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1773 CHECK_EQ('2', *char_ptr);
1774 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1775 CHECK_EQ('3', *char_ptr);
1776 i::DeleteArray(data);
1777}
1778
1779
1780THREADED_TEST(GlobalHandle) {
1781 v8::Persistent<String> global;
1782 {
1783 v8::HandleScope scope;
1784 Local<String> str = v8_str("str");
1785 global = v8::Persistent<String>::New(str);
1786 }
1787 CHECK_EQ(global->Length(), 3);
1788 global.Dispose();
1789}
1790
1791
1792THREADED_TEST(ScriptException) {
1793 v8::HandleScope scope;
1794 LocalContext env;
1795 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1796 v8::TryCatch try_catch;
1797 Local<Value> result = script->Run();
1798 CHECK(result.IsEmpty());
1799 CHECK(try_catch.HasCaught());
1800 String::AsciiValue exception_value(try_catch.Exception());
1801 CHECK_EQ(*exception_value, "panama!");
1802}
1803
1804
1805bool message_received;
1806
1807
1808static void check_message(v8::Handle<v8::Message> message,
1809 v8::Handle<Value> data) {
1810 CHECK_EQ(5.76, data->NumberValue());
1811 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1812 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1813 message_received = true;
1814}
1815
1816
1817THREADED_TEST(MessageHandlerData) {
1818 message_received = false;
1819 v8::HandleScope scope;
1820 CHECK(!message_received);
1821 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1822 LocalContext context;
1823 v8::ScriptOrigin origin =
1824 v8::ScriptOrigin(v8_str("6.75"));
1825 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1826 &origin);
1827 script->SetData(v8_str("7.56"));
1828 script->Run();
1829 CHECK(message_received);
1830 // clear out the message listener
1831 v8::V8::RemoveMessageListeners(check_message);
1832}
1833
1834
1835THREADED_TEST(GetSetProperty) {
1836 v8::HandleScope scope;
1837 LocalContext context;
1838 context->Global()->Set(v8_str("foo"), v8_num(14));
1839 context->Global()->Set(v8_str("12"), v8_num(92));
1840 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1841 context->Global()->Set(v8_num(13), v8_num(56));
1842 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1843 CHECK_EQ(14, foo->Int32Value());
1844 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1845 CHECK_EQ(92, twelve->Int32Value());
1846 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1847 CHECK_EQ(32, sixteen->Int32Value());
1848 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1849 CHECK_EQ(56, thirteen->Int32Value());
1850 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1851 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1852 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1853 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1854 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1855 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1856 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1857 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1858 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1859}
1860
1861
1862THREADED_TEST(PropertyAttributes) {
1863 v8::HandleScope scope;
1864 LocalContext context;
1865 // read-only
1866 Local<String> prop = v8_str("read_only");
1867 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1868 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1869 Script::Compile(v8_str("read_only = 9"))->Run();
1870 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1871 context->Global()->Set(prop, v8_num(10));
1872 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1873 // dont-delete
1874 prop = v8_str("dont_delete");
1875 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1876 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1877 Script::Compile(v8_str("delete dont_delete"))->Run();
1878 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1879}
1880
1881
1882THREADED_TEST(Array) {
1883 v8::HandleScope scope;
1884 LocalContext context;
1885 Local<v8::Array> array = v8::Array::New();
1886 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001887 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001888 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001889 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001890 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001891 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001892 CHECK_EQ(3, array->Length());
1893 CHECK(!array->Has(0));
1894 CHECK(!array->Has(1));
1895 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001896 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001897 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001898 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001899 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001900 CHECK_EQ(1, arr->Get(0)->Int32Value());
1901 CHECK_EQ(2, arr->Get(1)->Int32Value());
1902 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001903}
1904
1905
1906v8::Handle<Value> HandleF(const v8::Arguments& args) {
1907 v8::HandleScope scope;
1908 ApiTestFuzzer::Fuzz();
1909 Local<v8::Array> result = v8::Array::New(args.Length());
1910 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001911 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001912 return scope.Close(result);
1913}
1914
1915
1916THREADED_TEST(Vector) {
1917 v8::HandleScope scope;
1918 Local<ObjectTemplate> global = ObjectTemplate::New();
1919 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1920 LocalContext context(0, global);
1921
1922 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001923 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001924 CHECK_EQ(0, a0->Length());
1925
1926 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001927 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001928 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001929 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001930
1931 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001932 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001933 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001934 CHECK_EQ(12, a2->Get(0)->Int32Value());
1935 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001936
1937 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001938 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001939 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001940 CHECK_EQ(14, a3->Get(0)->Int32Value());
1941 CHECK_EQ(15, a3->Get(1)->Int32Value());
1942 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001943
1944 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001945 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001946 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001947 CHECK_EQ(17, a4->Get(0)->Int32Value());
1948 CHECK_EQ(18, a4->Get(1)->Int32Value());
1949 CHECK_EQ(19, a4->Get(2)->Int32Value());
1950 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001951}
1952
1953
1954THREADED_TEST(FunctionCall) {
1955 v8::HandleScope scope;
1956 LocalContext context;
1957 CompileRun(
1958 "function Foo() {"
1959 " var result = [];"
1960 " for (var i = 0; i < arguments.length; i++) {"
1961 " result.push(arguments[i]);"
1962 " }"
1963 " return result;"
1964 "}");
1965 Local<Function> Foo =
1966 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1967
1968 v8::Handle<Value>* args0 = NULL;
1969 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1970 CHECK_EQ(0, a0->Length());
1971
1972 v8::Handle<Value> args1[] = { v8_num(1.1) };
1973 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1974 CHECK_EQ(1, a1->Length());
1975 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1976
1977 v8::Handle<Value> args2[] = { v8_num(2.2),
1978 v8_num(3.3) };
1979 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1980 CHECK_EQ(2, a2->Length());
1981 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1982 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1983
1984 v8::Handle<Value> args3[] = { v8_num(4.4),
1985 v8_num(5.5),
1986 v8_num(6.6) };
1987 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1988 CHECK_EQ(3, a3->Length());
1989 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1990 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1991 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1992
1993 v8::Handle<Value> args4[] = { v8_num(7.7),
1994 v8_num(8.8),
1995 v8_num(9.9),
1996 v8_num(10.11) };
1997 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1998 CHECK_EQ(4, a4->Length());
1999 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2000 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2001 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2002 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2003}
2004
2005
2006static const char* js_code_causing_out_of_memory =
2007 "var a = new Array(); while(true) a.push(a);";
2008
2009
2010// These tests run for a long time and prevent us from running tests
2011// that come after them so they cannot run in parallel.
2012TEST(OutOfMemory) {
2013 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002014 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002015 // Set heap limits.
2016 static const int K = 1024;
2017 v8::ResourceConstraints constraints;
2018 constraints.set_max_young_space_size(256 * K);
2019 constraints.set_max_old_space_size(4 * K * K);
2020 v8::SetResourceConstraints(&constraints);
2021
2022 // Execute a script that causes out of memory.
2023 v8::HandleScope scope;
2024 LocalContext context;
2025 v8::V8::IgnoreOutOfMemoryException();
2026 Local<Script> script =
2027 Script::Compile(String::New(js_code_causing_out_of_memory));
2028 Local<Value> result = script->Run();
2029
2030 // Check for out of memory state.
2031 CHECK(result.IsEmpty());
2032 CHECK(context->HasOutOfMemoryException());
2033}
2034
2035
2036v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
2037 ApiTestFuzzer::Fuzz();
2038
2039 v8::HandleScope scope;
2040 LocalContext context;
2041 Local<Script> script =
2042 Script::Compile(String::New(js_code_causing_out_of_memory));
2043 Local<Value> result = script->Run();
2044
2045 // Check for out of memory state.
2046 CHECK(result.IsEmpty());
2047 CHECK(context->HasOutOfMemoryException());
2048
2049 return result;
2050}
2051
2052
2053TEST(OutOfMemoryNested) {
2054 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002055 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002056 // Set heap limits.
2057 static const int K = 1024;
2058 v8::ResourceConstraints constraints;
2059 constraints.set_max_young_space_size(256 * K);
2060 constraints.set_max_old_space_size(4 * K * K);
2061 v8::SetResourceConstraints(&constraints);
2062
2063 v8::HandleScope scope;
2064 Local<ObjectTemplate> templ = ObjectTemplate::New();
2065 templ->Set(v8_str("ProvokeOutOfMemory"),
2066 v8::FunctionTemplate::New(ProvokeOutOfMemory));
2067 LocalContext context(0, templ);
2068 v8::V8::IgnoreOutOfMemoryException();
2069 Local<Value> result = CompileRun(
2070 "var thrown = false;"
2071 "try {"
2072 " ProvokeOutOfMemory();"
2073 "} catch (e) {"
2074 " thrown = true;"
2075 "}");
2076 // Check for out of memory state.
2077 CHECK(result.IsEmpty());
2078 CHECK(context->HasOutOfMemoryException());
2079}
2080
2081
2082TEST(HugeConsStringOutOfMemory) {
2083 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01002084 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00002085 v8::HandleScope scope;
2086 LocalContext context;
2087 // Set heap limits.
2088 static const int K = 1024;
2089 v8::ResourceConstraints constraints;
2090 constraints.set_max_young_space_size(256 * K);
2091 constraints.set_max_old_space_size(2 * K * K);
2092 v8::SetResourceConstraints(&constraints);
2093
2094 // Execute a script that causes out of memory.
2095 v8::V8::IgnoreOutOfMemoryException();
2096
2097 // Build huge string. This should fail with out of memory exception.
2098 Local<Value> result = CompileRun(
2099 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00002100 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00002101
2102 // Check for out of memory state.
2103 CHECK(result.IsEmpty());
2104 CHECK(context->HasOutOfMemoryException());
2105}
2106
2107
2108THREADED_TEST(ConstructCall) {
2109 v8::HandleScope scope;
2110 LocalContext context;
2111 CompileRun(
2112 "function Foo() {"
2113 " var result = [];"
2114 " for (var i = 0; i < arguments.length; i++) {"
2115 " result.push(arguments[i]);"
2116 " }"
2117 " return result;"
2118 "}");
2119 Local<Function> Foo =
2120 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2121
2122 v8::Handle<Value>* args0 = NULL;
2123 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2124 CHECK_EQ(0, a0->Length());
2125
2126 v8::Handle<Value> args1[] = { v8_num(1.1) };
2127 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2128 CHECK_EQ(1, a1->Length());
2129 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2130
2131 v8::Handle<Value> args2[] = { v8_num(2.2),
2132 v8_num(3.3) };
2133 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2134 CHECK_EQ(2, a2->Length());
2135 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2136 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2137
2138 v8::Handle<Value> args3[] = { v8_num(4.4),
2139 v8_num(5.5),
2140 v8_num(6.6) };
2141 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2142 CHECK_EQ(3, a3->Length());
2143 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2144 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2145 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2146
2147 v8::Handle<Value> args4[] = { v8_num(7.7),
2148 v8_num(8.8),
2149 v8_num(9.9),
2150 v8_num(10.11) };
2151 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2152 CHECK_EQ(4, a4->Length());
2153 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2154 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2155 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2156 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2157}
2158
2159
2160static void CheckUncle(v8::TryCatch* try_catch) {
2161 CHECK(try_catch->HasCaught());
2162 String::AsciiValue str_value(try_catch->Exception());
2163 CHECK_EQ(*str_value, "uncle?");
2164 try_catch->Reset();
2165}
2166
2167
Steve Block6ded16b2010-05-10 14:33:55 +01002168THREADED_TEST(ConversionNumber) {
2169 v8::HandleScope scope;
2170 LocalContext env;
2171 // Very large number.
2172 CompileRun("var obj = Math.pow(2,32) * 1237;");
2173 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2174 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2175 CHECK_EQ(0, obj->ToInt32()->Value());
2176 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2177 // Large number.
2178 CompileRun("var obj = -1234567890123;");
2179 obj = env->Global()->Get(v8_str("obj"));
2180 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2181 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2182 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2183 // Small positive integer.
2184 CompileRun("var obj = 42;");
2185 obj = env->Global()->Get(v8_str("obj"));
2186 CHECK_EQ(42.0, obj->ToNumber()->Value());
2187 CHECK_EQ(42, obj->ToInt32()->Value());
2188 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2189 // Negative integer.
2190 CompileRun("var obj = -37;");
2191 obj = env->Global()->Get(v8_str("obj"));
2192 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2193 CHECK_EQ(-37, obj->ToInt32()->Value());
2194 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2195 // Positive non-int32 integer.
2196 CompileRun("var obj = 0x81234567;");
2197 obj = env->Global()->Get(v8_str("obj"));
2198 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2199 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2200 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2201 // Fraction.
2202 CompileRun("var obj = 42.3;");
2203 obj = env->Global()->Get(v8_str("obj"));
2204 CHECK_EQ(42.3, obj->ToNumber()->Value());
2205 CHECK_EQ(42, obj->ToInt32()->Value());
2206 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2207 // Large negative fraction.
2208 CompileRun("var obj = -5726623061.75;");
2209 obj = env->Global()->Get(v8_str("obj"));
2210 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2211 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2212 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2213}
2214
2215
2216THREADED_TEST(isNumberType) {
2217 v8::HandleScope scope;
2218 LocalContext env;
2219 // Very large number.
2220 CompileRun("var obj = Math.pow(2,32) * 1237;");
2221 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2222 CHECK(!obj->IsInt32());
2223 CHECK(!obj->IsUint32());
2224 // Large negative number.
2225 CompileRun("var obj = -1234567890123;");
2226 obj = env->Global()->Get(v8_str("obj"));
2227 CHECK(!obj->IsInt32());
2228 CHECK(!obj->IsUint32());
2229 // Small positive integer.
2230 CompileRun("var obj = 42;");
2231 obj = env->Global()->Get(v8_str("obj"));
2232 CHECK(obj->IsInt32());
2233 CHECK(obj->IsUint32());
2234 // Negative integer.
2235 CompileRun("var obj = -37;");
2236 obj = env->Global()->Get(v8_str("obj"));
2237 CHECK(obj->IsInt32());
2238 CHECK(!obj->IsUint32());
2239 // Positive non-int32 integer.
2240 CompileRun("var obj = 0x81234567;");
2241 obj = env->Global()->Get(v8_str("obj"));
2242 CHECK(!obj->IsInt32());
2243 CHECK(obj->IsUint32());
2244 // Fraction.
2245 CompileRun("var obj = 42.3;");
2246 obj = env->Global()->Get(v8_str("obj"));
2247 CHECK(!obj->IsInt32());
2248 CHECK(!obj->IsUint32());
2249 // Large negative fraction.
2250 CompileRun("var obj = -5726623061.75;");
2251 obj = env->Global()->Get(v8_str("obj"));
2252 CHECK(!obj->IsInt32());
2253 CHECK(!obj->IsUint32());
2254}
2255
2256
Steve Blocka7e24c12009-10-30 11:49:00 +00002257THREADED_TEST(ConversionException) {
2258 v8::HandleScope scope;
2259 LocalContext env;
2260 CompileRun(
2261 "function TestClass() { };"
2262 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2263 "var obj = new TestClass();");
2264 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2265
2266 v8::TryCatch try_catch;
2267
2268 Local<Value> to_string_result = obj->ToString();
2269 CHECK(to_string_result.IsEmpty());
2270 CheckUncle(&try_catch);
2271
2272 Local<Value> to_number_result = obj->ToNumber();
2273 CHECK(to_number_result.IsEmpty());
2274 CheckUncle(&try_catch);
2275
2276 Local<Value> to_integer_result = obj->ToInteger();
2277 CHECK(to_integer_result.IsEmpty());
2278 CheckUncle(&try_catch);
2279
2280 Local<Value> to_uint32_result = obj->ToUint32();
2281 CHECK(to_uint32_result.IsEmpty());
2282 CheckUncle(&try_catch);
2283
2284 Local<Value> to_int32_result = obj->ToInt32();
2285 CHECK(to_int32_result.IsEmpty());
2286 CheckUncle(&try_catch);
2287
2288 Local<Value> to_object_result = v8::Undefined()->ToObject();
2289 CHECK(to_object_result.IsEmpty());
2290 CHECK(try_catch.HasCaught());
2291 try_catch.Reset();
2292
2293 int32_t int32_value = obj->Int32Value();
2294 CHECK_EQ(0, int32_value);
2295 CheckUncle(&try_catch);
2296
2297 uint32_t uint32_value = obj->Uint32Value();
2298 CHECK_EQ(0, uint32_value);
2299 CheckUncle(&try_catch);
2300
2301 double number_value = obj->NumberValue();
2302 CHECK_NE(0, IsNaN(number_value));
2303 CheckUncle(&try_catch);
2304
2305 int64_t integer_value = obj->IntegerValue();
2306 CHECK_EQ(0.0, static_cast<double>(integer_value));
2307 CheckUncle(&try_catch);
2308}
2309
2310
2311v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2312 ApiTestFuzzer::Fuzz();
2313 return v8::ThrowException(v8_str("konto"));
2314}
2315
2316
2317v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2318 if (args.Length() < 1) return v8::Boolean::New(false);
2319 v8::HandleScope scope;
2320 v8::TryCatch try_catch;
2321 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2322 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2323 return v8::Boolean::New(try_catch.HasCaught());
2324}
2325
2326
2327THREADED_TEST(APICatch) {
2328 v8::HandleScope scope;
2329 Local<ObjectTemplate> templ = ObjectTemplate::New();
2330 templ->Set(v8_str("ThrowFromC"),
2331 v8::FunctionTemplate::New(ThrowFromC));
2332 LocalContext context(0, templ);
2333 CompileRun(
2334 "var thrown = false;"
2335 "try {"
2336 " ThrowFromC();"
2337 "} catch (e) {"
2338 " thrown = true;"
2339 "}");
2340 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2341 CHECK(thrown->BooleanValue());
2342}
2343
2344
2345THREADED_TEST(APIThrowTryCatch) {
2346 v8::HandleScope scope;
2347 Local<ObjectTemplate> templ = ObjectTemplate::New();
2348 templ->Set(v8_str("ThrowFromC"),
2349 v8::FunctionTemplate::New(ThrowFromC));
2350 LocalContext context(0, templ);
2351 v8::TryCatch try_catch;
2352 CompileRun("ThrowFromC();");
2353 CHECK(try_catch.HasCaught());
2354}
2355
2356
2357// Test that a try-finally block doesn't shadow a try-catch block
2358// when setting up an external handler.
2359//
2360// BUG(271): Some of the exception propagation does not work on the
2361// ARM simulator because the simulator separates the C++ stack and the
2362// JS stack. This test therefore fails on the simulator. The test is
2363// not threaded to allow the threading tests to run on the simulator.
2364TEST(TryCatchInTryFinally) {
2365 v8::HandleScope scope;
2366 Local<ObjectTemplate> templ = ObjectTemplate::New();
2367 templ->Set(v8_str("CCatcher"),
2368 v8::FunctionTemplate::New(CCatcher));
2369 LocalContext context(0, templ);
2370 Local<Value> result = CompileRun("try {"
2371 " try {"
2372 " CCatcher('throw 7;');"
2373 " } finally {"
2374 " }"
2375 "} catch (e) {"
2376 "}");
2377 CHECK(result->IsTrue());
2378}
2379
2380
Ben Murdochb8e0da22011-05-16 14:20:40 +01002381static void check_reference_error_message(
2382 v8::Handle<v8::Message> message,
2383 v8::Handle<v8::Value> data) {
2384 const char* reference_error = "Uncaught ReferenceError: asdf is not defined";
2385 CHECK(message->Get()->Equals(v8_str(reference_error)));
2386}
2387
2388
Steve Block1e0659c2011-05-24 12:43:12 +01002389static v8::Handle<Value> Fail(const v8::Arguments& args) {
2390 ApiTestFuzzer::Fuzz();
2391 CHECK(false);
2392 return v8::Undefined();
2393}
2394
2395
2396// Test that overwritten methods are not invoked on uncaught exception
2397// formatting. However, they are invoked when performing normal error
2398// string conversions.
Ben Murdochb8e0da22011-05-16 14:20:40 +01002399TEST(APIThrowMessageOverwrittenToString) {
2400 v8::HandleScope scope;
2401 v8::V8::AddMessageListener(check_reference_error_message);
Steve Block1e0659c2011-05-24 12:43:12 +01002402 Local<ObjectTemplate> templ = ObjectTemplate::New();
2403 templ->Set(v8_str("fail"), v8::FunctionTemplate::New(Fail));
2404 LocalContext context(NULL, templ);
2405 CompileRun("asdf;");
2406 CompileRun("var limit = {};"
2407 "limit.valueOf = fail;"
2408 "Error.stackTraceLimit = limit;");
2409 CompileRun("asdf");
2410 CompileRun("Array.prototype.pop = fail;");
2411 CompileRun("Object.prototype.hasOwnProperty = fail;");
2412 CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }");
2413 CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }");
2414 CompileRun("String.prototype.toString = function f() { return 'Yikes'; }");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002415 CompileRun("ReferenceError.prototype.toString ="
2416 " function() { return 'Whoops' }");
2417 CompileRun("asdf;");
Steve Block1e0659c2011-05-24 12:43:12 +01002418 CompileRun("ReferenceError.prototype.constructor.name = void 0;");
2419 CompileRun("asdf;");
2420 CompileRun("ReferenceError.prototype.constructor = void 0;");
2421 CompileRun("asdf;");
2422 CompileRun("ReferenceError.prototype.__proto__ = new Object();");
2423 CompileRun("asdf;");
2424 CompileRun("ReferenceError.prototype = new Object();");
2425 CompileRun("asdf;");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002426 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }");
2427 CHECK(string->Equals(v8_str("Whoops")));
Steve Block1e0659c2011-05-24 12:43:12 +01002428 CompileRun("ReferenceError.prototype.constructor = new Object();"
2429 "ReferenceError.prototype.constructor.name = 1;"
2430 "Number.prototype.toString = function() { return 'Whoops'; };"
2431 "ReferenceError.prototype.toString = Object.prototype.toString;");
2432 CompileRun("asdf;");
Ben Murdochb8e0da22011-05-16 14:20:40 +01002433 v8::V8::RemoveMessageListeners(check_message);
2434}
2435
2436
Steve Blocka7e24c12009-10-30 11:49:00 +00002437static void receive_message(v8::Handle<v8::Message> message,
2438 v8::Handle<v8::Value> data) {
2439 message->Get();
2440 message_received = true;
2441}
2442
2443
2444TEST(APIThrowMessage) {
2445 message_received = false;
2446 v8::HandleScope scope;
2447 v8::V8::AddMessageListener(receive_message);
2448 Local<ObjectTemplate> templ = ObjectTemplate::New();
2449 templ->Set(v8_str("ThrowFromC"),
2450 v8::FunctionTemplate::New(ThrowFromC));
2451 LocalContext context(0, templ);
2452 CompileRun("ThrowFromC();");
2453 CHECK(message_received);
2454 v8::V8::RemoveMessageListeners(check_message);
2455}
2456
2457
2458TEST(APIThrowMessageAndVerboseTryCatch) {
2459 message_received = false;
2460 v8::HandleScope scope;
2461 v8::V8::AddMessageListener(receive_message);
2462 Local<ObjectTemplate> templ = ObjectTemplate::New();
2463 templ->Set(v8_str("ThrowFromC"),
2464 v8::FunctionTemplate::New(ThrowFromC));
2465 LocalContext context(0, templ);
2466 v8::TryCatch try_catch;
2467 try_catch.SetVerbose(true);
2468 Local<Value> result = CompileRun("ThrowFromC();");
2469 CHECK(try_catch.HasCaught());
2470 CHECK(result.IsEmpty());
2471 CHECK(message_received);
2472 v8::V8::RemoveMessageListeners(check_message);
2473}
2474
2475
2476THREADED_TEST(ExternalScriptException) {
2477 v8::HandleScope scope;
2478 Local<ObjectTemplate> templ = ObjectTemplate::New();
2479 templ->Set(v8_str("ThrowFromC"),
2480 v8::FunctionTemplate::New(ThrowFromC));
2481 LocalContext context(0, templ);
2482
2483 v8::TryCatch try_catch;
2484 Local<Script> script
2485 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2486 Local<Value> result = script->Run();
2487 CHECK(result.IsEmpty());
2488 CHECK(try_catch.HasCaught());
2489 String::AsciiValue exception_value(try_catch.Exception());
2490 CHECK_EQ("konto", *exception_value);
2491}
2492
2493
2494
2495v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2496 ApiTestFuzzer::Fuzz();
2497 CHECK_EQ(4, args.Length());
2498 int count = args[0]->Int32Value();
2499 int cInterval = args[2]->Int32Value();
2500 if (count == 0) {
2501 return v8::ThrowException(v8_str("FromC"));
2502 } else {
2503 Local<v8::Object> global = Context::GetCurrent()->Global();
2504 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2505 v8::Handle<Value> argv[] = { v8_num(count - 1),
2506 args[1],
2507 args[2],
2508 args[3] };
2509 if (count % cInterval == 0) {
2510 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002511 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002512 int expected = args[3]->Int32Value();
2513 if (try_catch.HasCaught()) {
2514 CHECK_EQ(expected, count);
2515 CHECK(result.IsEmpty());
2516 CHECK(!i::Top::has_scheduled_exception());
2517 } else {
2518 CHECK_NE(expected, count);
2519 }
2520 return result;
2521 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002522 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002523 }
2524 }
2525}
2526
2527
2528v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2529 ApiTestFuzzer::Fuzz();
2530 CHECK_EQ(3, args.Length());
2531 bool equality = args[0]->BooleanValue();
2532 int count = args[1]->Int32Value();
2533 int expected = args[2]->Int32Value();
2534 if (equality) {
2535 CHECK_EQ(count, expected);
2536 } else {
2537 CHECK_NE(count, expected);
2538 }
2539 return v8::Undefined();
2540}
2541
2542
2543THREADED_TEST(EvalInTryFinally) {
2544 v8::HandleScope scope;
2545 LocalContext context;
2546 v8::TryCatch try_catch;
2547 CompileRun("(function() {"
2548 " try {"
2549 " eval('asldkf (*&^&*^');"
2550 " } finally {"
2551 " return;"
2552 " }"
2553 "})()");
2554 CHECK(!try_catch.HasCaught());
2555}
2556
2557
2558// This test works by making a stack of alternating JavaScript and C
2559// activations. These activations set up exception handlers with regular
2560// intervals, one interval for C activations and another for JavaScript
2561// activations. When enough activations have been created an exception is
2562// thrown and we check that the right activation catches the exception and that
2563// no other activations do. The right activation is always the topmost one with
2564// a handler, regardless of whether it is in JavaScript or C.
2565//
2566// The notation used to describe a test case looks like this:
2567//
2568// *JS[4] *C[3] @JS[2] C[1] JS[0]
2569//
2570// Each entry is an activation, either JS or C. The index is the count at that
2571// level. Stars identify activations with exception handlers, the @ identifies
2572// the exception handler that should catch the exception.
2573//
2574// BUG(271): Some of the exception propagation does not work on the
2575// ARM simulator because the simulator separates the C++ stack and the
2576// JS stack. This test therefore fails on the simulator. The test is
2577// not threaded to allow the threading tests to run on the simulator.
2578TEST(ExceptionOrder) {
2579 v8::HandleScope scope;
2580 Local<ObjectTemplate> templ = ObjectTemplate::New();
2581 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2582 templ->Set(v8_str("CThrowCountDown"),
2583 v8::FunctionTemplate::New(CThrowCountDown));
2584 LocalContext context(0, templ);
2585 CompileRun(
2586 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2587 " if (count == 0) throw 'FromJS';"
2588 " if (count % jsInterval == 0) {"
2589 " try {"
2590 " var value = CThrowCountDown(count - 1,"
2591 " jsInterval,"
2592 " cInterval,"
2593 " expected);"
2594 " check(false, count, expected);"
2595 " return value;"
2596 " } catch (e) {"
2597 " check(true, count, expected);"
2598 " }"
2599 " } else {"
2600 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2601 " }"
2602 "}");
2603 Local<Function> fun =
2604 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2605
2606 const int argc = 4;
2607 // count jsInterval cInterval expected
2608
2609 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2610 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2611 fun->Call(fun, argc, a0);
2612
2613 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2614 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2615 fun->Call(fun, argc, a1);
2616
2617 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2618 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2619 fun->Call(fun, argc, a2);
2620
2621 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2622 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2623 fun->Call(fun, argc, a3);
2624
2625 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2626 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2627 fun->Call(fun, argc, a4);
2628
2629 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2630 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2631 fun->Call(fun, argc, a5);
2632}
2633
2634
2635v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2636 ApiTestFuzzer::Fuzz();
2637 CHECK_EQ(1, args.Length());
2638 return v8::ThrowException(args[0]);
2639}
2640
2641
2642THREADED_TEST(ThrowValues) {
2643 v8::HandleScope scope;
2644 Local<ObjectTemplate> templ = ObjectTemplate::New();
2645 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2646 LocalContext context(0, templ);
2647 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2648 "function Run(obj) {"
2649 " try {"
2650 " Throw(obj);"
2651 " } catch (e) {"
2652 " return e;"
2653 " }"
2654 " return 'no exception';"
2655 "}"
2656 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2657 CHECK_EQ(5, result->Length());
2658 CHECK(result->Get(v8::Integer::New(0))->IsString());
2659 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2660 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2661 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2662 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2663 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2664 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2665}
2666
2667
2668THREADED_TEST(CatchZero) {
2669 v8::HandleScope scope;
2670 LocalContext context;
2671 v8::TryCatch try_catch;
2672 CHECK(!try_catch.HasCaught());
2673 Script::Compile(v8_str("throw 10"))->Run();
2674 CHECK(try_catch.HasCaught());
2675 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2676 try_catch.Reset();
2677 CHECK(!try_catch.HasCaught());
2678 Script::Compile(v8_str("throw 0"))->Run();
2679 CHECK(try_catch.HasCaught());
2680 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2681}
2682
2683
2684THREADED_TEST(CatchExceptionFromWith) {
2685 v8::HandleScope scope;
2686 LocalContext context;
2687 v8::TryCatch try_catch;
2688 CHECK(!try_catch.HasCaught());
2689 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2690 CHECK(try_catch.HasCaught());
2691}
2692
2693
Ben Murdoche0cee9b2011-05-25 10:26:03 +01002694THREADED_TEST(TryCatchAndFinallyHidingException) {
2695 v8::HandleScope scope;
2696 LocalContext context;
2697 v8::TryCatch try_catch;
2698 CHECK(!try_catch.HasCaught());
2699 CompileRun("function f(k) { try { this[k]; } finally { return 0; } };");
2700 CompileRun("f({toString: function() { throw 42; }});");
2701 CHECK(!try_catch.HasCaught());
2702}
2703
2704
2705v8::Handle<v8::Value> WithTryCatch(const v8::Arguments& args) {
2706 v8::TryCatch try_catch;
2707 return v8::Undefined();
2708}
2709
2710
2711THREADED_TEST(TryCatchAndFinally) {
2712 v8::HandleScope scope;
2713 LocalContext context;
2714 context->Global()->Set(
2715 v8_str("native_with_try_catch"),
2716 v8::FunctionTemplate::New(WithTryCatch)->GetFunction());
2717 v8::TryCatch try_catch;
2718 CHECK(!try_catch.HasCaught());
2719 CompileRun(
2720 "try {\n"
2721 " throw new Error('a');\n"
2722 "} finally {\n"
2723 " native_with_try_catch();\n"
2724 "}\n");
2725 CHECK(try_catch.HasCaught());
2726}
2727
2728
Steve Blocka7e24c12009-10-30 11:49:00 +00002729THREADED_TEST(Equality) {
2730 v8::HandleScope scope;
2731 LocalContext context;
2732 // Check that equality works at all before relying on CHECK_EQ
2733 CHECK(v8_str("a")->Equals(v8_str("a")));
2734 CHECK(!v8_str("a")->Equals(v8_str("b")));
2735
2736 CHECK_EQ(v8_str("a"), v8_str("a"));
2737 CHECK_NE(v8_str("a"), v8_str("b"));
2738 CHECK_EQ(v8_num(1), v8_num(1));
2739 CHECK_EQ(v8_num(1.00), v8_num(1));
2740 CHECK_NE(v8_num(1), v8_num(2));
2741
2742 // Assume String is not symbol.
2743 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2744 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2745 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2746 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2747 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2748 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2749 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2750 CHECK(!not_a_number->StrictEquals(not_a_number));
2751 CHECK(v8::False()->StrictEquals(v8::False()));
2752 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2753
2754 v8::Handle<v8::Object> obj = v8::Object::New();
2755 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2756 CHECK(alias->StrictEquals(obj));
2757 alias.Dispose();
2758}
2759
2760
2761THREADED_TEST(MultiRun) {
2762 v8::HandleScope scope;
2763 LocalContext context;
2764 Local<Script> script = Script::Compile(v8_str("x"));
2765 for (int i = 0; i < 10; i++)
2766 script->Run();
2767}
2768
2769
2770static v8::Handle<Value> GetXValue(Local<String> name,
2771 const AccessorInfo& info) {
2772 ApiTestFuzzer::Fuzz();
2773 CHECK_EQ(info.Data(), v8_str("donut"));
2774 CHECK_EQ(name, v8_str("x"));
2775 return name;
2776}
2777
2778
2779THREADED_TEST(SimplePropertyRead) {
2780 v8::HandleScope scope;
2781 Local<ObjectTemplate> templ = ObjectTemplate::New();
2782 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2783 LocalContext context;
2784 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2785 Local<Script> script = Script::Compile(v8_str("obj.x"));
2786 for (int i = 0; i < 10; i++) {
2787 Local<Value> result = script->Run();
2788 CHECK_EQ(result, v8_str("x"));
2789 }
2790}
2791
Andrei Popescu31002712010-02-23 13:46:05 +00002792THREADED_TEST(DefinePropertyOnAPIAccessor) {
2793 v8::HandleScope scope;
2794 Local<ObjectTemplate> templ = ObjectTemplate::New();
2795 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2796 LocalContext context;
2797 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2798
2799 // Uses getOwnPropertyDescriptor to check the configurable status
2800 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002801 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002802 "obj, 'x');"
2803 "prop.configurable;"));
2804 Local<Value> result = script_desc->Run();
2805 CHECK_EQ(result->BooleanValue(), true);
2806
2807 // Redefine get - but still configurable
2808 Local<Script> script_define
2809 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2810 " configurable: true };"
2811 "Object.defineProperty(obj, 'x', desc);"
2812 "obj.x"));
2813 result = script_define->Run();
2814 CHECK_EQ(result, v8_num(42));
2815
2816 // Check that the accessor is still configurable
2817 result = script_desc->Run();
2818 CHECK_EQ(result->BooleanValue(), true);
2819
2820 // Redefine to a non-configurable
2821 script_define
2822 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2823 " configurable: false };"
2824 "Object.defineProperty(obj, 'x', desc);"
2825 "obj.x"));
2826 result = script_define->Run();
2827 CHECK_EQ(result, v8_num(43));
2828 result = script_desc->Run();
2829 CHECK_EQ(result->BooleanValue(), false);
2830
2831 // Make sure that it is not possible to redefine again
2832 v8::TryCatch try_catch;
2833 result = script_define->Run();
2834 CHECK(try_catch.HasCaught());
2835 String::AsciiValue exception_value(try_catch.Exception());
2836 CHECK_EQ(*exception_value,
2837 "TypeError: Cannot redefine property: defineProperty");
2838}
2839
2840THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2841 v8::HandleScope scope;
2842 Local<ObjectTemplate> templ = ObjectTemplate::New();
2843 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2844 LocalContext context;
2845 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2846
2847 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2848 "Object.getOwnPropertyDescriptor( "
2849 "obj, 'x');"
2850 "prop.configurable;"));
2851 Local<Value> result = script_desc->Run();
2852 CHECK_EQ(result->BooleanValue(), true);
2853
2854 Local<Script> script_define =
2855 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2856 " configurable: true };"
2857 "Object.defineProperty(obj, 'x', desc);"
2858 "obj.x"));
2859 result = script_define->Run();
2860 CHECK_EQ(result, v8_num(42));
2861
2862
2863 result = script_desc->Run();
2864 CHECK_EQ(result->BooleanValue(), true);
2865
2866
2867 script_define =
2868 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2869 " configurable: false };"
2870 "Object.defineProperty(obj, 'x', desc);"
2871 "obj.x"));
2872 result = script_define->Run();
2873 CHECK_EQ(result, v8_num(43));
2874 result = script_desc->Run();
2875
2876 CHECK_EQ(result->BooleanValue(), false);
2877
2878 v8::TryCatch try_catch;
2879 result = script_define->Run();
2880 CHECK(try_catch.HasCaught());
2881 String::AsciiValue exception_value(try_catch.Exception());
2882 CHECK_EQ(*exception_value,
2883 "TypeError: Cannot redefine property: defineProperty");
2884}
2885
2886
Leon Clarkef7060e22010-06-03 12:02:55 +01002887static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2888 char const* name) {
2889 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2890}
Andrei Popescu31002712010-02-23 13:46:05 +00002891
2892
Leon Clarkef7060e22010-06-03 12:02:55 +01002893THREADED_TEST(DefineAPIAccessorOnObject) {
2894 v8::HandleScope scope;
2895 Local<ObjectTemplate> templ = ObjectTemplate::New();
2896 LocalContext context;
2897
2898 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2899 CompileRun("var obj2 = {};");
2900
2901 CHECK(CompileRun("obj1.x")->IsUndefined());
2902 CHECK(CompileRun("obj2.x")->IsUndefined());
2903
2904 CHECK(GetGlobalProperty(&context, "obj1")->
2905 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2906
2907 ExpectString("obj1.x", "x");
2908 CHECK(CompileRun("obj2.x")->IsUndefined());
2909
2910 CHECK(GetGlobalProperty(&context, "obj2")->
2911 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2912
2913 ExpectString("obj1.x", "x");
2914 ExpectString("obj2.x", "x");
2915
2916 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2917 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2918
2919 CompileRun("Object.defineProperty(obj1, 'x',"
2920 "{ get: function() { return 'y'; }, configurable: true })");
2921
2922 ExpectString("obj1.x", "y");
2923 ExpectString("obj2.x", "x");
2924
2925 CompileRun("Object.defineProperty(obj2, 'x',"
2926 "{ get: function() { return 'y'; }, configurable: true })");
2927
2928 ExpectString("obj1.x", "y");
2929 ExpectString("obj2.x", "y");
2930
2931 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2932 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2933
2934 CHECK(GetGlobalProperty(&context, "obj1")->
2935 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2936 CHECK(GetGlobalProperty(&context, "obj2")->
2937 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2938
2939 ExpectString("obj1.x", "x");
2940 ExpectString("obj2.x", "x");
2941
2942 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2943 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2944
2945 // Define getters/setters, but now make them not configurable.
2946 CompileRun("Object.defineProperty(obj1, 'x',"
2947 "{ get: function() { return 'z'; }, configurable: false })");
2948 CompileRun("Object.defineProperty(obj2, 'x',"
2949 "{ get: function() { return 'z'; }, configurable: false })");
2950
2951 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2952 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2953
2954 ExpectString("obj1.x", "z");
2955 ExpectString("obj2.x", "z");
2956
2957 CHECK(!GetGlobalProperty(&context, "obj1")->
2958 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2959 CHECK(!GetGlobalProperty(&context, "obj2")->
2960 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2961
2962 ExpectString("obj1.x", "z");
2963 ExpectString("obj2.x", "z");
2964}
2965
2966
2967THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2968 v8::HandleScope scope;
2969 Local<ObjectTemplate> templ = ObjectTemplate::New();
2970 LocalContext context;
2971
2972 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2973 CompileRun("var obj2 = {};");
2974
2975 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2976 v8_str("x"),
2977 GetXValue, NULL,
2978 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2979 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2980 v8_str("x"),
2981 GetXValue, NULL,
2982 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2983
2984 ExpectString("obj1.x", "x");
2985 ExpectString("obj2.x", "x");
2986
2987 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2988 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2989
2990 CHECK(!GetGlobalProperty(&context, "obj1")->
2991 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2992 CHECK(!GetGlobalProperty(&context, "obj2")->
2993 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2994
2995 {
2996 v8::TryCatch try_catch;
2997 CompileRun("Object.defineProperty(obj1, 'x',"
2998 "{get: function() { return 'func'; }})");
2999 CHECK(try_catch.HasCaught());
3000 String::AsciiValue exception_value(try_catch.Exception());
3001 CHECK_EQ(*exception_value,
3002 "TypeError: Cannot redefine property: defineProperty");
3003 }
3004 {
3005 v8::TryCatch try_catch;
3006 CompileRun("Object.defineProperty(obj2, 'x',"
3007 "{get: function() { return 'func'; }})");
3008 CHECK(try_catch.HasCaught());
3009 String::AsciiValue exception_value(try_catch.Exception());
3010 CHECK_EQ(*exception_value,
3011 "TypeError: Cannot redefine property: defineProperty");
3012 }
3013}
3014
3015
3016static v8::Handle<Value> Get239Value(Local<String> name,
3017 const AccessorInfo& info) {
3018 ApiTestFuzzer::Fuzz();
3019 CHECK_EQ(info.Data(), v8_str("donut"));
3020 CHECK_EQ(name, v8_str("239"));
3021 return name;
3022}
3023
3024
3025THREADED_TEST(ElementAPIAccessor) {
3026 v8::HandleScope scope;
3027 Local<ObjectTemplate> templ = ObjectTemplate::New();
3028 LocalContext context;
3029
3030 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
3031 CompileRun("var obj2 = {};");
3032
3033 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
3034 v8_str("239"),
3035 Get239Value, NULL,
3036 v8_str("donut")));
3037 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
3038 v8_str("239"),
3039 Get239Value, NULL,
3040 v8_str("donut")));
3041
3042 ExpectString("obj1[239]", "239");
3043 ExpectString("obj2[239]", "239");
3044 ExpectString("obj1['239']", "239");
3045 ExpectString("obj2['239']", "239");
3046}
3047
Steve Blocka7e24c12009-10-30 11:49:00 +00003048
3049v8::Persistent<Value> xValue;
3050
3051
3052static void SetXValue(Local<String> name,
3053 Local<Value> value,
3054 const AccessorInfo& info) {
3055 CHECK_EQ(value, v8_num(4));
3056 CHECK_EQ(info.Data(), v8_str("donut"));
3057 CHECK_EQ(name, v8_str("x"));
3058 CHECK(xValue.IsEmpty());
3059 xValue = v8::Persistent<Value>::New(value);
3060}
3061
3062
3063THREADED_TEST(SimplePropertyWrite) {
3064 v8::HandleScope scope;
3065 Local<ObjectTemplate> templ = ObjectTemplate::New();
3066 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
3067 LocalContext context;
3068 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3069 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
3070 for (int i = 0; i < 10; i++) {
3071 CHECK(xValue.IsEmpty());
3072 script->Run();
3073 CHECK_EQ(v8_num(4), xValue);
3074 xValue.Dispose();
3075 xValue = v8::Persistent<Value>();
3076 }
3077}
3078
3079
3080static v8::Handle<Value> XPropertyGetter(Local<String> property,
3081 const AccessorInfo& info) {
3082 ApiTestFuzzer::Fuzz();
3083 CHECK(info.Data()->IsUndefined());
3084 return property;
3085}
3086
3087
3088THREADED_TEST(NamedInterceptorPropertyRead) {
3089 v8::HandleScope scope;
3090 Local<ObjectTemplate> templ = ObjectTemplate::New();
3091 templ->SetNamedPropertyHandler(XPropertyGetter);
3092 LocalContext context;
3093 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3094 Local<Script> script = Script::Compile(v8_str("obj.x"));
3095 for (int i = 0; i < 10; i++) {
3096 Local<Value> result = script->Run();
3097 CHECK_EQ(result, v8_str("x"));
3098 }
3099}
3100
3101
Steve Block6ded16b2010-05-10 14:33:55 +01003102THREADED_TEST(NamedInterceptorDictionaryIC) {
3103 v8::HandleScope scope;
3104 Local<ObjectTemplate> templ = ObjectTemplate::New();
3105 templ->SetNamedPropertyHandler(XPropertyGetter);
3106 LocalContext context;
3107 // Create an object with a named interceptor.
3108 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
3109 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
3110 for (int i = 0; i < 10; i++) {
3111 Local<Value> result = script->Run();
3112 CHECK_EQ(result, v8_str("x"));
3113 }
3114 // Create a slow case object and a function accessing a property in
3115 // that slow case object (with dictionary probing in generated
3116 // code). Then force object with a named interceptor into slow-case,
3117 // pass it to the function, and check that the interceptor is called
3118 // instead of accessing the local property.
3119 Local<Value> result =
3120 CompileRun("function get_x(o) { return o.x; };"
3121 "var obj = { x : 42, y : 0 };"
3122 "delete obj.y;"
3123 "for (var i = 0; i < 10; i++) get_x(obj);"
3124 "interceptor_obj.x = 42;"
3125 "interceptor_obj.y = 10;"
3126 "delete interceptor_obj.y;"
3127 "get_x(interceptor_obj)");
3128 CHECK_EQ(result, v8_str("x"));
3129}
3130
3131
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003132THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
3133 v8::HandleScope scope;
3134
3135 v8::Persistent<Context> context1 = Context::New();
3136
3137 context1->Enter();
3138 Local<ObjectTemplate> templ = ObjectTemplate::New();
3139 templ->SetNamedPropertyHandler(XPropertyGetter);
3140 // Create an object with a named interceptor.
3141 v8::Local<v8::Object> object = templ->NewInstance();
3142 context1->Global()->Set(v8_str("interceptor_obj"), object);
3143
3144 // Force the object into the slow case.
3145 CompileRun("interceptor_obj.y = 0;"
3146 "delete interceptor_obj.y;");
3147 context1->Exit();
3148
3149 {
3150 // Introduce the object into a different context.
3151 // Repeat named loads to exercise ICs.
3152 LocalContext context2;
3153 context2->Global()->Set(v8_str("interceptor_obj"), object);
3154 Local<Value> result =
3155 CompileRun("function get_x(o) { return o.x; }"
3156 "interceptor_obj.x = 42;"
3157 "for (var i=0; i != 10; i++) {"
3158 " get_x(interceptor_obj);"
3159 "}"
3160 "get_x(interceptor_obj)");
3161 // Check that the interceptor was actually invoked.
3162 CHECK_EQ(result, v8_str("x"));
3163 }
3164
3165 // Return to the original context and force some object to the slow case
3166 // to cause the NormalizedMapCache to verify.
3167 context1->Enter();
3168 CompileRun("var obj = { x : 0 }; delete obj.x;");
3169 context1->Exit();
3170
3171 context1.Dispose();
3172}
3173
3174
Andrei Popescu402d9372010-02-26 13:31:12 +00003175static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
3176 const AccessorInfo& info) {
3177 // Set x on the prototype object and do not handle the get request.
3178 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01003179 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00003180 return v8::Handle<Value>();
3181}
3182
3183
3184// This is a regression test for http://crbug.com/20104. Map
3185// transitions should not interfere with post interceptor lookup.
3186THREADED_TEST(NamedInterceptorMapTransitionRead) {
3187 v8::HandleScope scope;
3188 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
3189 Local<v8::ObjectTemplate> instance_template
3190 = function_template->InstanceTemplate();
3191 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
3192 LocalContext context;
3193 context->Global()->Set(v8_str("F"), function_template->GetFunction());
3194 // Create an instance of F and introduce a map transition for x.
3195 CompileRun("var o = new F(); o.x = 23;");
3196 // Create an instance of F and invoke the getter. The result should be 23.
3197 Local<Value> result = CompileRun("o = new F(); o.x");
3198 CHECK_EQ(result->Int32Value(), 23);
3199}
3200
3201
Steve Blocka7e24c12009-10-30 11:49:00 +00003202static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
3203 const AccessorInfo& info) {
3204 ApiTestFuzzer::Fuzz();
3205 if (index == 37) {
3206 return v8::Handle<Value>(v8_num(625));
3207 }
3208 return v8::Handle<Value>();
3209}
3210
3211
3212static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
3213 Local<Value> value,
3214 const AccessorInfo& info) {
3215 ApiTestFuzzer::Fuzz();
3216 if (index == 39) {
3217 return value;
3218 }
3219 return v8::Handle<Value>();
3220}
3221
3222
3223THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
3224 v8::HandleScope scope;
3225 Local<ObjectTemplate> templ = ObjectTemplate::New();
3226 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
3227 IndexedPropertySetter);
3228 LocalContext context;
3229 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3230 Local<Script> getter_script = Script::Compile(v8_str(
3231 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
3232 Local<Script> setter_script = Script::Compile(v8_str(
3233 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
3234 "obj[17] = 23;"
3235 "obj.foo;"));
3236 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3237 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3238 "obj[39] = 47;"
3239 "obj.foo;")); // This setter should not run, due to the interceptor.
3240 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3241 "obj[37];"));
3242 Local<Value> result = getter_script->Run();
3243 CHECK_EQ(v8_num(5), result);
3244 result = setter_script->Run();
3245 CHECK_EQ(v8_num(23), result);
3246 result = interceptor_setter_script->Run();
3247 CHECK_EQ(v8_num(23), result);
3248 result = interceptor_getter_script->Run();
3249 CHECK_EQ(v8_num(625), result);
3250}
3251
3252
Leon Clarked91b9f72010-01-27 17:25:45 +00003253static v8::Handle<Value> IdentityIndexedPropertyGetter(
3254 uint32_t index,
3255 const AccessorInfo& info) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003256 return v8::Integer::NewFromUnsigned(index);
Leon Clarked91b9f72010-01-27 17:25:45 +00003257}
3258
3259
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003260THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3261 v8::HandleScope scope;
3262 Local<ObjectTemplate> templ = ObjectTemplate::New();
3263 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3264
3265 LocalContext context;
3266 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3267
3268 // Check fast object case.
3269 const char* fast_case_code =
3270 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3271 ExpectString(fast_case_code, "0");
3272
3273 // Check slow case.
3274 const char* slow_case_code =
3275 "obj.x = 1; delete obj.x;"
3276 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3277 ExpectString(slow_case_code, "1");
3278}
3279
3280
Leon Clarked91b9f72010-01-27 17:25:45 +00003281THREADED_TEST(IndexedInterceptorWithNoSetter) {
3282 v8::HandleScope scope;
3283 Local<ObjectTemplate> templ = ObjectTemplate::New();
3284 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3285
3286 LocalContext context;
3287 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3288
3289 const char* code =
3290 "try {"
3291 " obj[0] = 239;"
3292 " for (var i = 0; i < 100; i++) {"
3293 " var v = obj[0];"
3294 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3295 " }"
3296 " 'PASSED'"
3297 "} catch(e) {"
3298 " e"
3299 "}";
3300 ExpectString(code, "PASSED");
3301}
3302
3303
Andrei Popescu402d9372010-02-26 13:31:12 +00003304THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3305 v8::HandleScope scope;
3306 Local<ObjectTemplate> templ = ObjectTemplate::New();
3307 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3308
3309 LocalContext context;
3310 Local<v8::Object> obj = templ->NewInstance();
3311 obj->TurnOnAccessCheck();
3312 context->Global()->Set(v8_str("obj"), obj);
3313
3314 const char* code =
3315 "try {"
3316 " for (var i = 0; i < 100; i++) {"
3317 " var v = obj[0];"
3318 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3319 " }"
3320 " 'PASSED'"
3321 "} catch(e) {"
3322 " e"
3323 "}";
3324 ExpectString(code, "PASSED");
3325}
3326
3327
3328THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3329 i::FLAG_allow_natives_syntax = true;
3330 v8::HandleScope scope;
3331 Local<ObjectTemplate> templ = ObjectTemplate::New();
3332 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3333
3334 LocalContext context;
3335 Local<v8::Object> obj = templ->NewInstance();
3336 context->Global()->Set(v8_str("obj"), obj);
3337
3338 const char* code =
3339 "try {"
3340 " for (var i = 0; i < 100; i++) {"
3341 " var expected = i;"
3342 " if (i == 5) {"
3343 " %EnableAccessChecks(obj);"
3344 " expected = undefined;"
3345 " }"
3346 " var v = obj[i];"
3347 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3348 " if (i == 5) %DisableAccessChecks(obj);"
3349 " }"
3350 " 'PASSED'"
3351 "} catch(e) {"
3352 " e"
3353 "}";
3354 ExpectString(code, "PASSED");
3355}
3356
3357
3358THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3359 v8::HandleScope scope;
3360 Local<ObjectTemplate> templ = ObjectTemplate::New();
3361 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3362
3363 LocalContext context;
3364 Local<v8::Object> obj = templ->NewInstance();
3365 context->Global()->Set(v8_str("obj"), obj);
3366
3367 const char* code =
3368 "try {"
3369 " for (var i = 0; i < 100; i++) {"
3370 " var v = obj[i];"
3371 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3372 " }"
3373 " 'PASSED'"
3374 "} catch(e) {"
3375 " e"
3376 "}";
3377 ExpectString(code, "PASSED");
3378}
3379
3380
Ben Murdochf87a2032010-10-22 12:50:53 +01003381THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
3382 v8::HandleScope scope;
3383 Local<ObjectTemplate> templ = ObjectTemplate::New();
3384 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3385
3386 LocalContext context;
3387 Local<v8::Object> obj = templ->NewInstance();
3388 context->Global()->Set(v8_str("obj"), obj);
3389
3390 const char* code =
3391 "try {"
3392 " for (var i = 0; i < 100; i++) {"
3393 " var expected = i;"
3394 " var key = i;"
3395 " if (i == 25) {"
3396 " key = -1;"
3397 " expected = undefined;"
3398 " }"
3399 " if (i == 50) {"
3400 " /* probe minimal Smi number on 32-bit platforms */"
3401 " key = -(1 << 30);"
3402 " expected = undefined;"
3403 " }"
3404 " if (i == 75) {"
3405 " /* probe minimal Smi number on 64-bit platforms */"
3406 " key = 1 << 31;"
3407 " expected = undefined;"
3408 " }"
3409 " var v = obj[key];"
3410 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3411 " }"
3412 " 'PASSED'"
3413 "} catch(e) {"
3414 " e"
3415 "}";
3416 ExpectString(code, "PASSED");
3417}
3418
3419
Andrei Popescu402d9372010-02-26 13:31:12 +00003420THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3421 v8::HandleScope scope;
3422 Local<ObjectTemplate> templ = ObjectTemplate::New();
3423 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3424
3425 LocalContext context;
3426 Local<v8::Object> obj = templ->NewInstance();
3427 context->Global()->Set(v8_str("obj"), obj);
3428
3429 const char* code =
3430 "try {"
3431 " for (var i = 0; i < 100; i++) {"
3432 " var expected = i;"
Ben Murdochf87a2032010-10-22 12:50:53 +01003433 " var key = i;"
Andrei Popescu402d9372010-02-26 13:31:12 +00003434 " if (i == 50) {"
Ben Murdochf87a2032010-10-22 12:50:53 +01003435 " key = 'foobar';"
Andrei Popescu402d9372010-02-26 13:31:12 +00003436 " expected = undefined;"
3437 " }"
Ben Murdochf87a2032010-10-22 12:50:53 +01003438 " var v = obj[key];"
Andrei Popescu402d9372010-02-26 13:31:12 +00003439 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3440 " }"
3441 " 'PASSED'"
3442 "} catch(e) {"
3443 " e"
3444 "}";
3445 ExpectString(code, "PASSED");
3446}
3447
3448
3449THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3450 v8::HandleScope scope;
3451 Local<ObjectTemplate> templ = ObjectTemplate::New();
3452 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3453
3454 LocalContext context;
3455 Local<v8::Object> obj = templ->NewInstance();
3456 context->Global()->Set(v8_str("obj"), obj);
3457
3458 const char* code =
3459 "var original = obj;"
3460 "try {"
3461 " for (var i = 0; i < 100; i++) {"
3462 " var expected = i;"
3463 " if (i == 50) {"
3464 " obj = {50: 'foobar'};"
3465 " expected = 'foobar';"
3466 " }"
3467 " var v = obj[i];"
3468 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3469 " if (i == 50) obj = original;"
3470 " }"
3471 " 'PASSED'"
3472 "} catch(e) {"
3473 " e"
3474 "}";
3475 ExpectString(code, "PASSED");
3476}
3477
3478
3479THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3480 v8::HandleScope scope;
3481 Local<ObjectTemplate> templ = ObjectTemplate::New();
3482 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3483
3484 LocalContext context;
3485 Local<v8::Object> obj = templ->NewInstance();
3486 context->Global()->Set(v8_str("obj"), obj);
3487
3488 const char* code =
3489 "var original = obj;"
3490 "try {"
3491 " for (var i = 0; i < 100; i++) {"
3492 " var expected = i;"
3493 " if (i == 5) {"
3494 " obj = 239;"
3495 " expected = undefined;"
3496 " }"
3497 " var v = obj[i];"
3498 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3499 " if (i == 5) obj = original;"
3500 " }"
3501 " 'PASSED'"
3502 "} catch(e) {"
3503 " e"
3504 "}";
3505 ExpectString(code, "PASSED");
3506}
3507
3508
3509THREADED_TEST(IndexedInterceptorOnProto) {
3510 v8::HandleScope scope;
3511 Local<ObjectTemplate> templ = ObjectTemplate::New();
3512 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3513
3514 LocalContext context;
3515 Local<v8::Object> obj = templ->NewInstance();
3516 context->Global()->Set(v8_str("obj"), obj);
3517
3518 const char* code =
3519 "var o = {__proto__: obj};"
3520 "try {"
3521 " for (var i = 0; i < 100; i++) {"
3522 " var v = o[i];"
3523 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3524 " }"
3525 " 'PASSED'"
3526 "} catch(e) {"
3527 " e"
3528 "}";
3529 ExpectString(code, "PASSED");
3530}
3531
3532
Steve Blocka7e24c12009-10-30 11:49:00 +00003533THREADED_TEST(MultiContexts) {
3534 v8::HandleScope scope;
3535 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3536 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3537
3538 Local<String> password = v8_str("Password");
3539
3540 // Create an environment
3541 LocalContext context0(0, templ);
3542 context0->SetSecurityToken(password);
3543 v8::Handle<v8::Object> global0 = context0->Global();
3544 global0->Set(v8_str("custom"), v8_num(1234));
3545 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3546
3547 // Create an independent environment
3548 LocalContext context1(0, templ);
3549 context1->SetSecurityToken(password);
3550 v8::Handle<v8::Object> global1 = context1->Global();
3551 global1->Set(v8_str("custom"), v8_num(1234));
3552 CHECK_NE(global0, global1);
3553 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3554 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3555
3556 // Now create a new context with the old global
3557 LocalContext context2(0, templ, global1);
3558 context2->SetSecurityToken(password);
3559 v8::Handle<v8::Object> global2 = context2->Global();
3560 CHECK_EQ(global1, global2);
3561 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3562 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3563}
3564
3565
3566THREADED_TEST(FunctionPrototypeAcrossContexts) {
3567 // Make sure that functions created by cloning boilerplates cannot
3568 // communicate through their __proto__ field.
3569
3570 v8::HandleScope scope;
3571
3572 LocalContext env0;
3573 v8::Handle<v8::Object> global0 =
3574 env0->Global();
3575 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003576 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003577 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003578 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003579 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003580 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003581 proto0->Set(v8_str("custom"), v8_num(1234));
3582
3583 LocalContext env1;
3584 v8::Handle<v8::Object> global1 =
3585 env1->Global();
3586 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003587 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003588 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003589 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003590 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003591 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003592 CHECK(!proto1->Has(v8_str("custom")));
3593}
3594
3595
3596THREADED_TEST(Regress892105) {
3597 // Make sure that object and array literals created by cloning
3598 // boilerplates cannot communicate through their __proto__
3599 // field. This is rather difficult to check, but we try to add stuff
3600 // to Object.prototype and Array.prototype and create a new
3601 // environment. This should succeed.
3602
3603 v8::HandleScope scope;
3604
3605 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3606 "Array.prototype.arr = 4567;"
3607 "8901");
3608
3609 LocalContext env0;
3610 Local<Script> script0 = Script::Compile(source);
3611 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3612
3613 LocalContext env1;
3614 Local<Script> script1 = Script::Compile(source);
3615 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3616}
3617
3618
Steve Blocka7e24c12009-10-30 11:49:00 +00003619THREADED_TEST(UndetectableObject) {
3620 v8::HandleScope scope;
3621 LocalContext env;
3622
3623 Local<v8::FunctionTemplate> desc =
3624 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3625 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3626
3627 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3628 env->Global()->Set(v8_str("undetectable"), obj);
3629
3630 ExpectString("undetectable.toString()", "[object Object]");
3631 ExpectString("typeof undetectable", "undefined");
3632 ExpectString("typeof(undetectable)", "undefined");
3633 ExpectBoolean("typeof undetectable == 'undefined'", true);
3634 ExpectBoolean("typeof undetectable == 'object'", false);
3635 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3636 ExpectBoolean("!undetectable", true);
3637
3638 ExpectObject("true&&undetectable", obj);
3639 ExpectBoolean("false&&undetectable", false);
3640 ExpectBoolean("true||undetectable", true);
3641 ExpectObject("false||undetectable", obj);
3642
3643 ExpectObject("undetectable&&true", obj);
3644 ExpectObject("undetectable&&false", obj);
3645 ExpectBoolean("undetectable||true", true);
3646 ExpectBoolean("undetectable||false", false);
3647
3648 ExpectBoolean("undetectable==null", true);
3649 ExpectBoolean("null==undetectable", true);
3650 ExpectBoolean("undetectable==undefined", true);
3651 ExpectBoolean("undefined==undetectable", true);
3652 ExpectBoolean("undetectable==undetectable", true);
3653
3654
3655 ExpectBoolean("undetectable===null", false);
3656 ExpectBoolean("null===undetectable", false);
3657 ExpectBoolean("undetectable===undefined", false);
3658 ExpectBoolean("undefined===undetectable", false);
3659 ExpectBoolean("undetectable===undetectable", true);
3660}
3661
3662
Steve Block8defd9f2010-07-08 12:39:36 +01003663
3664THREADED_TEST(ExtensibleOnUndetectable) {
3665 v8::HandleScope scope;
3666 LocalContext env;
3667
3668 Local<v8::FunctionTemplate> desc =
3669 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3670 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3671
3672 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3673 env->Global()->Set(v8_str("undetectable"), obj);
3674
3675 Local<String> source = v8_str("undetectable.x = 42;"
3676 "undetectable.x");
3677
3678 Local<Script> script = Script::Compile(source);
3679
3680 CHECK_EQ(v8::Integer::New(42), script->Run());
3681
3682 ExpectBoolean("Object.isExtensible(undetectable)", true);
3683
3684 source = v8_str("Object.preventExtensions(undetectable);");
3685 script = Script::Compile(source);
3686 script->Run();
3687 ExpectBoolean("Object.isExtensible(undetectable)", false);
3688
3689 source = v8_str("undetectable.y = 2000;");
3690 script = Script::Compile(source);
3691 v8::TryCatch try_catch;
3692 Local<Value> result = script->Run();
3693 CHECK(result.IsEmpty());
3694 CHECK(try_catch.HasCaught());
3695}
3696
3697
3698
Steve Blocka7e24c12009-10-30 11:49:00 +00003699THREADED_TEST(UndetectableString) {
3700 v8::HandleScope scope;
3701 LocalContext env;
3702
3703 Local<String> obj = String::NewUndetectable("foo");
3704 env->Global()->Set(v8_str("undetectable"), obj);
3705
3706 ExpectString("undetectable", "foo");
3707 ExpectString("typeof undetectable", "undefined");
3708 ExpectString("typeof(undetectable)", "undefined");
3709 ExpectBoolean("typeof undetectable == 'undefined'", true);
3710 ExpectBoolean("typeof undetectable == 'string'", false);
3711 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3712 ExpectBoolean("!undetectable", true);
3713
3714 ExpectObject("true&&undetectable", obj);
3715 ExpectBoolean("false&&undetectable", false);
3716 ExpectBoolean("true||undetectable", true);
3717 ExpectObject("false||undetectable", obj);
3718
3719 ExpectObject("undetectable&&true", obj);
3720 ExpectObject("undetectable&&false", obj);
3721 ExpectBoolean("undetectable||true", true);
3722 ExpectBoolean("undetectable||false", false);
3723
3724 ExpectBoolean("undetectable==null", true);
3725 ExpectBoolean("null==undetectable", true);
3726 ExpectBoolean("undetectable==undefined", true);
3727 ExpectBoolean("undefined==undetectable", true);
3728 ExpectBoolean("undetectable==undetectable", true);
3729
3730
3731 ExpectBoolean("undetectable===null", false);
3732 ExpectBoolean("null===undetectable", false);
3733 ExpectBoolean("undetectable===undefined", false);
3734 ExpectBoolean("undefined===undetectable", false);
3735 ExpectBoolean("undetectable===undetectable", true);
3736}
3737
3738
3739template <typename T> static void USE(T) { }
3740
3741
3742// This test is not intended to be run, just type checked.
3743static void PersistentHandles() {
3744 USE(PersistentHandles);
3745 Local<String> str = v8_str("foo");
3746 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3747 USE(p_str);
3748 Local<Script> scr = Script::Compile(v8_str(""));
3749 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3750 USE(p_scr);
3751 Local<ObjectTemplate> templ = ObjectTemplate::New();
3752 v8::Persistent<ObjectTemplate> p_templ =
3753 v8::Persistent<ObjectTemplate>::New(templ);
3754 USE(p_templ);
3755}
3756
3757
3758static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3759 ApiTestFuzzer::Fuzz();
3760 return v8::Undefined();
3761}
3762
3763
3764THREADED_TEST(GlobalObjectTemplate) {
3765 v8::HandleScope handle_scope;
3766 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3767 global_template->Set(v8_str("JSNI_Log"),
3768 v8::FunctionTemplate::New(HandleLogDelegator));
3769 v8::Persistent<Context> context = Context::New(0, global_template);
3770 Context::Scope context_scope(context);
3771 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3772 context.Dispose();
3773}
3774
3775
3776static const char* kSimpleExtensionSource =
3777 "function Foo() {"
3778 " return 4;"
3779 "}";
3780
3781
3782THREADED_TEST(SimpleExtensions) {
3783 v8::HandleScope handle_scope;
3784 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3785 const char* extension_names[] = { "simpletest" };
3786 v8::ExtensionConfiguration extensions(1, extension_names);
3787 v8::Handle<Context> context = Context::New(&extensions);
3788 Context::Scope lock(context);
3789 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3790 CHECK_EQ(result, v8::Integer::New(4));
3791}
3792
3793
3794static const char* kEvalExtensionSource1 =
3795 "function UseEval1() {"
3796 " var x = 42;"
3797 " return eval('x');"
3798 "}";
3799
3800
3801static const char* kEvalExtensionSource2 =
3802 "(function() {"
3803 " var x = 42;"
3804 " function e() {"
3805 " return eval('x');"
3806 " }"
3807 " this.UseEval2 = e;"
3808 "})()";
3809
3810
3811THREADED_TEST(UseEvalFromExtension) {
3812 v8::HandleScope handle_scope;
3813 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3814 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3815 const char* extension_names[] = { "evaltest1", "evaltest2" };
3816 v8::ExtensionConfiguration extensions(2, extension_names);
3817 v8::Handle<Context> context = Context::New(&extensions);
3818 Context::Scope lock(context);
3819 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3820 CHECK_EQ(result, v8::Integer::New(42));
3821 result = Script::Compile(v8_str("UseEval2()"))->Run();
3822 CHECK_EQ(result, v8::Integer::New(42));
3823}
3824
3825
3826static const char* kWithExtensionSource1 =
3827 "function UseWith1() {"
3828 " var x = 42;"
3829 " with({x:87}) { return x; }"
3830 "}";
3831
3832
3833
3834static const char* kWithExtensionSource2 =
3835 "(function() {"
3836 " var x = 42;"
3837 " function e() {"
3838 " with ({x:87}) { return x; }"
3839 " }"
3840 " this.UseWith2 = e;"
3841 "})()";
3842
3843
3844THREADED_TEST(UseWithFromExtension) {
3845 v8::HandleScope handle_scope;
3846 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3847 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3848 const char* extension_names[] = { "withtest1", "withtest2" };
3849 v8::ExtensionConfiguration extensions(2, extension_names);
3850 v8::Handle<Context> context = Context::New(&extensions);
3851 Context::Scope lock(context);
3852 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3853 CHECK_EQ(result, v8::Integer::New(87));
3854 result = Script::Compile(v8_str("UseWith2()"))->Run();
3855 CHECK_EQ(result, v8::Integer::New(87));
3856}
3857
3858
3859THREADED_TEST(AutoExtensions) {
3860 v8::HandleScope handle_scope;
3861 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3862 extension->set_auto_enable(true);
3863 v8::RegisterExtension(extension);
3864 v8::Handle<Context> context = Context::New();
3865 Context::Scope lock(context);
3866 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3867 CHECK_EQ(result, v8::Integer::New(4));
3868}
3869
3870
Steve Blockd0582a62009-12-15 09:54:21 +00003871static const char* kSyntaxErrorInExtensionSource =
3872 "[";
3873
3874
3875// Test that a syntax error in an extension does not cause a fatal
3876// error but results in an empty context.
3877THREADED_TEST(SyntaxErrorExtensions) {
3878 v8::HandleScope handle_scope;
3879 v8::RegisterExtension(new Extension("syntaxerror",
3880 kSyntaxErrorInExtensionSource));
3881 const char* extension_names[] = { "syntaxerror" };
3882 v8::ExtensionConfiguration extensions(1, extension_names);
3883 v8::Handle<Context> context = Context::New(&extensions);
3884 CHECK(context.IsEmpty());
3885}
3886
3887
3888static const char* kExceptionInExtensionSource =
3889 "throw 42";
3890
3891
3892// Test that an exception when installing an extension does not cause
3893// a fatal error but results in an empty context.
3894THREADED_TEST(ExceptionExtensions) {
3895 v8::HandleScope handle_scope;
3896 v8::RegisterExtension(new Extension("exception",
3897 kExceptionInExtensionSource));
3898 const char* extension_names[] = { "exception" };
3899 v8::ExtensionConfiguration extensions(1, extension_names);
3900 v8::Handle<Context> context = Context::New(&extensions);
3901 CHECK(context.IsEmpty());
3902}
3903
3904
Iain Merrick9ac36c92010-09-13 15:29:50 +01003905static const char* kNativeCallInExtensionSource =
3906 "function call_runtime_last_index_of(x) {"
3907 " return %StringLastIndexOf(x, 'bob', 10);"
3908 "}";
3909
3910
3911static const char* kNativeCallTest =
3912 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
3913
3914// Test that a native runtime calls are supported in extensions.
3915THREADED_TEST(NativeCallInExtensions) {
3916 v8::HandleScope handle_scope;
3917 v8::RegisterExtension(new Extension("nativecall",
3918 kNativeCallInExtensionSource));
3919 const char* extension_names[] = { "nativecall" };
3920 v8::ExtensionConfiguration extensions(1, extension_names);
3921 v8::Handle<Context> context = Context::New(&extensions);
3922 Context::Scope lock(context);
3923 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
3924 CHECK_EQ(result, v8::Integer::New(3));
3925}
3926
3927
Steve Blocka7e24c12009-10-30 11:49:00 +00003928static void CheckDependencies(const char* name, const char* expected) {
3929 v8::HandleScope handle_scope;
3930 v8::ExtensionConfiguration config(1, &name);
3931 LocalContext context(&config);
3932 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3933}
3934
3935
3936/*
3937 * Configuration:
3938 *
3939 * /-- B <--\
3940 * A <- -- D <-- E
3941 * \-- C <--/
3942 */
3943THREADED_TEST(ExtensionDependency) {
3944 static const char* kEDeps[] = { "D" };
3945 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3946 static const char* kDDeps[] = { "B", "C" };
3947 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3948 static const char* kBCDeps[] = { "A" };
3949 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3950 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3951 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3952 CheckDependencies("A", "undefinedA");
3953 CheckDependencies("B", "undefinedAB");
3954 CheckDependencies("C", "undefinedAC");
3955 CheckDependencies("D", "undefinedABCD");
3956 CheckDependencies("E", "undefinedABCDE");
3957 v8::HandleScope handle_scope;
3958 static const char* exts[2] = { "C", "E" };
3959 v8::ExtensionConfiguration config(2, exts);
3960 LocalContext context(&config);
3961 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3962}
3963
3964
3965static const char* kExtensionTestScript =
3966 "native function A();"
3967 "native function B();"
3968 "native function C();"
3969 "function Foo(i) {"
3970 " if (i == 0) return A();"
3971 " if (i == 1) return B();"
3972 " if (i == 2) return C();"
3973 "}";
3974
3975
3976static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3977 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003978 if (args.IsConstructCall()) {
3979 args.This()->Set(v8_str("data"), args.Data());
3980 return v8::Null();
3981 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003982 return args.Data();
3983}
3984
3985
3986class FunctionExtension : public Extension {
3987 public:
3988 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3989 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3990 v8::Handle<String> name);
3991};
3992
3993
3994static int lookup_count = 0;
3995v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3996 v8::Handle<String> name) {
3997 lookup_count++;
3998 if (name->Equals(v8_str("A"))) {
3999 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
4000 } else if (name->Equals(v8_str("B"))) {
4001 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
4002 } else if (name->Equals(v8_str("C"))) {
4003 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
4004 } else {
4005 return v8::Handle<v8::FunctionTemplate>();
4006 }
4007}
4008
4009
4010THREADED_TEST(FunctionLookup) {
4011 v8::RegisterExtension(new FunctionExtension());
4012 v8::HandleScope handle_scope;
4013 static const char* exts[1] = { "functiontest" };
4014 v8::ExtensionConfiguration config(1, exts);
4015 LocalContext context(&config);
4016 CHECK_EQ(3, lookup_count);
4017 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
4018 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
4019 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
4020}
4021
4022
Leon Clarkee46be812010-01-19 14:06:41 +00004023THREADED_TEST(NativeFunctionConstructCall) {
4024 v8::RegisterExtension(new FunctionExtension());
4025 v8::HandleScope handle_scope;
4026 static const char* exts[1] = { "functiontest" };
4027 v8::ExtensionConfiguration config(1, exts);
4028 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00004029 for (int i = 0; i < 10; i++) {
4030 // Run a few times to ensure that allocation of objects doesn't
4031 // change behavior of a constructor function.
4032 CHECK_EQ(v8::Integer::New(8),
4033 Script::Compile(v8_str("(new A()).data"))->Run());
4034 CHECK_EQ(v8::Integer::New(7),
4035 Script::Compile(v8_str("(new B()).data"))->Run());
4036 CHECK_EQ(v8::Integer::New(6),
4037 Script::Compile(v8_str("(new C()).data"))->Run());
4038 }
Leon Clarkee46be812010-01-19 14:06:41 +00004039}
4040
4041
Steve Blocka7e24c12009-10-30 11:49:00 +00004042static const char* last_location;
4043static const char* last_message;
4044void StoringErrorCallback(const char* location, const char* message) {
4045 if (last_location == NULL) {
4046 last_location = location;
4047 last_message = message;
4048 }
4049}
4050
4051
4052// ErrorReporting creates a circular extensions configuration and
4053// tests that the fatal error handler gets called. This renders V8
4054// unusable and therefore this test cannot be run in parallel.
4055TEST(ErrorReporting) {
4056 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
4057 static const char* aDeps[] = { "B" };
4058 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
4059 static const char* bDeps[] = { "A" };
4060 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
4061 last_location = NULL;
4062 v8::ExtensionConfiguration config(1, bDeps);
4063 v8::Handle<Context> context = Context::New(&config);
4064 CHECK(context.IsEmpty());
4065 CHECK_NE(last_location, NULL);
4066}
4067
4068
4069static const char* js_code_causing_huge_string_flattening =
4070 "var str = 'X';"
4071 "for (var i = 0; i < 30; i++) {"
4072 " str = str + str;"
4073 "}"
4074 "str.match(/X/);";
4075
4076
4077void OOMCallback(const char* location, const char* message) {
4078 exit(0);
4079}
4080
4081
4082TEST(RegexpOutOfMemory) {
4083 // Execute a script that causes out of memory when flattening a string.
4084 v8::HandleScope scope;
4085 v8::V8::SetFatalErrorHandler(OOMCallback);
4086 LocalContext context;
4087 Local<Script> script =
4088 Script::Compile(String::New(js_code_causing_huge_string_flattening));
4089 last_location = NULL;
4090 Local<Value> result = script->Run();
4091
4092 CHECK(false); // Should not return.
4093}
4094
4095
4096static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
4097 v8::Handle<Value> data) {
4098 CHECK_EQ(v8::Undefined(), data);
4099 CHECK(message->GetScriptResourceName()->IsUndefined());
4100 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
4101 message->GetLineNumber();
4102 message->GetSourceLine();
4103}
4104
4105
4106THREADED_TEST(ErrorWithMissingScriptInfo) {
4107 v8::HandleScope scope;
4108 LocalContext context;
4109 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
4110 Script::Compile(v8_str("throw Error()"))->Run();
4111 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
4112}
4113
4114
4115int global_index = 0;
4116
4117class Snorkel {
4118 public:
4119 Snorkel() { index_ = global_index++; }
4120 int index_;
4121};
4122
4123class Whammy {
4124 public:
4125 Whammy() {
4126 cursor_ = 0;
4127 }
4128 ~Whammy() {
4129 script_.Dispose();
4130 }
4131 v8::Handle<Script> getScript() {
4132 if (script_.IsEmpty())
4133 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
4134 return Local<Script>(*script_);
4135 }
4136
4137 public:
4138 static const int kObjectCount = 256;
4139 int cursor_;
4140 v8::Persistent<v8::Object> objects_[kObjectCount];
4141 v8::Persistent<Script> script_;
4142};
4143
4144static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
4145 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
4146 delete snorkel;
4147 obj.ClearWeak();
4148}
4149
4150v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
4151 const AccessorInfo& info) {
4152 Whammy* whammy =
4153 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
4154
4155 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
4156
4157 v8::Handle<v8::Object> obj = v8::Object::New();
4158 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
4159 if (!prev.IsEmpty()) {
4160 prev->Set(v8_str("next"), obj);
4161 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
4162 whammy->objects_[whammy->cursor_].Clear();
4163 }
4164 whammy->objects_[whammy->cursor_] = global;
4165 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
4166 return whammy->getScript()->Run();
4167}
4168
4169THREADED_TEST(WeakReference) {
4170 v8::HandleScope handle_scope;
4171 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004172 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00004173 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
4174 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004175 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00004176 const char* extension_list[] = { "v8/gc" };
4177 v8::ExtensionConfiguration extensions(1, extension_list);
4178 v8::Persistent<Context> context = Context::New(&extensions);
4179 Context::Scope context_scope(context);
4180
4181 v8::Handle<v8::Object> interceptor = templ->NewInstance();
4182 context->Global()->Set(v8_str("whammy"), interceptor);
4183 const char* code =
4184 "var last;"
4185 "for (var i = 0; i < 10000; i++) {"
4186 " var obj = whammy.length;"
4187 " if (last) last.next = obj;"
4188 " last = obj;"
4189 "}"
4190 "gc();"
4191 "4";
4192 v8::Handle<Value> result = CompileRun(code);
4193 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004194 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00004195 context.Dispose();
4196}
4197
4198
Steve Blockd0582a62009-12-15 09:54:21 +00004199static bool in_scavenge = false;
4200static int last = -1;
4201
4202static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
4203 CHECK_EQ(-1, last);
4204 last = 0;
4205 obj.Dispose();
4206 obj.Clear();
4207 in_scavenge = true;
4208 i::Heap::PerformScavenge();
4209 in_scavenge = false;
4210 *(reinterpret_cast<bool*>(data)) = true;
4211}
4212
4213static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
4214 void* data) {
4215 CHECK_EQ(0, last);
4216 last = 1;
4217 *(reinterpret_cast<bool*>(data)) = in_scavenge;
4218 obj.Dispose();
4219 obj.Clear();
4220}
4221
4222THREADED_TEST(NoWeakRefCallbacksInScavenge) {
4223 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
4224 // Calling callbacks from scavenges is unsafe as objects held by those
4225 // handlers might have become strongly reachable, but scavenge doesn't
4226 // check that.
4227 v8::Persistent<Context> context = Context::New();
4228 Context::Scope context_scope(context);
4229
4230 v8::Persistent<v8::Object> object_a;
4231 v8::Persistent<v8::Object> object_b;
4232
4233 {
4234 v8::HandleScope handle_scope;
4235 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
4236 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
4237 }
4238
4239 bool object_a_disposed = false;
4240 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
4241 bool released_in_scavenge = false;
4242 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
4243
4244 while (!object_a_disposed) {
4245 i::Heap::CollectAllGarbage(false);
4246 }
4247 CHECK(!released_in_scavenge);
4248}
4249
4250
Steve Blocka7e24c12009-10-30 11:49:00 +00004251v8::Handle<Function> args_fun;
4252
4253
4254static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
4255 ApiTestFuzzer::Fuzz();
4256 CHECK_EQ(args_fun, args.Callee());
4257 CHECK_EQ(3, args.Length());
4258 CHECK_EQ(v8::Integer::New(1), args[0]);
4259 CHECK_EQ(v8::Integer::New(2), args[1]);
4260 CHECK_EQ(v8::Integer::New(3), args[2]);
4261 CHECK_EQ(v8::Undefined(), args[3]);
4262 v8::HandleScope scope;
4263 i::Heap::CollectAllGarbage(false);
4264 return v8::Undefined();
4265}
4266
4267
4268THREADED_TEST(Arguments) {
4269 v8::HandleScope scope;
4270 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
4271 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
4272 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01004273 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00004274 v8_compile("f(1, 2, 3)")->Run();
4275}
4276
4277
Steve Blocka7e24c12009-10-30 11:49:00 +00004278static v8::Handle<Value> NoBlockGetterX(Local<String> name,
4279 const AccessorInfo&) {
4280 return v8::Handle<Value>();
4281}
4282
4283
4284static v8::Handle<Value> NoBlockGetterI(uint32_t index,
4285 const AccessorInfo&) {
4286 return v8::Handle<Value>();
4287}
4288
4289
4290static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
4291 const AccessorInfo&) {
4292 if (!name->Equals(v8_str("foo"))) {
4293 return v8::Handle<v8::Boolean>(); // not intercepted
4294 }
4295
4296 return v8::False(); // intercepted, and don't delete the property
4297}
4298
4299
4300static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4301 if (index != 2) {
4302 return v8::Handle<v8::Boolean>(); // not intercepted
4303 }
4304
4305 return v8::False(); // intercepted, and don't delete the property
4306}
4307
4308
4309THREADED_TEST(Deleter) {
4310 v8::HandleScope scope;
4311 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4312 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4313 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4314 LocalContext context;
4315 context->Global()->Set(v8_str("k"), obj->NewInstance());
4316 CompileRun(
4317 "k.foo = 'foo';"
4318 "k.bar = 'bar';"
4319 "k[2] = 2;"
4320 "k[4] = 4;");
4321 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4322 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4323
4324 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4325 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4326
4327 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4328 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4329
4330 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4331 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4332}
4333
4334
4335static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4336 ApiTestFuzzer::Fuzz();
4337 if (name->Equals(v8_str("foo")) ||
4338 name->Equals(v8_str("bar")) ||
4339 name->Equals(v8_str("baz"))) {
4340 return v8::Undefined();
4341 }
4342 return v8::Handle<Value>();
4343}
4344
4345
4346static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4347 ApiTestFuzzer::Fuzz();
4348 if (index == 0 || index == 1) return v8::Undefined();
4349 return v8::Handle<Value>();
4350}
4351
4352
4353static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4354 ApiTestFuzzer::Fuzz();
4355 v8::Handle<v8::Array> result = v8::Array::New(3);
4356 result->Set(v8::Integer::New(0), v8_str("foo"));
4357 result->Set(v8::Integer::New(1), v8_str("bar"));
4358 result->Set(v8::Integer::New(2), v8_str("baz"));
4359 return result;
4360}
4361
4362
4363static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4364 ApiTestFuzzer::Fuzz();
4365 v8::Handle<v8::Array> result = v8::Array::New(2);
4366 result->Set(v8::Integer::New(0), v8_str("0"));
4367 result->Set(v8::Integer::New(1), v8_str("1"));
4368 return result;
4369}
4370
4371
4372THREADED_TEST(Enumerators) {
4373 v8::HandleScope scope;
4374 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4375 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4376 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4377 LocalContext context;
4378 context->Global()->Set(v8_str("k"), obj->NewInstance());
4379 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4380 "k[10] = 0;"
4381 "k.a = 0;"
4382 "k[5] = 0;"
4383 "k.b = 0;"
4384 "k[4294967295] = 0;"
4385 "k.c = 0;"
4386 "k[4294967296] = 0;"
4387 "k.d = 0;"
4388 "k[140000] = 0;"
4389 "k.e = 0;"
4390 "k[30000000000] = 0;"
4391 "k.f = 0;"
4392 "var result = [];"
4393 "for (var prop in k) {"
4394 " result.push(prop);"
4395 "}"
4396 "result"));
4397 // Check that we get all the property names returned including the
4398 // ones from the enumerators in the right order: indexed properties
4399 // in numerical order, indexed interceptor properties, named
4400 // properties in insertion order, named interceptor properties.
4401 // This order is not mandated by the spec, so this test is just
4402 // documenting our behavior.
4403 CHECK_EQ(17, result->Length());
4404 // Indexed properties in numerical order.
4405 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4406 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4407 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4408 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4409 // Indexed interceptor properties in the order they are returned
4410 // from the enumerator interceptor.
4411 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4412 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4413 // Named properties in insertion order.
4414 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4415 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4416 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4417 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4418 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4419 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4420 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4421 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4422 // Named interceptor properties.
4423 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4424 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4425 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4426}
4427
4428
4429int p_getter_count;
4430int p_getter_count2;
4431
4432
4433static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4434 ApiTestFuzzer::Fuzz();
4435 p_getter_count++;
4436 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4437 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4438 if (name->Equals(v8_str("p1"))) {
4439 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4440 } else if (name->Equals(v8_str("p2"))) {
4441 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4442 } else if (name->Equals(v8_str("p3"))) {
4443 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4444 } else if (name->Equals(v8_str("p4"))) {
4445 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4446 }
4447 return v8::Undefined();
4448}
4449
4450
4451static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4452 ApiTestFuzzer::Fuzz();
4453 LocalContext context;
4454 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4455 CompileRun(
4456 "o1.__proto__ = { };"
4457 "var o2 = { __proto__: o1 };"
4458 "var o3 = { __proto__: o2 };"
4459 "var o4 = { __proto__: o3 };"
4460 "for (var i = 0; i < 10; i++) o4.p4;"
4461 "for (var i = 0; i < 10; i++) o3.p3;"
4462 "for (var i = 0; i < 10; i++) o2.p2;"
4463 "for (var i = 0; i < 10; i++) o1.p1;");
4464}
4465
4466
4467static v8::Handle<Value> PGetter2(Local<String> name,
4468 const AccessorInfo& info) {
4469 ApiTestFuzzer::Fuzz();
4470 p_getter_count2++;
4471 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4472 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4473 if (name->Equals(v8_str("p1"))) {
4474 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4475 } else if (name->Equals(v8_str("p2"))) {
4476 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4477 } else if (name->Equals(v8_str("p3"))) {
4478 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4479 } else if (name->Equals(v8_str("p4"))) {
4480 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4481 }
4482 return v8::Undefined();
4483}
4484
4485
4486THREADED_TEST(GetterHolders) {
4487 v8::HandleScope scope;
4488 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4489 obj->SetAccessor(v8_str("p1"), PGetter);
4490 obj->SetAccessor(v8_str("p2"), PGetter);
4491 obj->SetAccessor(v8_str("p3"), PGetter);
4492 obj->SetAccessor(v8_str("p4"), PGetter);
4493 p_getter_count = 0;
4494 RunHolderTest(obj);
4495 CHECK_EQ(40, p_getter_count);
4496}
4497
4498
4499THREADED_TEST(PreInterceptorHolders) {
4500 v8::HandleScope scope;
4501 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4502 obj->SetNamedPropertyHandler(PGetter2);
4503 p_getter_count2 = 0;
4504 RunHolderTest(obj);
4505 CHECK_EQ(40, p_getter_count2);
4506}
4507
4508
4509THREADED_TEST(ObjectInstantiation) {
4510 v8::HandleScope scope;
4511 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4512 templ->SetAccessor(v8_str("t"), PGetter2);
4513 LocalContext context;
4514 context->Global()->Set(v8_str("o"), templ->NewInstance());
4515 for (int i = 0; i < 100; i++) {
4516 v8::HandleScope inner_scope;
4517 v8::Handle<v8::Object> obj = templ->NewInstance();
4518 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4519 context->Global()->Set(v8_str("o2"), obj);
4520 v8::Handle<Value> value =
4521 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4522 CHECK_EQ(v8::True(), value);
4523 context->Global()->Set(v8_str("o"), obj);
4524 }
4525}
4526
4527
Ben Murdochb0fe1622011-05-05 13:52:32 +01004528static int StrCmp16(uint16_t* a, uint16_t* b) {
4529 while (true) {
4530 if (*a == 0 && *b == 0) return 0;
4531 if (*a != *b) return 0 + *a - *b;
4532 a++;
4533 b++;
4534 }
4535}
4536
4537
4538static int StrNCmp16(uint16_t* a, uint16_t* b, int n) {
4539 while (true) {
4540 if (n-- == 0) return 0;
4541 if (*a == 0 && *b == 0) return 0;
4542 if (*a != *b) return 0 + *a - *b;
4543 a++;
4544 b++;
4545 }
4546}
4547
4548
Steve Blocka7e24c12009-10-30 11:49:00 +00004549THREADED_TEST(StringWrite) {
4550 v8::HandleScope scope;
4551 v8::Handle<String> str = v8_str("abcde");
Ben Murdochb0fe1622011-05-05 13:52:32 +01004552 // abc<Icelandic eth><Unicode snowman>.
4553 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203");
4554
4555 CHECK_EQ(5, str2->Length());
Steve Blocka7e24c12009-10-30 11:49:00 +00004556
4557 char buf[100];
Ben Murdochb0fe1622011-05-05 13:52:32 +01004558 char utf8buf[100];
4559 uint16_t wbuf[100];
Steve Blocka7e24c12009-10-30 11:49:00 +00004560 int len;
Ben Murdochb0fe1622011-05-05 13:52:32 +01004561 int charlen;
4562
4563 memset(utf8buf, 0x1, sizeof(utf8buf));
4564 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen);
4565 CHECK_EQ(len, 9);
4566 CHECK_EQ(charlen, 5);
4567 CHECK_EQ(strcmp(utf8buf, "abc\303\260\342\230\203"), 0);
4568
4569 memset(utf8buf, 0x1, sizeof(utf8buf));
4570 len = str2->WriteUtf8(utf8buf, 8, &charlen);
4571 CHECK_EQ(len, 8);
4572 CHECK_EQ(charlen, 5);
4573 CHECK_EQ(strncmp(utf8buf, "abc\303\260\342\230\203\1", 9), 0);
4574
4575 memset(utf8buf, 0x1, sizeof(utf8buf));
4576 len = str2->WriteUtf8(utf8buf, 7, &charlen);
4577 CHECK_EQ(len, 5);
4578 CHECK_EQ(charlen, 4);
4579 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0);
4580
4581 memset(utf8buf, 0x1, sizeof(utf8buf));
4582 len = str2->WriteUtf8(utf8buf, 6, &charlen);
4583 CHECK_EQ(len, 5);
4584 CHECK_EQ(charlen, 4);
4585 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0);
4586
4587 memset(utf8buf, 0x1, sizeof(utf8buf));
4588 len = str2->WriteUtf8(utf8buf, 5, &charlen);
4589 CHECK_EQ(len, 5);
4590 CHECK_EQ(charlen, 4);
4591 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0);
4592
4593 memset(utf8buf, 0x1, sizeof(utf8buf));
4594 len = str2->WriteUtf8(utf8buf, 4, &charlen);
4595 CHECK_EQ(len, 3);
4596 CHECK_EQ(charlen, 3);
4597 CHECK_EQ(strncmp(utf8buf, "abc\1", 4), 0);
4598
4599 memset(utf8buf, 0x1, sizeof(utf8buf));
4600 len = str2->WriteUtf8(utf8buf, 3, &charlen);
4601 CHECK_EQ(len, 3);
4602 CHECK_EQ(charlen, 3);
4603 CHECK_EQ(strncmp(utf8buf, "abc\1", 4), 0);
4604
4605 memset(utf8buf, 0x1, sizeof(utf8buf));
4606 len = str2->WriteUtf8(utf8buf, 2, &charlen);
4607 CHECK_EQ(len, 2);
4608 CHECK_EQ(charlen, 2);
4609 CHECK_EQ(strncmp(utf8buf, "ab\1", 3), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004610
4611 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004612 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004613 len = str->WriteAscii(buf);
4614 CHECK_EQ(len, 5);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004615 len = str->Write(wbuf);
4616 CHECK_EQ(len, 5);
4617 CHECK_EQ(strcmp("abcde", buf), 0);
4618 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'};
4619 CHECK_EQ(StrCmp16(answer1, wbuf), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004620
4621 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004622 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004623 len = str->WriteAscii(buf, 0, 4);
4624 CHECK_EQ(len, 4);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004625 len = str->Write(wbuf, 0, 4);
4626 CHECK_EQ(len, 4);
Steve Blocka7e24c12009-10-30 11:49:00 +00004627 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004628 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101};
4629 CHECK_EQ(StrNCmp16(answer2, wbuf, 5), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004630
4631 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004632 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004633 len = str->WriteAscii(buf, 0, 5);
4634 CHECK_EQ(len, 5);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004635 len = str->Write(wbuf, 0, 5);
4636 CHECK_EQ(len, 5);
Steve Blocka7e24c12009-10-30 11:49:00 +00004637 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004638 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101};
4639 CHECK_EQ(StrNCmp16(answer3, wbuf, 6), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004640
4641 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004642 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004643 len = str->WriteAscii(buf, 0, 6);
4644 CHECK_EQ(len, 5);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004645 len = str->Write(wbuf, 0, 6);
4646 CHECK_EQ(len, 5);
4647 CHECK_EQ(strcmp("abcde", buf), 0);
4648 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'};
4649 CHECK_EQ(StrCmp16(answer4, wbuf), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004650
4651 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004652 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004653 len = str->WriteAscii(buf, 4, -1);
4654 CHECK_EQ(len, 1);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004655 len = str->Write(wbuf, 4, -1);
4656 CHECK_EQ(len, 1);
4657 CHECK_EQ(strcmp("e", buf), 0);
4658 uint16_t answer5[] = {'e', '\0'};
4659 CHECK_EQ(StrCmp16(answer5, wbuf), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004660
4661 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004662 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004663 len = str->WriteAscii(buf, 4, 6);
4664 CHECK_EQ(len, 1);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004665 len = str->Write(wbuf, 4, 6);
4666 CHECK_EQ(len, 1);
4667 CHECK_EQ(strcmp("e", buf), 0);
4668 CHECK_EQ(StrCmp16(answer5, wbuf), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004669
4670 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004671 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004672 len = str->WriteAscii(buf, 4, 1);
4673 CHECK_EQ(len, 1);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004674 len = str->Write(wbuf, 4, 1);
4675 CHECK_EQ(len, 1);
Steve Blocka7e24c12009-10-30 11:49:00 +00004676 CHECK_EQ(strncmp("e\1", buf, 2), 0);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004677 uint16_t answer6[] = {'e', 0x101};
4678 CHECK_EQ(StrNCmp16(answer6, wbuf, 2), 0);
4679
4680 memset(buf, 0x1, sizeof(buf));
4681 memset(wbuf, 0x1, sizeof(wbuf));
4682 len = str->WriteAscii(buf, 3, 1);
4683 CHECK_EQ(len, 1);
4684 len = str->Write(wbuf, 3, 1);
4685 CHECK_EQ(len, 1);
4686 CHECK_EQ(strncmp("d\1", buf, 2), 0);
4687 uint16_t answer7[] = {'d', 0x101};
4688 CHECK_EQ(StrNCmp16(answer7, wbuf, 2), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004689}
4690
4691
4692THREADED_TEST(ToArrayIndex) {
4693 v8::HandleScope scope;
4694 LocalContext context;
4695
4696 v8::Handle<String> str = v8_str("42");
4697 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4698 CHECK(!index.IsEmpty());
4699 CHECK_EQ(42.0, index->Uint32Value());
4700 str = v8_str("42asdf");
4701 index = str->ToArrayIndex();
4702 CHECK(index.IsEmpty());
4703 str = v8_str("-42");
4704 index = str->ToArrayIndex();
4705 CHECK(index.IsEmpty());
4706 str = v8_str("4294967295");
4707 index = str->ToArrayIndex();
4708 CHECK(!index.IsEmpty());
4709 CHECK_EQ(4294967295.0, index->Uint32Value());
4710 v8::Handle<v8::Number> num = v8::Number::New(1);
4711 index = num->ToArrayIndex();
4712 CHECK(!index.IsEmpty());
4713 CHECK_EQ(1.0, index->Uint32Value());
4714 num = v8::Number::New(-1);
4715 index = num->ToArrayIndex();
4716 CHECK(index.IsEmpty());
4717 v8::Handle<v8::Object> obj = v8::Object::New();
4718 index = obj->ToArrayIndex();
4719 CHECK(index.IsEmpty());
4720}
4721
4722
4723THREADED_TEST(ErrorConstruction) {
4724 v8::HandleScope scope;
4725 LocalContext context;
4726
4727 v8::Handle<String> foo = v8_str("foo");
4728 v8::Handle<String> message = v8_str("message");
4729 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4730 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004731 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4732 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004733 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4734 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004735 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004736 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4737 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004738 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004739 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4740 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004741 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004742 v8::Handle<Value> error = v8::Exception::Error(foo);
4743 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004744 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004745}
4746
4747
4748static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4749 ApiTestFuzzer::Fuzz();
4750 return v8_num(10);
4751}
4752
4753
4754static void YSetter(Local<String> name,
4755 Local<Value> value,
4756 const AccessorInfo& info) {
4757 if (info.This()->Has(name)) {
4758 info.This()->Delete(name);
4759 }
4760 info.This()->Set(name, value);
4761}
4762
4763
4764THREADED_TEST(DeleteAccessor) {
4765 v8::HandleScope scope;
4766 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4767 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4768 LocalContext context;
4769 v8::Handle<v8::Object> holder = obj->NewInstance();
4770 context->Global()->Set(v8_str("holder"), holder);
4771 v8::Handle<Value> result = CompileRun(
4772 "holder.y = 11; holder.y = 12; holder.y");
4773 CHECK_EQ(12, result->Uint32Value());
4774}
4775
4776
4777THREADED_TEST(TypeSwitch) {
4778 v8::HandleScope scope;
4779 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4780 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4781 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4782 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4783 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4784 LocalContext context;
4785 v8::Handle<v8::Object> obj0 = v8::Object::New();
4786 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4787 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4788 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4789 for (int i = 0; i < 10; i++) {
4790 CHECK_EQ(0, type_switch->match(obj0));
4791 CHECK_EQ(1, type_switch->match(obj1));
4792 CHECK_EQ(2, type_switch->match(obj2));
4793 CHECK_EQ(3, type_switch->match(obj3));
4794 CHECK_EQ(3, type_switch->match(obj3));
4795 CHECK_EQ(2, type_switch->match(obj2));
4796 CHECK_EQ(1, type_switch->match(obj1));
4797 CHECK_EQ(0, type_switch->match(obj0));
4798 }
4799}
4800
4801
4802// For use within the TestSecurityHandler() test.
4803static bool g_security_callback_result = false;
4804static bool NamedSecurityTestCallback(Local<v8::Object> global,
4805 Local<Value> name,
4806 v8::AccessType type,
4807 Local<Value> data) {
4808 // Always allow read access.
4809 if (type == v8::ACCESS_GET)
4810 return true;
4811
4812 // Sometimes allow other access.
4813 return g_security_callback_result;
4814}
4815
4816
4817static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4818 uint32_t key,
4819 v8::AccessType type,
4820 Local<Value> data) {
4821 // Always allow read access.
4822 if (type == v8::ACCESS_GET)
4823 return true;
4824
4825 // Sometimes allow other access.
4826 return g_security_callback_result;
4827}
4828
4829
4830static int trouble_nesting = 0;
4831static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4832 ApiTestFuzzer::Fuzz();
4833 trouble_nesting++;
4834
4835 // Call a JS function that throws an uncaught exception.
4836 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4837 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4838 arg_this->Get(v8_str("trouble_callee")) :
4839 arg_this->Get(v8_str("trouble_caller"));
4840 CHECK(trouble_callee->IsFunction());
4841 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4842}
4843
4844
4845static int report_count = 0;
4846static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4847 v8::Handle<Value>) {
4848 report_count++;
4849}
4850
4851
4852// Counts uncaught exceptions, but other tests running in parallel
4853// also have uncaught exceptions.
4854TEST(ApiUncaughtException) {
4855 report_count = 0;
4856 v8::HandleScope scope;
4857 LocalContext env;
4858 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4859
4860 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4861 v8::Local<v8::Object> global = env->Global();
4862 global->Set(v8_str("trouble"), fun->GetFunction());
4863
4864 Script::Compile(v8_str("function trouble_callee() {"
4865 " var x = null;"
4866 " return x.foo;"
4867 "};"
4868 "function trouble_caller() {"
4869 " trouble();"
4870 "};"))->Run();
4871 Local<Value> trouble = global->Get(v8_str("trouble"));
4872 CHECK(trouble->IsFunction());
4873 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4874 CHECK(trouble_callee->IsFunction());
4875 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4876 CHECK(trouble_caller->IsFunction());
4877 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4878 CHECK_EQ(1, report_count);
4879 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4880}
4881
Leon Clarke4515c472010-02-03 11:58:03 +00004882static const char* script_resource_name = "ExceptionInNativeScript.js";
4883static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4884 v8::Handle<Value>) {
4885 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4886 CHECK(!name_val.IsEmpty() && name_val->IsString());
4887 v8::String::AsciiValue name(message->GetScriptResourceName());
4888 CHECK_EQ(script_resource_name, *name);
4889 CHECK_EQ(3, message->GetLineNumber());
4890 v8::String::AsciiValue source_line(message->GetSourceLine());
4891 CHECK_EQ(" new o.foo();", *source_line);
4892}
4893
4894TEST(ExceptionInNativeScript) {
4895 v8::HandleScope scope;
4896 LocalContext env;
4897 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4898
4899 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4900 v8::Local<v8::Object> global = env->Global();
4901 global->Set(v8_str("trouble"), fun->GetFunction());
4902
4903 Script::Compile(v8_str("function trouble() {\n"
4904 " var o = {};\n"
4905 " new o.foo();\n"
4906 "};"), v8::String::New(script_resource_name))->Run();
4907 Local<Value> trouble = global->Get(v8_str("trouble"));
4908 CHECK(trouble->IsFunction());
4909 Function::Cast(*trouble)->Call(global, 0, NULL);
4910 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4911}
4912
Steve Blocka7e24c12009-10-30 11:49:00 +00004913
4914TEST(CompilationErrorUsingTryCatchHandler) {
4915 v8::HandleScope scope;
4916 LocalContext env;
4917 v8::TryCatch try_catch;
4918 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4919 CHECK_NE(NULL, *try_catch.Exception());
4920 CHECK(try_catch.HasCaught());
4921}
4922
4923
4924TEST(TryCatchFinallyUsingTryCatchHandler) {
4925 v8::HandleScope scope;
4926 LocalContext env;
4927 v8::TryCatch try_catch;
4928 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4929 CHECK(!try_catch.HasCaught());
4930 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4931 CHECK(try_catch.HasCaught());
4932 try_catch.Reset();
4933 Script::Compile(v8_str("(function() {"
4934 "try { throw ''; } finally { return; }"
4935 "})()"))->Run();
4936 CHECK(!try_catch.HasCaught());
4937 Script::Compile(v8_str("(function()"
4938 " { try { throw ''; } finally { throw 0; }"
4939 "})()"))->Run();
4940 CHECK(try_catch.HasCaught());
4941}
4942
4943
4944// SecurityHandler can't be run twice
4945TEST(SecurityHandler) {
4946 v8::HandleScope scope0;
4947 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4948 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4949 IndexedSecurityTestCallback);
4950 // Create an environment
4951 v8::Persistent<Context> context0 =
4952 Context::New(NULL, global_template);
4953 context0->Enter();
4954
4955 v8::Handle<v8::Object> global0 = context0->Global();
4956 v8::Handle<Script> script0 = v8_compile("foo = 111");
4957 script0->Run();
4958 global0->Set(v8_str("0"), v8_num(999));
4959 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4960 CHECK_EQ(111, foo0->Int32Value());
4961 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4962 CHECK_EQ(999, z0->Int32Value());
4963
4964 // Create another environment, should fail security checks.
4965 v8::HandleScope scope1;
4966
4967 v8::Persistent<Context> context1 =
4968 Context::New(NULL, global_template);
4969 context1->Enter();
4970
4971 v8::Handle<v8::Object> global1 = context1->Global();
4972 global1->Set(v8_str("othercontext"), global0);
4973 // This set will fail the security check.
4974 v8::Handle<Script> script1 =
4975 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4976 script1->Run();
4977 // This read will pass the security check.
4978 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4979 CHECK_EQ(111, foo1->Int32Value());
4980 // This read will pass the security check.
4981 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4982 CHECK_EQ(999, z1->Int32Value());
4983
4984 // Create another environment, should pass security checks.
4985 { g_security_callback_result = true; // allow security handler to pass.
4986 v8::HandleScope scope2;
4987 LocalContext context2;
4988 v8::Handle<v8::Object> global2 = context2->Global();
4989 global2->Set(v8_str("othercontext"), global0);
4990 v8::Handle<Script> script2 =
4991 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4992 script2->Run();
4993 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4994 CHECK_EQ(333, foo2->Int32Value());
4995 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4996 CHECK_EQ(888, z2->Int32Value());
4997 }
4998
4999 context1->Exit();
5000 context1.Dispose();
5001
5002 context0->Exit();
5003 context0.Dispose();
5004}
5005
5006
5007THREADED_TEST(SecurityChecks) {
5008 v8::HandleScope handle_scope;
5009 LocalContext env1;
5010 v8::Persistent<Context> env2 = Context::New();
5011
5012 Local<Value> foo = v8_str("foo");
5013 Local<Value> bar = v8_str("bar");
5014
5015 // Set to the same domain.
5016 env1->SetSecurityToken(foo);
5017
5018 // Create a function in env1.
5019 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
5020 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
5021 CHECK(spy->IsFunction());
5022
5023 // Create another function accessing global objects.
5024 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
5025 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
5026 CHECK(spy2->IsFunction());
5027
5028 // Switch to env2 in the same domain and invoke spy on env2.
5029 {
5030 env2->SetSecurityToken(foo);
5031 // Enter env2
5032 Context::Scope scope_env2(env2);
5033 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
5034 CHECK(result->IsFunction());
5035 }
5036
5037 {
5038 env2->SetSecurityToken(bar);
5039 Context::Scope scope_env2(env2);
5040
5041 // Call cross_domain_call, it should throw an exception
5042 v8::TryCatch try_catch;
5043 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
5044 CHECK(try_catch.HasCaught());
5045 }
5046
5047 env2.Dispose();
5048}
5049
5050
5051// Regression test case for issue 1183439.
5052THREADED_TEST(SecurityChecksForPrototypeChain) {
5053 v8::HandleScope scope;
5054 LocalContext current;
5055 v8::Persistent<Context> other = Context::New();
5056
5057 // Change context to be able to get to the Object function in the
5058 // other context without hitting the security checks.
5059 v8::Local<Value> other_object;
5060 { Context::Scope scope(other);
5061 other_object = other->Global()->Get(v8_str("Object"));
5062 other->Global()->Set(v8_num(42), v8_num(87));
5063 }
5064
5065 current->Global()->Set(v8_str("other"), other->Global());
5066 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
5067
5068 // Make sure the security check fails here and we get an undefined
5069 // result instead of getting the Object function. Repeat in a loop
5070 // to make sure to exercise the IC code.
5071 v8::Local<Script> access_other0 = v8_compile("other.Object");
5072 v8::Local<Script> access_other1 = v8_compile("other[42]");
5073 for (int i = 0; i < 5; i++) {
5074 CHECK(!access_other0->Run()->Equals(other_object));
5075 CHECK(access_other0->Run()->IsUndefined());
5076 CHECK(!access_other1->Run()->Equals(v8_num(87)));
5077 CHECK(access_other1->Run()->IsUndefined());
5078 }
5079
5080 // Create an object that has 'other' in its prototype chain and make
5081 // sure we cannot access the Object function indirectly through
5082 // that. Repeat in a loop to make sure to exercise the IC code.
5083 v8_compile("function F() { };"
5084 "F.prototype = other;"
5085 "var f = new F();")->Run();
5086 v8::Local<Script> access_f0 = v8_compile("f.Object");
5087 v8::Local<Script> access_f1 = v8_compile("f[42]");
5088 for (int j = 0; j < 5; j++) {
5089 CHECK(!access_f0->Run()->Equals(other_object));
5090 CHECK(access_f0->Run()->IsUndefined());
5091 CHECK(!access_f1->Run()->Equals(v8_num(87)));
5092 CHECK(access_f1->Run()->IsUndefined());
5093 }
5094
5095 // Now it gets hairy: Set the prototype for the other global object
5096 // to be the current global object. The prototype chain for 'f' now
5097 // goes through 'other' but ends up in the current global object.
5098 { Context::Scope scope(other);
5099 other->Global()->Set(v8_str("__proto__"), current->Global());
5100 }
5101 // Set a named and an index property on the current global
5102 // object. To force the lookup to go through the other global object,
5103 // the properties must not exist in the other global object.
5104 current->Global()->Set(v8_str("foo"), v8_num(100));
5105 current->Global()->Set(v8_num(99), v8_num(101));
5106 // Try to read the properties from f and make sure that the access
5107 // gets stopped by the security checks on the other global object.
5108 Local<Script> access_f2 = v8_compile("f.foo");
5109 Local<Script> access_f3 = v8_compile("f[99]");
5110 for (int k = 0; k < 5; k++) {
5111 CHECK(!access_f2->Run()->Equals(v8_num(100)));
5112 CHECK(access_f2->Run()->IsUndefined());
5113 CHECK(!access_f3->Run()->Equals(v8_num(101)));
5114 CHECK(access_f3->Run()->IsUndefined());
5115 }
5116 other.Dispose();
5117}
5118
5119
5120THREADED_TEST(CrossDomainDelete) {
5121 v8::HandleScope handle_scope;
5122 LocalContext env1;
5123 v8::Persistent<Context> env2 = Context::New();
5124
5125 Local<Value> foo = v8_str("foo");
5126 Local<Value> bar = v8_str("bar");
5127
5128 // Set to the same domain.
5129 env1->SetSecurityToken(foo);
5130 env2->SetSecurityToken(foo);
5131
5132 env1->Global()->Set(v8_str("prop"), v8_num(3));
5133 env2->Global()->Set(v8_str("env1"), env1->Global());
5134
5135 // Change env2 to a different domain and delete env1.prop.
5136 env2->SetSecurityToken(bar);
5137 {
5138 Context::Scope scope_env2(env2);
5139 Local<Value> result =
5140 Script::Compile(v8_str("delete env1.prop"))->Run();
5141 CHECK(result->IsFalse());
5142 }
5143
5144 // Check that env1.prop still exists.
5145 Local<Value> v = env1->Global()->Get(v8_str("prop"));
5146 CHECK(v->IsNumber());
5147 CHECK_EQ(3, v->Int32Value());
5148
5149 env2.Dispose();
5150}
5151
5152
5153THREADED_TEST(CrossDomainIsPropertyEnumerable) {
5154 v8::HandleScope handle_scope;
5155 LocalContext env1;
5156 v8::Persistent<Context> env2 = Context::New();
5157
5158 Local<Value> foo = v8_str("foo");
5159 Local<Value> bar = v8_str("bar");
5160
5161 // Set to the same domain.
5162 env1->SetSecurityToken(foo);
5163 env2->SetSecurityToken(foo);
5164
5165 env1->Global()->Set(v8_str("prop"), v8_num(3));
5166 env2->Global()->Set(v8_str("env1"), env1->Global());
5167
5168 // env1.prop is enumerable in env2.
5169 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
5170 {
5171 Context::Scope scope_env2(env2);
5172 Local<Value> result = Script::Compile(test)->Run();
5173 CHECK(result->IsTrue());
5174 }
5175
5176 // Change env2 to a different domain and test again.
5177 env2->SetSecurityToken(bar);
5178 {
5179 Context::Scope scope_env2(env2);
5180 Local<Value> result = Script::Compile(test)->Run();
5181 CHECK(result->IsFalse());
5182 }
5183
5184 env2.Dispose();
5185}
5186
5187
5188THREADED_TEST(CrossDomainForIn) {
5189 v8::HandleScope handle_scope;
5190 LocalContext env1;
5191 v8::Persistent<Context> env2 = Context::New();
5192
5193 Local<Value> foo = v8_str("foo");
5194 Local<Value> bar = v8_str("bar");
5195
5196 // Set to the same domain.
5197 env1->SetSecurityToken(foo);
5198 env2->SetSecurityToken(foo);
5199
5200 env1->Global()->Set(v8_str("prop"), v8_num(3));
5201 env2->Global()->Set(v8_str("env1"), env1->Global());
5202
5203 // Change env2 to a different domain and set env1's global object
5204 // as the __proto__ of an object in env2 and enumerate properties
5205 // in for-in. It shouldn't enumerate properties on env1's global
5206 // object.
5207 env2->SetSecurityToken(bar);
5208 {
5209 Context::Scope scope_env2(env2);
5210 Local<Value> result =
5211 CompileRun("(function(){var obj = {'__proto__':env1};"
5212 "for (var p in obj)"
5213 " if (p == 'prop') return false;"
5214 "return true;})()");
5215 CHECK(result->IsTrue());
5216 }
5217 env2.Dispose();
5218}
5219
5220
5221TEST(ContextDetachGlobal) {
5222 v8::HandleScope handle_scope;
5223 LocalContext env1;
5224 v8::Persistent<Context> env2 = Context::New();
5225
5226 Local<v8::Object> global1 = env1->Global();
5227
5228 Local<Value> foo = v8_str("foo");
5229
5230 // Set to the same domain.
5231 env1->SetSecurityToken(foo);
5232 env2->SetSecurityToken(foo);
5233
5234 // Enter env2
5235 env2->Enter();
5236
Andrei Popescu74b3c142010-03-29 12:03:09 +01005237 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00005238 Local<v8::Object> global2 = env2->Global();
5239 global2->Set(v8_str("prop"), v8::Integer::New(1));
5240 CompileRun("function getProp() {return prop;}");
5241
5242 env1->Global()->Set(v8_str("getProp"),
5243 global2->Get(v8_str("getProp")));
5244
Andrei Popescu74b3c142010-03-29 12:03:09 +01005245 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00005246 env2->Exit();
5247 env2->DetachGlobal();
5248 // env2 has a new global object.
5249 CHECK(!env2->Global()->Equals(global2));
5250
5251 v8::Persistent<Context> env3 =
5252 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5253 env3->SetSecurityToken(v8_str("bar"));
5254 env3->Enter();
5255
5256 Local<v8::Object> global3 = env3->Global();
5257 CHECK_EQ(global2, global3);
5258 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
5259 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
5260 global3->Set(v8_str("prop"), v8::Integer::New(-1));
5261 global3->Set(v8_str("prop2"), v8::Integer::New(2));
5262 env3->Exit();
5263
5264 // Call getProp in env1, and it should return the value 1
5265 {
5266 Local<Value> get_prop = global1->Get(v8_str("getProp"));
5267 CHECK(get_prop->IsFunction());
5268 v8::TryCatch try_catch;
5269 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
5270 CHECK(!try_catch.HasCaught());
5271 CHECK_EQ(1, r->Int32Value());
5272 }
5273
5274 // Check that env3 is not accessible from env1
5275 {
5276 Local<Value> r = global3->Get(v8_str("prop2"));
5277 CHECK(r->IsUndefined());
5278 }
5279
5280 env2.Dispose();
5281 env3.Dispose();
5282}
5283
5284
Andrei Popescu74b3c142010-03-29 12:03:09 +01005285TEST(DetachAndReattachGlobal) {
5286 v8::HandleScope scope;
5287 LocalContext env1;
5288
5289 // Create second environment.
5290 v8::Persistent<Context> env2 = Context::New();
5291
5292 Local<Value> foo = v8_str("foo");
5293
5294 // Set same security token for env1 and env2.
5295 env1->SetSecurityToken(foo);
5296 env2->SetSecurityToken(foo);
5297
5298 // Create a property on the global object in env2.
5299 {
5300 v8::Context::Scope scope(env2);
5301 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
5302 }
5303
5304 // Create a reference to env2 global from env1 global.
5305 env1->Global()->Set(v8_str("other"), env2->Global());
5306
5307 // Check that we have access to other.p in env2 from env1.
5308 Local<Value> result = CompileRun("other.p");
5309 CHECK(result->IsInt32());
5310 CHECK_EQ(42, result->Int32Value());
5311
5312 // Hold on to global from env2 and detach global from env2.
5313 Local<v8::Object> global2 = env2->Global();
5314 env2->DetachGlobal();
5315
5316 // Check that the global has been detached. No other.p property can
5317 // be found.
5318 result = CompileRun("other.p");
5319 CHECK(result->IsUndefined());
5320
5321 // Reuse global2 for env3.
5322 v8::Persistent<Context> env3 =
5323 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5324 CHECK_EQ(global2, env3->Global());
5325
5326 // Start by using the same security token for env3 as for env1 and env2.
5327 env3->SetSecurityToken(foo);
5328
5329 // Create a property on the global object in env3.
5330 {
5331 v8::Context::Scope scope(env3);
5332 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
5333 }
5334
5335 // Check that other.p is now the property in env3 and that we have access.
5336 result = CompileRun("other.p");
5337 CHECK(result->IsInt32());
5338 CHECK_EQ(24, result->Int32Value());
5339
5340 // Change security token for env3 to something different from env1 and env2.
5341 env3->SetSecurityToken(v8_str("bar"));
5342
5343 // Check that we do not have access to other.p in env1. |other| is now
5344 // the global object for env3 which has a different security token,
5345 // so access should be blocked.
5346 result = CompileRun("other.p");
5347 CHECK(result->IsUndefined());
5348
5349 // Detach the global for env3 and reattach it to env2.
5350 env3->DetachGlobal();
5351 env2->ReattachGlobal(global2);
5352
5353 // Check that we have access to other.p again in env1. |other| is now
5354 // the global object for env2 which has the same security token as env1.
5355 result = CompileRun("other.p");
5356 CHECK(result->IsInt32());
5357 CHECK_EQ(42, result->Int32Value());
5358
5359 env2.Dispose();
5360 env3.Dispose();
5361}
5362
5363
Steve Block1e0659c2011-05-24 12:43:12 +01005364static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false };
Steve Blocka7e24c12009-10-30 11:49:00 +00005365static bool NamedAccessBlocker(Local<v8::Object> global,
5366 Local<Value> name,
5367 v8::AccessType type,
5368 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005369 return Context::GetCurrent()->Global()->Equals(global) ||
5370 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005371}
5372
5373
5374static bool IndexedAccessBlocker(Local<v8::Object> global,
5375 uint32_t key,
5376 v8::AccessType type,
5377 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005378 return Context::GetCurrent()->Global()->Equals(global) ||
5379 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005380}
5381
5382
5383static int g_echo_value = -1;
5384static v8::Handle<Value> EchoGetter(Local<String> name,
5385 const AccessorInfo& info) {
5386 return v8_num(g_echo_value);
5387}
5388
5389
5390static void EchoSetter(Local<String> name,
5391 Local<Value> value,
5392 const AccessorInfo&) {
5393 if (value->IsNumber())
5394 g_echo_value = value->Int32Value();
5395}
5396
5397
5398static v8::Handle<Value> UnreachableGetter(Local<String> name,
5399 const AccessorInfo& info) {
5400 CHECK(false); // This function should not be called..
5401 return v8::Undefined();
5402}
5403
5404
5405static void UnreachableSetter(Local<String>, Local<Value>,
5406 const AccessorInfo&) {
5407 CHECK(false); // This function should nto be called.
5408}
5409
5410
Steve Block1e0659c2011-05-24 12:43:12 +01005411TEST(AccessControl) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005412 v8::HandleScope handle_scope;
5413 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5414
5415 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5416 IndexedAccessBlocker);
5417
5418 // Add an accessor accessible by cross-domain JS code.
5419 global_template->SetAccessor(
5420 v8_str("accessible_prop"),
5421 EchoGetter, EchoSetter,
5422 v8::Handle<Value>(),
5423 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5424
5425 // Add an accessor that is not accessible by cross-domain JS code.
5426 global_template->SetAccessor(v8_str("blocked_prop"),
5427 UnreachableGetter, UnreachableSetter,
5428 v8::Handle<Value>(),
5429 v8::DEFAULT);
5430
5431 // Create an environment
5432 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5433 context0->Enter();
5434
5435 v8::Handle<v8::Object> global0 = context0->Global();
5436
Steve Block1e0659c2011-05-24 12:43:12 +01005437 // Define a property with JS getter and setter.
5438 CompileRun(
5439 "function getter() { return 'getter'; };\n"
5440 "function setter() { return 'setter'; }\n"
5441 "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})");
5442
5443 Local<Value> getter = global0->Get(v8_str("getter"));
5444 Local<Value> setter = global0->Get(v8_str("setter"));
5445
5446 // And define normal element.
5447 global0->Set(239, v8_str("239"));
5448
5449 // Define an element with JS getter and setter.
5450 CompileRun(
5451 "function el_getter() { return 'el_getter'; };\n"
5452 "function el_setter() { return 'el_setter'; };\n"
5453 "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});");
5454
5455 Local<Value> el_getter = global0->Get(v8_str("el_getter"));
5456 Local<Value> el_setter = global0->Get(v8_str("el_setter"));
5457
Steve Blocka7e24c12009-10-30 11:49:00 +00005458 v8::HandleScope scope1;
5459
5460 v8::Persistent<Context> context1 = Context::New();
5461 context1->Enter();
5462
5463 v8::Handle<v8::Object> global1 = context1->Global();
5464 global1->Set(v8_str("other"), global0);
5465
Steve Block1e0659c2011-05-24 12:43:12 +01005466 // Access blocked property.
5467 CompileRun("other.blocked_prop = 1");
5468
5469 ExpectUndefined("other.blocked_prop");
5470 ExpectUndefined(
5471 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5472 ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')");
5473
5474 // Enable ACCESS_HAS
5475 allowed_access_type[v8::ACCESS_HAS] = true;
5476 ExpectUndefined("other.blocked_prop");
5477 // ... and now we can get the descriptor...
5478 ExpectUndefined(
5479 "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value");
5480 // ... and enumerate the property.
5481 ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')");
5482 allowed_access_type[v8::ACCESS_HAS] = false;
5483
5484 // Access blocked element.
5485 CompileRun("other[239] = 1");
5486
5487 ExpectUndefined("other[239]");
5488 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')");
5489 ExpectFalse("propertyIsEnumerable.call(other, '239')");
5490
5491 // Enable ACCESS_HAS
5492 allowed_access_type[v8::ACCESS_HAS] = true;
5493 ExpectUndefined("other[239]");
5494 // ... and now we can get the descriptor...
5495 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value");
5496 // ... and enumerate the property.
5497 ExpectTrue("propertyIsEnumerable.call(other, '239')");
5498 allowed_access_type[v8::ACCESS_HAS] = false;
5499
5500 // Access a property with JS accessor.
5501 CompileRun("other.js_accessor_p = 2");
5502
5503 ExpectUndefined("other.js_accessor_p");
5504 ExpectUndefined(
5505 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')");
5506
5507 // Enable ACCESS_HAS.
5508 allowed_access_type[v8::ACCESS_HAS] = true;
5509 ExpectUndefined("other.js_accessor_p");
5510 ExpectUndefined(
5511 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5512 ExpectUndefined(
5513 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5514 ExpectUndefined(
5515 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5516 allowed_access_type[v8::ACCESS_HAS] = false;
5517
5518 // Enable both ACCESS_HAS and ACCESS_GET.
5519 allowed_access_type[v8::ACCESS_HAS] = true;
5520 allowed_access_type[v8::ACCESS_GET] = true;
5521
5522 ExpectString("other.js_accessor_p", "getter");
5523 ExpectObject(
5524 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5525 ExpectUndefined(
5526 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5527 ExpectUndefined(
5528 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5529
5530 allowed_access_type[v8::ACCESS_GET] = false;
5531 allowed_access_type[v8::ACCESS_HAS] = false;
5532
5533 // Enable both ACCESS_HAS and ACCESS_SET.
5534 allowed_access_type[v8::ACCESS_HAS] = true;
5535 allowed_access_type[v8::ACCESS_SET] = true;
5536
5537 ExpectUndefined("other.js_accessor_p");
5538 ExpectUndefined(
5539 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5540 ExpectObject(
5541 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5542 ExpectUndefined(
5543 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5544
5545 allowed_access_type[v8::ACCESS_SET] = false;
5546 allowed_access_type[v8::ACCESS_HAS] = false;
5547
5548 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5549 allowed_access_type[v8::ACCESS_HAS] = true;
5550 allowed_access_type[v8::ACCESS_GET] = true;
5551 allowed_access_type[v8::ACCESS_SET] = true;
5552
5553 ExpectString("other.js_accessor_p", "getter");
5554 ExpectObject(
5555 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5556 ExpectObject(
5557 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5558 ExpectUndefined(
5559 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5560
5561 allowed_access_type[v8::ACCESS_SET] = false;
5562 allowed_access_type[v8::ACCESS_GET] = false;
5563 allowed_access_type[v8::ACCESS_HAS] = false;
5564
5565 // Access an element with JS accessor.
5566 CompileRun("other[42] = 2");
5567
5568 ExpectUndefined("other[42]");
5569 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')");
5570
5571 // Enable ACCESS_HAS.
5572 allowed_access_type[v8::ACCESS_HAS] = true;
5573 ExpectUndefined("other[42]");
5574 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5575 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5576 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5577 allowed_access_type[v8::ACCESS_HAS] = false;
5578
5579 // Enable both ACCESS_HAS and ACCESS_GET.
5580 allowed_access_type[v8::ACCESS_HAS] = true;
5581 allowed_access_type[v8::ACCESS_GET] = true;
5582
5583 ExpectString("other[42]", "el_getter");
5584 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5585 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5586 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5587
5588 allowed_access_type[v8::ACCESS_GET] = false;
5589 allowed_access_type[v8::ACCESS_HAS] = false;
5590
5591 // Enable both ACCESS_HAS and ACCESS_SET.
5592 allowed_access_type[v8::ACCESS_HAS] = true;
5593 allowed_access_type[v8::ACCESS_SET] = true;
5594
5595 ExpectUndefined("other[42]");
5596 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5597 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5598 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5599
5600 allowed_access_type[v8::ACCESS_SET] = false;
5601 allowed_access_type[v8::ACCESS_HAS] = false;
5602
5603 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5604 allowed_access_type[v8::ACCESS_HAS] = true;
5605 allowed_access_type[v8::ACCESS_GET] = true;
5606 allowed_access_type[v8::ACCESS_SET] = true;
5607
5608 ExpectString("other[42]", "el_getter");
5609 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5610 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5611 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5612
5613 allowed_access_type[v8::ACCESS_SET] = false;
5614 allowed_access_type[v8::ACCESS_GET] = false;
5615 allowed_access_type[v8::ACCESS_HAS] = false;
5616
Steve Blocka7e24c12009-10-30 11:49:00 +00005617 v8::Handle<Value> value;
5618
Steve Blocka7e24c12009-10-30 11:49:00 +00005619 // Access accessible property
Steve Block1e0659c2011-05-24 12:43:12 +01005620 value = CompileRun("other.accessible_prop = 3");
Steve Blocka7e24c12009-10-30 11:49:00 +00005621 CHECK(value->IsNumber());
5622 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005623 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005624
Steve Block1e0659c2011-05-24 12:43:12 +01005625 value = CompileRun("other.accessible_prop");
Steve Blocka7e24c12009-10-30 11:49:00 +00005626 CHECK(value->IsNumber());
5627 CHECK_EQ(3, value->Int32Value());
5628
Steve Block1e0659c2011-05-24 12:43:12 +01005629 value = CompileRun(
5630 "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value");
5631 CHECK(value->IsNumber());
5632 CHECK_EQ(3, value->Int32Value());
5633
5634 value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')");
Steve Blocka7e24c12009-10-30 11:49:00 +00005635 CHECK(value->IsTrue());
5636
5637 // Enumeration doesn't enumerate accessors from inaccessible objects in
5638 // the prototype chain even if the accessors are in themselves accessible.
Steve Block1e0659c2011-05-24 12:43:12 +01005639 value =
Steve Blocka7e24c12009-10-30 11:49:00 +00005640 CompileRun("(function(){var obj = {'__proto__':other};"
5641 "for (var p in obj)"
5642 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5643 " return false;"
5644 " }"
5645 "return true;})()");
Steve Block1e0659c2011-05-24 12:43:12 +01005646 CHECK(value->IsTrue());
Steve Blocka7e24c12009-10-30 11:49:00 +00005647
5648 context1->Exit();
5649 context0->Exit();
5650 context1.Dispose();
5651 context0.Dispose();
5652}
5653
5654
Ben Murdoche0cee9b2011-05-25 10:26:03 +01005655TEST(AccessControlES5) {
5656 v8::HandleScope handle_scope;
5657 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5658
5659 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5660 IndexedAccessBlocker);
5661
5662 // Add an accessor that is not accessible by cross-domain JS code.
5663 global_template->SetAccessor(v8_str("blocked_prop"),
5664 UnreachableGetter, UnreachableSetter,
5665 v8::Handle<Value>(),
5666 v8::DEFAULT);
5667
5668 // Create an environment
5669 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5670 context0->Enter();
5671
5672 v8::Handle<v8::Object> global0 = context0->Global();
5673
5674 v8::Persistent<Context> context1 = Context::New();
5675 context1->Enter();
5676 v8::Handle<v8::Object> global1 = context1->Global();
5677 global1->Set(v8_str("other"), global0);
5678
5679 // Regression test for issue 1154.
5680 ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1");
5681
5682 ExpectUndefined("other.blocked_prop");
5683
5684 // Regression test for issue 1027.
5685 CompileRun("Object.defineProperty(\n"
5686 " other, 'blocked_prop', {configurable: false})");
5687 ExpectUndefined("other.blocked_prop");
5688 ExpectUndefined(
5689 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5690
5691 // Regression test for issue 1171.
5692 ExpectTrue("Object.isExtensible(other)");
5693 CompileRun("Object.preventExtensions(other)");
5694 ExpectTrue("Object.isExtensible(other)");
5695
5696 // Object.seal and Object.freeze.
5697 CompileRun("Object.freeze(other)");
5698 ExpectTrue("Object.isExtensible(other)");
5699
5700 CompileRun("Object.seal(other)");
5701 ExpectTrue("Object.isExtensible(other)");
5702}
5703
5704
Leon Clarke4515c472010-02-03 11:58:03 +00005705static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5706 Local<Value> name,
5707 v8::AccessType type,
5708 Local<Value> data) {
5709 return false;
5710}
5711
5712
5713static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5714 uint32_t key,
5715 v8::AccessType type,
5716 Local<Value> data) {
5717 return false;
5718}
5719
5720
5721THREADED_TEST(AccessControlGetOwnPropertyNames) {
5722 v8::HandleScope handle_scope;
5723 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5724
5725 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5726 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5727 GetOwnPropertyNamesIndexedBlocker);
5728
5729 // Create an environment
5730 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5731 context0->Enter();
5732
5733 v8::Handle<v8::Object> global0 = context0->Global();
5734
5735 v8::HandleScope scope1;
5736
5737 v8::Persistent<Context> context1 = Context::New();
5738 context1->Enter();
5739
5740 v8::Handle<v8::Object> global1 = context1->Global();
5741 global1->Set(v8_str("other"), global0);
5742 global1->Set(v8_str("object"), obj_template->NewInstance());
5743
5744 v8::Handle<Value> value;
5745
5746 // Attempt to get the property names of the other global object and
5747 // of an object that requires access checks. Accessing the other
5748 // global object should be blocked by access checks on the global
5749 // proxy object. Accessing the object that requires access checks
5750 // is blocked by the access checks on the object itself.
5751 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5752 CHECK(value->IsTrue());
5753
5754 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5755 CHECK(value->IsTrue());
5756
5757 context1->Exit();
5758 context0->Exit();
5759 context1.Dispose();
5760 context0.Dispose();
5761}
5762
5763
Steve Block8defd9f2010-07-08 12:39:36 +01005764static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5765 v8::Handle<v8::Array> result = v8::Array::New(1);
5766 result->Set(0, v8_str("x"));
5767 return result;
5768}
5769
5770
5771THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5772 v8::HandleScope handle_scope;
5773 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5774
5775 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5776 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5777 NamedPropertyEnumerator);
5778
5779 LocalContext context;
5780 v8::Handle<v8::Object> global = context->Global();
5781 global->Set(v8_str("object"), obj_template->NewInstance());
5782
5783 v8::Handle<Value> value =
5784 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5785 CHECK_EQ(v8_str("x"), value);
5786}
5787
5788
Steve Blocka7e24c12009-10-30 11:49:00 +00005789static v8::Handle<Value> ConstTenGetter(Local<String> name,
5790 const AccessorInfo& info) {
5791 return v8_num(10);
5792}
5793
5794
5795THREADED_TEST(CrossDomainAccessors) {
5796 v8::HandleScope handle_scope;
5797
5798 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5799
5800 v8::Handle<v8::ObjectTemplate> global_template =
5801 func_template->InstanceTemplate();
5802
5803 v8::Handle<v8::ObjectTemplate> proto_template =
5804 func_template->PrototypeTemplate();
5805
5806 // Add an accessor to proto that's accessible by cross-domain JS code.
5807 proto_template->SetAccessor(v8_str("accessible"),
5808 ConstTenGetter, 0,
5809 v8::Handle<Value>(),
5810 v8::ALL_CAN_READ);
5811
5812 // Add an accessor that is not accessible by cross-domain JS code.
5813 global_template->SetAccessor(v8_str("unreachable"),
5814 UnreachableGetter, 0,
5815 v8::Handle<Value>(),
5816 v8::DEFAULT);
5817
5818 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5819 context0->Enter();
5820
5821 Local<v8::Object> global = context0->Global();
5822 // Add a normal property that shadows 'accessible'
5823 global->Set(v8_str("accessible"), v8_num(11));
5824
5825 // Enter a new context.
5826 v8::HandleScope scope1;
5827 v8::Persistent<Context> context1 = Context::New();
5828 context1->Enter();
5829
5830 v8::Handle<v8::Object> global1 = context1->Global();
5831 global1->Set(v8_str("other"), global);
5832
5833 // Should return 10, instead of 11
5834 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5835 CHECK(value->IsNumber());
5836 CHECK_EQ(10, value->Int32Value());
5837
5838 value = v8_compile("other.unreachable")->Run();
5839 CHECK(value->IsUndefined());
5840
5841 context1->Exit();
5842 context0->Exit();
5843 context1.Dispose();
5844 context0.Dispose();
5845}
5846
5847
5848static int named_access_count = 0;
5849static int indexed_access_count = 0;
5850
5851static bool NamedAccessCounter(Local<v8::Object> global,
5852 Local<Value> name,
5853 v8::AccessType type,
5854 Local<Value> data) {
5855 named_access_count++;
5856 return true;
5857}
5858
5859
5860static bool IndexedAccessCounter(Local<v8::Object> global,
5861 uint32_t key,
5862 v8::AccessType type,
5863 Local<Value> data) {
5864 indexed_access_count++;
5865 return true;
5866}
5867
5868
5869// This one is too easily disturbed by other tests.
5870TEST(AccessControlIC) {
5871 named_access_count = 0;
5872 indexed_access_count = 0;
5873
5874 v8::HandleScope handle_scope;
5875
5876 // Create an environment.
5877 v8::Persistent<Context> context0 = Context::New();
5878 context0->Enter();
5879
5880 // Create an object that requires access-check functions to be
5881 // called for cross-domain access.
5882 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5883 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5884 IndexedAccessCounter);
5885 Local<v8::Object> object = object_template->NewInstance();
5886
5887 v8::HandleScope scope1;
5888
5889 // Create another environment.
5890 v8::Persistent<Context> context1 = Context::New();
5891 context1->Enter();
5892
5893 // Make easy access to the object from the other environment.
5894 v8::Handle<v8::Object> global1 = context1->Global();
5895 global1->Set(v8_str("obj"), object);
5896
5897 v8::Handle<Value> value;
5898
5899 // Check that the named access-control function is called every time.
5900 CompileRun("function testProp(obj) {"
5901 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5902 " for (var j = 0; j < 10; j++) obj.prop;"
5903 " return obj.prop"
5904 "}");
5905 value = CompileRun("testProp(obj)");
5906 CHECK(value->IsNumber());
5907 CHECK_EQ(1, value->Int32Value());
5908 CHECK_EQ(21, named_access_count);
5909
5910 // Check that the named access-control function is called every time.
5911 CompileRun("var p = 'prop';"
5912 "function testKeyed(obj) {"
5913 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5914 " for (var j = 0; j < 10; j++) obj[p];"
5915 " return obj[p];"
5916 "}");
5917 // Use obj which requires access checks. No inline caching is used
5918 // in that case.
5919 value = CompileRun("testKeyed(obj)");
5920 CHECK(value->IsNumber());
5921 CHECK_EQ(1, value->Int32Value());
5922 CHECK_EQ(42, named_access_count);
5923 // Force the inline caches into generic state and try again.
5924 CompileRun("testKeyed({ a: 0 })");
5925 CompileRun("testKeyed({ b: 0 })");
5926 value = CompileRun("testKeyed(obj)");
5927 CHECK(value->IsNumber());
5928 CHECK_EQ(1, value->Int32Value());
5929 CHECK_EQ(63, named_access_count);
5930
5931 // Check that the indexed access-control function is called every time.
5932 CompileRun("function testIndexed(obj) {"
5933 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5934 " for (var j = 0; j < 10; j++) obj[0];"
5935 " return obj[0]"
5936 "}");
5937 value = CompileRun("testIndexed(obj)");
5938 CHECK(value->IsNumber());
5939 CHECK_EQ(1, value->Int32Value());
5940 CHECK_EQ(21, indexed_access_count);
5941 // Force the inline caches into generic state.
5942 CompileRun("testIndexed(new Array(1))");
5943 // Test that the indexed access check is called.
5944 value = CompileRun("testIndexed(obj)");
5945 CHECK(value->IsNumber());
5946 CHECK_EQ(1, value->Int32Value());
5947 CHECK_EQ(42, indexed_access_count);
5948
5949 // Check that the named access check is called when invoking
5950 // functions on an object that requires access checks.
5951 CompileRun("obj.f = function() {}");
5952 CompileRun("function testCallNormal(obj) {"
5953 " for (var i = 0; i < 10; i++) obj.f();"
5954 "}");
5955 CompileRun("testCallNormal(obj)");
5956 CHECK_EQ(74, named_access_count);
5957
5958 // Force obj into slow case.
5959 value = CompileRun("delete obj.prop");
5960 CHECK(value->BooleanValue());
5961 // Force inline caches into dictionary probing mode.
5962 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5963 // Test that the named access check is called.
5964 value = CompileRun("testProp(obj);");
5965 CHECK(value->IsNumber());
5966 CHECK_EQ(1, value->Int32Value());
5967 CHECK_EQ(96, named_access_count);
5968
5969 // Force the call inline cache into dictionary probing mode.
5970 CompileRun("o.f = function() {}; testCallNormal(o)");
5971 // Test that the named access check is still called for each
5972 // invocation of the function.
5973 value = CompileRun("testCallNormal(obj)");
5974 CHECK_EQ(106, named_access_count);
5975
5976 context1->Exit();
5977 context0->Exit();
5978 context1.Dispose();
5979 context0.Dispose();
5980}
5981
5982
5983static bool NamedAccessFlatten(Local<v8::Object> global,
5984 Local<Value> name,
5985 v8::AccessType type,
5986 Local<Value> data) {
5987 char buf[100];
5988 int len;
5989
5990 CHECK(name->IsString());
5991
5992 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005993 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005994 CHECK_EQ(4, len);
5995
5996 uint16_t buf2[100];
5997
5998 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005999 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00006000 CHECK_EQ(4, len);
6001
6002 return true;
6003}
6004
6005
6006static bool IndexedAccessFlatten(Local<v8::Object> global,
6007 uint32_t key,
6008 v8::AccessType type,
6009 Local<Value> data) {
6010 return true;
6011}
6012
6013
6014// Regression test. In access checks, operations that may cause
6015// garbage collection are not allowed. It used to be the case that
6016// using the Write operation on a string could cause a garbage
6017// collection due to flattening of the string. This is no longer the
6018// case.
6019THREADED_TEST(AccessControlFlatten) {
6020 named_access_count = 0;
6021 indexed_access_count = 0;
6022
6023 v8::HandleScope handle_scope;
6024
6025 // Create an environment.
6026 v8::Persistent<Context> context0 = Context::New();
6027 context0->Enter();
6028
6029 // Create an object that requires access-check functions to be
6030 // called for cross-domain access.
6031 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6032 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
6033 IndexedAccessFlatten);
6034 Local<v8::Object> object = object_template->NewInstance();
6035
6036 v8::HandleScope scope1;
6037
6038 // Create another environment.
6039 v8::Persistent<Context> context1 = Context::New();
6040 context1->Enter();
6041
6042 // Make easy access to the object from the other environment.
6043 v8::Handle<v8::Object> global1 = context1->Global();
6044 global1->Set(v8_str("obj"), object);
6045
6046 v8::Handle<Value> value;
6047
6048 value = v8_compile("var p = 'as' + 'df';")->Run();
6049 value = v8_compile("obj[p];")->Run();
6050
6051 context1->Exit();
6052 context0->Exit();
6053 context1.Dispose();
6054 context0.Dispose();
6055}
6056
6057
6058static v8::Handle<Value> AccessControlNamedGetter(
6059 Local<String>, const AccessorInfo&) {
6060 return v8::Integer::New(42);
6061}
6062
6063
6064static v8::Handle<Value> AccessControlNamedSetter(
6065 Local<String>, Local<Value> value, const AccessorInfo&) {
6066 return value;
6067}
6068
6069
6070static v8::Handle<Value> AccessControlIndexedGetter(
6071 uint32_t index,
6072 const AccessorInfo& info) {
6073 return v8_num(42);
6074}
6075
6076
6077static v8::Handle<Value> AccessControlIndexedSetter(
6078 uint32_t, Local<Value> value, const AccessorInfo&) {
6079 return value;
6080}
6081
6082
6083THREADED_TEST(AccessControlInterceptorIC) {
6084 named_access_count = 0;
6085 indexed_access_count = 0;
6086
6087 v8::HandleScope handle_scope;
6088
6089 // Create an environment.
6090 v8::Persistent<Context> context0 = Context::New();
6091 context0->Enter();
6092
6093 // Create an object that requires access-check functions to be
6094 // called for cross-domain access. The object also has interceptors
6095 // interceptor.
6096 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6097 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
6098 IndexedAccessCounter);
6099 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
6100 AccessControlNamedSetter);
6101 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
6102 AccessControlIndexedSetter);
6103 Local<v8::Object> object = object_template->NewInstance();
6104
6105 v8::HandleScope scope1;
6106
6107 // Create another environment.
6108 v8::Persistent<Context> context1 = Context::New();
6109 context1->Enter();
6110
6111 // Make easy access to the object from the other environment.
6112 v8::Handle<v8::Object> global1 = context1->Global();
6113 global1->Set(v8_str("obj"), object);
6114
6115 v8::Handle<Value> value;
6116
6117 // Check that the named access-control function is called every time
6118 // eventhough there is an interceptor on the object.
6119 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
6120 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
6121 "obj.x")->Run();
6122 CHECK(value->IsNumber());
6123 CHECK_EQ(42, value->Int32Value());
6124 CHECK_EQ(21, named_access_count);
6125
6126 value = v8_compile("var p = 'x';")->Run();
6127 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
6128 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
6129 "obj[p]")->Run();
6130 CHECK(value->IsNumber());
6131 CHECK_EQ(42, value->Int32Value());
6132 CHECK_EQ(42, named_access_count);
6133
6134 // Check that the indexed access-control function is called every
6135 // time eventhough there is an interceptor on the object.
6136 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
6137 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
6138 "obj[0]")->Run();
6139 CHECK(value->IsNumber());
6140 CHECK_EQ(42, value->Int32Value());
6141 CHECK_EQ(21, indexed_access_count);
6142
6143 context1->Exit();
6144 context0->Exit();
6145 context1.Dispose();
6146 context0.Dispose();
6147}
6148
6149
6150THREADED_TEST(Version) {
6151 v8::V8::GetVersion();
6152}
6153
6154
6155static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
6156 ApiTestFuzzer::Fuzz();
6157 return v8_num(12);
6158}
6159
6160
6161THREADED_TEST(InstanceProperties) {
6162 v8::HandleScope handle_scope;
6163 LocalContext context;
6164
6165 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6166 Local<ObjectTemplate> instance = t->InstanceTemplate();
6167
6168 instance->Set(v8_str("x"), v8_num(42));
6169 instance->Set(v8_str("f"),
6170 v8::FunctionTemplate::New(InstanceFunctionCallback));
6171
6172 Local<Value> o = t->GetFunction()->NewInstance();
6173
6174 context->Global()->Set(v8_str("i"), o);
6175 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
6176 CHECK_EQ(42, value->Int32Value());
6177
6178 value = Script::Compile(v8_str("i.f()"))->Run();
6179 CHECK_EQ(12, value->Int32Value());
6180}
6181
6182
6183static v8::Handle<Value>
6184GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
6185 ApiTestFuzzer::Fuzz();
6186 return v8::Handle<Value>();
6187}
6188
6189
6190THREADED_TEST(GlobalObjectInstanceProperties) {
6191 v8::HandleScope handle_scope;
6192
6193 Local<Value> global_object;
6194
6195 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6196 t->InstanceTemplate()->SetNamedPropertyHandler(
6197 GlobalObjectInstancePropertiesGet);
6198 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6199 instance_template->Set(v8_str("x"), v8_num(42));
6200 instance_template->Set(v8_str("f"),
6201 v8::FunctionTemplate::New(InstanceFunctionCallback));
6202
Ben Murdochb0fe1622011-05-05 13:52:32 +01006203 // The script to check how Crankshaft compiles missing global function
6204 // invocations. function g is not defined and should throw on call.
6205 const char* script =
6206 "function wrapper(call) {"
6207 " var x = 0, y = 1;"
6208 " for (var i = 0; i < 1000; i++) {"
6209 " x += i * 100;"
6210 " y += i * 100;"
6211 " }"
6212 " if (call) g();"
6213 "}"
6214 "for (var i = 0; i < 17; i++) wrapper(false);"
6215 "var thrown = 0;"
6216 "try { wrapper(true); } catch (e) { thrown = 1; };"
6217 "thrown";
6218
Steve Blocka7e24c12009-10-30 11:49:00 +00006219 {
6220 LocalContext env(NULL, instance_template);
6221 // Hold on to the global object so it can be used again in another
6222 // environment initialization.
6223 global_object = env->Global();
6224
6225 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6226 CHECK_EQ(42, value->Int32Value());
6227 value = Script::Compile(v8_str("f()"))->Run();
6228 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006229 value = Script::Compile(v8_str(script))->Run();
6230 CHECK_EQ(1, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006231 }
6232
6233 {
6234 // Create new environment reusing the global object.
6235 LocalContext env(NULL, instance_template, global_object);
6236 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6237 CHECK_EQ(42, value->Int32Value());
6238 value = Script::Compile(v8_str("f()"))->Run();
6239 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006240 value = Script::Compile(v8_str(script))->Run();
6241 CHECK_EQ(1, value->Int32Value());
6242 }
6243}
6244
6245
6246THREADED_TEST(CallKnownGlobalReceiver) {
6247 v8::HandleScope handle_scope;
6248
6249 Local<Value> global_object;
6250
6251 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6252 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6253
6254 // The script to check that we leave global object not
6255 // global object proxy on stack when we deoptimize from inside
6256 // arguments evaluation.
6257 // To provoke error we need to both force deoptimization
6258 // from arguments evaluation and to force CallIC to take
6259 // CallIC_Miss code path that can't cope with global proxy.
6260 const char* script =
6261 "function bar(x, y) { try { } finally { } }"
6262 "function baz(x) { try { } finally { } }"
6263 "function bom(x) { try { } finally { } }"
6264 "function foo(x) { bar([x], bom(2)); }"
6265 "for (var i = 0; i < 10000; i++) foo(1);"
6266 "foo";
6267
6268 Local<Value> foo;
6269 {
6270 LocalContext env(NULL, instance_template);
6271 // Hold on to the global object so it can be used again in another
6272 // environment initialization.
6273 global_object = env->Global();
6274 foo = Script::Compile(v8_str(script))->Run();
6275 }
6276
6277 {
6278 // Create new environment reusing the global object.
6279 LocalContext env(NULL, instance_template, global_object);
6280 env->Global()->Set(v8_str("foo"), foo);
6281 Local<Value> value = Script::Compile(v8_str("foo()"))->Run();
Steve Blocka7e24c12009-10-30 11:49:00 +00006282 }
6283}
6284
6285
6286static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
6287 ApiTestFuzzer::Fuzz();
6288 return v8_num(42);
6289}
6290
6291
6292static int shadow_y;
6293static int shadow_y_setter_call_count;
6294static int shadow_y_getter_call_count;
6295
6296
6297static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
6298 shadow_y_setter_call_count++;
6299 shadow_y = 42;
6300}
6301
6302
6303static v8::Handle<Value> ShadowYGetter(Local<String> name,
6304 const AccessorInfo& info) {
6305 ApiTestFuzzer::Fuzz();
6306 shadow_y_getter_call_count++;
6307 return v8_num(shadow_y);
6308}
6309
6310
6311static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
6312 const AccessorInfo& info) {
6313 return v8::Handle<Value>();
6314}
6315
6316
6317static v8::Handle<Value> ShadowNamedGet(Local<String> key,
6318 const AccessorInfo&) {
6319 return v8::Handle<Value>();
6320}
6321
6322
6323THREADED_TEST(ShadowObject) {
6324 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
6325 v8::HandleScope handle_scope;
6326
6327 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
6328 LocalContext context(NULL, global_template);
6329
6330 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6331 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
6332 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
6333 Local<ObjectTemplate> proto = t->PrototypeTemplate();
6334 Local<ObjectTemplate> instance = t->InstanceTemplate();
6335
6336 // Only allow calls of f on instances of t.
6337 Local<v8::Signature> signature = v8::Signature::New(t);
6338 proto->Set(v8_str("f"),
6339 v8::FunctionTemplate::New(ShadowFunctionCallback,
6340 Local<Value>(),
6341 signature));
6342 proto->Set(v8_str("x"), v8_num(12));
6343
6344 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
6345
6346 Local<Value> o = t->GetFunction()->NewInstance();
6347 context->Global()->Set(v8_str("__proto__"), o);
6348
6349 Local<Value> value =
6350 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
6351 CHECK(value->IsBoolean());
6352 CHECK(!value->BooleanValue());
6353
6354 value = Script::Compile(v8_str("x"))->Run();
6355 CHECK_EQ(12, value->Int32Value());
6356
6357 value = Script::Compile(v8_str("f()"))->Run();
6358 CHECK_EQ(42, value->Int32Value());
6359
6360 Script::Compile(v8_str("y = 42"))->Run();
6361 CHECK_EQ(1, shadow_y_setter_call_count);
6362 value = Script::Compile(v8_str("y"))->Run();
6363 CHECK_EQ(1, shadow_y_getter_call_count);
6364 CHECK_EQ(42, value->Int32Value());
6365}
6366
6367
6368THREADED_TEST(HiddenPrototype) {
6369 v8::HandleScope handle_scope;
6370 LocalContext context;
6371
6372 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6373 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6374 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6375 t1->SetHiddenPrototype(true);
6376 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6377 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6378 t2->SetHiddenPrototype(true);
6379 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6380 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6381 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6382
6383 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6384 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6385 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6386 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6387
6388 // Setting the prototype on an object skips hidden prototypes.
6389 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6390 o0->Set(v8_str("__proto__"), o1);
6391 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6392 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6393 o0->Set(v8_str("__proto__"), o2);
6394 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6395 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6396 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6397 o0->Set(v8_str("__proto__"), o3);
6398 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6399 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6400 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6401 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6402
6403 // Getting the prototype of o0 should get the first visible one
6404 // which is o3. Therefore, z should not be defined on the prototype
6405 // object.
6406 Local<Value> proto = o0->Get(v8_str("__proto__"));
6407 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006408 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00006409}
6410
6411
Andrei Popescu402d9372010-02-26 13:31:12 +00006412THREADED_TEST(SetPrototype) {
6413 v8::HandleScope handle_scope;
6414 LocalContext context;
6415
6416 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6417 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6418 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6419 t1->SetHiddenPrototype(true);
6420 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6421 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6422 t2->SetHiddenPrototype(true);
6423 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6424 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6425 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6426
6427 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6428 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6429 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6430 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6431
6432 // Setting the prototype on an object does not skip hidden prototypes.
6433 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6434 CHECK(o0->SetPrototype(o1));
6435 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6436 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6437 CHECK(o1->SetPrototype(o2));
6438 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6439 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6440 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6441 CHECK(o2->SetPrototype(o3));
6442 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6443 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6444 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6445 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6446
6447 // Getting the prototype of o0 should get the first visible one
6448 // which is o3. Therefore, z should not be defined on the prototype
6449 // object.
6450 Local<Value> proto = o0->Get(v8_str("__proto__"));
6451 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006452 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006453
6454 // However, Object::GetPrototype ignores hidden prototype.
6455 Local<Value> proto0 = o0->GetPrototype();
6456 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006457 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00006458
6459 Local<Value> proto1 = o1->GetPrototype();
6460 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006461 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00006462
6463 Local<Value> proto2 = o2->GetPrototype();
6464 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006465 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006466}
6467
6468
6469THREADED_TEST(SetPrototypeThrows) {
6470 v8::HandleScope handle_scope;
6471 LocalContext context;
6472
6473 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6474
6475 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
6476 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
6477
6478 CHECK(o0->SetPrototype(o1));
6479 // If setting the prototype leads to the cycle, SetPrototype should
6480 // return false and keep VM in sane state.
6481 v8::TryCatch try_catch;
6482 CHECK(!o1->SetPrototype(o0));
6483 CHECK(!try_catch.HasCaught());
6484 ASSERT(!i::Top::has_pending_exception());
6485
6486 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
6487}
6488
6489
Steve Blocka7e24c12009-10-30 11:49:00 +00006490THREADED_TEST(GetterSetterExceptions) {
6491 v8::HandleScope handle_scope;
6492 LocalContext context;
6493 CompileRun(
6494 "function Foo() { };"
6495 "function Throw() { throw 5; };"
6496 "var x = { };"
6497 "x.__defineSetter__('set', Throw);"
6498 "x.__defineGetter__('get', Throw);");
6499 Local<v8::Object> x =
6500 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
6501 v8::TryCatch try_catch;
6502 x->Set(v8_str("set"), v8::Integer::New(8));
6503 x->Get(v8_str("get"));
6504 x->Set(v8_str("set"), v8::Integer::New(8));
6505 x->Get(v8_str("get"));
6506 x->Set(v8_str("set"), v8::Integer::New(8));
6507 x->Get(v8_str("get"));
6508 x->Set(v8_str("set"), v8::Integer::New(8));
6509 x->Get(v8_str("get"));
6510}
6511
6512
6513THREADED_TEST(Constructor) {
6514 v8::HandleScope handle_scope;
6515 LocalContext context;
6516 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6517 templ->SetClassName(v8_str("Fun"));
6518 Local<Function> cons = templ->GetFunction();
6519 context->Global()->Set(v8_str("Fun"), cons);
6520 Local<v8::Object> inst = cons->NewInstance();
6521 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
6522 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
6523 CHECK(value->BooleanValue());
6524}
6525
6526THREADED_TEST(FunctionDescriptorException) {
6527 v8::HandleScope handle_scope;
6528 LocalContext context;
6529 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6530 templ->SetClassName(v8_str("Fun"));
6531 Local<Function> cons = templ->GetFunction();
6532 context->Global()->Set(v8_str("Fun"), cons);
6533 Local<Value> value = CompileRun(
6534 "function test() {"
6535 " try {"
6536 " (new Fun()).blah()"
6537 " } catch (e) {"
6538 " var str = String(e);"
6539 " if (str.indexOf('TypeError') == -1) return 1;"
6540 " if (str.indexOf('[object Fun]') != -1) return 2;"
Steve Block1e0659c2011-05-24 12:43:12 +01006541 " if (str.indexOf('#<Fun>') == -1) return 3;"
Steve Blocka7e24c12009-10-30 11:49:00 +00006542 " return 0;"
6543 " }"
6544 " return 4;"
6545 "}"
6546 "test();");
6547 CHECK_EQ(0, value->Int32Value());
6548}
6549
6550
6551THREADED_TEST(EvalAliasedDynamic) {
6552 v8::HandleScope scope;
6553 LocalContext current;
6554
6555 // Tests where aliased eval can only be resolved dynamically.
6556 Local<Script> script =
6557 Script::Compile(v8_str("function f(x) { "
6558 " var foo = 2;"
6559 " with (x) { return eval('foo'); }"
6560 "}"
6561 "foo = 0;"
6562 "result1 = f(new Object());"
6563 "result2 = f(this);"
6564 "var x = new Object();"
6565 "x.eval = function(x) { return 1; };"
6566 "result3 = f(x);"));
6567 script->Run();
6568 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
6569 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
6570 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
6571
6572 v8::TryCatch try_catch;
6573 script =
6574 Script::Compile(v8_str("function f(x) { "
6575 " var bar = 2;"
6576 " with (x) { return eval('bar'); }"
6577 "}"
6578 "f(this)"));
6579 script->Run();
6580 CHECK(try_catch.HasCaught());
6581 try_catch.Reset();
6582}
6583
6584
6585THREADED_TEST(CrossEval) {
6586 v8::HandleScope scope;
6587 LocalContext other;
6588 LocalContext current;
6589
6590 Local<String> token = v8_str("<security token>");
6591 other->SetSecurityToken(token);
6592 current->SetSecurityToken(token);
6593
6594 // Setup reference from current to other.
6595 current->Global()->Set(v8_str("other"), other->Global());
6596
6597 // Check that new variables are introduced in other context.
6598 Local<Script> script =
6599 Script::Compile(v8_str("other.eval('var foo = 1234')"));
6600 script->Run();
6601 Local<Value> foo = other->Global()->Get(v8_str("foo"));
6602 CHECK_EQ(1234, foo->Int32Value());
6603 CHECK(!current->Global()->Has(v8_str("foo")));
6604
6605 // Check that writing to non-existing properties introduces them in
6606 // the other context.
6607 script =
6608 Script::Compile(v8_str("other.eval('na = 1234')"));
6609 script->Run();
6610 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
6611 CHECK(!current->Global()->Has(v8_str("na")));
6612
6613 // Check that global variables in current context are not visible in other
6614 // context.
6615 v8::TryCatch try_catch;
6616 script =
6617 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
6618 Local<Value> result = script->Run();
6619 CHECK(try_catch.HasCaught());
6620 try_catch.Reset();
6621
6622 // Check that local variables in current context are not visible in other
6623 // context.
6624 script =
6625 Script::Compile(v8_str("(function() { "
6626 " var baz = 87;"
6627 " return other.eval('baz');"
6628 "})();"));
6629 result = script->Run();
6630 CHECK(try_catch.HasCaught());
6631 try_catch.Reset();
6632
6633 // Check that global variables in the other environment are visible
6634 // when evaluting code.
6635 other->Global()->Set(v8_str("bis"), v8_num(1234));
6636 script = Script::Compile(v8_str("other.eval('bis')"));
6637 CHECK_EQ(1234, script->Run()->Int32Value());
6638 CHECK(!try_catch.HasCaught());
6639
6640 // Check that the 'this' pointer points to the global object evaluating
6641 // code.
6642 other->Global()->Set(v8_str("t"), other->Global());
6643 script = Script::Compile(v8_str("other.eval('this == t')"));
6644 result = script->Run();
6645 CHECK(result->IsTrue());
6646 CHECK(!try_catch.HasCaught());
6647
6648 // Check that variables introduced in with-statement are not visible in
6649 // other context.
6650 script =
6651 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
6652 result = script->Run();
6653 CHECK(try_catch.HasCaught());
6654 try_catch.Reset();
6655
6656 // Check that you cannot use 'eval.call' with another object than the
6657 // current global object.
6658 script =
6659 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
6660 result = script->Run();
6661 CHECK(try_catch.HasCaught());
6662}
6663
6664
6665// Test that calling eval in a context which has been detached from
6666// its global throws an exception. This behavior is consistent with
6667// other JavaScript implementations.
6668THREADED_TEST(EvalInDetachedGlobal) {
6669 v8::HandleScope scope;
6670
6671 v8::Persistent<Context> context0 = Context::New();
6672 v8::Persistent<Context> context1 = Context::New();
6673
6674 // Setup function in context0 that uses eval from context0.
6675 context0->Enter();
6676 v8::Handle<v8::Value> fun =
6677 CompileRun("var x = 42;"
6678 "(function() {"
6679 " var e = eval;"
6680 " return function(s) { return e(s); }"
6681 "})()");
6682 context0->Exit();
6683
6684 // Put the function into context1 and call it before and after
6685 // detaching the global. Before detaching, the call succeeds and
6686 // after detaching and exception is thrown.
6687 context1->Enter();
6688 context1->Global()->Set(v8_str("fun"), fun);
6689 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
6690 CHECK_EQ(42, x_value->Int32Value());
6691 context0->DetachGlobal();
6692 v8::TryCatch catcher;
6693 x_value = CompileRun("fun('x')");
6694 CHECK(x_value.IsEmpty());
6695 CHECK(catcher.HasCaught());
6696 context1->Exit();
6697
6698 context1.Dispose();
6699 context0.Dispose();
6700}
6701
6702
6703THREADED_TEST(CrossLazyLoad) {
6704 v8::HandleScope scope;
6705 LocalContext other;
6706 LocalContext current;
6707
6708 Local<String> token = v8_str("<security token>");
6709 other->SetSecurityToken(token);
6710 current->SetSecurityToken(token);
6711
6712 // Setup reference from current to other.
6713 current->Global()->Set(v8_str("other"), other->Global());
6714
6715 // Trigger lazy loading in other context.
6716 Local<Script> script =
6717 Script::Compile(v8_str("other.eval('new Date(42)')"));
6718 Local<Value> value = script->Run();
6719 CHECK_EQ(42.0, value->NumberValue());
6720}
6721
6722
6723static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006724 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006725 if (args.IsConstructCall()) {
6726 if (args[0]->IsInt32()) {
6727 return v8_num(-args[0]->Int32Value());
6728 }
6729 }
6730
6731 return args[0];
6732}
6733
6734
6735// Test that a call handler can be set for objects which will allow
6736// non-function objects created through the API to be called as
6737// functions.
6738THREADED_TEST(CallAsFunction) {
6739 v8::HandleScope scope;
6740 LocalContext context;
6741
6742 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6743 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6744 instance_template->SetCallAsFunctionHandler(call_as_function);
6745 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6746 context->Global()->Set(v8_str("obj"), instance);
6747 v8::TryCatch try_catch;
6748 Local<Value> value;
6749 CHECK(!try_catch.HasCaught());
6750
6751 value = CompileRun("obj(42)");
6752 CHECK(!try_catch.HasCaught());
6753 CHECK_EQ(42, value->Int32Value());
6754
6755 value = CompileRun("(function(o){return o(49)})(obj)");
6756 CHECK(!try_catch.HasCaught());
6757 CHECK_EQ(49, value->Int32Value());
6758
6759 // test special case of call as function
6760 value = CompileRun("[obj]['0'](45)");
6761 CHECK(!try_catch.HasCaught());
6762 CHECK_EQ(45, value->Int32Value());
6763
6764 value = CompileRun("obj.call = Function.prototype.call;"
6765 "obj.call(null, 87)");
6766 CHECK(!try_catch.HasCaught());
6767 CHECK_EQ(87, value->Int32Value());
6768
6769 // Regression tests for bug #1116356: Calling call through call/apply
6770 // must work for non-function receivers.
6771 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6772 value = CompileRun(apply_99);
6773 CHECK(!try_catch.HasCaught());
6774 CHECK_EQ(99, value->Int32Value());
6775
6776 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6777 value = CompileRun(call_17);
6778 CHECK(!try_catch.HasCaught());
6779 CHECK_EQ(17, value->Int32Value());
6780
6781 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006782 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006783 value = CompileRun("new obj(43)");
6784 CHECK(!try_catch.HasCaught());
6785 CHECK_EQ(-43, value->Int32Value());
6786}
6787
6788
6789static int CountHandles() {
6790 return v8::HandleScope::NumberOfHandles();
6791}
6792
6793
6794static int Recurse(int depth, int iterations) {
6795 v8::HandleScope scope;
6796 if (depth == 0) return CountHandles();
6797 for (int i = 0; i < iterations; i++) {
6798 Local<v8::Number> n = v8::Integer::New(42);
6799 }
6800 return Recurse(depth - 1, iterations);
6801}
6802
6803
6804THREADED_TEST(HandleIteration) {
6805 static const int kIterations = 500;
6806 static const int kNesting = 200;
6807 CHECK_EQ(0, CountHandles());
6808 {
6809 v8::HandleScope scope1;
6810 CHECK_EQ(0, CountHandles());
6811 for (int i = 0; i < kIterations; i++) {
6812 Local<v8::Number> n = v8::Integer::New(42);
6813 CHECK_EQ(i + 1, CountHandles());
6814 }
6815
6816 CHECK_EQ(kIterations, CountHandles());
6817 {
6818 v8::HandleScope scope2;
6819 for (int j = 0; j < kIterations; j++) {
6820 Local<v8::Number> n = v8::Integer::New(42);
6821 CHECK_EQ(j + 1 + kIterations, CountHandles());
6822 }
6823 }
6824 CHECK_EQ(kIterations, CountHandles());
6825 }
6826 CHECK_EQ(0, CountHandles());
6827 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6828}
6829
6830
6831static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6832 Local<String> name,
6833 const AccessorInfo& info) {
6834 ApiTestFuzzer::Fuzz();
6835 return v8::Handle<Value>();
6836}
6837
6838
6839THREADED_TEST(InterceptorHasOwnProperty) {
6840 v8::HandleScope scope;
6841 LocalContext context;
6842 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6843 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6844 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6845 Local<Function> function = fun_templ->GetFunction();
6846 context->Global()->Set(v8_str("constructor"), function);
6847 v8::Handle<Value> value = CompileRun(
6848 "var o = new constructor();"
6849 "o.hasOwnProperty('ostehaps');");
6850 CHECK_EQ(false, value->BooleanValue());
6851 value = CompileRun(
6852 "o.ostehaps = 42;"
6853 "o.hasOwnProperty('ostehaps');");
6854 CHECK_EQ(true, value->BooleanValue());
6855 value = CompileRun(
6856 "var p = new constructor();"
6857 "p.hasOwnProperty('ostehaps');");
6858 CHECK_EQ(false, value->BooleanValue());
6859}
6860
6861
6862static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6863 Local<String> name,
6864 const AccessorInfo& info) {
6865 ApiTestFuzzer::Fuzz();
6866 i::Heap::CollectAllGarbage(false);
6867 return v8::Handle<Value>();
6868}
6869
6870
6871THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6872 v8::HandleScope scope;
6873 LocalContext context;
6874 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6875 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6876 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6877 Local<Function> function = fun_templ->GetFunction();
6878 context->Global()->Set(v8_str("constructor"), function);
6879 // Let's first make some stuff so we can be sure to get a good GC.
6880 CompileRun(
6881 "function makestr(size) {"
6882 " switch (size) {"
6883 " case 1: return 'f';"
6884 " case 2: return 'fo';"
6885 " case 3: return 'foo';"
6886 " }"
6887 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6888 "}"
6889 "var x = makestr(12345);"
6890 "x = makestr(31415);"
6891 "x = makestr(23456);");
6892 v8::Handle<Value> value = CompileRun(
6893 "var o = new constructor();"
6894 "o.__proto__ = new String(x);"
6895 "o.hasOwnProperty('ostehaps');");
6896 CHECK_EQ(false, value->BooleanValue());
6897}
6898
6899
6900typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6901 const AccessorInfo& info);
6902
6903
6904static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6905 const char* source,
6906 int expected) {
6907 v8::HandleScope scope;
6908 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08006909 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00006910 LocalContext context;
6911 context->Global()->Set(v8_str("o"), templ->NewInstance());
6912 v8::Handle<Value> value = CompileRun(source);
6913 CHECK_EQ(expected, value->Int32Value());
6914}
6915
6916
6917static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6918 const AccessorInfo& info) {
6919 ApiTestFuzzer::Fuzz();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08006920 CHECK_EQ(v8_str("data"), info.Data());
6921 CHECK_EQ(v8_str("x"), name);
Steve Blocka7e24c12009-10-30 11:49:00 +00006922 return v8::Integer::New(42);
6923}
6924
6925
6926// This test should hit the load IC for the interceptor case.
6927THREADED_TEST(InterceptorLoadIC) {
6928 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6929 "var result = 0;"
6930 "for (var i = 0; i < 1000; i++) {"
6931 " result = o.x;"
6932 "}",
6933 42);
6934}
6935
6936
6937// Below go several tests which verify that JITing for various
6938// configurations of interceptor and explicit fields works fine
6939// (those cases are special cased to get better performance).
6940
6941static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6942 const AccessorInfo& info) {
6943 ApiTestFuzzer::Fuzz();
6944 return v8_str("x")->Equals(name)
6945 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6946}
6947
6948
6949THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6950 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6951 "var result = 0;"
6952 "o.y = 239;"
6953 "for (var i = 0; i < 1000; i++) {"
6954 " result = o.y;"
6955 "}",
6956 239);
6957}
6958
6959
6960THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6961 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6962 "var result = 0;"
6963 "o.__proto__ = { 'y': 239 };"
6964 "for (var i = 0; i < 1000; i++) {"
6965 " result = o.y + o.x;"
6966 "}",
6967 239 + 42);
6968}
6969
6970
6971THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6972 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6973 "var result = 0;"
6974 "o.__proto__.y = 239;"
6975 "for (var i = 0; i < 1000; i++) {"
6976 " result = o.y + o.x;"
6977 "}",
6978 239 + 42);
6979}
6980
6981
6982THREADED_TEST(InterceptorLoadICUndefined) {
6983 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6984 "var result = 0;"
6985 "for (var i = 0; i < 1000; i++) {"
6986 " result = (o.y == undefined) ? 239 : 42;"
6987 "}",
6988 239);
6989}
6990
6991
6992THREADED_TEST(InterceptorLoadICWithOverride) {
6993 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6994 "fst = new Object(); fst.__proto__ = o;"
6995 "snd = new Object(); snd.__proto__ = fst;"
6996 "var result1 = 0;"
6997 "for (var i = 0; i < 1000; i++) {"
6998 " result1 = snd.x;"
6999 "}"
7000 "fst.x = 239;"
7001 "var result = 0;"
7002 "for (var i = 0; i < 1000; i++) {"
7003 " result = snd.x;"
7004 "}"
7005 "result + result1",
7006 239 + 42);
7007}
7008
7009
7010// Test the case when we stored field into
7011// a stub, but interceptor produced value on its own.
7012THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
7013 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7014 "proto = new Object();"
7015 "o.__proto__ = proto;"
7016 "proto.x = 239;"
7017 "for (var i = 0; i < 1000; i++) {"
7018 " o.x;"
7019 // Now it should be ICed and keep a reference to x defined on proto
7020 "}"
7021 "var result = 0;"
7022 "for (var i = 0; i < 1000; i++) {"
7023 " result += o.x;"
7024 "}"
7025 "result;",
7026 42 * 1000);
7027}
7028
7029
7030// Test the case when we stored field into
7031// a stub, but it got invalidated later on.
7032THREADED_TEST(InterceptorLoadICInvalidatedField) {
7033 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7034 "proto1 = new Object();"
7035 "proto2 = new Object();"
7036 "o.__proto__ = proto1;"
7037 "proto1.__proto__ = proto2;"
7038 "proto2.y = 239;"
7039 "for (var i = 0; i < 1000; i++) {"
7040 " o.y;"
7041 // Now it should be ICed and keep a reference to y defined on proto2
7042 "}"
7043 "proto1.y = 42;"
7044 "var result = 0;"
7045 "for (var i = 0; i < 1000; i++) {"
7046 " result += o.y;"
7047 "}"
7048 "result;",
7049 42 * 1000);
7050}
7051
7052
Steve Block6ded16b2010-05-10 14:33:55 +01007053static int interceptor_load_not_handled_calls = 0;
7054static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
7055 const AccessorInfo& info) {
7056 ++interceptor_load_not_handled_calls;
7057 return v8::Handle<v8::Value>();
7058}
7059
7060
7061// Test how post-interceptor lookups are done in the non-cacheable
7062// case: the interceptor should not be invoked during this lookup.
7063THREADED_TEST(InterceptorLoadICPostInterceptor) {
7064 interceptor_load_not_handled_calls = 0;
7065 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
7066 "receiver = new Object();"
7067 "receiver.__proto__ = o;"
7068 "proto = new Object();"
7069 "/* Make proto a slow-case object. */"
7070 "for (var i = 0; i < 1000; i++) {"
7071 " proto[\"xxxxxxxx\" + i] = [];"
7072 "}"
7073 "proto.x = 17;"
7074 "o.__proto__ = proto;"
7075 "var result = 0;"
7076 "for (var i = 0; i < 1000; i++) {"
7077 " result += receiver.x;"
7078 "}"
7079 "result;",
7080 17 * 1000);
7081 CHECK_EQ(1000, interceptor_load_not_handled_calls);
7082}
7083
7084
Steve Blocka7e24c12009-10-30 11:49:00 +00007085// Test the case when we stored field into
7086// a stub, but it got invalidated later on due to override on
7087// global object which is between interceptor and fields' holders.
7088THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
7089 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7090 "o.__proto__ = this;" // set a global to be a proto of o.
7091 "this.__proto__.y = 239;"
7092 "for (var i = 0; i < 10; i++) {"
7093 " if (o.y != 239) throw 'oops: ' + o.y;"
7094 // Now it should be ICed and keep a reference to y defined on field_holder.
7095 "}"
7096 "this.y = 42;" // Assign on a global.
7097 "var result = 0;"
7098 "for (var i = 0; i < 10; i++) {"
7099 " result += o.y;"
7100 "}"
7101 "result;",
7102 42 * 10);
7103}
7104
7105
Steve Blocka7e24c12009-10-30 11:49:00 +00007106static void SetOnThis(Local<String> name,
7107 Local<Value> value,
7108 const AccessorInfo& info) {
7109 info.This()->ForceSet(name, value);
7110}
7111
7112
7113THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
7114 v8::HandleScope scope;
7115 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7116 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7117 templ->SetAccessor(v8_str("y"), Return239);
7118 LocalContext context;
7119 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007120
7121 // Check the case when receiver and interceptor's holder
7122 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007123 v8::Handle<Value> value = CompileRun(
7124 "var result = 0;"
7125 "for (var i = 0; i < 7; i++) {"
7126 " result = o.y;"
7127 "}");
7128 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007129
7130 // Check the case when interceptor's holder is in proto chain
7131 // of receiver.
7132 value = CompileRun(
7133 "r = { __proto__: o };"
7134 "var result = 0;"
7135 "for (var i = 0; i < 7; i++) {"
7136 " result = r.y;"
7137 "}");
7138 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007139}
7140
7141
7142THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
7143 v8::HandleScope scope;
7144 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7145 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7146 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7147 templ_p->SetAccessor(v8_str("y"), Return239);
7148
7149 LocalContext context;
7150 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7151 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7152
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007153 // Check the case when receiver and interceptor's holder
7154 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007155 v8::Handle<Value> value = CompileRun(
7156 "o.__proto__ = p;"
7157 "var result = 0;"
7158 "for (var i = 0; i < 7; i++) {"
7159 " result = o.x + o.y;"
7160 "}");
7161 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007162
7163 // Check the case when interceptor's holder is in proto chain
7164 // of receiver.
7165 value = CompileRun(
7166 "r = { __proto__: o };"
7167 "var result = 0;"
7168 "for (var i = 0; i < 7; i++) {"
7169 " result = r.x + r.y;"
7170 "}");
7171 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007172}
7173
7174
7175THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
7176 v8::HandleScope scope;
7177 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7178 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7179 templ->SetAccessor(v8_str("y"), Return239);
7180
7181 LocalContext context;
7182 context->Global()->Set(v8_str("o"), templ->NewInstance());
7183
7184 v8::Handle<Value> value = CompileRun(
7185 "fst = new Object(); fst.__proto__ = o;"
7186 "snd = new Object(); snd.__proto__ = fst;"
7187 "var result1 = 0;"
7188 "for (var i = 0; i < 7; i++) {"
7189 " result1 = snd.x;"
7190 "}"
7191 "fst.x = 239;"
7192 "var result = 0;"
7193 "for (var i = 0; i < 7; i++) {"
7194 " result = snd.x;"
7195 "}"
7196 "result + result1");
7197 CHECK_EQ(239 + 42, value->Int32Value());
7198}
7199
7200
7201// Test the case when we stored callback into
7202// a stub, but interceptor produced value on its own.
7203THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
7204 v8::HandleScope scope;
7205 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7206 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7207 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7208 templ_p->SetAccessor(v8_str("y"), Return239);
7209
7210 LocalContext context;
7211 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7212 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7213
7214 v8::Handle<Value> value = CompileRun(
7215 "o.__proto__ = p;"
7216 "for (var i = 0; i < 7; i++) {"
7217 " o.x;"
7218 // Now it should be ICed and keep a reference to x defined on p
7219 "}"
7220 "var result = 0;"
7221 "for (var i = 0; i < 7; i++) {"
7222 " result += o.x;"
7223 "}"
7224 "result");
7225 CHECK_EQ(42 * 7, value->Int32Value());
7226}
7227
7228
7229// Test the case when we stored callback into
7230// a stub, but it got invalidated later on.
7231THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
7232 v8::HandleScope scope;
7233 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7234 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7235 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7236 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7237
7238 LocalContext context;
7239 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7240 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7241
7242 v8::Handle<Value> value = CompileRun(
7243 "inbetween = new Object();"
7244 "o.__proto__ = inbetween;"
7245 "inbetween.__proto__ = p;"
7246 "for (var i = 0; i < 10; i++) {"
7247 " o.y;"
7248 // Now it should be ICed and keep a reference to y defined on p
7249 "}"
7250 "inbetween.y = 42;"
7251 "var result = 0;"
7252 "for (var i = 0; i < 10; i++) {"
7253 " result += o.y;"
7254 "}"
7255 "result");
7256 CHECK_EQ(42 * 10, value->Int32Value());
7257}
7258
7259
7260// Test the case when we stored callback into
7261// a stub, but it got invalidated later on due to override on
7262// global object which is between interceptor and callbacks' holders.
7263THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
7264 v8::HandleScope scope;
7265 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7266 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7267 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7268 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7269
7270 LocalContext context;
7271 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7272 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7273
7274 v8::Handle<Value> value = CompileRun(
7275 "o.__proto__ = this;"
7276 "this.__proto__ = p;"
7277 "for (var i = 0; i < 10; i++) {"
7278 " if (o.y != 239) throw 'oops: ' + o.y;"
7279 // Now it should be ICed and keep a reference to y defined on p
7280 "}"
7281 "this.y = 42;"
7282 "var result = 0;"
7283 "for (var i = 0; i < 10; i++) {"
7284 " result += o.y;"
7285 "}"
7286 "result");
7287 CHECK_EQ(42 * 10, value->Int32Value());
7288}
7289
7290
7291static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
7292 const AccessorInfo& info) {
7293 ApiTestFuzzer::Fuzz();
7294 CHECK(v8_str("x")->Equals(name));
7295 return v8::Integer::New(0);
7296}
7297
7298
7299THREADED_TEST(InterceptorReturningZero) {
7300 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
7301 "o.x == undefined ? 1 : 0",
7302 0);
7303}
7304
7305
7306static v8::Handle<Value> InterceptorStoreICSetter(
7307 Local<String> key, Local<Value> value, const AccessorInfo&) {
7308 CHECK(v8_str("x")->Equals(key));
7309 CHECK_EQ(42, value->Int32Value());
7310 return value;
7311}
7312
7313
7314// This test should hit the store IC for the interceptor case.
7315THREADED_TEST(InterceptorStoreIC) {
7316 v8::HandleScope scope;
7317 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7318 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007319 InterceptorStoreICSetter,
7320 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00007321 LocalContext context;
7322 context->Global()->Set(v8_str("o"), templ->NewInstance());
7323 v8::Handle<Value> value = CompileRun(
7324 "for (var i = 0; i < 1000; i++) {"
7325 " o.x = 42;"
7326 "}");
7327}
7328
7329
7330THREADED_TEST(InterceptorStoreICWithNoSetter) {
7331 v8::HandleScope scope;
7332 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7333 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7334 LocalContext context;
7335 context->Global()->Set(v8_str("o"), templ->NewInstance());
7336 v8::Handle<Value> value = CompileRun(
7337 "for (var i = 0; i < 1000; i++) {"
7338 " o.y = 239;"
7339 "}"
7340 "42 + o.y");
7341 CHECK_EQ(239 + 42, value->Int32Value());
7342}
7343
7344
7345
7346
7347v8::Handle<Value> call_ic_function;
7348v8::Handle<Value> call_ic_function2;
7349v8::Handle<Value> call_ic_function3;
7350
7351static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
7352 const AccessorInfo& info) {
7353 ApiTestFuzzer::Fuzz();
7354 CHECK(v8_str("x")->Equals(name));
7355 return call_ic_function;
7356}
7357
7358
7359// This test should hit the call IC for the interceptor case.
7360THREADED_TEST(InterceptorCallIC) {
7361 v8::HandleScope scope;
7362 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7363 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
7364 LocalContext context;
7365 context->Global()->Set(v8_str("o"), templ->NewInstance());
7366 call_ic_function =
7367 v8_compile("function f(x) { return x + 1; }; f")->Run();
7368 v8::Handle<Value> value = CompileRun(
7369 "var result = 0;"
7370 "for (var i = 0; i < 1000; i++) {"
7371 " result = o.x(41);"
7372 "}");
7373 CHECK_EQ(42, value->Int32Value());
7374}
7375
7376
7377// This test checks that if interceptor doesn't provide
7378// a value, we can fetch regular value.
7379THREADED_TEST(InterceptorCallICSeesOthers) {
7380 v8::HandleScope scope;
7381 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7382 templ->SetNamedPropertyHandler(NoBlockGetterX);
7383 LocalContext context;
7384 context->Global()->Set(v8_str("o"), templ->NewInstance());
7385 v8::Handle<Value> value = CompileRun(
7386 "o.x = function f(x) { return x + 1; };"
7387 "var result = 0;"
7388 "for (var i = 0; i < 7; i++) {"
7389 " result = o.x(41);"
7390 "}");
7391 CHECK_EQ(42, value->Int32Value());
7392}
7393
7394
7395static v8::Handle<Value> call_ic_function4;
7396static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
7397 const AccessorInfo& info) {
7398 ApiTestFuzzer::Fuzz();
7399 CHECK(v8_str("x")->Equals(name));
7400 return call_ic_function4;
7401}
7402
7403
7404// This test checks that if interceptor provides a function,
7405// even if we cached shadowed variant, interceptor's function
7406// is invoked
7407THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
7408 v8::HandleScope scope;
7409 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7410 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
7411 LocalContext context;
7412 context->Global()->Set(v8_str("o"), templ->NewInstance());
7413 call_ic_function4 =
7414 v8_compile("function f(x) { return x - 1; }; f")->Run();
7415 v8::Handle<Value> value = CompileRun(
7416 "o.__proto__.x = function(x) { return x + 1; };"
7417 "var result = 0;"
7418 "for (var i = 0; i < 1000; i++) {"
7419 " result = o.x(42);"
7420 "}");
7421 CHECK_EQ(41, value->Int32Value());
7422}
7423
7424
7425// Test the case when we stored cacheable lookup into
7426// a stub, but it got invalidated later on
7427THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
7428 v8::HandleScope scope;
7429 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7430 templ->SetNamedPropertyHandler(NoBlockGetterX);
7431 LocalContext context;
7432 context->Global()->Set(v8_str("o"), templ->NewInstance());
7433 v8::Handle<Value> value = CompileRun(
7434 "proto1 = new Object();"
7435 "proto2 = new Object();"
7436 "o.__proto__ = proto1;"
7437 "proto1.__proto__ = proto2;"
7438 "proto2.y = function(x) { return x + 1; };"
7439 // Invoke it many times to compile a stub
7440 "for (var i = 0; i < 7; i++) {"
7441 " o.y(42);"
7442 "}"
7443 "proto1.y = function(x) { return x - 1; };"
7444 "var result = 0;"
7445 "for (var i = 0; i < 7; i++) {"
7446 " result += o.y(42);"
7447 "}");
7448 CHECK_EQ(41 * 7, value->Int32Value());
7449}
7450
7451
7452static v8::Handle<Value> call_ic_function5;
7453static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
7454 const AccessorInfo& info) {
7455 ApiTestFuzzer::Fuzz();
7456 if (v8_str("x")->Equals(name))
7457 return call_ic_function5;
7458 else
7459 return Local<Value>();
7460}
7461
7462
7463// This test checks that if interceptor doesn't provide a function,
7464// cached constant function is used
7465THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
7466 v8::HandleScope scope;
7467 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7468 templ->SetNamedPropertyHandler(NoBlockGetterX);
7469 LocalContext context;
7470 context->Global()->Set(v8_str("o"), templ->NewInstance());
7471 v8::Handle<Value> value = CompileRun(
7472 "function inc(x) { return x + 1; };"
7473 "inc(1);"
7474 "o.x = inc;"
7475 "var result = 0;"
7476 "for (var i = 0; i < 1000; i++) {"
7477 " result = o.x(42);"
7478 "}");
7479 CHECK_EQ(43, value->Int32Value());
7480}
7481
7482
7483// This test checks that if interceptor provides a function,
7484// even if we cached constant function, interceptor's function
7485// is invoked
7486THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
7487 v8::HandleScope scope;
7488 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7489 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
7490 LocalContext context;
7491 context->Global()->Set(v8_str("o"), templ->NewInstance());
7492 call_ic_function5 =
7493 v8_compile("function f(x) { return x - 1; }; f")->Run();
7494 v8::Handle<Value> value = CompileRun(
7495 "function inc(x) { return x + 1; };"
7496 "inc(1);"
7497 "o.x = inc;"
7498 "var result = 0;"
7499 "for (var i = 0; i < 1000; i++) {"
7500 " result = o.x(42);"
7501 "}");
7502 CHECK_EQ(41, value->Int32Value());
7503}
7504
7505
7506// Test the case when we stored constant function into
7507// a stub, but it got invalidated later on
7508THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
7509 v8::HandleScope scope;
7510 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7511 templ->SetNamedPropertyHandler(NoBlockGetterX);
7512 LocalContext context;
7513 context->Global()->Set(v8_str("o"), templ->NewInstance());
7514 v8::Handle<Value> value = CompileRun(
7515 "function inc(x) { return x + 1; };"
7516 "inc(1);"
7517 "proto1 = new Object();"
7518 "proto2 = new Object();"
7519 "o.__proto__ = proto1;"
7520 "proto1.__proto__ = proto2;"
7521 "proto2.y = inc;"
7522 // Invoke it many times to compile a stub
7523 "for (var i = 0; i < 7; i++) {"
7524 " o.y(42);"
7525 "}"
7526 "proto1.y = function(x) { return x - 1; };"
7527 "var result = 0;"
7528 "for (var i = 0; i < 7; i++) {"
7529 " result += o.y(42);"
7530 "}");
7531 CHECK_EQ(41 * 7, value->Int32Value());
7532}
7533
7534
7535// Test the case when we stored constant function into
7536// a stub, but it got invalidated later on due to override on
7537// global object which is between interceptor and constant function' holders.
7538THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
7539 v8::HandleScope scope;
7540 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7541 templ->SetNamedPropertyHandler(NoBlockGetterX);
7542 LocalContext context;
7543 context->Global()->Set(v8_str("o"), templ->NewInstance());
7544 v8::Handle<Value> value = CompileRun(
7545 "function inc(x) { return x + 1; };"
7546 "inc(1);"
7547 "o.__proto__ = this;"
7548 "this.__proto__.y = inc;"
7549 // Invoke it many times to compile a stub
7550 "for (var i = 0; i < 7; i++) {"
7551 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
7552 "}"
7553 "this.y = function(x) { return x - 1; };"
7554 "var result = 0;"
7555 "for (var i = 0; i < 7; i++) {"
7556 " result += o.y(42);"
7557 "}");
7558 CHECK_EQ(41 * 7, value->Int32Value());
7559}
7560
7561
Leon Clarke4515c472010-02-03 11:58:03 +00007562// Test the case when actual function to call sits on global object.
7563THREADED_TEST(InterceptorCallICCachedFromGlobal) {
7564 v8::HandleScope scope;
7565 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7566 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7567
7568 LocalContext context;
7569 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7570
7571 v8::Handle<Value> value = CompileRun(
7572 "try {"
7573 " o.__proto__ = this;"
7574 " for (var i = 0; i < 10; i++) {"
7575 " var v = o.parseFloat('239');"
7576 " if (v != 239) throw v;"
7577 // Now it should be ICed and keep a reference to parseFloat.
7578 " }"
7579 " var result = 0;"
7580 " for (var i = 0; i < 10; i++) {"
7581 " result += o.parseFloat('239');"
7582 " }"
7583 " result"
7584 "} catch(e) {"
7585 " e"
7586 "};");
7587 CHECK_EQ(239 * 10, value->Int32Value());
7588}
7589
Andrei Popescu402d9372010-02-26 13:31:12 +00007590static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
7591 const AccessorInfo& info) {
7592 ApiTestFuzzer::Fuzz();
7593 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
7594 ++(*call_count);
7595 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01007596 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00007597 }
7598 return v8::Handle<Value>();
7599}
7600
7601static v8::Handle<Value> FastApiCallback_TrivialSignature(
7602 const v8::Arguments& args) {
7603 ApiTestFuzzer::Fuzz();
7604 CHECK_EQ(args.This(), args.Holder());
7605 CHECK(args.Data()->Equals(v8_str("method_data")));
7606 return v8::Integer::New(args[0]->Int32Value() + 1);
7607}
7608
7609static v8::Handle<Value> FastApiCallback_SimpleSignature(
7610 const v8::Arguments& args) {
7611 ApiTestFuzzer::Fuzz();
7612 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
7613 CHECK(args.Data()->Equals(v8_str("method_data")));
7614 // Note, we're using HasRealNamedProperty instead of Has to avoid
7615 // invoking the interceptor again.
7616 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
7617 return v8::Integer::New(args[0]->Int32Value() + 1);
7618}
7619
7620// Helper to maximize the odds of object moving.
7621static void GenerateSomeGarbage() {
7622 CompileRun(
7623 "var garbage;"
7624 "for (var i = 0; i < 1000; i++) {"
7625 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
7626 "}"
7627 "garbage = undefined;");
7628}
7629
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007630
Steve Block1e0659c2011-05-24 12:43:12 +01007631v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) {
7632 static int count = 0;
7633 if (count++ % 3 == 0) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007634 i::Heap::CollectAllGarbage(true); // This should move the stub
Steve Block1e0659c2011-05-24 12:43:12 +01007635 GenerateSomeGarbage(); // This should ensure the old stub memory is flushed
7636 }
7637 return v8::Handle<v8::Value>();
7638}
7639
7640
7641THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) {
7642 v8::HandleScope scope;
7643 LocalContext context;
7644 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
7645 nativeobject_templ->Set("callback",
7646 v8::FunctionTemplate::New(DirectApiCallback));
7647 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
7648 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
7649 // call the api function multiple times to ensure direct call stub creation.
7650 CompileRun(
7651 "function f() {"
7652 " for (var i = 1; i <= 30; i++) {"
7653 " nativeobject.callback();"
7654 " }"
7655 "}"
7656 "f();");
7657}
7658
7659
7660v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) {
7661 return v8::ThrowException(v8_str("g"));
7662}
7663
7664
7665THREADED_TEST(CallICFastApi_DirectCall_Throw) {
7666 v8::HandleScope scope;
7667 LocalContext context;
7668 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
7669 nativeobject_templ->Set("callback",
7670 v8::FunctionTemplate::New(ThrowingDirectApiCallback));
7671 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
7672 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
7673 // call the api function multiple times to ensure direct call stub creation.
7674 v8::Handle<Value> result = CompileRun(
7675 "var result = '';"
7676 "function f() {"
7677 " for (var i = 1; i <= 5; i++) {"
7678 " try { nativeobject.callback(); } catch (e) { result += e; }"
7679 " }"
7680 "}"
7681 "f(); result;");
7682 CHECK_EQ(v8_str("ggggg"), result);
7683}
7684
7685
Ben Murdoche0cee9b2011-05-25 10:26:03 +01007686v8::Handle<v8::Value> DirectGetterCallback(Local<String> name,
7687 const v8::AccessorInfo& info) {
7688 if (++p_getter_count % 3 == 0) {
7689 i::Heap::CollectAllGarbage(true);
7690 GenerateSomeGarbage();
7691 }
7692 return v8::Handle<v8::Value>();
7693}
7694
7695
7696THREADED_TEST(LoadICFastApi_DirectCall_GCMoveStub) {
7697 v8::HandleScope scope;
7698 LocalContext context;
7699 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New();
7700 obj->SetAccessor(v8_str("p1"), DirectGetterCallback);
7701 context->Global()->Set(v8_str("o1"), obj->NewInstance());
7702 p_getter_count = 0;
7703 CompileRun(
7704 "function f() {"
7705 " for (var i = 0; i < 30; i++) o1.p1;"
7706 "}"
7707 "f();");
7708 CHECK_EQ(30, p_getter_count);
7709}
7710
7711
7712v8::Handle<v8::Value> ThrowingDirectGetterCallback(
7713 Local<String> name, const v8::AccessorInfo& info) {
7714 return v8::ThrowException(v8_str("g"));
7715}
7716
7717
7718THREADED_TEST(LoadICFastApi_DirectCall_Throw) {
7719 v8::HandleScope scope;
7720 LocalContext context;
7721 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New();
7722 obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback);
7723 context->Global()->Set(v8_str("o1"), obj->NewInstance());
7724 v8::Handle<Value> result = CompileRun(
7725 "var result = '';"
7726 "for (var i = 0; i < 5; i++) {"
7727 " try { o1.p1; } catch (e) { result += e; }"
7728 "}"
7729 "result;");
7730 CHECK_EQ(v8_str("ggggg"), result);
7731}
7732
7733
Andrei Popescu402d9372010-02-26 13:31:12 +00007734THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
7735 int interceptor_call_count = 0;
7736 v8::HandleScope scope;
7737 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7738 v8::Handle<v8::FunctionTemplate> method_templ =
7739 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7740 v8_str("method_data"),
7741 v8::Handle<v8::Signature>());
7742 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7743 proto_templ->Set(v8_str("method"), method_templ);
7744 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7745 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7746 NULL, NULL, NULL, NULL,
7747 v8::External::Wrap(&interceptor_call_count));
7748 LocalContext context;
7749 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7750 GenerateSomeGarbage();
7751 context->Global()->Set(v8_str("o"), fun->NewInstance());
7752 v8::Handle<Value> value = CompileRun(
7753 "var result = 0;"
7754 "for (var i = 0; i < 100; i++) {"
7755 " result = o.method(41);"
7756 "}");
7757 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7758 CHECK_EQ(100, interceptor_call_count);
7759}
7760
7761THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
7762 int interceptor_call_count = 0;
7763 v8::HandleScope scope;
7764 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7765 v8::Handle<v8::FunctionTemplate> method_templ =
7766 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7767 v8_str("method_data"),
7768 v8::Signature::New(fun_templ));
7769 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7770 proto_templ->Set(v8_str("method"), method_templ);
7771 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7772 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7773 NULL, NULL, NULL, NULL,
7774 v8::External::Wrap(&interceptor_call_count));
7775 LocalContext context;
7776 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7777 GenerateSomeGarbage();
7778 context->Global()->Set(v8_str("o"), fun->NewInstance());
7779 v8::Handle<Value> value = CompileRun(
7780 "o.foo = 17;"
7781 "var receiver = {};"
7782 "receiver.__proto__ = o;"
7783 "var result = 0;"
7784 "for (var i = 0; i < 100; i++) {"
7785 " result = receiver.method(41);"
7786 "}");
7787 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7788 CHECK_EQ(100, interceptor_call_count);
7789}
7790
7791THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
7792 int interceptor_call_count = 0;
7793 v8::HandleScope scope;
7794 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7795 v8::Handle<v8::FunctionTemplate> method_templ =
7796 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7797 v8_str("method_data"),
7798 v8::Signature::New(fun_templ));
7799 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7800 proto_templ->Set(v8_str("method"), method_templ);
7801 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7802 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7803 NULL, NULL, NULL, NULL,
7804 v8::External::Wrap(&interceptor_call_count));
7805 LocalContext context;
7806 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7807 GenerateSomeGarbage();
7808 context->Global()->Set(v8_str("o"), fun->NewInstance());
7809 v8::Handle<Value> value = CompileRun(
7810 "o.foo = 17;"
7811 "var receiver = {};"
7812 "receiver.__proto__ = o;"
7813 "var result = 0;"
7814 "var saved_result = 0;"
7815 "for (var i = 0; i < 100; i++) {"
7816 " result = receiver.method(41);"
7817 " if (i == 50) {"
7818 " saved_result = result;"
7819 " receiver = {method: function(x) { return x - 1 }};"
7820 " }"
7821 "}");
7822 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7823 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7824 CHECK_GE(interceptor_call_count, 50);
7825}
7826
7827THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
7828 int interceptor_call_count = 0;
7829 v8::HandleScope scope;
7830 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7831 v8::Handle<v8::FunctionTemplate> method_templ =
7832 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7833 v8_str("method_data"),
7834 v8::Signature::New(fun_templ));
7835 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7836 proto_templ->Set(v8_str("method"), method_templ);
7837 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7838 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7839 NULL, NULL, NULL, NULL,
7840 v8::External::Wrap(&interceptor_call_count));
7841 LocalContext context;
7842 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7843 GenerateSomeGarbage();
7844 context->Global()->Set(v8_str("o"), fun->NewInstance());
7845 v8::Handle<Value> value = CompileRun(
7846 "o.foo = 17;"
7847 "var receiver = {};"
7848 "receiver.__proto__ = o;"
7849 "var result = 0;"
7850 "var saved_result = 0;"
7851 "for (var i = 0; i < 100; i++) {"
7852 " result = receiver.method(41);"
7853 " if (i == 50) {"
7854 " saved_result = result;"
7855 " o.method = function(x) { return x - 1 };"
7856 " }"
7857 "}");
7858 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7859 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7860 CHECK_GE(interceptor_call_count, 50);
7861}
7862
Steve Block6ded16b2010-05-10 14:33:55 +01007863THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7864 int interceptor_call_count = 0;
7865 v8::HandleScope scope;
7866 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7867 v8::Handle<v8::FunctionTemplate> method_templ =
7868 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7869 v8_str("method_data"),
7870 v8::Signature::New(fun_templ));
7871 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7872 proto_templ->Set(v8_str("method"), method_templ);
7873 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7874 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7875 NULL, NULL, NULL, NULL,
7876 v8::External::Wrap(&interceptor_call_count));
7877 LocalContext context;
7878 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7879 GenerateSomeGarbage();
7880 context->Global()->Set(v8_str("o"), fun->NewInstance());
7881 v8::TryCatch try_catch;
7882 v8::Handle<Value> value = CompileRun(
7883 "o.foo = 17;"
7884 "var receiver = {};"
7885 "receiver.__proto__ = o;"
7886 "var result = 0;"
7887 "var saved_result = 0;"
7888 "for (var i = 0; i < 100; i++) {"
7889 " result = receiver.method(41);"
7890 " if (i == 50) {"
7891 " saved_result = result;"
7892 " receiver = 333;"
7893 " }"
7894 "}");
7895 CHECK(try_catch.HasCaught());
7896 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7897 try_catch.Exception()->ToString());
7898 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7899 CHECK_GE(interceptor_call_count, 50);
7900}
7901
Andrei Popescu402d9372010-02-26 13:31:12 +00007902THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
7903 int interceptor_call_count = 0;
7904 v8::HandleScope scope;
7905 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7906 v8::Handle<v8::FunctionTemplate> method_templ =
7907 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7908 v8_str("method_data"),
7909 v8::Signature::New(fun_templ));
7910 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7911 proto_templ->Set(v8_str("method"), method_templ);
7912 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7913 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7914 NULL, NULL, NULL, NULL,
7915 v8::External::Wrap(&interceptor_call_count));
7916 LocalContext context;
7917 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7918 GenerateSomeGarbage();
7919 context->Global()->Set(v8_str("o"), fun->NewInstance());
7920 v8::TryCatch try_catch;
7921 v8::Handle<Value> value = CompileRun(
7922 "o.foo = 17;"
7923 "var receiver = {};"
7924 "receiver.__proto__ = o;"
7925 "var result = 0;"
7926 "var saved_result = 0;"
7927 "for (var i = 0; i < 100; i++) {"
7928 " result = receiver.method(41);"
7929 " if (i == 50) {"
7930 " saved_result = result;"
7931 " receiver = {method: receiver.method};"
7932 " }"
7933 "}");
7934 CHECK(try_catch.HasCaught());
7935 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7936 try_catch.Exception()->ToString());
7937 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7938 CHECK_GE(interceptor_call_count, 50);
7939}
7940
7941THREADED_TEST(CallICFastApi_TrivialSignature) {
7942 v8::HandleScope scope;
7943 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7944 v8::Handle<v8::FunctionTemplate> method_templ =
7945 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7946 v8_str("method_data"),
7947 v8::Handle<v8::Signature>());
7948 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7949 proto_templ->Set(v8_str("method"), method_templ);
7950 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7951 LocalContext context;
7952 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7953 GenerateSomeGarbage();
7954 context->Global()->Set(v8_str("o"), fun->NewInstance());
7955 v8::Handle<Value> value = CompileRun(
7956 "var result = 0;"
7957 "for (var i = 0; i < 100; i++) {"
7958 " result = o.method(41);"
7959 "}");
7960
7961 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7962}
7963
7964THREADED_TEST(CallICFastApi_SimpleSignature) {
7965 v8::HandleScope scope;
7966 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7967 v8::Handle<v8::FunctionTemplate> method_templ =
7968 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7969 v8_str("method_data"),
7970 v8::Signature::New(fun_templ));
7971 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7972 proto_templ->Set(v8_str("method"), method_templ);
7973 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7974 LocalContext context;
7975 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7976 GenerateSomeGarbage();
7977 context->Global()->Set(v8_str("o"), fun->NewInstance());
7978 v8::Handle<Value> value = CompileRun(
7979 "o.foo = 17;"
7980 "var receiver = {};"
7981 "receiver.__proto__ = o;"
7982 "var result = 0;"
7983 "for (var i = 0; i < 100; i++) {"
7984 " result = receiver.method(41);"
7985 "}");
7986
7987 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7988}
7989
Steve Block6ded16b2010-05-10 14:33:55 +01007990THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007991 v8::HandleScope scope;
7992 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7993 v8::Handle<v8::FunctionTemplate> method_templ =
7994 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7995 v8_str("method_data"),
7996 v8::Signature::New(fun_templ));
7997 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7998 proto_templ->Set(v8_str("method"), method_templ);
7999 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8000 LocalContext context;
8001 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8002 GenerateSomeGarbage();
8003 context->Global()->Set(v8_str("o"), fun->NewInstance());
8004 v8::Handle<Value> value = CompileRun(
8005 "o.foo = 17;"
8006 "var receiver = {};"
8007 "receiver.__proto__ = o;"
8008 "var result = 0;"
8009 "var saved_result = 0;"
8010 "for (var i = 0; i < 100; i++) {"
8011 " result = receiver.method(41);"
8012 " if (i == 50) {"
8013 " saved_result = result;"
8014 " receiver = {method: function(x) { return x - 1 }};"
8015 " }"
8016 "}");
8017 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
8018 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8019}
8020
Steve Block6ded16b2010-05-10 14:33:55 +01008021THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
8022 v8::HandleScope scope;
8023 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
8024 v8::Handle<v8::FunctionTemplate> method_templ =
8025 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
8026 v8_str("method_data"),
8027 v8::Signature::New(fun_templ));
8028 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
8029 proto_templ->Set(v8_str("method"), method_templ);
8030 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
8031 LocalContext context;
8032 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
8033 GenerateSomeGarbage();
8034 context->Global()->Set(v8_str("o"), fun->NewInstance());
8035 v8::TryCatch try_catch;
8036 v8::Handle<Value> value = CompileRun(
8037 "o.foo = 17;"
8038 "var receiver = {};"
8039 "receiver.__proto__ = o;"
8040 "var result = 0;"
8041 "var saved_result = 0;"
8042 "for (var i = 0; i < 100; i++) {"
8043 " result = receiver.method(41);"
8044 " if (i == 50) {"
8045 " saved_result = result;"
8046 " receiver = 333;"
8047 " }"
8048 "}");
8049 CHECK(try_catch.HasCaught());
8050 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
8051 try_catch.Exception()->ToString());
8052 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8053}
8054
Leon Clarke4515c472010-02-03 11:58:03 +00008055
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008056v8::Handle<Value> keyed_call_ic_function;
8057
8058static v8::Handle<Value> InterceptorKeyedCallICGetter(
8059 Local<String> name, const AccessorInfo& info) {
8060 ApiTestFuzzer::Fuzz();
8061 if (v8_str("x")->Equals(name)) {
8062 return keyed_call_ic_function;
8063 }
8064 return v8::Handle<Value>();
8065}
8066
8067
8068// Test the case when we stored cacheable lookup into
8069// a stub, but the function name changed (to another cacheable function).
8070THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
8071 v8::HandleScope scope;
8072 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8073 templ->SetNamedPropertyHandler(NoBlockGetterX);
8074 LocalContext context;
8075 context->Global()->Set(v8_str("o"), templ->NewInstance());
8076 v8::Handle<Value> value = CompileRun(
8077 "proto = new Object();"
8078 "proto.y = function(x) { return x + 1; };"
8079 "proto.z = function(x) { return x - 1; };"
8080 "o.__proto__ = proto;"
8081 "var result = 0;"
8082 "var method = 'y';"
8083 "for (var i = 0; i < 10; i++) {"
8084 " if (i == 5) { method = 'z'; };"
8085 " result += o[method](41);"
8086 "}");
8087 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8088}
8089
8090
8091// Test the case when we stored cacheable lookup into
8092// a stub, but the function name changed (and the new function is present
8093// both before and after the interceptor in the prototype chain).
8094THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
8095 v8::HandleScope scope;
8096 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8097 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
8098 LocalContext context;
8099 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
8100 keyed_call_ic_function =
8101 v8_compile("function f(x) { return x - 1; }; f")->Run();
8102 v8::Handle<Value> value = CompileRun(
8103 "o = new Object();"
8104 "proto2 = new Object();"
8105 "o.y = function(x) { return x + 1; };"
8106 "proto2.y = function(x) { return x + 2; };"
8107 "o.__proto__ = proto1;"
8108 "proto1.__proto__ = proto2;"
8109 "var result = 0;"
8110 "var method = 'x';"
8111 "for (var i = 0; i < 10; i++) {"
8112 " if (i == 5) { method = 'y'; };"
8113 " result += o[method](41);"
8114 "}");
8115 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8116}
8117
8118
8119// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
8120// on the global object.
8121THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
8122 v8::HandleScope scope;
8123 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8124 templ->SetNamedPropertyHandler(NoBlockGetterX);
8125 LocalContext context;
8126 context->Global()->Set(v8_str("o"), templ->NewInstance());
8127 v8::Handle<Value> value = CompileRun(
8128 "function inc(x) { return x + 1; };"
8129 "inc(1);"
8130 "function dec(x) { return x - 1; };"
8131 "dec(1);"
8132 "o.__proto__ = this;"
8133 "this.__proto__.x = inc;"
8134 "this.__proto__.y = dec;"
8135 "var result = 0;"
8136 "var method = 'x';"
8137 "for (var i = 0; i < 10; i++) {"
8138 " if (i == 5) { method = 'y'; };"
8139 " result += o[method](41);"
8140 "}");
8141 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8142}
8143
8144
8145// Test the case when actual function to call sits on global object.
8146THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
8147 v8::HandleScope scope;
8148 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8149 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8150 LocalContext context;
8151 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8152
8153 v8::Handle<Value> value = CompileRun(
8154 "function len(x) { return x.length; };"
8155 "o.__proto__ = this;"
8156 "var m = 'parseFloat';"
8157 "var result = 0;"
8158 "for (var i = 0; i < 10; i++) {"
8159 " if (i == 5) {"
8160 " m = 'len';"
8161 " saved_result = result;"
8162 " };"
8163 " result = o[m]('239');"
8164 "}");
8165 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
8166 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8167}
8168
8169// Test the map transition before the interceptor.
8170THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
8171 v8::HandleScope scope;
8172 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8173 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8174 LocalContext context;
8175 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
8176
8177 v8::Handle<Value> value = CompileRun(
8178 "var o = new Object();"
8179 "o.__proto__ = proto;"
8180 "o.method = function(x) { return x + 1; };"
8181 "var m = 'method';"
8182 "var result = 0;"
8183 "for (var i = 0; i < 10; i++) {"
8184 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
8185 " result += o[m](41);"
8186 "}");
8187 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8188}
8189
8190
8191// Test the map transition after the interceptor.
8192THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
8193 v8::HandleScope scope;
8194 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8195 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8196 LocalContext context;
8197 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8198
8199 v8::Handle<Value> value = CompileRun(
8200 "var proto = new Object();"
8201 "o.__proto__ = proto;"
8202 "proto.method = function(x) { return x + 1; };"
8203 "var m = 'method';"
8204 "var result = 0;"
8205 "for (var i = 0; i < 10; i++) {"
8206 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
8207 " result += o[m](41);"
8208 "}");
8209 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8210}
8211
8212
Steve Blocka7e24c12009-10-30 11:49:00 +00008213static int interceptor_call_count = 0;
8214
8215static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
8216 const AccessorInfo& info) {
8217 ApiTestFuzzer::Fuzz();
8218 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
8219 return call_ic_function2;
8220 }
8221 return v8::Handle<Value>();
8222}
8223
8224
8225// This test should hit load and call ICs for the interceptor case.
8226// Once in a while, the interceptor will reply that a property was not
8227// found in which case we should get a reference error.
8228THREADED_TEST(InterceptorICReferenceErrors) {
8229 v8::HandleScope scope;
8230 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8231 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
8232 LocalContext context(0, templ, v8::Handle<Value>());
8233 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
8234 v8::Handle<Value> value = CompileRun(
8235 "function f() {"
8236 " for (var i = 0; i < 1000; i++) {"
8237 " try { x; } catch(e) { return true; }"
8238 " }"
8239 " return false;"
8240 "};"
8241 "f();");
8242 CHECK_EQ(true, value->BooleanValue());
8243 interceptor_call_count = 0;
8244 value = CompileRun(
8245 "function g() {"
8246 " for (var i = 0; i < 1000; i++) {"
8247 " try { x(42); } catch(e) { return true; }"
8248 " }"
8249 " return false;"
8250 "};"
8251 "g();");
8252 CHECK_EQ(true, value->BooleanValue());
8253}
8254
8255
8256static int interceptor_ic_exception_get_count = 0;
8257
8258static v8::Handle<Value> InterceptorICExceptionGetter(
8259 Local<String> name,
8260 const AccessorInfo& info) {
8261 ApiTestFuzzer::Fuzz();
8262 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
8263 return call_ic_function3;
8264 }
8265 if (interceptor_ic_exception_get_count == 20) {
8266 return v8::ThrowException(v8_num(42));
8267 }
8268 // Do not handle get for properties other than x.
8269 return v8::Handle<Value>();
8270}
8271
8272// Test interceptor load/call IC where the interceptor throws an
8273// exception once in a while.
8274THREADED_TEST(InterceptorICGetterExceptions) {
8275 interceptor_ic_exception_get_count = 0;
8276 v8::HandleScope scope;
8277 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8278 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
8279 LocalContext context(0, templ, v8::Handle<Value>());
8280 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
8281 v8::Handle<Value> value = CompileRun(
8282 "function f() {"
8283 " for (var i = 0; i < 100; i++) {"
8284 " try { x; } catch(e) { return true; }"
8285 " }"
8286 " return false;"
8287 "};"
8288 "f();");
8289 CHECK_EQ(true, value->BooleanValue());
8290 interceptor_ic_exception_get_count = 0;
8291 value = CompileRun(
8292 "function f() {"
8293 " for (var i = 0; i < 100; i++) {"
8294 " try { x(42); } catch(e) { return true; }"
8295 " }"
8296 " return false;"
8297 "};"
8298 "f();");
8299 CHECK_EQ(true, value->BooleanValue());
8300}
8301
8302
8303static int interceptor_ic_exception_set_count = 0;
8304
8305static v8::Handle<Value> InterceptorICExceptionSetter(
8306 Local<String> key, Local<Value> value, const AccessorInfo&) {
8307 ApiTestFuzzer::Fuzz();
8308 if (++interceptor_ic_exception_set_count > 20) {
8309 return v8::ThrowException(v8_num(42));
8310 }
8311 // Do not actually handle setting.
8312 return v8::Handle<Value>();
8313}
8314
8315// Test interceptor store IC where the interceptor throws an exception
8316// once in a while.
8317THREADED_TEST(InterceptorICSetterExceptions) {
8318 interceptor_ic_exception_set_count = 0;
8319 v8::HandleScope scope;
8320 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8321 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
8322 LocalContext context(0, templ, v8::Handle<Value>());
8323 v8::Handle<Value> value = CompileRun(
8324 "function f() {"
8325 " for (var i = 0; i < 100; i++) {"
8326 " try { x = 42; } catch(e) { return true; }"
8327 " }"
8328 " return false;"
8329 "};"
8330 "f();");
8331 CHECK_EQ(true, value->BooleanValue());
8332}
8333
8334
8335// Test that we ignore null interceptors.
8336THREADED_TEST(NullNamedInterceptor) {
8337 v8::HandleScope scope;
8338 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8339 templ->SetNamedPropertyHandler(0);
8340 LocalContext context;
8341 templ->Set("x", v8_num(42));
8342 v8::Handle<v8::Object> obj = templ->NewInstance();
8343 context->Global()->Set(v8_str("obj"), obj);
8344 v8::Handle<Value> value = CompileRun("obj.x");
8345 CHECK(value->IsInt32());
8346 CHECK_EQ(42, value->Int32Value());
8347}
8348
8349
8350// Test that we ignore null interceptors.
8351THREADED_TEST(NullIndexedInterceptor) {
8352 v8::HandleScope scope;
8353 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8354 templ->SetIndexedPropertyHandler(0);
8355 LocalContext context;
8356 templ->Set("42", v8_num(42));
8357 v8::Handle<v8::Object> obj = templ->NewInstance();
8358 context->Global()->Set(v8_str("obj"), obj);
8359 v8::Handle<Value> value = CompileRun("obj[42]");
8360 CHECK(value->IsInt32());
8361 CHECK_EQ(42, value->Int32Value());
8362}
8363
8364
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008365THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
8366 v8::HandleScope scope;
8367 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8368 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
8369 LocalContext env;
8370 env->Global()->Set(v8_str("obj"),
8371 templ->GetFunction()->NewInstance());
8372 ExpectTrue("obj.x === 42");
8373 ExpectTrue("!obj.propertyIsEnumerable('x')");
8374}
8375
8376
Steve Blocka7e24c12009-10-30 11:49:00 +00008377static v8::Handle<Value> ParentGetter(Local<String> name,
8378 const AccessorInfo& info) {
8379 ApiTestFuzzer::Fuzz();
8380 return v8_num(1);
8381}
8382
8383
8384static v8::Handle<Value> ChildGetter(Local<String> name,
8385 const AccessorInfo& info) {
8386 ApiTestFuzzer::Fuzz();
8387 return v8_num(42);
8388}
8389
8390
8391THREADED_TEST(Overriding) {
8392 v8::HandleScope scope;
8393 LocalContext context;
8394
8395 // Parent template.
8396 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
8397 Local<ObjectTemplate> parent_instance_templ =
8398 parent_templ->InstanceTemplate();
8399 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
8400
8401 // Template that inherits from the parent template.
8402 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
8403 Local<ObjectTemplate> child_instance_templ =
8404 child_templ->InstanceTemplate();
8405 child_templ->Inherit(parent_templ);
8406 // Override 'f'. The child version of 'f' should get called for child
8407 // instances.
8408 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
8409 // Add 'g' twice. The 'g' added last should get called for instances.
8410 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
8411 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
8412
8413 // Add 'h' as an accessor to the proto template with ReadOnly attributes
8414 // so 'h' can be shadowed on the instance object.
8415 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
8416 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
8417 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
8418
8419 // Add 'i' as an accessor to the instance template with ReadOnly attributes
8420 // but the attribute does not have effect because it is duplicated with
8421 // NULL setter.
8422 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
8423 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
8424
8425
8426
8427 // Instantiate the child template.
8428 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
8429
8430 // Check that the child function overrides the parent one.
8431 context->Global()->Set(v8_str("o"), instance);
8432 Local<Value> value = v8_compile("o.f")->Run();
8433 // Check that the 'g' that was added last is hit.
8434 CHECK_EQ(42, value->Int32Value());
8435 value = v8_compile("o.g")->Run();
8436 CHECK_EQ(42, value->Int32Value());
8437
8438 // Check 'h' can be shadowed.
8439 value = v8_compile("o.h = 3; o.h")->Run();
8440 CHECK_EQ(3, value->Int32Value());
8441
8442 // Check 'i' is cannot be shadowed or changed.
8443 value = v8_compile("o.i = 3; o.i")->Run();
8444 CHECK_EQ(42, value->Int32Value());
8445}
8446
8447
8448static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
8449 ApiTestFuzzer::Fuzz();
8450 if (args.IsConstructCall()) {
8451 return v8::Boolean::New(true);
8452 }
8453 return v8::Boolean::New(false);
8454}
8455
8456
8457THREADED_TEST(IsConstructCall) {
8458 v8::HandleScope scope;
8459
8460 // Function template with call handler.
8461 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8462 templ->SetCallHandler(IsConstructHandler);
8463
8464 LocalContext context;
8465
8466 context->Global()->Set(v8_str("f"), templ->GetFunction());
8467 Local<Value> value = v8_compile("f()")->Run();
8468 CHECK(!value->BooleanValue());
8469 value = v8_compile("new f()")->Run();
8470 CHECK(value->BooleanValue());
8471}
8472
8473
8474THREADED_TEST(ObjectProtoToString) {
8475 v8::HandleScope scope;
8476 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8477 templ->SetClassName(v8_str("MyClass"));
8478
8479 LocalContext context;
8480
8481 Local<String> customized_tostring = v8_str("customized toString");
8482
8483 // Replace Object.prototype.toString
8484 v8_compile("Object.prototype.toString = function() {"
8485 " return 'customized toString';"
8486 "}")->Run();
8487
8488 // Normal ToString call should call replaced Object.prototype.toString
8489 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
8490 Local<String> value = instance->ToString();
8491 CHECK(value->IsString() && value->Equals(customized_tostring));
8492
8493 // ObjectProtoToString should not call replace toString function.
8494 value = instance->ObjectProtoToString();
8495 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
8496
8497 // Check global
8498 value = context->Global()->ObjectProtoToString();
8499 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
8500
8501 // Check ordinary object
8502 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008503 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00008504 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
8505}
8506
8507
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08008508THREADED_TEST(ObjectGetConstructorName) {
8509 v8::HandleScope scope;
8510 LocalContext context;
8511 v8_compile("function Parent() {};"
8512 "function Child() {};"
8513 "Child.prototype = new Parent();"
8514 "var outer = { inner: function() { } };"
8515 "var p = new Parent();"
8516 "var c = new Child();"
8517 "var x = new outer.inner();")->Run();
8518
8519 Local<v8::Value> p = context->Global()->Get(v8_str("p"));
8520 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals(
8521 v8_str("Parent")));
8522
8523 Local<v8::Value> c = context->Global()->Get(v8_str("c"));
8524 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals(
8525 v8_str("Child")));
8526
8527 Local<v8::Value> x = context->Global()->Get(v8_str("x"));
8528 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals(
8529 v8_str("outer.inner")));
8530}
8531
8532
Steve Blocka7e24c12009-10-30 11:49:00 +00008533bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01008534i::Semaphore* ApiTestFuzzer::all_tests_done_=
8535 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008536int ApiTestFuzzer::active_tests_;
8537int ApiTestFuzzer::tests_being_run_;
8538int ApiTestFuzzer::current_;
8539
8540
8541// We are in a callback and want to switch to another thread (if we
8542// are currently running the thread fuzzing test).
8543void ApiTestFuzzer::Fuzz() {
8544 if (!fuzzing_) return;
8545 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
8546 test->ContextSwitch();
8547}
8548
8549
8550// Let the next thread go. Since it is also waiting on the V8 lock it may
8551// not start immediately.
8552bool ApiTestFuzzer::NextThread() {
8553 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00008554 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00008555 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00008556 if (kLogThreading)
8557 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00008558 return false;
8559 }
Steve Blockd0582a62009-12-15 09:54:21 +00008560 if (kLogThreading) {
8561 printf("Switch from %s to %s\n",
8562 test_name,
8563 RegisterThreadedTest::nth(test_position)->name());
8564 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008565 current_ = test_position;
8566 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
8567 return true;
8568}
8569
8570
8571void ApiTestFuzzer::Run() {
8572 // When it is our turn...
8573 gate_->Wait();
8574 {
8575 // ... get the V8 lock and start running the test.
8576 v8::Locker locker;
8577 CallTest();
8578 }
8579 // This test finished.
8580 active_ = false;
8581 active_tests_--;
8582 // If it was the last then signal that fact.
8583 if (active_tests_ == 0) {
8584 all_tests_done_->Signal();
8585 } else {
8586 // Otherwise select a new test and start that.
8587 NextThread();
8588 }
8589}
8590
8591
8592static unsigned linear_congruential_generator;
8593
8594
8595void ApiTestFuzzer::Setup(PartOfTest part) {
8596 linear_congruential_generator = i::FLAG_testing_prng_seed;
8597 fuzzing_ = true;
8598 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
8599 int end = (part == FIRST_PART)
8600 ? (RegisterThreadedTest::count() >> 1)
8601 : RegisterThreadedTest::count();
8602 active_tests_ = tests_being_run_ = end - start;
8603 for (int i = 0; i < tests_being_run_; i++) {
8604 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
8605 }
8606 for (int i = 0; i < active_tests_; i++) {
8607 RegisterThreadedTest::nth(i)->fuzzer_->Start();
8608 }
8609}
8610
8611
8612static void CallTestNumber(int test_number) {
8613 (RegisterThreadedTest::nth(test_number)->callback())();
8614}
8615
8616
8617void ApiTestFuzzer::RunAllTests() {
8618 // Set off the first test.
8619 current_ = -1;
8620 NextThread();
8621 // Wait till they are all done.
8622 all_tests_done_->Wait();
8623}
8624
8625
8626int ApiTestFuzzer::GetNextTestNumber() {
8627 int next_test;
8628 do {
8629 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
8630 linear_congruential_generator *= 1664525u;
8631 linear_congruential_generator += 1013904223u;
8632 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
8633 return next_test;
8634}
8635
8636
8637void ApiTestFuzzer::ContextSwitch() {
8638 // If the new thread is the same as the current thread there is nothing to do.
8639 if (NextThread()) {
8640 // Now it can start.
8641 v8::Unlocker unlocker;
8642 // Wait till someone starts us again.
8643 gate_->Wait();
8644 // And we're off.
8645 }
8646}
8647
8648
8649void ApiTestFuzzer::TearDown() {
8650 fuzzing_ = false;
8651 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
8652 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
8653 if (fuzzer != NULL) fuzzer->Join();
8654 }
8655}
8656
8657
8658// Lets not be needlessly self-referential.
8659TEST(Threading) {
8660 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
8661 ApiTestFuzzer::RunAllTests();
8662 ApiTestFuzzer::TearDown();
8663}
8664
8665TEST(Threading2) {
8666 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
8667 ApiTestFuzzer::RunAllTests();
8668 ApiTestFuzzer::TearDown();
8669}
8670
8671
8672void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00008673 if (kLogThreading)
8674 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008675 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00008676 if (kLogThreading)
8677 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008678}
8679
8680
8681static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
8682 CHECK(v8::Locker::IsLocked());
8683 ApiTestFuzzer::Fuzz();
8684 v8::Unlocker unlocker;
8685 const char* code = "throw 7;";
8686 {
8687 v8::Locker nested_locker;
8688 v8::HandleScope scope;
8689 v8::Handle<Value> exception;
8690 { v8::TryCatch try_catch;
8691 v8::Handle<Value> value = CompileRun(code);
8692 CHECK(value.IsEmpty());
8693 CHECK(try_catch.HasCaught());
8694 // Make sure to wrap the exception in a new handle because
8695 // the handle returned from the TryCatch is destroyed
8696 // when the TryCatch is destroyed.
8697 exception = Local<Value>::New(try_catch.Exception());
8698 }
8699 return v8::ThrowException(exception);
8700 }
8701}
8702
8703
8704static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
8705 CHECK(v8::Locker::IsLocked());
8706 ApiTestFuzzer::Fuzz();
8707 v8::Unlocker unlocker;
8708 const char* code = "throw 7;";
8709 {
8710 v8::Locker nested_locker;
8711 v8::HandleScope scope;
8712 v8::Handle<Value> value = CompileRun(code);
8713 CHECK(value.IsEmpty());
8714 return v8_str("foo");
8715 }
8716}
8717
8718
8719// These are locking tests that don't need to be run again
8720// as part of the locking aggregation tests.
8721TEST(NestedLockers) {
8722 v8::Locker locker;
8723 CHECK(v8::Locker::IsLocked());
8724 v8::HandleScope scope;
8725 LocalContext env;
8726 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
8727 Local<Function> fun = fun_templ->GetFunction();
8728 env->Global()->Set(v8_str("throw_in_js"), fun);
8729 Local<Script> script = v8_compile("(function () {"
8730 " try {"
8731 " throw_in_js();"
8732 " return 42;"
8733 " } catch (e) {"
8734 " return e * 13;"
8735 " }"
8736 "})();");
8737 CHECK_EQ(91, script->Run()->Int32Value());
8738}
8739
8740
8741// These are locking tests that don't need to be run again
8742// as part of the locking aggregation tests.
8743TEST(NestedLockersNoTryCatch) {
8744 v8::Locker locker;
8745 v8::HandleScope scope;
8746 LocalContext env;
8747 Local<v8::FunctionTemplate> fun_templ =
8748 v8::FunctionTemplate::New(ThrowInJSNoCatch);
8749 Local<Function> fun = fun_templ->GetFunction();
8750 env->Global()->Set(v8_str("throw_in_js"), fun);
8751 Local<Script> script = v8_compile("(function () {"
8752 " try {"
8753 " throw_in_js();"
8754 " return 42;"
8755 " } catch (e) {"
8756 " return e * 13;"
8757 " }"
8758 "})();");
8759 CHECK_EQ(91, script->Run()->Int32Value());
8760}
8761
8762
8763THREADED_TEST(RecursiveLocking) {
8764 v8::Locker locker;
8765 {
8766 v8::Locker locker2;
8767 CHECK(v8::Locker::IsLocked());
8768 }
8769}
8770
8771
8772static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
8773 ApiTestFuzzer::Fuzz();
8774 v8::Unlocker unlocker;
8775 return v8::Undefined();
8776}
8777
8778
8779THREADED_TEST(LockUnlockLock) {
8780 {
8781 v8::Locker locker;
8782 v8::HandleScope scope;
8783 LocalContext env;
8784 Local<v8::FunctionTemplate> fun_templ =
8785 v8::FunctionTemplate::New(UnlockForAMoment);
8786 Local<Function> fun = fun_templ->GetFunction();
8787 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8788 Local<Script> script = v8_compile("(function () {"
8789 " unlock_for_a_moment();"
8790 " return 42;"
8791 "})();");
8792 CHECK_EQ(42, script->Run()->Int32Value());
8793 }
8794 {
8795 v8::Locker locker;
8796 v8::HandleScope scope;
8797 LocalContext env;
8798 Local<v8::FunctionTemplate> fun_templ =
8799 v8::FunctionTemplate::New(UnlockForAMoment);
8800 Local<Function> fun = fun_templ->GetFunction();
8801 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8802 Local<Script> script = v8_compile("(function () {"
8803 " unlock_for_a_moment();"
8804 " return 42;"
8805 "})();");
8806 CHECK_EQ(42, script->Run()->Int32Value());
8807 }
8808}
8809
8810
Leon Clarked91b9f72010-01-27 17:25:45 +00008811static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00008812 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01008813 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00008814 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
8815 if (object->IsJSGlobalObject()) count++;
8816 return count;
8817}
8818
8819
Ben Murdochf87a2032010-10-22 12:50:53 +01008820static void CheckSurvivingGlobalObjectsCount(int expected) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008821 // We need to collect all garbage twice to be sure that everything
8822 // has been collected. This is because inline caches are cleared in
8823 // the first garbage collection but some of the maps have already
8824 // been marked at that point. Therefore some of the maps are not
8825 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01008826 i::Heap::CollectAllGarbage(false);
8827 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00008828 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00008829#ifdef DEBUG
Ben Murdochf87a2032010-10-22 12:50:53 +01008830 if (count != expected) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00008831#endif
Ben Murdochf87a2032010-10-22 12:50:53 +01008832 CHECK_EQ(expected, count);
Steve Blocka7e24c12009-10-30 11:49:00 +00008833}
8834
8835
8836TEST(DontLeakGlobalObjects) {
8837 // Regression test for issues 1139850 and 1174891.
8838
8839 v8::V8::Initialize();
8840
Steve Blocka7e24c12009-10-30 11:49:00 +00008841 for (int i = 0; i < 5; i++) {
8842 { v8::HandleScope scope;
8843 LocalContext context;
8844 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008845 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008846
8847 { v8::HandleScope scope;
8848 LocalContext context;
8849 v8_compile("Date")->Run();
8850 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008851 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008852
8853 { v8::HandleScope scope;
8854 LocalContext context;
8855 v8_compile("/aaa/")->Run();
8856 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008857 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008858
8859 { v8::HandleScope scope;
8860 const char* extension_list[] = { "v8/gc" };
8861 v8::ExtensionConfiguration extensions(1, extension_list);
8862 LocalContext context(&extensions);
8863 v8_compile("gc();")->Run();
8864 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008865 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008866 }
8867}
8868
8869
8870v8::Persistent<v8::Object> some_object;
8871v8::Persistent<v8::Object> bad_handle;
8872
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008873void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008874 v8::HandleScope scope;
8875 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008876 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008877}
8878
8879
8880THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8881 LocalContext context;
8882
8883 v8::Persistent<v8::Object> handle1, handle2;
8884 {
8885 v8::HandleScope scope;
8886 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8887 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8888 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8889 }
8890 // Note: order is implementation dependent alas: currently
8891 // global handle nodes are processed by PostGarbageCollectionProcessing
8892 // in reverse allocation order, so if second allocated handle is deleted,
8893 // weak callback of the first handle would be able to 'reallocate' it.
8894 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8895 handle2.Dispose();
8896 i::Heap::CollectAllGarbage(false);
8897}
8898
8899
8900v8::Persistent<v8::Object> to_be_disposed;
8901
8902void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8903 to_be_disposed.Dispose();
8904 i::Heap::CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008905 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008906}
8907
8908
8909THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8910 LocalContext context;
8911
8912 v8::Persistent<v8::Object> handle1, handle2;
8913 {
8914 v8::HandleScope scope;
8915 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8916 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8917 }
8918 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8919 to_be_disposed = handle2;
8920 i::Heap::CollectAllGarbage(false);
8921}
8922
Steve Blockd0582a62009-12-15 09:54:21 +00008923void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8924 handle.Dispose();
8925}
8926
8927void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8928 v8::HandleScope scope;
8929 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008930 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00008931}
8932
8933
8934THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8935 LocalContext context;
8936
8937 v8::Persistent<v8::Object> handle1, handle2, handle3;
8938 {
8939 v8::HandleScope scope;
8940 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8941 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8942 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8943 }
8944 handle2.MakeWeak(NULL, DisposingCallback);
8945 handle3.MakeWeak(NULL, HandleCreatingCallback);
8946 i::Heap::CollectAllGarbage(false);
8947}
8948
Steve Blocka7e24c12009-10-30 11:49:00 +00008949
8950THREADED_TEST(CheckForCrossContextObjectLiterals) {
8951 v8::V8::Initialize();
8952
8953 const int nof = 2;
8954 const char* sources[nof] = {
8955 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8956 "Object()"
8957 };
8958
8959 for (int i = 0; i < nof; i++) {
8960 const char* source = sources[i];
8961 { v8::HandleScope scope;
8962 LocalContext context;
8963 CompileRun(source);
8964 }
8965 { v8::HandleScope scope;
8966 LocalContext context;
8967 CompileRun(source);
8968 }
8969 }
8970}
8971
8972
8973static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8974 v8::HandleScope inner;
8975 env->Enter();
8976 v8::Handle<Value> three = v8_num(3);
8977 v8::Handle<Value> value = inner.Close(three);
8978 env->Exit();
8979 return value;
8980}
8981
8982
8983THREADED_TEST(NestedHandleScopeAndContexts) {
8984 v8::HandleScope outer;
8985 v8::Persistent<Context> env = Context::New();
8986 env->Enter();
8987 v8::Handle<Value> value = NestedScope(env);
8988 v8::Handle<String> str = value->ToString();
8989 env->Exit();
8990 env.Dispose();
8991}
8992
8993
8994THREADED_TEST(ExternalAllocatedMemory) {
8995 v8::HandleScope outer;
8996 v8::Persistent<Context> env = Context::New();
8997 const int kSize = 1024*1024;
8998 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8999 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
9000}
9001
9002
9003THREADED_TEST(DisposeEnteredContext) {
9004 v8::HandleScope scope;
9005 LocalContext outer;
9006 { v8::Persistent<v8::Context> inner = v8::Context::New();
9007 inner->Enter();
9008 inner.Dispose();
9009 inner.Clear();
9010 inner->Exit();
9011 }
9012}
9013
9014
9015// Regression test for issue 54, object templates with internal fields
9016// but no accessors or interceptors did not get their internal field
9017// count set on instances.
9018THREADED_TEST(Regress54) {
9019 v8::HandleScope outer;
9020 LocalContext context;
9021 static v8::Persistent<v8::ObjectTemplate> templ;
9022 if (templ.IsEmpty()) {
9023 v8::HandleScope inner;
9024 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
9025 local->SetInternalFieldCount(1);
9026 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
9027 }
9028 v8::Handle<v8::Object> result = templ->NewInstance();
9029 CHECK_EQ(1, result->InternalFieldCount());
9030}
9031
9032
9033// If part of the threaded tests, this test makes ThreadingTest fail
9034// on mac.
9035TEST(CatchStackOverflow) {
9036 v8::HandleScope scope;
9037 LocalContext context;
9038 v8::TryCatch try_catch;
9039 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
9040 "function f() {"
9041 " return f();"
9042 "}"
9043 ""
9044 "f();"));
9045 v8::Handle<v8::Value> result = script->Run();
9046 CHECK(result.IsEmpty());
9047}
9048
9049
9050static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
9051 const char* resource_name,
9052 int line_offset) {
9053 v8::HandleScope scope;
9054 v8::TryCatch try_catch;
9055 v8::Handle<v8::Value> result = script->Run();
9056 CHECK(result.IsEmpty());
9057 CHECK(try_catch.HasCaught());
9058 v8::Handle<v8::Message> message = try_catch.Message();
9059 CHECK(!message.IsEmpty());
9060 CHECK_EQ(10 + line_offset, message->GetLineNumber());
9061 CHECK_EQ(91, message->GetStartPosition());
9062 CHECK_EQ(92, message->GetEndPosition());
9063 CHECK_EQ(2, message->GetStartColumn());
9064 CHECK_EQ(3, message->GetEndColumn());
9065 v8::String::AsciiValue line(message->GetSourceLine());
9066 CHECK_EQ(" throw 'nirk';", *line);
9067 v8::String::AsciiValue name(message->GetScriptResourceName());
9068 CHECK_EQ(resource_name, *name);
9069}
9070
9071
9072THREADED_TEST(TryCatchSourceInfo) {
9073 v8::HandleScope scope;
9074 LocalContext context;
9075 v8::Handle<v8::String> source = v8::String::New(
9076 "function Foo() {\n"
9077 " return Bar();\n"
9078 "}\n"
9079 "\n"
9080 "function Bar() {\n"
9081 " return Baz();\n"
9082 "}\n"
9083 "\n"
9084 "function Baz() {\n"
9085 " throw 'nirk';\n"
9086 "}\n"
9087 "\n"
9088 "Foo();\n");
9089
9090 const char* resource_name;
9091 v8::Handle<v8::Script> script;
9092 resource_name = "test.js";
9093 script = v8::Script::Compile(source, v8::String::New(resource_name));
9094 CheckTryCatchSourceInfo(script, resource_name, 0);
9095
9096 resource_name = "test1.js";
9097 v8::ScriptOrigin origin1(v8::String::New(resource_name));
9098 script = v8::Script::Compile(source, &origin1);
9099 CheckTryCatchSourceInfo(script, resource_name, 0);
9100
9101 resource_name = "test2.js";
9102 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
9103 script = v8::Script::Compile(source, &origin2);
9104 CheckTryCatchSourceInfo(script, resource_name, 7);
9105}
9106
9107
9108THREADED_TEST(CompilationCache) {
9109 v8::HandleScope scope;
9110 LocalContext context;
9111 v8::Handle<v8::String> source0 = v8::String::New("1234");
9112 v8::Handle<v8::String> source1 = v8::String::New("1234");
9113 v8::Handle<v8::Script> script0 =
9114 v8::Script::Compile(source0, v8::String::New("test.js"));
9115 v8::Handle<v8::Script> script1 =
9116 v8::Script::Compile(source1, v8::String::New("test.js"));
9117 v8::Handle<v8::Script> script2 =
9118 v8::Script::Compile(source0); // different origin
9119 CHECK_EQ(1234, script0->Run()->Int32Value());
9120 CHECK_EQ(1234, script1->Run()->Int32Value());
9121 CHECK_EQ(1234, script2->Run()->Int32Value());
9122}
9123
9124
9125static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
9126 ApiTestFuzzer::Fuzz();
9127 return v8_num(42);
9128}
9129
9130
9131THREADED_TEST(CallbackFunctionName) {
9132 v8::HandleScope scope;
9133 LocalContext context;
9134 Local<ObjectTemplate> t = ObjectTemplate::New();
9135 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
9136 context->Global()->Set(v8_str("obj"), t->NewInstance());
9137 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
9138 CHECK(value->IsString());
9139 v8::String::AsciiValue name(value);
9140 CHECK_EQ("asdf", *name);
9141}
9142
9143
9144THREADED_TEST(DateAccess) {
9145 v8::HandleScope scope;
9146 LocalContext context;
9147 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
9148 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01009149 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00009150}
9151
9152
9153void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01009154 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009155 v8::Handle<v8::Array> props = obj->GetPropertyNames();
9156 CHECK_EQ(elmc, props->Length());
9157 for (int i = 0; i < elmc; i++) {
9158 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
9159 CHECK_EQ(elmv[i], *elm);
9160 }
9161}
9162
9163
9164THREADED_TEST(PropertyEnumeration) {
9165 v8::HandleScope scope;
9166 LocalContext context;
9167 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
9168 "var result = [];"
9169 "result[0] = {};"
9170 "result[1] = {a: 1, b: 2};"
9171 "result[2] = [1, 2, 3];"
9172 "var proto = {x: 1, y: 2, z: 3};"
9173 "var x = { __proto__: proto, w: 0, z: 1 };"
9174 "result[3] = x;"
9175 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01009176 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009177 CHECK_EQ(4, elms->Length());
9178 int elmc0 = 0;
9179 const char** elmv0 = NULL;
9180 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
9181 int elmc1 = 2;
9182 const char* elmv1[] = {"a", "b"};
9183 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
9184 int elmc2 = 3;
9185 const char* elmv2[] = {"0", "1", "2"};
9186 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
9187 int elmc3 = 4;
9188 const char* elmv3[] = {"w", "z", "x", "y"};
9189 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
9190}
9191
9192
Steve Blocka7e24c12009-10-30 11:49:00 +00009193static bool NamedSetAccessBlocker(Local<v8::Object> obj,
9194 Local<Value> name,
9195 v8::AccessType type,
9196 Local<Value> data) {
9197 return type != v8::ACCESS_SET;
9198}
9199
9200
9201static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
9202 uint32_t key,
9203 v8::AccessType type,
9204 Local<Value> data) {
9205 return type != v8::ACCESS_SET;
9206}
9207
9208
9209THREADED_TEST(DisableAccessChecksWhileConfiguring) {
9210 v8::HandleScope scope;
9211 LocalContext context;
9212 Local<ObjectTemplate> templ = ObjectTemplate::New();
9213 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9214 IndexedSetAccessBlocker);
9215 templ->Set(v8_str("x"), v8::True());
9216 Local<v8::Object> instance = templ->NewInstance();
9217 context->Global()->Set(v8_str("obj"), instance);
9218 Local<Value> value = CompileRun("obj.x");
9219 CHECK(value->BooleanValue());
9220}
9221
9222
9223static bool NamedGetAccessBlocker(Local<v8::Object> obj,
9224 Local<Value> name,
9225 v8::AccessType type,
9226 Local<Value> data) {
9227 return false;
9228}
9229
9230
9231static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
9232 uint32_t key,
9233 v8::AccessType type,
9234 Local<Value> data) {
9235 return false;
9236}
9237
9238
9239
9240THREADED_TEST(AccessChecksReenabledCorrectly) {
9241 v8::HandleScope scope;
9242 LocalContext context;
9243 Local<ObjectTemplate> templ = ObjectTemplate::New();
9244 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
9245 IndexedGetAccessBlocker);
9246 templ->Set(v8_str("a"), v8_str("a"));
9247 // Add more than 8 (see kMaxFastProperties) properties
9248 // so that the constructor will force copying map.
9249 // Cannot sprintf, gcc complains unsafety.
9250 char buf[4];
9251 for (char i = '0'; i <= '9' ; i++) {
9252 buf[0] = i;
9253 for (char j = '0'; j <= '9'; j++) {
9254 buf[1] = j;
9255 for (char k = '0'; k <= '9'; k++) {
9256 buf[2] = k;
9257 buf[3] = 0;
9258 templ->Set(v8_str(buf), v8::Number::New(k));
9259 }
9260 }
9261 }
9262
9263 Local<v8::Object> instance_1 = templ->NewInstance();
9264 context->Global()->Set(v8_str("obj_1"), instance_1);
9265
9266 Local<Value> value_1 = CompileRun("obj_1.a");
9267 CHECK(value_1->IsUndefined());
9268
9269 Local<v8::Object> instance_2 = templ->NewInstance();
9270 context->Global()->Set(v8_str("obj_2"), instance_2);
9271
9272 Local<Value> value_2 = CompileRun("obj_2.a");
9273 CHECK(value_2->IsUndefined());
9274}
9275
9276
9277// This tests that access check information remains on the global
9278// object template when creating contexts.
9279THREADED_TEST(AccessControlRepeatedContextCreation) {
9280 v8::HandleScope handle_scope;
9281 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9282 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9283 IndexedSetAccessBlocker);
9284 i::Handle<i::ObjectTemplateInfo> internal_template =
9285 v8::Utils::OpenHandle(*global_template);
9286 CHECK(!internal_template->constructor()->IsUndefined());
9287 i::Handle<i::FunctionTemplateInfo> constructor(
9288 i::FunctionTemplateInfo::cast(internal_template->constructor()));
9289 CHECK(!constructor->access_check_info()->IsUndefined());
9290 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
9291 CHECK(!constructor->access_check_info()->IsUndefined());
9292}
9293
9294
9295THREADED_TEST(TurnOnAccessCheck) {
9296 v8::HandleScope handle_scope;
9297
9298 // Create an environment with access check to the global object disabled by
9299 // default.
9300 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9301 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
9302 IndexedGetAccessBlocker,
9303 v8::Handle<v8::Value>(),
9304 false);
9305 v8::Persistent<Context> context = Context::New(NULL, global_template);
9306 Context::Scope context_scope(context);
9307
9308 // Set up a property and a number of functions.
9309 context->Global()->Set(v8_str("a"), v8_num(1));
9310 CompileRun("function f1() {return a;}"
9311 "function f2() {return a;}"
9312 "function g1() {return h();}"
9313 "function g2() {return h();}"
9314 "function h() {return 1;}");
9315 Local<Function> f1 =
9316 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9317 Local<Function> f2 =
9318 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9319 Local<Function> g1 =
9320 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9321 Local<Function> g2 =
9322 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9323 Local<Function> h =
9324 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
9325
9326 // Get the global object.
9327 v8::Handle<v8::Object> global = context->Global();
9328
9329 // Call f1 one time and f2 a number of times. This will ensure that f1 still
9330 // uses the runtime system to retreive property a whereas f2 uses global load
9331 // inline cache.
9332 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
9333 for (int i = 0; i < 4; i++) {
9334 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
9335 }
9336
9337 // Same for g1 and g2.
9338 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
9339 for (int i = 0; i < 4; i++) {
9340 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
9341 }
9342
9343 // Detach the global and turn on access check.
9344 context->DetachGlobal();
9345 context->Global()->TurnOnAccessCheck();
9346
9347 // Failing access check to property get results in undefined.
9348 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9349 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9350
9351 // Failing access check to function call results in exception.
9352 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9353 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9354
9355 // No failing access check when just returning a constant.
9356 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
9357}
9358
9359
Ben Murdochb0fe1622011-05-05 13:52:32 +01009360v8::Handle<v8::String> a;
9361v8::Handle<v8::String> h;
9362
9363static bool NamedGetAccessBlockAandH(Local<v8::Object> obj,
9364 Local<Value> name,
9365 v8::AccessType type,
9366 Local<Value> data) {
9367 return !(name->Equals(a) || name->Equals(h));
9368}
9369
9370
9371THREADED_TEST(TurnOnAccessCheckAndRecompile) {
9372 v8::HandleScope handle_scope;
9373
9374 // Create an environment with access check to the global object disabled by
9375 // default. When the registered access checker will block access to properties
9376 // a and h
9377 a = v8_str("a");
9378 h = v8_str("h");
9379 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9380 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH,
9381 IndexedGetAccessBlocker,
9382 v8::Handle<v8::Value>(),
9383 false);
9384 v8::Persistent<Context> context = Context::New(NULL, global_template);
9385 Context::Scope context_scope(context);
9386
9387 // Set up a property and a number of functions.
9388 context->Global()->Set(v8_str("a"), v8_num(1));
9389 static const char* source = "function f1() {return a;}"
9390 "function f2() {return a;}"
9391 "function g1() {return h();}"
9392 "function g2() {return h();}"
9393 "function h() {return 1;}";
9394
9395 CompileRun(source);
9396 Local<Function> f1;
9397 Local<Function> f2;
9398 Local<Function> g1;
9399 Local<Function> g2;
9400 Local<Function> h;
9401 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9402 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9403 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9404 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9405 h = Local<Function>::Cast(context->Global()->Get(v8_str("h")));
9406
9407 // Get the global object.
9408 v8::Handle<v8::Object> global = context->Global();
9409
9410 // Call f1 one time and f2 a number of times. This will ensure that f1 still
9411 // uses the runtime system to retreive property a whereas f2 uses global load
9412 // inline cache.
9413 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
9414 for (int i = 0; i < 4; i++) {
9415 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
9416 }
9417
9418 // Same for g1 and g2.
9419 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
9420 for (int i = 0; i < 4; i++) {
9421 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
9422 }
9423
9424 // Detach the global and turn on access check now blocking access to property
9425 // a and function h.
9426 context->DetachGlobal();
9427 context->Global()->TurnOnAccessCheck();
9428
9429 // Failing access check to property get results in undefined.
9430 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9431 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9432
9433 // Failing access check to function call results in exception.
9434 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9435 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9436
9437 // No failing access check when just returning a constant.
9438 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
9439
9440 // Now compile the source again. And get the newly compiled functions, except
9441 // for h for which access is blocked.
9442 CompileRun(source);
9443 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9444 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9445 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9446 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9447 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined());
9448
9449 // Failing access check to property get results in undefined.
9450 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9451 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9452
9453 // Failing access check to function call results in exception.
9454 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9455 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9456}
9457
9458
Steve Blocka7e24c12009-10-30 11:49:00 +00009459// This test verifies that pre-compilation (aka preparsing) can be called
9460// without initializing the whole VM. Thus we cannot run this test in a
9461// multi-threaded setup.
9462TEST(PreCompile) {
9463 // TODO(155): This test would break without the initialization of V8. This is
9464 // a workaround for now to make this test not fail.
9465 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009466 const char* script = "function foo(a) { return a+1; }";
9467 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00009468 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00009469 CHECK_NE(sd->Length(), 0);
9470 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00009471 CHECK(!sd->HasError());
9472 delete sd;
9473}
9474
9475
9476TEST(PreCompileWithError) {
9477 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009478 const char* script = "function foo(a) { return 1 * * 2; }";
9479 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00009480 v8::ScriptData::PreCompile(script, i::StrLength(script));
9481 CHECK(sd->HasError());
9482 delete sd;
9483}
9484
9485
9486TEST(Regress31661) {
9487 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009488 const char* script = " The Definintive Guide";
9489 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00009490 v8::ScriptData::PreCompile(script, i::StrLength(script));
9491 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00009492 delete sd;
9493}
9494
9495
Leon Clarkef7060e22010-06-03 12:02:55 +01009496// Tests that ScriptData can be serialized and deserialized.
9497TEST(PreCompileSerialization) {
9498 v8::V8::Initialize();
9499 const char* script = "function foo(a) { return a+1; }";
9500 v8::ScriptData* sd =
9501 v8::ScriptData::PreCompile(script, i::StrLength(script));
9502
9503 // Serialize.
9504 int serialized_data_length = sd->Length();
9505 char* serialized_data = i::NewArray<char>(serialized_data_length);
9506 memcpy(serialized_data, sd->Data(), serialized_data_length);
9507
9508 // Deserialize.
9509 v8::ScriptData* deserialized_sd =
9510 v8::ScriptData::New(serialized_data, serialized_data_length);
9511
9512 // Verify that the original is the same as the deserialized.
9513 CHECK_EQ(sd->Length(), deserialized_sd->Length());
9514 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
9515 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
9516
9517 delete sd;
9518 delete deserialized_sd;
9519}
9520
9521
9522// Attempts to deserialize bad data.
9523TEST(PreCompileDeserializationError) {
9524 v8::V8::Initialize();
9525 const char* data = "DONT CARE";
9526 int invalid_size = 3;
9527 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
9528
9529 CHECK_EQ(0, sd->Length());
9530
9531 delete sd;
9532}
9533
9534
Leon Clarkeac952652010-07-15 11:15:24 +01009535// Attempts to deserialize bad data.
9536TEST(PreCompileInvalidPreparseDataError) {
9537 v8::V8::Initialize();
9538 v8::HandleScope scope;
9539 LocalContext context;
9540
9541 const char* script = "function foo(){ return 5;}\n"
9542 "function bar(){ return 6 + 7;} foo();";
9543 v8::ScriptData* sd =
9544 v8::ScriptData::PreCompile(script, i::StrLength(script));
9545 CHECK(!sd->HasError());
9546 // ScriptDataImpl private implementation details
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08009547 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize;
Iain Merrick9ac36c92010-09-13 15:29:50 +01009548 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +01009549 const int kFunctionEntryStartOffset = 0;
9550 const int kFunctionEntryEndOffset = 1;
9551 unsigned* sd_data =
9552 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01009553
9554 // Overwrite function bar's end position with 0.
9555 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
9556 v8::TryCatch try_catch;
9557
9558 Local<String> source = String::New(script);
9559 Local<Script> compiled_script = Script::New(source, NULL, sd);
9560 CHECK(try_catch.HasCaught());
9561 String::AsciiValue exception_value(try_catch.Message()->Get());
9562 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
9563 *exception_value);
9564
9565 try_catch.Reset();
9566 // Overwrite function bar's start position with 200. The function entry
9567 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01009568 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
9569 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01009570 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
9571 200;
9572 compiled_script = Script::New(source, NULL, sd);
9573 CHECK(try_catch.HasCaught());
9574 String::AsciiValue second_exception_value(try_catch.Message()->Get());
9575 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
9576 *second_exception_value);
9577
9578 delete sd;
9579}
9580
9581
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009582// Verifies that the Handle<String> and const char* versions of the API produce
9583// the same results (at least for one trivial case).
9584TEST(PreCompileAPIVariationsAreSame) {
9585 v8::V8::Initialize();
9586 v8::HandleScope scope;
9587
9588 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009589
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009590 v8::ScriptData* sd_from_cstring =
9591 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
9592
9593 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009594 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009595 v8::String::NewExternal(resource));
9596
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009597 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
9598 v8::String::New(cstring));
9599
9600 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009601 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009602 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009603 sd_from_cstring->Length()));
9604
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009605 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
9606 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
9607 sd_from_string->Data(),
9608 sd_from_cstring->Length()));
9609
9610
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009611 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009612 delete sd_from_external_string;
9613 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009614}
9615
9616
Steve Blocka7e24c12009-10-30 11:49:00 +00009617// This tests that we do not allow dictionary load/call inline caches
9618// to use functions that have not yet been compiled. The potential
9619// problem of loading a function that has not yet been compiled can
9620// arise because we share code between contexts via the compilation
9621// cache.
9622THREADED_TEST(DictionaryICLoadedFunction) {
9623 v8::HandleScope scope;
9624 // Test LoadIC.
9625 for (int i = 0; i < 2; i++) {
9626 LocalContext context;
9627 context->Global()->Set(v8_str("tmp"), v8::True());
9628 context->Global()->Delete(v8_str("tmp"));
9629 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
9630 }
9631 // Test CallIC.
9632 for (int i = 0; i < 2; i++) {
9633 LocalContext context;
9634 context->Global()->Set(v8_str("tmp"), v8::True());
9635 context->Global()->Delete(v8_str("tmp"));
9636 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
9637 }
9638}
9639
9640
9641// Test that cross-context new calls use the context of the callee to
9642// create the new JavaScript object.
9643THREADED_TEST(CrossContextNew) {
9644 v8::HandleScope scope;
9645 v8::Persistent<Context> context0 = Context::New();
9646 v8::Persistent<Context> context1 = Context::New();
9647
9648 // Allow cross-domain access.
9649 Local<String> token = v8_str("<security token>");
9650 context0->SetSecurityToken(token);
9651 context1->SetSecurityToken(token);
9652
9653 // Set an 'x' property on the Object prototype and define a
9654 // constructor function in context0.
9655 context0->Enter();
9656 CompileRun("Object.prototype.x = 42; function C() {};");
9657 context0->Exit();
9658
9659 // Call the constructor function from context0 and check that the
9660 // result has the 'x' property.
9661 context1->Enter();
9662 context1->Global()->Set(v8_str("other"), context0->Global());
9663 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
9664 CHECK(value->IsInt32());
9665 CHECK_EQ(42, value->Int32Value());
9666 context1->Exit();
9667
9668 // Dispose the contexts to allow them to be garbage collected.
9669 context0.Dispose();
9670 context1.Dispose();
9671}
9672
9673
9674class RegExpInterruptTest {
9675 public:
9676 RegExpInterruptTest() : block_(NULL) {}
9677 ~RegExpInterruptTest() { delete block_; }
9678 void RunTest() {
9679 block_ = i::OS::CreateSemaphore(0);
9680 gc_count_ = 0;
9681 gc_during_regexp_ = 0;
9682 regexp_success_ = false;
9683 gc_success_ = false;
9684 GCThread gc_thread(this);
9685 gc_thread.Start();
9686 v8::Locker::StartPreemption(1);
9687
9688 LongRunningRegExp();
9689 {
9690 v8::Unlocker unlock;
9691 gc_thread.Join();
9692 }
9693 v8::Locker::StopPreemption();
9694 CHECK(regexp_success_);
9695 CHECK(gc_success_);
9696 }
9697 private:
9698 // Number of garbage collections required.
9699 static const int kRequiredGCs = 5;
9700
9701 class GCThread : public i::Thread {
9702 public:
9703 explicit GCThread(RegExpInterruptTest* test)
9704 : test_(test) {}
9705 virtual void Run() {
9706 test_->CollectGarbage();
9707 }
9708 private:
9709 RegExpInterruptTest* test_;
9710 };
9711
9712 void CollectGarbage() {
9713 block_->Wait();
9714 while (gc_during_regexp_ < kRequiredGCs) {
9715 {
9716 v8::Locker lock;
9717 // TODO(lrn): Perhaps create some garbage before collecting.
9718 i::Heap::CollectAllGarbage(false);
9719 gc_count_++;
9720 }
9721 i::OS::Sleep(1);
9722 }
9723 gc_success_ = true;
9724 }
9725
9726 void LongRunningRegExp() {
9727 block_->Signal(); // Enable garbage collection thread on next preemption.
9728 int rounds = 0;
9729 while (gc_during_regexp_ < kRequiredGCs) {
9730 int gc_before = gc_count_;
9731 {
9732 // Match 15-30 "a"'s against 14 and a "b".
9733 const char* c_source =
9734 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9735 ".exec('aaaaaaaaaaaaaaab') === null";
9736 Local<String> source = String::New(c_source);
9737 Local<Script> script = Script::Compile(source);
9738 Local<Value> result = script->Run();
9739 if (!result->BooleanValue()) {
9740 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
9741 return;
9742 }
9743 }
9744 {
9745 // Match 15-30 "a"'s against 15 and a "b".
9746 const char* c_source =
9747 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9748 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
9749 Local<String> source = String::New(c_source);
9750 Local<Script> script = Script::Compile(source);
9751 Local<Value> result = script->Run();
9752 if (!result->BooleanValue()) {
9753 gc_during_regexp_ = kRequiredGCs;
9754 return;
9755 }
9756 }
9757 int gc_after = gc_count_;
9758 gc_during_regexp_ += gc_after - gc_before;
9759 rounds++;
9760 i::OS::Sleep(1);
9761 }
9762 regexp_success_ = true;
9763 }
9764
9765 i::Semaphore* block_;
9766 int gc_count_;
9767 int gc_during_regexp_;
9768 bool regexp_success_;
9769 bool gc_success_;
9770};
9771
9772
9773// Test that a regular expression execution can be interrupted and
9774// survive a garbage collection.
9775TEST(RegExpInterruption) {
9776 v8::Locker lock;
9777 v8::V8::Initialize();
9778 v8::HandleScope scope;
9779 Local<Context> local_env;
9780 {
9781 LocalContext env;
9782 local_env = env.local();
9783 }
9784
9785 // Local context should still be live.
9786 CHECK(!local_env.IsEmpty());
9787 local_env->Enter();
9788
9789 // Should complete without problems.
9790 RegExpInterruptTest().RunTest();
9791
9792 local_env->Exit();
9793}
9794
9795
9796class ApplyInterruptTest {
9797 public:
9798 ApplyInterruptTest() : block_(NULL) {}
9799 ~ApplyInterruptTest() { delete block_; }
9800 void RunTest() {
9801 block_ = i::OS::CreateSemaphore(0);
9802 gc_count_ = 0;
9803 gc_during_apply_ = 0;
9804 apply_success_ = false;
9805 gc_success_ = false;
9806 GCThread gc_thread(this);
9807 gc_thread.Start();
9808 v8::Locker::StartPreemption(1);
9809
9810 LongRunningApply();
9811 {
9812 v8::Unlocker unlock;
9813 gc_thread.Join();
9814 }
9815 v8::Locker::StopPreemption();
9816 CHECK(apply_success_);
9817 CHECK(gc_success_);
9818 }
9819 private:
9820 // Number of garbage collections required.
9821 static const int kRequiredGCs = 2;
9822
9823 class GCThread : public i::Thread {
9824 public:
9825 explicit GCThread(ApplyInterruptTest* test)
9826 : test_(test) {}
9827 virtual void Run() {
9828 test_->CollectGarbage();
9829 }
9830 private:
9831 ApplyInterruptTest* test_;
9832 };
9833
9834 void CollectGarbage() {
9835 block_->Wait();
9836 while (gc_during_apply_ < kRequiredGCs) {
9837 {
9838 v8::Locker lock;
9839 i::Heap::CollectAllGarbage(false);
9840 gc_count_++;
9841 }
9842 i::OS::Sleep(1);
9843 }
9844 gc_success_ = true;
9845 }
9846
9847 void LongRunningApply() {
9848 block_->Signal();
9849 int rounds = 0;
9850 while (gc_during_apply_ < kRequiredGCs) {
9851 int gc_before = gc_count_;
9852 {
9853 const char* c_source =
9854 "function do_very_little(bar) {"
9855 " this.foo = bar;"
9856 "}"
9857 "for (var i = 0; i < 100000; i++) {"
9858 " do_very_little.apply(this, ['bar']);"
9859 "}";
9860 Local<String> source = String::New(c_source);
9861 Local<Script> script = Script::Compile(source);
9862 Local<Value> result = script->Run();
9863 // Check that no exception was thrown.
9864 CHECK(!result.IsEmpty());
9865 }
9866 int gc_after = gc_count_;
9867 gc_during_apply_ += gc_after - gc_before;
9868 rounds++;
9869 }
9870 apply_success_ = true;
9871 }
9872
9873 i::Semaphore* block_;
9874 int gc_count_;
9875 int gc_during_apply_;
9876 bool apply_success_;
9877 bool gc_success_;
9878};
9879
9880
9881// Test that nothing bad happens if we get a preemption just when we were
9882// about to do an apply().
9883TEST(ApplyInterruption) {
9884 v8::Locker lock;
9885 v8::V8::Initialize();
9886 v8::HandleScope scope;
9887 Local<Context> local_env;
9888 {
9889 LocalContext env;
9890 local_env = env.local();
9891 }
9892
9893 // Local context should still be live.
9894 CHECK(!local_env.IsEmpty());
9895 local_env->Enter();
9896
9897 // Should complete without problems.
9898 ApplyInterruptTest().RunTest();
9899
9900 local_env->Exit();
9901}
9902
9903
9904// Verify that we can clone an object
9905TEST(ObjectClone) {
9906 v8::HandleScope scope;
9907 LocalContext env;
9908
9909 const char* sample =
9910 "var rv = {};" \
9911 "rv.alpha = 'hello';" \
9912 "rv.beta = 123;" \
9913 "rv;";
9914
9915 // Create an object, verify basics.
9916 Local<Value> val = CompileRun(sample);
9917 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01009918 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009919 obj->Set(v8_str("gamma"), v8_str("cloneme"));
9920
9921 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
9922 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9923 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
9924
9925 // Clone it.
9926 Local<v8::Object> clone = obj->Clone();
9927 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
9928 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
9929 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
9930
9931 // Set a property on the clone, verify each object.
9932 clone->Set(v8_str("beta"), v8::Integer::New(456));
9933 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9934 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
9935}
9936
9937
9938class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
9939 public:
9940 explicit AsciiVectorResource(i::Vector<const char> vector)
9941 : data_(vector) {}
9942 virtual ~AsciiVectorResource() {}
9943 virtual size_t length() const { return data_.length(); }
9944 virtual const char* data() const { return data_.start(); }
9945 private:
9946 i::Vector<const char> data_;
9947};
9948
9949
9950class UC16VectorResource : public v8::String::ExternalStringResource {
9951 public:
9952 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
9953 : data_(vector) {}
9954 virtual ~UC16VectorResource() {}
9955 virtual size_t length() const { return data_.length(); }
9956 virtual const i::uc16* data() const { return data_.start(); }
9957 private:
9958 i::Vector<const i::uc16> data_;
9959};
9960
9961
9962static void MorphAString(i::String* string,
9963 AsciiVectorResource* ascii_resource,
9964 UC16VectorResource* uc16_resource) {
9965 CHECK(i::StringShape(string).IsExternal());
9966 if (string->IsAsciiRepresentation()) {
9967 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009968 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009969 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009970 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009971 i::ExternalTwoByteString* morphed =
9972 i::ExternalTwoByteString::cast(string);
9973 morphed->set_resource(uc16_resource);
9974 } else {
9975 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009976 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009977 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009978 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009979 i::ExternalAsciiString* morphed =
9980 i::ExternalAsciiString::cast(string);
9981 morphed->set_resource(ascii_resource);
9982 }
9983}
9984
9985
9986// Test that we can still flatten a string if the components it is built up
9987// from have been turned into 16 bit strings in the mean time.
9988THREADED_TEST(MorphCompositeStringTest) {
9989 const char* c_string = "Now is the time for all good men"
9990 " to come to the aid of the party";
9991 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9992 {
9993 v8::HandleScope scope;
9994 LocalContext env;
9995 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009996 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009997 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009998 i::Vector<const uint16_t>(two_byte_string,
9999 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +000010000
10001 Local<String> lhs(v8::Utils::ToLocal(
10002 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
10003 Local<String> rhs(v8::Utils::ToLocal(
10004 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
10005
10006 env->Global()->Set(v8_str("lhs"), lhs);
10007 env->Global()->Set(v8_str("rhs"), rhs);
10008
10009 CompileRun(
10010 "var cons = lhs + rhs;"
10011 "var slice = lhs.substring(1, lhs.length - 1);"
10012 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
10013
10014 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
10015 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
10016
10017 // Now do some stuff to make sure the strings are flattened, etc.
10018 CompileRun(
10019 "/[^a-z]/.test(cons);"
10020 "/[^a-z]/.test(slice);"
10021 "/[^a-z]/.test(slice_on_cons);");
10022 const char* expected_cons =
10023 "Now is the time for all good men to come to the aid of the party"
10024 "Now is the time for all good men to come to the aid of the party";
10025 const char* expected_slice =
10026 "ow is the time for all good men to come to the aid of the part";
10027 const char* expected_slice_on_cons =
10028 "ow is the time for all good men to come to the aid of the party"
10029 "Now is the time for all good men to come to the aid of the part";
10030 CHECK_EQ(String::New(expected_cons),
10031 env->Global()->Get(v8_str("cons")));
10032 CHECK_EQ(String::New(expected_slice),
10033 env->Global()->Get(v8_str("slice")));
10034 CHECK_EQ(String::New(expected_slice_on_cons),
10035 env->Global()->Get(v8_str("slice_on_cons")));
10036 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010037 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +000010038}
10039
10040
10041TEST(CompileExternalTwoByteSource) {
10042 v8::HandleScope scope;
10043 LocalContext context;
10044
10045 // This is a very short list of sources, which currently is to check for a
10046 // regression caused by r2703.
10047 const char* ascii_sources[] = {
10048 "0.5",
10049 "-0.5", // This mainly testes PushBack in the Scanner.
10050 "--0.5", // This mainly testes PushBack in the Scanner.
10051 NULL
10052 };
10053
10054 // Compile the sources as external two byte strings.
10055 for (int i = 0; ascii_sources[i] != NULL; i++) {
10056 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
10057 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +000010058 i::Vector<const uint16_t>(two_byte_string,
10059 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +000010060 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
10061 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010062 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +000010063 }
10064}
10065
10066
10067class RegExpStringModificationTest {
10068 public:
10069 RegExpStringModificationTest()
10070 : block_(i::OS::CreateSemaphore(0)),
10071 morphs_(0),
10072 morphs_during_regexp_(0),
10073 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
10074 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
10075 ~RegExpStringModificationTest() { delete block_; }
10076 void RunTest() {
10077 regexp_success_ = false;
10078 morph_success_ = false;
10079
10080 // Initialize the contents of two_byte_content_ to be a uc16 representation
10081 // of "aaaaaaaaaaaaaab".
10082 for (int i = 0; i < 14; i++) {
10083 two_byte_content_[i] = 'a';
10084 }
10085 two_byte_content_[14] = 'b';
10086
10087 // Create the input string for the regexp - the one we are going to change
10088 // properties of.
10089 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
10090
10091 // Inject the input as a global variable.
10092 i::Handle<i::String> input_name =
10093 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010094 i::Top::global_context()->global()->SetProperty(
10095 *input_name,
10096 *input_,
10097 NONE,
10098 i::kNonStrictMode)->ToObjectChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +000010099
10100 MorphThread morph_thread(this);
10101 morph_thread.Start();
10102 v8::Locker::StartPreemption(1);
10103 LongRunningRegExp();
10104 {
10105 v8::Unlocker unlock;
10106 morph_thread.Join();
10107 }
10108 v8::Locker::StopPreemption();
10109 CHECK(regexp_success_);
10110 CHECK(morph_success_);
10111 }
10112 private:
10113
10114 // Number of string modifications required.
10115 static const int kRequiredModifications = 5;
10116 static const int kMaxModifications = 100;
10117
10118 class MorphThread : public i::Thread {
10119 public:
10120 explicit MorphThread(RegExpStringModificationTest* test)
10121 : test_(test) {}
10122 virtual void Run() {
10123 test_->MorphString();
10124 }
10125 private:
10126 RegExpStringModificationTest* test_;
10127 };
10128
10129 void MorphString() {
10130 block_->Wait();
10131 while (morphs_during_regexp_ < kRequiredModifications &&
10132 morphs_ < kMaxModifications) {
10133 {
10134 v8::Locker lock;
10135 // Swap string between ascii and two-byte representation.
10136 i::String* string = *input_;
10137 MorphAString(string, &ascii_resource_, &uc16_resource_);
10138 morphs_++;
10139 }
10140 i::OS::Sleep(1);
10141 }
10142 morph_success_ = true;
10143 }
10144
10145 void LongRunningRegExp() {
10146 block_->Signal(); // Enable morphing thread on next preemption.
10147 while (morphs_during_regexp_ < kRequiredModifications &&
10148 morphs_ < kMaxModifications) {
10149 int morphs_before = morphs_;
10150 {
Steve Block791712a2010-08-27 10:21:07 +010010151 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +000010152 // Match 15-30 "a"'s against 14 and a "b".
10153 const char* c_source =
10154 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10155 ".exec(input) === null";
10156 Local<String> source = String::New(c_source);
10157 Local<Script> script = Script::Compile(source);
10158 Local<Value> result = script->Run();
10159 CHECK(result->IsTrue());
10160 }
10161 int morphs_after = morphs_;
10162 morphs_during_regexp_ += morphs_after - morphs_before;
10163 }
10164 regexp_success_ = true;
10165 }
10166
10167 i::uc16 two_byte_content_[15];
10168 i::Semaphore* block_;
10169 int morphs_;
10170 int morphs_during_regexp_;
10171 bool regexp_success_;
10172 bool morph_success_;
10173 i::Handle<i::String> input_;
10174 AsciiVectorResource ascii_resource_;
10175 UC16VectorResource uc16_resource_;
10176};
10177
10178
10179// Test that a regular expression execution can be interrupted and
10180// the string changed without failing.
10181TEST(RegExpStringModification) {
10182 v8::Locker lock;
10183 v8::V8::Initialize();
10184 v8::HandleScope scope;
10185 Local<Context> local_env;
10186 {
10187 LocalContext env;
10188 local_env = env.local();
10189 }
10190
10191 // Local context should still be live.
10192 CHECK(!local_env.IsEmpty());
10193 local_env->Enter();
10194
10195 // Should complete without problems.
10196 RegExpStringModificationTest().RunTest();
10197
10198 local_env->Exit();
10199}
10200
10201
10202// Test that we can set a property on the global object even if there
10203// is a read-only property in the prototype chain.
10204TEST(ReadOnlyPropertyInGlobalProto) {
10205 v8::HandleScope scope;
10206 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10207 LocalContext context(0, templ);
10208 v8::Handle<v8::Object> global = context->Global();
10209 v8::Handle<v8::Object> global_proto =
10210 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
10211 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
10212 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
10213 // Check without 'eval' or 'with'.
10214 v8::Handle<v8::Value> res =
10215 CompileRun("function f() { x = 42; return x; }; f()");
10216 // Check with 'eval'.
10217 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
10218 CHECK_EQ(v8::Integer::New(42), res);
10219 // Check with 'with'.
10220 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
10221 CHECK_EQ(v8::Integer::New(42), res);
10222}
10223
10224static int force_set_set_count = 0;
10225static int force_set_get_count = 0;
10226bool pass_on_get = false;
10227
10228static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
10229 const v8::AccessorInfo& info) {
10230 force_set_get_count++;
10231 if (pass_on_get) {
10232 return v8::Handle<v8::Value>();
10233 } else {
10234 return v8::Int32::New(3);
10235 }
10236}
10237
10238static void ForceSetSetter(v8::Local<v8::String> name,
10239 v8::Local<v8::Value> value,
10240 const v8::AccessorInfo& info) {
10241 force_set_set_count++;
10242}
10243
10244static v8::Handle<v8::Value> ForceSetInterceptSetter(
10245 v8::Local<v8::String> name,
10246 v8::Local<v8::Value> value,
10247 const v8::AccessorInfo& info) {
10248 force_set_set_count++;
10249 return v8::Undefined();
10250}
10251
10252TEST(ForceSet) {
10253 force_set_get_count = 0;
10254 force_set_set_count = 0;
10255 pass_on_get = false;
10256
10257 v8::HandleScope scope;
10258 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10259 v8::Handle<v8::String> access_property = v8::String::New("a");
10260 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
10261 LocalContext context(NULL, templ);
10262 v8::Handle<v8::Object> global = context->Global();
10263
10264 // Ordinary properties
10265 v8::Handle<v8::String> simple_property = v8::String::New("p");
10266 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
10267 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10268 // This should fail because the property is read-only
10269 global->Set(simple_property, v8::Int32::New(5));
10270 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10271 // This should succeed even though the property is read-only
10272 global->ForceSet(simple_property, v8::Int32::New(6));
10273 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
10274
10275 // Accessors
10276 CHECK_EQ(0, force_set_set_count);
10277 CHECK_EQ(0, force_set_get_count);
10278 CHECK_EQ(3, global->Get(access_property)->Int32Value());
10279 // CHECK_EQ the property shouldn't override it, just call the setter
10280 // which in this case does nothing.
10281 global->Set(access_property, v8::Int32::New(7));
10282 CHECK_EQ(3, global->Get(access_property)->Int32Value());
10283 CHECK_EQ(1, force_set_set_count);
10284 CHECK_EQ(2, force_set_get_count);
10285 // Forcing the property to be set should override the accessor without
10286 // calling it
10287 global->ForceSet(access_property, v8::Int32::New(8));
10288 CHECK_EQ(8, global->Get(access_property)->Int32Value());
10289 CHECK_EQ(1, force_set_set_count);
10290 CHECK_EQ(2, force_set_get_count);
10291}
10292
10293TEST(ForceSetWithInterceptor) {
10294 force_set_get_count = 0;
10295 force_set_set_count = 0;
10296 pass_on_get = false;
10297
10298 v8::HandleScope scope;
10299 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10300 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
10301 LocalContext context(NULL, templ);
10302 v8::Handle<v8::Object> global = context->Global();
10303
10304 v8::Handle<v8::String> some_property = v8::String::New("a");
10305 CHECK_EQ(0, force_set_set_count);
10306 CHECK_EQ(0, force_set_get_count);
10307 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10308 // Setting the property shouldn't override it, just call the setter
10309 // which in this case does nothing.
10310 global->Set(some_property, v8::Int32::New(7));
10311 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10312 CHECK_EQ(1, force_set_set_count);
10313 CHECK_EQ(2, force_set_get_count);
10314 // Getting the property when the interceptor returns an empty handle
10315 // should yield undefined, since the property isn't present on the
10316 // object itself yet.
10317 pass_on_get = true;
10318 CHECK(global->Get(some_property)->IsUndefined());
10319 CHECK_EQ(1, force_set_set_count);
10320 CHECK_EQ(3, force_set_get_count);
10321 // Forcing the property to be set should cause the value to be
10322 // set locally without calling the interceptor.
10323 global->ForceSet(some_property, v8::Int32::New(8));
10324 CHECK_EQ(8, global->Get(some_property)->Int32Value());
10325 CHECK_EQ(1, force_set_set_count);
10326 CHECK_EQ(4, force_set_get_count);
10327 // Reenabling the interceptor should cause it to take precedence over
10328 // the property
10329 pass_on_get = false;
10330 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10331 CHECK_EQ(1, force_set_set_count);
10332 CHECK_EQ(5, force_set_get_count);
10333 // The interceptor should also work for other properties
10334 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
10335 CHECK_EQ(1, force_set_set_count);
10336 CHECK_EQ(6, force_set_get_count);
10337}
10338
10339
10340THREADED_TEST(ForceDelete) {
10341 v8::HandleScope scope;
10342 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10343 LocalContext context(NULL, templ);
10344 v8::Handle<v8::Object> global = context->Global();
10345
10346 // Ordinary properties
10347 v8::Handle<v8::String> simple_property = v8::String::New("p");
10348 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
10349 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10350 // This should fail because the property is dont-delete.
10351 CHECK(!global->Delete(simple_property));
10352 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10353 // This should succeed even though the property is dont-delete.
10354 CHECK(global->ForceDelete(simple_property));
10355 CHECK(global->Get(simple_property)->IsUndefined());
10356}
10357
10358
10359static int force_delete_interceptor_count = 0;
10360static bool pass_on_delete = false;
10361
10362
10363static v8::Handle<v8::Boolean> ForceDeleteDeleter(
10364 v8::Local<v8::String> name,
10365 const v8::AccessorInfo& info) {
10366 force_delete_interceptor_count++;
10367 if (pass_on_delete) {
10368 return v8::Handle<v8::Boolean>();
10369 } else {
10370 return v8::True();
10371 }
10372}
10373
10374
10375THREADED_TEST(ForceDeleteWithInterceptor) {
10376 force_delete_interceptor_count = 0;
10377 pass_on_delete = false;
10378
10379 v8::HandleScope scope;
10380 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10381 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
10382 LocalContext context(NULL, templ);
10383 v8::Handle<v8::Object> global = context->Global();
10384
10385 v8::Handle<v8::String> some_property = v8::String::New("a");
10386 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
10387
10388 // Deleting a property should get intercepted and nothing should
10389 // happen.
10390 CHECK_EQ(0, force_delete_interceptor_count);
10391 CHECK(global->Delete(some_property));
10392 CHECK_EQ(1, force_delete_interceptor_count);
10393 CHECK_EQ(42, global->Get(some_property)->Int32Value());
10394 // Deleting the property when the interceptor returns an empty
10395 // handle should not delete the property since it is DontDelete.
10396 pass_on_delete = true;
10397 CHECK(!global->Delete(some_property));
10398 CHECK_EQ(2, force_delete_interceptor_count);
10399 CHECK_EQ(42, global->Get(some_property)->Int32Value());
10400 // Forcing the property to be deleted should delete the value
10401 // without calling the interceptor.
10402 CHECK(global->ForceDelete(some_property));
10403 CHECK(global->Get(some_property)->IsUndefined());
10404 CHECK_EQ(2, force_delete_interceptor_count);
10405}
10406
10407
10408// Make sure that forcing a delete invalidates any IC stubs, so we
10409// don't read the hole value.
10410THREADED_TEST(ForceDeleteIC) {
10411 v8::HandleScope scope;
10412 LocalContext context;
10413 // Create a DontDelete variable on the global object.
10414 CompileRun("this.__proto__ = { foo: 'horse' };"
10415 "var foo = 'fish';"
10416 "function f() { return foo.length; }");
10417 // Initialize the IC for foo in f.
10418 CompileRun("for (var i = 0; i < 4; i++) f();");
10419 // Make sure the value of foo is correct before the deletion.
10420 CHECK_EQ(4, CompileRun("f()")->Int32Value());
10421 // Force the deletion of foo.
10422 CHECK(context->Global()->ForceDelete(v8_str("foo")));
10423 // Make sure the value for foo is read from the prototype, and that
10424 // we don't get in trouble with reading the deleted cell value
10425 // sentinel.
10426 CHECK_EQ(5, CompileRun("f()")->Int32Value());
10427}
10428
10429
10430v8::Persistent<Context> calling_context0;
10431v8::Persistent<Context> calling_context1;
10432v8::Persistent<Context> calling_context2;
10433
10434
10435// Check that the call to the callback is initiated in
10436// calling_context2, the directly calling context is calling_context1
10437// and the callback itself is in calling_context0.
10438static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
10439 ApiTestFuzzer::Fuzz();
10440 CHECK(Context::GetCurrent() == calling_context0);
10441 CHECK(Context::GetCalling() == calling_context1);
10442 CHECK(Context::GetEntered() == calling_context2);
10443 return v8::Integer::New(42);
10444}
10445
10446
10447THREADED_TEST(GetCallingContext) {
10448 v8::HandleScope scope;
10449
10450 calling_context0 = Context::New();
10451 calling_context1 = Context::New();
10452 calling_context2 = Context::New();
10453
10454 // Allow cross-domain access.
10455 Local<String> token = v8_str("<security token>");
10456 calling_context0->SetSecurityToken(token);
10457 calling_context1->SetSecurityToken(token);
10458 calling_context2->SetSecurityToken(token);
10459
10460 // Create an object with a C++ callback in context0.
10461 calling_context0->Enter();
10462 Local<v8::FunctionTemplate> callback_templ =
10463 v8::FunctionTemplate::New(GetCallingContextCallback);
10464 calling_context0->Global()->Set(v8_str("callback"),
10465 callback_templ->GetFunction());
10466 calling_context0->Exit();
10467
10468 // Expose context0 in context1 and setup a function that calls the
10469 // callback function.
10470 calling_context1->Enter();
10471 calling_context1->Global()->Set(v8_str("context0"),
10472 calling_context0->Global());
10473 CompileRun("function f() { context0.callback() }");
10474 calling_context1->Exit();
10475
10476 // Expose context1 in context2 and call the callback function in
10477 // context0 indirectly through f in context1.
10478 calling_context2->Enter();
10479 calling_context2->Global()->Set(v8_str("context1"),
10480 calling_context1->Global());
10481 CompileRun("context1.f()");
10482 calling_context2->Exit();
10483
10484 // Dispose the contexts to allow them to be garbage collected.
10485 calling_context0.Dispose();
10486 calling_context1.Dispose();
10487 calling_context2.Dispose();
10488 calling_context0.Clear();
10489 calling_context1.Clear();
10490 calling_context2.Clear();
10491}
10492
10493
10494// Check that a variable declaration with no explicit initialization
10495// value does not shadow an existing property in the prototype chain.
10496//
10497// This is consistent with Firefox and Safari.
10498//
10499// See http://crbug.com/12548.
10500THREADED_TEST(InitGlobalVarInProtoChain) {
10501 v8::HandleScope scope;
10502 LocalContext context;
10503 // Introduce a variable in the prototype chain.
10504 CompileRun("__proto__.x = 42");
10505 v8::Handle<v8::Value> result = CompileRun("var x; x");
10506 CHECK(!result->IsUndefined());
10507 CHECK_EQ(42, result->Int32Value());
10508}
10509
10510
10511// Regression test for issue 398.
10512// If a function is added to an object, creating a constant function
10513// field, and the result is cloned, replacing the constant function on the
10514// original should not affect the clone.
10515// See http://code.google.com/p/v8/issues/detail?id=398
10516THREADED_TEST(ReplaceConstantFunction) {
10517 v8::HandleScope scope;
10518 LocalContext context;
10519 v8::Handle<v8::Object> obj = v8::Object::New();
10520 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
10521 v8::Handle<v8::String> foo_string = v8::String::New("foo");
10522 obj->Set(foo_string, func_templ->GetFunction());
10523 v8::Handle<v8::Object> obj_clone = obj->Clone();
10524 obj_clone->Set(foo_string, v8::String::New("Hello"));
10525 CHECK(!obj->Get(foo_string)->IsUndefined());
10526}
10527
10528
10529// Regression test for http://crbug.com/16276.
10530THREADED_TEST(Regress16276) {
10531 v8::HandleScope scope;
10532 LocalContext context;
10533 // Force the IC in f to be a dictionary load IC.
10534 CompileRun("function f(obj) { return obj.x; }\n"
10535 "var obj = { x: { foo: 42 }, y: 87 };\n"
10536 "var x = obj.x;\n"
10537 "delete obj.y;\n"
10538 "for (var i = 0; i < 5; i++) f(obj);");
10539 // Detach the global object to make 'this' refer directly to the
10540 // global object (not the proxy), and make sure that the dictionary
10541 // load IC doesn't mess up loading directly from the global object.
10542 context->DetachGlobal();
10543 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
10544}
10545
10546
10547THREADED_TEST(PixelArray) {
10548 v8::HandleScope scope;
10549 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +000010550 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +000010551 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
10552 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
10553 pixel_data);
10554 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10555 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000010556 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +000010557 }
10558 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10559 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000010560 CHECK_EQ(i % 256, pixels->get(i));
10561 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +000010562 }
10563
10564 v8::Handle<v8::Object> obj = v8::Object::New();
10565 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
10566 // Set the elements to be the pixels.
10567 // jsobj->set_elements(*pixels);
10568 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
John Reck59135872010-11-02 12:39:01 -070010569 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010570 obj->Set(v8_str("field"), v8::Int32::New(1503));
10571 context->Global()->Set(v8_str("pixels"), obj);
10572 v8::Handle<v8::Value> result = CompileRun("pixels.field");
10573 CHECK_EQ(1503, result->Int32Value());
10574 result = CompileRun("pixels[1]");
10575 CHECK_EQ(1, result->Int32Value());
10576
10577 result = CompileRun("var sum = 0;"
10578 "for (var i = 0; i < 8; i++) {"
10579 " sum += pixels[i] = pixels[i] = -i;"
10580 "}"
10581 "sum;");
10582 CHECK_EQ(-28, result->Int32Value());
10583
10584 result = CompileRun("var sum = 0;"
10585 "for (var i = 0; i < 8; i++) {"
10586 " sum += pixels[i] = pixels[i] = 0;"
10587 "}"
10588 "sum;");
10589 CHECK_EQ(0, result->Int32Value());
10590
10591 result = CompileRun("var sum = 0;"
10592 "for (var i = 0; i < 8; i++) {"
10593 " sum += pixels[i] = pixels[i] = 255;"
10594 "}"
10595 "sum;");
10596 CHECK_EQ(8 * 255, result->Int32Value());
10597
10598 result = CompileRun("var sum = 0;"
10599 "for (var i = 0; i < 8; i++) {"
10600 " sum += pixels[i] = pixels[i] = 256 + i;"
10601 "}"
10602 "sum;");
10603 CHECK_EQ(2076, result->Int32Value());
10604
10605 result = CompileRun("var sum = 0;"
10606 "for (var i = 0; i < 8; i++) {"
10607 " sum += pixels[i] = pixels[i] = i;"
10608 "}"
10609 "sum;");
10610 CHECK_EQ(28, result->Int32Value());
10611
10612 result = CompileRun("var sum = 0;"
10613 "for (var i = 0; i < 8; i++) {"
10614 " sum += pixels[i];"
10615 "}"
10616 "sum;");
10617 CHECK_EQ(28, result->Int32Value());
10618
10619 i::Handle<i::Smi> value(i::Smi::FromInt(2));
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010620 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070010621 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010622 *value.location() = i::Smi::FromInt(256);
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010623 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070010624 CHECK_EQ(255,
10625 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010626 *value.location() = i::Smi::FromInt(-1);
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010627 i::SetElement(jsobj, 1, value, i::kNonStrictMode);
John Reck59135872010-11-02 12:39:01 -070010628 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010629
10630 result = CompileRun("for (var i = 0; i < 8; i++) {"
10631 " pixels[i] = (i * 65) - 109;"
10632 "}"
10633 "pixels[1] + pixels[6];");
10634 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010635 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
10636 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
10637 CHECK_EQ(21,
10638 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
10639 CHECK_EQ(86,
10640 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
10641 CHECK_EQ(151,
10642 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
10643 CHECK_EQ(216,
10644 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
10645 CHECK_EQ(255,
10646 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
10647 CHECK_EQ(255,
10648 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010649 result = CompileRun("var sum = 0;"
10650 "for (var i = 0; i < 8; i++) {"
10651 " sum += pixels[i];"
10652 "}"
10653 "sum;");
10654 CHECK_EQ(984, result->Int32Value());
10655
10656 result = CompileRun("for (var i = 0; i < 8; i++) {"
10657 " pixels[i] = (i * 1.1);"
10658 "}"
10659 "pixels[1] + pixels[6];");
10660 CHECK_EQ(8, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010661 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
10662 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
10663 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
10664 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
10665 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
10666 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
10667 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
10668 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010669
10670 result = CompileRun("for (var i = 0; i < 8; i++) {"
10671 " pixels[7] = undefined;"
10672 "}"
10673 "pixels[7];");
10674 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010675 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010676
10677 result = CompileRun("for (var i = 0; i < 8; i++) {"
10678 " pixels[6] = '2.3';"
10679 "}"
10680 "pixels[6];");
10681 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010682 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010683
10684 result = CompileRun("for (var i = 0; i < 8; i++) {"
10685 " pixels[5] = NaN;"
10686 "}"
10687 "pixels[5];");
10688 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010689 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010690
10691 result = CompileRun("for (var i = 0; i < 8; i++) {"
10692 " pixels[8] = Infinity;"
10693 "}"
10694 "pixels[8];");
10695 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010696 CHECK_EQ(255,
10697 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010698
10699 result = CompileRun("for (var i = 0; i < 8; i++) {"
10700 " pixels[9] = -Infinity;"
10701 "}"
10702 "pixels[9];");
10703 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010704 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010705
10706 result = CompileRun("pixels[3] = 33;"
10707 "delete pixels[3];"
10708 "pixels[3];");
10709 CHECK_EQ(33, result->Int32Value());
10710
10711 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
10712 "pixels[2] = 12; pixels[3] = 13;"
10713 "pixels.__defineGetter__('2',"
10714 "function() { return 120; });"
10715 "pixels[2];");
10716 CHECK_EQ(12, result->Int32Value());
10717
10718 result = CompileRun("var js_array = new Array(40);"
10719 "js_array[0] = 77;"
10720 "js_array;");
10721 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10722
10723 result = CompileRun("pixels[1] = 23;"
10724 "pixels.__proto__ = [];"
10725 "js_array.__proto__ = pixels;"
10726 "js_array.concat(pixels);");
10727 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10728 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10729
10730 result = CompileRun("pixels[1] = 23;");
10731 CHECK_EQ(23, result->Int32Value());
10732
Steve Blockd0582a62009-12-15 09:54:21 +000010733 // Test for index greater than 255. Regression test for:
10734 // http://code.google.com/p/chromium/issues/detail?id=26337.
10735 result = CompileRun("pixels[256] = 255;");
10736 CHECK_EQ(255, result->Int32Value());
10737 result = CompileRun("var i = 0;"
10738 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
10739 "i");
10740 CHECK_EQ(255, result->Int32Value());
10741
Steve Block1e0659c2011-05-24 12:43:12 +010010742 // Make sure that pixel array ICs recognize when a non-pixel array
10743 // is passed to it.
10744 result = CompileRun("function pa_load(p) {"
10745 " var sum = 0;"
10746 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
10747 " return sum;"
10748 "}"
10749 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10750 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
10751 "just_ints = new Object();"
10752 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10753 "for (var i = 0; i < 10; ++i) {"
10754 " result = pa_load(just_ints);"
10755 "}"
10756 "result");
10757 CHECK_EQ(32640, result->Int32Value());
10758
10759 // Make sure that pixel array ICs recognize out-of-bound accesses.
10760 result = CompileRun("function pa_load(p, start) {"
10761 " var sum = 0;"
10762 " for (var j = start; j < 256; j++) { sum += p[j]; }"
10763 " return sum;"
10764 "}"
10765 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10766 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
10767 "for (var i = 0; i < 10; ++i) {"
10768 " result = pa_load(pixels,-10);"
10769 "}"
10770 "result");
10771 CHECK_EQ(0, result->Int32Value());
10772
10773 // Make sure that generic ICs properly handles a pixel array.
10774 result = CompileRun("function pa_load(p) {"
10775 " var sum = 0;"
10776 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
10777 " return sum;"
10778 "}"
10779 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10780 "just_ints = new Object();"
10781 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10782 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
10783 "for (var i = 0; i < 10; ++i) {"
10784 " result = pa_load(pixels);"
10785 "}"
10786 "result");
10787 CHECK_EQ(32640, result->Int32Value());
10788
10789 // Make sure that generic load ICs recognize out-of-bound accesses in
10790 // pixel arrays.
10791 result = CompileRun("function pa_load(p, start) {"
10792 " var sum = 0;"
10793 " for (var j = start; j < 256; j++) { sum += p[j]; }"
10794 " return sum;"
10795 "}"
10796 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10797 "just_ints = new Object();"
10798 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10799 "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }"
10800 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
10801 "for (var i = 0; i < 10; ++i) {"
10802 " result = pa_load(pixels,-10);"
10803 "}"
10804 "result");
10805 CHECK_EQ(0, result->Int32Value());
10806
10807 // Make sure that generic ICs properly handles other types than pixel
10808 // arrays (that the inlined fast pixel array test leaves the right information
10809 // in the right registers).
10810 result = CompileRun("function pa_load(p) {"
10811 " var sum = 0;"
10812 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
10813 " return sum;"
10814 "}"
10815 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10816 "just_ints = new Object();"
10817 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10818 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
10819 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
10820 "sparse_array = new Object();"
10821 "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }"
10822 "sparse_array[1000000] = 3;"
10823 "for (var i = 0; i < 10; ++i) {"
10824 " result = pa_load(sparse_array);"
10825 "}"
10826 "result");
10827 CHECK_EQ(32640, result->Int32Value());
10828
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010829 // Make sure that pixel array store ICs clamp values correctly.
10830 result = CompileRun("function pa_store(p) {"
10831 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }"
10832 "}"
10833 "pa_store(pixels);"
10834 "var sum = 0;"
10835 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
10836 "sum");
10837 CHECK_EQ(48896, result->Int32Value());
10838
10839 // Make sure that pixel array stores correctly handle accesses outside
10840 // of the pixel array..
10841 result = CompileRun("function pa_store(p,start) {"
10842 " for (var j = 0; j < 256; j++) {"
10843 " p[j+start] = j * 2;"
10844 " }"
10845 "}"
10846 "pa_store(pixels,0);"
10847 "pa_store(pixels,-128);"
10848 "var sum = 0;"
10849 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
10850 "sum");
10851 CHECK_EQ(65280, result->Int32Value());
10852
10853 // Make sure that the generic store stub correctly handle accesses outside
10854 // of the pixel array..
10855 result = CompileRun("function pa_store(p,start) {"
10856 " for (var j = 0; j < 256; j++) {"
10857 " p[j+start] = j * 2;"
10858 " }"
10859 "}"
10860 "pa_store(pixels,0);"
10861 "just_ints = new Object();"
10862 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10863 "pa_store(just_ints, 0);"
10864 "pa_store(pixels,-128);"
10865 "var sum = 0;"
10866 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
10867 "sum");
10868 CHECK_EQ(65280, result->Int32Value());
10869
10870 // Make sure that the generic keyed store stub clamps pixel array values
10871 // correctly.
10872 result = CompileRun("function pa_store(p) {"
10873 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }"
10874 "}"
10875 "pa_store(pixels);"
10876 "just_ints = new Object();"
10877 "pa_store(just_ints);"
10878 "pa_store(pixels);"
10879 "var sum = 0;"
10880 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }"
10881 "sum");
10882 CHECK_EQ(48896, result->Int32Value());
10883
10884 // Make sure that pixel array loads are optimized by crankshaft.
Steve Block1e0659c2011-05-24 12:43:12 +010010885 result = CompileRun("function pa_load(p) {"
10886 " var sum = 0;"
10887 " for (var i=0; i<256; ++i) {"
10888 " sum += p[i];"
10889 " }"
10890 " return sum; "
10891 "}"
10892 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10893 "for (var i = 0; i < 10000; ++i) {"
10894 " result = pa_load(pixels);"
10895 "}"
10896 "result");
10897 CHECK_EQ(32640, result->Int32Value());
10898
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010899 // Make sure that pixel array stores are optimized by crankshaft.
10900 result = CompileRun("function pa_init(p) {"
10901 "for (var i = 0; i < 256; ++i) { p[i] = i; }"
10902 "}"
10903 "function pa_load(p) {"
10904 " var sum = 0;"
10905 " for (var i=0; i<256; ++i) {"
10906 " sum += p[i];"
10907 " }"
10908 " return sum; "
10909 "}"
10910 "for (var i = 0; i < 100000; ++i) {"
10911 " pa_init(pixels);"
10912 "}"
10913 "result = pa_load(pixels);"
10914 "result");
10915 CHECK_EQ(32640, result->Int32Value());
10916
Steve Blocka7e24c12009-10-30 11:49:00 +000010917 free(pixel_data);
10918}
10919
10920
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010921THREADED_TEST(PixelArrayInfo) {
10922 v8::HandleScope scope;
10923 LocalContext context;
10924 for (int size = 0; size < 100; size += 10) {
10925 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
10926 v8::Handle<v8::Object> obj = v8::Object::New();
10927 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
10928 CHECK(obj->HasIndexedPropertiesInPixelData());
10929 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
10930 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
10931 free(pixel_data);
10932 }
10933}
10934
10935
Ben Murdoche0cee9b2011-05-25 10:26:03 +010010936static v8::Handle<Value> NotHandledIndexedPropertyGetter(
10937 uint32_t index,
10938 const AccessorInfo& info) {
10939 ApiTestFuzzer::Fuzz();
10940 return v8::Handle<Value>();
10941}
10942
10943
10944static v8::Handle<Value> NotHandledIndexedPropertySetter(
10945 uint32_t index,
10946 Local<Value> value,
10947 const AccessorInfo& info) {
10948 ApiTestFuzzer::Fuzz();
10949 return v8::Handle<Value>();
10950}
10951
10952
10953THREADED_TEST(PixelArrayWithInterceptor) {
10954 v8::HandleScope scope;
10955 LocalContext context;
10956 const int kElementCount = 260;
10957 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
10958 i::Handle<i::PixelArray> pixels =
10959 i::Factory::NewPixelArray(kElementCount, pixel_data);
10960 for (int i = 0; i < kElementCount; i++) {
10961 pixels->set(i, i % 256);
10962 }
10963 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10964 templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter,
10965 NotHandledIndexedPropertySetter);
10966 v8::Handle<v8::Object> obj = templ->NewInstance();
10967 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
10968 context->Global()->Set(v8_str("pixels"), obj);
10969 v8::Handle<v8::Value> result = CompileRun("pixels[1]");
10970 CHECK_EQ(1, result->Int32Value());
10971 result = CompileRun("var sum = 0;"
10972 "for (var i = 0; i < 8; i++) {"
10973 " sum += pixels[i] = pixels[i] = -i;"
10974 "}"
10975 "sum;");
10976 CHECK_EQ(-28, result->Int32Value());
10977 result = CompileRun("pixels.hasOwnProperty('1')");
10978 CHECK(result->BooleanValue());
10979 free(pixel_data);
10980}
10981
10982
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010983static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
10984 switch (array_type) {
10985 case v8::kExternalByteArray:
10986 case v8::kExternalUnsignedByteArray:
10987 return 1;
10988 break;
10989 case v8::kExternalShortArray:
10990 case v8::kExternalUnsignedShortArray:
10991 return 2;
10992 break;
10993 case v8::kExternalIntArray:
10994 case v8::kExternalUnsignedIntArray:
10995 case v8::kExternalFloatArray:
10996 return 4;
10997 break;
10998 default:
10999 UNREACHABLE();
11000 return -1;
11001 }
11002 UNREACHABLE();
11003 return -1;
11004}
11005
11006
Steve Block3ce2e202009-11-05 08:53:23 +000011007template <class ExternalArrayClass, class ElementType>
11008static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
11009 int64_t low,
11010 int64_t high) {
11011 v8::HandleScope scope;
11012 LocalContext context;
11013 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011014 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +000011015 ElementType* array_data =
11016 static_cast<ElementType*>(malloc(kElementCount * element_size));
11017 i::Handle<ExternalArrayClass> array =
11018 i::Handle<ExternalArrayClass>::cast(
11019 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
11020 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
11021 for (int i = 0; i < kElementCount; i++) {
11022 array->set(i, static_cast<ElementType>(i));
11023 }
11024 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
11025 for (int i = 0; i < kElementCount; i++) {
11026 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
11027 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
11028 }
11029
11030 v8::Handle<v8::Object> obj = v8::Object::New();
11031 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
11032 // Set the elements to be the external array.
11033 obj->SetIndexedPropertiesToExternalArrayData(array_data,
11034 array_type,
11035 kElementCount);
John Reck59135872010-11-02 12:39:01 -070011036 CHECK_EQ(
11037 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011038 obj->Set(v8_str("field"), v8::Int32::New(1503));
11039 context->Global()->Set(v8_str("ext_array"), obj);
11040 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
11041 CHECK_EQ(1503, result->Int32Value());
11042 result = CompileRun("ext_array[1]");
11043 CHECK_EQ(1, result->Int32Value());
11044
11045 // Check pass through of assigned smis
11046 result = CompileRun("var sum = 0;"
11047 "for (var i = 0; i < 8; i++) {"
11048 " sum += ext_array[i] = ext_array[i] = -i;"
11049 "}"
11050 "sum;");
11051 CHECK_EQ(-28, result->Int32Value());
11052
11053 // Check assigned smis
11054 result = CompileRun("for (var i = 0; i < 8; i++) {"
11055 " ext_array[i] = i;"
11056 "}"
11057 "var sum = 0;"
11058 "for (var i = 0; i < 8; i++) {"
11059 " sum += ext_array[i];"
11060 "}"
11061 "sum;");
11062 CHECK_EQ(28, result->Int32Value());
11063
11064 // Check assigned smis in reverse order
11065 result = CompileRun("for (var i = 8; --i >= 0; ) {"
11066 " ext_array[i] = i;"
11067 "}"
11068 "var sum = 0;"
11069 "for (var i = 0; i < 8; i++) {"
11070 " sum += ext_array[i];"
11071 "}"
11072 "sum;");
11073 CHECK_EQ(28, result->Int32Value());
11074
11075 // Check pass through of assigned HeapNumbers
11076 result = CompileRun("var sum = 0;"
11077 "for (var i = 0; i < 16; i+=2) {"
11078 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
11079 "}"
11080 "sum;");
11081 CHECK_EQ(-28, result->Int32Value());
11082
11083 // Check assigned HeapNumbers
11084 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
11085 " ext_array[i] = (i * 0.5);"
11086 "}"
11087 "var sum = 0;"
11088 "for (var i = 0; i < 16; i+=2) {"
11089 " sum += ext_array[i];"
11090 "}"
11091 "sum;");
11092 CHECK_EQ(28, result->Int32Value());
11093
11094 // Check assigned HeapNumbers in reverse order
11095 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
11096 " ext_array[i] = (i * 0.5);"
11097 "}"
11098 "var sum = 0;"
11099 "for (var i = 0; i < 16; i+=2) {"
11100 " sum += ext_array[i];"
11101 "}"
11102 "sum;");
11103 CHECK_EQ(28, result->Int32Value());
11104
11105 i::ScopedVector<char> test_buf(1024);
11106
11107 // Check legal boundary conditions.
11108 // The repeated loads and stores ensure the ICs are exercised.
11109 const char* boundary_program =
11110 "var res = 0;"
11111 "for (var i = 0; i < 16; i++) {"
11112 " ext_array[i] = %lld;"
11113 " if (i > 8) {"
11114 " res = ext_array[i];"
11115 " }"
11116 "}"
11117 "res;";
11118 i::OS::SNPrintF(test_buf,
11119 boundary_program,
11120 low);
11121 result = CompileRun(test_buf.start());
11122 CHECK_EQ(low, result->IntegerValue());
11123
11124 i::OS::SNPrintF(test_buf,
11125 boundary_program,
11126 high);
11127 result = CompileRun(test_buf.start());
11128 CHECK_EQ(high, result->IntegerValue());
11129
11130 // Check misprediction of type in IC.
11131 result = CompileRun("var tmp_array = ext_array;"
11132 "var sum = 0;"
11133 "for (var i = 0; i < 8; i++) {"
11134 " tmp_array[i] = i;"
11135 " sum += tmp_array[i];"
11136 " if (i == 4) {"
11137 " tmp_array = {};"
11138 " }"
11139 "}"
11140 "sum;");
11141 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
11142 CHECK_EQ(28, result->Int32Value());
11143
11144 // Make sure out-of-range loads do not throw.
11145 i::OS::SNPrintF(test_buf,
11146 "var caught_exception = false;"
11147 "try {"
11148 " ext_array[%d];"
11149 "} catch (e) {"
11150 " caught_exception = true;"
11151 "}"
11152 "caught_exception;",
11153 kElementCount);
11154 result = CompileRun(test_buf.start());
11155 CHECK_EQ(false, result->BooleanValue());
11156
11157 // Make sure out-of-range stores do not throw.
11158 i::OS::SNPrintF(test_buf,
11159 "var caught_exception = false;"
11160 "try {"
11161 " ext_array[%d] = 1;"
11162 "} catch (e) {"
11163 " caught_exception = true;"
11164 "}"
11165 "caught_exception;",
11166 kElementCount);
11167 result = CompileRun(test_buf.start());
11168 CHECK_EQ(false, result->BooleanValue());
11169
11170 // Check other boundary conditions, values and operations.
11171 result = CompileRun("for (var i = 0; i < 8; i++) {"
11172 " ext_array[7] = undefined;"
11173 "}"
11174 "ext_array[7];");
11175 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011176 CHECK_EQ(
11177 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011178
11179 result = CompileRun("for (var i = 0; i < 8; i++) {"
11180 " ext_array[6] = '2.3';"
11181 "}"
11182 "ext_array[6];");
11183 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011184 CHECK_EQ(
11185 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000011186
11187 if (array_type != v8::kExternalFloatArray) {
11188 // Though the specification doesn't state it, be explicit about
11189 // converting NaNs and +/-Infinity to zero.
11190 result = CompileRun("for (var i = 0; i < 8; i++) {"
11191 " ext_array[i] = 5;"
11192 "}"
11193 "for (var i = 0; i < 8; i++) {"
11194 " ext_array[i] = NaN;"
11195 "}"
11196 "ext_array[5];");
11197 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011198 CHECK_EQ(0,
11199 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000011200
11201 result = CompileRun("for (var i = 0; i < 8; i++) {"
11202 " ext_array[i] = 5;"
11203 "}"
11204 "for (var i = 0; i < 8; i++) {"
11205 " ext_array[i] = Infinity;"
11206 "}"
11207 "ext_array[5];");
11208 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011209 CHECK_EQ(0,
11210 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000011211
11212 result = CompileRun("for (var i = 0; i < 8; i++) {"
11213 " ext_array[i] = 5;"
11214 "}"
11215 "for (var i = 0; i < 8; i++) {"
11216 " ext_array[i] = -Infinity;"
11217 "}"
11218 "ext_array[5];");
11219 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070011220 CHECK_EQ(0,
11221 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block1e0659c2011-05-24 12:43:12 +010011222
11223 // Check truncation behavior of integral arrays.
11224 const char* unsigned_data =
11225 "var source_data = [0.6, 10.6];"
11226 "var expected_results = [0, 10];";
11227 const char* signed_data =
11228 "var source_data = [0.6, 10.6, -0.6, -10.6];"
11229 "var expected_results = [0, 10, 0, -10];";
11230 bool is_unsigned =
11231 (array_type == v8::kExternalUnsignedByteArray ||
11232 array_type == v8::kExternalUnsignedShortArray ||
11233 array_type == v8::kExternalUnsignedIntArray);
11234
11235 i::OS::SNPrintF(test_buf,
11236 "%s"
11237 "var all_passed = true;"
11238 "for (var i = 0; i < source_data.length; i++) {"
11239 " for (var j = 0; j < 8; j++) {"
11240 " ext_array[j] = source_data[i];"
11241 " }"
11242 " all_passed = all_passed &&"
11243 " (ext_array[5] == expected_results[i]);"
11244 "}"
11245 "all_passed;",
11246 (is_unsigned ? unsigned_data : signed_data));
11247 result = CompileRun(test_buf.start());
11248 CHECK_EQ(true, result->BooleanValue());
Steve Block3ce2e202009-11-05 08:53:23 +000011249 }
11250
11251 result = CompileRun("ext_array[3] = 33;"
11252 "delete ext_array[3];"
11253 "ext_array[3];");
11254 CHECK_EQ(33, result->Int32Value());
11255
11256 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
11257 "ext_array[2] = 12; ext_array[3] = 13;"
11258 "ext_array.__defineGetter__('2',"
11259 "function() { return 120; });"
11260 "ext_array[2];");
11261 CHECK_EQ(12, result->Int32Value());
11262
11263 result = CompileRun("var js_array = new Array(40);"
11264 "js_array[0] = 77;"
11265 "js_array;");
11266 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11267
11268 result = CompileRun("ext_array[1] = 23;"
11269 "ext_array.__proto__ = [];"
11270 "js_array.__proto__ = ext_array;"
11271 "js_array.concat(ext_array);");
11272 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11273 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
11274
11275 result = CompileRun("ext_array[1] = 23;");
11276 CHECK_EQ(23, result->Int32Value());
11277
Steve Blockd0582a62009-12-15 09:54:21 +000011278 // Test more complex manipulations which cause eax to contain values
11279 // that won't be completely overwritten by loads from the arrays.
11280 // This catches bugs in the instructions used for the KeyedLoadIC
11281 // for byte and word types.
11282 {
11283 const int kXSize = 300;
11284 const int kYSize = 300;
11285 const int kLargeElementCount = kXSize * kYSize * 4;
11286 ElementType* large_array_data =
11287 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
11288 i::Handle<ExternalArrayClass> large_array =
11289 i::Handle<ExternalArrayClass>::cast(
11290 i::Factory::NewExternalArray(kLargeElementCount,
11291 array_type,
11292 array_data));
11293 v8::Handle<v8::Object> large_obj = v8::Object::New();
11294 // Set the elements to be the external array.
11295 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
11296 array_type,
11297 kLargeElementCount);
11298 context->Global()->Set(v8_str("large_array"), large_obj);
11299 // Initialize contents of a few rows.
11300 for (int x = 0; x < 300; x++) {
11301 int row = 0;
11302 int offset = row * 300 * 4;
11303 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11304 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11305 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11306 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11307 row = 150;
11308 offset = row * 300 * 4;
11309 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11310 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11311 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11312 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11313 row = 298;
11314 offset = row * 300 * 4;
11315 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11316 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11317 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11318 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11319 }
11320 // The goal of the code below is to make "offset" large enough
11321 // that the computation of the index (which goes into eax) has
11322 // high bits set which will not be overwritten by a byte or short
11323 // load.
11324 result = CompileRun("var failed = false;"
11325 "var offset = 0;"
11326 "for (var i = 0; i < 300; i++) {"
11327 " if (large_array[4 * i] != 127 ||"
11328 " large_array[4 * i + 1] != 0 ||"
11329 " large_array[4 * i + 2] != 0 ||"
11330 " large_array[4 * i + 3] != 127) {"
11331 " failed = true;"
11332 " }"
11333 "}"
11334 "offset = 150 * 300 * 4;"
11335 "for (var i = 0; i < 300; i++) {"
11336 " if (large_array[offset + 4 * i] != 127 ||"
11337 " large_array[offset + 4 * i + 1] != 0 ||"
11338 " large_array[offset + 4 * i + 2] != 0 ||"
11339 " large_array[offset + 4 * i + 3] != 127) {"
11340 " failed = true;"
11341 " }"
11342 "}"
11343 "offset = 298 * 300 * 4;"
11344 "for (var i = 0; i < 300; i++) {"
11345 " if (large_array[offset + 4 * i] != 127 ||"
11346 " large_array[offset + 4 * i + 1] != 0 ||"
11347 " large_array[offset + 4 * i + 2] != 0 ||"
11348 " large_array[offset + 4 * i + 3] != 127) {"
11349 " failed = true;"
11350 " }"
11351 "}"
11352 "!failed;");
11353 CHECK_EQ(true, result->BooleanValue());
11354 free(large_array_data);
11355 }
11356
Steve Block3ce2e202009-11-05 08:53:23 +000011357 free(array_data);
11358}
11359
11360
11361THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011362 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011363 v8::kExternalByteArray,
11364 -128,
11365 127);
11366}
11367
11368
11369THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011370 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011371 v8::kExternalUnsignedByteArray,
11372 0,
11373 255);
11374}
11375
11376
11377THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011378 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011379 v8::kExternalShortArray,
11380 -32768,
11381 32767);
11382}
11383
11384
11385THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011386 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011387 v8::kExternalUnsignedShortArray,
11388 0,
11389 65535);
11390}
11391
11392
11393THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011394 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011395 v8::kExternalIntArray,
11396 INT_MIN, // -2147483648
11397 INT_MAX); // 2147483647
11398}
11399
11400
11401THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011402 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011403 v8::kExternalUnsignedIntArray,
11404 0,
11405 UINT_MAX); // 4294967295
11406}
11407
11408
11409THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011410 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000011411 v8::kExternalFloatArray,
11412 -500,
11413 500);
11414}
11415
11416
11417THREADED_TEST(ExternalArrays) {
11418 TestExternalByteArray();
11419 TestExternalUnsignedByteArray();
11420 TestExternalShortArray();
11421 TestExternalUnsignedShortArray();
11422 TestExternalIntArray();
11423 TestExternalUnsignedIntArray();
11424 TestExternalFloatArray();
11425}
11426
11427
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011428void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
11429 v8::HandleScope scope;
11430 LocalContext context;
11431 for (int size = 0; size < 100; size += 10) {
11432 int element_size = ExternalArrayElementSize(array_type);
11433 void* external_data = malloc(size * element_size);
11434 v8::Handle<v8::Object> obj = v8::Object::New();
11435 obj->SetIndexedPropertiesToExternalArrayData(
11436 external_data, array_type, size);
11437 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
11438 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
11439 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
11440 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
11441 free(external_data);
11442 }
11443}
11444
11445
11446THREADED_TEST(ExternalArrayInfo) {
11447 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
11448 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
11449 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
11450 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
11451 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
11452 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
11453 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
11454}
11455
11456
Steve Blocka7e24c12009-10-30 11:49:00 +000011457THREADED_TEST(ScriptContextDependence) {
11458 v8::HandleScope scope;
11459 LocalContext c1;
11460 const char *source = "foo";
11461 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
11462 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
11463 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
11464 CHECK_EQ(dep->Run()->Int32Value(), 100);
11465 CHECK_EQ(indep->Run()->Int32Value(), 100);
11466 LocalContext c2;
11467 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
11468 CHECK_EQ(dep->Run()->Int32Value(), 100);
11469 CHECK_EQ(indep->Run()->Int32Value(), 101);
11470}
11471
11472
11473THREADED_TEST(StackTrace) {
11474 v8::HandleScope scope;
11475 LocalContext context;
11476 v8::TryCatch try_catch;
11477 const char *source = "function foo() { FAIL.FAIL; }; foo();";
11478 v8::Handle<v8::String> src = v8::String::New(source);
11479 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
11480 v8::Script::New(src, origin)->Run();
11481 CHECK(try_catch.HasCaught());
11482 v8::String::Utf8Value stack(try_catch.StackTrace());
11483 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
11484}
11485
11486
Kristian Monsen25f61362010-05-21 11:50:48 +010011487// Checks that a StackFrame has certain expected values.
11488void checkStackFrame(const char* expected_script_name,
11489 const char* expected_func_name, int expected_line_number,
11490 int expected_column, bool is_eval, bool is_constructor,
11491 v8::Handle<v8::StackFrame> frame) {
11492 v8::HandleScope scope;
11493 v8::String::Utf8Value func_name(frame->GetFunctionName());
11494 v8::String::Utf8Value script_name(frame->GetScriptName());
11495 if (*script_name == NULL) {
11496 // The situation where there is no associated script, like for evals.
11497 CHECK(expected_script_name == NULL);
11498 } else {
11499 CHECK(strstr(*script_name, expected_script_name) != NULL);
11500 }
11501 CHECK(strstr(*func_name, expected_func_name) != NULL);
11502 CHECK_EQ(expected_line_number, frame->GetLineNumber());
11503 CHECK_EQ(expected_column, frame->GetColumn());
11504 CHECK_EQ(is_eval, frame->IsEval());
11505 CHECK_EQ(is_constructor, frame->IsConstructor());
11506}
11507
11508
11509v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
11510 v8::HandleScope scope;
11511 const char* origin = "capture-stack-trace-test";
11512 const int kOverviewTest = 1;
11513 const int kDetailedTest = 2;
11514
11515 ASSERT(args.Length() == 1);
11516
11517 int testGroup = args[0]->Int32Value();
11518 if (testGroup == kOverviewTest) {
11519 v8::Handle<v8::StackTrace> stackTrace =
11520 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
11521 CHECK_EQ(4, stackTrace->GetFrameCount());
11522 checkStackFrame(origin, "bar", 2, 10, false, false,
11523 stackTrace->GetFrame(0));
11524 checkStackFrame(origin, "foo", 6, 3, false, false,
11525 stackTrace->GetFrame(1));
11526 checkStackFrame(NULL, "", 1, 1, false, false,
11527 stackTrace->GetFrame(2));
11528 // The last frame is an anonymous function that has the initial call.
11529 checkStackFrame(origin, "", 8, 7, false, false,
11530 stackTrace->GetFrame(3));
11531
11532 CHECK(stackTrace->AsArray()->IsArray());
11533 } else if (testGroup == kDetailedTest) {
11534 v8::Handle<v8::StackTrace> stackTrace =
11535 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
11536 CHECK_EQ(4, stackTrace->GetFrameCount());
11537 checkStackFrame(origin, "bat", 4, 22, false, false,
11538 stackTrace->GetFrame(0));
11539 checkStackFrame(origin, "baz", 8, 3, false, true,
11540 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011541#ifdef ENABLE_DEBUGGER_SUPPORT
11542 bool is_eval = true;
11543#else // ENABLE_DEBUGGER_SUPPORT
11544 bool is_eval = false;
11545#endif // ENABLE_DEBUGGER_SUPPORT
11546
11547 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010011548 stackTrace->GetFrame(2));
11549 // The last frame is an anonymous function that has the initial call to foo.
11550 checkStackFrame(origin, "", 10, 1, false, false,
11551 stackTrace->GetFrame(3));
11552
11553 CHECK(stackTrace->AsArray()->IsArray());
11554 }
11555 return v8::Undefined();
11556}
11557
11558
11559// Tests the C++ StackTrace API.
Ben Murdochb0fe1622011-05-05 13:52:32 +010011560// TODO(3074796): Reenable this as a THREADED_TEST once it passes.
11561// THREADED_TEST(CaptureStackTrace) {
11562TEST(CaptureStackTrace) {
Kristian Monsen25f61362010-05-21 11:50:48 +010011563 v8::HandleScope scope;
11564 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
11565 Local<ObjectTemplate> templ = ObjectTemplate::New();
11566 templ->Set(v8_str("AnalyzeStackInNativeCode"),
11567 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
11568 LocalContext context(0, templ);
11569
11570 // Test getting OVERVIEW information. Should ignore information that is not
11571 // script name, function name, line number, and column offset.
11572 const char *overview_source =
11573 "function bar() {\n"
11574 " var y; AnalyzeStackInNativeCode(1);\n"
11575 "}\n"
11576 "function foo() {\n"
11577 "\n"
11578 " bar();\n"
11579 "}\n"
11580 "var x;eval('new foo();');";
11581 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
11582 v8::Handle<Value> overview_result =
11583 v8::Script::New(overview_src, origin)->Run();
11584 ASSERT(!overview_result.IsEmpty());
11585 ASSERT(overview_result->IsObject());
11586
11587 // Test getting DETAILED information.
11588 const char *detailed_source =
11589 "function bat() {AnalyzeStackInNativeCode(2);\n"
11590 "}\n"
11591 "\n"
11592 "function baz() {\n"
11593 " bat();\n"
11594 "}\n"
11595 "eval('new baz();');";
11596 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
11597 // Make the script using a non-zero line and column offset.
11598 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
11599 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
11600 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
11601 v8::Handle<v8::Script> detailed_script(
11602 v8::Script::New(detailed_src, &detailed_origin));
11603 v8::Handle<Value> detailed_result = detailed_script->Run();
11604 ASSERT(!detailed_result.IsEmpty());
11605 ASSERT(detailed_result->IsObject());
11606}
11607
11608
Ben Murdoch3bec4d22010-07-22 14:51:16 +010011609static void StackTraceForUncaughtExceptionListener(
11610 v8::Handle<v8::Message> message,
11611 v8::Handle<Value>) {
11612 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
11613 CHECK_EQ(2, stack_trace->GetFrameCount());
11614 checkStackFrame("origin", "foo", 2, 3, false, false,
11615 stack_trace->GetFrame(0));
11616 checkStackFrame("origin", "bar", 5, 3, false, false,
11617 stack_trace->GetFrame(1));
11618}
11619
11620TEST(CaptureStackTraceForUncaughtException) {
11621 report_count = 0;
11622 v8::HandleScope scope;
11623 LocalContext env;
11624 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
11625 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
11626
11627 Script::Compile(v8_str("function foo() {\n"
11628 " throw 1;\n"
11629 "};\n"
11630 "function bar() {\n"
11631 " foo();\n"
11632 "};"),
11633 v8_str("origin"))->Run();
11634 v8::Local<v8::Object> global = env->Global();
11635 Local<Value> trouble = global->Get(v8_str("bar"));
11636 CHECK(trouble->IsFunction());
11637 Function::Cast(*trouble)->Call(global, 0, NULL);
11638 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
11639 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
11640}
11641
11642
Steve Block1e0659c2011-05-24 12:43:12 +010011643TEST(CaptureStackTraceForUncaughtExceptionAndSetters) {
11644 v8::HandleScope scope;
11645 LocalContext env;
11646 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
11647 1024,
11648 v8::StackTrace::kDetailed);
11649
11650 CompileRun(
11651 "var setters = ['column', 'lineNumber', 'scriptName',\n"
11652 " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n"
11653 " 'isConstructor'];\n"
11654 "for (var i = 0; i < setters.length; i++) {\n"
11655 " var prop = setters[i];\n"
11656 " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n"
11657 "}\n");
11658 CompileRun("throw 'exception';");
11659 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
11660}
11661
11662
Ben Murdochf87a2032010-10-22 12:50:53 +010011663v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) {
11664 v8::HandleScope scope;
11665 v8::Handle<v8::StackTrace> stackTrace =
11666 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
11667 CHECK_EQ(5, stackTrace->GetFrameCount());
11668 v8::Handle<v8::String> url = v8_str("eval_url");
11669 for (int i = 0; i < 3; i++) {
11670 v8::Handle<v8::String> name =
11671 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL();
11672 CHECK(!name.IsEmpty());
11673 CHECK_EQ(url, name);
11674 }
11675 return v8::Undefined();
11676}
11677
11678
11679TEST(SourceURLInStackTrace) {
11680 v8::HandleScope scope;
11681 Local<ObjectTemplate> templ = ObjectTemplate::New();
11682 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"),
11683 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL));
11684 LocalContext context(0, templ);
11685
11686 const char *source =
11687 "function outer() {\n"
11688 "function bar() {\n"
11689 " AnalyzeStackOfEvalWithSourceURL();\n"
11690 "}\n"
11691 "function foo() {\n"
11692 "\n"
11693 " bar();\n"
11694 "}\n"
11695 "foo();\n"
11696 "}\n"
11697 "eval('(' + outer +')()//@ sourceURL=eval_url');";
11698 CHECK(CompileRun(source)->IsUndefined());
11699}
11700
11701
Steve Block3ce2e202009-11-05 08:53:23 +000011702// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000011703THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000011704 bool rv = false;
11705 for (int i = 0; i < 100; i++) {
11706 rv = v8::V8::IdleNotification();
11707 if (rv)
11708 break;
11709 }
11710 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000011711}
11712
11713
11714static uint32_t* stack_limit;
11715
11716static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080011717 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::real_climit());
Steve Blocka7e24c12009-10-30 11:49:00 +000011718 return v8::Undefined();
11719}
11720
11721
11722// Uses the address of a local variable to determine the stack top now.
11723// Given a size, returns an address that is that far from the current
11724// top of stack.
11725static uint32_t* ComputeStackLimit(uint32_t size) {
11726 uint32_t* answer = &size - (size / sizeof(size));
11727 // If the size is very large and the stack is very near the bottom of
11728 // memory then the calculation above may wrap around and give an address
11729 // that is above the (downwards-growing) stack. In that case we return
11730 // a very low address.
11731 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
11732 return answer;
11733}
11734
11735
11736TEST(SetResourceConstraints) {
11737 static const int K = 1024;
11738 uint32_t* set_limit = ComputeStackLimit(128 * K);
11739
11740 // Set stack limit.
11741 v8::ResourceConstraints constraints;
11742 constraints.set_stack_limit(set_limit);
11743 CHECK(v8::SetResourceConstraints(&constraints));
11744
11745 // Execute a script.
11746 v8::HandleScope scope;
11747 LocalContext env;
11748 Local<v8::FunctionTemplate> fun_templ =
11749 v8::FunctionTemplate::New(GetStackLimitCallback);
11750 Local<Function> fun = fun_templ->GetFunction();
11751 env->Global()->Set(v8_str("get_stack_limit"), fun);
11752 CompileRun("get_stack_limit();");
11753
11754 CHECK(stack_limit == set_limit);
11755}
11756
11757
11758TEST(SetResourceConstraintsInThread) {
11759 uint32_t* set_limit;
11760 {
11761 v8::Locker locker;
11762 static const int K = 1024;
11763 set_limit = ComputeStackLimit(128 * K);
11764
11765 // Set stack limit.
11766 v8::ResourceConstraints constraints;
11767 constraints.set_stack_limit(set_limit);
11768 CHECK(v8::SetResourceConstraints(&constraints));
11769
11770 // Execute a script.
11771 v8::HandleScope scope;
11772 LocalContext env;
11773 Local<v8::FunctionTemplate> fun_templ =
11774 v8::FunctionTemplate::New(GetStackLimitCallback);
11775 Local<Function> fun = fun_templ->GetFunction();
11776 env->Global()->Set(v8_str("get_stack_limit"), fun);
11777 CompileRun("get_stack_limit();");
11778
11779 CHECK(stack_limit == set_limit);
11780 }
11781 {
11782 v8::Locker locker;
11783 CHECK(stack_limit == set_limit);
11784 }
11785}
Steve Block3ce2e202009-11-05 08:53:23 +000011786
11787
11788THREADED_TEST(GetHeapStatistics) {
11789 v8::HandleScope scope;
11790 LocalContext c1;
11791 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000011792 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
11793 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000011794 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000011795 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
11796 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
11797}
11798
11799
11800static double DoubleFromBits(uint64_t value) {
11801 double target;
11802#ifdef BIG_ENDIAN_FLOATING_POINT
11803 const int kIntSize = 4;
11804 // Somebody swapped the lower and higher half of doubles.
11805 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
11806 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
11807#else
11808 memcpy(&target, &value, sizeof(target));
11809#endif
11810 return target;
11811}
11812
11813
11814static uint64_t DoubleToBits(double value) {
11815 uint64_t target;
11816#ifdef BIG_ENDIAN_FLOATING_POINT
11817 const int kIntSize = 4;
11818 // Somebody swapped the lower and higher half of doubles.
11819 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
11820 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
11821#else
11822 memcpy(&target, &value, sizeof(target));
11823#endif
11824 return target;
11825}
11826
11827
11828static double DoubleToDateTime(double input) {
11829 double date_limit = 864e13;
11830 if (IsNaN(input) || input < -date_limit || input > date_limit) {
11831 return i::OS::nan_value();
11832 }
11833 return (input < 0) ? -(floor(-input)) : floor(input);
11834}
11835
11836// We don't have a consistent way to write 64-bit constants syntactically, so we
11837// split them into two 32-bit constants and combine them programmatically.
11838static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
11839 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
11840}
11841
11842
11843THREADED_TEST(QuietSignalingNaNs) {
11844 v8::HandleScope scope;
11845 LocalContext context;
11846 v8::TryCatch try_catch;
11847
11848 // Special double values.
11849 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
11850 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
11851 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
11852 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
11853 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
11854 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
11855 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
11856
11857 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
11858 // on either side of the epoch.
11859 double date_limit = 864e13;
11860
11861 double test_values[] = {
11862 snan,
11863 qnan,
11864 infinity,
11865 max_normal,
11866 date_limit + 1,
11867 date_limit,
11868 min_normal,
11869 max_denormal,
11870 min_denormal,
11871 0,
11872 -0,
11873 -min_denormal,
11874 -max_denormal,
11875 -min_normal,
11876 -date_limit,
11877 -date_limit - 1,
11878 -max_normal,
11879 -infinity,
11880 -qnan,
11881 -snan
11882 };
11883 int num_test_values = 20;
11884
11885 for (int i = 0; i < num_test_values; i++) {
11886 double test_value = test_values[i];
11887
11888 // Check that Number::New preserves non-NaNs and quiets SNaNs.
11889 v8::Handle<v8::Value> number = v8::Number::New(test_value);
11890 double stored_number = number->NumberValue();
11891 if (!IsNaN(test_value)) {
11892 CHECK_EQ(test_value, stored_number);
11893 } else {
11894 uint64_t stored_bits = DoubleToBits(stored_number);
11895 // Check if quiet nan (bits 51..62 all set).
11896 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
11897 }
11898
11899 // Check that Date::New preserves non-NaNs in the date range and
11900 // quiets SNaNs.
11901 v8::Handle<v8::Value> date = v8::Date::New(test_value);
11902 double expected_stored_date = DoubleToDateTime(test_value);
11903 double stored_date = date->NumberValue();
11904 if (!IsNaN(expected_stored_date)) {
11905 CHECK_EQ(expected_stored_date, stored_date);
11906 } else {
11907 uint64_t stored_bits = DoubleToBits(stored_date);
11908 // Check if quiet nan (bits 51..62 all set).
11909 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
11910 }
11911 }
11912}
11913
11914
11915static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
11916 v8::HandleScope scope;
11917 v8::TryCatch tc;
11918 v8::Handle<v8::String> str = args[0]->ToString();
11919 if (tc.HasCaught())
11920 return tc.ReThrow();
11921 return v8::Undefined();
11922}
11923
11924
11925// Test that an exception can be propagated down through a spaghetti
11926// stack using ReThrow.
11927THREADED_TEST(SpaghettiStackReThrow) {
11928 v8::HandleScope scope;
11929 LocalContext context;
11930 context->Global()->Set(
11931 v8::String::New("s"),
11932 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
11933 v8::TryCatch try_catch;
11934 CompileRun(
11935 "var i = 0;"
11936 "var o = {"
11937 " toString: function () {"
11938 " if (i == 10) {"
11939 " throw 'Hey!';"
11940 " } else {"
11941 " i++;"
11942 " return s(o);"
11943 " }"
11944 " }"
11945 "};"
11946 "s(o);");
11947 CHECK(try_catch.HasCaught());
11948 v8::String::Utf8Value value(try_catch.Exception());
11949 CHECK_EQ(0, strcmp(*value, "Hey!"));
11950}
11951
11952
Steve Blockd0582a62009-12-15 09:54:21 +000011953TEST(Regress528) {
11954 v8::V8::Initialize();
11955
11956 v8::HandleScope scope;
11957 v8::Persistent<Context> context;
11958 v8::Persistent<Context> other_context;
11959 int gc_count;
11960
11961 // Create a context used to keep the code from aging in the compilation
11962 // cache.
11963 other_context = Context::New();
11964
11965 // Context-dependent context data creates reference from the compilation
11966 // cache to the global object.
11967 const char* source_simple = "1";
11968 context = Context::New();
11969 {
11970 v8::HandleScope scope;
11971
11972 context->Enter();
11973 Local<v8::String> obj = v8::String::New("");
11974 context->SetData(obj);
11975 CompileRun(source_simple);
11976 context->Exit();
11977 }
11978 context.Dispose();
11979 for (gc_count = 1; gc_count < 10; gc_count++) {
11980 other_context->Enter();
11981 CompileRun(source_simple);
11982 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010011983 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000011984 if (GetGlobalObjectsCount() == 1) break;
11985 }
11986 CHECK_GE(2, gc_count);
11987 CHECK_EQ(1, GetGlobalObjectsCount());
11988
11989 // Eval in a function creates reference from the compilation cache to the
11990 // global object.
11991 const char* source_eval = "function f(){eval('1')}; f()";
11992 context = Context::New();
11993 {
11994 v8::HandleScope scope;
11995
11996 context->Enter();
11997 CompileRun(source_eval);
11998 context->Exit();
11999 }
12000 context.Dispose();
12001 for (gc_count = 1; gc_count < 10; gc_count++) {
12002 other_context->Enter();
12003 CompileRun(source_eval);
12004 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010012005 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012006 if (GetGlobalObjectsCount() == 1) break;
12007 }
12008 CHECK_GE(2, gc_count);
12009 CHECK_EQ(1, GetGlobalObjectsCount());
12010
12011 // Looking up the line number for an exception creates reference from the
12012 // compilation cache to the global object.
12013 const char* source_exception = "function f(){throw 1;} f()";
12014 context = Context::New();
12015 {
12016 v8::HandleScope scope;
12017
12018 context->Enter();
12019 v8::TryCatch try_catch;
12020 CompileRun(source_exception);
12021 CHECK(try_catch.HasCaught());
12022 v8::Handle<v8::Message> message = try_catch.Message();
12023 CHECK(!message.IsEmpty());
12024 CHECK_EQ(1, message->GetLineNumber());
12025 context->Exit();
12026 }
12027 context.Dispose();
12028 for (gc_count = 1; gc_count < 10; gc_count++) {
12029 other_context->Enter();
12030 CompileRun(source_exception);
12031 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010012032 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000012033 if (GetGlobalObjectsCount() == 1) break;
12034 }
12035 CHECK_GE(2, gc_count);
12036 CHECK_EQ(1, GetGlobalObjectsCount());
12037
12038 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000012039}
Andrei Popescu402d9372010-02-26 13:31:12 +000012040
12041
12042THREADED_TEST(ScriptOrigin) {
12043 v8::HandleScope scope;
12044 LocalContext env;
12045 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
12046 v8::Handle<v8::String> script = v8::String::New(
12047 "function f() {}\n\nfunction g() {}");
12048 v8::Script::Compile(script, &origin)->Run();
12049 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
12050 env->Global()->Get(v8::String::New("f")));
12051 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
12052 env->Global()->Get(v8::String::New("g")));
12053
12054 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
12055 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
12056 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
12057
12058 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
12059 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
12060 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
12061}
12062
12063
12064THREADED_TEST(ScriptLineNumber) {
12065 v8::HandleScope scope;
12066 LocalContext env;
12067 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
12068 v8::Handle<v8::String> script = v8::String::New(
12069 "function f() {}\n\nfunction g() {}");
12070 v8::Script::Compile(script, &origin)->Run();
12071 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
12072 env->Global()->Get(v8::String::New("f")));
12073 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
12074 env->Global()->Get(v8::String::New("g")));
12075 CHECK_EQ(0, f->GetScriptLineNumber());
12076 CHECK_EQ(2, g->GetScriptLineNumber());
12077}
12078
12079
12080static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
12081 const AccessorInfo& info) {
12082 return v8_num(42);
12083}
12084
12085
12086static void SetterWhichSetsYOnThisTo23(Local<String> name,
12087 Local<Value> value,
12088 const AccessorInfo& info) {
12089 info.This()->Set(v8_str("y"), v8_num(23));
12090}
12091
12092
Steve Block6ded16b2010-05-10 14:33:55 +010012093TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000012094 v8::HandleScope scope;
12095 Local<ObjectTemplate> templ = ObjectTemplate::New();
12096 templ->SetAccessor(v8_str("x"),
12097 GetterWhichReturns42,
12098 SetterWhichSetsYOnThisTo23);
12099 LocalContext context;
12100 context->Global()->Set(v8_str("P"), templ->NewInstance());
12101 CompileRun("function C1() {"
12102 " this.x = 23;"
12103 "};"
12104 "C1.prototype = P;"
12105 "function C2() {"
12106 " this.x = 23"
12107 "};"
12108 "C2.prototype = { };"
12109 "C2.prototype.__proto__ = P;");
12110
12111 v8::Local<v8::Script> script;
12112 script = v8::Script::Compile(v8_str("new C1();"));
12113 for (int i = 0; i < 10; i++) {
12114 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12115 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
12116 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
12117 }
12118
12119 script = v8::Script::Compile(v8_str("new C2();"));
12120 for (int i = 0; i < 10; i++) {
12121 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
12122 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
12123 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
12124 }
12125}
12126
12127
12128static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
12129 Local<String> name, const AccessorInfo& info) {
12130 return v8_num(42);
12131}
12132
12133
12134static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
12135 Local<String> name, Local<Value> value, const AccessorInfo& info) {
12136 if (name->Equals(v8_str("x"))) {
12137 info.This()->Set(v8_str("y"), v8_num(23));
12138 }
12139 return v8::Handle<Value>();
12140}
12141
12142
12143THREADED_TEST(InterceptorOnConstructorPrototype) {
12144 v8::HandleScope scope;
12145 Local<ObjectTemplate> templ = ObjectTemplate::New();
12146 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
12147 NamedPropertySetterWhichSetsYOnThisTo23);
12148 LocalContext context;
12149 context->Global()->Set(v8_str("P"), templ->NewInstance());
12150 CompileRun("function C1() {"
12151 " this.x = 23;"
12152 "};"
12153 "C1.prototype = P;"
12154 "function C2() {"
12155 " this.x = 23"
12156 "};"
12157 "C2.prototype = { };"
12158 "C2.prototype.__proto__ = P;");
12159
12160 v8::Local<v8::Script> script;
12161 script = v8::Script::Compile(v8_str("new C1();"));
12162 for (int i = 0; i < 10; i++) {
12163 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12164 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
12165 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
12166 }
12167
12168 script = v8::Script::Compile(v8_str("new C2();"));
12169 for (int i = 0; i < 10; i++) {
12170 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
12171 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
12172 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
12173 }
12174}
Steve Block6ded16b2010-05-10 14:33:55 +010012175
12176
12177TEST(Bug618) {
12178 const char* source = "function C1() {"
12179 " this.x = 23;"
12180 "};"
12181 "C1.prototype = P;";
12182
12183 v8::HandleScope scope;
12184 LocalContext context;
12185 v8::Local<v8::Script> script;
12186
12187 // Use a simple object as prototype.
12188 v8::Local<v8::Object> prototype = v8::Object::New();
12189 prototype->Set(v8_str("y"), v8_num(42));
12190 context->Global()->Set(v8_str("P"), prototype);
12191
12192 // This compile will add the code to the compilation cache.
12193 CompileRun(source);
12194
12195 script = v8::Script::Compile(v8_str("new C1();"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +010012196 // Allow enough iterations for the inobject slack tracking logic
12197 // to finalize instance size and install the fast construct stub.
12198 for (int i = 0; i < 256; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +010012199 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12200 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
12201 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
12202 }
12203
12204 // Use an API object with accessors as prototype.
12205 Local<ObjectTemplate> templ = ObjectTemplate::New();
12206 templ->SetAccessor(v8_str("x"),
12207 GetterWhichReturns42,
12208 SetterWhichSetsYOnThisTo23);
12209 context->Global()->Set(v8_str("P"), templ->NewInstance());
12210
12211 // This compile will get the code from the compilation cache.
12212 CompileRun(source);
12213
12214 script = v8::Script::Compile(v8_str("new C1();"));
12215 for (int i = 0; i < 10; i++) {
12216 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
12217 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
12218 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
12219 }
12220}
12221
12222int prologue_call_count = 0;
12223int epilogue_call_count = 0;
12224int prologue_call_count_second = 0;
12225int epilogue_call_count_second = 0;
12226
12227void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
12228 ++prologue_call_count;
12229}
12230
12231void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
12232 ++epilogue_call_count;
12233}
12234
12235void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
12236 ++prologue_call_count_second;
12237}
12238
12239void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
12240 ++epilogue_call_count_second;
12241}
12242
12243TEST(GCCallbacks) {
12244 LocalContext context;
12245
12246 v8::V8::AddGCPrologueCallback(PrologueCallback);
12247 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
12248 CHECK_EQ(0, prologue_call_count);
12249 CHECK_EQ(0, epilogue_call_count);
12250 i::Heap::CollectAllGarbage(false);
12251 CHECK_EQ(1, prologue_call_count);
12252 CHECK_EQ(1, epilogue_call_count);
12253 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
12254 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
12255 i::Heap::CollectAllGarbage(false);
12256 CHECK_EQ(2, prologue_call_count);
12257 CHECK_EQ(2, epilogue_call_count);
12258 CHECK_EQ(1, prologue_call_count_second);
12259 CHECK_EQ(1, epilogue_call_count_second);
12260 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
12261 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
12262 i::Heap::CollectAllGarbage(false);
12263 CHECK_EQ(2, prologue_call_count);
12264 CHECK_EQ(2, epilogue_call_count);
12265 CHECK_EQ(2, prologue_call_count_second);
12266 CHECK_EQ(2, epilogue_call_count_second);
12267 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
12268 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
12269 i::Heap::CollectAllGarbage(false);
12270 CHECK_EQ(2, prologue_call_count);
12271 CHECK_EQ(2, epilogue_call_count);
12272 CHECK_EQ(2, prologue_call_count_second);
12273 CHECK_EQ(2, epilogue_call_count_second);
12274}
Kristian Monsen25f61362010-05-21 11:50:48 +010012275
12276
12277THREADED_TEST(AddToJSFunctionResultCache) {
12278 i::FLAG_allow_natives_syntax = true;
12279 v8::HandleScope scope;
12280
12281 LocalContext context;
12282
12283 const char* code =
12284 "(function() {"
12285 " var key0 = 'a';"
12286 " var key1 = 'b';"
12287 " var r0 = %_GetFromCache(0, key0);"
12288 " var r1 = %_GetFromCache(0, key1);"
12289 " var r0_ = %_GetFromCache(0, key0);"
12290 " if (r0 !== r0_)"
12291 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
12292 " var r1_ = %_GetFromCache(0, key1);"
12293 " if (r1 !== r1_)"
12294 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
12295 " return 'PASSED';"
12296 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012297 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012298 ExpectString(code, "PASSED");
12299}
12300
12301
12302static const int k0CacheSize = 16;
12303
12304THREADED_TEST(FillJSFunctionResultCache) {
12305 i::FLAG_allow_natives_syntax = true;
12306 v8::HandleScope scope;
12307
12308 LocalContext context;
12309
12310 const char* code =
12311 "(function() {"
12312 " var k = 'a';"
12313 " var r = %_GetFromCache(0, k);"
12314 " for (var i = 0; i < 16; i++) {"
12315 " %_GetFromCache(0, 'a' + i);"
12316 " };"
12317 " if (r === %_GetFromCache(0, k))"
12318 " return 'FAILED: k0CacheSize is too small';"
12319 " return 'PASSED';"
12320 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012321 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012322 ExpectString(code, "PASSED");
12323}
12324
12325
12326THREADED_TEST(RoundRobinGetFromCache) {
12327 i::FLAG_allow_natives_syntax = true;
12328 v8::HandleScope scope;
12329
12330 LocalContext context;
12331
12332 const char* code =
12333 "(function() {"
12334 " var keys = [];"
12335 " for (var i = 0; i < 16; i++) keys.push(i);"
12336 " var values = [];"
12337 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
12338 " for (var i = 0; i < 16; i++) {"
12339 " var v = %_GetFromCache(0, keys[i]);"
12340 " if (v !== values[i])"
12341 " return 'Wrong value for ' + "
12342 " keys[i] + ': ' + v + ' vs. ' + values[i];"
12343 " };"
12344 " return 'PASSED';"
12345 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012346 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012347 ExpectString(code, "PASSED");
12348}
12349
12350
12351THREADED_TEST(ReverseGetFromCache) {
12352 i::FLAG_allow_natives_syntax = true;
12353 v8::HandleScope scope;
12354
12355 LocalContext context;
12356
12357 const char* code =
12358 "(function() {"
12359 " var keys = [];"
12360 " for (var i = 0; i < 16; i++) keys.push(i);"
12361 " var values = [];"
12362 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
12363 " for (var i = 15; i >= 16; i--) {"
12364 " var v = %_GetFromCache(0, keys[i]);"
12365 " if (v !== values[i])"
12366 " return 'Wrong value for ' + "
12367 " keys[i] + ': ' + v + ' vs. ' + values[i];"
12368 " };"
12369 " return 'PASSED';"
12370 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012371 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012372 ExpectString(code, "PASSED");
12373}
12374
12375
12376THREADED_TEST(TestEviction) {
12377 i::FLAG_allow_natives_syntax = true;
12378 v8::HandleScope scope;
12379
12380 LocalContext context;
12381
12382 const char* code =
12383 "(function() {"
12384 " for (var i = 0; i < 2*16; i++) {"
12385 " %_GetFromCache(0, 'a' + i);"
12386 " };"
12387 " return 'PASSED';"
12388 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012389 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012390 ExpectString(code, "PASSED");
12391}
Steve Block8defd9f2010-07-08 12:39:36 +010012392
12393
12394THREADED_TEST(TwoByteStringInAsciiCons) {
12395 // See Chromium issue 47824.
12396 v8::HandleScope scope;
12397
12398 LocalContext context;
12399 const char* init_code =
12400 "var str1 = 'abelspendabel';"
12401 "var str2 = str1 + str1 + str1;"
12402 "str2;";
12403 Local<Value> result = CompileRun(init_code);
12404
12405 CHECK(result->IsString());
12406 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
12407 int length = string->length();
12408 CHECK(string->IsAsciiRepresentation());
12409
12410 FlattenString(string);
12411 i::Handle<i::String> flat_string = FlattenGetString(string);
12412
12413 CHECK(string->IsAsciiRepresentation());
12414 CHECK(flat_string->IsAsciiRepresentation());
12415
12416 // Create external resource.
12417 uint16_t* uc16_buffer = new uint16_t[length + 1];
12418
12419 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
12420 uc16_buffer[length] = 0;
12421
12422 TestResource resource(uc16_buffer);
12423
12424 flat_string->MakeExternal(&resource);
12425
12426 CHECK(flat_string->IsTwoByteRepresentation());
12427
12428 // At this point, we should have a Cons string which is flat and ASCII,
12429 // with a first half that is a two-byte string (although it only contains
12430 // ASCII characters). This is a valid sequence of steps, and it can happen
12431 // in real pages.
12432
12433 CHECK(string->IsAsciiRepresentation());
12434 i::ConsString* cons = i::ConsString::cast(*string);
12435 CHECK_EQ(0, cons->second()->length());
12436 CHECK(cons->first()->IsTwoByteRepresentation());
12437
12438 // Check that some string operations work.
12439
12440 // Atom RegExp.
12441 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
12442 CHECK_EQ(6, reresult->Int32Value());
12443
12444 // Nonatom RegExp.
12445 reresult = CompileRun("str2.match(/abe./g).length;");
12446 CHECK_EQ(6, reresult->Int32Value());
12447
12448 reresult = CompileRun("str2.search(/bel/g);");
12449 CHECK_EQ(1, reresult->Int32Value());
12450
12451 reresult = CompileRun("str2.search(/be./g);");
12452 CHECK_EQ(1, reresult->Int32Value());
12453
12454 ExpectTrue("/bel/g.test(str2);");
12455
12456 ExpectTrue("/be./g.test(str2);");
12457
12458 reresult = CompileRun("/bel/g.exec(str2);");
12459 CHECK(!reresult->IsNull());
12460
12461 reresult = CompileRun("/be./g.exec(str2);");
12462 CHECK(!reresult->IsNull());
12463
12464 ExpectString("str2.substring(2, 10);", "elspenda");
12465
12466 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
12467
12468 ExpectString("str2.charAt(2);", "e");
12469
12470 reresult = CompileRun("str2.charCodeAt(2);");
12471 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
12472}
Iain Merrick75681382010-08-19 15:07:18 +010012473
12474
12475// Failed access check callback that performs a GC on each invocation.
12476void FailedAccessCheckCallbackGC(Local<v8::Object> target,
12477 v8::AccessType type,
12478 Local<v8::Value> data) {
12479 i::Heap::CollectAllGarbage(true);
12480}
12481
12482
12483TEST(GCInFailedAccessCheckCallback) {
12484 // Install a failed access check callback that performs a GC on each
12485 // invocation. Then force the callback to be called from va
12486
12487 v8::V8::Initialize();
12488 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
12489
12490 v8::HandleScope scope;
12491
12492 // Create an ObjectTemplate for global objects and install access
12493 // check callbacks that will block access.
12494 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
12495 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
12496 IndexedGetAccessBlocker,
12497 v8::Handle<v8::Value>(),
12498 false);
12499
12500 // Create a context and set an x property on it's global object.
12501 LocalContext context0(NULL, global_template);
12502 context0->Global()->Set(v8_str("x"), v8_num(42));
12503 v8::Handle<v8::Object> global0 = context0->Global();
12504
12505 // Create a context with a different security token so that the
12506 // failed access check callback will be called on each access.
12507 LocalContext context1(NULL, global_template);
12508 context1->Global()->Set(v8_str("other"), global0);
12509
12510 // Get property with failed access check.
12511 ExpectUndefined("other.x");
12512
12513 // Get element with failed access check.
12514 ExpectUndefined("other[0]");
12515
12516 // Set property with failed access check.
12517 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
12518 CHECK(result->IsObject());
12519
12520 // Set element with failed access check.
12521 result = CompileRun("other[0] = new Object()");
12522 CHECK(result->IsObject());
12523
12524 // Get property attribute with failed access check.
12525 ExpectFalse("\'x\' in other");
12526
12527 // Get property attribute for element with failed access check.
12528 ExpectFalse("0 in other");
12529
12530 // Delete property.
12531 ExpectFalse("delete other.x");
12532
12533 // Delete element.
12534 CHECK_EQ(false, global0->Delete(0));
12535
12536 // DefineAccessor.
12537 CHECK_EQ(false,
12538 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
12539
12540 // Define JavaScript accessor.
12541 ExpectUndefined("Object.prototype.__defineGetter__.call("
12542 " other, \'x\', function() { return 42; })");
12543
12544 // LookupAccessor.
12545 ExpectUndefined("Object.prototype.__lookupGetter__.call("
12546 " other, \'x\')");
12547
12548 // HasLocalElement.
12549 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
12550
12551 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
12552 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
12553 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
12554
12555 // Reset the failed access check callback so it does not influence
12556 // the other tests.
12557 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
12558}
Kristian Monsen0d5e1162010-09-30 15:31:59 +010012559
12560
12561TEST(StringCheckMultipleContexts) {
12562 const char* code =
12563 "(function() { return \"a\".charAt(0); })()";
12564
12565 {
12566 // Run the code twice in the first context to initialize the call IC.
12567 v8::HandleScope scope;
12568 LocalContext context1;
12569 ExpectString(code, "a");
12570 ExpectString(code, "a");
12571 }
12572
12573 {
12574 // Change the String.prototype in the second context and check
12575 // that the right function gets called.
12576 v8::HandleScope scope;
12577 LocalContext context2;
12578 CompileRun("String.prototype.charAt = function() { return \"not a\"; }");
12579 ExpectString(code, "not a");
12580 }
12581}
12582
12583
12584TEST(NumberCheckMultipleContexts) {
12585 const char* code =
12586 "(function() { return (42).toString(); })()";
12587
12588 {
12589 // Run the code twice in the first context to initialize the call IC.
12590 v8::HandleScope scope;
12591 LocalContext context1;
12592 ExpectString(code, "42");
12593 ExpectString(code, "42");
12594 }
12595
12596 {
12597 // Change the Number.prototype in the second context and check
12598 // that the right function gets called.
12599 v8::HandleScope scope;
12600 LocalContext context2;
12601 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }");
12602 ExpectString(code, "not 42");
12603 }
12604}
12605
12606
12607TEST(BooleanCheckMultipleContexts) {
12608 const char* code =
12609 "(function() { return true.toString(); })()";
12610
12611 {
12612 // Run the code twice in the first context to initialize the call IC.
12613 v8::HandleScope scope;
12614 LocalContext context1;
12615 ExpectString(code, "true");
12616 ExpectString(code, "true");
12617 }
12618
12619 {
12620 // Change the Boolean.prototype in the second context and check
12621 // that the right function gets called.
12622 v8::HandleScope scope;
12623 LocalContext context2;
12624 CompileRun("Boolean.prototype.toString = function() { return \"\"; }");
12625 ExpectString(code, "");
12626 }
12627}
Ben Murdochf87a2032010-10-22 12:50:53 +010012628
12629
12630TEST(DontDeleteCellLoadIC) {
12631 const char* function_code =
12632 "function readCell() { while (true) { return cell; } }";
12633
12634 {
12635 // Run the code twice in the first context to initialize the load
12636 // IC for a don't delete cell.
12637 v8::HandleScope scope;
12638 LocalContext context1;
12639 CompileRun("var cell = \"first\";");
12640 ExpectBoolean("delete cell", false);
12641 CompileRun(function_code);
12642 ExpectString("readCell()", "first");
12643 ExpectString("readCell()", "first");
12644 }
12645
12646 {
12647 // Use a deletable cell in the second context.
12648 v8::HandleScope scope;
12649 LocalContext context2;
12650 CompileRun("cell = \"second\";");
12651 CompileRun(function_code);
12652 ExpectString("readCell()", "second");
12653 ExpectBoolean("delete cell", true);
12654 ExpectString("(function() {"
12655 " try {"
12656 " return readCell();"
12657 " } catch(e) {"
12658 " return e.toString();"
12659 " }"
12660 "})()",
12661 "ReferenceError: cell is not defined");
12662 CompileRun("cell = \"new_second\";");
12663 i::Heap::CollectAllGarbage(true);
12664 ExpectString("readCell()", "new_second");
12665 ExpectString("readCell()", "new_second");
12666 }
12667}
12668
12669
12670TEST(DontDeleteCellLoadICForceDelete) {
12671 const char* function_code =
12672 "function readCell() { while (true) { return cell; } }";
12673
12674 // Run the code twice to initialize the load IC for a don't delete
12675 // cell.
12676 v8::HandleScope scope;
12677 LocalContext context;
12678 CompileRun("var cell = \"value\";");
12679 ExpectBoolean("delete cell", false);
12680 CompileRun(function_code);
12681 ExpectString("readCell()", "value");
12682 ExpectString("readCell()", "value");
12683
12684 // Delete the cell using the API and check the inlined code works
12685 // correctly.
12686 CHECK(context->Global()->ForceDelete(v8_str("cell")));
12687 ExpectString("(function() {"
12688 " try {"
12689 " return readCell();"
12690 " } catch(e) {"
12691 " return e.toString();"
12692 " }"
12693 "})()",
12694 "ReferenceError: cell is not defined");
12695}
12696
12697
12698TEST(DontDeleteCellLoadICAPI) {
12699 const char* function_code =
12700 "function readCell() { while (true) { return cell; } }";
12701
12702 // Run the code twice to initialize the load IC for a don't delete
12703 // cell created using the API.
12704 v8::HandleScope scope;
12705 LocalContext context;
12706 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete);
12707 ExpectBoolean("delete cell", false);
12708 CompileRun(function_code);
12709 ExpectString("readCell()", "value");
12710 ExpectString("readCell()", "value");
12711
12712 // Delete the cell using the API and check the inlined code works
12713 // correctly.
12714 CHECK(context->Global()->ForceDelete(v8_str("cell")));
12715 ExpectString("(function() {"
12716 " try {"
12717 " return readCell();"
12718 " } catch(e) {"
12719 " return e.toString();"
12720 " }"
12721 "})()",
12722 "ReferenceError: cell is not defined");
12723}
12724
12725
12726TEST(GlobalLoadICGC) {
12727 const char* function_code =
12728 "function readCell() { while (true) { return cell; } }";
12729
12730 // Check inline load code for a don't delete cell is cleared during
12731 // GC.
12732 {
12733 v8::HandleScope scope;
12734 LocalContext context;
12735 CompileRun("var cell = \"value\";");
12736 ExpectBoolean("delete cell", false);
12737 CompileRun(function_code);
12738 ExpectString("readCell()", "value");
12739 ExpectString("readCell()", "value");
12740 }
12741 {
12742 v8::HandleScope scope;
12743 LocalContext context2;
12744 // Hold the code object in the second context.
12745 CompileRun(function_code);
12746 CheckSurvivingGlobalObjectsCount(1);
12747 }
12748
12749 // Check inline load code for a deletable cell is cleared during GC.
12750 {
12751 v8::HandleScope scope;
12752 LocalContext context;
12753 CompileRun("cell = \"value\";");
12754 CompileRun(function_code);
12755 ExpectString("readCell()", "value");
12756 ExpectString("readCell()", "value");
12757 }
12758 {
12759 v8::HandleScope scope;
12760 LocalContext context2;
12761 // Hold the code object in the second context.
12762 CompileRun(function_code);
12763 CheckSurvivingGlobalObjectsCount(1);
12764 }
12765}
12766
12767
12768TEST(RegExp) {
12769 v8::HandleScope scope;
12770 LocalContext context;
12771
12772 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone);
12773 CHECK(re->IsRegExp());
12774 CHECK(re->GetSource()->Equals(v8_str("foo")));
12775 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
12776
12777 re = v8::RegExp::New(v8_str("bar"),
12778 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
12779 v8::RegExp::kGlobal));
12780 CHECK(re->IsRegExp());
12781 CHECK(re->GetSource()->Equals(v8_str("bar")));
12782 CHECK_EQ(static_cast<int>(re->GetFlags()),
12783 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal);
12784
12785 re = v8::RegExp::New(v8_str("baz"),
12786 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
12787 v8::RegExp::kMultiline));
12788 CHECK(re->IsRegExp());
12789 CHECK(re->GetSource()->Equals(v8_str("baz")));
12790 CHECK_EQ(static_cast<int>(re->GetFlags()),
12791 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
12792
12793 re = CompileRun("/quux/").As<v8::RegExp>();
12794 CHECK(re->IsRegExp());
12795 CHECK(re->GetSource()->Equals(v8_str("quux")));
12796 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
12797
12798 re = CompileRun("/quux/gm").As<v8::RegExp>();
12799 CHECK(re->IsRegExp());
12800 CHECK(re->GetSource()->Equals(v8_str("quux")));
12801 CHECK_EQ(static_cast<int>(re->GetFlags()),
12802 v8::RegExp::kGlobal | v8::RegExp::kMultiline);
12803
12804 // Override the RegExp constructor and check the API constructor
12805 // still works.
12806 CompileRun("RegExp = function() {}");
12807
12808 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone);
12809 CHECK(re->IsRegExp());
12810 CHECK(re->GetSource()->Equals(v8_str("foobar")));
12811 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
12812
12813 re = v8::RegExp::New(v8_str("foobarbaz"),
12814 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
12815 v8::RegExp::kMultiline));
12816 CHECK(re->IsRegExp());
12817 CHECK(re->GetSource()->Equals(v8_str("foobarbaz")));
12818 CHECK_EQ(static_cast<int>(re->GetFlags()),
12819 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
12820
12821 context->Global()->Set(v8_str("re"), re);
12822 ExpectTrue("re.test('FoobarbaZ')");
12823
12824 v8::TryCatch try_catch;
12825 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone);
12826 CHECK(re.IsEmpty());
12827 CHECK(try_catch.HasCaught());
12828 context->Global()->Set(v8_str("ex"), try_catch.Exception());
12829 ExpectTrue("ex instanceof SyntaxError");
12830}
12831
12832
Steve Block1e0659c2011-05-24 12:43:12 +010012833THREADED_TEST(Equals) {
12834 v8::HandleScope handleScope;
12835 LocalContext localContext;
12836
12837 v8::Handle<v8::Object> globalProxy = localContext->Global();
12838 v8::Handle<Value> global = globalProxy->GetPrototype();
12839
12840 CHECK(global->StrictEquals(global));
12841 CHECK(!global->StrictEquals(globalProxy));
12842 CHECK(!globalProxy->StrictEquals(global));
12843 CHECK(globalProxy->StrictEquals(globalProxy));
12844
12845 CHECK(global->Equals(global));
12846 CHECK(!global->Equals(globalProxy));
12847 CHECK(!globalProxy->Equals(global));
12848 CHECK(globalProxy->Equals(globalProxy));
12849}
12850
12851
Ben Murdochf87a2032010-10-22 12:50:53 +010012852static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
12853 const v8::AccessorInfo& info ) {
12854 return v8_str("42!");
12855}
12856
12857
12858static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) {
12859 v8::Handle<v8::Array> result = v8::Array::New();
12860 result->Set(0, v8_str("universalAnswer"));
12861 return result;
12862}
12863
12864
12865TEST(NamedEnumeratorAndForIn) {
12866 v8::HandleScope handle_scope;
12867 LocalContext context;
12868 v8::Context::Scope context_scope(context.local());
12869
12870 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New();
12871 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
12872 context->Global()->Set(v8_str("o"), tmpl->NewInstance());
12873 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
12874 "var result = []; for (var k in o) result.push(k); result"));
12875 CHECK_EQ(1, result->Length());
12876 CHECK_EQ(v8_str("universalAnswer"), result->Get(0));
12877}
Steve Block1e0659c2011-05-24 12:43:12 +010012878
12879
12880TEST(DefinePropertyPostDetach) {
12881 v8::HandleScope scope;
12882 LocalContext context;
12883 v8::Handle<v8::Object> proxy = context->Global();
12884 v8::Handle<v8::Function> define_property =
12885 CompileRun("(function() {"
12886 " Object.defineProperty("
12887 " this,"
12888 " 1,"
12889 " { configurable: true, enumerable: true, value: 3 });"
12890 "})").As<Function>();
12891 context->DetachGlobal();
12892 define_property->Call(proxy, 0, NULL);
12893}