blob: b92185f52afa933c8402e2eace8cf3fcf680d224 [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
2694THREADED_TEST(Equality) {
2695 v8::HandleScope scope;
2696 LocalContext context;
2697 // Check that equality works at all before relying on CHECK_EQ
2698 CHECK(v8_str("a")->Equals(v8_str("a")));
2699 CHECK(!v8_str("a")->Equals(v8_str("b")));
2700
2701 CHECK_EQ(v8_str("a"), v8_str("a"));
2702 CHECK_NE(v8_str("a"), v8_str("b"));
2703 CHECK_EQ(v8_num(1), v8_num(1));
2704 CHECK_EQ(v8_num(1.00), v8_num(1));
2705 CHECK_NE(v8_num(1), v8_num(2));
2706
2707 // Assume String is not symbol.
2708 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2709 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2710 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2711 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2712 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2713 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2714 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2715 CHECK(!not_a_number->StrictEquals(not_a_number));
2716 CHECK(v8::False()->StrictEquals(v8::False()));
2717 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2718
2719 v8::Handle<v8::Object> obj = v8::Object::New();
2720 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2721 CHECK(alias->StrictEquals(obj));
2722 alias.Dispose();
2723}
2724
2725
2726THREADED_TEST(MultiRun) {
2727 v8::HandleScope scope;
2728 LocalContext context;
2729 Local<Script> script = Script::Compile(v8_str("x"));
2730 for (int i = 0; i < 10; i++)
2731 script->Run();
2732}
2733
2734
2735static v8::Handle<Value> GetXValue(Local<String> name,
2736 const AccessorInfo& info) {
2737 ApiTestFuzzer::Fuzz();
2738 CHECK_EQ(info.Data(), v8_str("donut"));
2739 CHECK_EQ(name, v8_str("x"));
2740 return name;
2741}
2742
2743
2744THREADED_TEST(SimplePropertyRead) {
2745 v8::HandleScope scope;
2746 Local<ObjectTemplate> templ = ObjectTemplate::New();
2747 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2748 LocalContext context;
2749 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2750 Local<Script> script = Script::Compile(v8_str("obj.x"));
2751 for (int i = 0; i < 10; i++) {
2752 Local<Value> result = script->Run();
2753 CHECK_EQ(result, v8_str("x"));
2754 }
2755}
2756
Andrei Popescu31002712010-02-23 13:46:05 +00002757THREADED_TEST(DefinePropertyOnAPIAccessor) {
2758 v8::HandleScope scope;
2759 Local<ObjectTemplate> templ = ObjectTemplate::New();
2760 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2761 LocalContext context;
2762 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2763
2764 // Uses getOwnPropertyDescriptor to check the configurable status
2765 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002766 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002767 "obj, 'x');"
2768 "prop.configurable;"));
2769 Local<Value> result = script_desc->Run();
2770 CHECK_EQ(result->BooleanValue(), true);
2771
2772 // Redefine get - but still configurable
2773 Local<Script> script_define
2774 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2775 " configurable: true };"
2776 "Object.defineProperty(obj, 'x', desc);"
2777 "obj.x"));
2778 result = script_define->Run();
2779 CHECK_EQ(result, v8_num(42));
2780
2781 // Check that the accessor is still configurable
2782 result = script_desc->Run();
2783 CHECK_EQ(result->BooleanValue(), true);
2784
2785 // Redefine to a non-configurable
2786 script_define
2787 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2788 " configurable: false };"
2789 "Object.defineProperty(obj, 'x', desc);"
2790 "obj.x"));
2791 result = script_define->Run();
2792 CHECK_EQ(result, v8_num(43));
2793 result = script_desc->Run();
2794 CHECK_EQ(result->BooleanValue(), false);
2795
2796 // Make sure that it is not possible to redefine again
2797 v8::TryCatch try_catch;
2798 result = script_define->Run();
2799 CHECK(try_catch.HasCaught());
2800 String::AsciiValue exception_value(try_catch.Exception());
2801 CHECK_EQ(*exception_value,
2802 "TypeError: Cannot redefine property: defineProperty");
2803}
2804
2805THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2806 v8::HandleScope scope;
2807 Local<ObjectTemplate> templ = ObjectTemplate::New();
2808 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2809 LocalContext context;
2810 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2811
2812 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2813 "Object.getOwnPropertyDescriptor( "
2814 "obj, 'x');"
2815 "prop.configurable;"));
2816 Local<Value> result = script_desc->Run();
2817 CHECK_EQ(result->BooleanValue(), true);
2818
2819 Local<Script> script_define =
2820 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2821 " configurable: true };"
2822 "Object.defineProperty(obj, 'x', desc);"
2823 "obj.x"));
2824 result = script_define->Run();
2825 CHECK_EQ(result, v8_num(42));
2826
2827
2828 result = script_desc->Run();
2829 CHECK_EQ(result->BooleanValue(), true);
2830
2831
2832 script_define =
2833 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2834 " configurable: false };"
2835 "Object.defineProperty(obj, 'x', desc);"
2836 "obj.x"));
2837 result = script_define->Run();
2838 CHECK_EQ(result, v8_num(43));
2839 result = script_desc->Run();
2840
2841 CHECK_EQ(result->BooleanValue(), false);
2842
2843 v8::TryCatch try_catch;
2844 result = script_define->Run();
2845 CHECK(try_catch.HasCaught());
2846 String::AsciiValue exception_value(try_catch.Exception());
2847 CHECK_EQ(*exception_value,
2848 "TypeError: Cannot redefine property: defineProperty");
2849}
2850
2851
Leon Clarkef7060e22010-06-03 12:02:55 +01002852static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2853 char const* name) {
2854 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2855}
Andrei Popescu31002712010-02-23 13:46:05 +00002856
2857
Leon Clarkef7060e22010-06-03 12:02:55 +01002858THREADED_TEST(DefineAPIAccessorOnObject) {
2859 v8::HandleScope scope;
2860 Local<ObjectTemplate> templ = ObjectTemplate::New();
2861 LocalContext context;
2862
2863 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2864 CompileRun("var obj2 = {};");
2865
2866 CHECK(CompileRun("obj1.x")->IsUndefined());
2867 CHECK(CompileRun("obj2.x")->IsUndefined());
2868
2869 CHECK(GetGlobalProperty(&context, "obj1")->
2870 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2871
2872 ExpectString("obj1.x", "x");
2873 CHECK(CompileRun("obj2.x")->IsUndefined());
2874
2875 CHECK(GetGlobalProperty(&context, "obj2")->
2876 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2877
2878 ExpectString("obj1.x", "x");
2879 ExpectString("obj2.x", "x");
2880
2881 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2882 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2883
2884 CompileRun("Object.defineProperty(obj1, 'x',"
2885 "{ get: function() { return 'y'; }, configurable: true })");
2886
2887 ExpectString("obj1.x", "y");
2888 ExpectString("obj2.x", "x");
2889
2890 CompileRun("Object.defineProperty(obj2, 'x',"
2891 "{ get: function() { return 'y'; }, configurable: true })");
2892
2893 ExpectString("obj1.x", "y");
2894 ExpectString("obj2.x", "y");
2895
2896 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2897 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2898
2899 CHECK(GetGlobalProperty(&context, "obj1")->
2900 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2901 CHECK(GetGlobalProperty(&context, "obj2")->
2902 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2903
2904 ExpectString("obj1.x", "x");
2905 ExpectString("obj2.x", "x");
2906
2907 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2908 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2909
2910 // Define getters/setters, but now make them not configurable.
2911 CompileRun("Object.defineProperty(obj1, 'x',"
2912 "{ get: function() { return 'z'; }, configurable: false })");
2913 CompileRun("Object.defineProperty(obj2, 'x',"
2914 "{ get: function() { return 'z'; }, configurable: false })");
2915
2916 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2917 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2918
2919 ExpectString("obj1.x", "z");
2920 ExpectString("obj2.x", "z");
2921
2922 CHECK(!GetGlobalProperty(&context, "obj1")->
2923 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2924 CHECK(!GetGlobalProperty(&context, "obj2")->
2925 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2926
2927 ExpectString("obj1.x", "z");
2928 ExpectString("obj2.x", "z");
2929}
2930
2931
2932THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2933 v8::HandleScope scope;
2934 Local<ObjectTemplate> templ = ObjectTemplate::New();
2935 LocalContext context;
2936
2937 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2938 CompileRun("var obj2 = {};");
2939
2940 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2941 v8_str("x"),
2942 GetXValue, NULL,
2943 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2944 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2945 v8_str("x"),
2946 GetXValue, NULL,
2947 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2948
2949 ExpectString("obj1.x", "x");
2950 ExpectString("obj2.x", "x");
2951
2952 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2953 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2954
2955 CHECK(!GetGlobalProperty(&context, "obj1")->
2956 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2957 CHECK(!GetGlobalProperty(&context, "obj2")->
2958 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2959
2960 {
2961 v8::TryCatch try_catch;
2962 CompileRun("Object.defineProperty(obj1, 'x',"
2963 "{get: function() { return 'func'; }})");
2964 CHECK(try_catch.HasCaught());
2965 String::AsciiValue exception_value(try_catch.Exception());
2966 CHECK_EQ(*exception_value,
2967 "TypeError: Cannot redefine property: defineProperty");
2968 }
2969 {
2970 v8::TryCatch try_catch;
2971 CompileRun("Object.defineProperty(obj2, 'x',"
2972 "{get: function() { return 'func'; }})");
2973 CHECK(try_catch.HasCaught());
2974 String::AsciiValue exception_value(try_catch.Exception());
2975 CHECK_EQ(*exception_value,
2976 "TypeError: Cannot redefine property: defineProperty");
2977 }
2978}
2979
2980
2981static v8::Handle<Value> Get239Value(Local<String> name,
2982 const AccessorInfo& info) {
2983 ApiTestFuzzer::Fuzz();
2984 CHECK_EQ(info.Data(), v8_str("donut"));
2985 CHECK_EQ(name, v8_str("239"));
2986 return name;
2987}
2988
2989
2990THREADED_TEST(ElementAPIAccessor) {
2991 v8::HandleScope scope;
2992 Local<ObjectTemplate> templ = ObjectTemplate::New();
2993 LocalContext context;
2994
2995 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2996 CompileRun("var obj2 = {};");
2997
2998 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2999 v8_str("239"),
3000 Get239Value, NULL,
3001 v8_str("donut")));
3002 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
3003 v8_str("239"),
3004 Get239Value, NULL,
3005 v8_str("donut")));
3006
3007 ExpectString("obj1[239]", "239");
3008 ExpectString("obj2[239]", "239");
3009 ExpectString("obj1['239']", "239");
3010 ExpectString("obj2['239']", "239");
3011}
3012
Steve Blocka7e24c12009-10-30 11:49:00 +00003013
3014v8::Persistent<Value> xValue;
3015
3016
3017static void SetXValue(Local<String> name,
3018 Local<Value> value,
3019 const AccessorInfo& info) {
3020 CHECK_EQ(value, v8_num(4));
3021 CHECK_EQ(info.Data(), v8_str("donut"));
3022 CHECK_EQ(name, v8_str("x"));
3023 CHECK(xValue.IsEmpty());
3024 xValue = v8::Persistent<Value>::New(value);
3025}
3026
3027
3028THREADED_TEST(SimplePropertyWrite) {
3029 v8::HandleScope scope;
3030 Local<ObjectTemplate> templ = ObjectTemplate::New();
3031 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
3032 LocalContext context;
3033 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3034 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
3035 for (int i = 0; i < 10; i++) {
3036 CHECK(xValue.IsEmpty());
3037 script->Run();
3038 CHECK_EQ(v8_num(4), xValue);
3039 xValue.Dispose();
3040 xValue = v8::Persistent<Value>();
3041 }
3042}
3043
3044
3045static v8::Handle<Value> XPropertyGetter(Local<String> property,
3046 const AccessorInfo& info) {
3047 ApiTestFuzzer::Fuzz();
3048 CHECK(info.Data()->IsUndefined());
3049 return property;
3050}
3051
3052
3053THREADED_TEST(NamedInterceptorPropertyRead) {
3054 v8::HandleScope scope;
3055 Local<ObjectTemplate> templ = ObjectTemplate::New();
3056 templ->SetNamedPropertyHandler(XPropertyGetter);
3057 LocalContext context;
3058 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3059 Local<Script> script = Script::Compile(v8_str("obj.x"));
3060 for (int i = 0; i < 10; i++) {
3061 Local<Value> result = script->Run();
3062 CHECK_EQ(result, v8_str("x"));
3063 }
3064}
3065
3066
Steve Block6ded16b2010-05-10 14:33:55 +01003067THREADED_TEST(NamedInterceptorDictionaryIC) {
3068 v8::HandleScope scope;
3069 Local<ObjectTemplate> templ = ObjectTemplate::New();
3070 templ->SetNamedPropertyHandler(XPropertyGetter);
3071 LocalContext context;
3072 // Create an object with a named interceptor.
3073 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
3074 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
3075 for (int i = 0; i < 10; i++) {
3076 Local<Value> result = script->Run();
3077 CHECK_EQ(result, v8_str("x"));
3078 }
3079 // Create a slow case object and a function accessing a property in
3080 // that slow case object (with dictionary probing in generated
3081 // code). Then force object with a named interceptor into slow-case,
3082 // pass it to the function, and check that the interceptor is called
3083 // instead of accessing the local property.
3084 Local<Value> result =
3085 CompileRun("function get_x(o) { return o.x; };"
3086 "var obj = { x : 42, y : 0 };"
3087 "delete obj.y;"
3088 "for (var i = 0; i < 10; i++) get_x(obj);"
3089 "interceptor_obj.x = 42;"
3090 "interceptor_obj.y = 10;"
3091 "delete interceptor_obj.y;"
3092 "get_x(interceptor_obj)");
3093 CHECK_EQ(result, v8_str("x"));
3094}
3095
3096
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003097THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
3098 v8::HandleScope scope;
3099
3100 v8::Persistent<Context> context1 = Context::New();
3101
3102 context1->Enter();
3103 Local<ObjectTemplate> templ = ObjectTemplate::New();
3104 templ->SetNamedPropertyHandler(XPropertyGetter);
3105 // Create an object with a named interceptor.
3106 v8::Local<v8::Object> object = templ->NewInstance();
3107 context1->Global()->Set(v8_str("interceptor_obj"), object);
3108
3109 // Force the object into the slow case.
3110 CompileRun("interceptor_obj.y = 0;"
3111 "delete interceptor_obj.y;");
3112 context1->Exit();
3113
3114 {
3115 // Introduce the object into a different context.
3116 // Repeat named loads to exercise ICs.
3117 LocalContext context2;
3118 context2->Global()->Set(v8_str("interceptor_obj"), object);
3119 Local<Value> result =
3120 CompileRun("function get_x(o) { return o.x; }"
3121 "interceptor_obj.x = 42;"
3122 "for (var i=0; i != 10; i++) {"
3123 " get_x(interceptor_obj);"
3124 "}"
3125 "get_x(interceptor_obj)");
3126 // Check that the interceptor was actually invoked.
3127 CHECK_EQ(result, v8_str("x"));
3128 }
3129
3130 // Return to the original context and force some object to the slow case
3131 // to cause the NormalizedMapCache to verify.
3132 context1->Enter();
3133 CompileRun("var obj = { x : 0 }; delete obj.x;");
3134 context1->Exit();
3135
3136 context1.Dispose();
3137}
3138
3139
Andrei Popescu402d9372010-02-26 13:31:12 +00003140static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
3141 const AccessorInfo& info) {
3142 // Set x on the prototype object and do not handle the get request.
3143 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01003144 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00003145 return v8::Handle<Value>();
3146}
3147
3148
3149// This is a regression test for http://crbug.com/20104. Map
3150// transitions should not interfere with post interceptor lookup.
3151THREADED_TEST(NamedInterceptorMapTransitionRead) {
3152 v8::HandleScope scope;
3153 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
3154 Local<v8::ObjectTemplate> instance_template
3155 = function_template->InstanceTemplate();
3156 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
3157 LocalContext context;
3158 context->Global()->Set(v8_str("F"), function_template->GetFunction());
3159 // Create an instance of F and introduce a map transition for x.
3160 CompileRun("var o = new F(); o.x = 23;");
3161 // Create an instance of F and invoke the getter. The result should be 23.
3162 Local<Value> result = CompileRun("o = new F(); o.x");
3163 CHECK_EQ(result->Int32Value(), 23);
3164}
3165
3166
Steve Blocka7e24c12009-10-30 11:49:00 +00003167static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
3168 const AccessorInfo& info) {
3169 ApiTestFuzzer::Fuzz();
3170 if (index == 37) {
3171 return v8::Handle<Value>(v8_num(625));
3172 }
3173 return v8::Handle<Value>();
3174}
3175
3176
3177static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
3178 Local<Value> value,
3179 const AccessorInfo& info) {
3180 ApiTestFuzzer::Fuzz();
3181 if (index == 39) {
3182 return value;
3183 }
3184 return v8::Handle<Value>();
3185}
3186
3187
3188THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
3189 v8::HandleScope scope;
3190 Local<ObjectTemplate> templ = ObjectTemplate::New();
3191 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
3192 IndexedPropertySetter);
3193 LocalContext context;
3194 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3195 Local<Script> getter_script = Script::Compile(v8_str(
3196 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
3197 Local<Script> setter_script = Script::Compile(v8_str(
3198 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
3199 "obj[17] = 23;"
3200 "obj.foo;"));
3201 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3202 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3203 "obj[39] = 47;"
3204 "obj.foo;")); // This setter should not run, due to the interceptor.
3205 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3206 "obj[37];"));
3207 Local<Value> result = getter_script->Run();
3208 CHECK_EQ(v8_num(5), result);
3209 result = setter_script->Run();
3210 CHECK_EQ(v8_num(23), result);
3211 result = interceptor_setter_script->Run();
3212 CHECK_EQ(v8_num(23), result);
3213 result = interceptor_getter_script->Run();
3214 CHECK_EQ(v8_num(625), result);
3215}
3216
3217
Leon Clarked91b9f72010-01-27 17:25:45 +00003218static v8::Handle<Value> IdentityIndexedPropertyGetter(
3219 uint32_t index,
3220 const AccessorInfo& info) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003221 return v8::Integer::NewFromUnsigned(index);
Leon Clarked91b9f72010-01-27 17:25:45 +00003222}
3223
3224
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003225THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3226 v8::HandleScope scope;
3227 Local<ObjectTemplate> templ = ObjectTemplate::New();
3228 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3229
3230 LocalContext context;
3231 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3232
3233 // Check fast object case.
3234 const char* fast_case_code =
3235 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3236 ExpectString(fast_case_code, "0");
3237
3238 // Check slow case.
3239 const char* slow_case_code =
3240 "obj.x = 1; delete obj.x;"
3241 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3242 ExpectString(slow_case_code, "1");
3243}
3244
3245
Leon Clarked91b9f72010-01-27 17:25:45 +00003246THREADED_TEST(IndexedInterceptorWithNoSetter) {
3247 v8::HandleScope scope;
3248 Local<ObjectTemplate> templ = ObjectTemplate::New();
3249 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3250
3251 LocalContext context;
3252 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3253
3254 const char* code =
3255 "try {"
3256 " obj[0] = 239;"
3257 " for (var i = 0; i < 100; i++) {"
3258 " var v = obj[0];"
3259 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3260 " }"
3261 " 'PASSED'"
3262 "} catch(e) {"
3263 " e"
3264 "}";
3265 ExpectString(code, "PASSED");
3266}
3267
3268
Andrei Popescu402d9372010-02-26 13:31:12 +00003269THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3270 v8::HandleScope scope;
3271 Local<ObjectTemplate> templ = ObjectTemplate::New();
3272 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3273
3274 LocalContext context;
3275 Local<v8::Object> obj = templ->NewInstance();
3276 obj->TurnOnAccessCheck();
3277 context->Global()->Set(v8_str("obj"), obj);
3278
3279 const char* code =
3280 "try {"
3281 " for (var i = 0; i < 100; i++) {"
3282 " var v = obj[0];"
3283 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3284 " }"
3285 " 'PASSED'"
3286 "} catch(e) {"
3287 " e"
3288 "}";
3289 ExpectString(code, "PASSED");
3290}
3291
3292
3293THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3294 i::FLAG_allow_natives_syntax = true;
3295 v8::HandleScope scope;
3296 Local<ObjectTemplate> templ = ObjectTemplate::New();
3297 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3298
3299 LocalContext context;
3300 Local<v8::Object> obj = templ->NewInstance();
3301 context->Global()->Set(v8_str("obj"), obj);
3302
3303 const char* code =
3304 "try {"
3305 " for (var i = 0; i < 100; i++) {"
3306 " var expected = i;"
3307 " if (i == 5) {"
3308 " %EnableAccessChecks(obj);"
3309 " expected = undefined;"
3310 " }"
3311 " var v = obj[i];"
3312 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3313 " if (i == 5) %DisableAccessChecks(obj);"
3314 " }"
3315 " 'PASSED'"
3316 "} catch(e) {"
3317 " e"
3318 "}";
3319 ExpectString(code, "PASSED");
3320}
3321
3322
3323THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3324 v8::HandleScope scope;
3325 Local<ObjectTemplate> templ = ObjectTemplate::New();
3326 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3327
3328 LocalContext context;
3329 Local<v8::Object> obj = templ->NewInstance();
3330 context->Global()->Set(v8_str("obj"), obj);
3331
3332 const char* code =
3333 "try {"
3334 " for (var i = 0; i < 100; i++) {"
3335 " var v = obj[i];"
3336 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3337 " }"
3338 " 'PASSED'"
3339 "} catch(e) {"
3340 " e"
3341 "}";
3342 ExpectString(code, "PASSED");
3343}
3344
3345
Ben Murdochf87a2032010-10-22 12:50:53 +01003346THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
3347 v8::HandleScope scope;
3348 Local<ObjectTemplate> templ = ObjectTemplate::New();
3349 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3350
3351 LocalContext context;
3352 Local<v8::Object> obj = templ->NewInstance();
3353 context->Global()->Set(v8_str("obj"), obj);
3354
3355 const char* code =
3356 "try {"
3357 " for (var i = 0; i < 100; i++) {"
3358 " var expected = i;"
3359 " var key = i;"
3360 " if (i == 25) {"
3361 " key = -1;"
3362 " expected = undefined;"
3363 " }"
3364 " if (i == 50) {"
3365 " /* probe minimal Smi number on 32-bit platforms */"
3366 " key = -(1 << 30);"
3367 " expected = undefined;"
3368 " }"
3369 " if (i == 75) {"
3370 " /* probe minimal Smi number on 64-bit platforms */"
3371 " key = 1 << 31;"
3372 " expected = undefined;"
3373 " }"
3374 " var v = obj[key];"
3375 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3376 " }"
3377 " 'PASSED'"
3378 "} catch(e) {"
3379 " e"
3380 "}";
3381 ExpectString(code, "PASSED");
3382}
3383
3384
Andrei Popescu402d9372010-02-26 13:31:12 +00003385THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3386 v8::HandleScope scope;
3387 Local<ObjectTemplate> templ = ObjectTemplate::New();
3388 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3389
3390 LocalContext context;
3391 Local<v8::Object> obj = templ->NewInstance();
3392 context->Global()->Set(v8_str("obj"), obj);
3393
3394 const char* code =
3395 "try {"
3396 " for (var i = 0; i < 100; i++) {"
3397 " var expected = i;"
Ben Murdochf87a2032010-10-22 12:50:53 +01003398 " var key = i;"
Andrei Popescu402d9372010-02-26 13:31:12 +00003399 " if (i == 50) {"
Ben Murdochf87a2032010-10-22 12:50:53 +01003400 " key = 'foobar';"
Andrei Popescu402d9372010-02-26 13:31:12 +00003401 " expected = undefined;"
3402 " }"
Ben Murdochf87a2032010-10-22 12:50:53 +01003403 " var v = obj[key];"
Andrei Popescu402d9372010-02-26 13:31:12 +00003404 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3405 " }"
3406 " 'PASSED'"
3407 "} catch(e) {"
3408 " e"
3409 "}";
3410 ExpectString(code, "PASSED");
3411}
3412
3413
3414THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3415 v8::HandleScope scope;
3416 Local<ObjectTemplate> templ = ObjectTemplate::New();
3417 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3418
3419 LocalContext context;
3420 Local<v8::Object> obj = templ->NewInstance();
3421 context->Global()->Set(v8_str("obj"), obj);
3422
3423 const char* code =
3424 "var original = obj;"
3425 "try {"
3426 " for (var i = 0; i < 100; i++) {"
3427 " var expected = i;"
3428 " if (i == 50) {"
3429 " obj = {50: 'foobar'};"
3430 " expected = 'foobar';"
3431 " }"
3432 " var v = obj[i];"
3433 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3434 " if (i == 50) obj = original;"
3435 " }"
3436 " 'PASSED'"
3437 "} catch(e) {"
3438 " e"
3439 "}";
3440 ExpectString(code, "PASSED");
3441}
3442
3443
3444THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3445 v8::HandleScope scope;
3446 Local<ObjectTemplate> templ = ObjectTemplate::New();
3447 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3448
3449 LocalContext context;
3450 Local<v8::Object> obj = templ->NewInstance();
3451 context->Global()->Set(v8_str("obj"), obj);
3452
3453 const char* code =
3454 "var original = obj;"
3455 "try {"
3456 " for (var i = 0; i < 100; i++) {"
3457 " var expected = i;"
3458 " if (i == 5) {"
3459 " obj = 239;"
3460 " expected = undefined;"
3461 " }"
3462 " var v = obj[i];"
3463 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3464 " if (i == 5) obj = original;"
3465 " }"
3466 " 'PASSED'"
3467 "} catch(e) {"
3468 " e"
3469 "}";
3470 ExpectString(code, "PASSED");
3471}
3472
3473
3474THREADED_TEST(IndexedInterceptorOnProto) {
3475 v8::HandleScope scope;
3476 Local<ObjectTemplate> templ = ObjectTemplate::New();
3477 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3478
3479 LocalContext context;
3480 Local<v8::Object> obj = templ->NewInstance();
3481 context->Global()->Set(v8_str("obj"), obj);
3482
3483 const char* code =
3484 "var o = {__proto__: obj};"
3485 "try {"
3486 " for (var i = 0; i < 100; i++) {"
3487 " var v = o[i];"
3488 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3489 " }"
3490 " 'PASSED'"
3491 "} catch(e) {"
3492 " e"
3493 "}";
3494 ExpectString(code, "PASSED");
3495}
3496
3497
Steve Blocka7e24c12009-10-30 11:49:00 +00003498THREADED_TEST(MultiContexts) {
3499 v8::HandleScope scope;
3500 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3501 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3502
3503 Local<String> password = v8_str("Password");
3504
3505 // Create an environment
3506 LocalContext context0(0, templ);
3507 context0->SetSecurityToken(password);
3508 v8::Handle<v8::Object> global0 = context0->Global();
3509 global0->Set(v8_str("custom"), v8_num(1234));
3510 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3511
3512 // Create an independent environment
3513 LocalContext context1(0, templ);
3514 context1->SetSecurityToken(password);
3515 v8::Handle<v8::Object> global1 = context1->Global();
3516 global1->Set(v8_str("custom"), v8_num(1234));
3517 CHECK_NE(global0, global1);
3518 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3519 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3520
3521 // Now create a new context with the old global
3522 LocalContext context2(0, templ, global1);
3523 context2->SetSecurityToken(password);
3524 v8::Handle<v8::Object> global2 = context2->Global();
3525 CHECK_EQ(global1, global2);
3526 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3527 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3528}
3529
3530
3531THREADED_TEST(FunctionPrototypeAcrossContexts) {
3532 // Make sure that functions created by cloning boilerplates cannot
3533 // communicate through their __proto__ field.
3534
3535 v8::HandleScope scope;
3536
3537 LocalContext env0;
3538 v8::Handle<v8::Object> global0 =
3539 env0->Global();
3540 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003541 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003542 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003543 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003544 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003545 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003546 proto0->Set(v8_str("custom"), v8_num(1234));
3547
3548 LocalContext env1;
3549 v8::Handle<v8::Object> global1 =
3550 env1->Global();
3551 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003552 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003553 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003554 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003555 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003556 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003557 CHECK(!proto1->Has(v8_str("custom")));
3558}
3559
3560
3561THREADED_TEST(Regress892105) {
3562 // Make sure that object and array literals created by cloning
3563 // boilerplates cannot communicate through their __proto__
3564 // field. This is rather difficult to check, but we try to add stuff
3565 // to Object.prototype and Array.prototype and create a new
3566 // environment. This should succeed.
3567
3568 v8::HandleScope scope;
3569
3570 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3571 "Array.prototype.arr = 4567;"
3572 "8901");
3573
3574 LocalContext env0;
3575 Local<Script> script0 = Script::Compile(source);
3576 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3577
3578 LocalContext env1;
3579 Local<Script> script1 = Script::Compile(source);
3580 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3581}
3582
3583
Steve Blocka7e24c12009-10-30 11:49:00 +00003584THREADED_TEST(UndetectableObject) {
3585 v8::HandleScope scope;
3586 LocalContext env;
3587
3588 Local<v8::FunctionTemplate> desc =
3589 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3590 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3591
3592 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3593 env->Global()->Set(v8_str("undetectable"), obj);
3594
3595 ExpectString("undetectable.toString()", "[object Object]");
3596 ExpectString("typeof undetectable", "undefined");
3597 ExpectString("typeof(undetectable)", "undefined");
3598 ExpectBoolean("typeof undetectable == 'undefined'", true);
3599 ExpectBoolean("typeof undetectable == 'object'", false);
3600 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3601 ExpectBoolean("!undetectable", true);
3602
3603 ExpectObject("true&&undetectable", obj);
3604 ExpectBoolean("false&&undetectable", false);
3605 ExpectBoolean("true||undetectable", true);
3606 ExpectObject("false||undetectable", obj);
3607
3608 ExpectObject("undetectable&&true", obj);
3609 ExpectObject("undetectable&&false", obj);
3610 ExpectBoolean("undetectable||true", true);
3611 ExpectBoolean("undetectable||false", false);
3612
3613 ExpectBoolean("undetectable==null", true);
3614 ExpectBoolean("null==undetectable", true);
3615 ExpectBoolean("undetectable==undefined", true);
3616 ExpectBoolean("undefined==undetectable", true);
3617 ExpectBoolean("undetectable==undetectable", true);
3618
3619
3620 ExpectBoolean("undetectable===null", false);
3621 ExpectBoolean("null===undetectable", false);
3622 ExpectBoolean("undetectable===undefined", false);
3623 ExpectBoolean("undefined===undetectable", false);
3624 ExpectBoolean("undetectable===undetectable", true);
3625}
3626
3627
Steve Block8defd9f2010-07-08 12:39:36 +01003628
3629THREADED_TEST(ExtensibleOnUndetectable) {
3630 v8::HandleScope scope;
3631 LocalContext env;
3632
3633 Local<v8::FunctionTemplate> desc =
3634 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3635 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3636
3637 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3638 env->Global()->Set(v8_str("undetectable"), obj);
3639
3640 Local<String> source = v8_str("undetectable.x = 42;"
3641 "undetectable.x");
3642
3643 Local<Script> script = Script::Compile(source);
3644
3645 CHECK_EQ(v8::Integer::New(42), script->Run());
3646
3647 ExpectBoolean("Object.isExtensible(undetectable)", true);
3648
3649 source = v8_str("Object.preventExtensions(undetectable);");
3650 script = Script::Compile(source);
3651 script->Run();
3652 ExpectBoolean("Object.isExtensible(undetectable)", false);
3653
3654 source = v8_str("undetectable.y = 2000;");
3655 script = Script::Compile(source);
3656 v8::TryCatch try_catch;
3657 Local<Value> result = script->Run();
3658 CHECK(result.IsEmpty());
3659 CHECK(try_catch.HasCaught());
3660}
3661
3662
3663
Steve Blocka7e24c12009-10-30 11:49:00 +00003664THREADED_TEST(UndetectableString) {
3665 v8::HandleScope scope;
3666 LocalContext env;
3667
3668 Local<String> obj = String::NewUndetectable("foo");
3669 env->Global()->Set(v8_str("undetectable"), obj);
3670
3671 ExpectString("undetectable", "foo");
3672 ExpectString("typeof undetectable", "undefined");
3673 ExpectString("typeof(undetectable)", "undefined");
3674 ExpectBoolean("typeof undetectable == 'undefined'", true);
3675 ExpectBoolean("typeof undetectable == 'string'", false);
3676 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3677 ExpectBoolean("!undetectable", true);
3678
3679 ExpectObject("true&&undetectable", obj);
3680 ExpectBoolean("false&&undetectable", false);
3681 ExpectBoolean("true||undetectable", true);
3682 ExpectObject("false||undetectable", obj);
3683
3684 ExpectObject("undetectable&&true", obj);
3685 ExpectObject("undetectable&&false", obj);
3686 ExpectBoolean("undetectable||true", true);
3687 ExpectBoolean("undetectable||false", false);
3688
3689 ExpectBoolean("undetectable==null", true);
3690 ExpectBoolean("null==undetectable", true);
3691 ExpectBoolean("undetectable==undefined", true);
3692 ExpectBoolean("undefined==undetectable", true);
3693 ExpectBoolean("undetectable==undetectable", true);
3694
3695
3696 ExpectBoolean("undetectable===null", false);
3697 ExpectBoolean("null===undetectable", false);
3698 ExpectBoolean("undetectable===undefined", false);
3699 ExpectBoolean("undefined===undetectable", false);
3700 ExpectBoolean("undetectable===undetectable", true);
3701}
3702
3703
3704template <typename T> static void USE(T) { }
3705
3706
3707// This test is not intended to be run, just type checked.
3708static void PersistentHandles() {
3709 USE(PersistentHandles);
3710 Local<String> str = v8_str("foo");
3711 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3712 USE(p_str);
3713 Local<Script> scr = Script::Compile(v8_str(""));
3714 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3715 USE(p_scr);
3716 Local<ObjectTemplate> templ = ObjectTemplate::New();
3717 v8::Persistent<ObjectTemplate> p_templ =
3718 v8::Persistent<ObjectTemplate>::New(templ);
3719 USE(p_templ);
3720}
3721
3722
3723static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3724 ApiTestFuzzer::Fuzz();
3725 return v8::Undefined();
3726}
3727
3728
3729THREADED_TEST(GlobalObjectTemplate) {
3730 v8::HandleScope handle_scope;
3731 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3732 global_template->Set(v8_str("JSNI_Log"),
3733 v8::FunctionTemplate::New(HandleLogDelegator));
3734 v8::Persistent<Context> context = Context::New(0, global_template);
3735 Context::Scope context_scope(context);
3736 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3737 context.Dispose();
3738}
3739
3740
3741static const char* kSimpleExtensionSource =
3742 "function Foo() {"
3743 " return 4;"
3744 "}";
3745
3746
3747THREADED_TEST(SimpleExtensions) {
3748 v8::HandleScope handle_scope;
3749 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3750 const char* extension_names[] = { "simpletest" };
3751 v8::ExtensionConfiguration extensions(1, extension_names);
3752 v8::Handle<Context> context = Context::New(&extensions);
3753 Context::Scope lock(context);
3754 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3755 CHECK_EQ(result, v8::Integer::New(4));
3756}
3757
3758
3759static const char* kEvalExtensionSource1 =
3760 "function UseEval1() {"
3761 " var x = 42;"
3762 " return eval('x');"
3763 "}";
3764
3765
3766static const char* kEvalExtensionSource2 =
3767 "(function() {"
3768 " var x = 42;"
3769 " function e() {"
3770 " return eval('x');"
3771 " }"
3772 " this.UseEval2 = e;"
3773 "})()";
3774
3775
3776THREADED_TEST(UseEvalFromExtension) {
3777 v8::HandleScope handle_scope;
3778 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3779 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3780 const char* extension_names[] = { "evaltest1", "evaltest2" };
3781 v8::ExtensionConfiguration extensions(2, extension_names);
3782 v8::Handle<Context> context = Context::New(&extensions);
3783 Context::Scope lock(context);
3784 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3785 CHECK_EQ(result, v8::Integer::New(42));
3786 result = Script::Compile(v8_str("UseEval2()"))->Run();
3787 CHECK_EQ(result, v8::Integer::New(42));
3788}
3789
3790
3791static const char* kWithExtensionSource1 =
3792 "function UseWith1() {"
3793 " var x = 42;"
3794 " with({x:87}) { return x; }"
3795 "}";
3796
3797
3798
3799static const char* kWithExtensionSource2 =
3800 "(function() {"
3801 " var x = 42;"
3802 " function e() {"
3803 " with ({x:87}) { return x; }"
3804 " }"
3805 " this.UseWith2 = e;"
3806 "})()";
3807
3808
3809THREADED_TEST(UseWithFromExtension) {
3810 v8::HandleScope handle_scope;
3811 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3812 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3813 const char* extension_names[] = { "withtest1", "withtest2" };
3814 v8::ExtensionConfiguration extensions(2, extension_names);
3815 v8::Handle<Context> context = Context::New(&extensions);
3816 Context::Scope lock(context);
3817 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3818 CHECK_EQ(result, v8::Integer::New(87));
3819 result = Script::Compile(v8_str("UseWith2()"))->Run();
3820 CHECK_EQ(result, v8::Integer::New(87));
3821}
3822
3823
3824THREADED_TEST(AutoExtensions) {
3825 v8::HandleScope handle_scope;
3826 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3827 extension->set_auto_enable(true);
3828 v8::RegisterExtension(extension);
3829 v8::Handle<Context> context = Context::New();
3830 Context::Scope lock(context);
3831 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3832 CHECK_EQ(result, v8::Integer::New(4));
3833}
3834
3835
Steve Blockd0582a62009-12-15 09:54:21 +00003836static const char* kSyntaxErrorInExtensionSource =
3837 "[";
3838
3839
3840// Test that a syntax error in an extension does not cause a fatal
3841// error but results in an empty context.
3842THREADED_TEST(SyntaxErrorExtensions) {
3843 v8::HandleScope handle_scope;
3844 v8::RegisterExtension(new Extension("syntaxerror",
3845 kSyntaxErrorInExtensionSource));
3846 const char* extension_names[] = { "syntaxerror" };
3847 v8::ExtensionConfiguration extensions(1, extension_names);
3848 v8::Handle<Context> context = Context::New(&extensions);
3849 CHECK(context.IsEmpty());
3850}
3851
3852
3853static const char* kExceptionInExtensionSource =
3854 "throw 42";
3855
3856
3857// Test that an exception when installing an extension does not cause
3858// a fatal error but results in an empty context.
3859THREADED_TEST(ExceptionExtensions) {
3860 v8::HandleScope handle_scope;
3861 v8::RegisterExtension(new Extension("exception",
3862 kExceptionInExtensionSource));
3863 const char* extension_names[] = { "exception" };
3864 v8::ExtensionConfiguration extensions(1, extension_names);
3865 v8::Handle<Context> context = Context::New(&extensions);
3866 CHECK(context.IsEmpty());
3867}
3868
3869
Iain Merrick9ac36c92010-09-13 15:29:50 +01003870static const char* kNativeCallInExtensionSource =
3871 "function call_runtime_last_index_of(x) {"
3872 " return %StringLastIndexOf(x, 'bob', 10);"
3873 "}";
3874
3875
3876static const char* kNativeCallTest =
3877 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
3878
3879// Test that a native runtime calls are supported in extensions.
3880THREADED_TEST(NativeCallInExtensions) {
3881 v8::HandleScope handle_scope;
3882 v8::RegisterExtension(new Extension("nativecall",
3883 kNativeCallInExtensionSource));
3884 const char* extension_names[] = { "nativecall" };
3885 v8::ExtensionConfiguration extensions(1, extension_names);
3886 v8::Handle<Context> context = Context::New(&extensions);
3887 Context::Scope lock(context);
3888 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
3889 CHECK_EQ(result, v8::Integer::New(3));
3890}
3891
3892
Steve Blocka7e24c12009-10-30 11:49:00 +00003893static void CheckDependencies(const char* name, const char* expected) {
3894 v8::HandleScope handle_scope;
3895 v8::ExtensionConfiguration config(1, &name);
3896 LocalContext context(&config);
3897 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3898}
3899
3900
3901/*
3902 * Configuration:
3903 *
3904 * /-- B <--\
3905 * A <- -- D <-- E
3906 * \-- C <--/
3907 */
3908THREADED_TEST(ExtensionDependency) {
3909 static const char* kEDeps[] = { "D" };
3910 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3911 static const char* kDDeps[] = { "B", "C" };
3912 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3913 static const char* kBCDeps[] = { "A" };
3914 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3915 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3916 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3917 CheckDependencies("A", "undefinedA");
3918 CheckDependencies("B", "undefinedAB");
3919 CheckDependencies("C", "undefinedAC");
3920 CheckDependencies("D", "undefinedABCD");
3921 CheckDependencies("E", "undefinedABCDE");
3922 v8::HandleScope handle_scope;
3923 static const char* exts[2] = { "C", "E" };
3924 v8::ExtensionConfiguration config(2, exts);
3925 LocalContext context(&config);
3926 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3927}
3928
3929
3930static const char* kExtensionTestScript =
3931 "native function A();"
3932 "native function B();"
3933 "native function C();"
3934 "function Foo(i) {"
3935 " if (i == 0) return A();"
3936 " if (i == 1) return B();"
3937 " if (i == 2) return C();"
3938 "}";
3939
3940
3941static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3942 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003943 if (args.IsConstructCall()) {
3944 args.This()->Set(v8_str("data"), args.Data());
3945 return v8::Null();
3946 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003947 return args.Data();
3948}
3949
3950
3951class FunctionExtension : public Extension {
3952 public:
3953 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3954 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3955 v8::Handle<String> name);
3956};
3957
3958
3959static int lookup_count = 0;
3960v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3961 v8::Handle<String> name) {
3962 lookup_count++;
3963 if (name->Equals(v8_str("A"))) {
3964 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3965 } else if (name->Equals(v8_str("B"))) {
3966 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3967 } else if (name->Equals(v8_str("C"))) {
3968 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3969 } else {
3970 return v8::Handle<v8::FunctionTemplate>();
3971 }
3972}
3973
3974
3975THREADED_TEST(FunctionLookup) {
3976 v8::RegisterExtension(new FunctionExtension());
3977 v8::HandleScope handle_scope;
3978 static const char* exts[1] = { "functiontest" };
3979 v8::ExtensionConfiguration config(1, exts);
3980 LocalContext context(&config);
3981 CHECK_EQ(3, lookup_count);
3982 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3983 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3984 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3985}
3986
3987
Leon Clarkee46be812010-01-19 14:06:41 +00003988THREADED_TEST(NativeFunctionConstructCall) {
3989 v8::RegisterExtension(new FunctionExtension());
3990 v8::HandleScope handle_scope;
3991 static const char* exts[1] = { "functiontest" };
3992 v8::ExtensionConfiguration config(1, exts);
3993 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003994 for (int i = 0; i < 10; i++) {
3995 // Run a few times to ensure that allocation of objects doesn't
3996 // change behavior of a constructor function.
3997 CHECK_EQ(v8::Integer::New(8),
3998 Script::Compile(v8_str("(new A()).data"))->Run());
3999 CHECK_EQ(v8::Integer::New(7),
4000 Script::Compile(v8_str("(new B()).data"))->Run());
4001 CHECK_EQ(v8::Integer::New(6),
4002 Script::Compile(v8_str("(new C()).data"))->Run());
4003 }
Leon Clarkee46be812010-01-19 14:06:41 +00004004}
4005
4006
Steve Blocka7e24c12009-10-30 11:49:00 +00004007static const char* last_location;
4008static const char* last_message;
4009void StoringErrorCallback(const char* location, const char* message) {
4010 if (last_location == NULL) {
4011 last_location = location;
4012 last_message = message;
4013 }
4014}
4015
4016
4017// ErrorReporting creates a circular extensions configuration and
4018// tests that the fatal error handler gets called. This renders V8
4019// unusable and therefore this test cannot be run in parallel.
4020TEST(ErrorReporting) {
4021 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
4022 static const char* aDeps[] = { "B" };
4023 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
4024 static const char* bDeps[] = { "A" };
4025 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
4026 last_location = NULL;
4027 v8::ExtensionConfiguration config(1, bDeps);
4028 v8::Handle<Context> context = Context::New(&config);
4029 CHECK(context.IsEmpty());
4030 CHECK_NE(last_location, NULL);
4031}
4032
4033
4034static const char* js_code_causing_huge_string_flattening =
4035 "var str = 'X';"
4036 "for (var i = 0; i < 30; i++) {"
4037 " str = str + str;"
4038 "}"
4039 "str.match(/X/);";
4040
4041
4042void OOMCallback(const char* location, const char* message) {
4043 exit(0);
4044}
4045
4046
4047TEST(RegexpOutOfMemory) {
4048 // Execute a script that causes out of memory when flattening a string.
4049 v8::HandleScope scope;
4050 v8::V8::SetFatalErrorHandler(OOMCallback);
4051 LocalContext context;
4052 Local<Script> script =
4053 Script::Compile(String::New(js_code_causing_huge_string_flattening));
4054 last_location = NULL;
4055 Local<Value> result = script->Run();
4056
4057 CHECK(false); // Should not return.
4058}
4059
4060
4061static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
4062 v8::Handle<Value> data) {
4063 CHECK_EQ(v8::Undefined(), data);
4064 CHECK(message->GetScriptResourceName()->IsUndefined());
4065 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
4066 message->GetLineNumber();
4067 message->GetSourceLine();
4068}
4069
4070
4071THREADED_TEST(ErrorWithMissingScriptInfo) {
4072 v8::HandleScope scope;
4073 LocalContext context;
4074 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
4075 Script::Compile(v8_str("throw Error()"))->Run();
4076 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
4077}
4078
4079
4080int global_index = 0;
4081
4082class Snorkel {
4083 public:
4084 Snorkel() { index_ = global_index++; }
4085 int index_;
4086};
4087
4088class Whammy {
4089 public:
4090 Whammy() {
4091 cursor_ = 0;
4092 }
4093 ~Whammy() {
4094 script_.Dispose();
4095 }
4096 v8::Handle<Script> getScript() {
4097 if (script_.IsEmpty())
4098 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
4099 return Local<Script>(*script_);
4100 }
4101
4102 public:
4103 static const int kObjectCount = 256;
4104 int cursor_;
4105 v8::Persistent<v8::Object> objects_[kObjectCount];
4106 v8::Persistent<Script> script_;
4107};
4108
4109static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
4110 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
4111 delete snorkel;
4112 obj.ClearWeak();
4113}
4114
4115v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
4116 const AccessorInfo& info) {
4117 Whammy* whammy =
4118 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
4119
4120 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
4121
4122 v8::Handle<v8::Object> obj = v8::Object::New();
4123 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
4124 if (!prev.IsEmpty()) {
4125 prev->Set(v8_str("next"), obj);
4126 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
4127 whammy->objects_[whammy->cursor_].Clear();
4128 }
4129 whammy->objects_[whammy->cursor_] = global;
4130 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
4131 return whammy->getScript()->Run();
4132}
4133
4134THREADED_TEST(WeakReference) {
4135 v8::HandleScope handle_scope;
4136 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004137 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00004138 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
4139 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004140 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00004141 const char* extension_list[] = { "v8/gc" };
4142 v8::ExtensionConfiguration extensions(1, extension_list);
4143 v8::Persistent<Context> context = Context::New(&extensions);
4144 Context::Scope context_scope(context);
4145
4146 v8::Handle<v8::Object> interceptor = templ->NewInstance();
4147 context->Global()->Set(v8_str("whammy"), interceptor);
4148 const char* code =
4149 "var last;"
4150 "for (var i = 0; i < 10000; i++) {"
4151 " var obj = whammy.length;"
4152 " if (last) last.next = obj;"
4153 " last = obj;"
4154 "}"
4155 "gc();"
4156 "4";
4157 v8::Handle<Value> result = CompileRun(code);
4158 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004159 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00004160 context.Dispose();
4161}
4162
4163
Steve Blockd0582a62009-12-15 09:54:21 +00004164static bool in_scavenge = false;
4165static int last = -1;
4166
4167static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
4168 CHECK_EQ(-1, last);
4169 last = 0;
4170 obj.Dispose();
4171 obj.Clear();
4172 in_scavenge = true;
4173 i::Heap::PerformScavenge();
4174 in_scavenge = false;
4175 *(reinterpret_cast<bool*>(data)) = true;
4176}
4177
4178static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
4179 void* data) {
4180 CHECK_EQ(0, last);
4181 last = 1;
4182 *(reinterpret_cast<bool*>(data)) = in_scavenge;
4183 obj.Dispose();
4184 obj.Clear();
4185}
4186
4187THREADED_TEST(NoWeakRefCallbacksInScavenge) {
4188 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
4189 // Calling callbacks from scavenges is unsafe as objects held by those
4190 // handlers might have become strongly reachable, but scavenge doesn't
4191 // check that.
4192 v8::Persistent<Context> context = Context::New();
4193 Context::Scope context_scope(context);
4194
4195 v8::Persistent<v8::Object> object_a;
4196 v8::Persistent<v8::Object> object_b;
4197
4198 {
4199 v8::HandleScope handle_scope;
4200 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
4201 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
4202 }
4203
4204 bool object_a_disposed = false;
4205 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
4206 bool released_in_scavenge = false;
4207 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
4208
4209 while (!object_a_disposed) {
4210 i::Heap::CollectAllGarbage(false);
4211 }
4212 CHECK(!released_in_scavenge);
4213}
4214
4215
Steve Blocka7e24c12009-10-30 11:49:00 +00004216v8::Handle<Function> args_fun;
4217
4218
4219static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
4220 ApiTestFuzzer::Fuzz();
4221 CHECK_EQ(args_fun, args.Callee());
4222 CHECK_EQ(3, args.Length());
4223 CHECK_EQ(v8::Integer::New(1), args[0]);
4224 CHECK_EQ(v8::Integer::New(2), args[1]);
4225 CHECK_EQ(v8::Integer::New(3), args[2]);
4226 CHECK_EQ(v8::Undefined(), args[3]);
4227 v8::HandleScope scope;
4228 i::Heap::CollectAllGarbage(false);
4229 return v8::Undefined();
4230}
4231
4232
4233THREADED_TEST(Arguments) {
4234 v8::HandleScope scope;
4235 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
4236 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
4237 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01004238 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00004239 v8_compile("f(1, 2, 3)")->Run();
4240}
4241
4242
Steve Blocka7e24c12009-10-30 11:49:00 +00004243static v8::Handle<Value> NoBlockGetterX(Local<String> name,
4244 const AccessorInfo&) {
4245 return v8::Handle<Value>();
4246}
4247
4248
4249static v8::Handle<Value> NoBlockGetterI(uint32_t index,
4250 const AccessorInfo&) {
4251 return v8::Handle<Value>();
4252}
4253
4254
4255static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
4256 const AccessorInfo&) {
4257 if (!name->Equals(v8_str("foo"))) {
4258 return v8::Handle<v8::Boolean>(); // not intercepted
4259 }
4260
4261 return v8::False(); // intercepted, and don't delete the property
4262}
4263
4264
4265static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4266 if (index != 2) {
4267 return v8::Handle<v8::Boolean>(); // not intercepted
4268 }
4269
4270 return v8::False(); // intercepted, and don't delete the property
4271}
4272
4273
4274THREADED_TEST(Deleter) {
4275 v8::HandleScope scope;
4276 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4277 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4278 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4279 LocalContext context;
4280 context->Global()->Set(v8_str("k"), obj->NewInstance());
4281 CompileRun(
4282 "k.foo = 'foo';"
4283 "k.bar = 'bar';"
4284 "k[2] = 2;"
4285 "k[4] = 4;");
4286 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4287 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4288
4289 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4290 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4291
4292 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4293 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4294
4295 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4296 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4297}
4298
4299
4300static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4301 ApiTestFuzzer::Fuzz();
4302 if (name->Equals(v8_str("foo")) ||
4303 name->Equals(v8_str("bar")) ||
4304 name->Equals(v8_str("baz"))) {
4305 return v8::Undefined();
4306 }
4307 return v8::Handle<Value>();
4308}
4309
4310
4311static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4312 ApiTestFuzzer::Fuzz();
4313 if (index == 0 || index == 1) return v8::Undefined();
4314 return v8::Handle<Value>();
4315}
4316
4317
4318static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4319 ApiTestFuzzer::Fuzz();
4320 v8::Handle<v8::Array> result = v8::Array::New(3);
4321 result->Set(v8::Integer::New(0), v8_str("foo"));
4322 result->Set(v8::Integer::New(1), v8_str("bar"));
4323 result->Set(v8::Integer::New(2), v8_str("baz"));
4324 return result;
4325}
4326
4327
4328static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4329 ApiTestFuzzer::Fuzz();
4330 v8::Handle<v8::Array> result = v8::Array::New(2);
4331 result->Set(v8::Integer::New(0), v8_str("0"));
4332 result->Set(v8::Integer::New(1), v8_str("1"));
4333 return result;
4334}
4335
4336
4337THREADED_TEST(Enumerators) {
4338 v8::HandleScope scope;
4339 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4340 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4341 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4342 LocalContext context;
4343 context->Global()->Set(v8_str("k"), obj->NewInstance());
4344 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4345 "k[10] = 0;"
4346 "k.a = 0;"
4347 "k[5] = 0;"
4348 "k.b = 0;"
4349 "k[4294967295] = 0;"
4350 "k.c = 0;"
4351 "k[4294967296] = 0;"
4352 "k.d = 0;"
4353 "k[140000] = 0;"
4354 "k.e = 0;"
4355 "k[30000000000] = 0;"
4356 "k.f = 0;"
4357 "var result = [];"
4358 "for (var prop in k) {"
4359 " result.push(prop);"
4360 "}"
4361 "result"));
4362 // Check that we get all the property names returned including the
4363 // ones from the enumerators in the right order: indexed properties
4364 // in numerical order, indexed interceptor properties, named
4365 // properties in insertion order, named interceptor properties.
4366 // This order is not mandated by the spec, so this test is just
4367 // documenting our behavior.
4368 CHECK_EQ(17, result->Length());
4369 // Indexed properties in numerical order.
4370 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4371 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4372 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4373 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4374 // Indexed interceptor properties in the order they are returned
4375 // from the enumerator interceptor.
4376 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4377 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4378 // Named properties in insertion order.
4379 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4380 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4381 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4382 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4383 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4384 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4385 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4386 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4387 // Named interceptor properties.
4388 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4389 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4390 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4391}
4392
4393
4394int p_getter_count;
4395int p_getter_count2;
4396
4397
4398static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4399 ApiTestFuzzer::Fuzz();
4400 p_getter_count++;
4401 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4402 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4403 if (name->Equals(v8_str("p1"))) {
4404 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4405 } else if (name->Equals(v8_str("p2"))) {
4406 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4407 } else if (name->Equals(v8_str("p3"))) {
4408 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4409 } else if (name->Equals(v8_str("p4"))) {
4410 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4411 }
4412 return v8::Undefined();
4413}
4414
4415
4416static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4417 ApiTestFuzzer::Fuzz();
4418 LocalContext context;
4419 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4420 CompileRun(
4421 "o1.__proto__ = { };"
4422 "var o2 = { __proto__: o1 };"
4423 "var o3 = { __proto__: o2 };"
4424 "var o4 = { __proto__: o3 };"
4425 "for (var i = 0; i < 10; i++) o4.p4;"
4426 "for (var i = 0; i < 10; i++) o3.p3;"
4427 "for (var i = 0; i < 10; i++) o2.p2;"
4428 "for (var i = 0; i < 10; i++) o1.p1;");
4429}
4430
4431
4432static v8::Handle<Value> PGetter2(Local<String> name,
4433 const AccessorInfo& info) {
4434 ApiTestFuzzer::Fuzz();
4435 p_getter_count2++;
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
4451THREADED_TEST(GetterHolders) {
4452 v8::HandleScope scope;
4453 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4454 obj->SetAccessor(v8_str("p1"), PGetter);
4455 obj->SetAccessor(v8_str("p2"), PGetter);
4456 obj->SetAccessor(v8_str("p3"), PGetter);
4457 obj->SetAccessor(v8_str("p4"), PGetter);
4458 p_getter_count = 0;
4459 RunHolderTest(obj);
4460 CHECK_EQ(40, p_getter_count);
4461}
4462
4463
4464THREADED_TEST(PreInterceptorHolders) {
4465 v8::HandleScope scope;
4466 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4467 obj->SetNamedPropertyHandler(PGetter2);
4468 p_getter_count2 = 0;
4469 RunHolderTest(obj);
4470 CHECK_EQ(40, p_getter_count2);
4471}
4472
4473
4474THREADED_TEST(ObjectInstantiation) {
4475 v8::HandleScope scope;
4476 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4477 templ->SetAccessor(v8_str("t"), PGetter2);
4478 LocalContext context;
4479 context->Global()->Set(v8_str("o"), templ->NewInstance());
4480 for (int i = 0; i < 100; i++) {
4481 v8::HandleScope inner_scope;
4482 v8::Handle<v8::Object> obj = templ->NewInstance();
4483 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4484 context->Global()->Set(v8_str("o2"), obj);
4485 v8::Handle<Value> value =
4486 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4487 CHECK_EQ(v8::True(), value);
4488 context->Global()->Set(v8_str("o"), obj);
4489 }
4490}
4491
4492
Ben Murdochb0fe1622011-05-05 13:52:32 +01004493static int StrCmp16(uint16_t* a, uint16_t* b) {
4494 while (true) {
4495 if (*a == 0 && *b == 0) return 0;
4496 if (*a != *b) return 0 + *a - *b;
4497 a++;
4498 b++;
4499 }
4500}
4501
4502
4503static int StrNCmp16(uint16_t* a, uint16_t* b, int n) {
4504 while (true) {
4505 if (n-- == 0) return 0;
4506 if (*a == 0 && *b == 0) return 0;
4507 if (*a != *b) return 0 + *a - *b;
4508 a++;
4509 b++;
4510 }
4511}
4512
4513
Steve Blocka7e24c12009-10-30 11:49:00 +00004514THREADED_TEST(StringWrite) {
4515 v8::HandleScope scope;
4516 v8::Handle<String> str = v8_str("abcde");
Ben Murdochb0fe1622011-05-05 13:52:32 +01004517 // abc<Icelandic eth><Unicode snowman>.
4518 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203");
4519
4520 CHECK_EQ(5, str2->Length());
Steve Blocka7e24c12009-10-30 11:49:00 +00004521
4522 char buf[100];
Ben Murdochb0fe1622011-05-05 13:52:32 +01004523 char utf8buf[100];
4524 uint16_t wbuf[100];
Steve Blocka7e24c12009-10-30 11:49:00 +00004525 int len;
Ben Murdochb0fe1622011-05-05 13:52:32 +01004526 int charlen;
4527
4528 memset(utf8buf, 0x1, sizeof(utf8buf));
4529 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen);
4530 CHECK_EQ(len, 9);
4531 CHECK_EQ(charlen, 5);
4532 CHECK_EQ(strcmp(utf8buf, "abc\303\260\342\230\203"), 0);
4533
4534 memset(utf8buf, 0x1, sizeof(utf8buf));
4535 len = str2->WriteUtf8(utf8buf, 8, &charlen);
4536 CHECK_EQ(len, 8);
4537 CHECK_EQ(charlen, 5);
4538 CHECK_EQ(strncmp(utf8buf, "abc\303\260\342\230\203\1", 9), 0);
4539
4540 memset(utf8buf, 0x1, sizeof(utf8buf));
4541 len = str2->WriteUtf8(utf8buf, 7, &charlen);
4542 CHECK_EQ(len, 5);
4543 CHECK_EQ(charlen, 4);
4544 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0);
4545
4546 memset(utf8buf, 0x1, sizeof(utf8buf));
4547 len = str2->WriteUtf8(utf8buf, 6, &charlen);
4548 CHECK_EQ(len, 5);
4549 CHECK_EQ(charlen, 4);
4550 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0);
4551
4552 memset(utf8buf, 0x1, sizeof(utf8buf));
4553 len = str2->WriteUtf8(utf8buf, 5, &charlen);
4554 CHECK_EQ(len, 5);
4555 CHECK_EQ(charlen, 4);
4556 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0);
4557
4558 memset(utf8buf, 0x1, sizeof(utf8buf));
4559 len = str2->WriteUtf8(utf8buf, 4, &charlen);
4560 CHECK_EQ(len, 3);
4561 CHECK_EQ(charlen, 3);
4562 CHECK_EQ(strncmp(utf8buf, "abc\1", 4), 0);
4563
4564 memset(utf8buf, 0x1, sizeof(utf8buf));
4565 len = str2->WriteUtf8(utf8buf, 3, &charlen);
4566 CHECK_EQ(len, 3);
4567 CHECK_EQ(charlen, 3);
4568 CHECK_EQ(strncmp(utf8buf, "abc\1", 4), 0);
4569
4570 memset(utf8buf, 0x1, sizeof(utf8buf));
4571 len = str2->WriteUtf8(utf8buf, 2, &charlen);
4572 CHECK_EQ(len, 2);
4573 CHECK_EQ(charlen, 2);
4574 CHECK_EQ(strncmp(utf8buf, "ab\1", 3), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004575
4576 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004577 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004578 len = str->WriteAscii(buf);
4579 CHECK_EQ(len, 5);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004580 len = str->Write(wbuf);
4581 CHECK_EQ(len, 5);
4582 CHECK_EQ(strcmp("abcde", buf), 0);
4583 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'};
4584 CHECK_EQ(StrCmp16(answer1, wbuf), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004585
4586 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004587 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004588 len = str->WriteAscii(buf, 0, 4);
4589 CHECK_EQ(len, 4);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004590 len = str->Write(wbuf, 0, 4);
4591 CHECK_EQ(len, 4);
Steve Blocka7e24c12009-10-30 11:49:00 +00004592 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004593 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101};
4594 CHECK_EQ(StrNCmp16(answer2, wbuf, 5), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004595
4596 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004597 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004598 len = str->WriteAscii(buf, 0, 5);
4599 CHECK_EQ(len, 5);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004600 len = str->Write(wbuf, 0, 5);
4601 CHECK_EQ(len, 5);
Steve Blocka7e24c12009-10-30 11:49:00 +00004602 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004603 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101};
4604 CHECK_EQ(StrNCmp16(answer3, wbuf, 6), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004605
4606 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004607 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004608 len = str->WriteAscii(buf, 0, 6);
4609 CHECK_EQ(len, 5);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004610 len = str->Write(wbuf, 0, 6);
4611 CHECK_EQ(len, 5);
4612 CHECK_EQ(strcmp("abcde", buf), 0);
4613 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'};
4614 CHECK_EQ(StrCmp16(answer4, wbuf), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004615
4616 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004617 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004618 len = str->WriteAscii(buf, 4, -1);
4619 CHECK_EQ(len, 1);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004620 len = str->Write(wbuf, 4, -1);
4621 CHECK_EQ(len, 1);
4622 CHECK_EQ(strcmp("e", buf), 0);
4623 uint16_t answer5[] = {'e', '\0'};
4624 CHECK_EQ(StrCmp16(answer5, wbuf), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004625
4626 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004627 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004628 len = str->WriteAscii(buf, 4, 6);
4629 CHECK_EQ(len, 1);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004630 len = str->Write(wbuf, 4, 6);
4631 CHECK_EQ(len, 1);
4632 CHECK_EQ(strcmp("e", buf), 0);
4633 CHECK_EQ(StrCmp16(answer5, wbuf), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004634
4635 memset(buf, 0x1, sizeof(buf));
Ben Murdochb0fe1622011-05-05 13:52:32 +01004636 memset(wbuf, 0x1, sizeof(wbuf));
Steve Blocka7e24c12009-10-30 11:49:00 +00004637 len = str->WriteAscii(buf, 4, 1);
4638 CHECK_EQ(len, 1);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004639 len = str->Write(wbuf, 4, 1);
4640 CHECK_EQ(len, 1);
Steve Blocka7e24c12009-10-30 11:49:00 +00004641 CHECK_EQ(strncmp("e\1", buf, 2), 0);
Ben Murdochb0fe1622011-05-05 13:52:32 +01004642 uint16_t answer6[] = {'e', 0x101};
4643 CHECK_EQ(StrNCmp16(answer6, wbuf, 2), 0);
4644
4645 memset(buf, 0x1, sizeof(buf));
4646 memset(wbuf, 0x1, sizeof(wbuf));
4647 len = str->WriteAscii(buf, 3, 1);
4648 CHECK_EQ(len, 1);
4649 len = str->Write(wbuf, 3, 1);
4650 CHECK_EQ(len, 1);
4651 CHECK_EQ(strncmp("d\1", buf, 2), 0);
4652 uint16_t answer7[] = {'d', 0x101};
4653 CHECK_EQ(StrNCmp16(answer7, wbuf, 2), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00004654}
4655
4656
4657THREADED_TEST(ToArrayIndex) {
4658 v8::HandleScope scope;
4659 LocalContext context;
4660
4661 v8::Handle<String> str = v8_str("42");
4662 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4663 CHECK(!index.IsEmpty());
4664 CHECK_EQ(42.0, index->Uint32Value());
4665 str = v8_str("42asdf");
4666 index = str->ToArrayIndex();
4667 CHECK(index.IsEmpty());
4668 str = v8_str("-42");
4669 index = str->ToArrayIndex();
4670 CHECK(index.IsEmpty());
4671 str = v8_str("4294967295");
4672 index = str->ToArrayIndex();
4673 CHECK(!index.IsEmpty());
4674 CHECK_EQ(4294967295.0, index->Uint32Value());
4675 v8::Handle<v8::Number> num = v8::Number::New(1);
4676 index = num->ToArrayIndex();
4677 CHECK(!index.IsEmpty());
4678 CHECK_EQ(1.0, index->Uint32Value());
4679 num = v8::Number::New(-1);
4680 index = num->ToArrayIndex();
4681 CHECK(index.IsEmpty());
4682 v8::Handle<v8::Object> obj = v8::Object::New();
4683 index = obj->ToArrayIndex();
4684 CHECK(index.IsEmpty());
4685}
4686
4687
4688THREADED_TEST(ErrorConstruction) {
4689 v8::HandleScope scope;
4690 LocalContext context;
4691
4692 v8::Handle<String> foo = v8_str("foo");
4693 v8::Handle<String> message = v8_str("message");
4694 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4695 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004696 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4697 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004698 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4699 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004700 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004701 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4702 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004703 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004704 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4705 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004706 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004707 v8::Handle<Value> error = v8::Exception::Error(foo);
4708 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004709 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004710}
4711
4712
4713static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4714 ApiTestFuzzer::Fuzz();
4715 return v8_num(10);
4716}
4717
4718
4719static void YSetter(Local<String> name,
4720 Local<Value> value,
4721 const AccessorInfo& info) {
4722 if (info.This()->Has(name)) {
4723 info.This()->Delete(name);
4724 }
4725 info.This()->Set(name, value);
4726}
4727
4728
4729THREADED_TEST(DeleteAccessor) {
4730 v8::HandleScope scope;
4731 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4732 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4733 LocalContext context;
4734 v8::Handle<v8::Object> holder = obj->NewInstance();
4735 context->Global()->Set(v8_str("holder"), holder);
4736 v8::Handle<Value> result = CompileRun(
4737 "holder.y = 11; holder.y = 12; holder.y");
4738 CHECK_EQ(12, result->Uint32Value());
4739}
4740
4741
4742THREADED_TEST(TypeSwitch) {
4743 v8::HandleScope scope;
4744 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4745 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4746 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4747 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4748 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4749 LocalContext context;
4750 v8::Handle<v8::Object> obj0 = v8::Object::New();
4751 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4752 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4753 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4754 for (int i = 0; i < 10; i++) {
4755 CHECK_EQ(0, type_switch->match(obj0));
4756 CHECK_EQ(1, type_switch->match(obj1));
4757 CHECK_EQ(2, type_switch->match(obj2));
4758 CHECK_EQ(3, type_switch->match(obj3));
4759 CHECK_EQ(3, type_switch->match(obj3));
4760 CHECK_EQ(2, type_switch->match(obj2));
4761 CHECK_EQ(1, type_switch->match(obj1));
4762 CHECK_EQ(0, type_switch->match(obj0));
4763 }
4764}
4765
4766
4767// For use within the TestSecurityHandler() test.
4768static bool g_security_callback_result = false;
4769static bool NamedSecurityTestCallback(Local<v8::Object> global,
4770 Local<Value> name,
4771 v8::AccessType type,
4772 Local<Value> data) {
4773 // Always allow read access.
4774 if (type == v8::ACCESS_GET)
4775 return true;
4776
4777 // Sometimes allow other access.
4778 return g_security_callback_result;
4779}
4780
4781
4782static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4783 uint32_t key,
4784 v8::AccessType type,
4785 Local<Value> data) {
4786 // Always allow read access.
4787 if (type == v8::ACCESS_GET)
4788 return true;
4789
4790 // Sometimes allow other access.
4791 return g_security_callback_result;
4792}
4793
4794
4795static int trouble_nesting = 0;
4796static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4797 ApiTestFuzzer::Fuzz();
4798 trouble_nesting++;
4799
4800 // Call a JS function that throws an uncaught exception.
4801 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4802 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4803 arg_this->Get(v8_str("trouble_callee")) :
4804 arg_this->Get(v8_str("trouble_caller"));
4805 CHECK(trouble_callee->IsFunction());
4806 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4807}
4808
4809
4810static int report_count = 0;
4811static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4812 v8::Handle<Value>) {
4813 report_count++;
4814}
4815
4816
4817// Counts uncaught exceptions, but other tests running in parallel
4818// also have uncaught exceptions.
4819TEST(ApiUncaughtException) {
4820 report_count = 0;
4821 v8::HandleScope scope;
4822 LocalContext env;
4823 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4824
4825 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4826 v8::Local<v8::Object> global = env->Global();
4827 global->Set(v8_str("trouble"), fun->GetFunction());
4828
4829 Script::Compile(v8_str("function trouble_callee() {"
4830 " var x = null;"
4831 " return x.foo;"
4832 "};"
4833 "function trouble_caller() {"
4834 " trouble();"
4835 "};"))->Run();
4836 Local<Value> trouble = global->Get(v8_str("trouble"));
4837 CHECK(trouble->IsFunction());
4838 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4839 CHECK(trouble_callee->IsFunction());
4840 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4841 CHECK(trouble_caller->IsFunction());
4842 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4843 CHECK_EQ(1, report_count);
4844 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4845}
4846
Leon Clarke4515c472010-02-03 11:58:03 +00004847static const char* script_resource_name = "ExceptionInNativeScript.js";
4848static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4849 v8::Handle<Value>) {
4850 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4851 CHECK(!name_val.IsEmpty() && name_val->IsString());
4852 v8::String::AsciiValue name(message->GetScriptResourceName());
4853 CHECK_EQ(script_resource_name, *name);
4854 CHECK_EQ(3, message->GetLineNumber());
4855 v8::String::AsciiValue source_line(message->GetSourceLine());
4856 CHECK_EQ(" new o.foo();", *source_line);
4857}
4858
4859TEST(ExceptionInNativeScript) {
4860 v8::HandleScope scope;
4861 LocalContext env;
4862 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4863
4864 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4865 v8::Local<v8::Object> global = env->Global();
4866 global->Set(v8_str("trouble"), fun->GetFunction());
4867
4868 Script::Compile(v8_str("function trouble() {\n"
4869 " var o = {};\n"
4870 " new o.foo();\n"
4871 "};"), v8::String::New(script_resource_name))->Run();
4872 Local<Value> trouble = global->Get(v8_str("trouble"));
4873 CHECK(trouble->IsFunction());
4874 Function::Cast(*trouble)->Call(global, 0, NULL);
4875 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4876}
4877
Steve Blocka7e24c12009-10-30 11:49:00 +00004878
4879TEST(CompilationErrorUsingTryCatchHandler) {
4880 v8::HandleScope scope;
4881 LocalContext env;
4882 v8::TryCatch try_catch;
4883 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4884 CHECK_NE(NULL, *try_catch.Exception());
4885 CHECK(try_catch.HasCaught());
4886}
4887
4888
4889TEST(TryCatchFinallyUsingTryCatchHandler) {
4890 v8::HandleScope scope;
4891 LocalContext env;
4892 v8::TryCatch try_catch;
4893 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4894 CHECK(!try_catch.HasCaught());
4895 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4896 CHECK(try_catch.HasCaught());
4897 try_catch.Reset();
4898 Script::Compile(v8_str("(function() {"
4899 "try { throw ''; } finally { return; }"
4900 "})()"))->Run();
4901 CHECK(!try_catch.HasCaught());
4902 Script::Compile(v8_str("(function()"
4903 " { try { throw ''; } finally { throw 0; }"
4904 "})()"))->Run();
4905 CHECK(try_catch.HasCaught());
4906}
4907
4908
4909// SecurityHandler can't be run twice
4910TEST(SecurityHandler) {
4911 v8::HandleScope scope0;
4912 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4913 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4914 IndexedSecurityTestCallback);
4915 // Create an environment
4916 v8::Persistent<Context> context0 =
4917 Context::New(NULL, global_template);
4918 context0->Enter();
4919
4920 v8::Handle<v8::Object> global0 = context0->Global();
4921 v8::Handle<Script> script0 = v8_compile("foo = 111");
4922 script0->Run();
4923 global0->Set(v8_str("0"), v8_num(999));
4924 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4925 CHECK_EQ(111, foo0->Int32Value());
4926 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4927 CHECK_EQ(999, z0->Int32Value());
4928
4929 // Create another environment, should fail security checks.
4930 v8::HandleScope scope1;
4931
4932 v8::Persistent<Context> context1 =
4933 Context::New(NULL, global_template);
4934 context1->Enter();
4935
4936 v8::Handle<v8::Object> global1 = context1->Global();
4937 global1->Set(v8_str("othercontext"), global0);
4938 // This set will fail the security check.
4939 v8::Handle<Script> script1 =
4940 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4941 script1->Run();
4942 // This read will pass the security check.
4943 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4944 CHECK_EQ(111, foo1->Int32Value());
4945 // This read will pass the security check.
4946 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4947 CHECK_EQ(999, z1->Int32Value());
4948
4949 // Create another environment, should pass security checks.
4950 { g_security_callback_result = true; // allow security handler to pass.
4951 v8::HandleScope scope2;
4952 LocalContext context2;
4953 v8::Handle<v8::Object> global2 = context2->Global();
4954 global2->Set(v8_str("othercontext"), global0);
4955 v8::Handle<Script> script2 =
4956 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4957 script2->Run();
4958 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4959 CHECK_EQ(333, foo2->Int32Value());
4960 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4961 CHECK_EQ(888, z2->Int32Value());
4962 }
4963
4964 context1->Exit();
4965 context1.Dispose();
4966
4967 context0->Exit();
4968 context0.Dispose();
4969}
4970
4971
4972THREADED_TEST(SecurityChecks) {
4973 v8::HandleScope handle_scope;
4974 LocalContext env1;
4975 v8::Persistent<Context> env2 = Context::New();
4976
4977 Local<Value> foo = v8_str("foo");
4978 Local<Value> bar = v8_str("bar");
4979
4980 // Set to the same domain.
4981 env1->SetSecurityToken(foo);
4982
4983 // Create a function in env1.
4984 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4985 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4986 CHECK(spy->IsFunction());
4987
4988 // Create another function accessing global objects.
4989 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4990 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4991 CHECK(spy2->IsFunction());
4992
4993 // Switch to env2 in the same domain and invoke spy on env2.
4994 {
4995 env2->SetSecurityToken(foo);
4996 // Enter env2
4997 Context::Scope scope_env2(env2);
4998 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4999 CHECK(result->IsFunction());
5000 }
5001
5002 {
5003 env2->SetSecurityToken(bar);
5004 Context::Scope scope_env2(env2);
5005
5006 // Call cross_domain_call, it should throw an exception
5007 v8::TryCatch try_catch;
5008 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
5009 CHECK(try_catch.HasCaught());
5010 }
5011
5012 env2.Dispose();
5013}
5014
5015
5016// Regression test case for issue 1183439.
5017THREADED_TEST(SecurityChecksForPrototypeChain) {
5018 v8::HandleScope scope;
5019 LocalContext current;
5020 v8::Persistent<Context> other = Context::New();
5021
5022 // Change context to be able to get to the Object function in the
5023 // other context without hitting the security checks.
5024 v8::Local<Value> other_object;
5025 { Context::Scope scope(other);
5026 other_object = other->Global()->Get(v8_str("Object"));
5027 other->Global()->Set(v8_num(42), v8_num(87));
5028 }
5029
5030 current->Global()->Set(v8_str("other"), other->Global());
5031 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
5032
5033 // Make sure the security check fails here and we get an undefined
5034 // result instead of getting the Object function. Repeat in a loop
5035 // to make sure to exercise the IC code.
5036 v8::Local<Script> access_other0 = v8_compile("other.Object");
5037 v8::Local<Script> access_other1 = v8_compile("other[42]");
5038 for (int i = 0; i < 5; i++) {
5039 CHECK(!access_other0->Run()->Equals(other_object));
5040 CHECK(access_other0->Run()->IsUndefined());
5041 CHECK(!access_other1->Run()->Equals(v8_num(87)));
5042 CHECK(access_other1->Run()->IsUndefined());
5043 }
5044
5045 // Create an object that has 'other' in its prototype chain and make
5046 // sure we cannot access the Object function indirectly through
5047 // that. Repeat in a loop to make sure to exercise the IC code.
5048 v8_compile("function F() { };"
5049 "F.prototype = other;"
5050 "var f = new F();")->Run();
5051 v8::Local<Script> access_f0 = v8_compile("f.Object");
5052 v8::Local<Script> access_f1 = v8_compile("f[42]");
5053 for (int j = 0; j < 5; j++) {
5054 CHECK(!access_f0->Run()->Equals(other_object));
5055 CHECK(access_f0->Run()->IsUndefined());
5056 CHECK(!access_f1->Run()->Equals(v8_num(87)));
5057 CHECK(access_f1->Run()->IsUndefined());
5058 }
5059
5060 // Now it gets hairy: Set the prototype for the other global object
5061 // to be the current global object. The prototype chain for 'f' now
5062 // goes through 'other' but ends up in the current global object.
5063 { Context::Scope scope(other);
5064 other->Global()->Set(v8_str("__proto__"), current->Global());
5065 }
5066 // Set a named and an index property on the current global
5067 // object. To force the lookup to go through the other global object,
5068 // the properties must not exist in the other global object.
5069 current->Global()->Set(v8_str("foo"), v8_num(100));
5070 current->Global()->Set(v8_num(99), v8_num(101));
5071 // Try to read the properties from f and make sure that the access
5072 // gets stopped by the security checks on the other global object.
5073 Local<Script> access_f2 = v8_compile("f.foo");
5074 Local<Script> access_f3 = v8_compile("f[99]");
5075 for (int k = 0; k < 5; k++) {
5076 CHECK(!access_f2->Run()->Equals(v8_num(100)));
5077 CHECK(access_f2->Run()->IsUndefined());
5078 CHECK(!access_f3->Run()->Equals(v8_num(101)));
5079 CHECK(access_f3->Run()->IsUndefined());
5080 }
5081 other.Dispose();
5082}
5083
5084
5085THREADED_TEST(CrossDomainDelete) {
5086 v8::HandleScope handle_scope;
5087 LocalContext env1;
5088 v8::Persistent<Context> env2 = Context::New();
5089
5090 Local<Value> foo = v8_str("foo");
5091 Local<Value> bar = v8_str("bar");
5092
5093 // Set to the same domain.
5094 env1->SetSecurityToken(foo);
5095 env2->SetSecurityToken(foo);
5096
5097 env1->Global()->Set(v8_str("prop"), v8_num(3));
5098 env2->Global()->Set(v8_str("env1"), env1->Global());
5099
5100 // Change env2 to a different domain and delete env1.prop.
5101 env2->SetSecurityToken(bar);
5102 {
5103 Context::Scope scope_env2(env2);
5104 Local<Value> result =
5105 Script::Compile(v8_str("delete env1.prop"))->Run();
5106 CHECK(result->IsFalse());
5107 }
5108
5109 // Check that env1.prop still exists.
5110 Local<Value> v = env1->Global()->Get(v8_str("prop"));
5111 CHECK(v->IsNumber());
5112 CHECK_EQ(3, v->Int32Value());
5113
5114 env2.Dispose();
5115}
5116
5117
5118THREADED_TEST(CrossDomainIsPropertyEnumerable) {
5119 v8::HandleScope handle_scope;
5120 LocalContext env1;
5121 v8::Persistent<Context> env2 = Context::New();
5122
5123 Local<Value> foo = v8_str("foo");
5124 Local<Value> bar = v8_str("bar");
5125
5126 // Set to the same domain.
5127 env1->SetSecurityToken(foo);
5128 env2->SetSecurityToken(foo);
5129
5130 env1->Global()->Set(v8_str("prop"), v8_num(3));
5131 env2->Global()->Set(v8_str("env1"), env1->Global());
5132
5133 // env1.prop is enumerable in env2.
5134 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
5135 {
5136 Context::Scope scope_env2(env2);
5137 Local<Value> result = Script::Compile(test)->Run();
5138 CHECK(result->IsTrue());
5139 }
5140
5141 // Change env2 to a different domain and test again.
5142 env2->SetSecurityToken(bar);
5143 {
5144 Context::Scope scope_env2(env2);
5145 Local<Value> result = Script::Compile(test)->Run();
5146 CHECK(result->IsFalse());
5147 }
5148
5149 env2.Dispose();
5150}
5151
5152
5153THREADED_TEST(CrossDomainForIn) {
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 // Change env2 to a different domain and set env1's global object
5169 // as the __proto__ of an object in env2 and enumerate properties
5170 // in for-in. It shouldn't enumerate properties on env1's global
5171 // object.
5172 env2->SetSecurityToken(bar);
5173 {
5174 Context::Scope scope_env2(env2);
5175 Local<Value> result =
5176 CompileRun("(function(){var obj = {'__proto__':env1};"
5177 "for (var p in obj)"
5178 " if (p == 'prop') return false;"
5179 "return true;})()");
5180 CHECK(result->IsTrue());
5181 }
5182 env2.Dispose();
5183}
5184
5185
5186TEST(ContextDetachGlobal) {
5187 v8::HandleScope handle_scope;
5188 LocalContext env1;
5189 v8::Persistent<Context> env2 = Context::New();
5190
5191 Local<v8::Object> global1 = env1->Global();
5192
5193 Local<Value> foo = v8_str("foo");
5194
5195 // Set to the same domain.
5196 env1->SetSecurityToken(foo);
5197 env2->SetSecurityToken(foo);
5198
5199 // Enter env2
5200 env2->Enter();
5201
Andrei Popescu74b3c142010-03-29 12:03:09 +01005202 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00005203 Local<v8::Object> global2 = env2->Global();
5204 global2->Set(v8_str("prop"), v8::Integer::New(1));
5205 CompileRun("function getProp() {return prop;}");
5206
5207 env1->Global()->Set(v8_str("getProp"),
5208 global2->Get(v8_str("getProp")));
5209
Andrei Popescu74b3c142010-03-29 12:03:09 +01005210 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00005211 env2->Exit();
5212 env2->DetachGlobal();
5213 // env2 has a new global object.
5214 CHECK(!env2->Global()->Equals(global2));
5215
5216 v8::Persistent<Context> env3 =
5217 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5218 env3->SetSecurityToken(v8_str("bar"));
5219 env3->Enter();
5220
5221 Local<v8::Object> global3 = env3->Global();
5222 CHECK_EQ(global2, global3);
5223 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
5224 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
5225 global3->Set(v8_str("prop"), v8::Integer::New(-1));
5226 global3->Set(v8_str("prop2"), v8::Integer::New(2));
5227 env3->Exit();
5228
5229 // Call getProp in env1, and it should return the value 1
5230 {
5231 Local<Value> get_prop = global1->Get(v8_str("getProp"));
5232 CHECK(get_prop->IsFunction());
5233 v8::TryCatch try_catch;
5234 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
5235 CHECK(!try_catch.HasCaught());
5236 CHECK_EQ(1, r->Int32Value());
5237 }
5238
5239 // Check that env3 is not accessible from env1
5240 {
5241 Local<Value> r = global3->Get(v8_str("prop2"));
5242 CHECK(r->IsUndefined());
5243 }
5244
5245 env2.Dispose();
5246 env3.Dispose();
5247}
5248
5249
Andrei Popescu74b3c142010-03-29 12:03:09 +01005250TEST(DetachAndReattachGlobal) {
5251 v8::HandleScope scope;
5252 LocalContext env1;
5253
5254 // Create second environment.
5255 v8::Persistent<Context> env2 = Context::New();
5256
5257 Local<Value> foo = v8_str("foo");
5258
5259 // Set same security token for env1 and env2.
5260 env1->SetSecurityToken(foo);
5261 env2->SetSecurityToken(foo);
5262
5263 // Create a property on the global object in env2.
5264 {
5265 v8::Context::Scope scope(env2);
5266 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
5267 }
5268
5269 // Create a reference to env2 global from env1 global.
5270 env1->Global()->Set(v8_str("other"), env2->Global());
5271
5272 // Check that we have access to other.p in env2 from env1.
5273 Local<Value> result = CompileRun("other.p");
5274 CHECK(result->IsInt32());
5275 CHECK_EQ(42, result->Int32Value());
5276
5277 // Hold on to global from env2 and detach global from env2.
5278 Local<v8::Object> global2 = env2->Global();
5279 env2->DetachGlobal();
5280
5281 // Check that the global has been detached. No other.p property can
5282 // be found.
5283 result = CompileRun("other.p");
5284 CHECK(result->IsUndefined());
5285
5286 // Reuse global2 for env3.
5287 v8::Persistent<Context> env3 =
5288 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5289 CHECK_EQ(global2, env3->Global());
5290
5291 // Start by using the same security token for env3 as for env1 and env2.
5292 env3->SetSecurityToken(foo);
5293
5294 // Create a property on the global object in env3.
5295 {
5296 v8::Context::Scope scope(env3);
5297 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
5298 }
5299
5300 // Check that other.p is now the property in env3 and that we have access.
5301 result = CompileRun("other.p");
5302 CHECK(result->IsInt32());
5303 CHECK_EQ(24, result->Int32Value());
5304
5305 // Change security token for env3 to something different from env1 and env2.
5306 env3->SetSecurityToken(v8_str("bar"));
5307
5308 // Check that we do not have access to other.p in env1. |other| is now
5309 // the global object for env3 which has a different security token,
5310 // so access should be blocked.
5311 result = CompileRun("other.p");
5312 CHECK(result->IsUndefined());
5313
5314 // Detach the global for env3 and reattach it to env2.
5315 env3->DetachGlobal();
5316 env2->ReattachGlobal(global2);
5317
5318 // Check that we have access to other.p again in env1. |other| is now
5319 // the global object for env2 which has the same security token as env1.
5320 result = CompileRun("other.p");
5321 CHECK(result->IsInt32());
5322 CHECK_EQ(42, result->Int32Value());
5323
5324 env2.Dispose();
5325 env3.Dispose();
5326}
5327
5328
Steve Block1e0659c2011-05-24 12:43:12 +01005329static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false };
Steve Blocka7e24c12009-10-30 11:49:00 +00005330static bool NamedAccessBlocker(Local<v8::Object> global,
5331 Local<Value> name,
5332 v8::AccessType type,
5333 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005334 return Context::GetCurrent()->Global()->Equals(global) ||
5335 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005336}
5337
5338
5339static bool IndexedAccessBlocker(Local<v8::Object> global,
5340 uint32_t key,
5341 v8::AccessType type,
5342 Local<Value> data) {
Steve Block1e0659c2011-05-24 12:43:12 +01005343 return Context::GetCurrent()->Global()->Equals(global) ||
5344 allowed_access_type[type];
Steve Blocka7e24c12009-10-30 11:49:00 +00005345}
5346
5347
5348static int g_echo_value = -1;
5349static v8::Handle<Value> EchoGetter(Local<String> name,
5350 const AccessorInfo& info) {
5351 return v8_num(g_echo_value);
5352}
5353
5354
5355static void EchoSetter(Local<String> name,
5356 Local<Value> value,
5357 const AccessorInfo&) {
5358 if (value->IsNumber())
5359 g_echo_value = value->Int32Value();
5360}
5361
5362
5363static v8::Handle<Value> UnreachableGetter(Local<String> name,
5364 const AccessorInfo& info) {
5365 CHECK(false); // This function should not be called..
5366 return v8::Undefined();
5367}
5368
5369
5370static void UnreachableSetter(Local<String>, Local<Value>,
5371 const AccessorInfo&) {
5372 CHECK(false); // This function should nto be called.
5373}
5374
5375
Steve Block1e0659c2011-05-24 12:43:12 +01005376TEST(AccessControl) {
Steve Blocka7e24c12009-10-30 11:49:00 +00005377 v8::HandleScope handle_scope;
5378 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5379
5380 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5381 IndexedAccessBlocker);
5382
5383 // Add an accessor accessible by cross-domain JS code.
5384 global_template->SetAccessor(
5385 v8_str("accessible_prop"),
5386 EchoGetter, EchoSetter,
5387 v8::Handle<Value>(),
5388 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5389
5390 // Add an accessor that is not accessible by cross-domain JS code.
5391 global_template->SetAccessor(v8_str("blocked_prop"),
5392 UnreachableGetter, UnreachableSetter,
5393 v8::Handle<Value>(),
5394 v8::DEFAULT);
5395
5396 // Create an environment
5397 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5398 context0->Enter();
5399
5400 v8::Handle<v8::Object> global0 = context0->Global();
5401
Steve Block1e0659c2011-05-24 12:43:12 +01005402 // Define a property with JS getter and setter.
5403 CompileRun(
5404 "function getter() { return 'getter'; };\n"
5405 "function setter() { return 'setter'; }\n"
5406 "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})");
5407
5408 Local<Value> getter = global0->Get(v8_str("getter"));
5409 Local<Value> setter = global0->Get(v8_str("setter"));
5410
5411 // And define normal element.
5412 global0->Set(239, v8_str("239"));
5413
5414 // Define an element with JS getter and setter.
5415 CompileRun(
5416 "function el_getter() { return 'el_getter'; };\n"
5417 "function el_setter() { return 'el_setter'; };\n"
5418 "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});");
5419
5420 Local<Value> el_getter = global0->Get(v8_str("el_getter"));
5421 Local<Value> el_setter = global0->Get(v8_str("el_setter"));
5422
Steve Blocka7e24c12009-10-30 11:49:00 +00005423 v8::HandleScope scope1;
5424
5425 v8::Persistent<Context> context1 = Context::New();
5426 context1->Enter();
5427
5428 v8::Handle<v8::Object> global1 = context1->Global();
5429 global1->Set(v8_str("other"), global0);
5430
Steve Block1e0659c2011-05-24 12:43:12 +01005431 // Access blocked property.
5432 CompileRun("other.blocked_prop = 1");
5433
5434 ExpectUndefined("other.blocked_prop");
5435 ExpectUndefined(
5436 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
5437 ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')");
5438
5439 // Enable ACCESS_HAS
5440 allowed_access_type[v8::ACCESS_HAS] = true;
5441 ExpectUndefined("other.blocked_prop");
5442 // ... and now we can get the descriptor...
5443 ExpectUndefined(
5444 "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value");
5445 // ... and enumerate the property.
5446 ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')");
5447 allowed_access_type[v8::ACCESS_HAS] = false;
5448
5449 // Access blocked element.
5450 CompileRun("other[239] = 1");
5451
5452 ExpectUndefined("other[239]");
5453 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')");
5454 ExpectFalse("propertyIsEnumerable.call(other, '239')");
5455
5456 // Enable ACCESS_HAS
5457 allowed_access_type[v8::ACCESS_HAS] = true;
5458 ExpectUndefined("other[239]");
5459 // ... and now we can get the descriptor...
5460 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value");
5461 // ... and enumerate the property.
5462 ExpectTrue("propertyIsEnumerable.call(other, '239')");
5463 allowed_access_type[v8::ACCESS_HAS] = false;
5464
5465 // Access a property with JS accessor.
5466 CompileRun("other.js_accessor_p = 2");
5467
5468 ExpectUndefined("other.js_accessor_p");
5469 ExpectUndefined(
5470 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')");
5471
5472 // Enable ACCESS_HAS.
5473 allowed_access_type[v8::ACCESS_HAS] = true;
5474 ExpectUndefined("other.js_accessor_p");
5475 ExpectUndefined(
5476 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5477 ExpectUndefined(
5478 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5479 ExpectUndefined(
5480 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5481 allowed_access_type[v8::ACCESS_HAS] = false;
5482
5483 // Enable both ACCESS_HAS and ACCESS_GET.
5484 allowed_access_type[v8::ACCESS_HAS] = true;
5485 allowed_access_type[v8::ACCESS_GET] = true;
5486
5487 ExpectString("other.js_accessor_p", "getter");
5488 ExpectObject(
5489 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5490 ExpectUndefined(
5491 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set");
5492 ExpectUndefined(
5493 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5494
5495 allowed_access_type[v8::ACCESS_GET] = false;
5496 allowed_access_type[v8::ACCESS_HAS] = false;
5497
5498 // Enable both ACCESS_HAS and ACCESS_SET.
5499 allowed_access_type[v8::ACCESS_HAS] = true;
5500 allowed_access_type[v8::ACCESS_SET] = true;
5501
5502 ExpectUndefined("other.js_accessor_p");
5503 ExpectUndefined(
5504 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get");
5505 ExpectObject(
5506 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5507 ExpectUndefined(
5508 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5509
5510 allowed_access_type[v8::ACCESS_SET] = false;
5511 allowed_access_type[v8::ACCESS_HAS] = false;
5512
5513 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5514 allowed_access_type[v8::ACCESS_HAS] = true;
5515 allowed_access_type[v8::ACCESS_GET] = true;
5516 allowed_access_type[v8::ACCESS_SET] = true;
5517
5518 ExpectString("other.js_accessor_p", "getter");
5519 ExpectObject(
5520 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter);
5521 ExpectObject(
5522 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter);
5523 ExpectUndefined(
5524 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value");
5525
5526 allowed_access_type[v8::ACCESS_SET] = false;
5527 allowed_access_type[v8::ACCESS_GET] = false;
5528 allowed_access_type[v8::ACCESS_HAS] = false;
5529
5530 // Access an element with JS accessor.
5531 CompileRun("other[42] = 2");
5532
5533 ExpectUndefined("other[42]");
5534 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')");
5535
5536 // Enable ACCESS_HAS.
5537 allowed_access_type[v8::ACCESS_HAS] = true;
5538 ExpectUndefined("other[42]");
5539 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5540 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5541 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5542 allowed_access_type[v8::ACCESS_HAS] = false;
5543
5544 // Enable both ACCESS_HAS and ACCESS_GET.
5545 allowed_access_type[v8::ACCESS_HAS] = true;
5546 allowed_access_type[v8::ACCESS_GET] = true;
5547
5548 ExpectString("other[42]", "el_getter");
5549 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5550 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set");
5551 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5552
5553 allowed_access_type[v8::ACCESS_GET] = false;
5554 allowed_access_type[v8::ACCESS_HAS] = false;
5555
5556 // Enable both ACCESS_HAS and ACCESS_SET.
5557 allowed_access_type[v8::ACCESS_HAS] = true;
5558 allowed_access_type[v8::ACCESS_SET] = true;
5559
5560 ExpectUndefined("other[42]");
5561 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get");
5562 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5563 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5564
5565 allowed_access_type[v8::ACCESS_SET] = false;
5566 allowed_access_type[v8::ACCESS_HAS] = false;
5567
5568 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET.
5569 allowed_access_type[v8::ACCESS_HAS] = true;
5570 allowed_access_type[v8::ACCESS_GET] = true;
5571 allowed_access_type[v8::ACCESS_SET] = true;
5572
5573 ExpectString("other[42]", "el_getter");
5574 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter);
5575 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter);
5576 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value");
5577
5578 allowed_access_type[v8::ACCESS_SET] = false;
5579 allowed_access_type[v8::ACCESS_GET] = false;
5580 allowed_access_type[v8::ACCESS_HAS] = false;
5581
Steve Blocka7e24c12009-10-30 11:49:00 +00005582 v8::Handle<Value> value;
5583
Steve Blocka7e24c12009-10-30 11:49:00 +00005584 // Access accessible property
Steve Block1e0659c2011-05-24 12:43:12 +01005585 value = CompileRun("other.accessible_prop = 3");
Steve Blocka7e24c12009-10-30 11:49:00 +00005586 CHECK(value->IsNumber());
5587 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005588 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005589
Steve Block1e0659c2011-05-24 12:43:12 +01005590 value = CompileRun("other.accessible_prop");
Steve Blocka7e24c12009-10-30 11:49:00 +00005591 CHECK(value->IsNumber());
5592 CHECK_EQ(3, value->Int32Value());
5593
Steve Block1e0659c2011-05-24 12:43:12 +01005594 value = CompileRun(
5595 "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value");
5596 CHECK(value->IsNumber());
5597 CHECK_EQ(3, value->Int32Value());
5598
5599 value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')");
Steve Blocka7e24c12009-10-30 11:49:00 +00005600 CHECK(value->IsTrue());
5601
5602 // Enumeration doesn't enumerate accessors from inaccessible objects in
5603 // the prototype chain even if the accessors are in themselves accessible.
Steve Block1e0659c2011-05-24 12:43:12 +01005604 value =
Steve Blocka7e24c12009-10-30 11:49:00 +00005605 CompileRun("(function(){var obj = {'__proto__':other};"
5606 "for (var p in obj)"
5607 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5608 " return false;"
5609 " }"
5610 "return true;})()");
Steve Block1e0659c2011-05-24 12:43:12 +01005611 CHECK(value->IsTrue());
Steve Blocka7e24c12009-10-30 11:49:00 +00005612
5613 context1->Exit();
5614 context0->Exit();
5615 context1.Dispose();
5616 context0.Dispose();
5617}
5618
5619
Leon Clarke4515c472010-02-03 11:58:03 +00005620static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5621 Local<Value> name,
5622 v8::AccessType type,
5623 Local<Value> data) {
5624 return false;
5625}
5626
5627
5628static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5629 uint32_t key,
5630 v8::AccessType type,
5631 Local<Value> data) {
5632 return false;
5633}
5634
5635
5636THREADED_TEST(AccessControlGetOwnPropertyNames) {
5637 v8::HandleScope handle_scope;
5638 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5639
5640 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5641 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5642 GetOwnPropertyNamesIndexedBlocker);
5643
5644 // Create an environment
5645 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5646 context0->Enter();
5647
5648 v8::Handle<v8::Object> global0 = context0->Global();
5649
5650 v8::HandleScope scope1;
5651
5652 v8::Persistent<Context> context1 = Context::New();
5653 context1->Enter();
5654
5655 v8::Handle<v8::Object> global1 = context1->Global();
5656 global1->Set(v8_str("other"), global0);
5657 global1->Set(v8_str("object"), obj_template->NewInstance());
5658
5659 v8::Handle<Value> value;
5660
5661 // Attempt to get the property names of the other global object and
5662 // of an object that requires access checks. Accessing the other
5663 // global object should be blocked by access checks on the global
5664 // proxy object. Accessing the object that requires access checks
5665 // is blocked by the access checks on the object itself.
5666 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5667 CHECK(value->IsTrue());
5668
5669 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5670 CHECK(value->IsTrue());
5671
5672 context1->Exit();
5673 context0->Exit();
5674 context1.Dispose();
5675 context0.Dispose();
5676}
5677
5678
Steve Block8defd9f2010-07-08 12:39:36 +01005679static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5680 v8::Handle<v8::Array> result = v8::Array::New(1);
5681 result->Set(0, v8_str("x"));
5682 return result;
5683}
5684
5685
5686THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5687 v8::HandleScope handle_scope;
5688 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5689
5690 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5691 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5692 NamedPropertyEnumerator);
5693
5694 LocalContext context;
5695 v8::Handle<v8::Object> global = context->Global();
5696 global->Set(v8_str("object"), obj_template->NewInstance());
5697
5698 v8::Handle<Value> value =
5699 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5700 CHECK_EQ(v8_str("x"), value);
5701}
5702
5703
Steve Blocka7e24c12009-10-30 11:49:00 +00005704static v8::Handle<Value> ConstTenGetter(Local<String> name,
5705 const AccessorInfo& info) {
5706 return v8_num(10);
5707}
5708
5709
5710THREADED_TEST(CrossDomainAccessors) {
5711 v8::HandleScope handle_scope;
5712
5713 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5714
5715 v8::Handle<v8::ObjectTemplate> global_template =
5716 func_template->InstanceTemplate();
5717
5718 v8::Handle<v8::ObjectTemplate> proto_template =
5719 func_template->PrototypeTemplate();
5720
5721 // Add an accessor to proto that's accessible by cross-domain JS code.
5722 proto_template->SetAccessor(v8_str("accessible"),
5723 ConstTenGetter, 0,
5724 v8::Handle<Value>(),
5725 v8::ALL_CAN_READ);
5726
5727 // Add an accessor that is not accessible by cross-domain JS code.
5728 global_template->SetAccessor(v8_str("unreachable"),
5729 UnreachableGetter, 0,
5730 v8::Handle<Value>(),
5731 v8::DEFAULT);
5732
5733 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5734 context0->Enter();
5735
5736 Local<v8::Object> global = context0->Global();
5737 // Add a normal property that shadows 'accessible'
5738 global->Set(v8_str("accessible"), v8_num(11));
5739
5740 // Enter a new context.
5741 v8::HandleScope scope1;
5742 v8::Persistent<Context> context1 = Context::New();
5743 context1->Enter();
5744
5745 v8::Handle<v8::Object> global1 = context1->Global();
5746 global1->Set(v8_str("other"), global);
5747
5748 // Should return 10, instead of 11
5749 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5750 CHECK(value->IsNumber());
5751 CHECK_EQ(10, value->Int32Value());
5752
5753 value = v8_compile("other.unreachable")->Run();
5754 CHECK(value->IsUndefined());
5755
5756 context1->Exit();
5757 context0->Exit();
5758 context1.Dispose();
5759 context0.Dispose();
5760}
5761
5762
5763static int named_access_count = 0;
5764static int indexed_access_count = 0;
5765
5766static bool NamedAccessCounter(Local<v8::Object> global,
5767 Local<Value> name,
5768 v8::AccessType type,
5769 Local<Value> data) {
5770 named_access_count++;
5771 return true;
5772}
5773
5774
5775static bool IndexedAccessCounter(Local<v8::Object> global,
5776 uint32_t key,
5777 v8::AccessType type,
5778 Local<Value> data) {
5779 indexed_access_count++;
5780 return true;
5781}
5782
5783
5784// This one is too easily disturbed by other tests.
5785TEST(AccessControlIC) {
5786 named_access_count = 0;
5787 indexed_access_count = 0;
5788
5789 v8::HandleScope handle_scope;
5790
5791 // Create an environment.
5792 v8::Persistent<Context> context0 = Context::New();
5793 context0->Enter();
5794
5795 // Create an object that requires access-check functions to be
5796 // called for cross-domain access.
5797 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5798 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5799 IndexedAccessCounter);
5800 Local<v8::Object> object = object_template->NewInstance();
5801
5802 v8::HandleScope scope1;
5803
5804 // Create another environment.
5805 v8::Persistent<Context> context1 = Context::New();
5806 context1->Enter();
5807
5808 // Make easy access to the object from the other environment.
5809 v8::Handle<v8::Object> global1 = context1->Global();
5810 global1->Set(v8_str("obj"), object);
5811
5812 v8::Handle<Value> value;
5813
5814 // Check that the named access-control function is called every time.
5815 CompileRun("function testProp(obj) {"
5816 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5817 " for (var j = 0; j < 10; j++) obj.prop;"
5818 " return obj.prop"
5819 "}");
5820 value = CompileRun("testProp(obj)");
5821 CHECK(value->IsNumber());
5822 CHECK_EQ(1, value->Int32Value());
5823 CHECK_EQ(21, named_access_count);
5824
5825 // Check that the named access-control function is called every time.
5826 CompileRun("var p = 'prop';"
5827 "function testKeyed(obj) {"
5828 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5829 " for (var j = 0; j < 10; j++) obj[p];"
5830 " return obj[p];"
5831 "}");
5832 // Use obj which requires access checks. No inline caching is used
5833 // in that case.
5834 value = CompileRun("testKeyed(obj)");
5835 CHECK(value->IsNumber());
5836 CHECK_EQ(1, value->Int32Value());
5837 CHECK_EQ(42, named_access_count);
5838 // Force the inline caches into generic state and try again.
5839 CompileRun("testKeyed({ a: 0 })");
5840 CompileRun("testKeyed({ b: 0 })");
5841 value = CompileRun("testKeyed(obj)");
5842 CHECK(value->IsNumber());
5843 CHECK_EQ(1, value->Int32Value());
5844 CHECK_EQ(63, named_access_count);
5845
5846 // Check that the indexed access-control function is called every time.
5847 CompileRun("function testIndexed(obj) {"
5848 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5849 " for (var j = 0; j < 10; j++) obj[0];"
5850 " return obj[0]"
5851 "}");
5852 value = CompileRun("testIndexed(obj)");
5853 CHECK(value->IsNumber());
5854 CHECK_EQ(1, value->Int32Value());
5855 CHECK_EQ(21, indexed_access_count);
5856 // Force the inline caches into generic state.
5857 CompileRun("testIndexed(new Array(1))");
5858 // Test that the indexed access check is called.
5859 value = CompileRun("testIndexed(obj)");
5860 CHECK(value->IsNumber());
5861 CHECK_EQ(1, value->Int32Value());
5862 CHECK_EQ(42, indexed_access_count);
5863
5864 // Check that the named access check is called when invoking
5865 // functions on an object that requires access checks.
5866 CompileRun("obj.f = function() {}");
5867 CompileRun("function testCallNormal(obj) {"
5868 " for (var i = 0; i < 10; i++) obj.f();"
5869 "}");
5870 CompileRun("testCallNormal(obj)");
5871 CHECK_EQ(74, named_access_count);
5872
5873 // Force obj into slow case.
5874 value = CompileRun("delete obj.prop");
5875 CHECK(value->BooleanValue());
5876 // Force inline caches into dictionary probing mode.
5877 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5878 // Test that the named access check is called.
5879 value = CompileRun("testProp(obj);");
5880 CHECK(value->IsNumber());
5881 CHECK_EQ(1, value->Int32Value());
5882 CHECK_EQ(96, named_access_count);
5883
5884 // Force the call inline cache into dictionary probing mode.
5885 CompileRun("o.f = function() {}; testCallNormal(o)");
5886 // Test that the named access check is still called for each
5887 // invocation of the function.
5888 value = CompileRun("testCallNormal(obj)");
5889 CHECK_EQ(106, named_access_count);
5890
5891 context1->Exit();
5892 context0->Exit();
5893 context1.Dispose();
5894 context0.Dispose();
5895}
5896
5897
5898static bool NamedAccessFlatten(Local<v8::Object> global,
5899 Local<Value> name,
5900 v8::AccessType type,
5901 Local<Value> data) {
5902 char buf[100];
5903 int len;
5904
5905 CHECK(name->IsString());
5906
5907 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005908 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005909 CHECK_EQ(4, len);
5910
5911 uint16_t buf2[100];
5912
5913 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005914 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005915 CHECK_EQ(4, len);
5916
5917 return true;
5918}
5919
5920
5921static bool IndexedAccessFlatten(Local<v8::Object> global,
5922 uint32_t key,
5923 v8::AccessType type,
5924 Local<Value> data) {
5925 return true;
5926}
5927
5928
5929// Regression test. In access checks, operations that may cause
5930// garbage collection are not allowed. It used to be the case that
5931// using the Write operation on a string could cause a garbage
5932// collection due to flattening of the string. This is no longer the
5933// case.
5934THREADED_TEST(AccessControlFlatten) {
5935 named_access_count = 0;
5936 indexed_access_count = 0;
5937
5938 v8::HandleScope handle_scope;
5939
5940 // Create an environment.
5941 v8::Persistent<Context> context0 = Context::New();
5942 context0->Enter();
5943
5944 // Create an object that requires access-check functions to be
5945 // called for cross-domain access.
5946 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5947 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5948 IndexedAccessFlatten);
5949 Local<v8::Object> object = object_template->NewInstance();
5950
5951 v8::HandleScope scope1;
5952
5953 // Create another environment.
5954 v8::Persistent<Context> context1 = Context::New();
5955 context1->Enter();
5956
5957 // Make easy access to the object from the other environment.
5958 v8::Handle<v8::Object> global1 = context1->Global();
5959 global1->Set(v8_str("obj"), object);
5960
5961 v8::Handle<Value> value;
5962
5963 value = v8_compile("var p = 'as' + 'df';")->Run();
5964 value = v8_compile("obj[p];")->Run();
5965
5966 context1->Exit();
5967 context0->Exit();
5968 context1.Dispose();
5969 context0.Dispose();
5970}
5971
5972
5973static v8::Handle<Value> AccessControlNamedGetter(
5974 Local<String>, const AccessorInfo&) {
5975 return v8::Integer::New(42);
5976}
5977
5978
5979static v8::Handle<Value> AccessControlNamedSetter(
5980 Local<String>, Local<Value> value, const AccessorInfo&) {
5981 return value;
5982}
5983
5984
5985static v8::Handle<Value> AccessControlIndexedGetter(
5986 uint32_t index,
5987 const AccessorInfo& info) {
5988 return v8_num(42);
5989}
5990
5991
5992static v8::Handle<Value> AccessControlIndexedSetter(
5993 uint32_t, Local<Value> value, const AccessorInfo&) {
5994 return value;
5995}
5996
5997
5998THREADED_TEST(AccessControlInterceptorIC) {
5999 named_access_count = 0;
6000 indexed_access_count = 0;
6001
6002 v8::HandleScope handle_scope;
6003
6004 // Create an environment.
6005 v8::Persistent<Context> context0 = Context::New();
6006 context0->Enter();
6007
6008 // Create an object that requires access-check functions to be
6009 // called for cross-domain access. The object also has interceptors
6010 // interceptor.
6011 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
6012 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
6013 IndexedAccessCounter);
6014 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
6015 AccessControlNamedSetter);
6016 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
6017 AccessControlIndexedSetter);
6018 Local<v8::Object> object = object_template->NewInstance();
6019
6020 v8::HandleScope scope1;
6021
6022 // Create another environment.
6023 v8::Persistent<Context> context1 = Context::New();
6024 context1->Enter();
6025
6026 // Make easy access to the object from the other environment.
6027 v8::Handle<v8::Object> global1 = context1->Global();
6028 global1->Set(v8_str("obj"), object);
6029
6030 v8::Handle<Value> value;
6031
6032 // Check that the named access-control function is called every time
6033 // eventhough there is an interceptor on the object.
6034 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
6035 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
6036 "obj.x")->Run();
6037 CHECK(value->IsNumber());
6038 CHECK_EQ(42, value->Int32Value());
6039 CHECK_EQ(21, named_access_count);
6040
6041 value = v8_compile("var p = 'x';")->Run();
6042 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
6043 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
6044 "obj[p]")->Run();
6045 CHECK(value->IsNumber());
6046 CHECK_EQ(42, value->Int32Value());
6047 CHECK_EQ(42, named_access_count);
6048
6049 // Check that the indexed access-control function is called every
6050 // time eventhough there is an interceptor on the object.
6051 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
6052 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
6053 "obj[0]")->Run();
6054 CHECK(value->IsNumber());
6055 CHECK_EQ(42, value->Int32Value());
6056 CHECK_EQ(21, indexed_access_count);
6057
6058 context1->Exit();
6059 context0->Exit();
6060 context1.Dispose();
6061 context0.Dispose();
6062}
6063
6064
6065THREADED_TEST(Version) {
6066 v8::V8::GetVersion();
6067}
6068
6069
6070static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
6071 ApiTestFuzzer::Fuzz();
6072 return v8_num(12);
6073}
6074
6075
6076THREADED_TEST(InstanceProperties) {
6077 v8::HandleScope handle_scope;
6078 LocalContext context;
6079
6080 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6081 Local<ObjectTemplate> instance = t->InstanceTemplate();
6082
6083 instance->Set(v8_str("x"), v8_num(42));
6084 instance->Set(v8_str("f"),
6085 v8::FunctionTemplate::New(InstanceFunctionCallback));
6086
6087 Local<Value> o = t->GetFunction()->NewInstance();
6088
6089 context->Global()->Set(v8_str("i"), o);
6090 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
6091 CHECK_EQ(42, value->Int32Value());
6092
6093 value = Script::Compile(v8_str("i.f()"))->Run();
6094 CHECK_EQ(12, value->Int32Value());
6095}
6096
6097
6098static v8::Handle<Value>
6099GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
6100 ApiTestFuzzer::Fuzz();
6101 return v8::Handle<Value>();
6102}
6103
6104
6105THREADED_TEST(GlobalObjectInstanceProperties) {
6106 v8::HandleScope handle_scope;
6107
6108 Local<Value> global_object;
6109
6110 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6111 t->InstanceTemplate()->SetNamedPropertyHandler(
6112 GlobalObjectInstancePropertiesGet);
6113 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6114 instance_template->Set(v8_str("x"), v8_num(42));
6115 instance_template->Set(v8_str("f"),
6116 v8::FunctionTemplate::New(InstanceFunctionCallback));
6117
Ben Murdochb0fe1622011-05-05 13:52:32 +01006118 // The script to check how Crankshaft compiles missing global function
6119 // invocations. function g is not defined and should throw on call.
6120 const char* script =
6121 "function wrapper(call) {"
6122 " var x = 0, y = 1;"
6123 " for (var i = 0; i < 1000; i++) {"
6124 " x += i * 100;"
6125 " y += i * 100;"
6126 " }"
6127 " if (call) g();"
6128 "}"
6129 "for (var i = 0; i < 17; i++) wrapper(false);"
6130 "var thrown = 0;"
6131 "try { wrapper(true); } catch (e) { thrown = 1; };"
6132 "thrown";
6133
Steve Blocka7e24c12009-10-30 11:49:00 +00006134 {
6135 LocalContext env(NULL, instance_template);
6136 // Hold on to the global object so it can be used again in another
6137 // environment initialization.
6138 global_object = env->Global();
6139
6140 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6141 CHECK_EQ(42, value->Int32Value());
6142 value = Script::Compile(v8_str("f()"))->Run();
6143 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006144 value = Script::Compile(v8_str(script))->Run();
6145 CHECK_EQ(1, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006146 }
6147
6148 {
6149 // Create new environment reusing the global object.
6150 LocalContext env(NULL, instance_template, global_object);
6151 Local<Value> value = Script::Compile(v8_str("x"))->Run();
6152 CHECK_EQ(42, value->Int32Value());
6153 value = Script::Compile(v8_str("f()"))->Run();
6154 CHECK_EQ(12, value->Int32Value());
Ben Murdochb0fe1622011-05-05 13:52:32 +01006155 value = Script::Compile(v8_str(script))->Run();
6156 CHECK_EQ(1, value->Int32Value());
6157 }
6158}
6159
6160
6161THREADED_TEST(CallKnownGlobalReceiver) {
6162 v8::HandleScope handle_scope;
6163
6164 Local<Value> global_object;
6165
6166 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6167 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6168
6169 // The script to check that we leave global object not
6170 // global object proxy on stack when we deoptimize from inside
6171 // arguments evaluation.
6172 // To provoke error we need to both force deoptimization
6173 // from arguments evaluation and to force CallIC to take
6174 // CallIC_Miss code path that can't cope with global proxy.
6175 const char* script =
6176 "function bar(x, y) { try { } finally { } }"
6177 "function baz(x) { try { } finally { } }"
6178 "function bom(x) { try { } finally { } }"
6179 "function foo(x) { bar([x], bom(2)); }"
6180 "for (var i = 0; i < 10000; i++) foo(1);"
6181 "foo";
6182
6183 Local<Value> foo;
6184 {
6185 LocalContext env(NULL, instance_template);
6186 // Hold on to the global object so it can be used again in another
6187 // environment initialization.
6188 global_object = env->Global();
6189 foo = Script::Compile(v8_str(script))->Run();
6190 }
6191
6192 {
6193 // Create new environment reusing the global object.
6194 LocalContext env(NULL, instance_template, global_object);
6195 env->Global()->Set(v8_str("foo"), foo);
6196 Local<Value> value = Script::Compile(v8_str("foo()"))->Run();
Steve Blocka7e24c12009-10-30 11:49:00 +00006197 }
6198}
6199
6200
6201static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
6202 ApiTestFuzzer::Fuzz();
6203 return v8_num(42);
6204}
6205
6206
6207static int shadow_y;
6208static int shadow_y_setter_call_count;
6209static int shadow_y_getter_call_count;
6210
6211
6212static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
6213 shadow_y_setter_call_count++;
6214 shadow_y = 42;
6215}
6216
6217
6218static v8::Handle<Value> ShadowYGetter(Local<String> name,
6219 const AccessorInfo& info) {
6220 ApiTestFuzzer::Fuzz();
6221 shadow_y_getter_call_count++;
6222 return v8_num(shadow_y);
6223}
6224
6225
6226static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
6227 const AccessorInfo& info) {
6228 return v8::Handle<Value>();
6229}
6230
6231
6232static v8::Handle<Value> ShadowNamedGet(Local<String> key,
6233 const AccessorInfo&) {
6234 return v8::Handle<Value>();
6235}
6236
6237
6238THREADED_TEST(ShadowObject) {
6239 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
6240 v8::HandleScope handle_scope;
6241
6242 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
6243 LocalContext context(NULL, global_template);
6244
6245 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6246 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
6247 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
6248 Local<ObjectTemplate> proto = t->PrototypeTemplate();
6249 Local<ObjectTemplate> instance = t->InstanceTemplate();
6250
6251 // Only allow calls of f on instances of t.
6252 Local<v8::Signature> signature = v8::Signature::New(t);
6253 proto->Set(v8_str("f"),
6254 v8::FunctionTemplate::New(ShadowFunctionCallback,
6255 Local<Value>(),
6256 signature));
6257 proto->Set(v8_str("x"), v8_num(12));
6258
6259 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
6260
6261 Local<Value> o = t->GetFunction()->NewInstance();
6262 context->Global()->Set(v8_str("__proto__"), o);
6263
6264 Local<Value> value =
6265 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
6266 CHECK(value->IsBoolean());
6267 CHECK(!value->BooleanValue());
6268
6269 value = Script::Compile(v8_str("x"))->Run();
6270 CHECK_EQ(12, value->Int32Value());
6271
6272 value = Script::Compile(v8_str("f()"))->Run();
6273 CHECK_EQ(42, value->Int32Value());
6274
6275 Script::Compile(v8_str("y = 42"))->Run();
6276 CHECK_EQ(1, shadow_y_setter_call_count);
6277 value = Script::Compile(v8_str("y"))->Run();
6278 CHECK_EQ(1, shadow_y_getter_call_count);
6279 CHECK_EQ(42, value->Int32Value());
6280}
6281
6282
6283THREADED_TEST(HiddenPrototype) {
6284 v8::HandleScope handle_scope;
6285 LocalContext context;
6286
6287 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6288 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6289 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6290 t1->SetHiddenPrototype(true);
6291 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6292 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6293 t2->SetHiddenPrototype(true);
6294 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6295 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6296 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6297
6298 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6299 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6300 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6301 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6302
6303 // Setting the prototype on an object skips hidden prototypes.
6304 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6305 o0->Set(v8_str("__proto__"), o1);
6306 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6307 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6308 o0->Set(v8_str("__proto__"), o2);
6309 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6310 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6311 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6312 o0->Set(v8_str("__proto__"), o3);
6313 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6314 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6315 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6316 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6317
6318 // Getting the prototype of o0 should get the first visible one
6319 // which is o3. Therefore, z should not be defined on the prototype
6320 // object.
6321 Local<Value> proto = o0->Get(v8_str("__proto__"));
6322 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006323 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00006324}
6325
6326
Andrei Popescu402d9372010-02-26 13:31:12 +00006327THREADED_TEST(SetPrototype) {
6328 v8::HandleScope handle_scope;
6329 LocalContext context;
6330
6331 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
6332 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
6333 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
6334 t1->SetHiddenPrototype(true);
6335 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
6336 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
6337 t2->SetHiddenPrototype(true);
6338 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
6339 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
6340 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
6341
6342 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
6343 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
6344 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
6345 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
6346
6347 // Setting the prototype on an object does not skip hidden prototypes.
6348 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6349 CHECK(o0->SetPrototype(o1));
6350 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6351 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6352 CHECK(o1->SetPrototype(o2));
6353 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6354 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6355 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6356 CHECK(o2->SetPrototype(o3));
6357 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
6358 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
6359 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
6360 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
6361
6362 // Getting the prototype of o0 should get the first visible one
6363 // which is o3. Therefore, z should not be defined on the prototype
6364 // object.
6365 Local<Value> proto = o0->Get(v8_str("__proto__"));
6366 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006367 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006368
6369 // However, Object::GetPrototype ignores hidden prototype.
6370 Local<Value> proto0 = o0->GetPrototype();
6371 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006372 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00006373
6374 Local<Value> proto1 = o1->GetPrototype();
6375 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006376 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00006377
6378 Local<Value> proto2 = o2->GetPrototype();
6379 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01006380 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00006381}
6382
6383
6384THREADED_TEST(SetPrototypeThrows) {
6385 v8::HandleScope handle_scope;
6386 LocalContext context;
6387
6388 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6389
6390 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
6391 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
6392
6393 CHECK(o0->SetPrototype(o1));
6394 // If setting the prototype leads to the cycle, SetPrototype should
6395 // return false and keep VM in sane state.
6396 v8::TryCatch try_catch;
6397 CHECK(!o1->SetPrototype(o0));
6398 CHECK(!try_catch.HasCaught());
6399 ASSERT(!i::Top::has_pending_exception());
6400
6401 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
6402}
6403
6404
Steve Blocka7e24c12009-10-30 11:49:00 +00006405THREADED_TEST(GetterSetterExceptions) {
6406 v8::HandleScope handle_scope;
6407 LocalContext context;
6408 CompileRun(
6409 "function Foo() { };"
6410 "function Throw() { throw 5; };"
6411 "var x = { };"
6412 "x.__defineSetter__('set', Throw);"
6413 "x.__defineGetter__('get', Throw);");
6414 Local<v8::Object> x =
6415 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
6416 v8::TryCatch try_catch;
6417 x->Set(v8_str("set"), v8::Integer::New(8));
6418 x->Get(v8_str("get"));
6419 x->Set(v8_str("set"), v8::Integer::New(8));
6420 x->Get(v8_str("get"));
6421 x->Set(v8_str("set"), v8::Integer::New(8));
6422 x->Get(v8_str("get"));
6423 x->Set(v8_str("set"), v8::Integer::New(8));
6424 x->Get(v8_str("get"));
6425}
6426
6427
6428THREADED_TEST(Constructor) {
6429 v8::HandleScope handle_scope;
6430 LocalContext context;
6431 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6432 templ->SetClassName(v8_str("Fun"));
6433 Local<Function> cons = templ->GetFunction();
6434 context->Global()->Set(v8_str("Fun"), cons);
6435 Local<v8::Object> inst = cons->NewInstance();
6436 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
6437 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
6438 CHECK(value->BooleanValue());
6439}
6440
6441THREADED_TEST(FunctionDescriptorException) {
6442 v8::HandleScope handle_scope;
6443 LocalContext context;
6444 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
6445 templ->SetClassName(v8_str("Fun"));
6446 Local<Function> cons = templ->GetFunction();
6447 context->Global()->Set(v8_str("Fun"), cons);
6448 Local<Value> value = CompileRun(
6449 "function test() {"
6450 " try {"
6451 " (new Fun()).blah()"
6452 " } catch (e) {"
6453 " var str = String(e);"
6454 " if (str.indexOf('TypeError') == -1) return 1;"
6455 " if (str.indexOf('[object Fun]') != -1) return 2;"
Steve Block1e0659c2011-05-24 12:43:12 +01006456 " if (str.indexOf('#<Fun>') == -1) return 3;"
Steve Blocka7e24c12009-10-30 11:49:00 +00006457 " return 0;"
6458 " }"
6459 " return 4;"
6460 "}"
6461 "test();");
6462 CHECK_EQ(0, value->Int32Value());
6463}
6464
6465
6466THREADED_TEST(EvalAliasedDynamic) {
6467 v8::HandleScope scope;
6468 LocalContext current;
6469
6470 // Tests where aliased eval can only be resolved dynamically.
6471 Local<Script> script =
6472 Script::Compile(v8_str("function f(x) { "
6473 " var foo = 2;"
6474 " with (x) { return eval('foo'); }"
6475 "}"
6476 "foo = 0;"
6477 "result1 = f(new Object());"
6478 "result2 = f(this);"
6479 "var x = new Object();"
6480 "x.eval = function(x) { return 1; };"
6481 "result3 = f(x);"));
6482 script->Run();
6483 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
6484 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
6485 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
6486
6487 v8::TryCatch try_catch;
6488 script =
6489 Script::Compile(v8_str("function f(x) { "
6490 " var bar = 2;"
6491 " with (x) { return eval('bar'); }"
6492 "}"
6493 "f(this)"));
6494 script->Run();
6495 CHECK(try_catch.HasCaught());
6496 try_catch.Reset();
6497}
6498
6499
6500THREADED_TEST(CrossEval) {
6501 v8::HandleScope scope;
6502 LocalContext other;
6503 LocalContext current;
6504
6505 Local<String> token = v8_str("<security token>");
6506 other->SetSecurityToken(token);
6507 current->SetSecurityToken(token);
6508
6509 // Setup reference from current to other.
6510 current->Global()->Set(v8_str("other"), other->Global());
6511
6512 // Check that new variables are introduced in other context.
6513 Local<Script> script =
6514 Script::Compile(v8_str("other.eval('var foo = 1234')"));
6515 script->Run();
6516 Local<Value> foo = other->Global()->Get(v8_str("foo"));
6517 CHECK_EQ(1234, foo->Int32Value());
6518 CHECK(!current->Global()->Has(v8_str("foo")));
6519
6520 // Check that writing to non-existing properties introduces them in
6521 // the other context.
6522 script =
6523 Script::Compile(v8_str("other.eval('na = 1234')"));
6524 script->Run();
6525 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
6526 CHECK(!current->Global()->Has(v8_str("na")));
6527
6528 // Check that global variables in current context are not visible in other
6529 // context.
6530 v8::TryCatch try_catch;
6531 script =
6532 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
6533 Local<Value> result = script->Run();
6534 CHECK(try_catch.HasCaught());
6535 try_catch.Reset();
6536
6537 // Check that local variables in current context are not visible in other
6538 // context.
6539 script =
6540 Script::Compile(v8_str("(function() { "
6541 " var baz = 87;"
6542 " return other.eval('baz');"
6543 "})();"));
6544 result = script->Run();
6545 CHECK(try_catch.HasCaught());
6546 try_catch.Reset();
6547
6548 // Check that global variables in the other environment are visible
6549 // when evaluting code.
6550 other->Global()->Set(v8_str("bis"), v8_num(1234));
6551 script = Script::Compile(v8_str("other.eval('bis')"));
6552 CHECK_EQ(1234, script->Run()->Int32Value());
6553 CHECK(!try_catch.HasCaught());
6554
6555 // Check that the 'this' pointer points to the global object evaluating
6556 // code.
6557 other->Global()->Set(v8_str("t"), other->Global());
6558 script = Script::Compile(v8_str("other.eval('this == t')"));
6559 result = script->Run();
6560 CHECK(result->IsTrue());
6561 CHECK(!try_catch.HasCaught());
6562
6563 // Check that variables introduced in with-statement are not visible in
6564 // other context.
6565 script =
6566 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
6567 result = script->Run();
6568 CHECK(try_catch.HasCaught());
6569 try_catch.Reset();
6570
6571 // Check that you cannot use 'eval.call' with another object than the
6572 // current global object.
6573 script =
6574 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
6575 result = script->Run();
6576 CHECK(try_catch.HasCaught());
6577}
6578
6579
6580// Test that calling eval in a context which has been detached from
6581// its global throws an exception. This behavior is consistent with
6582// other JavaScript implementations.
6583THREADED_TEST(EvalInDetachedGlobal) {
6584 v8::HandleScope scope;
6585
6586 v8::Persistent<Context> context0 = Context::New();
6587 v8::Persistent<Context> context1 = Context::New();
6588
6589 // Setup function in context0 that uses eval from context0.
6590 context0->Enter();
6591 v8::Handle<v8::Value> fun =
6592 CompileRun("var x = 42;"
6593 "(function() {"
6594 " var e = eval;"
6595 " return function(s) { return e(s); }"
6596 "})()");
6597 context0->Exit();
6598
6599 // Put the function into context1 and call it before and after
6600 // detaching the global. Before detaching, the call succeeds and
6601 // after detaching and exception is thrown.
6602 context1->Enter();
6603 context1->Global()->Set(v8_str("fun"), fun);
6604 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
6605 CHECK_EQ(42, x_value->Int32Value());
6606 context0->DetachGlobal();
6607 v8::TryCatch catcher;
6608 x_value = CompileRun("fun('x')");
6609 CHECK(x_value.IsEmpty());
6610 CHECK(catcher.HasCaught());
6611 context1->Exit();
6612
6613 context1.Dispose();
6614 context0.Dispose();
6615}
6616
6617
6618THREADED_TEST(CrossLazyLoad) {
6619 v8::HandleScope scope;
6620 LocalContext other;
6621 LocalContext current;
6622
6623 Local<String> token = v8_str("<security token>");
6624 other->SetSecurityToken(token);
6625 current->SetSecurityToken(token);
6626
6627 // Setup reference from current to other.
6628 current->Global()->Set(v8_str("other"), other->Global());
6629
6630 // Trigger lazy loading in other context.
6631 Local<Script> script =
6632 Script::Compile(v8_str("other.eval('new Date(42)')"));
6633 Local<Value> value = script->Run();
6634 CHECK_EQ(42.0, value->NumberValue());
6635}
6636
6637
6638static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006639 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006640 if (args.IsConstructCall()) {
6641 if (args[0]->IsInt32()) {
6642 return v8_num(-args[0]->Int32Value());
6643 }
6644 }
6645
6646 return args[0];
6647}
6648
6649
6650// Test that a call handler can be set for objects which will allow
6651// non-function objects created through the API to be called as
6652// functions.
6653THREADED_TEST(CallAsFunction) {
6654 v8::HandleScope scope;
6655 LocalContext context;
6656
6657 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6658 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6659 instance_template->SetCallAsFunctionHandler(call_as_function);
6660 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6661 context->Global()->Set(v8_str("obj"), instance);
6662 v8::TryCatch try_catch;
6663 Local<Value> value;
6664 CHECK(!try_catch.HasCaught());
6665
6666 value = CompileRun("obj(42)");
6667 CHECK(!try_catch.HasCaught());
6668 CHECK_EQ(42, value->Int32Value());
6669
6670 value = CompileRun("(function(o){return o(49)})(obj)");
6671 CHECK(!try_catch.HasCaught());
6672 CHECK_EQ(49, value->Int32Value());
6673
6674 // test special case of call as function
6675 value = CompileRun("[obj]['0'](45)");
6676 CHECK(!try_catch.HasCaught());
6677 CHECK_EQ(45, value->Int32Value());
6678
6679 value = CompileRun("obj.call = Function.prototype.call;"
6680 "obj.call(null, 87)");
6681 CHECK(!try_catch.HasCaught());
6682 CHECK_EQ(87, value->Int32Value());
6683
6684 // Regression tests for bug #1116356: Calling call through call/apply
6685 // must work for non-function receivers.
6686 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6687 value = CompileRun(apply_99);
6688 CHECK(!try_catch.HasCaught());
6689 CHECK_EQ(99, value->Int32Value());
6690
6691 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6692 value = CompileRun(call_17);
6693 CHECK(!try_catch.HasCaught());
6694 CHECK_EQ(17, value->Int32Value());
6695
6696 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006697 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006698 value = CompileRun("new obj(43)");
6699 CHECK(!try_catch.HasCaught());
6700 CHECK_EQ(-43, value->Int32Value());
6701}
6702
6703
6704static int CountHandles() {
6705 return v8::HandleScope::NumberOfHandles();
6706}
6707
6708
6709static int Recurse(int depth, int iterations) {
6710 v8::HandleScope scope;
6711 if (depth == 0) return CountHandles();
6712 for (int i = 0; i < iterations; i++) {
6713 Local<v8::Number> n = v8::Integer::New(42);
6714 }
6715 return Recurse(depth - 1, iterations);
6716}
6717
6718
6719THREADED_TEST(HandleIteration) {
6720 static const int kIterations = 500;
6721 static const int kNesting = 200;
6722 CHECK_EQ(0, CountHandles());
6723 {
6724 v8::HandleScope scope1;
6725 CHECK_EQ(0, CountHandles());
6726 for (int i = 0; i < kIterations; i++) {
6727 Local<v8::Number> n = v8::Integer::New(42);
6728 CHECK_EQ(i + 1, CountHandles());
6729 }
6730
6731 CHECK_EQ(kIterations, CountHandles());
6732 {
6733 v8::HandleScope scope2;
6734 for (int j = 0; j < kIterations; j++) {
6735 Local<v8::Number> n = v8::Integer::New(42);
6736 CHECK_EQ(j + 1 + kIterations, CountHandles());
6737 }
6738 }
6739 CHECK_EQ(kIterations, CountHandles());
6740 }
6741 CHECK_EQ(0, CountHandles());
6742 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6743}
6744
6745
6746static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6747 Local<String> name,
6748 const AccessorInfo& info) {
6749 ApiTestFuzzer::Fuzz();
6750 return v8::Handle<Value>();
6751}
6752
6753
6754THREADED_TEST(InterceptorHasOwnProperty) {
6755 v8::HandleScope scope;
6756 LocalContext context;
6757 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6758 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6759 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6760 Local<Function> function = fun_templ->GetFunction();
6761 context->Global()->Set(v8_str("constructor"), function);
6762 v8::Handle<Value> value = CompileRun(
6763 "var o = new constructor();"
6764 "o.hasOwnProperty('ostehaps');");
6765 CHECK_EQ(false, value->BooleanValue());
6766 value = CompileRun(
6767 "o.ostehaps = 42;"
6768 "o.hasOwnProperty('ostehaps');");
6769 CHECK_EQ(true, value->BooleanValue());
6770 value = CompileRun(
6771 "var p = new constructor();"
6772 "p.hasOwnProperty('ostehaps');");
6773 CHECK_EQ(false, value->BooleanValue());
6774}
6775
6776
6777static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6778 Local<String> name,
6779 const AccessorInfo& info) {
6780 ApiTestFuzzer::Fuzz();
6781 i::Heap::CollectAllGarbage(false);
6782 return v8::Handle<Value>();
6783}
6784
6785
6786THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6787 v8::HandleScope scope;
6788 LocalContext context;
6789 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6790 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6791 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6792 Local<Function> function = fun_templ->GetFunction();
6793 context->Global()->Set(v8_str("constructor"), function);
6794 // Let's first make some stuff so we can be sure to get a good GC.
6795 CompileRun(
6796 "function makestr(size) {"
6797 " switch (size) {"
6798 " case 1: return 'f';"
6799 " case 2: return 'fo';"
6800 " case 3: return 'foo';"
6801 " }"
6802 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6803 "}"
6804 "var x = makestr(12345);"
6805 "x = makestr(31415);"
6806 "x = makestr(23456);");
6807 v8::Handle<Value> value = CompileRun(
6808 "var o = new constructor();"
6809 "o.__proto__ = new String(x);"
6810 "o.hasOwnProperty('ostehaps');");
6811 CHECK_EQ(false, value->BooleanValue());
6812}
6813
6814
6815typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6816 const AccessorInfo& info);
6817
6818
6819static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6820 const char* source,
6821 int expected) {
6822 v8::HandleScope scope;
6823 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08006824 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00006825 LocalContext context;
6826 context->Global()->Set(v8_str("o"), templ->NewInstance());
6827 v8::Handle<Value> value = CompileRun(source);
6828 CHECK_EQ(expected, value->Int32Value());
6829}
6830
6831
6832static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6833 const AccessorInfo& info) {
6834 ApiTestFuzzer::Fuzz();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08006835 CHECK_EQ(v8_str("data"), info.Data());
6836 CHECK_EQ(v8_str("x"), name);
Steve Blocka7e24c12009-10-30 11:49:00 +00006837 return v8::Integer::New(42);
6838}
6839
6840
6841// This test should hit the load IC for the interceptor case.
6842THREADED_TEST(InterceptorLoadIC) {
6843 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6844 "var result = 0;"
6845 "for (var i = 0; i < 1000; i++) {"
6846 " result = o.x;"
6847 "}",
6848 42);
6849}
6850
6851
6852// Below go several tests which verify that JITing for various
6853// configurations of interceptor and explicit fields works fine
6854// (those cases are special cased to get better performance).
6855
6856static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6857 const AccessorInfo& info) {
6858 ApiTestFuzzer::Fuzz();
6859 return v8_str("x")->Equals(name)
6860 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6861}
6862
6863
6864THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6865 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6866 "var result = 0;"
6867 "o.y = 239;"
6868 "for (var i = 0; i < 1000; i++) {"
6869 " result = o.y;"
6870 "}",
6871 239);
6872}
6873
6874
6875THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6876 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6877 "var result = 0;"
6878 "o.__proto__ = { 'y': 239 };"
6879 "for (var i = 0; i < 1000; i++) {"
6880 " result = o.y + o.x;"
6881 "}",
6882 239 + 42);
6883}
6884
6885
6886THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6887 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6888 "var result = 0;"
6889 "o.__proto__.y = 239;"
6890 "for (var i = 0; i < 1000; i++) {"
6891 " result = o.y + o.x;"
6892 "}",
6893 239 + 42);
6894}
6895
6896
6897THREADED_TEST(InterceptorLoadICUndefined) {
6898 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6899 "var result = 0;"
6900 "for (var i = 0; i < 1000; i++) {"
6901 " result = (o.y == undefined) ? 239 : 42;"
6902 "}",
6903 239);
6904}
6905
6906
6907THREADED_TEST(InterceptorLoadICWithOverride) {
6908 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6909 "fst = new Object(); fst.__proto__ = o;"
6910 "snd = new Object(); snd.__proto__ = fst;"
6911 "var result1 = 0;"
6912 "for (var i = 0; i < 1000; i++) {"
6913 " result1 = snd.x;"
6914 "}"
6915 "fst.x = 239;"
6916 "var result = 0;"
6917 "for (var i = 0; i < 1000; i++) {"
6918 " result = snd.x;"
6919 "}"
6920 "result + result1",
6921 239 + 42);
6922}
6923
6924
6925// Test the case when we stored field into
6926// a stub, but interceptor produced value on its own.
6927THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6928 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6929 "proto = new Object();"
6930 "o.__proto__ = proto;"
6931 "proto.x = 239;"
6932 "for (var i = 0; i < 1000; i++) {"
6933 " o.x;"
6934 // Now it should be ICed and keep a reference to x defined on proto
6935 "}"
6936 "var result = 0;"
6937 "for (var i = 0; i < 1000; i++) {"
6938 " result += o.x;"
6939 "}"
6940 "result;",
6941 42 * 1000);
6942}
6943
6944
6945// Test the case when we stored field into
6946// a stub, but it got invalidated later on.
6947THREADED_TEST(InterceptorLoadICInvalidatedField) {
6948 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6949 "proto1 = new Object();"
6950 "proto2 = new Object();"
6951 "o.__proto__ = proto1;"
6952 "proto1.__proto__ = proto2;"
6953 "proto2.y = 239;"
6954 "for (var i = 0; i < 1000; i++) {"
6955 " o.y;"
6956 // Now it should be ICed and keep a reference to y defined on proto2
6957 "}"
6958 "proto1.y = 42;"
6959 "var result = 0;"
6960 "for (var i = 0; i < 1000; i++) {"
6961 " result += o.y;"
6962 "}"
6963 "result;",
6964 42 * 1000);
6965}
6966
6967
Steve Block6ded16b2010-05-10 14:33:55 +01006968static int interceptor_load_not_handled_calls = 0;
6969static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6970 const AccessorInfo& info) {
6971 ++interceptor_load_not_handled_calls;
6972 return v8::Handle<v8::Value>();
6973}
6974
6975
6976// Test how post-interceptor lookups are done in the non-cacheable
6977// case: the interceptor should not be invoked during this lookup.
6978THREADED_TEST(InterceptorLoadICPostInterceptor) {
6979 interceptor_load_not_handled_calls = 0;
6980 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6981 "receiver = new Object();"
6982 "receiver.__proto__ = o;"
6983 "proto = new Object();"
6984 "/* Make proto a slow-case object. */"
6985 "for (var i = 0; i < 1000; i++) {"
6986 " proto[\"xxxxxxxx\" + i] = [];"
6987 "}"
6988 "proto.x = 17;"
6989 "o.__proto__ = proto;"
6990 "var result = 0;"
6991 "for (var i = 0; i < 1000; i++) {"
6992 " result += receiver.x;"
6993 "}"
6994 "result;",
6995 17 * 1000);
6996 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6997}
6998
6999
Steve Blocka7e24c12009-10-30 11:49:00 +00007000// Test the case when we stored field into
7001// a stub, but it got invalidated later on due to override on
7002// global object which is between interceptor and fields' holders.
7003THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
7004 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
7005 "o.__proto__ = this;" // set a global to be a proto of o.
7006 "this.__proto__.y = 239;"
7007 "for (var i = 0; i < 10; i++) {"
7008 " if (o.y != 239) throw 'oops: ' + o.y;"
7009 // Now it should be ICed and keep a reference to y defined on field_holder.
7010 "}"
7011 "this.y = 42;" // Assign on a global.
7012 "var result = 0;"
7013 "for (var i = 0; i < 10; i++) {"
7014 " result += o.y;"
7015 "}"
7016 "result;",
7017 42 * 10);
7018}
7019
7020
Steve Blocka7e24c12009-10-30 11:49:00 +00007021static void SetOnThis(Local<String> name,
7022 Local<Value> value,
7023 const AccessorInfo& info) {
7024 info.This()->ForceSet(name, value);
7025}
7026
7027
7028THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
7029 v8::HandleScope scope;
7030 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7031 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7032 templ->SetAccessor(v8_str("y"), Return239);
7033 LocalContext context;
7034 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007035
7036 // Check the case when receiver and interceptor's holder
7037 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007038 v8::Handle<Value> value = CompileRun(
7039 "var result = 0;"
7040 "for (var i = 0; i < 7; i++) {"
7041 " result = o.y;"
7042 "}");
7043 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007044
7045 // Check the case when interceptor's holder is in proto chain
7046 // of receiver.
7047 value = CompileRun(
7048 "r = { __proto__: o };"
7049 "var result = 0;"
7050 "for (var i = 0; i < 7; i++) {"
7051 " result = r.y;"
7052 "}");
7053 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007054}
7055
7056
7057THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
7058 v8::HandleScope scope;
7059 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7060 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7061 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7062 templ_p->SetAccessor(v8_str("y"), Return239);
7063
7064 LocalContext context;
7065 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7066 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7067
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007068 // Check the case when receiver and interceptor's holder
7069 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00007070 v8::Handle<Value> value = CompileRun(
7071 "o.__proto__ = p;"
7072 "var result = 0;"
7073 "for (var i = 0; i < 7; i++) {"
7074 " result = o.x + o.y;"
7075 "}");
7076 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007077
7078 // Check the case when interceptor's holder is in proto chain
7079 // of receiver.
7080 value = CompileRun(
7081 "r = { __proto__: o };"
7082 "var result = 0;"
7083 "for (var i = 0; i < 7; i++) {"
7084 " result = r.x + r.y;"
7085 "}");
7086 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00007087}
7088
7089
7090THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
7091 v8::HandleScope scope;
7092 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7093 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7094 templ->SetAccessor(v8_str("y"), Return239);
7095
7096 LocalContext context;
7097 context->Global()->Set(v8_str("o"), templ->NewInstance());
7098
7099 v8::Handle<Value> value = CompileRun(
7100 "fst = new Object(); fst.__proto__ = o;"
7101 "snd = new Object(); snd.__proto__ = fst;"
7102 "var result1 = 0;"
7103 "for (var i = 0; i < 7; i++) {"
7104 " result1 = snd.x;"
7105 "}"
7106 "fst.x = 239;"
7107 "var result = 0;"
7108 "for (var i = 0; i < 7; i++) {"
7109 " result = snd.x;"
7110 "}"
7111 "result + result1");
7112 CHECK_EQ(239 + 42, value->Int32Value());
7113}
7114
7115
7116// Test the case when we stored callback into
7117// a stub, but interceptor produced value on its own.
7118THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
7119 v8::HandleScope scope;
7120 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7121 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7122 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7123 templ_p->SetAccessor(v8_str("y"), Return239);
7124
7125 LocalContext context;
7126 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7127 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7128
7129 v8::Handle<Value> value = CompileRun(
7130 "o.__proto__ = p;"
7131 "for (var i = 0; i < 7; i++) {"
7132 " o.x;"
7133 // Now it should be ICed and keep a reference to x defined on p
7134 "}"
7135 "var result = 0;"
7136 "for (var i = 0; i < 7; i++) {"
7137 " result += o.x;"
7138 "}"
7139 "result");
7140 CHECK_EQ(42 * 7, value->Int32Value());
7141}
7142
7143
7144// Test the case when we stored callback into
7145// a stub, but it got invalidated later on.
7146THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
7147 v8::HandleScope scope;
7148 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7149 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7150 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7151 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7152
7153 LocalContext context;
7154 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7155 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7156
7157 v8::Handle<Value> value = CompileRun(
7158 "inbetween = new Object();"
7159 "o.__proto__ = inbetween;"
7160 "inbetween.__proto__ = p;"
7161 "for (var i = 0; i < 10; i++) {"
7162 " o.y;"
7163 // Now it should be ICed and keep a reference to y defined on p
7164 "}"
7165 "inbetween.y = 42;"
7166 "var result = 0;"
7167 "for (var i = 0; i < 10; i++) {"
7168 " result += o.y;"
7169 "}"
7170 "result");
7171 CHECK_EQ(42 * 10, value->Int32Value());
7172}
7173
7174
7175// Test the case when we stored callback into
7176// a stub, but it got invalidated later on due to override on
7177// global object which is between interceptor and callbacks' holders.
7178THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
7179 v8::HandleScope scope;
7180 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7181 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7182 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
7183 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
7184
7185 LocalContext context;
7186 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7187 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
7188
7189 v8::Handle<Value> value = CompileRun(
7190 "o.__proto__ = this;"
7191 "this.__proto__ = p;"
7192 "for (var i = 0; i < 10; i++) {"
7193 " if (o.y != 239) throw 'oops: ' + o.y;"
7194 // Now it should be ICed and keep a reference to y defined on p
7195 "}"
7196 "this.y = 42;"
7197 "var result = 0;"
7198 "for (var i = 0; i < 10; i++) {"
7199 " result += o.y;"
7200 "}"
7201 "result");
7202 CHECK_EQ(42 * 10, value->Int32Value());
7203}
7204
7205
7206static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
7207 const AccessorInfo& info) {
7208 ApiTestFuzzer::Fuzz();
7209 CHECK(v8_str("x")->Equals(name));
7210 return v8::Integer::New(0);
7211}
7212
7213
7214THREADED_TEST(InterceptorReturningZero) {
7215 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
7216 "o.x == undefined ? 1 : 0",
7217 0);
7218}
7219
7220
7221static v8::Handle<Value> InterceptorStoreICSetter(
7222 Local<String> key, Local<Value> value, const AccessorInfo&) {
7223 CHECK(v8_str("x")->Equals(key));
7224 CHECK_EQ(42, value->Int32Value());
7225 return value;
7226}
7227
7228
7229// This test should hit the store IC for the interceptor case.
7230THREADED_TEST(InterceptorStoreIC) {
7231 v8::HandleScope scope;
7232 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7233 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007234 InterceptorStoreICSetter,
7235 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00007236 LocalContext context;
7237 context->Global()->Set(v8_str("o"), templ->NewInstance());
7238 v8::Handle<Value> value = CompileRun(
7239 "for (var i = 0; i < 1000; i++) {"
7240 " o.x = 42;"
7241 "}");
7242}
7243
7244
7245THREADED_TEST(InterceptorStoreICWithNoSetter) {
7246 v8::HandleScope scope;
7247 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7248 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7249 LocalContext context;
7250 context->Global()->Set(v8_str("o"), templ->NewInstance());
7251 v8::Handle<Value> value = CompileRun(
7252 "for (var i = 0; i < 1000; i++) {"
7253 " o.y = 239;"
7254 "}"
7255 "42 + o.y");
7256 CHECK_EQ(239 + 42, value->Int32Value());
7257}
7258
7259
7260
7261
7262v8::Handle<Value> call_ic_function;
7263v8::Handle<Value> call_ic_function2;
7264v8::Handle<Value> call_ic_function3;
7265
7266static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
7267 const AccessorInfo& info) {
7268 ApiTestFuzzer::Fuzz();
7269 CHECK(v8_str("x")->Equals(name));
7270 return call_ic_function;
7271}
7272
7273
7274// This test should hit the call IC for the interceptor case.
7275THREADED_TEST(InterceptorCallIC) {
7276 v8::HandleScope scope;
7277 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7278 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
7279 LocalContext context;
7280 context->Global()->Set(v8_str("o"), templ->NewInstance());
7281 call_ic_function =
7282 v8_compile("function f(x) { return x + 1; }; f")->Run();
7283 v8::Handle<Value> value = CompileRun(
7284 "var result = 0;"
7285 "for (var i = 0; i < 1000; i++) {"
7286 " result = o.x(41);"
7287 "}");
7288 CHECK_EQ(42, value->Int32Value());
7289}
7290
7291
7292// This test checks that if interceptor doesn't provide
7293// a value, we can fetch regular value.
7294THREADED_TEST(InterceptorCallICSeesOthers) {
7295 v8::HandleScope scope;
7296 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7297 templ->SetNamedPropertyHandler(NoBlockGetterX);
7298 LocalContext context;
7299 context->Global()->Set(v8_str("o"), templ->NewInstance());
7300 v8::Handle<Value> value = CompileRun(
7301 "o.x = function f(x) { return x + 1; };"
7302 "var result = 0;"
7303 "for (var i = 0; i < 7; i++) {"
7304 " result = o.x(41);"
7305 "}");
7306 CHECK_EQ(42, value->Int32Value());
7307}
7308
7309
7310static v8::Handle<Value> call_ic_function4;
7311static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
7312 const AccessorInfo& info) {
7313 ApiTestFuzzer::Fuzz();
7314 CHECK(v8_str("x")->Equals(name));
7315 return call_ic_function4;
7316}
7317
7318
7319// This test checks that if interceptor provides a function,
7320// even if we cached shadowed variant, interceptor's function
7321// is invoked
7322THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
7323 v8::HandleScope scope;
7324 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7325 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
7326 LocalContext context;
7327 context->Global()->Set(v8_str("o"), templ->NewInstance());
7328 call_ic_function4 =
7329 v8_compile("function f(x) { return x - 1; }; f")->Run();
7330 v8::Handle<Value> value = CompileRun(
7331 "o.__proto__.x = function(x) { return x + 1; };"
7332 "var result = 0;"
7333 "for (var i = 0; i < 1000; i++) {"
7334 " result = o.x(42);"
7335 "}");
7336 CHECK_EQ(41, value->Int32Value());
7337}
7338
7339
7340// Test the case when we stored cacheable lookup into
7341// a stub, but it got invalidated later on
7342THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
7343 v8::HandleScope scope;
7344 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7345 templ->SetNamedPropertyHandler(NoBlockGetterX);
7346 LocalContext context;
7347 context->Global()->Set(v8_str("o"), templ->NewInstance());
7348 v8::Handle<Value> value = CompileRun(
7349 "proto1 = new Object();"
7350 "proto2 = new Object();"
7351 "o.__proto__ = proto1;"
7352 "proto1.__proto__ = proto2;"
7353 "proto2.y = function(x) { return x + 1; };"
7354 // Invoke it many times to compile a stub
7355 "for (var i = 0; i < 7; i++) {"
7356 " o.y(42);"
7357 "}"
7358 "proto1.y = function(x) { return x - 1; };"
7359 "var result = 0;"
7360 "for (var i = 0; i < 7; i++) {"
7361 " result += o.y(42);"
7362 "}");
7363 CHECK_EQ(41 * 7, value->Int32Value());
7364}
7365
7366
7367static v8::Handle<Value> call_ic_function5;
7368static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
7369 const AccessorInfo& info) {
7370 ApiTestFuzzer::Fuzz();
7371 if (v8_str("x")->Equals(name))
7372 return call_ic_function5;
7373 else
7374 return Local<Value>();
7375}
7376
7377
7378// This test checks that if interceptor doesn't provide a function,
7379// cached constant function is used
7380THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
7381 v8::HandleScope scope;
7382 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7383 templ->SetNamedPropertyHandler(NoBlockGetterX);
7384 LocalContext context;
7385 context->Global()->Set(v8_str("o"), templ->NewInstance());
7386 v8::Handle<Value> value = CompileRun(
7387 "function inc(x) { return x + 1; };"
7388 "inc(1);"
7389 "o.x = inc;"
7390 "var result = 0;"
7391 "for (var i = 0; i < 1000; i++) {"
7392 " result = o.x(42);"
7393 "}");
7394 CHECK_EQ(43, value->Int32Value());
7395}
7396
7397
7398// This test checks that if interceptor provides a function,
7399// even if we cached constant function, interceptor's function
7400// is invoked
7401THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
7402 v8::HandleScope scope;
7403 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7404 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
7405 LocalContext context;
7406 context->Global()->Set(v8_str("o"), templ->NewInstance());
7407 call_ic_function5 =
7408 v8_compile("function f(x) { return x - 1; }; f")->Run();
7409 v8::Handle<Value> value = CompileRun(
7410 "function inc(x) { return x + 1; };"
7411 "inc(1);"
7412 "o.x = inc;"
7413 "var result = 0;"
7414 "for (var i = 0; i < 1000; i++) {"
7415 " result = o.x(42);"
7416 "}");
7417 CHECK_EQ(41, value->Int32Value());
7418}
7419
7420
7421// Test the case when we stored constant function into
7422// a stub, but it got invalidated later on
7423THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
7424 v8::HandleScope scope;
7425 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7426 templ->SetNamedPropertyHandler(NoBlockGetterX);
7427 LocalContext context;
7428 context->Global()->Set(v8_str("o"), templ->NewInstance());
7429 v8::Handle<Value> value = CompileRun(
7430 "function inc(x) { return x + 1; };"
7431 "inc(1);"
7432 "proto1 = new Object();"
7433 "proto2 = new Object();"
7434 "o.__proto__ = proto1;"
7435 "proto1.__proto__ = proto2;"
7436 "proto2.y = inc;"
7437 // Invoke it many times to compile a stub
7438 "for (var i = 0; i < 7; i++) {"
7439 " o.y(42);"
7440 "}"
7441 "proto1.y = function(x) { return x - 1; };"
7442 "var result = 0;"
7443 "for (var i = 0; i < 7; i++) {"
7444 " result += o.y(42);"
7445 "}");
7446 CHECK_EQ(41 * 7, value->Int32Value());
7447}
7448
7449
7450// Test the case when we stored constant function into
7451// a stub, but it got invalidated later on due to override on
7452// global object which is between interceptor and constant function' holders.
7453THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
7454 v8::HandleScope scope;
7455 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7456 templ->SetNamedPropertyHandler(NoBlockGetterX);
7457 LocalContext context;
7458 context->Global()->Set(v8_str("o"), templ->NewInstance());
7459 v8::Handle<Value> value = CompileRun(
7460 "function inc(x) { return x + 1; };"
7461 "inc(1);"
7462 "o.__proto__ = this;"
7463 "this.__proto__.y = inc;"
7464 // Invoke it many times to compile a stub
7465 "for (var i = 0; i < 7; i++) {"
7466 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
7467 "}"
7468 "this.y = function(x) { return x - 1; };"
7469 "var result = 0;"
7470 "for (var i = 0; i < 7; i++) {"
7471 " result += o.y(42);"
7472 "}");
7473 CHECK_EQ(41 * 7, value->Int32Value());
7474}
7475
7476
Leon Clarke4515c472010-02-03 11:58:03 +00007477// Test the case when actual function to call sits on global object.
7478THREADED_TEST(InterceptorCallICCachedFromGlobal) {
7479 v8::HandleScope scope;
7480 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7481 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7482
7483 LocalContext context;
7484 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7485
7486 v8::Handle<Value> value = CompileRun(
7487 "try {"
7488 " o.__proto__ = this;"
7489 " for (var i = 0; i < 10; i++) {"
7490 " var v = o.parseFloat('239');"
7491 " if (v != 239) throw v;"
7492 // Now it should be ICed and keep a reference to parseFloat.
7493 " }"
7494 " var result = 0;"
7495 " for (var i = 0; i < 10; i++) {"
7496 " result += o.parseFloat('239');"
7497 " }"
7498 " result"
7499 "} catch(e) {"
7500 " e"
7501 "};");
7502 CHECK_EQ(239 * 10, value->Int32Value());
7503}
7504
Andrei Popescu402d9372010-02-26 13:31:12 +00007505static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
7506 const AccessorInfo& info) {
7507 ApiTestFuzzer::Fuzz();
7508 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
7509 ++(*call_count);
7510 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01007511 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00007512 }
7513 return v8::Handle<Value>();
7514}
7515
7516static v8::Handle<Value> FastApiCallback_TrivialSignature(
7517 const v8::Arguments& args) {
7518 ApiTestFuzzer::Fuzz();
7519 CHECK_EQ(args.This(), args.Holder());
7520 CHECK(args.Data()->Equals(v8_str("method_data")));
7521 return v8::Integer::New(args[0]->Int32Value() + 1);
7522}
7523
7524static v8::Handle<Value> FastApiCallback_SimpleSignature(
7525 const v8::Arguments& args) {
7526 ApiTestFuzzer::Fuzz();
7527 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
7528 CHECK(args.Data()->Equals(v8_str("method_data")));
7529 // Note, we're using HasRealNamedProperty instead of Has to avoid
7530 // invoking the interceptor again.
7531 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
7532 return v8::Integer::New(args[0]->Int32Value() + 1);
7533}
7534
7535// Helper to maximize the odds of object moving.
7536static void GenerateSomeGarbage() {
7537 CompileRun(
7538 "var garbage;"
7539 "for (var i = 0; i < 1000; i++) {"
7540 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
7541 "}"
7542 "garbage = undefined;");
7543}
7544
Steve Block1e0659c2011-05-24 12:43:12 +01007545v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) {
7546 static int count = 0;
7547 if (count++ % 3 == 0) {
7548 v8::V8::LowMemoryNotification(); // This should move the stub
7549 GenerateSomeGarbage(); // This should ensure the old stub memory is flushed
7550 }
7551 return v8::Handle<v8::Value>();
7552}
7553
7554
7555THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) {
7556 v8::HandleScope scope;
7557 LocalContext context;
7558 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
7559 nativeobject_templ->Set("callback",
7560 v8::FunctionTemplate::New(DirectApiCallback));
7561 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
7562 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
7563 // call the api function multiple times to ensure direct call stub creation.
7564 CompileRun(
7565 "function f() {"
7566 " for (var i = 1; i <= 30; i++) {"
7567 " nativeobject.callback();"
7568 " }"
7569 "}"
7570 "f();");
7571}
7572
7573
7574v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) {
7575 return v8::ThrowException(v8_str("g"));
7576}
7577
7578
7579THREADED_TEST(CallICFastApi_DirectCall_Throw) {
7580 v8::HandleScope scope;
7581 LocalContext context;
7582 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New();
7583 nativeobject_templ->Set("callback",
7584 v8::FunctionTemplate::New(ThrowingDirectApiCallback));
7585 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance();
7586 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj);
7587 // call the api function multiple times to ensure direct call stub creation.
7588 v8::Handle<Value> result = CompileRun(
7589 "var result = '';"
7590 "function f() {"
7591 " for (var i = 1; i <= 5; i++) {"
7592 " try { nativeobject.callback(); } catch (e) { result += e; }"
7593 " }"
7594 "}"
7595 "f(); result;");
7596 CHECK_EQ(v8_str("ggggg"), result);
7597}
7598
7599
Andrei Popescu402d9372010-02-26 13:31:12 +00007600THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
7601 int interceptor_call_count = 0;
7602 v8::HandleScope scope;
7603 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7604 v8::Handle<v8::FunctionTemplate> method_templ =
7605 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7606 v8_str("method_data"),
7607 v8::Handle<v8::Signature>());
7608 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7609 proto_templ->Set(v8_str("method"), method_templ);
7610 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7611 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7612 NULL, NULL, NULL, NULL,
7613 v8::External::Wrap(&interceptor_call_count));
7614 LocalContext context;
7615 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7616 GenerateSomeGarbage();
7617 context->Global()->Set(v8_str("o"), fun->NewInstance());
7618 v8::Handle<Value> value = CompileRun(
7619 "var result = 0;"
7620 "for (var i = 0; i < 100; i++) {"
7621 " result = o.method(41);"
7622 "}");
7623 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7624 CHECK_EQ(100, interceptor_call_count);
7625}
7626
7627THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
7628 int interceptor_call_count = 0;
7629 v8::HandleScope scope;
7630 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7631 v8::Handle<v8::FunctionTemplate> method_templ =
7632 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7633 v8_str("method_data"),
7634 v8::Signature::New(fun_templ));
7635 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7636 proto_templ->Set(v8_str("method"), method_templ);
7637 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7638 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7639 NULL, NULL, NULL, NULL,
7640 v8::External::Wrap(&interceptor_call_count));
7641 LocalContext context;
7642 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7643 GenerateSomeGarbage();
7644 context->Global()->Set(v8_str("o"), fun->NewInstance());
7645 v8::Handle<Value> value = CompileRun(
7646 "o.foo = 17;"
7647 "var receiver = {};"
7648 "receiver.__proto__ = o;"
7649 "var result = 0;"
7650 "for (var i = 0; i < 100; i++) {"
7651 " result = receiver.method(41);"
7652 "}");
7653 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7654 CHECK_EQ(100, interceptor_call_count);
7655}
7656
7657THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
7658 int interceptor_call_count = 0;
7659 v8::HandleScope scope;
7660 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7661 v8::Handle<v8::FunctionTemplate> method_templ =
7662 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7663 v8_str("method_data"),
7664 v8::Signature::New(fun_templ));
7665 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7666 proto_templ->Set(v8_str("method"), method_templ);
7667 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7668 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7669 NULL, NULL, NULL, NULL,
7670 v8::External::Wrap(&interceptor_call_count));
7671 LocalContext context;
7672 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7673 GenerateSomeGarbage();
7674 context->Global()->Set(v8_str("o"), fun->NewInstance());
7675 v8::Handle<Value> value = CompileRun(
7676 "o.foo = 17;"
7677 "var receiver = {};"
7678 "receiver.__proto__ = o;"
7679 "var result = 0;"
7680 "var saved_result = 0;"
7681 "for (var i = 0; i < 100; i++) {"
7682 " result = receiver.method(41);"
7683 " if (i == 50) {"
7684 " saved_result = result;"
7685 " receiver = {method: function(x) { return x - 1 }};"
7686 " }"
7687 "}");
7688 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7689 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7690 CHECK_GE(interceptor_call_count, 50);
7691}
7692
7693THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
7694 int interceptor_call_count = 0;
7695 v8::HandleScope scope;
7696 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7697 v8::Handle<v8::FunctionTemplate> method_templ =
7698 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7699 v8_str("method_data"),
7700 v8::Signature::New(fun_templ));
7701 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7702 proto_templ->Set(v8_str("method"), method_templ);
7703 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7704 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7705 NULL, NULL, NULL, NULL,
7706 v8::External::Wrap(&interceptor_call_count));
7707 LocalContext context;
7708 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7709 GenerateSomeGarbage();
7710 context->Global()->Set(v8_str("o"), fun->NewInstance());
7711 v8::Handle<Value> value = CompileRun(
7712 "o.foo = 17;"
7713 "var receiver = {};"
7714 "receiver.__proto__ = o;"
7715 "var result = 0;"
7716 "var saved_result = 0;"
7717 "for (var i = 0; i < 100; i++) {"
7718 " result = receiver.method(41);"
7719 " if (i == 50) {"
7720 " saved_result = result;"
7721 " o.method = function(x) { return x - 1 };"
7722 " }"
7723 "}");
7724 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7725 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7726 CHECK_GE(interceptor_call_count, 50);
7727}
7728
Steve Block6ded16b2010-05-10 14:33:55 +01007729THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7730 int interceptor_call_count = 0;
7731 v8::HandleScope scope;
7732 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7733 v8::Handle<v8::FunctionTemplate> method_templ =
7734 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7735 v8_str("method_data"),
7736 v8::Signature::New(fun_templ));
7737 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7738 proto_templ->Set(v8_str("method"), method_templ);
7739 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7740 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7741 NULL, NULL, NULL, NULL,
7742 v8::External::Wrap(&interceptor_call_count));
7743 LocalContext context;
7744 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7745 GenerateSomeGarbage();
7746 context->Global()->Set(v8_str("o"), fun->NewInstance());
7747 v8::TryCatch try_catch;
7748 v8::Handle<Value> value = CompileRun(
7749 "o.foo = 17;"
7750 "var receiver = {};"
7751 "receiver.__proto__ = o;"
7752 "var result = 0;"
7753 "var saved_result = 0;"
7754 "for (var i = 0; i < 100; i++) {"
7755 " result = receiver.method(41);"
7756 " if (i == 50) {"
7757 " saved_result = result;"
7758 " receiver = 333;"
7759 " }"
7760 "}");
7761 CHECK(try_catch.HasCaught());
7762 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7763 try_catch.Exception()->ToString());
7764 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7765 CHECK_GE(interceptor_call_count, 50);
7766}
7767
Andrei Popescu402d9372010-02-26 13:31:12 +00007768THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
7769 int interceptor_call_count = 0;
7770 v8::HandleScope scope;
7771 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7772 v8::Handle<v8::FunctionTemplate> method_templ =
7773 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7774 v8_str("method_data"),
7775 v8::Signature::New(fun_templ));
7776 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7777 proto_templ->Set(v8_str("method"), method_templ);
7778 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7779 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7780 NULL, NULL, NULL, NULL,
7781 v8::External::Wrap(&interceptor_call_count));
7782 LocalContext context;
7783 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7784 GenerateSomeGarbage();
7785 context->Global()->Set(v8_str("o"), fun->NewInstance());
7786 v8::TryCatch try_catch;
7787 v8::Handle<Value> value = CompileRun(
7788 "o.foo = 17;"
7789 "var receiver = {};"
7790 "receiver.__proto__ = o;"
7791 "var result = 0;"
7792 "var saved_result = 0;"
7793 "for (var i = 0; i < 100; i++) {"
7794 " result = receiver.method(41);"
7795 " if (i == 50) {"
7796 " saved_result = result;"
7797 " receiver = {method: receiver.method};"
7798 " }"
7799 "}");
7800 CHECK(try_catch.HasCaught());
7801 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7802 try_catch.Exception()->ToString());
7803 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7804 CHECK_GE(interceptor_call_count, 50);
7805}
7806
7807THREADED_TEST(CallICFastApi_TrivialSignature) {
7808 v8::HandleScope scope;
7809 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7810 v8::Handle<v8::FunctionTemplate> method_templ =
7811 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7812 v8_str("method_data"),
7813 v8::Handle<v8::Signature>());
7814 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7815 proto_templ->Set(v8_str("method"), method_templ);
7816 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7817 LocalContext context;
7818 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7819 GenerateSomeGarbage();
7820 context->Global()->Set(v8_str("o"), fun->NewInstance());
7821 v8::Handle<Value> value = CompileRun(
7822 "var result = 0;"
7823 "for (var i = 0; i < 100; i++) {"
7824 " result = o.method(41);"
7825 "}");
7826
7827 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7828}
7829
7830THREADED_TEST(CallICFastApi_SimpleSignature) {
7831 v8::HandleScope scope;
7832 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7833 v8::Handle<v8::FunctionTemplate> method_templ =
7834 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7835 v8_str("method_data"),
7836 v8::Signature::New(fun_templ));
7837 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7838 proto_templ->Set(v8_str("method"), method_templ);
7839 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7840 LocalContext context;
7841 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7842 GenerateSomeGarbage();
7843 context->Global()->Set(v8_str("o"), fun->NewInstance());
7844 v8::Handle<Value> value = CompileRun(
7845 "o.foo = 17;"
7846 "var receiver = {};"
7847 "receiver.__proto__ = o;"
7848 "var result = 0;"
7849 "for (var i = 0; i < 100; i++) {"
7850 " result = receiver.method(41);"
7851 "}");
7852
7853 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7854}
7855
Steve Block6ded16b2010-05-10 14:33:55 +01007856THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007857 v8::HandleScope scope;
7858 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7859 v8::Handle<v8::FunctionTemplate> method_templ =
7860 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7861 v8_str("method_data"),
7862 v8::Signature::New(fun_templ));
7863 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7864 proto_templ->Set(v8_str("method"), method_templ);
7865 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7866 LocalContext context;
7867 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7868 GenerateSomeGarbage();
7869 context->Global()->Set(v8_str("o"), fun->NewInstance());
7870 v8::Handle<Value> value = CompileRun(
7871 "o.foo = 17;"
7872 "var receiver = {};"
7873 "receiver.__proto__ = o;"
7874 "var result = 0;"
7875 "var saved_result = 0;"
7876 "for (var i = 0; i < 100; i++) {"
7877 " result = receiver.method(41);"
7878 " if (i == 50) {"
7879 " saved_result = result;"
7880 " receiver = {method: function(x) { return x - 1 }};"
7881 " }"
7882 "}");
7883 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7884 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7885}
7886
Steve Block6ded16b2010-05-10 14:33:55 +01007887THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7888 v8::HandleScope scope;
7889 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7890 v8::Handle<v8::FunctionTemplate> method_templ =
7891 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7892 v8_str("method_data"),
7893 v8::Signature::New(fun_templ));
7894 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7895 proto_templ->Set(v8_str("method"), method_templ);
7896 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7897 LocalContext context;
7898 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7899 GenerateSomeGarbage();
7900 context->Global()->Set(v8_str("o"), fun->NewInstance());
7901 v8::TryCatch try_catch;
7902 v8::Handle<Value> value = CompileRun(
7903 "o.foo = 17;"
7904 "var receiver = {};"
7905 "receiver.__proto__ = o;"
7906 "var result = 0;"
7907 "var saved_result = 0;"
7908 "for (var i = 0; i < 100; i++) {"
7909 " result = receiver.method(41);"
7910 " if (i == 50) {"
7911 " saved_result = result;"
7912 " receiver = 333;"
7913 " }"
7914 "}");
7915 CHECK(try_catch.HasCaught());
7916 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7917 try_catch.Exception()->ToString());
7918 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7919}
7920
Leon Clarke4515c472010-02-03 11:58:03 +00007921
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007922v8::Handle<Value> keyed_call_ic_function;
7923
7924static v8::Handle<Value> InterceptorKeyedCallICGetter(
7925 Local<String> name, const AccessorInfo& info) {
7926 ApiTestFuzzer::Fuzz();
7927 if (v8_str("x")->Equals(name)) {
7928 return keyed_call_ic_function;
7929 }
7930 return v8::Handle<Value>();
7931}
7932
7933
7934// Test the case when we stored cacheable lookup into
7935// a stub, but the function name changed (to another cacheable function).
7936THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7937 v8::HandleScope scope;
7938 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7939 templ->SetNamedPropertyHandler(NoBlockGetterX);
7940 LocalContext context;
7941 context->Global()->Set(v8_str("o"), templ->NewInstance());
7942 v8::Handle<Value> value = CompileRun(
7943 "proto = new Object();"
7944 "proto.y = function(x) { return x + 1; };"
7945 "proto.z = function(x) { return x - 1; };"
7946 "o.__proto__ = proto;"
7947 "var result = 0;"
7948 "var method = 'y';"
7949 "for (var i = 0; i < 10; i++) {"
7950 " if (i == 5) { method = 'z'; };"
7951 " result += o[method](41);"
7952 "}");
7953 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7954}
7955
7956
7957// Test the case when we stored cacheable lookup into
7958// a stub, but the function name changed (and the new function is present
7959// both before and after the interceptor in the prototype chain).
7960THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7961 v8::HandleScope scope;
7962 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7963 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7964 LocalContext context;
7965 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7966 keyed_call_ic_function =
7967 v8_compile("function f(x) { return x - 1; }; f")->Run();
7968 v8::Handle<Value> value = CompileRun(
7969 "o = new Object();"
7970 "proto2 = new Object();"
7971 "o.y = function(x) { return x + 1; };"
7972 "proto2.y = function(x) { return x + 2; };"
7973 "o.__proto__ = proto1;"
7974 "proto1.__proto__ = proto2;"
7975 "var result = 0;"
7976 "var method = 'x';"
7977 "for (var i = 0; i < 10; i++) {"
7978 " if (i == 5) { method = 'y'; };"
7979 " result += o[method](41);"
7980 "}");
7981 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7982}
7983
7984
7985// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7986// on the global object.
7987THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7988 v8::HandleScope scope;
7989 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7990 templ->SetNamedPropertyHandler(NoBlockGetterX);
7991 LocalContext context;
7992 context->Global()->Set(v8_str("o"), templ->NewInstance());
7993 v8::Handle<Value> value = CompileRun(
7994 "function inc(x) { return x + 1; };"
7995 "inc(1);"
7996 "function dec(x) { return x - 1; };"
7997 "dec(1);"
7998 "o.__proto__ = this;"
7999 "this.__proto__.x = inc;"
8000 "this.__proto__.y = dec;"
8001 "var result = 0;"
8002 "var method = 'x';"
8003 "for (var i = 0; i < 10; i++) {"
8004 " if (i == 5) { method = 'y'; };"
8005 " result += o[method](41);"
8006 "}");
8007 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8008}
8009
8010
8011// Test the case when actual function to call sits on global object.
8012THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
8013 v8::HandleScope scope;
8014 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8015 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8016 LocalContext context;
8017 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8018
8019 v8::Handle<Value> value = CompileRun(
8020 "function len(x) { return x.length; };"
8021 "o.__proto__ = this;"
8022 "var m = 'parseFloat';"
8023 "var result = 0;"
8024 "for (var i = 0; i < 10; i++) {"
8025 " if (i == 5) {"
8026 " m = 'len';"
8027 " saved_result = result;"
8028 " };"
8029 " result = o[m]('239');"
8030 "}");
8031 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
8032 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
8033}
8034
8035// Test the map transition before the interceptor.
8036THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
8037 v8::HandleScope scope;
8038 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8039 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8040 LocalContext context;
8041 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
8042
8043 v8::Handle<Value> value = CompileRun(
8044 "var o = new Object();"
8045 "o.__proto__ = proto;"
8046 "o.method = function(x) { return x + 1; };"
8047 "var m = 'method';"
8048 "var result = 0;"
8049 "for (var i = 0; i < 10; i++) {"
8050 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
8051 " result += o[m](41);"
8052 "}");
8053 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8054}
8055
8056
8057// Test the map transition after the interceptor.
8058THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
8059 v8::HandleScope scope;
8060 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
8061 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
8062 LocalContext context;
8063 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
8064
8065 v8::Handle<Value> value = CompileRun(
8066 "var proto = new Object();"
8067 "o.__proto__ = proto;"
8068 "proto.method = function(x) { return x + 1; };"
8069 "var m = 'method';"
8070 "var result = 0;"
8071 "for (var i = 0; i < 10; i++) {"
8072 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
8073 " result += o[m](41);"
8074 "}");
8075 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
8076}
8077
8078
Steve Blocka7e24c12009-10-30 11:49:00 +00008079static int interceptor_call_count = 0;
8080
8081static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
8082 const AccessorInfo& info) {
8083 ApiTestFuzzer::Fuzz();
8084 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
8085 return call_ic_function2;
8086 }
8087 return v8::Handle<Value>();
8088}
8089
8090
8091// This test should hit load and call ICs for the interceptor case.
8092// Once in a while, the interceptor will reply that a property was not
8093// found in which case we should get a reference error.
8094THREADED_TEST(InterceptorICReferenceErrors) {
8095 v8::HandleScope scope;
8096 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8097 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
8098 LocalContext context(0, templ, v8::Handle<Value>());
8099 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
8100 v8::Handle<Value> value = CompileRun(
8101 "function f() {"
8102 " for (var i = 0; i < 1000; i++) {"
8103 " try { x; } catch(e) { return true; }"
8104 " }"
8105 " return false;"
8106 "};"
8107 "f();");
8108 CHECK_EQ(true, value->BooleanValue());
8109 interceptor_call_count = 0;
8110 value = CompileRun(
8111 "function g() {"
8112 " for (var i = 0; i < 1000; i++) {"
8113 " try { x(42); } catch(e) { return true; }"
8114 " }"
8115 " return false;"
8116 "};"
8117 "g();");
8118 CHECK_EQ(true, value->BooleanValue());
8119}
8120
8121
8122static int interceptor_ic_exception_get_count = 0;
8123
8124static v8::Handle<Value> InterceptorICExceptionGetter(
8125 Local<String> name,
8126 const AccessorInfo& info) {
8127 ApiTestFuzzer::Fuzz();
8128 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
8129 return call_ic_function3;
8130 }
8131 if (interceptor_ic_exception_get_count == 20) {
8132 return v8::ThrowException(v8_num(42));
8133 }
8134 // Do not handle get for properties other than x.
8135 return v8::Handle<Value>();
8136}
8137
8138// Test interceptor load/call IC where the interceptor throws an
8139// exception once in a while.
8140THREADED_TEST(InterceptorICGetterExceptions) {
8141 interceptor_ic_exception_get_count = 0;
8142 v8::HandleScope scope;
8143 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8144 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
8145 LocalContext context(0, templ, v8::Handle<Value>());
8146 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
8147 v8::Handle<Value> value = CompileRun(
8148 "function f() {"
8149 " for (var i = 0; i < 100; i++) {"
8150 " try { x; } catch(e) { return true; }"
8151 " }"
8152 " return false;"
8153 "};"
8154 "f();");
8155 CHECK_EQ(true, value->BooleanValue());
8156 interceptor_ic_exception_get_count = 0;
8157 value = CompileRun(
8158 "function f() {"
8159 " for (var i = 0; i < 100; i++) {"
8160 " try { x(42); } catch(e) { return true; }"
8161 " }"
8162 " return false;"
8163 "};"
8164 "f();");
8165 CHECK_EQ(true, value->BooleanValue());
8166}
8167
8168
8169static int interceptor_ic_exception_set_count = 0;
8170
8171static v8::Handle<Value> InterceptorICExceptionSetter(
8172 Local<String> key, Local<Value> value, const AccessorInfo&) {
8173 ApiTestFuzzer::Fuzz();
8174 if (++interceptor_ic_exception_set_count > 20) {
8175 return v8::ThrowException(v8_num(42));
8176 }
8177 // Do not actually handle setting.
8178 return v8::Handle<Value>();
8179}
8180
8181// Test interceptor store IC where the interceptor throws an exception
8182// once in a while.
8183THREADED_TEST(InterceptorICSetterExceptions) {
8184 interceptor_ic_exception_set_count = 0;
8185 v8::HandleScope scope;
8186 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8187 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
8188 LocalContext context(0, templ, v8::Handle<Value>());
8189 v8::Handle<Value> value = CompileRun(
8190 "function f() {"
8191 " for (var i = 0; i < 100; i++) {"
8192 " try { x = 42; } catch(e) { return true; }"
8193 " }"
8194 " return false;"
8195 "};"
8196 "f();");
8197 CHECK_EQ(true, value->BooleanValue());
8198}
8199
8200
8201// Test that we ignore null interceptors.
8202THREADED_TEST(NullNamedInterceptor) {
8203 v8::HandleScope scope;
8204 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8205 templ->SetNamedPropertyHandler(0);
8206 LocalContext context;
8207 templ->Set("x", v8_num(42));
8208 v8::Handle<v8::Object> obj = templ->NewInstance();
8209 context->Global()->Set(v8_str("obj"), obj);
8210 v8::Handle<Value> value = CompileRun("obj.x");
8211 CHECK(value->IsInt32());
8212 CHECK_EQ(42, value->Int32Value());
8213}
8214
8215
8216// Test that we ignore null interceptors.
8217THREADED_TEST(NullIndexedInterceptor) {
8218 v8::HandleScope scope;
8219 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
8220 templ->SetIndexedPropertyHandler(0);
8221 LocalContext context;
8222 templ->Set("42", v8_num(42));
8223 v8::Handle<v8::Object> obj = templ->NewInstance();
8224 context->Global()->Set(v8_str("obj"), obj);
8225 v8::Handle<Value> value = CompileRun("obj[42]");
8226 CHECK(value->IsInt32());
8227 CHECK_EQ(42, value->Int32Value());
8228}
8229
8230
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008231THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
8232 v8::HandleScope scope;
8233 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8234 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
8235 LocalContext env;
8236 env->Global()->Set(v8_str("obj"),
8237 templ->GetFunction()->NewInstance());
8238 ExpectTrue("obj.x === 42");
8239 ExpectTrue("!obj.propertyIsEnumerable('x')");
8240}
8241
8242
Steve Blocka7e24c12009-10-30 11:49:00 +00008243static v8::Handle<Value> ParentGetter(Local<String> name,
8244 const AccessorInfo& info) {
8245 ApiTestFuzzer::Fuzz();
8246 return v8_num(1);
8247}
8248
8249
8250static v8::Handle<Value> ChildGetter(Local<String> name,
8251 const AccessorInfo& info) {
8252 ApiTestFuzzer::Fuzz();
8253 return v8_num(42);
8254}
8255
8256
8257THREADED_TEST(Overriding) {
8258 v8::HandleScope scope;
8259 LocalContext context;
8260
8261 // Parent template.
8262 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
8263 Local<ObjectTemplate> parent_instance_templ =
8264 parent_templ->InstanceTemplate();
8265 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
8266
8267 // Template that inherits from the parent template.
8268 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
8269 Local<ObjectTemplate> child_instance_templ =
8270 child_templ->InstanceTemplate();
8271 child_templ->Inherit(parent_templ);
8272 // Override 'f'. The child version of 'f' should get called for child
8273 // instances.
8274 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
8275 // Add 'g' twice. The 'g' added last should get called for instances.
8276 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
8277 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
8278
8279 // Add 'h' as an accessor to the proto template with ReadOnly attributes
8280 // so 'h' can be shadowed on the instance object.
8281 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
8282 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
8283 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
8284
8285 // Add 'i' as an accessor to the instance template with ReadOnly attributes
8286 // but the attribute does not have effect because it is duplicated with
8287 // NULL setter.
8288 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
8289 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
8290
8291
8292
8293 // Instantiate the child template.
8294 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
8295
8296 // Check that the child function overrides the parent one.
8297 context->Global()->Set(v8_str("o"), instance);
8298 Local<Value> value = v8_compile("o.f")->Run();
8299 // Check that the 'g' that was added last is hit.
8300 CHECK_EQ(42, value->Int32Value());
8301 value = v8_compile("o.g")->Run();
8302 CHECK_EQ(42, value->Int32Value());
8303
8304 // Check 'h' can be shadowed.
8305 value = v8_compile("o.h = 3; o.h")->Run();
8306 CHECK_EQ(3, value->Int32Value());
8307
8308 // Check 'i' is cannot be shadowed or changed.
8309 value = v8_compile("o.i = 3; o.i")->Run();
8310 CHECK_EQ(42, value->Int32Value());
8311}
8312
8313
8314static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
8315 ApiTestFuzzer::Fuzz();
8316 if (args.IsConstructCall()) {
8317 return v8::Boolean::New(true);
8318 }
8319 return v8::Boolean::New(false);
8320}
8321
8322
8323THREADED_TEST(IsConstructCall) {
8324 v8::HandleScope scope;
8325
8326 // Function template with call handler.
8327 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8328 templ->SetCallHandler(IsConstructHandler);
8329
8330 LocalContext context;
8331
8332 context->Global()->Set(v8_str("f"), templ->GetFunction());
8333 Local<Value> value = v8_compile("f()")->Run();
8334 CHECK(!value->BooleanValue());
8335 value = v8_compile("new f()")->Run();
8336 CHECK(value->BooleanValue());
8337}
8338
8339
8340THREADED_TEST(ObjectProtoToString) {
8341 v8::HandleScope scope;
8342 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
8343 templ->SetClassName(v8_str("MyClass"));
8344
8345 LocalContext context;
8346
8347 Local<String> customized_tostring = v8_str("customized toString");
8348
8349 // Replace Object.prototype.toString
8350 v8_compile("Object.prototype.toString = function() {"
8351 " return 'customized toString';"
8352 "}")->Run();
8353
8354 // Normal ToString call should call replaced Object.prototype.toString
8355 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
8356 Local<String> value = instance->ToString();
8357 CHECK(value->IsString() && value->Equals(customized_tostring));
8358
8359 // ObjectProtoToString should not call replace toString function.
8360 value = instance->ObjectProtoToString();
8361 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
8362
8363 // Check global
8364 value = context->Global()->ObjectProtoToString();
8365 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
8366
8367 // Check ordinary object
8368 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008369 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00008370 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
8371}
8372
8373
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08008374THREADED_TEST(ObjectGetConstructorName) {
8375 v8::HandleScope scope;
8376 LocalContext context;
8377 v8_compile("function Parent() {};"
8378 "function Child() {};"
8379 "Child.prototype = new Parent();"
8380 "var outer = { inner: function() { } };"
8381 "var p = new Parent();"
8382 "var c = new Child();"
8383 "var x = new outer.inner();")->Run();
8384
8385 Local<v8::Value> p = context->Global()->Get(v8_str("p"));
8386 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals(
8387 v8_str("Parent")));
8388
8389 Local<v8::Value> c = context->Global()->Get(v8_str("c"));
8390 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals(
8391 v8_str("Child")));
8392
8393 Local<v8::Value> x = context->Global()->Get(v8_str("x"));
8394 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals(
8395 v8_str("outer.inner")));
8396}
8397
8398
Steve Blocka7e24c12009-10-30 11:49:00 +00008399bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01008400i::Semaphore* ApiTestFuzzer::all_tests_done_=
8401 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008402int ApiTestFuzzer::active_tests_;
8403int ApiTestFuzzer::tests_being_run_;
8404int ApiTestFuzzer::current_;
8405
8406
8407// We are in a callback and want to switch to another thread (if we
8408// are currently running the thread fuzzing test).
8409void ApiTestFuzzer::Fuzz() {
8410 if (!fuzzing_) return;
8411 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
8412 test->ContextSwitch();
8413}
8414
8415
8416// Let the next thread go. Since it is also waiting on the V8 lock it may
8417// not start immediately.
8418bool ApiTestFuzzer::NextThread() {
8419 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00008420 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00008421 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00008422 if (kLogThreading)
8423 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00008424 return false;
8425 }
Steve Blockd0582a62009-12-15 09:54:21 +00008426 if (kLogThreading) {
8427 printf("Switch from %s to %s\n",
8428 test_name,
8429 RegisterThreadedTest::nth(test_position)->name());
8430 }
Steve Blocka7e24c12009-10-30 11:49:00 +00008431 current_ = test_position;
8432 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
8433 return true;
8434}
8435
8436
8437void ApiTestFuzzer::Run() {
8438 // When it is our turn...
8439 gate_->Wait();
8440 {
8441 // ... get the V8 lock and start running the test.
8442 v8::Locker locker;
8443 CallTest();
8444 }
8445 // This test finished.
8446 active_ = false;
8447 active_tests_--;
8448 // If it was the last then signal that fact.
8449 if (active_tests_ == 0) {
8450 all_tests_done_->Signal();
8451 } else {
8452 // Otherwise select a new test and start that.
8453 NextThread();
8454 }
8455}
8456
8457
8458static unsigned linear_congruential_generator;
8459
8460
8461void ApiTestFuzzer::Setup(PartOfTest part) {
8462 linear_congruential_generator = i::FLAG_testing_prng_seed;
8463 fuzzing_ = true;
8464 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
8465 int end = (part == FIRST_PART)
8466 ? (RegisterThreadedTest::count() >> 1)
8467 : RegisterThreadedTest::count();
8468 active_tests_ = tests_being_run_ = end - start;
8469 for (int i = 0; i < tests_being_run_; i++) {
8470 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
8471 }
8472 for (int i = 0; i < active_tests_; i++) {
8473 RegisterThreadedTest::nth(i)->fuzzer_->Start();
8474 }
8475}
8476
8477
8478static void CallTestNumber(int test_number) {
8479 (RegisterThreadedTest::nth(test_number)->callback())();
8480}
8481
8482
8483void ApiTestFuzzer::RunAllTests() {
8484 // Set off the first test.
8485 current_ = -1;
8486 NextThread();
8487 // Wait till they are all done.
8488 all_tests_done_->Wait();
8489}
8490
8491
8492int ApiTestFuzzer::GetNextTestNumber() {
8493 int next_test;
8494 do {
8495 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
8496 linear_congruential_generator *= 1664525u;
8497 linear_congruential_generator += 1013904223u;
8498 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
8499 return next_test;
8500}
8501
8502
8503void ApiTestFuzzer::ContextSwitch() {
8504 // If the new thread is the same as the current thread there is nothing to do.
8505 if (NextThread()) {
8506 // Now it can start.
8507 v8::Unlocker unlocker;
8508 // Wait till someone starts us again.
8509 gate_->Wait();
8510 // And we're off.
8511 }
8512}
8513
8514
8515void ApiTestFuzzer::TearDown() {
8516 fuzzing_ = false;
8517 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
8518 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
8519 if (fuzzer != NULL) fuzzer->Join();
8520 }
8521}
8522
8523
8524// Lets not be needlessly self-referential.
8525TEST(Threading) {
8526 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
8527 ApiTestFuzzer::RunAllTests();
8528 ApiTestFuzzer::TearDown();
8529}
8530
8531TEST(Threading2) {
8532 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
8533 ApiTestFuzzer::RunAllTests();
8534 ApiTestFuzzer::TearDown();
8535}
8536
8537
8538void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00008539 if (kLogThreading)
8540 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008541 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00008542 if (kLogThreading)
8543 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00008544}
8545
8546
8547static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
8548 CHECK(v8::Locker::IsLocked());
8549 ApiTestFuzzer::Fuzz();
8550 v8::Unlocker unlocker;
8551 const char* code = "throw 7;";
8552 {
8553 v8::Locker nested_locker;
8554 v8::HandleScope scope;
8555 v8::Handle<Value> exception;
8556 { v8::TryCatch try_catch;
8557 v8::Handle<Value> value = CompileRun(code);
8558 CHECK(value.IsEmpty());
8559 CHECK(try_catch.HasCaught());
8560 // Make sure to wrap the exception in a new handle because
8561 // the handle returned from the TryCatch is destroyed
8562 // when the TryCatch is destroyed.
8563 exception = Local<Value>::New(try_catch.Exception());
8564 }
8565 return v8::ThrowException(exception);
8566 }
8567}
8568
8569
8570static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
8571 CHECK(v8::Locker::IsLocked());
8572 ApiTestFuzzer::Fuzz();
8573 v8::Unlocker unlocker;
8574 const char* code = "throw 7;";
8575 {
8576 v8::Locker nested_locker;
8577 v8::HandleScope scope;
8578 v8::Handle<Value> value = CompileRun(code);
8579 CHECK(value.IsEmpty());
8580 return v8_str("foo");
8581 }
8582}
8583
8584
8585// These are locking tests that don't need to be run again
8586// as part of the locking aggregation tests.
8587TEST(NestedLockers) {
8588 v8::Locker locker;
8589 CHECK(v8::Locker::IsLocked());
8590 v8::HandleScope scope;
8591 LocalContext env;
8592 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
8593 Local<Function> fun = fun_templ->GetFunction();
8594 env->Global()->Set(v8_str("throw_in_js"), fun);
8595 Local<Script> script = v8_compile("(function () {"
8596 " try {"
8597 " throw_in_js();"
8598 " return 42;"
8599 " } catch (e) {"
8600 " return e * 13;"
8601 " }"
8602 "})();");
8603 CHECK_EQ(91, script->Run()->Int32Value());
8604}
8605
8606
8607// These are locking tests that don't need to be run again
8608// as part of the locking aggregation tests.
8609TEST(NestedLockersNoTryCatch) {
8610 v8::Locker locker;
8611 v8::HandleScope scope;
8612 LocalContext env;
8613 Local<v8::FunctionTemplate> fun_templ =
8614 v8::FunctionTemplate::New(ThrowInJSNoCatch);
8615 Local<Function> fun = fun_templ->GetFunction();
8616 env->Global()->Set(v8_str("throw_in_js"), fun);
8617 Local<Script> script = v8_compile("(function () {"
8618 " try {"
8619 " throw_in_js();"
8620 " return 42;"
8621 " } catch (e) {"
8622 " return e * 13;"
8623 " }"
8624 "})();");
8625 CHECK_EQ(91, script->Run()->Int32Value());
8626}
8627
8628
8629THREADED_TEST(RecursiveLocking) {
8630 v8::Locker locker;
8631 {
8632 v8::Locker locker2;
8633 CHECK(v8::Locker::IsLocked());
8634 }
8635}
8636
8637
8638static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
8639 ApiTestFuzzer::Fuzz();
8640 v8::Unlocker unlocker;
8641 return v8::Undefined();
8642}
8643
8644
8645THREADED_TEST(LockUnlockLock) {
8646 {
8647 v8::Locker locker;
8648 v8::HandleScope scope;
8649 LocalContext env;
8650 Local<v8::FunctionTemplate> fun_templ =
8651 v8::FunctionTemplate::New(UnlockForAMoment);
8652 Local<Function> fun = fun_templ->GetFunction();
8653 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8654 Local<Script> script = v8_compile("(function () {"
8655 " unlock_for_a_moment();"
8656 " return 42;"
8657 "})();");
8658 CHECK_EQ(42, script->Run()->Int32Value());
8659 }
8660 {
8661 v8::Locker locker;
8662 v8::HandleScope scope;
8663 LocalContext env;
8664 Local<v8::FunctionTemplate> fun_templ =
8665 v8::FunctionTemplate::New(UnlockForAMoment);
8666 Local<Function> fun = fun_templ->GetFunction();
8667 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8668 Local<Script> script = v8_compile("(function () {"
8669 " unlock_for_a_moment();"
8670 " return 42;"
8671 "})();");
8672 CHECK_EQ(42, script->Run()->Int32Value());
8673 }
8674}
8675
8676
Leon Clarked91b9f72010-01-27 17:25:45 +00008677static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00008678 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01008679 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00008680 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
8681 if (object->IsJSGlobalObject()) count++;
8682 return count;
8683}
8684
8685
Ben Murdochf87a2032010-10-22 12:50:53 +01008686static void CheckSurvivingGlobalObjectsCount(int expected) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008687 // We need to collect all garbage twice to be sure that everything
8688 // has been collected. This is because inline caches are cleared in
8689 // the first garbage collection but some of the maps have already
8690 // been marked at that point. Therefore some of the maps are not
8691 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01008692 i::Heap::CollectAllGarbage(false);
8693 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00008694 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00008695#ifdef DEBUG
Ben Murdochf87a2032010-10-22 12:50:53 +01008696 if (count != expected) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00008697#endif
Ben Murdochf87a2032010-10-22 12:50:53 +01008698 CHECK_EQ(expected, count);
Steve Blocka7e24c12009-10-30 11:49:00 +00008699}
8700
8701
8702TEST(DontLeakGlobalObjects) {
8703 // Regression test for issues 1139850 and 1174891.
8704
8705 v8::V8::Initialize();
8706
Steve Blocka7e24c12009-10-30 11:49:00 +00008707 for (int i = 0; i < 5; i++) {
8708 { v8::HandleScope scope;
8709 LocalContext context;
8710 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008711 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008712
8713 { v8::HandleScope scope;
8714 LocalContext context;
8715 v8_compile("Date")->Run();
8716 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008717 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008718
8719 { v8::HandleScope scope;
8720 LocalContext context;
8721 v8_compile("/aaa/")->Run();
8722 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008723 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008724
8725 { v8::HandleScope scope;
8726 const char* extension_list[] = { "v8/gc" };
8727 v8::ExtensionConfiguration extensions(1, extension_list);
8728 LocalContext context(&extensions);
8729 v8_compile("gc();")->Run();
8730 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008731 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008732 }
8733}
8734
8735
8736v8::Persistent<v8::Object> some_object;
8737v8::Persistent<v8::Object> bad_handle;
8738
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008739void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008740 v8::HandleScope scope;
8741 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008742 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008743}
8744
8745
8746THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8747 LocalContext context;
8748
8749 v8::Persistent<v8::Object> handle1, handle2;
8750 {
8751 v8::HandleScope scope;
8752 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8753 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8754 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8755 }
8756 // Note: order is implementation dependent alas: currently
8757 // global handle nodes are processed by PostGarbageCollectionProcessing
8758 // in reverse allocation order, so if second allocated handle is deleted,
8759 // weak callback of the first handle would be able to 'reallocate' it.
8760 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8761 handle2.Dispose();
8762 i::Heap::CollectAllGarbage(false);
8763}
8764
8765
8766v8::Persistent<v8::Object> to_be_disposed;
8767
8768void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8769 to_be_disposed.Dispose();
8770 i::Heap::CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008771 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008772}
8773
8774
8775THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8776 LocalContext context;
8777
8778 v8::Persistent<v8::Object> handle1, handle2;
8779 {
8780 v8::HandleScope scope;
8781 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8782 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8783 }
8784 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8785 to_be_disposed = handle2;
8786 i::Heap::CollectAllGarbage(false);
8787}
8788
Steve Blockd0582a62009-12-15 09:54:21 +00008789void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8790 handle.Dispose();
8791}
8792
8793void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8794 v8::HandleScope scope;
8795 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008796 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00008797}
8798
8799
8800THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8801 LocalContext context;
8802
8803 v8::Persistent<v8::Object> handle1, handle2, handle3;
8804 {
8805 v8::HandleScope scope;
8806 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8807 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8808 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8809 }
8810 handle2.MakeWeak(NULL, DisposingCallback);
8811 handle3.MakeWeak(NULL, HandleCreatingCallback);
8812 i::Heap::CollectAllGarbage(false);
8813}
8814
Steve Blocka7e24c12009-10-30 11:49:00 +00008815
8816THREADED_TEST(CheckForCrossContextObjectLiterals) {
8817 v8::V8::Initialize();
8818
8819 const int nof = 2;
8820 const char* sources[nof] = {
8821 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8822 "Object()"
8823 };
8824
8825 for (int i = 0; i < nof; i++) {
8826 const char* source = sources[i];
8827 { v8::HandleScope scope;
8828 LocalContext context;
8829 CompileRun(source);
8830 }
8831 { v8::HandleScope scope;
8832 LocalContext context;
8833 CompileRun(source);
8834 }
8835 }
8836}
8837
8838
8839static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8840 v8::HandleScope inner;
8841 env->Enter();
8842 v8::Handle<Value> three = v8_num(3);
8843 v8::Handle<Value> value = inner.Close(three);
8844 env->Exit();
8845 return value;
8846}
8847
8848
8849THREADED_TEST(NestedHandleScopeAndContexts) {
8850 v8::HandleScope outer;
8851 v8::Persistent<Context> env = Context::New();
8852 env->Enter();
8853 v8::Handle<Value> value = NestedScope(env);
8854 v8::Handle<String> str = value->ToString();
8855 env->Exit();
8856 env.Dispose();
8857}
8858
8859
8860THREADED_TEST(ExternalAllocatedMemory) {
8861 v8::HandleScope outer;
8862 v8::Persistent<Context> env = Context::New();
8863 const int kSize = 1024*1024;
8864 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8865 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8866}
8867
8868
8869THREADED_TEST(DisposeEnteredContext) {
8870 v8::HandleScope scope;
8871 LocalContext outer;
8872 { v8::Persistent<v8::Context> inner = v8::Context::New();
8873 inner->Enter();
8874 inner.Dispose();
8875 inner.Clear();
8876 inner->Exit();
8877 }
8878}
8879
8880
8881// Regression test for issue 54, object templates with internal fields
8882// but no accessors or interceptors did not get their internal field
8883// count set on instances.
8884THREADED_TEST(Regress54) {
8885 v8::HandleScope outer;
8886 LocalContext context;
8887 static v8::Persistent<v8::ObjectTemplate> templ;
8888 if (templ.IsEmpty()) {
8889 v8::HandleScope inner;
8890 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8891 local->SetInternalFieldCount(1);
8892 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8893 }
8894 v8::Handle<v8::Object> result = templ->NewInstance();
8895 CHECK_EQ(1, result->InternalFieldCount());
8896}
8897
8898
8899// If part of the threaded tests, this test makes ThreadingTest fail
8900// on mac.
8901TEST(CatchStackOverflow) {
8902 v8::HandleScope scope;
8903 LocalContext context;
8904 v8::TryCatch try_catch;
8905 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8906 "function f() {"
8907 " return f();"
8908 "}"
8909 ""
8910 "f();"));
8911 v8::Handle<v8::Value> result = script->Run();
8912 CHECK(result.IsEmpty());
8913}
8914
8915
8916static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8917 const char* resource_name,
8918 int line_offset) {
8919 v8::HandleScope scope;
8920 v8::TryCatch try_catch;
8921 v8::Handle<v8::Value> result = script->Run();
8922 CHECK(result.IsEmpty());
8923 CHECK(try_catch.HasCaught());
8924 v8::Handle<v8::Message> message = try_catch.Message();
8925 CHECK(!message.IsEmpty());
8926 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8927 CHECK_EQ(91, message->GetStartPosition());
8928 CHECK_EQ(92, message->GetEndPosition());
8929 CHECK_EQ(2, message->GetStartColumn());
8930 CHECK_EQ(3, message->GetEndColumn());
8931 v8::String::AsciiValue line(message->GetSourceLine());
8932 CHECK_EQ(" throw 'nirk';", *line);
8933 v8::String::AsciiValue name(message->GetScriptResourceName());
8934 CHECK_EQ(resource_name, *name);
8935}
8936
8937
8938THREADED_TEST(TryCatchSourceInfo) {
8939 v8::HandleScope scope;
8940 LocalContext context;
8941 v8::Handle<v8::String> source = v8::String::New(
8942 "function Foo() {\n"
8943 " return Bar();\n"
8944 "}\n"
8945 "\n"
8946 "function Bar() {\n"
8947 " return Baz();\n"
8948 "}\n"
8949 "\n"
8950 "function Baz() {\n"
8951 " throw 'nirk';\n"
8952 "}\n"
8953 "\n"
8954 "Foo();\n");
8955
8956 const char* resource_name;
8957 v8::Handle<v8::Script> script;
8958 resource_name = "test.js";
8959 script = v8::Script::Compile(source, v8::String::New(resource_name));
8960 CheckTryCatchSourceInfo(script, resource_name, 0);
8961
8962 resource_name = "test1.js";
8963 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8964 script = v8::Script::Compile(source, &origin1);
8965 CheckTryCatchSourceInfo(script, resource_name, 0);
8966
8967 resource_name = "test2.js";
8968 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8969 script = v8::Script::Compile(source, &origin2);
8970 CheckTryCatchSourceInfo(script, resource_name, 7);
8971}
8972
8973
8974THREADED_TEST(CompilationCache) {
8975 v8::HandleScope scope;
8976 LocalContext context;
8977 v8::Handle<v8::String> source0 = v8::String::New("1234");
8978 v8::Handle<v8::String> source1 = v8::String::New("1234");
8979 v8::Handle<v8::Script> script0 =
8980 v8::Script::Compile(source0, v8::String::New("test.js"));
8981 v8::Handle<v8::Script> script1 =
8982 v8::Script::Compile(source1, v8::String::New("test.js"));
8983 v8::Handle<v8::Script> script2 =
8984 v8::Script::Compile(source0); // different origin
8985 CHECK_EQ(1234, script0->Run()->Int32Value());
8986 CHECK_EQ(1234, script1->Run()->Int32Value());
8987 CHECK_EQ(1234, script2->Run()->Int32Value());
8988}
8989
8990
8991static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8992 ApiTestFuzzer::Fuzz();
8993 return v8_num(42);
8994}
8995
8996
8997THREADED_TEST(CallbackFunctionName) {
8998 v8::HandleScope scope;
8999 LocalContext context;
9000 Local<ObjectTemplate> t = ObjectTemplate::New();
9001 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
9002 context->Global()->Set(v8_str("obj"), t->NewInstance());
9003 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
9004 CHECK(value->IsString());
9005 v8::String::AsciiValue name(value);
9006 CHECK_EQ("asdf", *name);
9007}
9008
9009
9010THREADED_TEST(DateAccess) {
9011 v8::HandleScope scope;
9012 LocalContext context;
9013 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
9014 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01009015 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00009016}
9017
9018
9019void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01009020 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009021 v8::Handle<v8::Array> props = obj->GetPropertyNames();
9022 CHECK_EQ(elmc, props->Length());
9023 for (int i = 0; i < elmc; i++) {
9024 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
9025 CHECK_EQ(elmv[i], *elm);
9026 }
9027}
9028
9029
9030THREADED_TEST(PropertyEnumeration) {
9031 v8::HandleScope scope;
9032 LocalContext context;
9033 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
9034 "var result = [];"
9035 "result[0] = {};"
9036 "result[1] = {a: 1, b: 2};"
9037 "result[2] = [1, 2, 3];"
9038 "var proto = {x: 1, y: 2, z: 3};"
9039 "var x = { __proto__: proto, w: 0, z: 1 };"
9040 "result[3] = x;"
9041 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01009042 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009043 CHECK_EQ(4, elms->Length());
9044 int elmc0 = 0;
9045 const char** elmv0 = NULL;
9046 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
9047 int elmc1 = 2;
9048 const char* elmv1[] = {"a", "b"};
9049 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
9050 int elmc2 = 3;
9051 const char* elmv2[] = {"0", "1", "2"};
9052 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
9053 int elmc3 = 4;
9054 const char* elmv3[] = {"w", "z", "x", "y"};
9055 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
9056}
9057
9058
Steve Blocka7e24c12009-10-30 11:49:00 +00009059static bool NamedSetAccessBlocker(Local<v8::Object> obj,
9060 Local<Value> name,
9061 v8::AccessType type,
9062 Local<Value> data) {
9063 return type != v8::ACCESS_SET;
9064}
9065
9066
9067static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
9068 uint32_t key,
9069 v8::AccessType type,
9070 Local<Value> data) {
9071 return type != v8::ACCESS_SET;
9072}
9073
9074
9075THREADED_TEST(DisableAccessChecksWhileConfiguring) {
9076 v8::HandleScope scope;
9077 LocalContext context;
9078 Local<ObjectTemplate> templ = ObjectTemplate::New();
9079 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9080 IndexedSetAccessBlocker);
9081 templ->Set(v8_str("x"), v8::True());
9082 Local<v8::Object> instance = templ->NewInstance();
9083 context->Global()->Set(v8_str("obj"), instance);
9084 Local<Value> value = CompileRun("obj.x");
9085 CHECK(value->BooleanValue());
9086}
9087
9088
9089static bool NamedGetAccessBlocker(Local<v8::Object> obj,
9090 Local<Value> name,
9091 v8::AccessType type,
9092 Local<Value> data) {
9093 return false;
9094}
9095
9096
9097static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
9098 uint32_t key,
9099 v8::AccessType type,
9100 Local<Value> data) {
9101 return false;
9102}
9103
9104
9105
9106THREADED_TEST(AccessChecksReenabledCorrectly) {
9107 v8::HandleScope scope;
9108 LocalContext context;
9109 Local<ObjectTemplate> templ = ObjectTemplate::New();
9110 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
9111 IndexedGetAccessBlocker);
9112 templ->Set(v8_str("a"), v8_str("a"));
9113 // Add more than 8 (see kMaxFastProperties) properties
9114 // so that the constructor will force copying map.
9115 // Cannot sprintf, gcc complains unsafety.
9116 char buf[4];
9117 for (char i = '0'; i <= '9' ; i++) {
9118 buf[0] = i;
9119 for (char j = '0'; j <= '9'; j++) {
9120 buf[1] = j;
9121 for (char k = '0'; k <= '9'; k++) {
9122 buf[2] = k;
9123 buf[3] = 0;
9124 templ->Set(v8_str(buf), v8::Number::New(k));
9125 }
9126 }
9127 }
9128
9129 Local<v8::Object> instance_1 = templ->NewInstance();
9130 context->Global()->Set(v8_str("obj_1"), instance_1);
9131
9132 Local<Value> value_1 = CompileRun("obj_1.a");
9133 CHECK(value_1->IsUndefined());
9134
9135 Local<v8::Object> instance_2 = templ->NewInstance();
9136 context->Global()->Set(v8_str("obj_2"), instance_2);
9137
9138 Local<Value> value_2 = CompileRun("obj_2.a");
9139 CHECK(value_2->IsUndefined());
9140}
9141
9142
9143// This tests that access check information remains on the global
9144// object template when creating contexts.
9145THREADED_TEST(AccessControlRepeatedContextCreation) {
9146 v8::HandleScope handle_scope;
9147 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9148 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
9149 IndexedSetAccessBlocker);
9150 i::Handle<i::ObjectTemplateInfo> internal_template =
9151 v8::Utils::OpenHandle(*global_template);
9152 CHECK(!internal_template->constructor()->IsUndefined());
9153 i::Handle<i::FunctionTemplateInfo> constructor(
9154 i::FunctionTemplateInfo::cast(internal_template->constructor()));
9155 CHECK(!constructor->access_check_info()->IsUndefined());
9156 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
9157 CHECK(!constructor->access_check_info()->IsUndefined());
9158}
9159
9160
9161THREADED_TEST(TurnOnAccessCheck) {
9162 v8::HandleScope handle_scope;
9163
9164 // Create an environment with access check to the global object disabled by
9165 // default.
9166 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9167 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
9168 IndexedGetAccessBlocker,
9169 v8::Handle<v8::Value>(),
9170 false);
9171 v8::Persistent<Context> context = Context::New(NULL, global_template);
9172 Context::Scope context_scope(context);
9173
9174 // Set up a property and a number of functions.
9175 context->Global()->Set(v8_str("a"), v8_num(1));
9176 CompileRun("function f1() {return a;}"
9177 "function f2() {return a;}"
9178 "function g1() {return h();}"
9179 "function g2() {return h();}"
9180 "function h() {return 1;}");
9181 Local<Function> f1 =
9182 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9183 Local<Function> f2 =
9184 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9185 Local<Function> g1 =
9186 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9187 Local<Function> g2 =
9188 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9189 Local<Function> h =
9190 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
9191
9192 // Get the global object.
9193 v8::Handle<v8::Object> global = context->Global();
9194
9195 // Call f1 one time and f2 a number of times. This will ensure that f1 still
9196 // uses the runtime system to retreive property a whereas f2 uses global load
9197 // inline cache.
9198 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
9199 for (int i = 0; i < 4; i++) {
9200 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
9201 }
9202
9203 // Same for g1 and g2.
9204 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
9205 for (int i = 0; i < 4; i++) {
9206 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
9207 }
9208
9209 // Detach the global and turn on access check.
9210 context->DetachGlobal();
9211 context->Global()->TurnOnAccessCheck();
9212
9213 // Failing access check to property get results in undefined.
9214 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9215 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9216
9217 // Failing access check to function call results in exception.
9218 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9219 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9220
9221 // No failing access check when just returning a constant.
9222 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
9223}
9224
9225
Ben Murdochb0fe1622011-05-05 13:52:32 +01009226v8::Handle<v8::String> a;
9227v8::Handle<v8::String> h;
9228
9229static bool NamedGetAccessBlockAandH(Local<v8::Object> obj,
9230 Local<Value> name,
9231 v8::AccessType type,
9232 Local<Value> data) {
9233 return !(name->Equals(a) || name->Equals(h));
9234}
9235
9236
9237THREADED_TEST(TurnOnAccessCheckAndRecompile) {
9238 v8::HandleScope handle_scope;
9239
9240 // Create an environment with access check to the global object disabled by
9241 // default. When the registered access checker will block access to properties
9242 // a and h
9243 a = v8_str("a");
9244 h = v8_str("h");
9245 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
9246 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH,
9247 IndexedGetAccessBlocker,
9248 v8::Handle<v8::Value>(),
9249 false);
9250 v8::Persistent<Context> context = Context::New(NULL, global_template);
9251 Context::Scope context_scope(context);
9252
9253 // Set up a property and a number of functions.
9254 context->Global()->Set(v8_str("a"), v8_num(1));
9255 static const char* source = "function f1() {return a;}"
9256 "function f2() {return a;}"
9257 "function g1() {return h();}"
9258 "function g2() {return h();}"
9259 "function h() {return 1;}";
9260
9261 CompileRun(source);
9262 Local<Function> f1;
9263 Local<Function> f2;
9264 Local<Function> g1;
9265 Local<Function> g2;
9266 Local<Function> h;
9267 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9268 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9269 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9270 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9271 h = Local<Function>::Cast(context->Global()->Get(v8_str("h")));
9272
9273 // Get the global object.
9274 v8::Handle<v8::Object> global = context->Global();
9275
9276 // Call f1 one time and f2 a number of times. This will ensure that f1 still
9277 // uses the runtime system to retreive property a whereas f2 uses global load
9278 // inline cache.
9279 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
9280 for (int i = 0; i < 4; i++) {
9281 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
9282 }
9283
9284 // Same for g1 and g2.
9285 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
9286 for (int i = 0; i < 4; i++) {
9287 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
9288 }
9289
9290 // Detach the global and turn on access check now blocking access to property
9291 // a and function h.
9292 context->DetachGlobal();
9293 context->Global()->TurnOnAccessCheck();
9294
9295 // Failing access check to property get results in undefined.
9296 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9297 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9298
9299 // Failing access check to function call results in exception.
9300 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9301 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9302
9303 // No failing access check when just returning a constant.
9304 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
9305
9306 // Now compile the source again. And get the newly compiled functions, except
9307 // for h for which access is blocked.
9308 CompileRun(source);
9309 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
9310 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
9311 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
9312 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
9313 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined());
9314
9315 // Failing access check to property get results in undefined.
9316 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
9317 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
9318
9319 // Failing access check to function call results in exception.
9320 CHECK(g1->Call(global, 0, NULL).IsEmpty());
9321 CHECK(g2->Call(global, 0, NULL).IsEmpty());
9322}
9323
9324
Steve Blocka7e24c12009-10-30 11:49:00 +00009325// This test verifies that pre-compilation (aka preparsing) can be called
9326// without initializing the whole VM. Thus we cannot run this test in a
9327// multi-threaded setup.
9328TEST(PreCompile) {
9329 // TODO(155): This test would break without the initialization of V8. This is
9330 // a workaround for now to make this test not fail.
9331 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009332 const char* script = "function foo(a) { return a+1; }";
9333 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00009334 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00009335 CHECK_NE(sd->Length(), 0);
9336 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00009337 CHECK(!sd->HasError());
9338 delete sd;
9339}
9340
9341
9342TEST(PreCompileWithError) {
9343 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009344 const char* script = "function foo(a) { return 1 * * 2; }";
9345 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00009346 v8::ScriptData::PreCompile(script, i::StrLength(script));
9347 CHECK(sd->HasError());
9348 delete sd;
9349}
9350
9351
9352TEST(Regress31661) {
9353 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01009354 const char* script = " The Definintive Guide";
9355 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00009356 v8::ScriptData::PreCompile(script, i::StrLength(script));
9357 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00009358 delete sd;
9359}
9360
9361
Leon Clarkef7060e22010-06-03 12:02:55 +01009362// Tests that ScriptData can be serialized and deserialized.
9363TEST(PreCompileSerialization) {
9364 v8::V8::Initialize();
9365 const char* script = "function foo(a) { return a+1; }";
9366 v8::ScriptData* sd =
9367 v8::ScriptData::PreCompile(script, i::StrLength(script));
9368
9369 // Serialize.
9370 int serialized_data_length = sd->Length();
9371 char* serialized_data = i::NewArray<char>(serialized_data_length);
9372 memcpy(serialized_data, sd->Data(), serialized_data_length);
9373
9374 // Deserialize.
9375 v8::ScriptData* deserialized_sd =
9376 v8::ScriptData::New(serialized_data, serialized_data_length);
9377
9378 // Verify that the original is the same as the deserialized.
9379 CHECK_EQ(sd->Length(), deserialized_sd->Length());
9380 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
9381 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
9382
9383 delete sd;
9384 delete deserialized_sd;
9385}
9386
9387
9388// Attempts to deserialize bad data.
9389TEST(PreCompileDeserializationError) {
9390 v8::V8::Initialize();
9391 const char* data = "DONT CARE";
9392 int invalid_size = 3;
9393 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
9394
9395 CHECK_EQ(0, sd->Length());
9396
9397 delete sd;
9398}
9399
9400
Leon Clarkeac952652010-07-15 11:15:24 +01009401// Attempts to deserialize bad data.
9402TEST(PreCompileInvalidPreparseDataError) {
9403 v8::V8::Initialize();
9404 v8::HandleScope scope;
9405 LocalContext context;
9406
9407 const char* script = "function foo(){ return 5;}\n"
9408 "function bar(){ return 6 + 7;} foo();";
9409 v8::ScriptData* sd =
9410 v8::ScriptData::PreCompile(script, i::StrLength(script));
9411 CHECK(!sd->HasError());
9412 // ScriptDataImpl private implementation details
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08009413 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize;
Iain Merrick9ac36c92010-09-13 15:29:50 +01009414 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +01009415 const int kFunctionEntryStartOffset = 0;
9416 const int kFunctionEntryEndOffset = 1;
9417 unsigned* sd_data =
9418 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01009419
9420 // Overwrite function bar's end position with 0.
9421 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
9422 v8::TryCatch try_catch;
9423
9424 Local<String> source = String::New(script);
9425 Local<Script> compiled_script = Script::New(source, NULL, sd);
9426 CHECK(try_catch.HasCaught());
9427 String::AsciiValue exception_value(try_catch.Message()->Get());
9428 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
9429 *exception_value);
9430
9431 try_catch.Reset();
9432 // Overwrite function bar's start position with 200. The function entry
9433 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01009434 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
9435 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01009436 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
9437 200;
9438 compiled_script = Script::New(source, NULL, sd);
9439 CHECK(try_catch.HasCaught());
9440 String::AsciiValue second_exception_value(try_catch.Message()->Get());
9441 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
9442 *second_exception_value);
9443
9444 delete sd;
9445}
9446
9447
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009448// Verifies that the Handle<String> and const char* versions of the API produce
9449// the same results (at least for one trivial case).
9450TEST(PreCompileAPIVariationsAreSame) {
9451 v8::V8::Initialize();
9452 v8::HandleScope scope;
9453
9454 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009455
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009456 v8::ScriptData* sd_from_cstring =
9457 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
9458
9459 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009460 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009461 v8::String::NewExternal(resource));
9462
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009463 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
9464 v8::String::New(cstring));
9465
9466 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009467 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009468 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009469 sd_from_cstring->Length()));
9470
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009471 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
9472 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
9473 sd_from_string->Data(),
9474 sd_from_cstring->Length()));
9475
9476
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009477 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009478 delete sd_from_external_string;
9479 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01009480}
9481
9482
Steve Blocka7e24c12009-10-30 11:49:00 +00009483// This tests that we do not allow dictionary load/call inline caches
9484// to use functions that have not yet been compiled. The potential
9485// problem of loading a function that has not yet been compiled can
9486// arise because we share code between contexts via the compilation
9487// cache.
9488THREADED_TEST(DictionaryICLoadedFunction) {
9489 v8::HandleScope scope;
9490 // Test LoadIC.
9491 for (int i = 0; i < 2; i++) {
9492 LocalContext context;
9493 context->Global()->Set(v8_str("tmp"), v8::True());
9494 context->Global()->Delete(v8_str("tmp"));
9495 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
9496 }
9497 // Test CallIC.
9498 for (int i = 0; i < 2; i++) {
9499 LocalContext context;
9500 context->Global()->Set(v8_str("tmp"), v8::True());
9501 context->Global()->Delete(v8_str("tmp"));
9502 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
9503 }
9504}
9505
9506
9507// Test that cross-context new calls use the context of the callee to
9508// create the new JavaScript object.
9509THREADED_TEST(CrossContextNew) {
9510 v8::HandleScope scope;
9511 v8::Persistent<Context> context0 = Context::New();
9512 v8::Persistent<Context> context1 = Context::New();
9513
9514 // Allow cross-domain access.
9515 Local<String> token = v8_str("<security token>");
9516 context0->SetSecurityToken(token);
9517 context1->SetSecurityToken(token);
9518
9519 // Set an 'x' property on the Object prototype and define a
9520 // constructor function in context0.
9521 context0->Enter();
9522 CompileRun("Object.prototype.x = 42; function C() {};");
9523 context0->Exit();
9524
9525 // Call the constructor function from context0 and check that the
9526 // result has the 'x' property.
9527 context1->Enter();
9528 context1->Global()->Set(v8_str("other"), context0->Global());
9529 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
9530 CHECK(value->IsInt32());
9531 CHECK_EQ(42, value->Int32Value());
9532 context1->Exit();
9533
9534 // Dispose the contexts to allow them to be garbage collected.
9535 context0.Dispose();
9536 context1.Dispose();
9537}
9538
9539
9540class RegExpInterruptTest {
9541 public:
9542 RegExpInterruptTest() : block_(NULL) {}
9543 ~RegExpInterruptTest() { delete block_; }
9544 void RunTest() {
9545 block_ = i::OS::CreateSemaphore(0);
9546 gc_count_ = 0;
9547 gc_during_regexp_ = 0;
9548 regexp_success_ = false;
9549 gc_success_ = false;
9550 GCThread gc_thread(this);
9551 gc_thread.Start();
9552 v8::Locker::StartPreemption(1);
9553
9554 LongRunningRegExp();
9555 {
9556 v8::Unlocker unlock;
9557 gc_thread.Join();
9558 }
9559 v8::Locker::StopPreemption();
9560 CHECK(regexp_success_);
9561 CHECK(gc_success_);
9562 }
9563 private:
9564 // Number of garbage collections required.
9565 static const int kRequiredGCs = 5;
9566
9567 class GCThread : public i::Thread {
9568 public:
9569 explicit GCThread(RegExpInterruptTest* test)
9570 : test_(test) {}
9571 virtual void Run() {
9572 test_->CollectGarbage();
9573 }
9574 private:
9575 RegExpInterruptTest* test_;
9576 };
9577
9578 void CollectGarbage() {
9579 block_->Wait();
9580 while (gc_during_regexp_ < kRequiredGCs) {
9581 {
9582 v8::Locker lock;
9583 // TODO(lrn): Perhaps create some garbage before collecting.
9584 i::Heap::CollectAllGarbage(false);
9585 gc_count_++;
9586 }
9587 i::OS::Sleep(1);
9588 }
9589 gc_success_ = true;
9590 }
9591
9592 void LongRunningRegExp() {
9593 block_->Signal(); // Enable garbage collection thread on next preemption.
9594 int rounds = 0;
9595 while (gc_during_regexp_ < kRequiredGCs) {
9596 int gc_before = gc_count_;
9597 {
9598 // Match 15-30 "a"'s against 14 and a "b".
9599 const char* c_source =
9600 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9601 ".exec('aaaaaaaaaaaaaaab') === null";
9602 Local<String> source = String::New(c_source);
9603 Local<Script> script = Script::Compile(source);
9604 Local<Value> result = script->Run();
9605 if (!result->BooleanValue()) {
9606 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
9607 return;
9608 }
9609 }
9610 {
9611 // Match 15-30 "a"'s against 15 and a "b".
9612 const char* c_source =
9613 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9614 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
9615 Local<String> source = String::New(c_source);
9616 Local<Script> script = Script::Compile(source);
9617 Local<Value> result = script->Run();
9618 if (!result->BooleanValue()) {
9619 gc_during_regexp_ = kRequiredGCs;
9620 return;
9621 }
9622 }
9623 int gc_after = gc_count_;
9624 gc_during_regexp_ += gc_after - gc_before;
9625 rounds++;
9626 i::OS::Sleep(1);
9627 }
9628 regexp_success_ = true;
9629 }
9630
9631 i::Semaphore* block_;
9632 int gc_count_;
9633 int gc_during_regexp_;
9634 bool regexp_success_;
9635 bool gc_success_;
9636};
9637
9638
9639// Test that a regular expression execution can be interrupted and
9640// survive a garbage collection.
9641TEST(RegExpInterruption) {
9642 v8::Locker lock;
9643 v8::V8::Initialize();
9644 v8::HandleScope scope;
9645 Local<Context> local_env;
9646 {
9647 LocalContext env;
9648 local_env = env.local();
9649 }
9650
9651 // Local context should still be live.
9652 CHECK(!local_env.IsEmpty());
9653 local_env->Enter();
9654
9655 // Should complete without problems.
9656 RegExpInterruptTest().RunTest();
9657
9658 local_env->Exit();
9659}
9660
9661
9662class ApplyInterruptTest {
9663 public:
9664 ApplyInterruptTest() : block_(NULL) {}
9665 ~ApplyInterruptTest() { delete block_; }
9666 void RunTest() {
9667 block_ = i::OS::CreateSemaphore(0);
9668 gc_count_ = 0;
9669 gc_during_apply_ = 0;
9670 apply_success_ = false;
9671 gc_success_ = false;
9672 GCThread gc_thread(this);
9673 gc_thread.Start();
9674 v8::Locker::StartPreemption(1);
9675
9676 LongRunningApply();
9677 {
9678 v8::Unlocker unlock;
9679 gc_thread.Join();
9680 }
9681 v8::Locker::StopPreemption();
9682 CHECK(apply_success_);
9683 CHECK(gc_success_);
9684 }
9685 private:
9686 // Number of garbage collections required.
9687 static const int kRequiredGCs = 2;
9688
9689 class GCThread : public i::Thread {
9690 public:
9691 explicit GCThread(ApplyInterruptTest* test)
9692 : test_(test) {}
9693 virtual void Run() {
9694 test_->CollectGarbage();
9695 }
9696 private:
9697 ApplyInterruptTest* test_;
9698 };
9699
9700 void CollectGarbage() {
9701 block_->Wait();
9702 while (gc_during_apply_ < kRequiredGCs) {
9703 {
9704 v8::Locker lock;
9705 i::Heap::CollectAllGarbage(false);
9706 gc_count_++;
9707 }
9708 i::OS::Sleep(1);
9709 }
9710 gc_success_ = true;
9711 }
9712
9713 void LongRunningApply() {
9714 block_->Signal();
9715 int rounds = 0;
9716 while (gc_during_apply_ < kRequiredGCs) {
9717 int gc_before = gc_count_;
9718 {
9719 const char* c_source =
9720 "function do_very_little(bar) {"
9721 " this.foo = bar;"
9722 "}"
9723 "for (var i = 0; i < 100000; i++) {"
9724 " do_very_little.apply(this, ['bar']);"
9725 "}";
9726 Local<String> source = String::New(c_source);
9727 Local<Script> script = Script::Compile(source);
9728 Local<Value> result = script->Run();
9729 // Check that no exception was thrown.
9730 CHECK(!result.IsEmpty());
9731 }
9732 int gc_after = gc_count_;
9733 gc_during_apply_ += gc_after - gc_before;
9734 rounds++;
9735 }
9736 apply_success_ = true;
9737 }
9738
9739 i::Semaphore* block_;
9740 int gc_count_;
9741 int gc_during_apply_;
9742 bool apply_success_;
9743 bool gc_success_;
9744};
9745
9746
9747// Test that nothing bad happens if we get a preemption just when we were
9748// about to do an apply().
9749TEST(ApplyInterruption) {
9750 v8::Locker lock;
9751 v8::V8::Initialize();
9752 v8::HandleScope scope;
9753 Local<Context> local_env;
9754 {
9755 LocalContext env;
9756 local_env = env.local();
9757 }
9758
9759 // Local context should still be live.
9760 CHECK(!local_env.IsEmpty());
9761 local_env->Enter();
9762
9763 // Should complete without problems.
9764 ApplyInterruptTest().RunTest();
9765
9766 local_env->Exit();
9767}
9768
9769
9770// Verify that we can clone an object
9771TEST(ObjectClone) {
9772 v8::HandleScope scope;
9773 LocalContext env;
9774
9775 const char* sample =
9776 "var rv = {};" \
9777 "rv.alpha = 'hello';" \
9778 "rv.beta = 123;" \
9779 "rv;";
9780
9781 // Create an object, verify basics.
9782 Local<Value> val = CompileRun(sample);
9783 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01009784 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009785 obj->Set(v8_str("gamma"), v8_str("cloneme"));
9786
9787 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
9788 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9789 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
9790
9791 // Clone it.
9792 Local<v8::Object> clone = obj->Clone();
9793 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
9794 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
9795 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
9796
9797 // Set a property on the clone, verify each object.
9798 clone->Set(v8_str("beta"), v8::Integer::New(456));
9799 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9800 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
9801}
9802
9803
9804class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
9805 public:
9806 explicit AsciiVectorResource(i::Vector<const char> vector)
9807 : data_(vector) {}
9808 virtual ~AsciiVectorResource() {}
9809 virtual size_t length() const { return data_.length(); }
9810 virtual const char* data() const { return data_.start(); }
9811 private:
9812 i::Vector<const char> data_;
9813};
9814
9815
9816class UC16VectorResource : public v8::String::ExternalStringResource {
9817 public:
9818 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
9819 : data_(vector) {}
9820 virtual ~UC16VectorResource() {}
9821 virtual size_t length() const { return data_.length(); }
9822 virtual const i::uc16* data() const { return data_.start(); }
9823 private:
9824 i::Vector<const i::uc16> data_;
9825};
9826
9827
9828static void MorphAString(i::String* string,
9829 AsciiVectorResource* ascii_resource,
9830 UC16VectorResource* uc16_resource) {
9831 CHECK(i::StringShape(string).IsExternal());
9832 if (string->IsAsciiRepresentation()) {
9833 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009834 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009835 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009836 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009837 i::ExternalTwoByteString* morphed =
9838 i::ExternalTwoByteString::cast(string);
9839 morphed->set_resource(uc16_resource);
9840 } else {
9841 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009842 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009843 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009844 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009845 i::ExternalAsciiString* morphed =
9846 i::ExternalAsciiString::cast(string);
9847 morphed->set_resource(ascii_resource);
9848 }
9849}
9850
9851
9852// Test that we can still flatten a string if the components it is built up
9853// from have been turned into 16 bit strings in the mean time.
9854THREADED_TEST(MorphCompositeStringTest) {
9855 const char* c_string = "Now is the time for all good men"
9856 " to come to the aid of the party";
9857 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9858 {
9859 v8::HandleScope scope;
9860 LocalContext env;
9861 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009862 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009863 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009864 i::Vector<const uint16_t>(two_byte_string,
9865 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009866
9867 Local<String> lhs(v8::Utils::ToLocal(
9868 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9869 Local<String> rhs(v8::Utils::ToLocal(
9870 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9871
9872 env->Global()->Set(v8_str("lhs"), lhs);
9873 env->Global()->Set(v8_str("rhs"), rhs);
9874
9875 CompileRun(
9876 "var cons = lhs + rhs;"
9877 "var slice = lhs.substring(1, lhs.length - 1);"
9878 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
9879
9880 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
9881 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
9882
9883 // Now do some stuff to make sure the strings are flattened, etc.
9884 CompileRun(
9885 "/[^a-z]/.test(cons);"
9886 "/[^a-z]/.test(slice);"
9887 "/[^a-z]/.test(slice_on_cons);");
9888 const char* expected_cons =
9889 "Now is the time for all good men to come to the aid of the party"
9890 "Now is the time for all good men to come to the aid of the party";
9891 const char* expected_slice =
9892 "ow is the time for all good men to come to the aid of the part";
9893 const char* expected_slice_on_cons =
9894 "ow is the time for all good men to come to the aid of the party"
9895 "Now is the time for all good men to come to the aid of the part";
9896 CHECK_EQ(String::New(expected_cons),
9897 env->Global()->Get(v8_str("cons")));
9898 CHECK_EQ(String::New(expected_slice),
9899 env->Global()->Get(v8_str("slice")));
9900 CHECK_EQ(String::New(expected_slice_on_cons),
9901 env->Global()->Get(v8_str("slice_on_cons")));
9902 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009903 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009904}
9905
9906
9907TEST(CompileExternalTwoByteSource) {
9908 v8::HandleScope scope;
9909 LocalContext context;
9910
9911 // This is a very short list of sources, which currently is to check for a
9912 // regression caused by r2703.
9913 const char* ascii_sources[] = {
9914 "0.5",
9915 "-0.5", // This mainly testes PushBack in the Scanner.
9916 "--0.5", // This mainly testes PushBack in the Scanner.
9917 NULL
9918 };
9919
9920 // Compile the sources as external two byte strings.
9921 for (int i = 0; ascii_sources[i] != NULL; i++) {
9922 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9923 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009924 i::Vector<const uint16_t>(two_byte_string,
9925 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009926 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9927 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009928 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009929 }
9930}
9931
9932
9933class RegExpStringModificationTest {
9934 public:
9935 RegExpStringModificationTest()
9936 : block_(i::OS::CreateSemaphore(0)),
9937 morphs_(0),
9938 morphs_during_regexp_(0),
9939 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9940 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9941 ~RegExpStringModificationTest() { delete block_; }
9942 void RunTest() {
9943 regexp_success_ = false;
9944 morph_success_ = false;
9945
9946 // Initialize the contents of two_byte_content_ to be a uc16 representation
9947 // of "aaaaaaaaaaaaaab".
9948 for (int i = 0; i < 14; i++) {
9949 two_byte_content_[i] = 'a';
9950 }
9951 two_byte_content_[14] = 'b';
9952
9953 // Create the input string for the regexp - the one we are going to change
9954 // properties of.
9955 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9956
9957 // Inject the input as a global variable.
9958 i::Handle<i::String> input_name =
9959 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
John Reck59135872010-11-02 12:39:01 -07009960 i::Top::global_context()->global()->SetProperty(*input_name,
9961 *input_,
9962 NONE)->ToObjectChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00009963
9964
9965 MorphThread morph_thread(this);
9966 morph_thread.Start();
9967 v8::Locker::StartPreemption(1);
9968 LongRunningRegExp();
9969 {
9970 v8::Unlocker unlock;
9971 morph_thread.Join();
9972 }
9973 v8::Locker::StopPreemption();
9974 CHECK(regexp_success_);
9975 CHECK(morph_success_);
9976 }
9977 private:
9978
9979 // Number of string modifications required.
9980 static const int kRequiredModifications = 5;
9981 static const int kMaxModifications = 100;
9982
9983 class MorphThread : public i::Thread {
9984 public:
9985 explicit MorphThread(RegExpStringModificationTest* test)
9986 : test_(test) {}
9987 virtual void Run() {
9988 test_->MorphString();
9989 }
9990 private:
9991 RegExpStringModificationTest* test_;
9992 };
9993
9994 void MorphString() {
9995 block_->Wait();
9996 while (morphs_during_regexp_ < kRequiredModifications &&
9997 morphs_ < kMaxModifications) {
9998 {
9999 v8::Locker lock;
10000 // Swap string between ascii and two-byte representation.
10001 i::String* string = *input_;
10002 MorphAString(string, &ascii_resource_, &uc16_resource_);
10003 morphs_++;
10004 }
10005 i::OS::Sleep(1);
10006 }
10007 morph_success_ = true;
10008 }
10009
10010 void LongRunningRegExp() {
10011 block_->Signal(); // Enable morphing thread on next preemption.
10012 while (morphs_during_regexp_ < kRequiredModifications &&
10013 morphs_ < kMaxModifications) {
10014 int morphs_before = morphs_;
10015 {
Steve Block791712a2010-08-27 10:21:07 +010010016 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +000010017 // Match 15-30 "a"'s against 14 and a "b".
10018 const char* c_source =
10019 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
10020 ".exec(input) === null";
10021 Local<String> source = String::New(c_source);
10022 Local<Script> script = Script::Compile(source);
10023 Local<Value> result = script->Run();
10024 CHECK(result->IsTrue());
10025 }
10026 int morphs_after = morphs_;
10027 morphs_during_regexp_ += morphs_after - morphs_before;
10028 }
10029 regexp_success_ = true;
10030 }
10031
10032 i::uc16 two_byte_content_[15];
10033 i::Semaphore* block_;
10034 int morphs_;
10035 int morphs_during_regexp_;
10036 bool regexp_success_;
10037 bool morph_success_;
10038 i::Handle<i::String> input_;
10039 AsciiVectorResource ascii_resource_;
10040 UC16VectorResource uc16_resource_;
10041};
10042
10043
10044// Test that a regular expression execution can be interrupted and
10045// the string changed without failing.
10046TEST(RegExpStringModification) {
10047 v8::Locker lock;
10048 v8::V8::Initialize();
10049 v8::HandleScope scope;
10050 Local<Context> local_env;
10051 {
10052 LocalContext env;
10053 local_env = env.local();
10054 }
10055
10056 // Local context should still be live.
10057 CHECK(!local_env.IsEmpty());
10058 local_env->Enter();
10059
10060 // Should complete without problems.
10061 RegExpStringModificationTest().RunTest();
10062
10063 local_env->Exit();
10064}
10065
10066
10067// Test that we can set a property on the global object even if there
10068// is a read-only property in the prototype chain.
10069TEST(ReadOnlyPropertyInGlobalProto) {
10070 v8::HandleScope scope;
10071 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10072 LocalContext context(0, templ);
10073 v8::Handle<v8::Object> global = context->Global();
10074 v8::Handle<v8::Object> global_proto =
10075 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
10076 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
10077 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
10078 // Check without 'eval' or 'with'.
10079 v8::Handle<v8::Value> res =
10080 CompileRun("function f() { x = 42; return x; }; f()");
10081 // Check with 'eval'.
10082 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
10083 CHECK_EQ(v8::Integer::New(42), res);
10084 // Check with 'with'.
10085 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
10086 CHECK_EQ(v8::Integer::New(42), res);
10087}
10088
10089static int force_set_set_count = 0;
10090static int force_set_get_count = 0;
10091bool pass_on_get = false;
10092
10093static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
10094 const v8::AccessorInfo& info) {
10095 force_set_get_count++;
10096 if (pass_on_get) {
10097 return v8::Handle<v8::Value>();
10098 } else {
10099 return v8::Int32::New(3);
10100 }
10101}
10102
10103static void ForceSetSetter(v8::Local<v8::String> name,
10104 v8::Local<v8::Value> value,
10105 const v8::AccessorInfo& info) {
10106 force_set_set_count++;
10107}
10108
10109static v8::Handle<v8::Value> ForceSetInterceptSetter(
10110 v8::Local<v8::String> name,
10111 v8::Local<v8::Value> value,
10112 const v8::AccessorInfo& info) {
10113 force_set_set_count++;
10114 return v8::Undefined();
10115}
10116
10117TEST(ForceSet) {
10118 force_set_get_count = 0;
10119 force_set_set_count = 0;
10120 pass_on_get = false;
10121
10122 v8::HandleScope scope;
10123 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10124 v8::Handle<v8::String> access_property = v8::String::New("a");
10125 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
10126 LocalContext context(NULL, templ);
10127 v8::Handle<v8::Object> global = context->Global();
10128
10129 // Ordinary properties
10130 v8::Handle<v8::String> simple_property = v8::String::New("p");
10131 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
10132 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10133 // This should fail because the property is read-only
10134 global->Set(simple_property, v8::Int32::New(5));
10135 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10136 // This should succeed even though the property is read-only
10137 global->ForceSet(simple_property, v8::Int32::New(6));
10138 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
10139
10140 // Accessors
10141 CHECK_EQ(0, force_set_set_count);
10142 CHECK_EQ(0, force_set_get_count);
10143 CHECK_EQ(3, global->Get(access_property)->Int32Value());
10144 // CHECK_EQ the property shouldn't override it, just call the setter
10145 // which in this case does nothing.
10146 global->Set(access_property, v8::Int32::New(7));
10147 CHECK_EQ(3, global->Get(access_property)->Int32Value());
10148 CHECK_EQ(1, force_set_set_count);
10149 CHECK_EQ(2, force_set_get_count);
10150 // Forcing the property to be set should override the accessor without
10151 // calling it
10152 global->ForceSet(access_property, v8::Int32::New(8));
10153 CHECK_EQ(8, global->Get(access_property)->Int32Value());
10154 CHECK_EQ(1, force_set_set_count);
10155 CHECK_EQ(2, force_set_get_count);
10156}
10157
10158TEST(ForceSetWithInterceptor) {
10159 force_set_get_count = 0;
10160 force_set_set_count = 0;
10161 pass_on_get = false;
10162
10163 v8::HandleScope scope;
10164 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10165 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
10166 LocalContext context(NULL, templ);
10167 v8::Handle<v8::Object> global = context->Global();
10168
10169 v8::Handle<v8::String> some_property = v8::String::New("a");
10170 CHECK_EQ(0, force_set_set_count);
10171 CHECK_EQ(0, force_set_get_count);
10172 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10173 // Setting the property shouldn't override it, just call the setter
10174 // which in this case does nothing.
10175 global->Set(some_property, v8::Int32::New(7));
10176 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10177 CHECK_EQ(1, force_set_set_count);
10178 CHECK_EQ(2, force_set_get_count);
10179 // Getting the property when the interceptor returns an empty handle
10180 // should yield undefined, since the property isn't present on the
10181 // object itself yet.
10182 pass_on_get = true;
10183 CHECK(global->Get(some_property)->IsUndefined());
10184 CHECK_EQ(1, force_set_set_count);
10185 CHECK_EQ(3, force_set_get_count);
10186 // Forcing the property to be set should cause the value to be
10187 // set locally without calling the interceptor.
10188 global->ForceSet(some_property, v8::Int32::New(8));
10189 CHECK_EQ(8, global->Get(some_property)->Int32Value());
10190 CHECK_EQ(1, force_set_set_count);
10191 CHECK_EQ(4, force_set_get_count);
10192 // Reenabling the interceptor should cause it to take precedence over
10193 // the property
10194 pass_on_get = false;
10195 CHECK_EQ(3, global->Get(some_property)->Int32Value());
10196 CHECK_EQ(1, force_set_set_count);
10197 CHECK_EQ(5, force_set_get_count);
10198 // The interceptor should also work for other properties
10199 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
10200 CHECK_EQ(1, force_set_set_count);
10201 CHECK_EQ(6, force_set_get_count);
10202}
10203
10204
10205THREADED_TEST(ForceDelete) {
10206 v8::HandleScope scope;
10207 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10208 LocalContext context(NULL, templ);
10209 v8::Handle<v8::Object> global = context->Global();
10210
10211 // Ordinary properties
10212 v8::Handle<v8::String> simple_property = v8::String::New("p");
10213 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
10214 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10215 // This should fail because the property is dont-delete.
10216 CHECK(!global->Delete(simple_property));
10217 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
10218 // This should succeed even though the property is dont-delete.
10219 CHECK(global->ForceDelete(simple_property));
10220 CHECK(global->Get(simple_property)->IsUndefined());
10221}
10222
10223
10224static int force_delete_interceptor_count = 0;
10225static bool pass_on_delete = false;
10226
10227
10228static v8::Handle<v8::Boolean> ForceDeleteDeleter(
10229 v8::Local<v8::String> name,
10230 const v8::AccessorInfo& info) {
10231 force_delete_interceptor_count++;
10232 if (pass_on_delete) {
10233 return v8::Handle<v8::Boolean>();
10234 } else {
10235 return v8::True();
10236 }
10237}
10238
10239
10240THREADED_TEST(ForceDeleteWithInterceptor) {
10241 force_delete_interceptor_count = 0;
10242 pass_on_delete = false;
10243
10244 v8::HandleScope scope;
10245 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
10246 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
10247 LocalContext context(NULL, templ);
10248 v8::Handle<v8::Object> global = context->Global();
10249
10250 v8::Handle<v8::String> some_property = v8::String::New("a");
10251 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
10252
10253 // Deleting a property should get intercepted and nothing should
10254 // happen.
10255 CHECK_EQ(0, force_delete_interceptor_count);
10256 CHECK(global->Delete(some_property));
10257 CHECK_EQ(1, force_delete_interceptor_count);
10258 CHECK_EQ(42, global->Get(some_property)->Int32Value());
10259 // Deleting the property when the interceptor returns an empty
10260 // handle should not delete the property since it is DontDelete.
10261 pass_on_delete = true;
10262 CHECK(!global->Delete(some_property));
10263 CHECK_EQ(2, force_delete_interceptor_count);
10264 CHECK_EQ(42, global->Get(some_property)->Int32Value());
10265 // Forcing the property to be deleted should delete the value
10266 // without calling the interceptor.
10267 CHECK(global->ForceDelete(some_property));
10268 CHECK(global->Get(some_property)->IsUndefined());
10269 CHECK_EQ(2, force_delete_interceptor_count);
10270}
10271
10272
10273// Make sure that forcing a delete invalidates any IC stubs, so we
10274// don't read the hole value.
10275THREADED_TEST(ForceDeleteIC) {
10276 v8::HandleScope scope;
10277 LocalContext context;
10278 // Create a DontDelete variable on the global object.
10279 CompileRun("this.__proto__ = { foo: 'horse' };"
10280 "var foo = 'fish';"
10281 "function f() { return foo.length; }");
10282 // Initialize the IC for foo in f.
10283 CompileRun("for (var i = 0; i < 4; i++) f();");
10284 // Make sure the value of foo is correct before the deletion.
10285 CHECK_EQ(4, CompileRun("f()")->Int32Value());
10286 // Force the deletion of foo.
10287 CHECK(context->Global()->ForceDelete(v8_str("foo")));
10288 // Make sure the value for foo is read from the prototype, and that
10289 // we don't get in trouble with reading the deleted cell value
10290 // sentinel.
10291 CHECK_EQ(5, CompileRun("f()")->Int32Value());
10292}
10293
10294
10295v8::Persistent<Context> calling_context0;
10296v8::Persistent<Context> calling_context1;
10297v8::Persistent<Context> calling_context2;
10298
10299
10300// Check that the call to the callback is initiated in
10301// calling_context2, the directly calling context is calling_context1
10302// and the callback itself is in calling_context0.
10303static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
10304 ApiTestFuzzer::Fuzz();
10305 CHECK(Context::GetCurrent() == calling_context0);
10306 CHECK(Context::GetCalling() == calling_context1);
10307 CHECK(Context::GetEntered() == calling_context2);
10308 return v8::Integer::New(42);
10309}
10310
10311
10312THREADED_TEST(GetCallingContext) {
10313 v8::HandleScope scope;
10314
10315 calling_context0 = Context::New();
10316 calling_context1 = Context::New();
10317 calling_context2 = Context::New();
10318
10319 // Allow cross-domain access.
10320 Local<String> token = v8_str("<security token>");
10321 calling_context0->SetSecurityToken(token);
10322 calling_context1->SetSecurityToken(token);
10323 calling_context2->SetSecurityToken(token);
10324
10325 // Create an object with a C++ callback in context0.
10326 calling_context0->Enter();
10327 Local<v8::FunctionTemplate> callback_templ =
10328 v8::FunctionTemplate::New(GetCallingContextCallback);
10329 calling_context0->Global()->Set(v8_str("callback"),
10330 callback_templ->GetFunction());
10331 calling_context0->Exit();
10332
10333 // Expose context0 in context1 and setup a function that calls the
10334 // callback function.
10335 calling_context1->Enter();
10336 calling_context1->Global()->Set(v8_str("context0"),
10337 calling_context0->Global());
10338 CompileRun("function f() { context0.callback() }");
10339 calling_context1->Exit();
10340
10341 // Expose context1 in context2 and call the callback function in
10342 // context0 indirectly through f in context1.
10343 calling_context2->Enter();
10344 calling_context2->Global()->Set(v8_str("context1"),
10345 calling_context1->Global());
10346 CompileRun("context1.f()");
10347 calling_context2->Exit();
10348
10349 // Dispose the contexts to allow them to be garbage collected.
10350 calling_context0.Dispose();
10351 calling_context1.Dispose();
10352 calling_context2.Dispose();
10353 calling_context0.Clear();
10354 calling_context1.Clear();
10355 calling_context2.Clear();
10356}
10357
10358
10359// Check that a variable declaration with no explicit initialization
10360// value does not shadow an existing property in the prototype chain.
10361//
10362// This is consistent with Firefox and Safari.
10363//
10364// See http://crbug.com/12548.
10365THREADED_TEST(InitGlobalVarInProtoChain) {
10366 v8::HandleScope scope;
10367 LocalContext context;
10368 // Introduce a variable in the prototype chain.
10369 CompileRun("__proto__.x = 42");
10370 v8::Handle<v8::Value> result = CompileRun("var x; x");
10371 CHECK(!result->IsUndefined());
10372 CHECK_EQ(42, result->Int32Value());
10373}
10374
10375
10376// Regression test for issue 398.
10377// If a function is added to an object, creating a constant function
10378// field, and the result is cloned, replacing the constant function on the
10379// original should not affect the clone.
10380// See http://code.google.com/p/v8/issues/detail?id=398
10381THREADED_TEST(ReplaceConstantFunction) {
10382 v8::HandleScope scope;
10383 LocalContext context;
10384 v8::Handle<v8::Object> obj = v8::Object::New();
10385 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
10386 v8::Handle<v8::String> foo_string = v8::String::New("foo");
10387 obj->Set(foo_string, func_templ->GetFunction());
10388 v8::Handle<v8::Object> obj_clone = obj->Clone();
10389 obj_clone->Set(foo_string, v8::String::New("Hello"));
10390 CHECK(!obj->Get(foo_string)->IsUndefined());
10391}
10392
10393
10394// Regression test for http://crbug.com/16276.
10395THREADED_TEST(Regress16276) {
10396 v8::HandleScope scope;
10397 LocalContext context;
10398 // Force the IC in f to be a dictionary load IC.
10399 CompileRun("function f(obj) { return obj.x; }\n"
10400 "var obj = { x: { foo: 42 }, y: 87 };\n"
10401 "var x = obj.x;\n"
10402 "delete obj.y;\n"
10403 "for (var i = 0; i < 5; i++) f(obj);");
10404 // Detach the global object to make 'this' refer directly to the
10405 // global object (not the proxy), and make sure that the dictionary
10406 // load IC doesn't mess up loading directly from the global object.
10407 context->DetachGlobal();
10408 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
10409}
10410
10411
10412THREADED_TEST(PixelArray) {
10413 v8::HandleScope scope;
10414 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +000010415 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +000010416 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
10417 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
10418 pixel_data);
10419 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10420 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000010421 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +000010422 }
10423 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10424 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +000010425 CHECK_EQ(i % 256, pixels->get(i));
10426 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +000010427 }
10428
10429 v8::Handle<v8::Object> obj = v8::Object::New();
10430 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
10431 // Set the elements to be the pixels.
10432 // jsobj->set_elements(*pixels);
10433 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
John Reck59135872010-11-02 12:39:01 -070010434 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010435 obj->Set(v8_str("field"), v8::Int32::New(1503));
10436 context->Global()->Set(v8_str("pixels"), obj);
10437 v8::Handle<v8::Value> result = CompileRun("pixels.field");
10438 CHECK_EQ(1503, result->Int32Value());
10439 result = CompileRun("pixels[1]");
10440 CHECK_EQ(1, result->Int32Value());
10441
10442 result = CompileRun("var sum = 0;"
10443 "for (var i = 0; i < 8; i++) {"
10444 " sum += pixels[i] = pixels[i] = -i;"
10445 "}"
10446 "sum;");
10447 CHECK_EQ(-28, result->Int32Value());
10448
10449 result = CompileRun("var sum = 0;"
10450 "for (var i = 0; i < 8; i++) {"
10451 " sum += pixels[i] = pixels[i] = 0;"
10452 "}"
10453 "sum;");
10454 CHECK_EQ(0, result->Int32Value());
10455
10456 result = CompileRun("var sum = 0;"
10457 "for (var i = 0; i < 8; i++) {"
10458 " sum += pixels[i] = pixels[i] = 255;"
10459 "}"
10460 "sum;");
10461 CHECK_EQ(8 * 255, result->Int32Value());
10462
10463 result = CompileRun("var sum = 0;"
10464 "for (var i = 0; i < 8; i++) {"
10465 " sum += pixels[i] = pixels[i] = 256 + i;"
10466 "}"
10467 "sum;");
10468 CHECK_EQ(2076, result->Int32Value());
10469
10470 result = CompileRun("var sum = 0;"
10471 "for (var i = 0; i < 8; i++) {"
10472 " sum += pixels[i] = pixels[i] = i;"
10473 "}"
10474 "sum;");
10475 CHECK_EQ(28, result->Int32Value());
10476
10477 result = CompileRun("var sum = 0;"
10478 "for (var i = 0; i < 8; i++) {"
10479 " sum += pixels[i];"
10480 "}"
10481 "sum;");
10482 CHECK_EQ(28, result->Int32Value());
10483
10484 i::Handle<i::Smi> value(i::Smi::FromInt(2));
10485 i::SetElement(jsobj, 1, value);
John Reck59135872010-11-02 12:39:01 -070010486 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010487 *value.location() = i::Smi::FromInt(256);
10488 i::SetElement(jsobj, 1, value);
John Reck59135872010-11-02 12:39:01 -070010489 CHECK_EQ(255,
10490 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010491 *value.location() = i::Smi::FromInt(-1);
10492 i::SetElement(jsobj, 1, value);
John Reck59135872010-11-02 12:39:01 -070010493 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010494
10495 result = CompileRun("for (var i = 0; i < 8; i++) {"
10496 " pixels[i] = (i * 65) - 109;"
10497 "}"
10498 "pixels[1] + pixels[6];");
10499 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010500 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
10501 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
10502 CHECK_EQ(21,
10503 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
10504 CHECK_EQ(86,
10505 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
10506 CHECK_EQ(151,
10507 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
10508 CHECK_EQ(216,
10509 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
10510 CHECK_EQ(255,
10511 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
10512 CHECK_EQ(255,
10513 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010514 result = CompileRun("var sum = 0;"
10515 "for (var i = 0; i < 8; i++) {"
10516 " sum += pixels[i];"
10517 "}"
10518 "sum;");
10519 CHECK_EQ(984, result->Int32Value());
10520
10521 result = CompileRun("for (var i = 0; i < 8; i++) {"
10522 " pixels[i] = (i * 1.1);"
10523 "}"
10524 "pixels[1] + pixels[6];");
10525 CHECK_EQ(8, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010526 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
10527 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
10528 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
10529 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
10530 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
10531 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
10532 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
10533 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010534
10535 result = CompileRun("for (var i = 0; i < 8; i++) {"
10536 " pixels[7] = undefined;"
10537 "}"
10538 "pixels[7];");
10539 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010540 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010541
10542 result = CompileRun("for (var i = 0; i < 8; i++) {"
10543 " pixels[6] = '2.3';"
10544 "}"
10545 "pixels[6];");
10546 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010547 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010548
10549 result = CompileRun("for (var i = 0; i < 8; i++) {"
10550 " pixels[5] = NaN;"
10551 "}"
10552 "pixels[5];");
10553 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010554 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010555
10556 result = CompileRun("for (var i = 0; i < 8; i++) {"
10557 " pixels[8] = Infinity;"
10558 "}"
10559 "pixels[8];");
10560 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010561 CHECK_EQ(255,
10562 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010563
10564 result = CompileRun("for (var i = 0; i < 8; i++) {"
10565 " pixels[9] = -Infinity;"
10566 "}"
10567 "pixels[9];");
10568 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010569 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +000010570
10571 result = CompileRun("pixels[3] = 33;"
10572 "delete pixels[3];"
10573 "pixels[3];");
10574 CHECK_EQ(33, result->Int32Value());
10575
10576 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
10577 "pixels[2] = 12; pixels[3] = 13;"
10578 "pixels.__defineGetter__('2',"
10579 "function() { return 120; });"
10580 "pixels[2];");
10581 CHECK_EQ(12, result->Int32Value());
10582
10583 result = CompileRun("var js_array = new Array(40);"
10584 "js_array[0] = 77;"
10585 "js_array;");
10586 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10587
10588 result = CompileRun("pixels[1] = 23;"
10589 "pixels.__proto__ = [];"
10590 "js_array.__proto__ = pixels;"
10591 "js_array.concat(pixels);");
10592 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10593 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10594
10595 result = CompileRun("pixels[1] = 23;");
10596 CHECK_EQ(23, result->Int32Value());
10597
Steve Blockd0582a62009-12-15 09:54:21 +000010598 // Test for index greater than 255. Regression test for:
10599 // http://code.google.com/p/chromium/issues/detail?id=26337.
10600 result = CompileRun("pixels[256] = 255;");
10601 CHECK_EQ(255, result->Int32Value());
10602 result = CompileRun("var i = 0;"
10603 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
10604 "i");
10605 CHECK_EQ(255, result->Int32Value());
10606
Steve Block1e0659c2011-05-24 12:43:12 +010010607 // Make sure that pixel array ICs recognize when a non-pixel array
10608 // is passed to it.
10609 result = CompileRun("function pa_load(p) {"
10610 " var sum = 0;"
10611 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
10612 " return sum;"
10613 "}"
10614 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10615 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
10616 "just_ints = new Object();"
10617 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10618 "for (var i = 0; i < 10; ++i) {"
10619 " result = pa_load(just_ints);"
10620 "}"
10621 "result");
10622 CHECK_EQ(32640, result->Int32Value());
10623
10624 // Make sure that pixel array ICs recognize out-of-bound accesses.
10625 result = CompileRun("function pa_load(p, start) {"
10626 " var sum = 0;"
10627 " for (var j = start; j < 256; j++) { sum += p[j]; }"
10628 " return sum;"
10629 "}"
10630 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10631 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
10632 "for (var i = 0; i < 10; ++i) {"
10633 " result = pa_load(pixels,-10);"
10634 "}"
10635 "result");
10636 CHECK_EQ(0, result->Int32Value());
10637
10638 // Make sure that generic ICs properly handles a pixel array.
10639 result = CompileRun("function pa_load(p) {"
10640 " var sum = 0;"
10641 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
10642 " return sum;"
10643 "}"
10644 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10645 "just_ints = new Object();"
10646 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10647 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
10648 "for (var i = 0; i < 10; ++i) {"
10649 " result = pa_load(pixels);"
10650 "}"
10651 "result");
10652 CHECK_EQ(32640, result->Int32Value());
10653
10654 // Make sure that generic load ICs recognize out-of-bound accesses in
10655 // pixel arrays.
10656 result = CompileRun("function pa_load(p, start) {"
10657 " var sum = 0;"
10658 " for (var j = start; j < 256; j++) { sum += p[j]; }"
10659 " return sum;"
10660 "}"
10661 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10662 "just_ints = new Object();"
10663 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10664 "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }"
10665 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }"
10666 "for (var i = 0; i < 10; ++i) {"
10667 " result = pa_load(pixels,-10);"
10668 "}"
10669 "result");
10670 CHECK_EQ(0, result->Int32Value());
10671
10672 // Make sure that generic ICs properly handles other types than pixel
10673 // arrays (that the inlined fast pixel array test leaves the right information
10674 // in the right registers).
10675 result = CompileRun("function pa_load(p) {"
10676 " var sum = 0;"
10677 " for (var j = 0; j < 256; j++) { sum += p[j]; }"
10678 " return sum;"
10679 "}"
10680 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10681 "just_ints = new Object();"
10682 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }"
10683 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }"
10684 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }"
10685 "sparse_array = new Object();"
10686 "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }"
10687 "sparse_array[1000000] = 3;"
10688 "for (var i = 0; i < 10; ++i) {"
10689 " result = pa_load(sparse_array);"
10690 "}"
10691 "result");
10692 CHECK_EQ(32640, result->Int32Value());
10693
10694 // Make sure that pixel array loads are optimized by crankshaft.
10695 result = CompileRun("function pa_load(p) {"
10696 " var sum = 0;"
10697 " for (var i=0; i<256; ++i) {"
10698 " sum += p[i];"
10699 " }"
10700 " return sum; "
10701 "}"
10702 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }"
10703 "for (var i = 0; i < 10000; ++i) {"
10704 " result = pa_load(pixels);"
10705 "}"
10706 "result");
10707 CHECK_EQ(32640, result->Int32Value());
10708
Steve Blocka7e24c12009-10-30 11:49:00 +000010709 free(pixel_data);
10710}
10711
10712
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010713THREADED_TEST(PixelArrayInfo) {
10714 v8::HandleScope scope;
10715 LocalContext context;
10716 for (int size = 0; size < 100; size += 10) {
10717 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
10718 v8::Handle<v8::Object> obj = v8::Object::New();
10719 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
10720 CHECK(obj->HasIndexedPropertiesInPixelData());
10721 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
10722 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
10723 free(pixel_data);
10724 }
10725}
10726
10727
10728static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
10729 switch (array_type) {
10730 case v8::kExternalByteArray:
10731 case v8::kExternalUnsignedByteArray:
10732 return 1;
10733 break;
10734 case v8::kExternalShortArray:
10735 case v8::kExternalUnsignedShortArray:
10736 return 2;
10737 break;
10738 case v8::kExternalIntArray:
10739 case v8::kExternalUnsignedIntArray:
10740 case v8::kExternalFloatArray:
10741 return 4;
10742 break;
10743 default:
10744 UNREACHABLE();
10745 return -1;
10746 }
10747 UNREACHABLE();
10748 return -1;
10749}
10750
10751
Steve Block3ce2e202009-11-05 08:53:23 +000010752template <class ExternalArrayClass, class ElementType>
10753static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
10754 int64_t low,
10755 int64_t high) {
10756 v8::HandleScope scope;
10757 LocalContext context;
10758 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010759 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +000010760 ElementType* array_data =
10761 static_cast<ElementType*>(malloc(kElementCount * element_size));
10762 i::Handle<ExternalArrayClass> array =
10763 i::Handle<ExternalArrayClass>::cast(
10764 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
10765 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10766 for (int i = 0; i < kElementCount; i++) {
10767 array->set(i, static_cast<ElementType>(i));
10768 }
10769 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10770 for (int i = 0; i < kElementCount; i++) {
10771 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
10772 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
10773 }
10774
10775 v8::Handle<v8::Object> obj = v8::Object::New();
10776 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
10777 // Set the elements to be the external array.
10778 obj->SetIndexedPropertiesToExternalArrayData(array_data,
10779 array_type,
10780 kElementCount);
John Reck59135872010-11-02 12:39:01 -070010781 CHECK_EQ(
10782 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000010783 obj->Set(v8_str("field"), v8::Int32::New(1503));
10784 context->Global()->Set(v8_str("ext_array"), obj);
10785 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
10786 CHECK_EQ(1503, result->Int32Value());
10787 result = CompileRun("ext_array[1]");
10788 CHECK_EQ(1, result->Int32Value());
10789
10790 // Check pass through of assigned smis
10791 result = CompileRun("var sum = 0;"
10792 "for (var i = 0; i < 8; i++) {"
10793 " sum += ext_array[i] = ext_array[i] = -i;"
10794 "}"
10795 "sum;");
10796 CHECK_EQ(-28, result->Int32Value());
10797
10798 // Check assigned smis
10799 result = CompileRun("for (var i = 0; i < 8; i++) {"
10800 " ext_array[i] = i;"
10801 "}"
10802 "var sum = 0;"
10803 "for (var i = 0; i < 8; i++) {"
10804 " sum += ext_array[i];"
10805 "}"
10806 "sum;");
10807 CHECK_EQ(28, result->Int32Value());
10808
10809 // Check assigned smis in reverse order
10810 result = CompileRun("for (var i = 8; --i >= 0; ) {"
10811 " ext_array[i] = i;"
10812 "}"
10813 "var sum = 0;"
10814 "for (var i = 0; i < 8; i++) {"
10815 " sum += ext_array[i];"
10816 "}"
10817 "sum;");
10818 CHECK_EQ(28, result->Int32Value());
10819
10820 // Check pass through of assigned HeapNumbers
10821 result = CompileRun("var sum = 0;"
10822 "for (var i = 0; i < 16; i+=2) {"
10823 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
10824 "}"
10825 "sum;");
10826 CHECK_EQ(-28, result->Int32Value());
10827
10828 // Check assigned HeapNumbers
10829 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
10830 " ext_array[i] = (i * 0.5);"
10831 "}"
10832 "var sum = 0;"
10833 "for (var i = 0; i < 16; i+=2) {"
10834 " sum += ext_array[i];"
10835 "}"
10836 "sum;");
10837 CHECK_EQ(28, result->Int32Value());
10838
10839 // Check assigned HeapNumbers in reverse order
10840 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
10841 " ext_array[i] = (i * 0.5);"
10842 "}"
10843 "var sum = 0;"
10844 "for (var i = 0; i < 16; i+=2) {"
10845 " sum += ext_array[i];"
10846 "}"
10847 "sum;");
10848 CHECK_EQ(28, result->Int32Value());
10849
10850 i::ScopedVector<char> test_buf(1024);
10851
10852 // Check legal boundary conditions.
10853 // The repeated loads and stores ensure the ICs are exercised.
10854 const char* boundary_program =
10855 "var res = 0;"
10856 "for (var i = 0; i < 16; i++) {"
10857 " ext_array[i] = %lld;"
10858 " if (i > 8) {"
10859 " res = ext_array[i];"
10860 " }"
10861 "}"
10862 "res;";
10863 i::OS::SNPrintF(test_buf,
10864 boundary_program,
10865 low);
10866 result = CompileRun(test_buf.start());
10867 CHECK_EQ(low, result->IntegerValue());
10868
10869 i::OS::SNPrintF(test_buf,
10870 boundary_program,
10871 high);
10872 result = CompileRun(test_buf.start());
10873 CHECK_EQ(high, result->IntegerValue());
10874
10875 // Check misprediction of type in IC.
10876 result = CompileRun("var tmp_array = ext_array;"
10877 "var sum = 0;"
10878 "for (var i = 0; i < 8; i++) {"
10879 " tmp_array[i] = i;"
10880 " sum += tmp_array[i];"
10881 " if (i == 4) {"
10882 " tmp_array = {};"
10883 " }"
10884 "}"
10885 "sum;");
10886 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10887 CHECK_EQ(28, result->Int32Value());
10888
10889 // Make sure out-of-range loads do not throw.
10890 i::OS::SNPrintF(test_buf,
10891 "var caught_exception = false;"
10892 "try {"
10893 " ext_array[%d];"
10894 "} catch (e) {"
10895 " caught_exception = true;"
10896 "}"
10897 "caught_exception;",
10898 kElementCount);
10899 result = CompileRun(test_buf.start());
10900 CHECK_EQ(false, result->BooleanValue());
10901
10902 // Make sure out-of-range stores do not throw.
10903 i::OS::SNPrintF(test_buf,
10904 "var caught_exception = false;"
10905 "try {"
10906 " ext_array[%d] = 1;"
10907 "} catch (e) {"
10908 " caught_exception = true;"
10909 "}"
10910 "caught_exception;",
10911 kElementCount);
10912 result = CompileRun(test_buf.start());
10913 CHECK_EQ(false, result->BooleanValue());
10914
10915 // Check other boundary conditions, values and operations.
10916 result = CompileRun("for (var i = 0; i < 8; i++) {"
10917 " ext_array[7] = undefined;"
10918 "}"
10919 "ext_array[7];");
10920 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010921 CHECK_EQ(
10922 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000010923
10924 result = CompileRun("for (var i = 0; i < 8; i++) {"
10925 " ext_array[6] = '2.3';"
10926 "}"
10927 "ext_array[6];");
10928 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010929 CHECK_EQ(
10930 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000010931
10932 if (array_type != v8::kExternalFloatArray) {
10933 // Though the specification doesn't state it, be explicit about
10934 // converting NaNs and +/-Infinity to zero.
10935 result = CompileRun("for (var i = 0; i < 8; i++) {"
10936 " ext_array[i] = 5;"
10937 "}"
10938 "for (var i = 0; i < 8; i++) {"
10939 " ext_array[i] = NaN;"
10940 "}"
10941 "ext_array[5];");
10942 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010943 CHECK_EQ(0,
10944 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000010945
10946 result = CompileRun("for (var i = 0; i < 8; i++) {"
10947 " ext_array[i] = 5;"
10948 "}"
10949 "for (var i = 0; i < 8; i++) {"
10950 " ext_array[i] = Infinity;"
10951 "}"
10952 "ext_array[5];");
10953 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010954 CHECK_EQ(0,
10955 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000010956
10957 result = CompileRun("for (var i = 0; i < 8; i++) {"
10958 " ext_array[i] = 5;"
10959 "}"
10960 "for (var i = 0; i < 8; i++) {"
10961 " ext_array[i] = -Infinity;"
10962 "}"
10963 "ext_array[5];");
10964 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010965 CHECK_EQ(0,
10966 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block1e0659c2011-05-24 12:43:12 +010010967
10968 // Check truncation behavior of integral arrays.
10969 const char* unsigned_data =
10970 "var source_data = [0.6, 10.6];"
10971 "var expected_results = [0, 10];";
10972 const char* signed_data =
10973 "var source_data = [0.6, 10.6, -0.6, -10.6];"
10974 "var expected_results = [0, 10, 0, -10];";
10975 bool is_unsigned =
10976 (array_type == v8::kExternalUnsignedByteArray ||
10977 array_type == v8::kExternalUnsignedShortArray ||
10978 array_type == v8::kExternalUnsignedIntArray);
10979
10980 i::OS::SNPrintF(test_buf,
10981 "%s"
10982 "var all_passed = true;"
10983 "for (var i = 0; i < source_data.length; i++) {"
10984 " for (var j = 0; j < 8; j++) {"
10985 " ext_array[j] = source_data[i];"
10986 " }"
10987 " all_passed = all_passed &&"
10988 " (ext_array[5] == expected_results[i]);"
10989 "}"
10990 "all_passed;",
10991 (is_unsigned ? unsigned_data : signed_data));
10992 result = CompileRun(test_buf.start());
10993 CHECK_EQ(true, result->BooleanValue());
Steve Block3ce2e202009-11-05 08:53:23 +000010994 }
10995
10996 result = CompileRun("ext_array[3] = 33;"
10997 "delete ext_array[3];"
10998 "ext_array[3];");
10999 CHECK_EQ(33, result->Int32Value());
11000
11001 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
11002 "ext_array[2] = 12; ext_array[3] = 13;"
11003 "ext_array.__defineGetter__('2',"
11004 "function() { return 120; });"
11005 "ext_array[2];");
11006 CHECK_EQ(12, result->Int32Value());
11007
11008 result = CompileRun("var js_array = new Array(40);"
11009 "js_array[0] = 77;"
11010 "js_array;");
11011 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11012
11013 result = CompileRun("ext_array[1] = 23;"
11014 "ext_array.__proto__ = [];"
11015 "js_array.__proto__ = ext_array;"
11016 "js_array.concat(ext_array);");
11017 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
11018 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
11019
11020 result = CompileRun("ext_array[1] = 23;");
11021 CHECK_EQ(23, result->Int32Value());
11022
Steve Blockd0582a62009-12-15 09:54:21 +000011023 // Test more complex manipulations which cause eax to contain values
11024 // that won't be completely overwritten by loads from the arrays.
11025 // This catches bugs in the instructions used for the KeyedLoadIC
11026 // for byte and word types.
11027 {
11028 const int kXSize = 300;
11029 const int kYSize = 300;
11030 const int kLargeElementCount = kXSize * kYSize * 4;
11031 ElementType* large_array_data =
11032 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
11033 i::Handle<ExternalArrayClass> large_array =
11034 i::Handle<ExternalArrayClass>::cast(
11035 i::Factory::NewExternalArray(kLargeElementCount,
11036 array_type,
11037 array_data));
11038 v8::Handle<v8::Object> large_obj = v8::Object::New();
11039 // Set the elements to be the external array.
11040 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
11041 array_type,
11042 kLargeElementCount);
11043 context->Global()->Set(v8_str("large_array"), large_obj);
11044 // Initialize contents of a few rows.
11045 for (int x = 0; x < 300; x++) {
11046 int row = 0;
11047 int offset = row * 300 * 4;
11048 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11049 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11050 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11051 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11052 row = 150;
11053 offset = row * 300 * 4;
11054 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11055 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11056 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11057 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11058 row = 298;
11059 offset = row * 300 * 4;
11060 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
11061 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
11062 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
11063 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
11064 }
11065 // The goal of the code below is to make "offset" large enough
11066 // that the computation of the index (which goes into eax) has
11067 // high bits set which will not be overwritten by a byte or short
11068 // load.
11069 result = CompileRun("var failed = false;"
11070 "var offset = 0;"
11071 "for (var i = 0; i < 300; i++) {"
11072 " if (large_array[4 * i] != 127 ||"
11073 " large_array[4 * i + 1] != 0 ||"
11074 " large_array[4 * i + 2] != 0 ||"
11075 " large_array[4 * i + 3] != 127) {"
11076 " failed = true;"
11077 " }"
11078 "}"
11079 "offset = 150 * 300 * 4;"
11080 "for (var i = 0; i < 300; i++) {"
11081 " if (large_array[offset + 4 * i] != 127 ||"
11082 " large_array[offset + 4 * i + 1] != 0 ||"
11083 " large_array[offset + 4 * i + 2] != 0 ||"
11084 " large_array[offset + 4 * i + 3] != 127) {"
11085 " failed = true;"
11086 " }"
11087 "}"
11088 "offset = 298 * 300 * 4;"
11089 "for (var i = 0; i < 300; i++) {"
11090 " if (large_array[offset + 4 * i] != 127 ||"
11091 " large_array[offset + 4 * i + 1] != 0 ||"
11092 " large_array[offset + 4 * i + 2] != 0 ||"
11093 " large_array[offset + 4 * i + 3] != 127) {"
11094 " failed = true;"
11095 " }"
11096 "}"
11097 "!failed;");
11098 CHECK_EQ(true, result->BooleanValue());
11099 free(large_array_data);
11100 }
11101
Steve Block3ce2e202009-11-05 08:53:23 +000011102 free(array_data);
11103}
11104
11105
11106THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011107 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011108 v8::kExternalByteArray,
11109 -128,
11110 127);
11111}
11112
11113
11114THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011115 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011116 v8::kExternalUnsignedByteArray,
11117 0,
11118 255);
11119}
11120
11121
11122THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011123 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011124 v8::kExternalShortArray,
11125 -32768,
11126 32767);
11127}
11128
11129
11130THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011131 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011132 v8::kExternalUnsignedShortArray,
11133 0,
11134 65535);
11135}
11136
11137
11138THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011139 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011140 v8::kExternalIntArray,
11141 INT_MIN, // -2147483648
11142 INT_MAX); // 2147483647
11143}
11144
11145
11146THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011147 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000011148 v8::kExternalUnsignedIntArray,
11149 0,
11150 UINT_MAX); // 4294967295
11151}
11152
11153
11154THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010011155 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000011156 v8::kExternalFloatArray,
11157 -500,
11158 500);
11159}
11160
11161
11162THREADED_TEST(ExternalArrays) {
11163 TestExternalByteArray();
11164 TestExternalUnsignedByteArray();
11165 TestExternalShortArray();
11166 TestExternalUnsignedShortArray();
11167 TestExternalIntArray();
11168 TestExternalUnsignedIntArray();
11169 TestExternalFloatArray();
11170}
11171
11172
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011173void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
11174 v8::HandleScope scope;
11175 LocalContext context;
11176 for (int size = 0; size < 100; size += 10) {
11177 int element_size = ExternalArrayElementSize(array_type);
11178 void* external_data = malloc(size * element_size);
11179 v8::Handle<v8::Object> obj = v8::Object::New();
11180 obj->SetIndexedPropertiesToExternalArrayData(
11181 external_data, array_type, size);
11182 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
11183 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
11184 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
11185 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
11186 free(external_data);
11187 }
11188}
11189
11190
11191THREADED_TEST(ExternalArrayInfo) {
11192 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
11193 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
11194 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
11195 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
11196 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
11197 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
11198 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
11199}
11200
11201
Steve Blocka7e24c12009-10-30 11:49:00 +000011202THREADED_TEST(ScriptContextDependence) {
11203 v8::HandleScope scope;
11204 LocalContext c1;
11205 const char *source = "foo";
11206 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
11207 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
11208 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
11209 CHECK_EQ(dep->Run()->Int32Value(), 100);
11210 CHECK_EQ(indep->Run()->Int32Value(), 100);
11211 LocalContext c2;
11212 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
11213 CHECK_EQ(dep->Run()->Int32Value(), 100);
11214 CHECK_EQ(indep->Run()->Int32Value(), 101);
11215}
11216
11217
11218THREADED_TEST(StackTrace) {
11219 v8::HandleScope scope;
11220 LocalContext context;
11221 v8::TryCatch try_catch;
11222 const char *source = "function foo() { FAIL.FAIL; }; foo();";
11223 v8::Handle<v8::String> src = v8::String::New(source);
11224 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
11225 v8::Script::New(src, origin)->Run();
11226 CHECK(try_catch.HasCaught());
11227 v8::String::Utf8Value stack(try_catch.StackTrace());
11228 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
11229}
11230
11231
Kristian Monsen25f61362010-05-21 11:50:48 +010011232// Checks that a StackFrame has certain expected values.
11233void checkStackFrame(const char* expected_script_name,
11234 const char* expected_func_name, int expected_line_number,
11235 int expected_column, bool is_eval, bool is_constructor,
11236 v8::Handle<v8::StackFrame> frame) {
11237 v8::HandleScope scope;
11238 v8::String::Utf8Value func_name(frame->GetFunctionName());
11239 v8::String::Utf8Value script_name(frame->GetScriptName());
11240 if (*script_name == NULL) {
11241 // The situation where there is no associated script, like for evals.
11242 CHECK(expected_script_name == NULL);
11243 } else {
11244 CHECK(strstr(*script_name, expected_script_name) != NULL);
11245 }
11246 CHECK(strstr(*func_name, expected_func_name) != NULL);
11247 CHECK_EQ(expected_line_number, frame->GetLineNumber());
11248 CHECK_EQ(expected_column, frame->GetColumn());
11249 CHECK_EQ(is_eval, frame->IsEval());
11250 CHECK_EQ(is_constructor, frame->IsConstructor());
11251}
11252
11253
11254v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
11255 v8::HandleScope scope;
11256 const char* origin = "capture-stack-trace-test";
11257 const int kOverviewTest = 1;
11258 const int kDetailedTest = 2;
11259
11260 ASSERT(args.Length() == 1);
11261
11262 int testGroup = args[0]->Int32Value();
11263 if (testGroup == kOverviewTest) {
11264 v8::Handle<v8::StackTrace> stackTrace =
11265 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
11266 CHECK_EQ(4, stackTrace->GetFrameCount());
11267 checkStackFrame(origin, "bar", 2, 10, false, false,
11268 stackTrace->GetFrame(0));
11269 checkStackFrame(origin, "foo", 6, 3, false, false,
11270 stackTrace->GetFrame(1));
11271 checkStackFrame(NULL, "", 1, 1, false, false,
11272 stackTrace->GetFrame(2));
11273 // The last frame is an anonymous function that has the initial call.
11274 checkStackFrame(origin, "", 8, 7, false, false,
11275 stackTrace->GetFrame(3));
11276
11277 CHECK(stackTrace->AsArray()->IsArray());
11278 } else if (testGroup == kDetailedTest) {
11279 v8::Handle<v8::StackTrace> stackTrace =
11280 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
11281 CHECK_EQ(4, stackTrace->GetFrameCount());
11282 checkStackFrame(origin, "bat", 4, 22, false, false,
11283 stackTrace->GetFrame(0));
11284 checkStackFrame(origin, "baz", 8, 3, false, true,
11285 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010011286#ifdef ENABLE_DEBUGGER_SUPPORT
11287 bool is_eval = true;
11288#else // ENABLE_DEBUGGER_SUPPORT
11289 bool is_eval = false;
11290#endif // ENABLE_DEBUGGER_SUPPORT
11291
11292 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010011293 stackTrace->GetFrame(2));
11294 // The last frame is an anonymous function that has the initial call to foo.
11295 checkStackFrame(origin, "", 10, 1, false, false,
11296 stackTrace->GetFrame(3));
11297
11298 CHECK(stackTrace->AsArray()->IsArray());
11299 }
11300 return v8::Undefined();
11301}
11302
11303
11304// Tests the C++ StackTrace API.
Ben Murdochb0fe1622011-05-05 13:52:32 +010011305// TODO(3074796): Reenable this as a THREADED_TEST once it passes.
11306// THREADED_TEST(CaptureStackTrace) {
11307TEST(CaptureStackTrace) {
Kristian Monsen25f61362010-05-21 11:50:48 +010011308 v8::HandleScope scope;
11309 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
11310 Local<ObjectTemplate> templ = ObjectTemplate::New();
11311 templ->Set(v8_str("AnalyzeStackInNativeCode"),
11312 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
11313 LocalContext context(0, templ);
11314
11315 // Test getting OVERVIEW information. Should ignore information that is not
11316 // script name, function name, line number, and column offset.
11317 const char *overview_source =
11318 "function bar() {\n"
11319 " var y; AnalyzeStackInNativeCode(1);\n"
11320 "}\n"
11321 "function foo() {\n"
11322 "\n"
11323 " bar();\n"
11324 "}\n"
11325 "var x;eval('new foo();');";
11326 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
11327 v8::Handle<Value> overview_result =
11328 v8::Script::New(overview_src, origin)->Run();
11329 ASSERT(!overview_result.IsEmpty());
11330 ASSERT(overview_result->IsObject());
11331
11332 // Test getting DETAILED information.
11333 const char *detailed_source =
11334 "function bat() {AnalyzeStackInNativeCode(2);\n"
11335 "}\n"
11336 "\n"
11337 "function baz() {\n"
11338 " bat();\n"
11339 "}\n"
11340 "eval('new baz();');";
11341 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
11342 // Make the script using a non-zero line and column offset.
11343 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
11344 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
11345 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
11346 v8::Handle<v8::Script> detailed_script(
11347 v8::Script::New(detailed_src, &detailed_origin));
11348 v8::Handle<Value> detailed_result = detailed_script->Run();
11349 ASSERT(!detailed_result.IsEmpty());
11350 ASSERT(detailed_result->IsObject());
11351}
11352
11353
Ben Murdoch3bec4d22010-07-22 14:51:16 +010011354static void StackTraceForUncaughtExceptionListener(
11355 v8::Handle<v8::Message> message,
11356 v8::Handle<Value>) {
11357 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
11358 CHECK_EQ(2, stack_trace->GetFrameCount());
11359 checkStackFrame("origin", "foo", 2, 3, false, false,
11360 stack_trace->GetFrame(0));
11361 checkStackFrame("origin", "bar", 5, 3, false, false,
11362 stack_trace->GetFrame(1));
11363}
11364
11365TEST(CaptureStackTraceForUncaughtException) {
11366 report_count = 0;
11367 v8::HandleScope scope;
11368 LocalContext env;
11369 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
11370 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
11371
11372 Script::Compile(v8_str("function foo() {\n"
11373 " throw 1;\n"
11374 "};\n"
11375 "function bar() {\n"
11376 " foo();\n"
11377 "};"),
11378 v8_str("origin"))->Run();
11379 v8::Local<v8::Object> global = env->Global();
11380 Local<Value> trouble = global->Get(v8_str("bar"));
11381 CHECK(trouble->IsFunction());
11382 Function::Cast(*trouble)->Call(global, 0, NULL);
11383 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
11384 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
11385}
11386
11387
Steve Block1e0659c2011-05-24 12:43:12 +010011388TEST(CaptureStackTraceForUncaughtExceptionAndSetters) {
11389 v8::HandleScope scope;
11390 LocalContext env;
11391 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true,
11392 1024,
11393 v8::StackTrace::kDetailed);
11394
11395 CompileRun(
11396 "var setters = ['column', 'lineNumber', 'scriptName',\n"
11397 " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n"
11398 " 'isConstructor'];\n"
11399 "for (var i = 0; i < setters.length; i++) {\n"
11400 " var prop = setters[i];\n"
11401 " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n"
11402 "}\n");
11403 CompileRun("throw 'exception';");
11404 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
11405}
11406
11407
Ben Murdochf87a2032010-10-22 12:50:53 +010011408v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) {
11409 v8::HandleScope scope;
11410 v8::Handle<v8::StackTrace> stackTrace =
11411 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
11412 CHECK_EQ(5, stackTrace->GetFrameCount());
11413 v8::Handle<v8::String> url = v8_str("eval_url");
11414 for (int i = 0; i < 3; i++) {
11415 v8::Handle<v8::String> name =
11416 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL();
11417 CHECK(!name.IsEmpty());
11418 CHECK_EQ(url, name);
11419 }
11420 return v8::Undefined();
11421}
11422
11423
11424TEST(SourceURLInStackTrace) {
11425 v8::HandleScope scope;
11426 Local<ObjectTemplate> templ = ObjectTemplate::New();
11427 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"),
11428 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL));
11429 LocalContext context(0, templ);
11430
11431 const char *source =
11432 "function outer() {\n"
11433 "function bar() {\n"
11434 " AnalyzeStackOfEvalWithSourceURL();\n"
11435 "}\n"
11436 "function foo() {\n"
11437 "\n"
11438 " bar();\n"
11439 "}\n"
11440 "foo();\n"
11441 "}\n"
11442 "eval('(' + outer +')()//@ sourceURL=eval_url');";
11443 CHECK(CompileRun(source)->IsUndefined());
11444}
11445
11446
Steve Block3ce2e202009-11-05 08:53:23 +000011447// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000011448THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000011449 bool rv = false;
11450 for (int i = 0; i < 100; i++) {
11451 rv = v8::V8::IdleNotification();
11452 if (rv)
11453 break;
11454 }
11455 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000011456}
11457
11458
11459static uint32_t* stack_limit;
11460
11461static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080011462 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::real_climit());
Steve Blocka7e24c12009-10-30 11:49:00 +000011463 return v8::Undefined();
11464}
11465
11466
11467// Uses the address of a local variable to determine the stack top now.
11468// Given a size, returns an address that is that far from the current
11469// top of stack.
11470static uint32_t* ComputeStackLimit(uint32_t size) {
11471 uint32_t* answer = &size - (size / sizeof(size));
11472 // If the size is very large and the stack is very near the bottom of
11473 // memory then the calculation above may wrap around and give an address
11474 // that is above the (downwards-growing) stack. In that case we return
11475 // a very low address.
11476 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
11477 return answer;
11478}
11479
11480
11481TEST(SetResourceConstraints) {
11482 static const int K = 1024;
11483 uint32_t* set_limit = ComputeStackLimit(128 * K);
11484
11485 // Set stack limit.
11486 v8::ResourceConstraints constraints;
11487 constraints.set_stack_limit(set_limit);
11488 CHECK(v8::SetResourceConstraints(&constraints));
11489
11490 // Execute a script.
11491 v8::HandleScope scope;
11492 LocalContext env;
11493 Local<v8::FunctionTemplate> fun_templ =
11494 v8::FunctionTemplate::New(GetStackLimitCallback);
11495 Local<Function> fun = fun_templ->GetFunction();
11496 env->Global()->Set(v8_str("get_stack_limit"), fun);
11497 CompileRun("get_stack_limit();");
11498
11499 CHECK(stack_limit == set_limit);
11500}
11501
11502
11503TEST(SetResourceConstraintsInThread) {
11504 uint32_t* set_limit;
11505 {
11506 v8::Locker locker;
11507 static const int K = 1024;
11508 set_limit = ComputeStackLimit(128 * K);
11509
11510 // Set stack limit.
11511 v8::ResourceConstraints constraints;
11512 constraints.set_stack_limit(set_limit);
11513 CHECK(v8::SetResourceConstraints(&constraints));
11514
11515 // Execute a script.
11516 v8::HandleScope scope;
11517 LocalContext env;
11518 Local<v8::FunctionTemplate> fun_templ =
11519 v8::FunctionTemplate::New(GetStackLimitCallback);
11520 Local<Function> fun = fun_templ->GetFunction();
11521 env->Global()->Set(v8_str("get_stack_limit"), fun);
11522 CompileRun("get_stack_limit();");
11523
11524 CHECK(stack_limit == set_limit);
11525 }
11526 {
11527 v8::Locker locker;
11528 CHECK(stack_limit == set_limit);
11529 }
11530}
Steve Block3ce2e202009-11-05 08:53:23 +000011531
11532
11533THREADED_TEST(GetHeapStatistics) {
11534 v8::HandleScope scope;
11535 LocalContext c1;
11536 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000011537 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
11538 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000011539 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000011540 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
11541 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
11542}
11543
11544
11545static double DoubleFromBits(uint64_t value) {
11546 double target;
11547#ifdef BIG_ENDIAN_FLOATING_POINT
11548 const int kIntSize = 4;
11549 // Somebody swapped the lower and higher half of doubles.
11550 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
11551 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
11552#else
11553 memcpy(&target, &value, sizeof(target));
11554#endif
11555 return target;
11556}
11557
11558
11559static uint64_t DoubleToBits(double value) {
11560 uint64_t target;
11561#ifdef BIG_ENDIAN_FLOATING_POINT
11562 const int kIntSize = 4;
11563 // Somebody swapped the lower and higher half of doubles.
11564 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
11565 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
11566#else
11567 memcpy(&target, &value, sizeof(target));
11568#endif
11569 return target;
11570}
11571
11572
11573static double DoubleToDateTime(double input) {
11574 double date_limit = 864e13;
11575 if (IsNaN(input) || input < -date_limit || input > date_limit) {
11576 return i::OS::nan_value();
11577 }
11578 return (input < 0) ? -(floor(-input)) : floor(input);
11579}
11580
11581// We don't have a consistent way to write 64-bit constants syntactically, so we
11582// split them into two 32-bit constants and combine them programmatically.
11583static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
11584 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
11585}
11586
11587
11588THREADED_TEST(QuietSignalingNaNs) {
11589 v8::HandleScope scope;
11590 LocalContext context;
11591 v8::TryCatch try_catch;
11592
11593 // Special double values.
11594 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
11595 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
11596 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
11597 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
11598 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
11599 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
11600 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
11601
11602 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
11603 // on either side of the epoch.
11604 double date_limit = 864e13;
11605
11606 double test_values[] = {
11607 snan,
11608 qnan,
11609 infinity,
11610 max_normal,
11611 date_limit + 1,
11612 date_limit,
11613 min_normal,
11614 max_denormal,
11615 min_denormal,
11616 0,
11617 -0,
11618 -min_denormal,
11619 -max_denormal,
11620 -min_normal,
11621 -date_limit,
11622 -date_limit - 1,
11623 -max_normal,
11624 -infinity,
11625 -qnan,
11626 -snan
11627 };
11628 int num_test_values = 20;
11629
11630 for (int i = 0; i < num_test_values; i++) {
11631 double test_value = test_values[i];
11632
11633 // Check that Number::New preserves non-NaNs and quiets SNaNs.
11634 v8::Handle<v8::Value> number = v8::Number::New(test_value);
11635 double stored_number = number->NumberValue();
11636 if (!IsNaN(test_value)) {
11637 CHECK_EQ(test_value, stored_number);
11638 } else {
11639 uint64_t stored_bits = DoubleToBits(stored_number);
11640 // Check if quiet nan (bits 51..62 all set).
11641 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
11642 }
11643
11644 // Check that Date::New preserves non-NaNs in the date range and
11645 // quiets SNaNs.
11646 v8::Handle<v8::Value> date = v8::Date::New(test_value);
11647 double expected_stored_date = DoubleToDateTime(test_value);
11648 double stored_date = date->NumberValue();
11649 if (!IsNaN(expected_stored_date)) {
11650 CHECK_EQ(expected_stored_date, stored_date);
11651 } else {
11652 uint64_t stored_bits = DoubleToBits(stored_date);
11653 // Check if quiet nan (bits 51..62 all set).
11654 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
11655 }
11656 }
11657}
11658
11659
11660static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
11661 v8::HandleScope scope;
11662 v8::TryCatch tc;
11663 v8::Handle<v8::String> str = args[0]->ToString();
11664 if (tc.HasCaught())
11665 return tc.ReThrow();
11666 return v8::Undefined();
11667}
11668
11669
11670// Test that an exception can be propagated down through a spaghetti
11671// stack using ReThrow.
11672THREADED_TEST(SpaghettiStackReThrow) {
11673 v8::HandleScope scope;
11674 LocalContext context;
11675 context->Global()->Set(
11676 v8::String::New("s"),
11677 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
11678 v8::TryCatch try_catch;
11679 CompileRun(
11680 "var i = 0;"
11681 "var o = {"
11682 " toString: function () {"
11683 " if (i == 10) {"
11684 " throw 'Hey!';"
11685 " } else {"
11686 " i++;"
11687 " return s(o);"
11688 " }"
11689 " }"
11690 "};"
11691 "s(o);");
11692 CHECK(try_catch.HasCaught());
11693 v8::String::Utf8Value value(try_catch.Exception());
11694 CHECK_EQ(0, strcmp(*value, "Hey!"));
11695}
11696
11697
Steve Blockd0582a62009-12-15 09:54:21 +000011698TEST(Regress528) {
11699 v8::V8::Initialize();
11700
11701 v8::HandleScope scope;
11702 v8::Persistent<Context> context;
11703 v8::Persistent<Context> other_context;
11704 int gc_count;
11705
11706 // Create a context used to keep the code from aging in the compilation
11707 // cache.
11708 other_context = Context::New();
11709
11710 // Context-dependent context data creates reference from the compilation
11711 // cache to the global object.
11712 const char* source_simple = "1";
11713 context = Context::New();
11714 {
11715 v8::HandleScope scope;
11716
11717 context->Enter();
11718 Local<v8::String> obj = v8::String::New("");
11719 context->SetData(obj);
11720 CompileRun(source_simple);
11721 context->Exit();
11722 }
11723 context.Dispose();
11724 for (gc_count = 1; gc_count < 10; gc_count++) {
11725 other_context->Enter();
11726 CompileRun(source_simple);
11727 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010011728 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000011729 if (GetGlobalObjectsCount() == 1) break;
11730 }
11731 CHECK_GE(2, gc_count);
11732 CHECK_EQ(1, GetGlobalObjectsCount());
11733
11734 // Eval in a function creates reference from the compilation cache to the
11735 // global object.
11736 const char* source_eval = "function f(){eval('1')}; f()";
11737 context = Context::New();
11738 {
11739 v8::HandleScope scope;
11740
11741 context->Enter();
11742 CompileRun(source_eval);
11743 context->Exit();
11744 }
11745 context.Dispose();
11746 for (gc_count = 1; gc_count < 10; gc_count++) {
11747 other_context->Enter();
11748 CompileRun(source_eval);
11749 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010011750 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000011751 if (GetGlobalObjectsCount() == 1) break;
11752 }
11753 CHECK_GE(2, gc_count);
11754 CHECK_EQ(1, GetGlobalObjectsCount());
11755
11756 // Looking up the line number for an exception creates reference from the
11757 // compilation cache to the global object.
11758 const char* source_exception = "function f(){throw 1;} f()";
11759 context = Context::New();
11760 {
11761 v8::HandleScope scope;
11762
11763 context->Enter();
11764 v8::TryCatch try_catch;
11765 CompileRun(source_exception);
11766 CHECK(try_catch.HasCaught());
11767 v8::Handle<v8::Message> message = try_catch.Message();
11768 CHECK(!message.IsEmpty());
11769 CHECK_EQ(1, message->GetLineNumber());
11770 context->Exit();
11771 }
11772 context.Dispose();
11773 for (gc_count = 1; gc_count < 10; gc_count++) {
11774 other_context->Enter();
11775 CompileRun(source_exception);
11776 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010011777 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000011778 if (GetGlobalObjectsCount() == 1) break;
11779 }
11780 CHECK_GE(2, gc_count);
11781 CHECK_EQ(1, GetGlobalObjectsCount());
11782
11783 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000011784}
Andrei Popescu402d9372010-02-26 13:31:12 +000011785
11786
11787THREADED_TEST(ScriptOrigin) {
11788 v8::HandleScope scope;
11789 LocalContext env;
11790 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
11791 v8::Handle<v8::String> script = v8::String::New(
11792 "function f() {}\n\nfunction g() {}");
11793 v8::Script::Compile(script, &origin)->Run();
11794 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
11795 env->Global()->Get(v8::String::New("f")));
11796 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
11797 env->Global()->Get(v8::String::New("g")));
11798
11799 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
11800 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
11801 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
11802
11803 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
11804 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
11805 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
11806}
11807
11808
11809THREADED_TEST(ScriptLineNumber) {
11810 v8::HandleScope scope;
11811 LocalContext env;
11812 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
11813 v8::Handle<v8::String> script = v8::String::New(
11814 "function f() {}\n\nfunction g() {}");
11815 v8::Script::Compile(script, &origin)->Run();
11816 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
11817 env->Global()->Get(v8::String::New("f")));
11818 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
11819 env->Global()->Get(v8::String::New("g")));
11820 CHECK_EQ(0, f->GetScriptLineNumber());
11821 CHECK_EQ(2, g->GetScriptLineNumber());
11822}
11823
11824
11825static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
11826 const AccessorInfo& info) {
11827 return v8_num(42);
11828}
11829
11830
11831static void SetterWhichSetsYOnThisTo23(Local<String> name,
11832 Local<Value> value,
11833 const AccessorInfo& info) {
11834 info.This()->Set(v8_str("y"), v8_num(23));
11835}
11836
11837
Steve Block6ded16b2010-05-10 14:33:55 +010011838TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000011839 v8::HandleScope scope;
11840 Local<ObjectTemplate> templ = ObjectTemplate::New();
11841 templ->SetAccessor(v8_str("x"),
11842 GetterWhichReturns42,
11843 SetterWhichSetsYOnThisTo23);
11844 LocalContext context;
11845 context->Global()->Set(v8_str("P"), templ->NewInstance());
11846 CompileRun("function C1() {"
11847 " this.x = 23;"
11848 "};"
11849 "C1.prototype = P;"
11850 "function C2() {"
11851 " this.x = 23"
11852 "};"
11853 "C2.prototype = { };"
11854 "C2.prototype.__proto__ = P;");
11855
11856 v8::Local<v8::Script> script;
11857 script = v8::Script::Compile(v8_str("new C1();"));
11858 for (int i = 0; i < 10; i++) {
11859 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11860 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
11861 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
11862 }
11863
11864 script = v8::Script::Compile(v8_str("new C2();"));
11865 for (int i = 0; i < 10; i++) {
11866 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
11867 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
11868 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
11869 }
11870}
11871
11872
11873static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
11874 Local<String> name, const AccessorInfo& info) {
11875 return v8_num(42);
11876}
11877
11878
11879static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
11880 Local<String> name, Local<Value> value, const AccessorInfo& info) {
11881 if (name->Equals(v8_str("x"))) {
11882 info.This()->Set(v8_str("y"), v8_num(23));
11883 }
11884 return v8::Handle<Value>();
11885}
11886
11887
11888THREADED_TEST(InterceptorOnConstructorPrototype) {
11889 v8::HandleScope scope;
11890 Local<ObjectTemplate> templ = ObjectTemplate::New();
11891 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
11892 NamedPropertySetterWhichSetsYOnThisTo23);
11893 LocalContext context;
11894 context->Global()->Set(v8_str("P"), templ->NewInstance());
11895 CompileRun("function C1() {"
11896 " this.x = 23;"
11897 "};"
11898 "C1.prototype = P;"
11899 "function C2() {"
11900 " this.x = 23"
11901 "};"
11902 "C2.prototype = { };"
11903 "C2.prototype.__proto__ = P;");
11904
11905 v8::Local<v8::Script> script;
11906 script = v8::Script::Compile(v8_str("new C1();"));
11907 for (int i = 0; i < 10; i++) {
11908 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11909 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
11910 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
11911 }
11912
11913 script = v8::Script::Compile(v8_str("new C2();"));
11914 for (int i = 0; i < 10; i++) {
11915 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
11916 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
11917 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
11918 }
11919}
Steve Block6ded16b2010-05-10 14:33:55 +010011920
11921
11922TEST(Bug618) {
11923 const char* source = "function C1() {"
11924 " this.x = 23;"
11925 "};"
11926 "C1.prototype = P;";
11927
11928 v8::HandleScope scope;
11929 LocalContext context;
11930 v8::Local<v8::Script> script;
11931
11932 // Use a simple object as prototype.
11933 v8::Local<v8::Object> prototype = v8::Object::New();
11934 prototype->Set(v8_str("y"), v8_num(42));
11935 context->Global()->Set(v8_str("P"), prototype);
11936
11937 // This compile will add the code to the compilation cache.
11938 CompileRun(source);
11939
11940 script = v8::Script::Compile(v8_str("new C1();"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +010011941 // Allow enough iterations for the inobject slack tracking logic
11942 // to finalize instance size and install the fast construct stub.
11943 for (int i = 0; i < 256; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +010011944 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11945 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
11946 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
11947 }
11948
11949 // Use an API object with accessors as prototype.
11950 Local<ObjectTemplate> templ = ObjectTemplate::New();
11951 templ->SetAccessor(v8_str("x"),
11952 GetterWhichReturns42,
11953 SetterWhichSetsYOnThisTo23);
11954 context->Global()->Set(v8_str("P"), templ->NewInstance());
11955
11956 // This compile will get the code from the compilation cache.
11957 CompileRun(source);
11958
11959 script = v8::Script::Compile(v8_str("new C1();"));
11960 for (int i = 0; i < 10; i++) {
11961 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11962 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
11963 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
11964 }
11965}
11966
11967int prologue_call_count = 0;
11968int epilogue_call_count = 0;
11969int prologue_call_count_second = 0;
11970int epilogue_call_count_second = 0;
11971
11972void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
11973 ++prologue_call_count;
11974}
11975
11976void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
11977 ++epilogue_call_count;
11978}
11979
11980void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
11981 ++prologue_call_count_second;
11982}
11983
11984void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
11985 ++epilogue_call_count_second;
11986}
11987
11988TEST(GCCallbacks) {
11989 LocalContext context;
11990
11991 v8::V8::AddGCPrologueCallback(PrologueCallback);
11992 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
11993 CHECK_EQ(0, prologue_call_count);
11994 CHECK_EQ(0, epilogue_call_count);
11995 i::Heap::CollectAllGarbage(false);
11996 CHECK_EQ(1, prologue_call_count);
11997 CHECK_EQ(1, epilogue_call_count);
11998 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
11999 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
12000 i::Heap::CollectAllGarbage(false);
12001 CHECK_EQ(2, prologue_call_count);
12002 CHECK_EQ(2, epilogue_call_count);
12003 CHECK_EQ(1, prologue_call_count_second);
12004 CHECK_EQ(1, epilogue_call_count_second);
12005 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
12006 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
12007 i::Heap::CollectAllGarbage(false);
12008 CHECK_EQ(2, prologue_call_count);
12009 CHECK_EQ(2, epilogue_call_count);
12010 CHECK_EQ(2, prologue_call_count_second);
12011 CHECK_EQ(2, epilogue_call_count_second);
12012 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
12013 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
12014 i::Heap::CollectAllGarbage(false);
12015 CHECK_EQ(2, prologue_call_count);
12016 CHECK_EQ(2, epilogue_call_count);
12017 CHECK_EQ(2, prologue_call_count_second);
12018 CHECK_EQ(2, epilogue_call_count_second);
12019}
Kristian Monsen25f61362010-05-21 11:50:48 +010012020
12021
12022THREADED_TEST(AddToJSFunctionResultCache) {
12023 i::FLAG_allow_natives_syntax = true;
12024 v8::HandleScope scope;
12025
12026 LocalContext context;
12027
12028 const char* code =
12029 "(function() {"
12030 " var key0 = 'a';"
12031 " var key1 = 'b';"
12032 " var r0 = %_GetFromCache(0, key0);"
12033 " var r1 = %_GetFromCache(0, key1);"
12034 " var r0_ = %_GetFromCache(0, key0);"
12035 " if (r0 !== r0_)"
12036 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
12037 " var r1_ = %_GetFromCache(0, key1);"
12038 " if (r1 !== r1_)"
12039 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
12040 " return 'PASSED';"
12041 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012042 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012043 ExpectString(code, "PASSED");
12044}
12045
12046
12047static const int k0CacheSize = 16;
12048
12049THREADED_TEST(FillJSFunctionResultCache) {
12050 i::FLAG_allow_natives_syntax = true;
12051 v8::HandleScope scope;
12052
12053 LocalContext context;
12054
12055 const char* code =
12056 "(function() {"
12057 " var k = 'a';"
12058 " var r = %_GetFromCache(0, k);"
12059 " for (var i = 0; i < 16; i++) {"
12060 " %_GetFromCache(0, 'a' + i);"
12061 " };"
12062 " if (r === %_GetFromCache(0, k))"
12063 " return 'FAILED: k0CacheSize is too small';"
12064 " return 'PASSED';"
12065 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012066 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012067 ExpectString(code, "PASSED");
12068}
12069
12070
12071THREADED_TEST(RoundRobinGetFromCache) {
12072 i::FLAG_allow_natives_syntax = true;
12073 v8::HandleScope scope;
12074
12075 LocalContext context;
12076
12077 const char* code =
12078 "(function() {"
12079 " var keys = [];"
12080 " for (var i = 0; i < 16; i++) keys.push(i);"
12081 " var values = [];"
12082 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
12083 " for (var i = 0; i < 16; i++) {"
12084 " var v = %_GetFromCache(0, keys[i]);"
12085 " if (v !== values[i])"
12086 " return 'Wrong value for ' + "
12087 " keys[i] + ': ' + v + ' vs. ' + values[i];"
12088 " };"
12089 " return 'PASSED';"
12090 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012091 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012092 ExpectString(code, "PASSED");
12093}
12094
12095
12096THREADED_TEST(ReverseGetFromCache) {
12097 i::FLAG_allow_natives_syntax = true;
12098 v8::HandleScope scope;
12099
12100 LocalContext context;
12101
12102 const char* code =
12103 "(function() {"
12104 " var keys = [];"
12105 " for (var i = 0; i < 16; i++) keys.push(i);"
12106 " var values = [];"
12107 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
12108 " for (var i = 15; i >= 16; i--) {"
12109 " var v = %_GetFromCache(0, keys[i]);"
12110 " if (v !== values[i])"
12111 " return 'Wrong value for ' + "
12112 " keys[i] + ': ' + v + ' vs. ' + values[i];"
12113 " };"
12114 " return 'PASSED';"
12115 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012116 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012117 ExpectString(code, "PASSED");
12118}
12119
12120
12121THREADED_TEST(TestEviction) {
12122 i::FLAG_allow_natives_syntax = true;
12123 v8::HandleScope scope;
12124
12125 LocalContext context;
12126
12127 const char* code =
12128 "(function() {"
12129 " for (var i = 0; i < 2*16; i++) {"
12130 " %_GetFromCache(0, 'a' + i);"
12131 " };"
12132 " return 'PASSED';"
12133 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010012134 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010012135 ExpectString(code, "PASSED");
12136}
Steve Block8defd9f2010-07-08 12:39:36 +010012137
12138
12139THREADED_TEST(TwoByteStringInAsciiCons) {
12140 // See Chromium issue 47824.
12141 v8::HandleScope scope;
12142
12143 LocalContext context;
12144 const char* init_code =
12145 "var str1 = 'abelspendabel';"
12146 "var str2 = str1 + str1 + str1;"
12147 "str2;";
12148 Local<Value> result = CompileRun(init_code);
12149
12150 CHECK(result->IsString());
12151 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
12152 int length = string->length();
12153 CHECK(string->IsAsciiRepresentation());
12154
12155 FlattenString(string);
12156 i::Handle<i::String> flat_string = FlattenGetString(string);
12157
12158 CHECK(string->IsAsciiRepresentation());
12159 CHECK(flat_string->IsAsciiRepresentation());
12160
12161 // Create external resource.
12162 uint16_t* uc16_buffer = new uint16_t[length + 1];
12163
12164 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
12165 uc16_buffer[length] = 0;
12166
12167 TestResource resource(uc16_buffer);
12168
12169 flat_string->MakeExternal(&resource);
12170
12171 CHECK(flat_string->IsTwoByteRepresentation());
12172
12173 // At this point, we should have a Cons string which is flat and ASCII,
12174 // with a first half that is a two-byte string (although it only contains
12175 // ASCII characters). This is a valid sequence of steps, and it can happen
12176 // in real pages.
12177
12178 CHECK(string->IsAsciiRepresentation());
12179 i::ConsString* cons = i::ConsString::cast(*string);
12180 CHECK_EQ(0, cons->second()->length());
12181 CHECK(cons->first()->IsTwoByteRepresentation());
12182
12183 // Check that some string operations work.
12184
12185 // Atom RegExp.
12186 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
12187 CHECK_EQ(6, reresult->Int32Value());
12188
12189 // Nonatom RegExp.
12190 reresult = CompileRun("str2.match(/abe./g).length;");
12191 CHECK_EQ(6, reresult->Int32Value());
12192
12193 reresult = CompileRun("str2.search(/bel/g);");
12194 CHECK_EQ(1, reresult->Int32Value());
12195
12196 reresult = CompileRun("str2.search(/be./g);");
12197 CHECK_EQ(1, reresult->Int32Value());
12198
12199 ExpectTrue("/bel/g.test(str2);");
12200
12201 ExpectTrue("/be./g.test(str2);");
12202
12203 reresult = CompileRun("/bel/g.exec(str2);");
12204 CHECK(!reresult->IsNull());
12205
12206 reresult = CompileRun("/be./g.exec(str2);");
12207 CHECK(!reresult->IsNull());
12208
12209 ExpectString("str2.substring(2, 10);", "elspenda");
12210
12211 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
12212
12213 ExpectString("str2.charAt(2);", "e");
12214
12215 reresult = CompileRun("str2.charCodeAt(2);");
12216 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
12217}
Iain Merrick75681382010-08-19 15:07:18 +010012218
12219
12220// Failed access check callback that performs a GC on each invocation.
12221void FailedAccessCheckCallbackGC(Local<v8::Object> target,
12222 v8::AccessType type,
12223 Local<v8::Value> data) {
12224 i::Heap::CollectAllGarbage(true);
12225}
12226
12227
12228TEST(GCInFailedAccessCheckCallback) {
12229 // Install a failed access check callback that performs a GC on each
12230 // invocation. Then force the callback to be called from va
12231
12232 v8::V8::Initialize();
12233 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
12234
12235 v8::HandleScope scope;
12236
12237 // Create an ObjectTemplate for global objects and install access
12238 // check callbacks that will block access.
12239 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
12240 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
12241 IndexedGetAccessBlocker,
12242 v8::Handle<v8::Value>(),
12243 false);
12244
12245 // Create a context and set an x property on it's global object.
12246 LocalContext context0(NULL, global_template);
12247 context0->Global()->Set(v8_str("x"), v8_num(42));
12248 v8::Handle<v8::Object> global0 = context0->Global();
12249
12250 // Create a context with a different security token so that the
12251 // failed access check callback will be called on each access.
12252 LocalContext context1(NULL, global_template);
12253 context1->Global()->Set(v8_str("other"), global0);
12254
12255 // Get property with failed access check.
12256 ExpectUndefined("other.x");
12257
12258 // Get element with failed access check.
12259 ExpectUndefined("other[0]");
12260
12261 // Set property with failed access check.
12262 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
12263 CHECK(result->IsObject());
12264
12265 // Set element with failed access check.
12266 result = CompileRun("other[0] = new Object()");
12267 CHECK(result->IsObject());
12268
12269 // Get property attribute with failed access check.
12270 ExpectFalse("\'x\' in other");
12271
12272 // Get property attribute for element with failed access check.
12273 ExpectFalse("0 in other");
12274
12275 // Delete property.
12276 ExpectFalse("delete other.x");
12277
12278 // Delete element.
12279 CHECK_EQ(false, global0->Delete(0));
12280
12281 // DefineAccessor.
12282 CHECK_EQ(false,
12283 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
12284
12285 // Define JavaScript accessor.
12286 ExpectUndefined("Object.prototype.__defineGetter__.call("
12287 " other, \'x\', function() { return 42; })");
12288
12289 // LookupAccessor.
12290 ExpectUndefined("Object.prototype.__lookupGetter__.call("
12291 " other, \'x\')");
12292
12293 // HasLocalElement.
12294 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
12295
12296 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
12297 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
12298 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
12299
12300 // Reset the failed access check callback so it does not influence
12301 // the other tests.
12302 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
12303}
Kristian Monsen0d5e1162010-09-30 15:31:59 +010012304
12305
12306TEST(StringCheckMultipleContexts) {
12307 const char* code =
12308 "(function() { return \"a\".charAt(0); })()";
12309
12310 {
12311 // Run the code twice in the first context to initialize the call IC.
12312 v8::HandleScope scope;
12313 LocalContext context1;
12314 ExpectString(code, "a");
12315 ExpectString(code, "a");
12316 }
12317
12318 {
12319 // Change the String.prototype in the second context and check
12320 // that the right function gets called.
12321 v8::HandleScope scope;
12322 LocalContext context2;
12323 CompileRun("String.prototype.charAt = function() { return \"not a\"; }");
12324 ExpectString(code, "not a");
12325 }
12326}
12327
12328
12329TEST(NumberCheckMultipleContexts) {
12330 const char* code =
12331 "(function() { return (42).toString(); })()";
12332
12333 {
12334 // Run the code twice in the first context to initialize the call IC.
12335 v8::HandleScope scope;
12336 LocalContext context1;
12337 ExpectString(code, "42");
12338 ExpectString(code, "42");
12339 }
12340
12341 {
12342 // Change the Number.prototype in the second context and check
12343 // that the right function gets called.
12344 v8::HandleScope scope;
12345 LocalContext context2;
12346 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }");
12347 ExpectString(code, "not 42");
12348 }
12349}
12350
12351
12352TEST(BooleanCheckMultipleContexts) {
12353 const char* code =
12354 "(function() { return true.toString(); })()";
12355
12356 {
12357 // Run the code twice in the first context to initialize the call IC.
12358 v8::HandleScope scope;
12359 LocalContext context1;
12360 ExpectString(code, "true");
12361 ExpectString(code, "true");
12362 }
12363
12364 {
12365 // Change the Boolean.prototype in the second context and check
12366 // that the right function gets called.
12367 v8::HandleScope scope;
12368 LocalContext context2;
12369 CompileRun("Boolean.prototype.toString = function() { return \"\"; }");
12370 ExpectString(code, "");
12371 }
12372}
Ben Murdochf87a2032010-10-22 12:50:53 +010012373
12374
12375TEST(DontDeleteCellLoadIC) {
12376 const char* function_code =
12377 "function readCell() { while (true) { return cell; } }";
12378
12379 {
12380 // Run the code twice in the first context to initialize the load
12381 // IC for a don't delete cell.
12382 v8::HandleScope scope;
12383 LocalContext context1;
12384 CompileRun("var cell = \"first\";");
12385 ExpectBoolean("delete cell", false);
12386 CompileRun(function_code);
12387 ExpectString("readCell()", "first");
12388 ExpectString("readCell()", "first");
12389 }
12390
12391 {
12392 // Use a deletable cell in the second context.
12393 v8::HandleScope scope;
12394 LocalContext context2;
12395 CompileRun("cell = \"second\";");
12396 CompileRun(function_code);
12397 ExpectString("readCell()", "second");
12398 ExpectBoolean("delete cell", true);
12399 ExpectString("(function() {"
12400 " try {"
12401 " return readCell();"
12402 " } catch(e) {"
12403 " return e.toString();"
12404 " }"
12405 "})()",
12406 "ReferenceError: cell is not defined");
12407 CompileRun("cell = \"new_second\";");
12408 i::Heap::CollectAllGarbage(true);
12409 ExpectString("readCell()", "new_second");
12410 ExpectString("readCell()", "new_second");
12411 }
12412}
12413
12414
12415TEST(DontDeleteCellLoadICForceDelete) {
12416 const char* function_code =
12417 "function readCell() { while (true) { return cell; } }";
12418
12419 // Run the code twice to initialize the load IC for a don't delete
12420 // cell.
12421 v8::HandleScope scope;
12422 LocalContext context;
12423 CompileRun("var cell = \"value\";");
12424 ExpectBoolean("delete cell", false);
12425 CompileRun(function_code);
12426 ExpectString("readCell()", "value");
12427 ExpectString("readCell()", "value");
12428
12429 // Delete the cell using the API and check the inlined code works
12430 // correctly.
12431 CHECK(context->Global()->ForceDelete(v8_str("cell")));
12432 ExpectString("(function() {"
12433 " try {"
12434 " return readCell();"
12435 " } catch(e) {"
12436 " return e.toString();"
12437 " }"
12438 "})()",
12439 "ReferenceError: cell is not defined");
12440}
12441
12442
12443TEST(DontDeleteCellLoadICAPI) {
12444 const char* function_code =
12445 "function readCell() { while (true) { return cell; } }";
12446
12447 // Run the code twice to initialize the load IC for a don't delete
12448 // cell created using the API.
12449 v8::HandleScope scope;
12450 LocalContext context;
12451 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete);
12452 ExpectBoolean("delete cell", false);
12453 CompileRun(function_code);
12454 ExpectString("readCell()", "value");
12455 ExpectString("readCell()", "value");
12456
12457 // Delete the cell using the API and check the inlined code works
12458 // correctly.
12459 CHECK(context->Global()->ForceDelete(v8_str("cell")));
12460 ExpectString("(function() {"
12461 " try {"
12462 " return readCell();"
12463 " } catch(e) {"
12464 " return e.toString();"
12465 " }"
12466 "})()",
12467 "ReferenceError: cell is not defined");
12468}
12469
12470
12471TEST(GlobalLoadICGC) {
12472 const char* function_code =
12473 "function readCell() { while (true) { return cell; } }";
12474
12475 // Check inline load code for a don't delete cell is cleared during
12476 // GC.
12477 {
12478 v8::HandleScope scope;
12479 LocalContext context;
12480 CompileRun("var cell = \"value\";");
12481 ExpectBoolean("delete cell", false);
12482 CompileRun(function_code);
12483 ExpectString("readCell()", "value");
12484 ExpectString("readCell()", "value");
12485 }
12486 {
12487 v8::HandleScope scope;
12488 LocalContext context2;
12489 // Hold the code object in the second context.
12490 CompileRun(function_code);
12491 CheckSurvivingGlobalObjectsCount(1);
12492 }
12493
12494 // Check inline load code for a deletable cell is cleared during GC.
12495 {
12496 v8::HandleScope scope;
12497 LocalContext context;
12498 CompileRun("cell = \"value\";");
12499 CompileRun(function_code);
12500 ExpectString("readCell()", "value");
12501 ExpectString("readCell()", "value");
12502 }
12503 {
12504 v8::HandleScope scope;
12505 LocalContext context2;
12506 // Hold the code object in the second context.
12507 CompileRun(function_code);
12508 CheckSurvivingGlobalObjectsCount(1);
12509 }
12510}
12511
12512
12513TEST(RegExp) {
12514 v8::HandleScope scope;
12515 LocalContext context;
12516
12517 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone);
12518 CHECK(re->IsRegExp());
12519 CHECK(re->GetSource()->Equals(v8_str("foo")));
12520 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
12521
12522 re = v8::RegExp::New(v8_str("bar"),
12523 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
12524 v8::RegExp::kGlobal));
12525 CHECK(re->IsRegExp());
12526 CHECK(re->GetSource()->Equals(v8_str("bar")));
12527 CHECK_EQ(static_cast<int>(re->GetFlags()),
12528 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal);
12529
12530 re = v8::RegExp::New(v8_str("baz"),
12531 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
12532 v8::RegExp::kMultiline));
12533 CHECK(re->IsRegExp());
12534 CHECK(re->GetSource()->Equals(v8_str("baz")));
12535 CHECK_EQ(static_cast<int>(re->GetFlags()),
12536 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
12537
12538 re = CompileRun("/quux/").As<v8::RegExp>();
12539 CHECK(re->IsRegExp());
12540 CHECK(re->GetSource()->Equals(v8_str("quux")));
12541 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
12542
12543 re = CompileRun("/quux/gm").As<v8::RegExp>();
12544 CHECK(re->IsRegExp());
12545 CHECK(re->GetSource()->Equals(v8_str("quux")));
12546 CHECK_EQ(static_cast<int>(re->GetFlags()),
12547 v8::RegExp::kGlobal | v8::RegExp::kMultiline);
12548
12549 // Override the RegExp constructor and check the API constructor
12550 // still works.
12551 CompileRun("RegExp = function() {}");
12552
12553 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone);
12554 CHECK(re->IsRegExp());
12555 CHECK(re->GetSource()->Equals(v8_str("foobar")));
12556 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
12557
12558 re = v8::RegExp::New(v8_str("foobarbaz"),
12559 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
12560 v8::RegExp::kMultiline));
12561 CHECK(re->IsRegExp());
12562 CHECK(re->GetSource()->Equals(v8_str("foobarbaz")));
12563 CHECK_EQ(static_cast<int>(re->GetFlags()),
12564 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
12565
12566 context->Global()->Set(v8_str("re"), re);
12567 ExpectTrue("re.test('FoobarbaZ')");
12568
12569 v8::TryCatch try_catch;
12570 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone);
12571 CHECK(re.IsEmpty());
12572 CHECK(try_catch.HasCaught());
12573 context->Global()->Set(v8_str("ex"), try_catch.Exception());
12574 ExpectTrue("ex instanceof SyntaxError");
12575}
12576
12577
Steve Block1e0659c2011-05-24 12:43:12 +010012578THREADED_TEST(Equals) {
12579 v8::HandleScope handleScope;
12580 LocalContext localContext;
12581
12582 v8::Handle<v8::Object> globalProxy = localContext->Global();
12583 v8::Handle<Value> global = globalProxy->GetPrototype();
12584
12585 CHECK(global->StrictEquals(global));
12586 CHECK(!global->StrictEquals(globalProxy));
12587 CHECK(!globalProxy->StrictEquals(global));
12588 CHECK(globalProxy->StrictEquals(globalProxy));
12589
12590 CHECK(global->Equals(global));
12591 CHECK(!global->Equals(globalProxy));
12592 CHECK(!globalProxy->Equals(global));
12593 CHECK(globalProxy->Equals(globalProxy));
12594}
12595
12596
Ben Murdochf87a2032010-10-22 12:50:53 +010012597static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
12598 const v8::AccessorInfo& info ) {
12599 return v8_str("42!");
12600}
12601
12602
12603static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) {
12604 v8::Handle<v8::Array> result = v8::Array::New();
12605 result->Set(0, v8_str("universalAnswer"));
12606 return result;
12607}
12608
12609
12610TEST(NamedEnumeratorAndForIn) {
12611 v8::HandleScope handle_scope;
12612 LocalContext context;
12613 v8::Context::Scope context_scope(context.local());
12614
12615 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New();
12616 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
12617 context->Global()->Set(v8_str("o"), tmpl->NewInstance());
12618 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
12619 "var result = []; for (var k in o) result.push(k); result"));
12620 CHECK_EQ(1, result->Length());
12621 CHECK_EQ(v8_str("universalAnswer"), result->Get(0));
12622}
Steve Block1e0659c2011-05-24 12:43:12 +010012623
12624
12625TEST(DefinePropertyPostDetach) {
12626 v8::HandleScope scope;
12627 LocalContext context;
12628 v8::Handle<v8::Object> proxy = context->Global();
12629 v8::Handle<v8::Function> define_property =
12630 CompileRun("(function() {"
12631 " Object.defineProperty("
12632 " this,"
12633 " 1,"
12634 " { configurable: true, enumerable: true, value: 3 });"
12635 "})").As<Function>();
12636 context->DetachGlobal();
12637 define_property->Call(proxy, 0, NULL);
12638}