blob: 8ce7a79a9be02c97ab3d22196c3098379cb6d84f [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
817THREADED_TEST(FindInstanceInPrototypeChain) {
818 v8::HandleScope scope;
819 LocalContext env;
820
821 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
822 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
823 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
824 derived->Inherit(base);
825
826 Local<v8::Function> base_function = base->GetFunction();
827 Local<v8::Function> derived_function = derived->GetFunction();
828 Local<v8::Function> other_function = other->GetFunction();
829
830 Local<v8::Object> base_instance = base_function->NewInstance();
831 Local<v8::Object> derived_instance = derived_function->NewInstance();
832 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
833 Local<v8::Object> other_instance = other_function->NewInstance();
834 derived_instance2->Set(v8_str("__proto__"), derived_instance);
835 other_instance->Set(v8_str("__proto__"), derived_instance2);
836
837 // base_instance is only an instance of base.
838 CHECK_EQ(base_instance,
839 base_instance->FindInstanceInPrototypeChain(base));
840 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
841 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
842
843 // derived_instance is an instance of base and derived.
844 CHECK_EQ(derived_instance,
845 derived_instance->FindInstanceInPrototypeChain(base));
846 CHECK_EQ(derived_instance,
847 derived_instance->FindInstanceInPrototypeChain(derived));
848 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
849
850 // other_instance is an instance of other and its immediate
851 // prototype derived_instance2 is an instance of base and derived.
852 // Note, derived_instance is an instance of base and derived too,
853 // but it comes after derived_instance2 in the prototype chain of
854 // other_instance.
855 CHECK_EQ(derived_instance2,
856 other_instance->FindInstanceInPrototypeChain(base));
857 CHECK_EQ(derived_instance2,
858 other_instance->FindInstanceInPrototypeChain(derived));
859 CHECK_EQ(other_instance,
860 other_instance->FindInstanceInPrototypeChain(other));
861}
862
863
Steve Block3ce2e202009-11-05 08:53:23 +0000864THREADED_TEST(TinyInteger) {
865 v8::HandleScope scope;
866 LocalContext env;
867 int32_t value = 239;
868 Local<v8::Integer> value_obj = v8::Integer::New(value);
869 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
870}
871
872
873THREADED_TEST(BigSmiInteger) {
874 v8::HandleScope scope;
875 LocalContext env;
876 int32_t value = i::Smi::kMaxValue;
877 // We cannot add one to a Smi::kMaxValue without wrapping.
878 if (i::kSmiValueSize < 32) {
879 CHECK(i::Smi::IsValid(value));
880 CHECK(!i::Smi::IsValid(value + 1));
881 Local<v8::Integer> value_obj = v8::Integer::New(value);
882 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
883 }
884}
885
886
887THREADED_TEST(BigInteger) {
888 v8::HandleScope scope;
889 LocalContext env;
890 // We cannot add one to a Smi::kMaxValue without wrapping.
891 if (i::kSmiValueSize < 32) {
892 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
893 // The code will not be run in that case, due to the "if" guard.
894 int32_t value =
895 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
896 CHECK(value > i::Smi::kMaxValue);
897 CHECK(!i::Smi::IsValid(value));
898 Local<v8::Integer> value_obj = v8::Integer::New(value);
899 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
900 }
901}
902
903
904THREADED_TEST(TinyUnsignedInteger) {
905 v8::HandleScope scope;
906 LocalContext env;
907 uint32_t value = 239;
908 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
909 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
910}
911
912
913THREADED_TEST(BigUnsignedSmiInteger) {
914 v8::HandleScope scope;
915 LocalContext env;
916 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
917 CHECK(i::Smi::IsValid(value));
918 CHECK(!i::Smi::IsValid(value + 1));
919 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
920 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
921}
922
923
924THREADED_TEST(BigUnsignedInteger) {
925 v8::HandleScope scope;
926 LocalContext env;
927 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
928 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
929 CHECK(!i::Smi::IsValid(value));
930 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
931 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
932}
933
934
935THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
936 v8::HandleScope scope;
937 LocalContext env;
938 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
939 uint32_t value = INT32_MAX_AS_UINT + 1;
940 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
941 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
942 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
943}
944
945
Steve Blocka7e24c12009-10-30 11:49:00 +0000946THREADED_TEST(Number) {
947 v8::HandleScope scope;
948 LocalContext env;
949 double PI = 3.1415926;
950 Local<v8::Number> pi_obj = v8::Number::New(PI);
951 CHECK_EQ(PI, pi_obj->NumberValue());
952}
953
954
955THREADED_TEST(ToNumber) {
956 v8::HandleScope scope;
957 LocalContext env;
958 Local<String> str = v8_str("3.1415926");
959 CHECK_EQ(3.1415926, str->NumberValue());
960 v8::Handle<v8::Boolean> t = v8::True();
961 CHECK_EQ(1.0, t->NumberValue());
962 v8::Handle<v8::Boolean> f = v8::False();
963 CHECK_EQ(0.0, f->NumberValue());
964}
965
966
967THREADED_TEST(Date) {
968 v8::HandleScope scope;
969 LocalContext env;
970 double PI = 3.1415926;
971 Local<Value> date_obj = v8::Date::New(PI);
972 CHECK_EQ(3.0, date_obj->NumberValue());
973}
974
975
976THREADED_TEST(Boolean) {
977 v8::HandleScope scope;
978 LocalContext env;
979 v8::Handle<v8::Boolean> t = v8::True();
980 CHECK(t->Value());
981 v8::Handle<v8::Boolean> f = v8::False();
982 CHECK(!f->Value());
983 v8::Handle<v8::Primitive> u = v8::Undefined();
984 CHECK(!u->BooleanValue());
985 v8::Handle<v8::Primitive> n = v8::Null();
986 CHECK(!n->BooleanValue());
987 v8::Handle<String> str1 = v8_str("");
988 CHECK(!str1->BooleanValue());
989 v8::Handle<String> str2 = v8_str("x");
990 CHECK(str2->BooleanValue());
991 CHECK(!v8::Number::New(0)->BooleanValue());
992 CHECK(v8::Number::New(-1)->BooleanValue());
993 CHECK(v8::Number::New(1)->BooleanValue());
994 CHECK(v8::Number::New(42)->BooleanValue());
995 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
996}
997
998
999static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
1000 ApiTestFuzzer::Fuzz();
1001 return v8_num(13.4);
1002}
1003
1004
1005static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
1006 ApiTestFuzzer::Fuzz();
1007 return v8_num(876);
1008}
1009
1010
1011THREADED_TEST(GlobalPrototype) {
1012 v8::HandleScope scope;
1013 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
1014 func_templ->PrototypeTemplate()->Set(
1015 "dummy",
1016 v8::FunctionTemplate::New(DummyCallHandler));
1017 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
1018 templ->Set("x", v8_num(200));
1019 templ->SetAccessor(v8_str("m"), GetM);
1020 LocalContext env(0, templ);
1021 v8::Handle<v8::Object> obj = env->Global();
1022 v8::Handle<Script> script = v8_compile("dummy()");
1023 v8::Handle<Value> result = script->Run();
1024 CHECK_EQ(13.4, result->NumberValue());
1025 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1026 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1027}
1028
1029
Steve Blocka7e24c12009-10-30 11:49:00 +00001030THREADED_TEST(ObjectTemplate) {
1031 v8::HandleScope scope;
1032 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1033 templ1->Set("x", v8_num(10));
1034 templ1->Set("y", v8_num(13));
1035 LocalContext env;
1036 Local<v8::Object> instance1 = templ1->NewInstance();
1037 env->Global()->Set(v8_str("p"), instance1);
1038 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1039 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1040 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1041 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1042 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1043 templ2->Set("a", v8_num(12));
1044 templ2->Set("b", templ1);
1045 Local<v8::Object> instance2 = templ2->NewInstance();
1046 env->Global()->Set(v8_str("q"), instance2);
1047 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1048 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1049 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1050 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1051}
1052
1053
1054static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1055 ApiTestFuzzer::Fuzz();
1056 return v8_num(17.2);
1057}
1058
1059
1060static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1061 ApiTestFuzzer::Fuzz();
1062 return v8_num(15.2);
1063}
1064
1065
1066THREADED_TEST(DescriptorInheritance) {
1067 v8::HandleScope scope;
1068 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1069 super->PrototypeTemplate()->Set("flabby",
1070 v8::FunctionTemplate::New(GetFlabby));
1071 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1072
1073 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1074
1075 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1076 base1->Inherit(super);
1077 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1078
1079 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1080 base2->Inherit(super);
1081 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1082
1083 LocalContext env;
1084
1085 env->Global()->Set(v8_str("s"), super->GetFunction());
1086 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1087 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1088
1089 // Checks right __proto__ chain.
1090 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1091 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1092
1093 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1094
1095 // Instance accessor should not be visible on function object or its prototype
1096 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1097 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1098 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1099
1100 env->Global()->Set(v8_str("obj"),
1101 base1->GetFunction()->NewInstance());
1102 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1103 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1104 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1105 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1106 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1107
1108 env->Global()->Set(v8_str("obj2"),
1109 base2->GetFunction()->NewInstance());
1110 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1111 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1112 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1113 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1114 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1115
1116 // base1 and base2 cannot cross reference to each's prototype
1117 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1118 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1119}
1120
1121
1122int echo_named_call_count;
1123
1124
1125static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1126 const AccessorInfo& info) {
1127 ApiTestFuzzer::Fuzz();
1128 CHECK_EQ(v8_str("data"), info.Data());
1129 echo_named_call_count++;
1130 return name;
1131}
1132
1133
1134THREADED_TEST(NamedPropertyHandlerGetter) {
1135 echo_named_call_count = 0;
1136 v8::HandleScope scope;
1137 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1138 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1139 0, 0, 0, 0,
1140 v8_str("data"));
1141 LocalContext env;
1142 env->Global()->Set(v8_str("obj"),
1143 templ->GetFunction()->NewInstance());
1144 CHECK_EQ(echo_named_call_count, 0);
1145 v8_compile("obj.x")->Run();
1146 CHECK_EQ(echo_named_call_count, 1);
1147 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1148 v8::Handle<Value> str = CompileRun(code);
1149 String::AsciiValue value(str);
1150 CHECK_EQ(*value, "oddlepoddle");
1151 // Check default behavior
1152 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1153 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1154 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1155}
1156
1157
1158int echo_indexed_call_count = 0;
1159
1160
1161static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1162 const AccessorInfo& info) {
1163 ApiTestFuzzer::Fuzz();
1164 CHECK_EQ(v8_num(637), info.Data());
1165 echo_indexed_call_count++;
1166 return v8_num(index);
1167}
1168
1169
1170THREADED_TEST(IndexedPropertyHandlerGetter) {
1171 v8::HandleScope scope;
1172 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1173 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1174 0, 0, 0, 0,
1175 v8_num(637));
1176 LocalContext env;
1177 env->Global()->Set(v8_str("obj"),
1178 templ->GetFunction()->NewInstance());
1179 Local<Script> script = v8_compile("obj[900]");
1180 CHECK_EQ(script->Run()->Int32Value(), 900);
1181}
1182
1183
1184v8::Handle<v8::Object> bottom;
1185
1186static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1187 uint32_t index,
1188 const AccessorInfo& info) {
1189 ApiTestFuzzer::Fuzz();
1190 CHECK(info.This()->Equals(bottom));
1191 return v8::Handle<Value>();
1192}
1193
1194static v8::Handle<Value> CheckThisNamedPropertyHandler(
1195 Local<String> name,
1196 const AccessorInfo& info) {
1197 ApiTestFuzzer::Fuzz();
1198 CHECK(info.This()->Equals(bottom));
1199 return v8::Handle<Value>();
1200}
1201
1202
1203v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1204 Local<Value> value,
1205 const AccessorInfo& info) {
1206 ApiTestFuzzer::Fuzz();
1207 CHECK(info.This()->Equals(bottom));
1208 return v8::Handle<Value>();
1209}
1210
1211
1212v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1213 Local<Value> value,
1214 const AccessorInfo& info) {
1215 ApiTestFuzzer::Fuzz();
1216 CHECK(info.This()->Equals(bottom));
1217 return v8::Handle<Value>();
1218}
1219
Iain Merrick75681382010-08-19 15:07:18 +01001220v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery(
Steve Blocka7e24c12009-10-30 11:49:00 +00001221 uint32_t index,
1222 const AccessorInfo& info) {
1223 ApiTestFuzzer::Fuzz();
1224 CHECK(info.This()->Equals(bottom));
Iain Merrick75681382010-08-19 15:07:18 +01001225 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001226}
1227
1228
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001229v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001230 const AccessorInfo& info) {
1231 ApiTestFuzzer::Fuzz();
1232 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001233 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001234}
1235
1236
1237v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1238 uint32_t index,
1239 const AccessorInfo& info) {
1240 ApiTestFuzzer::Fuzz();
1241 CHECK(info.This()->Equals(bottom));
1242 return v8::Handle<v8::Boolean>();
1243}
1244
1245
1246v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1247 Local<String> property,
1248 const AccessorInfo& info) {
1249 ApiTestFuzzer::Fuzz();
1250 CHECK(info.This()->Equals(bottom));
1251 return v8::Handle<v8::Boolean>();
1252}
1253
1254
1255v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1256 const AccessorInfo& info) {
1257 ApiTestFuzzer::Fuzz();
1258 CHECK(info.This()->Equals(bottom));
1259 return v8::Handle<v8::Array>();
1260}
1261
1262
1263v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1264 const AccessorInfo& info) {
1265 ApiTestFuzzer::Fuzz();
1266 CHECK(info.This()->Equals(bottom));
1267 return v8::Handle<v8::Array>();
1268}
1269
1270
1271THREADED_TEST(PropertyHandlerInPrototype) {
1272 v8::HandleScope scope;
1273 LocalContext env;
1274
1275 // Set up a prototype chain with three interceptors.
1276 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1277 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1278 CheckThisIndexedPropertyHandler,
1279 CheckThisIndexedPropertySetter,
1280 CheckThisIndexedPropertyQuery,
1281 CheckThisIndexedPropertyDeleter,
1282 CheckThisIndexedPropertyEnumerator);
1283
1284 templ->InstanceTemplate()->SetNamedPropertyHandler(
1285 CheckThisNamedPropertyHandler,
1286 CheckThisNamedPropertySetter,
1287 CheckThisNamedPropertyQuery,
1288 CheckThisNamedPropertyDeleter,
1289 CheckThisNamedPropertyEnumerator);
1290
1291 bottom = templ->GetFunction()->NewInstance();
1292 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1293 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1294
1295 bottom->Set(v8_str("__proto__"), middle);
1296 middle->Set(v8_str("__proto__"), top);
1297 env->Global()->Set(v8_str("obj"), bottom);
1298
1299 // Indexed and named get.
1300 Script::Compile(v8_str("obj[0]"))->Run();
1301 Script::Compile(v8_str("obj.x"))->Run();
1302
1303 // Indexed and named set.
1304 Script::Compile(v8_str("obj[1] = 42"))->Run();
1305 Script::Compile(v8_str("obj.y = 42"))->Run();
1306
1307 // Indexed and named query.
1308 Script::Compile(v8_str("0 in obj"))->Run();
1309 Script::Compile(v8_str("'x' in obj"))->Run();
1310
1311 // Indexed and named deleter.
1312 Script::Compile(v8_str("delete obj[0]"))->Run();
1313 Script::Compile(v8_str("delete obj.x"))->Run();
1314
1315 // Enumerators.
1316 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1317}
1318
1319
1320static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1321 const AccessorInfo& info) {
1322 ApiTestFuzzer::Fuzz();
1323 if (v8_str("pre")->Equals(key)) {
1324 return v8_str("PrePropertyHandler: pre");
1325 }
1326 return v8::Handle<String>();
1327}
1328
1329
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001330static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1331 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001332 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001333 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001334 }
1335
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001336 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001337}
1338
1339
1340THREADED_TEST(PrePropertyHandler) {
1341 v8::HandleScope scope;
1342 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1343 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1344 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001345 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001346 LocalContext env(NULL, desc->InstanceTemplate());
1347 Script::Compile(v8_str(
1348 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1349 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1350 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1351 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1352 CHECK_EQ(v8_str("Object: on"), result_on);
1353 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1354 CHECK(result_post.IsEmpty());
1355}
1356
1357
1358THREADED_TEST(UndefinedIsNotEnumerable) {
1359 v8::HandleScope scope;
1360 LocalContext env;
1361 v8::Handle<Value> result = Script::Compile(v8_str(
1362 "this.propertyIsEnumerable(undefined)"))->Run();
1363 CHECK(result->IsFalse());
1364}
1365
1366
1367v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001368static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001369
1370
1371static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1372 ApiTestFuzzer::Fuzz();
1373 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1374 if (depth == kTargetRecursionDepth) return v8::Undefined();
1375 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1376 return call_recursively_script->Run();
1377}
1378
1379
1380static v8::Handle<Value> CallFunctionRecursivelyCall(
1381 const v8::Arguments& args) {
1382 ApiTestFuzzer::Fuzz();
1383 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1384 if (depth == kTargetRecursionDepth) {
1385 printf("[depth = %d]\n", depth);
1386 return v8::Undefined();
1387 }
1388 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1389 v8::Handle<Value> function =
1390 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001391 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001392}
1393
1394
1395THREADED_TEST(DeepCrossLanguageRecursion) {
1396 v8::HandleScope scope;
1397 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1398 global->Set(v8_str("callScriptRecursively"),
1399 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1400 global->Set(v8_str("callFunctionRecursively"),
1401 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1402 LocalContext env(NULL, global);
1403
1404 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1405 call_recursively_script = v8_compile("callScriptRecursively()");
1406 v8::Handle<Value> result = call_recursively_script->Run();
1407 call_recursively_script = v8::Handle<Script>();
1408
1409 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1410 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1411}
1412
1413
1414static v8::Handle<Value>
1415 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1416 ApiTestFuzzer::Fuzz();
1417 return v8::ThrowException(key);
1418}
1419
1420
1421static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1422 Local<Value>,
1423 const AccessorInfo&) {
1424 v8::ThrowException(key);
1425 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1426}
1427
1428
1429THREADED_TEST(CallbackExceptionRegression) {
1430 v8::HandleScope scope;
1431 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1432 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1433 ThrowingPropertyHandlerSet);
1434 LocalContext env;
1435 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1436 v8::Handle<Value> otto = Script::Compile(v8_str(
1437 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1438 CHECK_EQ(v8_str("otto"), otto);
1439 v8::Handle<Value> netto = Script::Compile(v8_str(
1440 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1441 CHECK_EQ(v8_str("netto"), netto);
1442}
1443
1444
Steve Blocka7e24c12009-10-30 11:49:00 +00001445THREADED_TEST(FunctionPrototype) {
1446 v8::HandleScope scope;
1447 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1448 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1449 LocalContext env;
1450 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1451 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1452 CHECK_EQ(script->Run()->Int32Value(), 321);
1453}
1454
1455
1456THREADED_TEST(InternalFields) {
1457 v8::HandleScope scope;
1458 LocalContext env;
1459
1460 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1461 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1462 instance_templ->SetInternalFieldCount(1);
1463 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1464 CHECK_EQ(1, obj->InternalFieldCount());
1465 CHECK(obj->GetInternalField(0)->IsUndefined());
1466 obj->SetInternalField(0, v8_num(17));
1467 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1468}
1469
1470
Steve Block6ded16b2010-05-10 14:33:55 +01001471THREADED_TEST(GlobalObjectInternalFields) {
1472 v8::HandleScope scope;
1473 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1474 global_template->SetInternalFieldCount(1);
1475 LocalContext env(NULL, global_template);
1476 v8::Handle<v8::Object> global_proxy = env->Global();
1477 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1478 CHECK_EQ(1, global->InternalFieldCount());
1479 CHECK(global->GetInternalField(0)->IsUndefined());
1480 global->SetInternalField(0, v8_num(17));
1481 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1482}
1483
1484
Steve Blocka7e24c12009-10-30 11:49:00 +00001485THREADED_TEST(InternalFieldsNativePointers) {
1486 v8::HandleScope scope;
1487 LocalContext env;
1488
1489 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1490 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1491 instance_templ->SetInternalFieldCount(1);
1492 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1493 CHECK_EQ(1, obj->InternalFieldCount());
1494 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1495
1496 char* data = new char[100];
1497
1498 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001499 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001500 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001501 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001502
1503 // Check reading and writing aligned pointers.
1504 obj->SetPointerInInternalField(0, aligned);
1505 i::Heap::CollectAllGarbage(false);
1506 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1507
1508 // Check reading and writing unaligned pointers.
1509 obj->SetPointerInInternalField(0, unaligned);
1510 i::Heap::CollectAllGarbage(false);
1511 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1512
1513 delete[] data;
1514}
1515
1516
Steve Block3ce2e202009-11-05 08:53:23 +00001517THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1518 v8::HandleScope scope;
1519 LocalContext env;
1520
1521 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1522 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1523 instance_templ->SetInternalFieldCount(1);
1524 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1525 CHECK_EQ(1, obj->InternalFieldCount());
1526 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1527
1528 char* data = new char[100];
1529
1530 void* aligned = data;
Ben Murdochf87a2032010-10-22 12:50:53 +01001531 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001532 void* unaligned = data + 1;
Ben Murdochf87a2032010-10-22 12:50:53 +01001533 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1));
Steve Block3ce2e202009-11-05 08:53:23 +00001534
1535 obj->SetPointerInInternalField(0, aligned);
1536 i::Heap::CollectAllGarbage(false);
1537 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1538
1539 obj->SetPointerInInternalField(0, unaligned);
1540 i::Heap::CollectAllGarbage(false);
1541 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1542
1543 obj->SetInternalField(0, v8::External::Wrap(aligned));
1544 i::Heap::CollectAllGarbage(false);
1545 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1546
1547 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1548 i::Heap::CollectAllGarbage(false);
1549 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1550
1551 delete[] data;
1552}
1553
1554
Steve Blocka7e24c12009-10-30 11:49:00 +00001555THREADED_TEST(IdentityHash) {
1556 v8::HandleScope scope;
1557 LocalContext env;
1558
1559 // Ensure that the test starts with an fresh heap to test whether the hash
1560 // code is based on the address.
1561 i::Heap::CollectAllGarbage(false);
1562 Local<v8::Object> obj = v8::Object::New();
1563 int hash = obj->GetIdentityHash();
1564 int hash1 = obj->GetIdentityHash();
1565 CHECK_EQ(hash, hash1);
1566 int hash2 = v8::Object::New()->GetIdentityHash();
1567 // Since the identity hash is essentially a random number two consecutive
1568 // objects should not be assigned the same hash code. If the test below fails
1569 // the random number generator should be evaluated.
1570 CHECK_NE(hash, hash2);
1571 i::Heap::CollectAllGarbage(false);
1572 int hash3 = v8::Object::New()->GetIdentityHash();
1573 // Make sure that the identity hash is not based on the initial address of
1574 // the object alone. If the test below fails the random number generator
1575 // should be evaluated.
1576 CHECK_NE(hash, hash3);
1577 int hash4 = obj->GetIdentityHash();
1578 CHECK_EQ(hash, hash4);
1579}
1580
1581
1582THREADED_TEST(HiddenProperties) {
1583 v8::HandleScope scope;
1584 LocalContext env;
1585
1586 v8::Local<v8::Object> obj = v8::Object::New();
1587 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1588 v8::Local<v8::String> empty = v8_str("");
1589 v8::Local<v8::String> prop_name = v8_str("prop_name");
1590
1591 i::Heap::CollectAllGarbage(false);
1592
1593 // Make sure delete of a non-existent hidden value works
1594 CHECK(obj->DeleteHiddenValue(key));
1595
1596 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1597 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1598 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1599 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1600
1601 i::Heap::CollectAllGarbage(false);
1602
1603 // Make sure we do not find the hidden property.
1604 CHECK(!obj->Has(empty));
1605 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1606 CHECK(obj->Get(empty)->IsUndefined());
1607 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1608 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1609 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1610 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1611
1612 i::Heap::CollectAllGarbage(false);
1613
1614 // Add another property and delete it afterwards to force the object in
1615 // slow case.
1616 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1617 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1618 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1619 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1620 CHECK(obj->Delete(prop_name));
1621 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1622
1623 i::Heap::CollectAllGarbage(false);
1624
1625 CHECK(obj->DeleteHiddenValue(key));
1626 CHECK(obj->GetHiddenValue(key).IsEmpty());
1627}
1628
1629
Steve Blockd0582a62009-12-15 09:54:21 +00001630static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001631static v8::Handle<Value> InterceptorForHiddenProperties(
1632 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001633 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001634 return v8::Handle<Value>();
1635}
1636
1637
1638THREADED_TEST(HiddenPropertiesWithInterceptors) {
1639 v8::HandleScope scope;
1640 LocalContext context;
1641
Steve Blockd0582a62009-12-15 09:54:21 +00001642 interceptor_for_hidden_properties_called = false;
1643
Steve Blocka7e24c12009-10-30 11:49:00 +00001644 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1645
1646 // Associate an interceptor with an object and start setting hidden values.
1647 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1648 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1649 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1650 Local<v8::Function> function = fun_templ->GetFunction();
1651 Local<v8::Object> obj = function->NewInstance();
1652 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1653 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001654 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001655}
1656
1657
1658THREADED_TEST(External) {
1659 v8::HandleScope scope;
1660 int x = 3;
1661 Local<v8::External> ext = v8::External::New(&x);
1662 LocalContext env;
1663 env->Global()->Set(v8_str("ext"), ext);
1664 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001665 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001666 int* ptr = static_cast<int*>(reext->Value());
1667 CHECK_EQ(x, 3);
1668 *ptr = 10;
1669 CHECK_EQ(x, 10);
1670
1671 // Make sure unaligned pointers are wrapped properly.
1672 char* data = i::StrDup("0123456789");
1673 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1674 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1675 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1676 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1677
1678 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1679 CHECK_EQ('0', *char_ptr);
1680 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1681 CHECK_EQ('1', *char_ptr);
1682 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1683 CHECK_EQ('2', *char_ptr);
1684 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1685 CHECK_EQ('3', *char_ptr);
1686 i::DeleteArray(data);
1687}
1688
1689
1690THREADED_TEST(GlobalHandle) {
1691 v8::Persistent<String> global;
1692 {
1693 v8::HandleScope scope;
1694 Local<String> str = v8_str("str");
1695 global = v8::Persistent<String>::New(str);
1696 }
1697 CHECK_EQ(global->Length(), 3);
1698 global.Dispose();
1699}
1700
1701
1702THREADED_TEST(ScriptException) {
1703 v8::HandleScope scope;
1704 LocalContext env;
1705 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1706 v8::TryCatch try_catch;
1707 Local<Value> result = script->Run();
1708 CHECK(result.IsEmpty());
1709 CHECK(try_catch.HasCaught());
1710 String::AsciiValue exception_value(try_catch.Exception());
1711 CHECK_EQ(*exception_value, "panama!");
1712}
1713
1714
1715bool message_received;
1716
1717
1718static void check_message(v8::Handle<v8::Message> message,
1719 v8::Handle<Value> data) {
1720 CHECK_EQ(5.76, data->NumberValue());
1721 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1722 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1723 message_received = true;
1724}
1725
1726
1727THREADED_TEST(MessageHandlerData) {
1728 message_received = false;
1729 v8::HandleScope scope;
1730 CHECK(!message_received);
1731 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1732 LocalContext context;
1733 v8::ScriptOrigin origin =
1734 v8::ScriptOrigin(v8_str("6.75"));
1735 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1736 &origin);
1737 script->SetData(v8_str("7.56"));
1738 script->Run();
1739 CHECK(message_received);
1740 // clear out the message listener
1741 v8::V8::RemoveMessageListeners(check_message);
1742}
1743
1744
1745THREADED_TEST(GetSetProperty) {
1746 v8::HandleScope scope;
1747 LocalContext context;
1748 context->Global()->Set(v8_str("foo"), v8_num(14));
1749 context->Global()->Set(v8_str("12"), v8_num(92));
1750 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1751 context->Global()->Set(v8_num(13), v8_num(56));
1752 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1753 CHECK_EQ(14, foo->Int32Value());
1754 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1755 CHECK_EQ(92, twelve->Int32Value());
1756 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1757 CHECK_EQ(32, sixteen->Int32Value());
1758 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1759 CHECK_EQ(56, thirteen->Int32Value());
1760 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1761 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1762 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1763 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1764 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1765 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1766 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1767 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1768 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1769}
1770
1771
1772THREADED_TEST(PropertyAttributes) {
1773 v8::HandleScope scope;
1774 LocalContext context;
1775 // read-only
1776 Local<String> prop = v8_str("read_only");
1777 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1778 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1779 Script::Compile(v8_str("read_only = 9"))->Run();
1780 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1781 context->Global()->Set(prop, v8_num(10));
1782 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1783 // dont-delete
1784 prop = v8_str("dont_delete");
1785 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1786 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1787 Script::Compile(v8_str("delete dont_delete"))->Run();
1788 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1789}
1790
1791
1792THREADED_TEST(Array) {
1793 v8::HandleScope scope;
1794 LocalContext context;
1795 Local<v8::Array> array = v8::Array::New();
1796 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001797 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001798 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001799 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001800 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001801 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001802 CHECK_EQ(3, array->Length());
1803 CHECK(!array->Has(0));
1804 CHECK(!array->Has(1));
1805 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001806 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001807 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001808 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001809 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001810 CHECK_EQ(1, arr->Get(0)->Int32Value());
1811 CHECK_EQ(2, arr->Get(1)->Int32Value());
1812 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001813}
1814
1815
1816v8::Handle<Value> HandleF(const v8::Arguments& args) {
1817 v8::HandleScope scope;
1818 ApiTestFuzzer::Fuzz();
1819 Local<v8::Array> result = v8::Array::New(args.Length());
1820 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001821 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001822 return scope.Close(result);
1823}
1824
1825
1826THREADED_TEST(Vector) {
1827 v8::HandleScope scope;
1828 Local<ObjectTemplate> global = ObjectTemplate::New();
1829 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1830 LocalContext context(0, global);
1831
1832 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001833 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001834 CHECK_EQ(0, a0->Length());
1835
1836 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001837 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001838 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001839 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001840
1841 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001842 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001843 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001844 CHECK_EQ(12, a2->Get(0)->Int32Value());
1845 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001846
1847 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001848 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001849 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001850 CHECK_EQ(14, a3->Get(0)->Int32Value());
1851 CHECK_EQ(15, a3->Get(1)->Int32Value());
1852 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001853
1854 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001855 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001856 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001857 CHECK_EQ(17, a4->Get(0)->Int32Value());
1858 CHECK_EQ(18, a4->Get(1)->Int32Value());
1859 CHECK_EQ(19, a4->Get(2)->Int32Value());
1860 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001861}
1862
1863
1864THREADED_TEST(FunctionCall) {
1865 v8::HandleScope scope;
1866 LocalContext context;
1867 CompileRun(
1868 "function Foo() {"
1869 " var result = [];"
1870 " for (var i = 0; i < arguments.length; i++) {"
1871 " result.push(arguments[i]);"
1872 " }"
1873 " return result;"
1874 "}");
1875 Local<Function> Foo =
1876 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1877
1878 v8::Handle<Value>* args0 = NULL;
1879 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1880 CHECK_EQ(0, a0->Length());
1881
1882 v8::Handle<Value> args1[] = { v8_num(1.1) };
1883 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1884 CHECK_EQ(1, a1->Length());
1885 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1886
1887 v8::Handle<Value> args2[] = { v8_num(2.2),
1888 v8_num(3.3) };
1889 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1890 CHECK_EQ(2, a2->Length());
1891 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1892 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1893
1894 v8::Handle<Value> args3[] = { v8_num(4.4),
1895 v8_num(5.5),
1896 v8_num(6.6) };
1897 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1898 CHECK_EQ(3, a3->Length());
1899 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1900 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1901 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1902
1903 v8::Handle<Value> args4[] = { v8_num(7.7),
1904 v8_num(8.8),
1905 v8_num(9.9),
1906 v8_num(10.11) };
1907 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1908 CHECK_EQ(4, a4->Length());
1909 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1910 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1911 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1912 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1913}
1914
1915
1916static const char* js_code_causing_out_of_memory =
1917 "var a = new Array(); while(true) a.push(a);";
1918
1919
1920// These tests run for a long time and prevent us from running tests
1921// that come after them so they cannot run in parallel.
1922TEST(OutOfMemory) {
1923 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001924 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001925 // Set heap limits.
1926 static const int K = 1024;
1927 v8::ResourceConstraints constraints;
1928 constraints.set_max_young_space_size(256 * K);
1929 constraints.set_max_old_space_size(4 * K * K);
1930 v8::SetResourceConstraints(&constraints);
1931
1932 // Execute a script that causes out of memory.
1933 v8::HandleScope scope;
1934 LocalContext context;
1935 v8::V8::IgnoreOutOfMemoryException();
1936 Local<Script> script =
1937 Script::Compile(String::New(js_code_causing_out_of_memory));
1938 Local<Value> result = script->Run();
1939
1940 // Check for out of memory state.
1941 CHECK(result.IsEmpty());
1942 CHECK(context->HasOutOfMemoryException());
1943}
1944
1945
1946v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1947 ApiTestFuzzer::Fuzz();
1948
1949 v8::HandleScope scope;
1950 LocalContext context;
1951 Local<Script> script =
1952 Script::Compile(String::New(js_code_causing_out_of_memory));
1953 Local<Value> result = script->Run();
1954
1955 // Check for out of memory state.
1956 CHECK(result.IsEmpty());
1957 CHECK(context->HasOutOfMemoryException());
1958
1959 return result;
1960}
1961
1962
1963TEST(OutOfMemoryNested) {
1964 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001965 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001966 // Set heap limits.
1967 static const int K = 1024;
1968 v8::ResourceConstraints constraints;
1969 constraints.set_max_young_space_size(256 * K);
1970 constraints.set_max_old_space_size(4 * K * K);
1971 v8::SetResourceConstraints(&constraints);
1972
1973 v8::HandleScope scope;
1974 Local<ObjectTemplate> templ = ObjectTemplate::New();
1975 templ->Set(v8_str("ProvokeOutOfMemory"),
1976 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1977 LocalContext context(0, templ);
1978 v8::V8::IgnoreOutOfMemoryException();
1979 Local<Value> result = CompileRun(
1980 "var thrown = false;"
1981 "try {"
1982 " ProvokeOutOfMemory();"
1983 "} catch (e) {"
1984 " thrown = true;"
1985 "}");
1986 // Check for out of memory state.
1987 CHECK(result.IsEmpty());
1988 CHECK(context->HasOutOfMemoryException());
1989}
1990
1991
1992TEST(HugeConsStringOutOfMemory) {
1993 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001994 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001995 v8::HandleScope scope;
1996 LocalContext context;
1997 // Set heap limits.
1998 static const int K = 1024;
1999 v8::ResourceConstraints constraints;
2000 constraints.set_max_young_space_size(256 * K);
2001 constraints.set_max_old_space_size(2 * K * K);
2002 v8::SetResourceConstraints(&constraints);
2003
2004 // Execute a script that causes out of memory.
2005 v8::V8::IgnoreOutOfMemoryException();
2006
2007 // Build huge string. This should fail with out of memory exception.
2008 Local<Value> result = CompileRun(
2009 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00002010 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00002011
2012 // Check for out of memory state.
2013 CHECK(result.IsEmpty());
2014 CHECK(context->HasOutOfMemoryException());
2015}
2016
2017
2018THREADED_TEST(ConstructCall) {
2019 v8::HandleScope scope;
2020 LocalContext context;
2021 CompileRun(
2022 "function Foo() {"
2023 " var result = [];"
2024 " for (var i = 0; i < arguments.length; i++) {"
2025 " result.push(arguments[i]);"
2026 " }"
2027 " return result;"
2028 "}");
2029 Local<Function> Foo =
2030 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2031
2032 v8::Handle<Value>* args0 = NULL;
2033 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2034 CHECK_EQ(0, a0->Length());
2035
2036 v8::Handle<Value> args1[] = { v8_num(1.1) };
2037 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2038 CHECK_EQ(1, a1->Length());
2039 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2040
2041 v8::Handle<Value> args2[] = { v8_num(2.2),
2042 v8_num(3.3) };
2043 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2044 CHECK_EQ(2, a2->Length());
2045 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2046 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2047
2048 v8::Handle<Value> args3[] = { v8_num(4.4),
2049 v8_num(5.5),
2050 v8_num(6.6) };
2051 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2052 CHECK_EQ(3, a3->Length());
2053 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2054 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2055 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2056
2057 v8::Handle<Value> args4[] = { v8_num(7.7),
2058 v8_num(8.8),
2059 v8_num(9.9),
2060 v8_num(10.11) };
2061 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2062 CHECK_EQ(4, a4->Length());
2063 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2064 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2065 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2066 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2067}
2068
2069
2070static void CheckUncle(v8::TryCatch* try_catch) {
2071 CHECK(try_catch->HasCaught());
2072 String::AsciiValue str_value(try_catch->Exception());
2073 CHECK_EQ(*str_value, "uncle?");
2074 try_catch->Reset();
2075}
2076
2077
Steve Block6ded16b2010-05-10 14:33:55 +01002078THREADED_TEST(ConversionNumber) {
2079 v8::HandleScope scope;
2080 LocalContext env;
2081 // Very large number.
2082 CompileRun("var obj = Math.pow(2,32) * 1237;");
2083 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2084 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2085 CHECK_EQ(0, obj->ToInt32()->Value());
2086 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2087 // Large number.
2088 CompileRun("var obj = -1234567890123;");
2089 obj = env->Global()->Get(v8_str("obj"));
2090 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2091 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2092 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2093 // Small positive integer.
2094 CompileRun("var obj = 42;");
2095 obj = env->Global()->Get(v8_str("obj"));
2096 CHECK_EQ(42.0, obj->ToNumber()->Value());
2097 CHECK_EQ(42, obj->ToInt32()->Value());
2098 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2099 // Negative integer.
2100 CompileRun("var obj = -37;");
2101 obj = env->Global()->Get(v8_str("obj"));
2102 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2103 CHECK_EQ(-37, obj->ToInt32()->Value());
2104 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2105 // Positive non-int32 integer.
2106 CompileRun("var obj = 0x81234567;");
2107 obj = env->Global()->Get(v8_str("obj"));
2108 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2109 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2110 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2111 // Fraction.
2112 CompileRun("var obj = 42.3;");
2113 obj = env->Global()->Get(v8_str("obj"));
2114 CHECK_EQ(42.3, obj->ToNumber()->Value());
2115 CHECK_EQ(42, obj->ToInt32()->Value());
2116 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2117 // Large negative fraction.
2118 CompileRun("var obj = -5726623061.75;");
2119 obj = env->Global()->Get(v8_str("obj"));
2120 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2121 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2122 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2123}
2124
2125
2126THREADED_TEST(isNumberType) {
2127 v8::HandleScope scope;
2128 LocalContext env;
2129 // Very large number.
2130 CompileRun("var obj = Math.pow(2,32) * 1237;");
2131 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2132 CHECK(!obj->IsInt32());
2133 CHECK(!obj->IsUint32());
2134 // Large negative number.
2135 CompileRun("var obj = -1234567890123;");
2136 obj = env->Global()->Get(v8_str("obj"));
2137 CHECK(!obj->IsInt32());
2138 CHECK(!obj->IsUint32());
2139 // Small positive integer.
2140 CompileRun("var obj = 42;");
2141 obj = env->Global()->Get(v8_str("obj"));
2142 CHECK(obj->IsInt32());
2143 CHECK(obj->IsUint32());
2144 // Negative integer.
2145 CompileRun("var obj = -37;");
2146 obj = env->Global()->Get(v8_str("obj"));
2147 CHECK(obj->IsInt32());
2148 CHECK(!obj->IsUint32());
2149 // Positive non-int32 integer.
2150 CompileRun("var obj = 0x81234567;");
2151 obj = env->Global()->Get(v8_str("obj"));
2152 CHECK(!obj->IsInt32());
2153 CHECK(obj->IsUint32());
2154 // Fraction.
2155 CompileRun("var obj = 42.3;");
2156 obj = env->Global()->Get(v8_str("obj"));
2157 CHECK(!obj->IsInt32());
2158 CHECK(!obj->IsUint32());
2159 // Large negative fraction.
2160 CompileRun("var obj = -5726623061.75;");
2161 obj = env->Global()->Get(v8_str("obj"));
2162 CHECK(!obj->IsInt32());
2163 CHECK(!obj->IsUint32());
2164}
2165
2166
Steve Blocka7e24c12009-10-30 11:49:00 +00002167THREADED_TEST(ConversionException) {
2168 v8::HandleScope scope;
2169 LocalContext env;
2170 CompileRun(
2171 "function TestClass() { };"
2172 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2173 "var obj = new TestClass();");
2174 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2175
2176 v8::TryCatch try_catch;
2177
2178 Local<Value> to_string_result = obj->ToString();
2179 CHECK(to_string_result.IsEmpty());
2180 CheckUncle(&try_catch);
2181
2182 Local<Value> to_number_result = obj->ToNumber();
2183 CHECK(to_number_result.IsEmpty());
2184 CheckUncle(&try_catch);
2185
2186 Local<Value> to_integer_result = obj->ToInteger();
2187 CHECK(to_integer_result.IsEmpty());
2188 CheckUncle(&try_catch);
2189
2190 Local<Value> to_uint32_result = obj->ToUint32();
2191 CHECK(to_uint32_result.IsEmpty());
2192 CheckUncle(&try_catch);
2193
2194 Local<Value> to_int32_result = obj->ToInt32();
2195 CHECK(to_int32_result.IsEmpty());
2196 CheckUncle(&try_catch);
2197
2198 Local<Value> to_object_result = v8::Undefined()->ToObject();
2199 CHECK(to_object_result.IsEmpty());
2200 CHECK(try_catch.HasCaught());
2201 try_catch.Reset();
2202
2203 int32_t int32_value = obj->Int32Value();
2204 CHECK_EQ(0, int32_value);
2205 CheckUncle(&try_catch);
2206
2207 uint32_t uint32_value = obj->Uint32Value();
2208 CHECK_EQ(0, uint32_value);
2209 CheckUncle(&try_catch);
2210
2211 double number_value = obj->NumberValue();
2212 CHECK_NE(0, IsNaN(number_value));
2213 CheckUncle(&try_catch);
2214
2215 int64_t integer_value = obj->IntegerValue();
2216 CHECK_EQ(0.0, static_cast<double>(integer_value));
2217 CheckUncle(&try_catch);
2218}
2219
2220
2221v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2222 ApiTestFuzzer::Fuzz();
2223 return v8::ThrowException(v8_str("konto"));
2224}
2225
2226
2227v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2228 if (args.Length() < 1) return v8::Boolean::New(false);
2229 v8::HandleScope scope;
2230 v8::TryCatch try_catch;
2231 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2232 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2233 return v8::Boolean::New(try_catch.HasCaught());
2234}
2235
2236
2237THREADED_TEST(APICatch) {
2238 v8::HandleScope scope;
2239 Local<ObjectTemplate> templ = ObjectTemplate::New();
2240 templ->Set(v8_str("ThrowFromC"),
2241 v8::FunctionTemplate::New(ThrowFromC));
2242 LocalContext context(0, templ);
2243 CompileRun(
2244 "var thrown = false;"
2245 "try {"
2246 " ThrowFromC();"
2247 "} catch (e) {"
2248 " thrown = true;"
2249 "}");
2250 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2251 CHECK(thrown->BooleanValue());
2252}
2253
2254
2255THREADED_TEST(APIThrowTryCatch) {
2256 v8::HandleScope scope;
2257 Local<ObjectTemplate> templ = ObjectTemplate::New();
2258 templ->Set(v8_str("ThrowFromC"),
2259 v8::FunctionTemplate::New(ThrowFromC));
2260 LocalContext context(0, templ);
2261 v8::TryCatch try_catch;
2262 CompileRun("ThrowFromC();");
2263 CHECK(try_catch.HasCaught());
2264}
2265
2266
2267// Test that a try-finally block doesn't shadow a try-catch block
2268// when setting up an external handler.
2269//
2270// BUG(271): Some of the exception propagation does not work on the
2271// ARM simulator because the simulator separates the C++ stack and the
2272// JS stack. This test therefore fails on the simulator. The test is
2273// not threaded to allow the threading tests to run on the simulator.
2274TEST(TryCatchInTryFinally) {
2275 v8::HandleScope scope;
2276 Local<ObjectTemplate> templ = ObjectTemplate::New();
2277 templ->Set(v8_str("CCatcher"),
2278 v8::FunctionTemplate::New(CCatcher));
2279 LocalContext context(0, templ);
2280 Local<Value> result = CompileRun("try {"
2281 " try {"
2282 " CCatcher('throw 7;');"
2283 " } finally {"
2284 " }"
2285 "} catch (e) {"
2286 "}");
2287 CHECK(result->IsTrue());
2288}
2289
2290
2291static void receive_message(v8::Handle<v8::Message> message,
2292 v8::Handle<v8::Value> data) {
2293 message->Get();
2294 message_received = true;
2295}
2296
2297
2298TEST(APIThrowMessage) {
2299 message_received = false;
2300 v8::HandleScope scope;
2301 v8::V8::AddMessageListener(receive_message);
2302 Local<ObjectTemplate> templ = ObjectTemplate::New();
2303 templ->Set(v8_str("ThrowFromC"),
2304 v8::FunctionTemplate::New(ThrowFromC));
2305 LocalContext context(0, templ);
2306 CompileRun("ThrowFromC();");
2307 CHECK(message_received);
2308 v8::V8::RemoveMessageListeners(check_message);
2309}
2310
2311
2312TEST(APIThrowMessageAndVerboseTryCatch) {
2313 message_received = false;
2314 v8::HandleScope scope;
2315 v8::V8::AddMessageListener(receive_message);
2316 Local<ObjectTemplate> templ = ObjectTemplate::New();
2317 templ->Set(v8_str("ThrowFromC"),
2318 v8::FunctionTemplate::New(ThrowFromC));
2319 LocalContext context(0, templ);
2320 v8::TryCatch try_catch;
2321 try_catch.SetVerbose(true);
2322 Local<Value> result = CompileRun("ThrowFromC();");
2323 CHECK(try_catch.HasCaught());
2324 CHECK(result.IsEmpty());
2325 CHECK(message_received);
2326 v8::V8::RemoveMessageListeners(check_message);
2327}
2328
2329
2330THREADED_TEST(ExternalScriptException) {
2331 v8::HandleScope scope;
2332 Local<ObjectTemplate> templ = ObjectTemplate::New();
2333 templ->Set(v8_str("ThrowFromC"),
2334 v8::FunctionTemplate::New(ThrowFromC));
2335 LocalContext context(0, templ);
2336
2337 v8::TryCatch try_catch;
2338 Local<Script> script
2339 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2340 Local<Value> result = script->Run();
2341 CHECK(result.IsEmpty());
2342 CHECK(try_catch.HasCaught());
2343 String::AsciiValue exception_value(try_catch.Exception());
2344 CHECK_EQ("konto", *exception_value);
2345}
2346
2347
2348
2349v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2350 ApiTestFuzzer::Fuzz();
2351 CHECK_EQ(4, args.Length());
2352 int count = args[0]->Int32Value();
2353 int cInterval = args[2]->Int32Value();
2354 if (count == 0) {
2355 return v8::ThrowException(v8_str("FromC"));
2356 } else {
2357 Local<v8::Object> global = Context::GetCurrent()->Global();
2358 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2359 v8::Handle<Value> argv[] = { v8_num(count - 1),
2360 args[1],
2361 args[2],
2362 args[3] };
2363 if (count % cInterval == 0) {
2364 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002365 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002366 int expected = args[3]->Int32Value();
2367 if (try_catch.HasCaught()) {
2368 CHECK_EQ(expected, count);
2369 CHECK(result.IsEmpty());
2370 CHECK(!i::Top::has_scheduled_exception());
2371 } else {
2372 CHECK_NE(expected, count);
2373 }
2374 return result;
2375 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002376 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002377 }
2378 }
2379}
2380
2381
2382v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2383 ApiTestFuzzer::Fuzz();
2384 CHECK_EQ(3, args.Length());
2385 bool equality = args[0]->BooleanValue();
2386 int count = args[1]->Int32Value();
2387 int expected = args[2]->Int32Value();
2388 if (equality) {
2389 CHECK_EQ(count, expected);
2390 } else {
2391 CHECK_NE(count, expected);
2392 }
2393 return v8::Undefined();
2394}
2395
2396
2397THREADED_TEST(EvalInTryFinally) {
2398 v8::HandleScope scope;
2399 LocalContext context;
2400 v8::TryCatch try_catch;
2401 CompileRun("(function() {"
2402 " try {"
2403 " eval('asldkf (*&^&*^');"
2404 " } finally {"
2405 " return;"
2406 " }"
2407 "})()");
2408 CHECK(!try_catch.HasCaught());
2409}
2410
2411
2412// This test works by making a stack of alternating JavaScript and C
2413// activations. These activations set up exception handlers with regular
2414// intervals, one interval for C activations and another for JavaScript
2415// activations. When enough activations have been created an exception is
2416// thrown and we check that the right activation catches the exception and that
2417// no other activations do. The right activation is always the topmost one with
2418// a handler, regardless of whether it is in JavaScript or C.
2419//
2420// The notation used to describe a test case looks like this:
2421//
2422// *JS[4] *C[3] @JS[2] C[1] JS[0]
2423//
2424// Each entry is an activation, either JS or C. The index is the count at that
2425// level. Stars identify activations with exception handlers, the @ identifies
2426// the exception handler that should catch the exception.
2427//
2428// BUG(271): Some of the exception propagation does not work on the
2429// ARM simulator because the simulator separates the C++ stack and the
2430// JS stack. This test therefore fails on the simulator. The test is
2431// not threaded to allow the threading tests to run on the simulator.
2432TEST(ExceptionOrder) {
2433 v8::HandleScope scope;
2434 Local<ObjectTemplate> templ = ObjectTemplate::New();
2435 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2436 templ->Set(v8_str("CThrowCountDown"),
2437 v8::FunctionTemplate::New(CThrowCountDown));
2438 LocalContext context(0, templ);
2439 CompileRun(
2440 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2441 " if (count == 0) throw 'FromJS';"
2442 " if (count % jsInterval == 0) {"
2443 " try {"
2444 " var value = CThrowCountDown(count - 1,"
2445 " jsInterval,"
2446 " cInterval,"
2447 " expected);"
2448 " check(false, count, expected);"
2449 " return value;"
2450 " } catch (e) {"
2451 " check(true, count, expected);"
2452 " }"
2453 " } else {"
2454 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2455 " }"
2456 "}");
2457 Local<Function> fun =
2458 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2459
2460 const int argc = 4;
2461 // count jsInterval cInterval expected
2462
2463 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2464 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2465 fun->Call(fun, argc, a0);
2466
2467 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2468 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2469 fun->Call(fun, argc, a1);
2470
2471 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2472 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2473 fun->Call(fun, argc, a2);
2474
2475 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2476 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2477 fun->Call(fun, argc, a3);
2478
2479 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2480 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2481 fun->Call(fun, argc, a4);
2482
2483 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2484 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2485 fun->Call(fun, argc, a5);
2486}
2487
2488
2489v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2490 ApiTestFuzzer::Fuzz();
2491 CHECK_EQ(1, args.Length());
2492 return v8::ThrowException(args[0]);
2493}
2494
2495
2496THREADED_TEST(ThrowValues) {
2497 v8::HandleScope scope;
2498 Local<ObjectTemplate> templ = ObjectTemplate::New();
2499 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2500 LocalContext context(0, templ);
2501 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2502 "function Run(obj) {"
2503 " try {"
2504 " Throw(obj);"
2505 " } catch (e) {"
2506 " return e;"
2507 " }"
2508 " return 'no exception';"
2509 "}"
2510 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2511 CHECK_EQ(5, result->Length());
2512 CHECK(result->Get(v8::Integer::New(0))->IsString());
2513 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2514 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2515 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2516 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2517 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2518 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2519}
2520
2521
2522THREADED_TEST(CatchZero) {
2523 v8::HandleScope scope;
2524 LocalContext context;
2525 v8::TryCatch try_catch;
2526 CHECK(!try_catch.HasCaught());
2527 Script::Compile(v8_str("throw 10"))->Run();
2528 CHECK(try_catch.HasCaught());
2529 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2530 try_catch.Reset();
2531 CHECK(!try_catch.HasCaught());
2532 Script::Compile(v8_str("throw 0"))->Run();
2533 CHECK(try_catch.HasCaught());
2534 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2535}
2536
2537
2538THREADED_TEST(CatchExceptionFromWith) {
2539 v8::HandleScope scope;
2540 LocalContext context;
2541 v8::TryCatch try_catch;
2542 CHECK(!try_catch.HasCaught());
2543 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2544 CHECK(try_catch.HasCaught());
2545}
2546
2547
2548THREADED_TEST(Equality) {
2549 v8::HandleScope scope;
2550 LocalContext context;
2551 // Check that equality works at all before relying on CHECK_EQ
2552 CHECK(v8_str("a")->Equals(v8_str("a")));
2553 CHECK(!v8_str("a")->Equals(v8_str("b")));
2554
2555 CHECK_EQ(v8_str("a"), v8_str("a"));
2556 CHECK_NE(v8_str("a"), v8_str("b"));
2557 CHECK_EQ(v8_num(1), v8_num(1));
2558 CHECK_EQ(v8_num(1.00), v8_num(1));
2559 CHECK_NE(v8_num(1), v8_num(2));
2560
2561 // Assume String is not symbol.
2562 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2563 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2564 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2565 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2566 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2567 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2568 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2569 CHECK(!not_a_number->StrictEquals(not_a_number));
2570 CHECK(v8::False()->StrictEquals(v8::False()));
2571 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2572
2573 v8::Handle<v8::Object> obj = v8::Object::New();
2574 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2575 CHECK(alias->StrictEquals(obj));
2576 alias.Dispose();
2577}
2578
2579
2580THREADED_TEST(MultiRun) {
2581 v8::HandleScope scope;
2582 LocalContext context;
2583 Local<Script> script = Script::Compile(v8_str("x"));
2584 for (int i = 0; i < 10; i++)
2585 script->Run();
2586}
2587
2588
2589static v8::Handle<Value> GetXValue(Local<String> name,
2590 const AccessorInfo& info) {
2591 ApiTestFuzzer::Fuzz();
2592 CHECK_EQ(info.Data(), v8_str("donut"));
2593 CHECK_EQ(name, v8_str("x"));
2594 return name;
2595}
2596
2597
2598THREADED_TEST(SimplePropertyRead) {
2599 v8::HandleScope scope;
2600 Local<ObjectTemplate> templ = ObjectTemplate::New();
2601 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2602 LocalContext context;
2603 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2604 Local<Script> script = Script::Compile(v8_str("obj.x"));
2605 for (int i = 0; i < 10; i++) {
2606 Local<Value> result = script->Run();
2607 CHECK_EQ(result, v8_str("x"));
2608 }
2609}
2610
Andrei Popescu31002712010-02-23 13:46:05 +00002611THREADED_TEST(DefinePropertyOnAPIAccessor) {
2612 v8::HandleScope scope;
2613 Local<ObjectTemplate> templ = ObjectTemplate::New();
2614 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2615 LocalContext context;
2616 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2617
2618 // Uses getOwnPropertyDescriptor to check the configurable status
2619 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002620 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002621 "obj, 'x');"
2622 "prop.configurable;"));
2623 Local<Value> result = script_desc->Run();
2624 CHECK_EQ(result->BooleanValue(), true);
2625
2626 // Redefine get - but still configurable
2627 Local<Script> script_define
2628 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2629 " configurable: true };"
2630 "Object.defineProperty(obj, 'x', desc);"
2631 "obj.x"));
2632 result = script_define->Run();
2633 CHECK_EQ(result, v8_num(42));
2634
2635 // Check that the accessor is still configurable
2636 result = script_desc->Run();
2637 CHECK_EQ(result->BooleanValue(), true);
2638
2639 // Redefine to a non-configurable
2640 script_define
2641 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2642 " configurable: false };"
2643 "Object.defineProperty(obj, 'x', desc);"
2644 "obj.x"));
2645 result = script_define->Run();
2646 CHECK_EQ(result, v8_num(43));
2647 result = script_desc->Run();
2648 CHECK_EQ(result->BooleanValue(), false);
2649
2650 // Make sure that it is not possible to redefine again
2651 v8::TryCatch try_catch;
2652 result = script_define->Run();
2653 CHECK(try_catch.HasCaught());
2654 String::AsciiValue exception_value(try_catch.Exception());
2655 CHECK_EQ(*exception_value,
2656 "TypeError: Cannot redefine property: defineProperty");
2657}
2658
2659THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2660 v8::HandleScope scope;
2661 Local<ObjectTemplate> templ = ObjectTemplate::New();
2662 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2663 LocalContext context;
2664 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2665
2666 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2667 "Object.getOwnPropertyDescriptor( "
2668 "obj, 'x');"
2669 "prop.configurable;"));
2670 Local<Value> result = script_desc->Run();
2671 CHECK_EQ(result->BooleanValue(), true);
2672
2673 Local<Script> script_define =
2674 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2675 " configurable: true };"
2676 "Object.defineProperty(obj, 'x', desc);"
2677 "obj.x"));
2678 result = script_define->Run();
2679 CHECK_EQ(result, v8_num(42));
2680
2681
2682 result = script_desc->Run();
2683 CHECK_EQ(result->BooleanValue(), true);
2684
2685
2686 script_define =
2687 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2688 " configurable: false };"
2689 "Object.defineProperty(obj, 'x', desc);"
2690 "obj.x"));
2691 result = script_define->Run();
2692 CHECK_EQ(result, v8_num(43));
2693 result = script_desc->Run();
2694
2695 CHECK_EQ(result->BooleanValue(), false);
2696
2697 v8::TryCatch try_catch;
2698 result = script_define->Run();
2699 CHECK(try_catch.HasCaught());
2700 String::AsciiValue exception_value(try_catch.Exception());
2701 CHECK_EQ(*exception_value,
2702 "TypeError: Cannot redefine property: defineProperty");
2703}
2704
2705
Leon Clarkef7060e22010-06-03 12:02:55 +01002706static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2707 char const* name) {
2708 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2709}
Andrei Popescu31002712010-02-23 13:46:05 +00002710
2711
Leon Clarkef7060e22010-06-03 12:02:55 +01002712THREADED_TEST(DefineAPIAccessorOnObject) {
2713 v8::HandleScope scope;
2714 Local<ObjectTemplate> templ = ObjectTemplate::New();
2715 LocalContext context;
2716
2717 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2718 CompileRun("var obj2 = {};");
2719
2720 CHECK(CompileRun("obj1.x")->IsUndefined());
2721 CHECK(CompileRun("obj2.x")->IsUndefined());
2722
2723 CHECK(GetGlobalProperty(&context, "obj1")->
2724 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2725
2726 ExpectString("obj1.x", "x");
2727 CHECK(CompileRun("obj2.x")->IsUndefined());
2728
2729 CHECK(GetGlobalProperty(&context, "obj2")->
2730 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2731
2732 ExpectString("obj1.x", "x");
2733 ExpectString("obj2.x", "x");
2734
2735 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2736 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2737
2738 CompileRun("Object.defineProperty(obj1, 'x',"
2739 "{ get: function() { return 'y'; }, configurable: true })");
2740
2741 ExpectString("obj1.x", "y");
2742 ExpectString("obj2.x", "x");
2743
2744 CompileRun("Object.defineProperty(obj2, 'x',"
2745 "{ get: function() { return 'y'; }, configurable: true })");
2746
2747 ExpectString("obj1.x", "y");
2748 ExpectString("obj2.x", "y");
2749
2750 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2751 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2752
2753 CHECK(GetGlobalProperty(&context, "obj1")->
2754 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2755 CHECK(GetGlobalProperty(&context, "obj2")->
2756 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2757
2758 ExpectString("obj1.x", "x");
2759 ExpectString("obj2.x", "x");
2760
2761 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2762 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2763
2764 // Define getters/setters, but now make them not configurable.
2765 CompileRun("Object.defineProperty(obj1, 'x',"
2766 "{ get: function() { return 'z'; }, configurable: false })");
2767 CompileRun("Object.defineProperty(obj2, 'x',"
2768 "{ get: function() { return 'z'; }, configurable: false })");
2769
2770 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2771 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2772
2773 ExpectString("obj1.x", "z");
2774 ExpectString("obj2.x", "z");
2775
2776 CHECK(!GetGlobalProperty(&context, "obj1")->
2777 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2778 CHECK(!GetGlobalProperty(&context, "obj2")->
2779 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2780
2781 ExpectString("obj1.x", "z");
2782 ExpectString("obj2.x", "z");
2783}
2784
2785
2786THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2787 v8::HandleScope scope;
2788 Local<ObjectTemplate> templ = ObjectTemplate::New();
2789 LocalContext context;
2790
2791 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2792 CompileRun("var obj2 = {};");
2793
2794 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2795 v8_str("x"),
2796 GetXValue, NULL,
2797 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2798 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2799 v8_str("x"),
2800 GetXValue, NULL,
2801 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2802
2803 ExpectString("obj1.x", "x");
2804 ExpectString("obj2.x", "x");
2805
2806 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2807 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2808
2809 CHECK(!GetGlobalProperty(&context, "obj1")->
2810 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2811 CHECK(!GetGlobalProperty(&context, "obj2")->
2812 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2813
2814 {
2815 v8::TryCatch try_catch;
2816 CompileRun("Object.defineProperty(obj1, 'x',"
2817 "{get: function() { return 'func'; }})");
2818 CHECK(try_catch.HasCaught());
2819 String::AsciiValue exception_value(try_catch.Exception());
2820 CHECK_EQ(*exception_value,
2821 "TypeError: Cannot redefine property: defineProperty");
2822 }
2823 {
2824 v8::TryCatch try_catch;
2825 CompileRun("Object.defineProperty(obj2, 'x',"
2826 "{get: function() { return 'func'; }})");
2827 CHECK(try_catch.HasCaught());
2828 String::AsciiValue exception_value(try_catch.Exception());
2829 CHECK_EQ(*exception_value,
2830 "TypeError: Cannot redefine property: defineProperty");
2831 }
2832}
2833
2834
2835static v8::Handle<Value> Get239Value(Local<String> name,
2836 const AccessorInfo& info) {
2837 ApiTestFuzzer::Fuzz();
2838 CHECK_EQ(info.Data(), v8_str("donut"));
2839 CHECK_EQ(name, v8_str("239"));
2840 return name;
2841}
2842
2843
2844THREADED_TEST(ElementAPIAccessor) {
2845 v8::HandleScope scope;
2846 Local<ObjectTemplate> templ = ObjectTemplate::New();
2847 LocalContext context;
2848
2849 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2850 CompileRun("var obj2 = {};");
2851
2852 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2853 v8_str("239"),
2854 Get239Value, NULL,
2855 v8_str("donut")));
2856 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2857 v8_str("239"),
2858 Get239Value, NULL,
2859 v8_str("donut")));
2860
2861 ExpectString("obj1[239]", "239");
2862 ExpectString("obj2[239]", "239");
2863 ExpectString("obj1['239']", "239");
2864 ExpectString("obj2['239']", "239");
2865}
2866
Steve Blocka7e24c12009-10-30 11:49:00 +00002867
2868v8::Persistent<Value> xValue;
2869
2870
2871static void SetXValue(Local<String> name,
2872 Local<Value> value,
2873 const AccessorInfo& info) {
2874 CHECK_EQ(value, v8_num(4));
2875 CHECK_EQ(info.Data(), v8_str("donut"));
2876 CHECK_EQ(name, v8_str("x"));
2877 CHECK(xValue.IsEmpty());
2878 xValue = v8::Persistent<Value>::New(value);
2879}
2880
2881
2882THREADED_TEST(SimplePropertyWrite) {
2883 v8::HandleScope scope;
2884 Local<ObjectTemplate> templ = ObjectTemplate::New();
2885 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2886 LocalContext context;
2887 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2888 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2889 for (int i = 0; i < 10; i++) {
2890 CHECK(xValue.IsEmpty());
2891 script->Run();
2892 CHECK_EQ(v8_num(4), xValue);
2893 xValue.Dispose();
2894 xValue = v8::Persistent<Value>();
2895 }
2896}
2897
2898
2899static v8::Handle<Value> XPropertyGetter(Local<String> property,
2900 const AccessorInfo& info) {
2901 ApiTestFuzzer::Fuzz();
2902 CHECK(info.Data()->IsUndefined());
2903 return property;
2904}
2905
2906
2907THREADED_TEST(NamedInterceptorPropertyRead) {
2908 v8::HandleScope scope;
2909 Local<ObjectTemplate> templ = ObjectTemplate::New();
2910 templ->SetNamedPropertyHandler(XPropertyGetter);
2911 LocalContext context;
2912 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2913 Local<Script> script = Script::Compile(v8_str("obj.x"));
2914 for (int i = 0; i < 10; i++) {
2915 Local<Value> result = script->Run();
2916 CHECK_EQ(result, v8_str("x"));
2917 }
2918}
2919
2920
Steve Block6ded16b2010-05-10 14:33:55 +01002921THREADED_TEST(NamedInterceptorDictionaryIC) {
2922 v8::HandleScope scope;
2923 Local<ObjectTemplate> templ = ObjectTemplate::New();
2924 templ->SetNamedPropertyHandler(XPropertyGetter);
2925 LocalContext context;
2926 // Create an object with a named interceptor.
2927 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2928 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2929 for (int i = 0; i < 10; i++) {
2930 Local<Value> result = script->Run();
2931 CHECK_EQ(result, v8_str("x"));
2932 }
2933 // Create a slow case object and a function accessing a property in
2934 // that slow case object (with dictionary probing in generated
2935 // code). Then force object with a named interceptor into slow-case,
2936 // pass it to the function, and check that the interceptor is called
2937 // instead of accessing the local property.
2938 Local<Value> result =
2939 CompileRun("function get_x(o) { return o.x; };"
2940 "var obj = { x : 42, y : 0 };"
2941 "delete obj.y;"
2942 "for (var i = 0; i < 10; i++) get_x(obj);"
2943 "interceptor_obj.x = 42;"
2944 "interceptor_obj.y = 10;"
2945 "delete interceptor_obj.y;"
2946 "get_x(interceptor_obj)");
2947 CHECK_EQ(result, v8_str("x"));
2948}
2949
2950
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002951THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
2952 v8::HandleScope scope;
2953
2954 v8::Persistent<Context> context1 = Context::New();
2955
2956 context1->Enter();
2957 Local<ObjectTemplate> templ = ObjectTemplate::New();
2958 templ->SetNamedPropertyHandler(XPropertyGetter);
2959 // Create an object with a named interceptor.
2960 v8::Local<v8::Object> object = templ->NewInstance();
2961 context1->Global()->Set(v8_str("interceptor_obj"), object);
2962
2963 // Force the object into the slow case.
2964 CompileRun("interceptor_obj.y = 0;"
2965 "delete interceptor_obj.y;");
2966 context1->Exit();
2967
2968 {
2969 // Introduce the object into a different context.
2970 // Repeat named loads to exercise ICs.
2971 LocalContext context2;
2972 context2->Global()->Set(v8_str("interceptor_obj"), object);
2973 Local<Value> result =
2974 CompileRun("function get_x(o) { return o.x; }"
2975 "interceptor_obj.x = 42;"
2976 "for (var i=0; i != 10; i++) {"
2977 " get_x(interceptor_obj);"
2978 "}"
2979 "get_x(interceptor_obj)");
2980 // Check that the interceptor was actually invoked.
2981 CHECK_EQ(result, v8_str("x"));
2982 }
2983
2984 // Return to the original context and force some object to the slow case
2985 // to cause the NormalizedMapCache to verify.
2986 context1->Enter();
2987 CompileRun("var obj = { x : 0 }; delete obj.x;");
2988 context1->Exit();
2989
2990 context1.Dispose();
2991}
2992
2993
Andrei Popescu402d9372010-02-26 13:31:12 +00002994static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2995 const AccessorInfo& info) {
2996 // Set x on the prototype object and do not handle the get request.
2997 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002998 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002999 return v8::Handle<Value>();
3000}
3001
3002
3003// This is a regression test for http://crbug.com/20104. Map
3004// transitions should not interfere with post interceptor lookup.
3005THREADED_TEST(NamedInterceptorMapTransitionRead) {
3006 v8::HandleScope scope;
3007 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
3008 Local<v8::ObjectTemplate> instance_template
3009 = function_template->InstanceTemplate();
3010 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
3011 LocalContext context;
3012 context->Global()->Set(v8_str("F"), function_template->GetFunction());
3013 // Create an instance of F and introduce a map transition for x.
3014 CompileRun("var o = new F(); o.x = 23;");
3015 // Create an instance of F and invoke the getter. The result should be 23.
3016 Local<Value> result = CompileRun("o = new F(); o.x");
3017 CHECK_EQ(result->Int32Value(), 23);
3018}
3019
3020
Steve Blocka7e24c12009-10-30 11:49:00 +00003021static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
3022 const AccessorInfo& info) {
3023 ApiTestFuzzer::Fuzz();
3024 if (index == 37) {
3025 return v8::Handle<Value>(v8_num(625));
3026 }
3027 return v8::Handle<Value>();
3028}
3029
3030
3031static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
3032 Local<Value> value,
3033 const AccessorInfo& info) {
3034 ApiTestFuzzer::Fuzz();
3035 if (index == 39) {
3036 return value;
3037 }
3038 return v8::Handle<Value>();
3039}
3040
3041
3042THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
3043 v8::HandleScope scope;
3044 Local<ObjectTemplate> templ = ObjectTemplate::New();
3045 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
3046 IndexedPropertySetter);
3047 LocalContext context;
3048 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3049 Local<Script> getter_script = Script::Compile(v8_str(
3050 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
3051 Local<Script> setter_script = Script::Compile(v8_str(
3052 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
3053 "obj[17] = 23;"
3054 "obj.foo;"));
3055 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3056 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3057 "obj[39] = 47;"
3058 "obj.foo;")); // This setter should not run, due to the interceptor.
3059 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3060 "obj[37];"));
3061 Local<Value> result = getter_script->Run();
3062 CHECK_EQ(v8_num(5), result);
3063 result = setter_script->Run();
3064 CHECK_EQ(v8_num(23), result);
3065 result = interceptor_setter_script->Run();
3066 CHECK_EQ(v8_num(23), result);
3067 result = interceptor_getter_script->Run();
3068 CHECK_EQ(v8_num(625), result);
3069}
3070
3071
Leon Clarked91b9f72010-01-27 17:25:45 +00003072static v8::Handle<Value> IdentityIndexedPropertyGetter(
3073 uint32_t index,
3074 const AccessorInfo& info) {
Ben Murdochf87a2032010-10-22 12:50:53 +01003075 return v8::Integer::NewFromUnsigned(index);
Leon Clarked91b9f72010-01-27 17:25:45 +00003076}
3077
3078
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003079THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3080 v8::HandleScope scope;
3081 Local<ObjectTemplate> templ = ObjectTemplate::New();
3082 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3083
3084 LocalContext context;
3085 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3086
3087 // Check fast object case.
3088 const char* fast_case_code =
3089 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3090 ExpectString(fast_case_code, "0");
3091
3092 // Check slow case.
3093 const char* slow_case_code =
3094 "obj.x = 1; delete obj.x;"
3095 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3096 ExpectString(slow_case_code, "1");
3097}
3098
3099
Leon Clarked91b9f72010-01-27 17:25:45 +00003100THREADED_TEST(IndexedInterceptorWithNoSetter) {
3101 v8::HandleScope scope;
3102 Local<ObjectTemplate> templ = ObjectTemplate::New();
3103 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3104
3105 LocalContext context;
3106 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3107
3108 const char* code =
3109 "try {"
3110 " obj[0] = 239;"
3111 " for (var i = 0; i < 100; i++) {"
3112 " var v = obj[0];"
3113 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3114 " }"
3115 " 'PASSED'"
3116 "} catch(e) {"
3117 " e"
3118 "}";
3119 ExpectString(code, "PASSED");
3120}
3121
3122
Andrei Popescu402d9372010-02-26 13:31:12 +00003123THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3124 v8::HandleScope scope;
3125 Local<ObjectTemplate> templ = ObjectTemplate::New();
3126 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3127
3128 LocalContext context;
3129 Local<v8::Object> obj = templ->NewInstance();
3130 obj->TurnOnAccessCheck();
3131 context->Global()->Set(v8_str("obj"), obj);
3132
3133 const char* code =
3134 "try {"
3135 " for (var i = 0; i < 100; i++) {"
3136 " var v = obj[0];"
3137 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3138 " }"
3139 " 'PASSED'"
3140 "} catch(e) {"
3141 " e"
3142 "}";
3143 ExpectString(code, "PASSED");
3144}
3145
3146
3147THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3148 i::FLAG_allow_natives_syntax = true;
3149 v8::HandleScope scope;
3150 Local<ObjectTemplate> templ = ObjectTemplate::New();
3151 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3152
3153 LocalContext context;
3154 Local<v8::Object> obj = templ->NewInstance();
3155 context->Global()->Set(v8_str("obj"), obj);
3156
3157 const char* code =
3158 "try {"
3159 " for (var i = 0; i < 100; i++) {"
3160 " var expected = i;"
3161 " if (i == 5) {"
3162 " %EnableAccessChecks(obj);"
3163 " expected = undefined;"
3164 " }"
3165 " var v = obj[i];"
3166 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3167 " if (i == 5) %DisableAccessChecks(obj);"
3168 " }"
3169 " 'PASSED'"
3170 "} catch(e) {"
3171 " e"
3172 "}";
3173 ExpectString(code, "PASSED");
3174}
3175
3176
3177THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3178 v8::HandleScope scope;
3179 Local<ObjectTemplate> templ = ObjectTemplate::New();
3180 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3181
3182 LocalContext context;
3183 Local<v8::Object> obj = templ->NewInstance();
3184 context->Global()->Set(v8_str("obj"), obj);
3185
3186 const char* code =
3187 "try {"
3188 " for (var i = 0; i < 100; i++) {"
3189 " var v = obj[i];"
3190 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3191 " }"
3192 " 'PASSED'"
3193 "} catch(e) {"
3194 " e"
3195 "}";
3196 ExpectString(code, "PASSED");
3197}
3198
3199
Ben Murdochf87a2032010-10-22 12:50:53 +01003200THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
3201 v8::HandleScope scope;
3202 Local<ObjectTemplate> templ = ObjectTemplate::New();
3203 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3204
3205 LocalContext context;
3206 Local<v8::Object> obj = templ->NewInstance();
3207 context->Global()->Set(v8_str("obj"), obj);
3208
3209 const char* code =
3210 "try {"
3211 " for (var i = 0; i < 100; i++) {"
3212 " var expected = i;"
3213 " var key = i;"
3214 " if (i == 25) {"
3215 " key = -1;"
3216 " expected = undefined;"
3217 " }"
3218 " if (i == 50) {"
3219 " /* probe minimal Smi number on 32-bit platforms */"
3220 " key = -(1 << 30);"
3221 " expected = undefined;"
3222 " }"
3223 " if (i == 75) {"
3224 " /* probe minimal Smi number on 64-bit platforms */"
3225 " key = 1 << 31;"
3226 " expected = undefined;"
3227 " }"
3228 " var v = obj[key];"
3229 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3230 " }"
3231 " 'PASSED'"
3232 "} catch(e) {"
3233 " e"
3234 "}";
3235 ExpectString(code, "PASSED");
3236}
3237
3238
Andrei Popescu402d9372010-02-26 13:31:12 +00003239THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3240 v8::HandleScope scope;
3241 Local<ObjectTemplate> templ = ObjectTemplate::New();
3242 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3243
3244 LocalContext context;
3245 Local<v8::Object> obj = templ->NewInstance();
3246 context->Global()->Set(v8_str("obj"), obj);
3247
3248 const char* code =
3249 "try {"
3250 " for (var i = 0; i < 100; i++) {"
3251 " var expected = i;"
Ben Murdochf87a2032010-10-22 12:50:53 +01003252 " var key = i;"
Andrei Popescu402d9372010-02-26 13:31:12 +00003253 " if (i == 50) {"
Ben Murdochf87a2032010-10-22 12:50:53 +01003254 " key = 'foobar';"
Andrei Popescu402d9372010-02-26 13:31:12 +00003255 " expected = undefined;"
3256 " }"
Ben Murdochf87a2032010-10-22 12:50:53 +01003257 " var v = obj[key];"
Andrei Popescu402d9372010-02-26 13:31:12 +00003258 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3259 " }"
3260 " 'PASSED'"
3261 "} catch(e) {"
3262 " e"
3263 "}";
3264 ExpectString(code, "PASSED");
3265}
3266
3267
3268THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3269 v8::HandleScope scope;
3270 Local<ObjectTemplate> templ = ObjectTemplate::New();
3271 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3272
3273 LocalContext context;
3274 Local<v8::Object> obj = templ->NewInstance();
3275 context->Global()->Set(v8_str("obj"), obj);
3276
3277 const char* code =
3278 "var original = obj;"
3279 "try {"
3280 " for (var i = 0; i < 100; i++) {"
3281 " var expected = i;"
3282 " if (i == 50) {"
3283 " obj = {50: 'foobar'};"
3284 " expected = 'foobar';"
3285 " }"
3286 " var v = obj[i];"
3287 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3288 " if (i == 50) obj = original;"
3289 " }"
3290 " 'PASSED'"
3291 "} catch(e) {"
3292 " e"
3293 "}";
3294 ExpectString(code, "PASSED");
3295}
3296
3297
3298THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3299 v8::HandleScope scope;
3300 Local<ObjectTemplate> templ = ObjectTemplate::New();
3301 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3302
3303 LocalContext context;
3304 Local<v8::Object> obj = templ->NewInstance();
3305 context->Global()->Set(v8_str("obj"), obj);
3306
3307 const char* code =
3308 "var original = obj;"
3309 "try {"
3310 " for (var i = 0; i < 100; i++) {"
3311 " var expected = i;"
3312 " if (i == 5) {"
3313 " obj = 239;"
3314 " expected = undefined;"
3315 " }"
3316 " var v = obj[i];"
3317 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3318 " if (i == 5) obj = original;"
3319 " }"
3320 " 'PASSED'"
3321 "} catch(e) {"
3322 " e"
3323 "}";
3324 ExpectString(code, "PASSED");
3325}
3326
3327
3328THREADED_TEST(IndexedInterceptorOnProto) {
3329 v8::HandleScope scope;
3330 Local<ObjectTemplate> templ = ObjectTemplate::New();
3331 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3332
3333 LocalContext context;
3334 Local<v8::Object> obj = templ->NewInstance();
3335 context->Global()->Set(v8_str("obj"), obj);
3336
3337 const char* code =
3338 "var o = {__proto__: obj};"
3339 "try {"
3340 " for (var i = 0; i < 100; i++) {"
3341 " var v = o[i];"
3342 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3343 " }"
3344 " 'PASSED'"
3345 "} catch(e) {"
3346 " e"
3347 "}";
3348 ExpectString(code, "PASSED");
3349}
3350
3351
Steve Blocka7e24c12009-10-30 11:49:00 +00003352THREADED_TEST(MultiContexts) {
3353 v8::HandleScope scope;
3354 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3355 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3356
3357 Local<String> password = v8_str("Password");
3358
3359 // Create an environment
3360 LocalContext context0(0, templ);
3361 context0->SetSecurityToken(password);
3362 v8::Handle<v8::Object> global0 = context0->Global();
3363 global0->Set(v8_str("custom"), v8_num(1234));
3364 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3365
3366 // Create an independent environment
3367 LocalContext context1(0, templ);
3368 context1->SetSecurityToken(password);
3369 v8::Handle<v8::Object> global1 = context1->Global();
3370 global1->Set(v8_str("custom"), v8_num(1234));
3371 CHECK_NE(global0, global1);
3372 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3373 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3374
3375 // Now create a new context with the old global
3376 LocalContext context2(0, templ, global1);
3377 context2->SetSecurityToken(password);
3378 v8::Handle<v8::Object> global2 = context2->Global();
3379 CHECK_EQ(global1, global2);
3380 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3381 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3382}
3383
3384
3385THREADED_TEST(FunctionPrototypeAcrossContexts) {
3386 // Make sure that functions created by cloning boilerplates cannot
3387 // communicate through their __proto__ field.
3388
3389 v8::HandleScope scope;
3390
3391 LocalContext env0;
3392 v8::Handle<v8::Object> global0 =
3393 env0->Global();
3394 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003395 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003396 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003397 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003398 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003399 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003400 proto0->Set(v8_str("custom"), v8_num(1234));
3401
3402 LocalContext env1;
3403 v8::Handle<v8::Object> global1 =
3404 env1->Global();
3405 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003406 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003407 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003408 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003409 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003410 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003411 CHECK(!proto1->Has(v8_str("custom")));
3412}
3413
3414
3415THREADED_TEST(Regress892105) {
3416 // Make sure that object and array literals created by cloning
3417 // boilerplates cannot communicate through their __proto__
3418 // field. This is rather difficult to check, but we try to add stuff
3419 // to Object.prototype and Array.prototype and create a new
3420 // environment. This should succeed.
3421
3422 v8::HandleScope scope;
3423
3424 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3425 "Array.prototype.arr = 4567;"
3426 "8901");
3427
3428 LocalContext env0;
3429 Local<Script> script0 = Script::Compile(source);
3430 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3431
3432 LocalContext env1;
3433 Local<Script> script1 = Script::Compile(source);
3434 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3435}
3436
3437
Steve Blocka7e24c12009-10-30 11:49:00 +00003438THREADED_TEST(UndetectableObject) {
3439 v8::HandleScope scope;
3440 LocalContext env;
3441
3442 Local<v8::FunctionTemplate> desc =
3443 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3444 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3445
3446 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3447 env->Global()->Set(v8_str("undetectable"), obj);
3448
3449 ExpectString("undetectable.toString()", "[object Object]");
3450 ExpectString("typeof undetectable", "undefined");
3451 ExpectString("typeof(undetectable)", "undefined");
3452 ExpectBoolean("typeof undetectable == 'undefined'", true);
3453 ExpectBoolean("typeof undetectable == 'object'", false);
3454 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3455 ExpectBoolean("!undetectable", true);
3456
3457 ExpectObject("true&&undetectable", obj);
3458 ExpectBoolean("false&&undetectable", false);
3459 ExpectBoolean("true||undetectable", true);
3460 ExpectObject("false||undetectable", obj);
3461
3462 ExpectObject("undetectable&&true", obj);
3463 ExpectObject("undetectable&&false", obj);
3464 ExpectBoolean("undetectable||true", true);
3465 ExpectBoolean("undetectable||false", false);
3466
3467 ExpectBoolean("undetectable==null", true);
3468 ExpectBoolean("null==undetectable", true);
3469 ExpectBoolean("undetectable==undefined", true);
3470 ExpectBoolean("undefined==undetectable", true);
3471 ExpectBoolean("undetectable==undetectable", true);
3472
3473
3474 ExpectBoolean("undetectable===null", false);
3475 ExpectBoolean("null===undetectable", false);
3476 ExpectBoolean("undetectable===undefined", false);
3477 ExpectBoolean("undefined===undetectable", false);
3478 ExpectBoolean("undetectable===undetectable", true);
3479}
3480
3481
Steve Block8defd9f2010-07-08 12:39:36 +01003482
3483THREADED_TEST(ExtensibleOnUndetectable) {
3484 v8::HandleScope scope;
3485 LocalContext env;
3486
3487 Local<v8::FunctionTemplate> desc =
3488 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3489 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3490
3491 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3492 env->Global()->Set(v8_str("undetectable"), obj);
3493
3494 Local<String> source = v8_str("undetectable.x = 42;"
3495 "undetectable.x");
3496
3497 Local<Script> script = Script::Compile(source);
3498
3499 CHECK_EQ(v8::Integer::New(42), script->Run());
3500
3501 ExpectBoolean("Object.isExtensible(undetectable)", true);
3502
3503 source = v8_str("Object.preventExtensions(undetectable);");
3504 script = Script::Compile(source);
3505 script->Run();
3506 ExpectBoolean("Object.isExtensible(undetectable)", false);
3507
3508 source = v8_str("undetectable.y = 2000;");
3509 script = Script::Compile(source);
3510 v8::TryCatch try_catch;
3511 Local<Value> result = script->Run();
3512 CHECK(result.IsEmpty());
3513 CHECK(try_catch.HasCaught());
3514}
3515
3516
3517
Steve Blocka7e24c12009-10-30 11:49:00 +00003518THREADED_TEST(UndetectableString) {
3519 v8::HandleScope scope;
3520 LocalContext env;
3521
3522 Local<String> obj = String::NewUndetectable("foo");
3523 env->Global()->Set(v8_str("undetectable"), obj);
3524
3525 ExpectString("undetectable", "foo");
3526 ExpectString("typeof undetectable", "undefined");
3527 ExpectString("typeof(undetectable)", "undefined");
3528 ExpectBoolean("typeof undetectable == 'undefined'", true);
3529 ExpectBoolean("typeof undetectable == 'string'", false);
3530 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3531 ExpectBoolean("!undetectable", true);
3532
3533 ExpectObject("true&&undetectable", obj);
3534 ExpectBoolean("false&&undetectable", false);
3535 ExpectBoolean("true||undetectable", true);
3536 ExpectObject("false||undetectable", obj);
3537
3538 ExpectObject("undetectable&&true", obj);
3539 ExpectObject("undetectable&&false", obj);
3540 ExpectBoolean("undetectable||true", true);
3541 ExpectBoolean("undetectable||false", false);
3542
3543 ExpectBoolean("undetectable==null", true);
3544 ExpectBoolean("null==undetectable", true);
3545 ExpectBoolean("undetectable==undefined", true);
3546 ExpectBoolean("undefined==undetectable", true);
3547 ExpectBoolean("undetectable==undetectable", true);
3548
3549
3550 ExpectBoolean("undetectable===null", false);
3551 ExpectBoolean("null===undetectable", false);
3552 ExpectBoolean("undetectable===undefined", false);
3553 ExpectBoolean("undefined===undetectable", false);
3554 ExpectBoolean("undetectable===undetectable", true);
3555}
3556
3557
3558template <typename T> static void USE(T) { }
3559
3560
3561// This test is not intended to be run, just type checked.
3562static void PersistentHandles() {
3563 USE(PersistentHandles);
3564 Local<String> str = v8_str("foo");
3565 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3566 USE(p_str);
3567 Local<Script> scr = Script::Compile(v8_str(""));
3568 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3569 USE(p_scr);
3570 Local<ObjectTemplate> templ = ObjectTemplate::New();
3571 v8::Persistent<ObjectTemplate> p_templ =
3572 v8::Persistent<ObjectTemplate>::New(templ);
3573 USE(p_templ);
3574}
3575
3576
3577static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3578 ApiTestFuzzer::Fuzz();
3579 return v8::Undefined();
3580}
3581
3582
3583THREADED_TEST(GlobalObjectTemplate) {
3584 v8::HandleScope handle_scope;
3585 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3586 global_template->Set(v8_str("JSNI_Log"),
3587 v8::FunctionTemplate::New(HandleLogDelegator));
3588 v8::Persistent<Context> context = Context::New(0, global_template);
3589 Context::Scope context_scope(context);
3590 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3591 context.Dispose();
3592}
3593
3594
3595static const char* kSimpleExtensionSource =
3596 "function Foo() {"
3597 " return 4;"
3598 "}";
3599
3600
3601THREADED_TEST(SimpleExtensions) {
3602 v8::HandleScope handle_scope;
3603 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3604 const char* extension_names[] = { "simpletest" };
3605 v8::ExtensionConfiguration extensions(1, extension_names);
3606 v8::Handle<Context> context = Context::New(&extensions);
3607 Context::Scope lock(context);
3608 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3609 CHECK_EQ(result, v8::Integer::New(4));
3610}
3611
3612
3613static const char* kEvalExtensionSource1 =
3614 "function UseEval1() {"
3615 " var x = 42;"
3616 " return eval('x');"
3617 "}";
3618
3619
3620static const char* kEvalExtensionSource2 =
3621 "(function() {"
3622 " var x = 42;"
3623 " function e() {"
3624 " return eval('x');"
3625 " }"
3626 " this.UseEval2 = e;"
3627 "})()";
3628
3629
3630THREADED_TEST(UseEvalFromExtension) {
3631 v8::HandleScope handle_scope;
3632 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3633 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3634 const char* extension_names[] = { "evaltest1", "evaltest2" };
3635 v8::ExtensionConfiguration extensions(2, extension_names);
3636 v8::Handle<Context> context = Context::New(&extensions);
3637 Context::Scope lock(context);
3638 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3639 CHECK_EQ(result, v8::Integer::New(42));
3640 result = Script::Compile(v8_str("UseEval2()"))->Run();
3641 CHECK_EQ(result, v8::Integer::New(42));
3642}
3643
3644
3645static const char* kWithExtensionSource1 =
3646 "function UseWith1() {"
3647 " var x = 42;"
3648 " with({x:87}) { return x; }"
3649 "}";
3650
3651
3652
3653static const char* kWithExtensionSource2 =
3654 "(function() {"
3655 " var x = 42;"
3656 " function e() {"
3657 " with ({x:87}) { return x; }"
3658 " }"
3659 " this.UseWith2 = e;"
3660 "})()";
3661
3662
3663THREADED_TEST(UseWithFromExtension) {
3664 v8::HandleScope handle_scope;
3665 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3666 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3667 const char* extension_names[] = { "withtest1", "withtest2" };
3668 v8::ExtensionConfiguration extensions(2, extension_names);
3669 v8::Handle<Context> context = Context::New(&extensions);
3670 Context::Scope lock(context);
3671 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3672 CHECK_EQ(result, v8::Integer::New(87));
3673 result = Script::Compile(v8_str("UseWith2()"))->Run();
3674 CHECK_EQ(result, v8::Integer::New(87));
3675}
3676
3677
3678THREADED_TEST(AutoExtensions) {
3679 v8::HandleScope handle_scope;
3680 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3681 extension->set_auto_enable(true);
3682 v8::RegisterExtension(extension);
3683 v8::Handle<Context> context = Context::New();
3684 Context::Scope lock(context);
3685 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3686 CHECK_EQ(result, v8::Integer::New(4));
3687}
3688
3689
Steve Blockd0582a62009-12-15 09:54:21 +00003690static const char* kSyntaxErrorInExtensionSource =
3691 "[";
3692
3693
3694// Test that a syntax error in an extension does not cause a fatal
3695// error but results in an empty context.
3696THREADED_TEST(SyntaxErrorExtensions) {
3697 v8::HandleScope handle_scope;
3698 v8::RegisterExtension(new Extension("syntaxerror",
3699 kSyntaxErrorInExtensionSource));
3700 const char* extension_names[] = { "syntaxerror" };
3701 v8::ExtensionConfiguration extensions(1, extension_names);
3702 v8::Handle<Context> context = Context::New(&extensions);
3703 CHECK(context.IsEmpty());
3704}
3705
3706
3707static const char* kExceptionInExtensionSource =
3708 "throw 42";
3709
3710
3711// Test that an exception when installing an extension does not cause
3712// a fatal error but results in an empty context.
3713THREADED_TEST(ExceptionExtensions) {
3714 v8::HandleScope handle_scope;
3715 v8::RegisterExtension(new Extension("exception",
3716 kExceptionInExtensionSource));
3717 const char* extension_names[] = { "exception" };
3718 v8::ExtensionConfiguration extensions(1, extension_names);
3719 v8::Handle<Context> context = Context::New(&extensions);
3720 CHECK(context.IsEmpty());
3721}
3722
3723
Iain Merrick9ac36c92010-09-13 15:29:50 +01003724static const char* kNativeCallInExtensionSource =
3725 "function call_runtime_last_index_of(x) {"
3726 " return %StringLastIndexOf(x, 'bob', 10);"
3727 "}";
3728
3729
3730static const char* kNativeCallTest =
3731 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
3732
3733// Test that a native runtime calls are supported in extensions.
3734THREADED_TEST(NativeCallInExtensions) {
3735 v8::HandleScope handle_scope;
3736 v8::RegisterExtension(new Extension("nativecall",
3737 kNativeCallInExtensionSource));
3738 const char* extension_names[] = { "nativecall" };
3739 v8::ExtensionConfiguration extensions(1, extension_names);
3740 v8::Handle<Context> context = Context::New(&extensions);
3741 Context::Scope lock(context);
3742 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
3743 CHECK_EQ(result, v8::Integer::New(3));
3744}
3745
3746
Steve Blocka7e24c12009-10-30 11:49:00 +00003747static void CheckDependencies(const char* name, const char* expected) {
3748 v8::HandleScope handle_scope;
3749 v8::ExtensionConfiguration config(1, &name);
3750 LocalContext context(&config);
3751 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3752}
3753
3754
3755/*
3756 * Configuration:
3757 *
3758 * /-- B <--\
3759 * A <- -- D <-- E
3760 * \-- C <--/
3761 */
3762THREADED_TEST(ExtensionDependency) {
3763 static const char* kEDeps[] = { "D" };
3764 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3765 static const char* kDDeps[] = { "B", "C" };
3766 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3767 static const char* kBCDeps[] = { "A" };
3768 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3769 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3770 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3771 CheckDependencies("A", "undefinedA");
3772 CheckDependencies("B", "undefinedAB");
3773 CheckDependencies("C", "undefinedAC");
3774 CheckDependencies("D", "undefinedABCD");
3775 CheckDependencies("E", "undefinedABCDE");
3776 v8::HandleScope handle_scope;
3777 static const char* exts[2] = { "C", "E" };
3778 v8::ExtensionConfiguration config(2, exts);
3779 LocalContext context(&config);
3780 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3781}
3782
3783
3784static const char* kExtensionTestScript =
3785 "native function A();"
3786 "native function B();"
3787 "native function C();"
3788 "function Foo(i) {"
3789 " if (i == 0) return A();"
3790 " if (i == 1) return B();"
3791 " if (i == 2) return C();"
3792 "}";
3793
3794
3795static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3796 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003797 if (args.IsConstructCall()) {
3798 args.This()->Set(v8_str("data"), args.Data());
3799 return v8::Null();
3800 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003801 return args.Data();
3802}
3803
3804
3805class FunctionExtension : public Extension {
3806 public:
3807 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3808 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3809 v8::Handle<String> name);
3810};
3811
3812
3813static int lookup_count = 0;
3814v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3815 v8::Handle<String> name) {
3816 lookup_count++;
3817 if (name->Equals(v8_str("A"))) {
3818 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3819 } else if (name->Equals(v8_str("B"))) {
3820 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3821 } else if (name->Equals(v8_str("C"))) {
3822 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3823 } else {
3824 return v8::Handle<v8::FunctionTemplate>();
3825 }
3826}
3827
3828
3829THREADED_TEST(FunctionLookup) {
3830 v8::RegisterExtension(new FunctionExtension());
3831 v8::HandleScope handle_scope;
3832 static const char* exts[1] = { "functiontest" };
3833 v8::ExtensionConfiguration config(1, exts);
3834 LocalContext context(&config);
3835 CHECK_EQ(3, lookup_count);
3836 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3837 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3838 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3839}
3840
3841
Leon Clarkee46be812010-01-19 14:06:41 +00003842THREADED_TEST(NativeFunctionConstructCall) {
3843 v8::RegisterExtension(new FunctionExtension());
3844 v8::HandleScope handle_scope;
3845 static const char* exts[1] = { "functiontest" };
3846 v8::ExtensionConfiguration config(1, exts);
3847 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003848 for (int i = 0; i < 10; i++) {
3849 // Run a few times to ensure that allocation of objects doesn't
3850 // change behavior of a constructor function.
3851 CHECK_EQ(v8::Integer::New(8),
3852 Script::Compile(v8_str("(new A()).data"))->Run());
3853 CHECK_EQ(v8::Integer::New(7),
3854 Script::Compile(v8_str("(new B()).data"))->Run());
3855 CHECK_EQ(v8::Integer::New(6),
3856 Script::Compile(v8_str("(new C()).data"))->Run());
3857 }
Leon Clarkee46be812010-01-19 14:06:41 +00003858}
3859
3860
Steve Blocka7e24c12009-10-30 11:49:00 +00003861static const char* last_location;
3862static const char* last_message;
3863void StoringErrorCallback(const char* location, const char* message) {
3864 if (last_location == NULL) {
3865 last_location = location;
3866 last_message = message;
3867 }
3868}
3869
3870
3871// ErrorReporting creates a circular extensions configuration and
3872// tests that the fatal error handler gets called. This renders V8
3873// unusable and therefore this test cannot be run in parallel.
3874TEST(ErrorReporting) {
3875 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3876 static const char* aDeps[] = { "B" };
3877 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3878 static const char* bDeps[] = { "A" };
3879 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3880 last_location = NULL;
3881 v8::ExtensionConfiguration config(1, bDeps);
3882 v8::Handle<Context> context = Context::New(&config);
3883 CHECK(context.IsEmpty());
3884 CHECK_NE(last_location, NULL);
3885}
3886
3887
3888static const char* js_code_causing_huge_string_flattening =
3889 "var str = 'X';"
3890 "for (var i = 0; i < 30; i++) {"
3891 " str = str + str;"
3892 "}"
3893 "str.match(/X/);";
3894
3895
3896void OOMCallback(const char* location, const char* message) {
3897 exit(0);
3898}
3899
3900
3901TEST(RegexpOutOfMemory) {
3902 // Execute a script that causes out of memory when flattening a string.
3903 v8::HandleScope scope;
3904 v8::V8::SetFatalErrorHandler(OOMCallback);
3905 LocalContext context;
3906 Local<Script> script =
3907 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3908 last_location = NULL;
3909 Local<Value> result = script->Run();
3910
3911 CHECK(false); // Should not return.
3912}
3913
3914
3915static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3916 v8::Handle<Value> data) {
3917 CHECK_EQ(v8::Undefined(), data);
3918 CHECK(message->GetScriptResourceName()->IsUndefined());
3919 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3920 message->GetLineNumber();
3921 message->GetSourceLine();
3922}
3923
3924
3925THREADED_TEST(ErrorWithMissingScriptInfo) {
3926 v8::HandleScope scope;
3927 LocalContext context;
3928 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3929 Script::Compile(v8_str("throw Error()"))->Run();
3930 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3931}
3932
3933
3934int global_index = 0;
3935
3936class Snorkel {
3937 public:
3938 Snorkel() { index_ = global_index++; }
3939 int index_;
3940};
3941
3942class Whammy {
3943 public:
3944 Whammy() {
3945 cursor_ = 0;
3946 }
3947 ~Whammy() {
3948 script_.Dispose();
3949 }
3950 v8::Handle<Script> getScript() {
3951 if (script_.IsEmpty())
3952 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3953 return Local<Script>(*script_);
3954 }
3955
3956 public:
3957 static const int kObjectCount = 256;
3958 int cursor_;
3959 v8::Persistent<v8::Object> objects_[kObjectCount];
3960 v8::Persistent<Script> script_;
3961};
3962
3963static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3964 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3965 delete snorkel;
3966 obj.ClearWeak();
3967}
3968
3969v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3970 const AccessorInfo& info) {
3971 Whammy* whammy =
3972 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3973
3974 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3975
3976 v8::Handle<v8::Object> obj = v8::Object::New();
3977 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3978 if (!prev.IsEmpty()) {
3979 prev->Set(v8_str("next"), obj);
3980 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3981 whammy->objects_[whammy->cursor_].Clear();
3982 }
3983 whammy->objects_[whammy->cursor_] = global;
3984 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3985 return whammy->getScript()->Run();
3986}
3987
3988THREADED_TEST(WeakReference) {
3989 v8::HandleScope handle_scope;
3990 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003991 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00003992 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3993 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003994 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00003995 const char* extension_list[] = { "v8/gc" };
3996 v8::ExtensionConfiguration extensions(1, extension_list);
3997 v8::Persistent<Context> context = Context::New(&extensions);
3998 Context::Scope context_scope(context);
3999
4000 v8::Handle<v8::Object> interceptor = templ->NewInstance();
4001 context->Global()->Set(v8_str("whammy"), interceptor);
4002 const char* code =
4003 "var last;"
4004 "for (var i = 0; i < 10000; i++) {"
4005 " var obj = whammy.length;"
4006 " if (last) last.next = obj;"
4007 " last = obj;"
4008 "}"
4009 "gc();"
4010 "4";
4011 v8::Handle<Value> result = CompileRun(code);
4012 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01004013 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00004014 context.Dispose();
4015}
4016
4017
Steve Blockd0582a62009-12-15 09:54:21 +00004018static bool in_scavenge = false;
4019static int last = -1;
4020
4021static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
4022 CHECK_EQ(-1, last);
4023 last = 0;
4024 obj.Dispose();
4025 obj.Clear();
4026 in_scavenge = true;
4027 i::Heap::PerformScavenge();
4028 in_scavenge = false;
4029 *(reinterpret_cast<bool*>(data)) = true;
4030}
4031
4032static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
4033 void* data) {
4034 CHECK_EQ(0, last);
4035 last = 1;
4036 *(reinterpret_cast<bool*>(data)) = in_scavenge;
4037 obj.Dispose();
4038 obj.Clear();
4039}
4040
4041THREADED_TEST(NoWeakRefCallbacksInScavenge) {
4042 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
4043 // Calling callbacks from scavenges is unsafe as objects held by those
4044 // handlers might have become strongly reachable, but scavenge doesn't
4045 // check that.
4046 v8::Persistent<Context> context = Context::New();
4047 Context::Scope context_scope(context);
4048
4049 v8::Persistent<v8::Object> object_a;
4050 v8::Persistent<v8::Object> object_b;
4051
4052 {
4053 v8::HandleScope handle_scope;
4054 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
4055 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
4056 }
4057
4058 bool object_a_disposed = false;
4059 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
4060 bool released_in_scavenge = false;
4061 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
4062
4063 while (!object_a_disposed) {
4064 i::Heap::CollectAllGarbage(false);
4065 }
4066 CHECK(!released_in_scavenge);
4067}
4068
4069
Steve Blocka7e24c12009-10-30 11:49:00 +00004070v8::Handle<Function> args_fun;
4071
4072
4073static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
4074 ApiTestFuzzer::Fuzz();
4075 CHECK_EQ(args_fun, args.Callee());
4076 CHECK_EQ(3, args.Length());
4077 CHECK_EQ(v8::Integer::New(1), args[0]);
4078 CHECK_EQ(v8::Integer::New(2), args[1]);
4079 CHECK_EQ(v8::Integer::New(3), args[2]);
4080 CHECK_EQ(v8::Undefined(), args[3]);
4081 v8::HandleScope scope;
4082 i::Heap::CollectAllGarbage(false);
4083 return v8::Undefined();
4084}
4085
4086
4087THREADED_TEST(Arguments) {
4088 v8::HandleScope scope;
4089 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
4090 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
4091 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01004092 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00004093 v8_compile("f(1, 2, 3)")->Run();
4094}
4095
4096
Steve Blocka7e24c12009-10-30 11:49:00 +00004097static v8::Handle<Value> NoBlockGetterX(Local<String> name,
4098 const AccessorInfo&) {
4099 return v8::Handle<Value>();
4100}
4101
4102
4103static v8::Handle<Value> NoBlockGetterI(uint32_t index,
4104 const AccessorInfo&) {
4105 return v8::Handle<Value>();
4106}
4107
4108
4109static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
4110 const AccessorInfo&) {
4111 if (!name->Equals(v8_str("foo"))) {
4112 return v8::Handle<v8::Boolean>(); // not intercepted
4113 }
4114
4115 return v8::False(); // intercepted, and don't delete the property
4116}
4117
4118
4119static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4120 if (index != 2) {
4121 return v8::Handle<v8::Boolean>(); // not intercepted
4122 }
4123
4124 return v8::False(); // intercepted, and don't delete the property
4125}
4126
4127
4128THREADED_TEST(Deleter) {
4129 v8::HandleScope scope;
4130 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4131 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4132 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4133 LocalContext context;
4134 context->Global()->Set(v8_str("k"), obj->NewInstance());
4135 CompileRun(
4136 "k.foo = 'foo';"
4137 "k.bar = 'bar';"
4138 "k[2] = 2;"
4139 "k[4] = 4;");
4140 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4141 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4142
4143 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4144 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4145
4146 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4147 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4148
4149 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4150 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4151}
4152
4153
4154static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4155 ApiTestFuzzer::Fuzz();
4156 if (name->Equals(v8_str("foo")) ||
4157 name->Equals(v8_str("bar")) ||
4158 name->Equals(v8_str("baz"))) {
4159 return v8::Undefined();
4160 }
4161 return v8::Handle<Value>();
4162}
4163
4164
4165static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4166 ApiTestFuzzer::Fuzz();
4167 if (index == 0 || index == 1) return v8::Undefined();
4168 return v8::Handle<Value>();
4169}
4170
4171
4172static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4173 ApiTestFuzzer::Fuzz();
4174 v8::Handle<v8::Array> result = v8::Array::New(3);
4175 result->Set(v8::Integer::New(0), v8_str("foo"));
4176 result->Set(v8::Integer::New(1), v8_str("bar"));
4177 result->Set(v8::Integer::New(2), v8_str("baz"));
4178 return result;
4179}
4180
4181
4182static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4183 ApiTestFuzzer::Fuzz();
4184 v8::Handle<v8::Array> result = v8::Array::New(2);
4185 result->Set(v8::Integer::New(0), v8_str("0"));
4186 result->Set(v8::Integer::New(1), v8_str("1"));
4187 return result;
4188}
4189
4190
4191THREADED_TEST(Enumerators) {
4192 v8::HandleScope scope;
4193 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4194 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4195 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4196 LocalContext context;
4197 context->Global()->Set(v8_str("k"), obj->NewInstance());
4198 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4199 "k[10] = 0;"
4200 "k.a = 0;"
4201 "k[5] = 0;"
4202 "k.b = 0;"
4203 "k[4294967295] = 0;"
4204 "k.c = 0;"
4205 "k[4294967296] = 0;"
4206 "k.d = 0;"
4207 "k[140000] = 0;"
4208 "k.e = 0;"
4209 "k[30000000000] = 0;"
4210 "k.f = 0;"
4211 "var result = [];"
4212 "for (var prop in k) {"
4213 " result.push(prop);"
4214 "}"
4215 "result"));
4216 // Check that we get all the property names returned including the
4217 // ones from the enumerators in the right order: indexed properties
4218 // in numerical order, indexed interceptor properties, named
4219 // properties in insertion order, named interceptor properties.
4220 // This order is not mandated by the spec, so this test is just
4221 // documenting our behavior.
4222 CHECK_EQ(17, result->Length());
4223 // Indexed properties in numerical order.
4224 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4225 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4226 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4227 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4228 // Indexed interceptor properties in the order they are returned
4229 // from the enumerator interceptor.
4230 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4231 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4232 // Named properties in insertion order.
4233 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4234 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4235 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4236 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4237 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4238 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4239 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4240 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4241 // Named interceptor properties.
4242 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4243 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4244 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4245}
4246
4247
4248int p_getter_count;
4249int p_getter_count2;
4250
4251
4252static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4253 ApiTestFuzzer::Fuzz();
4254 p_getter_count++;
4255 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4256 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4257 if (name->Equals(v8_str("p1"))) {
4258 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4259 } else if (name->Equals(v8_str("p2"))) {
4260 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4261 } else if (name->Equals(v8_str("p3"))) {
4262 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4263 } else if (name->Equals(v8_str("p4"))) {
4264 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4265 }
4266 return v8::Undefined();
4267}
4268
4269
4270static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4271 ApiTestFuzzer::Fuzz();
4272 LocalContext context;
4273 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4274 CompileRun(
4275 "o1.__proto__ = { };"
4276 "var o2 = { __proto__: o1 };"
4277 "var o3 = { __proto__: o2 };"
4278 "var o4 = { __proto__: o3 };"
4279 "for (var i = 0; i < 10; i++) o4.p4;"
4280 "for (var i = 0; i < 10; i++) o3.p3;"
4281 "for (var i = 0; i < 10; i++) o2.p2;"
4282 "for (var i = 0; i < 10; i++) o1.p1;");
4283}
4284
4285
4286static v8::Handle<Value> PGetter2(Local<String> name,
4287 const AccessorInfo& info) {
4288 ApiTestFuzzer::Fuzz();
4289 p_getter_count2++;
4290 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4291 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4292 if (name->Equals(v8_str("p1"))) {
4293 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4294 } else if (name->Equals(v8_str("p2"))) {
4295 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4296 } else if (name->Equals(v8_str("p3"))) {
4297 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4298 } else if (name->Equals(v8_str("p4"))) {
4299 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4300 }
4301 return v8::Undefined();
4302}
4303
4304
4305THREADED_TEST(GetterHolders) {
4306 v8::HandleScope scope;
4307 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4308 obj->SetAccessor(v8_str("p1"), PGetter);
4309 obj->SetAccessor(v8_str("p2"), PGetter);
4310 obj->SetAccessor(v8_str("p3"), PGetter);
4311 obj->SetAccessor(v8_str("p4"), PGetter);
4312 p_getter_count = 0;
4313 RunHolderTest(obj);
4314 CHECK_EQ(40, p_getter_count);
4315}
4316
4317
4318THREADED_TEST(PreInterceptorHolders) {
4319 v8::HandleScope scope;
4320 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4321 obj->SetNamedPropertyHandler(PGetter2);
4322 p_getter_count2 = 0;
4323 RunHolderTest(obj);
4324 CHECK_EQ(40, p_getter_count2);
4325}
4326
4327
4328THREADED_TEST(ObjectInstantiation) {
4329 v8::HandleScope scope;
4330 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4331 templ->SetAccessor(v8_str("t"), PGetter2);
4332 LocalContext context;
4333 context->Global()->Set(v8_str("o"), templ->NewInstance());
4334 for (int i = 0; i < 100; i++) {
4335 v8::HandleScope inner_scope;
4336 v8::Handle<v8::Object> obj = templ->NewInstance();
4337 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4338 context->Global()->Set(v8_str("o2"), obj);
4339 v8::Handle<Value> value =
4340 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4341 CHECK_EQ(v8::True(), value);
4342 context->Global()->Set(v8_str("o"), obj);
4343 }
4344}
4345
4346
4347THREADED_TEST(StringWrite) {
4348 v8::HandleScope scope;
4349 v8::Handle<String> str = v8_str("abcde");
4350
4351 char buf[100];
4352 int len;
4353
4354 memset(buf, 0x1, sizeof(buf));
4355 len = str->WriteAscii(buf);
4356 CHECK_EQ(len, 5);
4357 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4358
4359 memset(buf, 0x1, sizeof(buf));
4360 len = str->WriteAscii(buf, 0, 4);
4361 CHECK_EQ(len, 4);
4362 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4363
4364 memset(buf, 0x1, sizeof(buf));
4365 len = str->WriteAscii(buf, 0, 5);
4366 CHECK_EQ(len, 5);
4367 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4368
4369 memset(buf, 0x1, sizeof(buf));
4370 len = str->WriteAscii(buf, 0, 6);
4371 CHECK_EQ(len, 5);
4372 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4373
4374 memset(buf, 0x1, sizeof(buf));
4375 len = str->WriteAscii(buf, 4, -1);
4376 CHECK_EQ(len, 1);
4377 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4378
4379 memset(buf, 0x1, sizeof(buf));
4380 len = str->WriteAscii(buf, 4, 6);
4381 CHECK_EQ(len, 1);
4382 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4383
4384 memset(buf, 0x1, sizeof(buf));
4385 len = str->WriteAscii(buf, 4, 1);
4386 CHECK_EQ(len, 1);
4387 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4388}
4389
4390
4391THREADED_TEST(ToArrayIndex) {
4392 v8::HandleScope scope;
4393 LocalContext context;
4394
4395 v8::Handle<String> str = v8_str("42");
4396 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4397 CHECK(!index.IsEmpty());
4398 CHECK_EQ(42.0, index->Uint32Value());
4399 str = v8_str("42asdf");
4400 index = str->ToArrayIndex();
4401 CHECK(index.IsEmpty());
4402 str = v8_str("-42");
4403 index = str->ToArrayIndex();
4404 CHECK(index.IsEmpty());
4405 str = v8_str("4294967295");
4406 index = str->ToArrayIndex();
4407 CHECK(!index.IsEmpty());
4408 CHECK_EQ(4294967295.0, index->Uint32Value());
4409 v8::Handle<v8::Number> num = v8::Number::New(1);
4410 index = num->ToArrayIndex();
4411 CHECK(!index.IsEmpty());
4412 CHECK_EQ(1.0, index->Uint32Value());
4413 num = v8::Number::New(-1);
4414 index = num->ToArrayIndex();
4415 CHECK(index.IsEmpty());
4416 v8::Handle<v8::Object> obj = v8::Object::New();
4417 index = obj->ToArrayIndex();
4418 CHECK(index.IsEmpty());
4419}
4420
4421
4422THREADED_TEST(ErrorConstruction) {
4423 v8::HandleScope scope;
4424 LocalContext context;
4425
4426 v8::Handle<String> foo = v8_str("foo");
4427 v8::Handle<String> message = v8_str("message");
4428 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4429 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004430 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4431 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004432 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4433 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004434 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004435 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4436 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004437 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004438 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4439 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004440 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004441 v8::Handle<Value> error = v8::Exception::Error(foo);
4442 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004443 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004444}
4445
4446
4447static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4448 ApiTestFuzzer::Fuzz();
4449 return v8_num(10);
4450}
4451
4452
4453static void YSetter(Local<String> name,
4454 Local<Value> value,
4455 const AccessorInfo& info) {
4456 if (info.This()->Has(name)) {
4457 info.This()->Delete(name);
4458 }
4459 info.This()->Set(name, value);
4460}
4461
4462
4463THREADED_TEST(DeleteAccessor) {
4464 v8::HandleScope scope;
4465 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4466 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4467 LocalContext context;
4468 v8::Handle<v8::Object> holder = obj->NewInstance();
4469 context->Global()->Set(v8_str("holder"), holder);
4470 v8::Handle<Value> result = CompileRun(
4471 "holder.y = 11; holder.y = 12; holder.y");
4472 CHECK_EQ(12, result->Uint32Value());
4473}
4474
4475
4476THREADED_TEST(TypeSwitch) {
4477 v8::HandleScope scope;
4478 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4479 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4480 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4481 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4482 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4483 LocalContext context;
4484 v8::Handle<v8::Object> obj0 = v8::Object::New();
4485 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4486 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4487 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4488 for (int i = 0; i < 10; i++) {
4489 CHECK_EQ(0, type_switch->match(obj0));
4490 CHECK_EQ(1, type_switch->match(obj1));
4491 CHECK_EQ(2, type_switch->match(obj2));
4492 CHECK_EQ(3, type_switch->match(obj3));
4493 CHECK_EQ(3, type_switch->match(obj3));
4494 CHECK_EQ(2, type_switch->match(obj2));
4495 CHECK_EQ(1, type_switch->match(obj1));
4496 CHECK_EQ(0, type_switch->match(obj0));
4497 }
4498}
4499
4500
4501// For use within the TestSecurityHandler() test.
4502static bool g_security_callback_result = false;
4503static bool NamedSecurityTestCallback(Local<v8::Object> global,
4504 Local<Value> name,
4505 v8::AccessType type,
4506 Local<Value> data) {
4507 // Always allow read access.
4508 if (type == v8::ACCESS_GET)
4509 return true;
4510
4511 // Sometimes allow other access.
4512 return g_security_callback_result;
4513}
4514
4515
4516static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4517 uint32_t key,
4518 v8::AccessType type,
4519 Local<Value> data) {
4520 // Always allow read access.
4521 if (type == v8::ACCESS_GET)
4522 return true;
4523
4524 // Sometimes allow other access.
4525 return g_security_callback_result;
4526}
4527
4528
4529static int trouble_nesting = 0;
4530static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4531 ApiTestFuzzer::Fuzz();
4532 trouble_nesting++;
4533
4534 // Call a JS function that throws an uncaught exception.
4535 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4536 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4537 arg_this->Get(v8_str("trouble_callee")) :
4538 arg_this->Get(v8_str("trouble_caller"));
4539 CHECK(trouble_callee->IsFunction());
4540 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4541}
4542
4543
4544static int report_count = 0;
4545static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4546 v8::Handle<Value>) {
4547 report_count++;
4548}
4549
4550
4551// Counts uncaught exceptions, but other tests running in parallel
4552// also have uncaught exceptions.
4553TEST(ApiUncaughtException) {
4554 report_count = 0;
4555 v8::HandleScope scope;
4556 LocalContext env;
4557 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4558
4559 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4560 v8::Local<v8::Object> global = env->Global();
4561 global->Set(v8_str("trouble"), fun->GetFunction());
4562
4563 Script::Compile(v8_str("function trouble_callee() {"
4564 " var x = null;"
4565 " return x.foo;"
4566 "};"
4567 "function trouble_caller() {"
4568 " trouble();"
4569 "};"))->Run();
4570 Local<Value> trouble = global->Get(v8_str("trouble"));
4571 CHECK(trouble->IsFunction());
4572 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4573 CHECK(trouble_callee->IsFunction());
4574 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4575 CHECK(trouble_caller->IsFunction());
4576 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4577 CHECK_EQ(1, report_count);
4578 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4579}
4580
Leon Clarke4515c472010-02-03 11:58:03 +00004581static const char* script_resource_name = "ExceptionInNativeScript.js";
4582static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4583 v8::Handle<Value>) {
4584 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4585 CHECK(!name_val.IsEmpty() && name_val->IsString());
4586 v8::String::AsciiValue name(message->GetScriptResourceName());
4587 CHECK_EQ(script_resource_name, *name);
4588 CHECK_EQ(3, message->GetLineNumber());
4589 v8::String::AsciiValue source_line(message->GetSourceLine());
4590 CHECK_EQ(" new o.foo();", *source_line);
4591}
4592
4593TEST(ExceptionInNativeScript) {
4594 v8::HandleScope scope;
4595 LocalContext env;
4596 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4597
4598 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4599 v8::Local<v8::Object> global = env->Global();
4600 global->Set(v8_str("trouble"), fun->GetFunction());
4601
4602 Script::Compile(v8_str("function trouble() {\n"
4603 " var o = {};\n"
4604 " new o.foo();\n"
4605 "};"), v8::String::New(script_resource_name))->Run();
4606 Local<Value> trouble = global->Get(v8_str("trouble"));
4607 CHECK(trouble->IsFunction());
4608 Function::Cast(*trouble)->Call(global, 0, NULL);
4609 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4610}
4611
Steve Blocka7e24c12009-10-30 11:49:00 +00004612
4613TEST(CompilationErrorUsingTryCatchHandler) {
4614 v8::HandleScope scope;
4615 LocalContext env;
4616 v8::TryCatch try_catch;
4617 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4618 CHECK_NE(NULL, *try_catch.Exception());
4619 CHECK(try_catch.HasCaught());
4620}
4621
4622
4623TEST(TryCatchFinallyUsingTryCatchHandler) {
4624 v8::HandleScope scope;
4625 LocalContext env;
4626 v8::TryCatch try_catch;
4627 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4628 CHECK(!try_catch.HasCaught());
4629 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4630 CHECK(try_catch.HasCaught());
4631 try_catch.Reset();
4632 Script::Compile(v8_str("(function() {"
4633 "try { throw ''; } finally { return; }"
4634 "})()"))->Run();
4635 CHECK(!try_catch.HasCaught());
4636 Script::Compile(v8_str("(function()"
4637 " { try { throw ''; } finally { throw 0; }"
4638 "})()"))->Run();
4639 CHECK(try_catch.HasCaught());
4640}
4641
4642
4643// SecurityHandler can't be run twice
4644TEST(SecurityHandler) {
4645 v8::HandleScope scope0;
4646 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4647 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4648 IndexedSecurityTestCallback);
4649 // Create an environment
4650 v8::Persistent<Context> context0 =
4651 Context::New(NULL, global_template);
4652 context0->Enter();
4653
4654 v8::Handle<v8::Object> global0 = context0->Global();
4655 v8::Handle<Script> script0 = v8_compile("foo = 111");
4656 script0->Run();
4657 global0->Set(v8_str("0"), v8_num(999));
4658 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4659 CHECK_EQ(111, foo0->Int32Value());
4660 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4661 CHECK_EQ(999, z0->Int32Value());
4662
4663 // Create another environment, should fail security checks.
4664 v8::HandleScope scope1;
4665
4666 v8::Persistent<Context> context1 =
4667 Context::New(NULL, global_template);
4668 context1->Enter();
4669
4670 v8::Handle<v8::Object> global1 = context1->Global();
4671 global1->Set(v8_str("othercontext"), global0);
4672 // This set will fail the security check.
4673 v8::Handle<Script> script1 =
4674 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4675 script1->Run();
4676 // This read will pass the security check.
4677 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4678 CHECK_EQ(111, foo1->Int32Value());
4679 // This read will pass the security check.
4680 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4681 CHECK_EQ(999, z1->Int32Value());
4682
4683 // Create another environment, should pass security checks.
4684 { g_security_callback_result = true; // allow security handler to pass.
4685 v8::HandleScope scope2;
4686 LocalContext context2;
4687 v8::Handle<v8::Object> global2 = context2->Global();
4688 global2->Set(v8_str("othercontext"), global0);
4689 v8::Handle<Script> script2 =
4690 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4691 script2->Run();
4692 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4693 CHECK_EQ(333, foo2->Int32Value());
4694 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4695 CHECK_EQ(888, z2->Int32Value());
4696 }
4697
4698 context1->Exit();
4699 context1.Dispose();
4700
4701 context0->Exit();
4702 context0.Dispose();
4703}
4704
4705
4706THREADED_TEST(SecurityChecks) {
4707 v8::HandleScope handle_scope;
4708 LocalContext env1;
4709 v8::Persistent<Context> env2 = Context::New();
4710
4711 Local<Value> foo = v8_str("foo");
4712 Local<Value> bar = v8_str("bar");
4713
4714 // Set to the same domain.
4715 env1->SetSecurityToken(foo);
4716
4717 // Create a function in env1.
4718 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4719 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4720 CHECK(spy->IsFunction());
4721
4722 // Create another function accessing global objects.
4723 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4724 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4725 CHECK(spy2->IsFunction());
4726
4727 // Switch to env2 in the same domain and invoke spy on env2.
4728 {
4729 env2->SetSecurityToken(foo);
4730 // Enter env2
4731 Context::Scope scope_env2(env2);
4732 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4733 CHECK(result->IsFunction());
4734 }
4735
4736 {
4737 env2->SetSecurityToken(bar);
4738 Context::Scope scope_env2(env2);
4739
4740 // Call cross_domain_call, it should throw an exception
4741 v8::TryCatch try_catch;
4742 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4743 CHECK(try_catch.HasCaught());
4744 }
4745
4746 env2.Dispose();
4747}
4748
4749
4750// Regression test case for issue 1183439.
4751THREADED_TEST(SecurityChecksForPrototypeChain) {
4752 v8::HandleScope scope;
4753 LocalContext current;
4754 v8::Persistent<Context> other = Context::New();
4755
4756 // Change context to be able to get to the Object function in the
4757 // other context without hitting the security checks.
4758 v8::Local<Value> other_object;
4759 { Context::Scope scope(other);
4760 other_object = other->Global()->Get(v8_str("Object"));
4761 other->Global()->Set(v8_num(42), v8_num(87));
4762 }
4763
4764 current->Global()->Set(v8_str("other"), other->Global());
4765 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4766
4767 // Make sure the security check fails here and we get an undefined
4768 // result instead of getting the Object function. Repeat in a loop
4769 // to make sure to exercise the IC code.
4770 v8::Local<Script> access_other0 = v8_compile("other.Object");
4771 v8::Local<Script> access_other1 = v8_compile("other[42]");
4772 for (int i = 0; i < 5; i++) {
4773 CHECK(!access_other0->Run()->Equals(other_object));
4774 CHECK(access_other0->Run()->IsUndefined());
4775 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4776 CHECK(access_other1->Run()->IsUndefined());
4777 }
4778
4779 // Create an object that has 'other' in its prototype chain and make
4780 // sure we cannot access the Object function indirectly through
4781 // that. Repeat in a loop to make sure to exercise the IC code.
4782 v8_compile("function F() { };"
4783 "F.prototype = other;"
4784 "var f = new F();")->Run();
4785 v8::Local<Script> access_f0 = v8_compile("f.Object");
4786 v8::Local<Script> access_f1 = v8_compile("f[42]");
4787 for (int j = 0; j < 5; j++) {
4788 CHECK(!access_f0->Run()->Equals(other_object));
4789 CHECK(access_f0->Run()->IsUndefined());
4790 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4791 CHECK(access_f1->Run()->IsUndefined());
4792 }
4793
4794 // Now it gets hairy: Set the prototype for the other global object
4795 // to be the current global object. The prototype chain for 'f' now
4796 // goes through 'other' but ends up in the current global object.
4797 { Context::Scope scope(other);
4798 other->Global()->Set(v8_str("__proto__"), current->Global());
4799 }
4800 // Set a named and an index property on the current global
4801 // object. To force the lookup to go through the other global object,
4802 // the properties must not exist in the other global object.
4803 current->Global()->Set(v8_str("foo"), v8_num(100));
4804 current->Global()->Set(v8_num(99), v8_num(101));
4805 // Try to read the properties from f and make sure that the access
4806 // gets stopped by the security checks on the other global object.
4807 Local<Script> access_f2 = v8_compile("f.foo");
4808 Local<Script> access_f3 = v8_compile("f[99]");
4809 for (int k = 0; k < 5; k++) {
4810 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4811 CHECK(access_f2->Run()->IsUndefined());
4812 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4813 CHECK(access_f3->Run()->IsUndefined());
4814 }
4815 other.Dispose();
4816}
4817
4818
4819THREADED_TEST(CrossDomainDelete) {
4820 v8::HandleScope handle_scope;
4821 LocalContext env1;
4822 v8::Persistent<Context> env2 = Context::New();
4823
4824 Local<Value> foo = v8_str("foo");
4825 Local<Value> bar = v8_str("bar");
4826
4827 // Set to the same domain.
4828 env1->SetSecurityToken(foo);
4829 env2->SetSecurityToken(foo);
4830
4831 env1->Global()->Set(v8_str("prop"), v8_num(3));
4832 env2->Global()->Set(v8_str("env1"), env1->Global());
4833
4834 // Change env2 to a different domain and delete env1.prop.
4835 env2->SetSecurityToken(bar);
4836 {
4837 Context::Scope scope_env2(env2);
4838 Local<Value> result =
4839 Script::Compile(v8_str("delete env1.prop"))->Run();
4840 CHECK(result->IsFalse());
4841 }
4842
4843 // Check that env1.prop still exists.
4844 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4845 CHECK(v->IsNumber());
4846 CHECK_EQ(3, v->Int32Value());
4847
4848 env2.Dispose();
4849}
4850
4851
4852THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4853 v8::HandleScope handle_scope;
4854 LocalContext env1;
4855 v8::Persistent<Context> env2 = Context::New();
4856
4857 Local<Value> foo = v8_str("foo");
4858 Local<Value> bar = v8_str("bar");
4859
4860 // Set to the same domain.
4861 env1->SetSecurityToken(foo);
4862 env2->SetSecurityToken(foo);
4863
4864 env1->Global()->Set(v8_str("prop"), v8_num(3));
4865 env2->Global()->Set(v8_str("env1"), env1->Global());
4866
4867 // env1.prop is enumerable in env2.
4868 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4869 {
4870 Context::Scope scope_env2(env2);
4871 Local<Value> result = Script::Compile(test)->Run();
4872 CHECK(result->IsTrue());
4873 }
4874
4875 // Change env2 to a different domain and test again.
4876 env2->SetSecurityToken(bar);
4877 {
4878 Context::Scope scope_env2(env2);
4879 Local<Value> result = Script::Compile(test)->Run();
4880 CHECK(result->IsFalse());
4881 }
4882
4883 env2.Dispose();
4884}
4885
4886
4887THREADED_TEST(CrossDomainForIn) {
4888 v8::HandleScope handle_scope;
4889 LocalContext env1;
4890 v8::Persistent<Context> env2 = Context::New();
4891
4892 Local<Value> foo = v8_str("foo");
4893 Local<Value> bar = v8_str("bar");
4894
4895 // Set to the same domain.
4896 env1->SetSecurityToken(foo);
4897 env2->SetSecurityToken(foo);
4898
4899 env1->Global()->Set(v8_str("prop"), v8_num(3));
4900 env2->Global()->Set(v8_str("env1"), env1->Global());
4901
4902 // Change env2 to a different domain and set env1's global object
4903 // as the __proto__ of an object in env2 and enumerate properties
4904 // in for-in. It shouldn't enumerate properties on env1's global
4905 // object.
4906 env2->SetSecurityToken(bar);
4907 {
4908 Context::Scope scope_env2(env2);
4909 Local<Value> result =
4910 CompileRun("(function(){var obj = {'__proto__':env1};"
4911 "for (var p in obj)"
4912 " if (p == 'prop') return false;"
4913 "return true;})()");
4914 CHECK(result->IsTrue());
4915 }
4916 env2.Dispose();
4917}
4918
4919
4920TEST(ContextDetachGlobal) {
4921 v8::HandleScope handle_scope;
4922 LocalContext env1;
4923 v8::Persistent<Context> env2 = Context::New();
4924
4925 Local<v8::Object> global1 = env1->Global();
4926
4927 Local<Value> foo = v8_str("foo");
4928
4929 // Set to the same domain.
4930 env1->SetSecurityToken(foo);
4931 env2->SetSecurityToken(foo);
4932
4933 // Enter env2
4934 env2->Enter();
4935
Andrei Popescu74b3c142010-03-29 12:03:09 +01004936 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004937 Local<v8::Object> global2 = env2->Global();
4938 global2->Set(v8_str("prop"), v8::Integer::New(1));
4939 CompileRun("function getProp() {return prop;}");
4940
4941 env1->Global()->Set(v8_str("getProp"),
4942 global2->Get(v8_str("getProp")));
4943
Andrei Popescu74b3c142010-03-29 12:03:09 +01004944 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004945 env2->Exit();
4946 env2->DetachGlobal();
4947 // env2 has a new global object.
4948 CHECK(!env2->Global()->Equals(global2));
4949
4950 v8::Persistent<Context> env3 =
4951 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4952 env3->SetSecurityToken(v8_str("bar"));
4953 env3->Enter();
4954
4955 Local<v8::Object> global3 = env3->Global();
4956 CHECK_EQ(global2, global3);
4957 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4958 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4959 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4960 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4961 env3->Exit();
4962
4963 // Call getProp in env1, and it should return the value 1
4964 {
4965 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4966 CHECK(get_prop->IsFunction());
4967 v8::TryCatch try_catch;
4968 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4969 CHECK(!try_catch.HasCaught());
4970 CHECK_EQ(1, r->Int32Value());
4971 }
4972
4973 // Check that env3 is not accessible from env1
4974 {
4975 Local<Value> r = global3->Get(v8_str("prop2"));
4976 CHECK(r->IsUndefined());
4977 }
4978
4979 env2.Dispose();
4980 env3.Dispose();
4981}
4982
4983
Andrei Popescu74b3c142010-03-29 12:03:09 +01004984TEST(DetachAndReattachGlobal) {
4985 v8::HandleScope scope;
4986 LocalContext env1;
4987
4988 // Create second environment.
4989 v8::Persistent<Context> env2 = Context::New();
4990
4991 Local<Value> foo = v8_str("foo");
4992
4993 // Set same security token for env1 and env2.
4994 env1->SetSecurityToken(foo);
4995 env2->SetSecurityToken(foo);
4996
4997 // Create a property on the global object in env2.
4998 {
4999 v8::Context::Scope scope(env2);
5000 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
5001 }
5002
5003 // Create a reference to env2 global from env1 global.
5004 env1->Global()->Set(v8_str("other"), env2->Global());
5005
5006 // Check that we have access to other.p in env2 from env1.
5007 Local<Value> result = CompileRun("other.p");
5008 CHECK(result->IsInt32());
5009 CHECK_EQ(42, result->Int32Value());
5010
5011 // Hold on to global from env2 and detach global from env2.
5012 Local<v8::Object> global2 = env2->Global();
5013 env2->DetachGlobal();
5014
5015 // Check that the global has been detached. No other.p property can
5016 // be found.
5017 result = CompileRun("other.p");
5018 CHECK(result->IsUndefined());
5019
5020 // Reuse global2 for env3.
5021 v8::Persistent<Context> env3 =
5022 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
5023 CHECK_EQ(global2, env3->Global());
5024
5025 // Start by using the same security token for env3 as for env1 and env2.
5026 env3->SetSecurityToken(foo);
5027
5028 // Create a property on the global object in env3.
5029 {
5030 v8::Context::Scope scope(env3);
5031 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
5032 }
5033
5034 // Check that other.p is now the property in env3 and that we have access.
5035 result = CompileRun("other.p");
5036 CHECK(result->IsInt32());
5037 CHECK_EQ(24, result->Int32Value());
5038
5039 // Change security token for env3 to something different from env1 and env2.
5040 env3->SetSecurityToken(v8_str("bar"));
5041
5042 // Check that we do not have access to other.p in env1. |other| is now
5043 // the global object for env3 which has a different security token,
5044 // so access should be blocked.
5045 result = CompileRun("other.p");
5046 CHECK(result->IsUndefined());
5047
5048 // Detach the global for env3 and reattach it to env2.
5049 env3->DetachGlobal();
5050 env2->ReattachGlobal(global2);
5051
5052 // Check that we have access to other.p again in env1. |other| is now
5053 // the global object for env2 which has the same security token as env1.
5054 result = CompileRun("other.p");
5055 CHECK(result->IsInt32());
5056 CHECK_EQ(42, result->Int32Value());
5057
5058 env2.Dispose();
5059 env3.Dispose();
5060}
5061
5062
Steve Blocka7e24c12009-10-30 11:49:00 +00005063static bool NamedAccessBlocker(Local<v8::Object> global,
5064 Local<Value> name,
5065 v8::AccessType type,
5066 Local<Value> data) {
5067 return Context::GetCurrent()->Global()->Equals(global);
5068}
5069
5070
5071static bool IndexedAccessBlocker(Local<v8::Object> global,
5072 uint32_t key,
5073 v8::AccessType type,
5074 Local<Value> data) {
5075 return Context::GetCurrent()->Global()->Equals(global);
5076}
5077
5078
5079static int g_echo_value = -1;
5080static v8::Handle<Value> EchoGetter(Local<String> name,
5081 const AccessorInfo& info) {
5082 return v8_num(g_echo_value);
5083}
5084
5085
5086static void EchoSetter(Local<String> name,
5087 Local<Value> value,
5088 const AccessorInfo&) {
5089 if (value->IsNumber())
5090 g_echo_value = value->Int32Value();
5091}
5092
5093
5094static v8::Handle<Value> UnreachableGetter(Local<String> name,
5095 const AccessorInfo& info) {
5096 CHECK(false); // This function should not be called..
5097 return v8::Undefined();
5098}
5099
5100
5101static void UnreachableSetter(Local<String>, Local<Value>,
5102 const AccessorInfo&) {
5103 CHECK(false); // This function should nto be called.
5104}
5105
5106
5107THREADED_TEST(AccessControl) {
5108 v8::HandleScope handle_scope;
5109 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5110
5111 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5112 IndexedAccessBlocker);
5113
5114 // Add an accessor accessible by cross-domain JS code.
5115 global_template->SetAccessor(
5116 v8_str("accessible_prop"),
5117 EchoGetter, EchoSetter,
5118 v8::Handle<Value>(),
5119 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5120
5121 // Add an accessor that is not accessible by cross-domain JS code.
5122 global_template->SetAccessor(v8_str("blocked_prop"),
5123 UnreachableGetter, UnreachableSetter,
5124 v8::Handle<Value>(),
5125 v8::DEFAULT);
5126
5127 // Create an environment
5128 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5129 context0->Enter();
5130
5131 v8::Handle<v8::Object> global0 = context0->Global();
5132
5133 v8::HandleScope scope1;
5134
5135 v8::Persistent<Context> context1 = Context::New();
5136 context1->Enter();
5137
5138 v8::Handle<v8::Object> global1 = context1->Global();
5139 global1->Set(v8_str("other"), global0);
5140
5141 v8::Handle<Value> value;
5142
5143 // Access blocked property
5144 value = v8_compile("other.blocked_prop = 1")->Run();
5145 value = v8_compile("other.blocked_prop")->Run();
5146 CHECK(value->IsUndefined());
5147
5148 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
5149 CHECK(value->IsFalse());
5150
5151 // Access accessible property
5152 value = v8_compile("other.accessible_prop = 3")->Run();
5153 CHECK(value->IsNumber());
5154 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005155 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005156
5157 value = v8_compile("other.accessible_prop")->Run();
5158 CHECK(value->IsNumber());
5159 CHECK_EQ(3, value->Int32Value());
5160
5161 value =
5162 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
5163 CHECK(value->IsTrue());
5164
5165 // Enumeration doesn't enumerate accessors from inaccessible objects in
5166 // the prototype chain even if the accessors are in themselves accessible.
5167 Local<Value> result =
5168 CompileRun("(function(){var obj = {'__proto__':other};"
5169 "for (var p in obj)"
5170 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5171 " return false;"
5172 " }"
5173 "return true;})()");
5174 CHECK(result->IsTrue());
5175
5176 context1->Exit();
5177 context0->Exit();
5178 context1.Dispose();
5179 context0.Dispose();
5180}
5181
5182
Leon Clarke4515c472010-02-03 11:58:03 +00005183static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5184 Local<Value> name,
5185 v8::AccessType type,
5186 Local<Value> data) {
5187 return false;
5188}
5189
5190
5191static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5192 uint32_t key,
5193 v8::AccessType type,
5194 Local<Value> data) {
5195 return false;
5196}
5197
5198
5199THREADED_TEST(AccessControlGetOwnPropertyNames) {
5200 v8::HandleScope handle_scope;
5201 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5202
5203 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5204 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5205 GetOwnPropertyNamesIndexedBlocker);
5206
5207 // Create an environment
5208 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5209 context0->Enter();
5210
5211 v8::Handle<v8::Object> global0 = context0->Global();
5212
5213 v8::HandleScope scope1;
5214
5215 v8::Persistent<Context> context1 = Context::New();
5216 context1->Enter();
5217
5218 v8::Handle<v8::Object> global1 = context1->Global();
5219 global1->Set(v8_str("other"), global0);
5220 global1->Set(v8_str("object"), obj_template->NewInstance());
5221
5222 v8::Handle<Value> value;
5223
5224 // Attempt to get the property names of the other global object and
5225 // of an object that requires access checks. Accessing the other
5226 // global object should be blocked by access checks on the global
5227 // proxy object. Accessing the object that requires access checks
5228 // is blocked by the access checks on the object itself.
5229 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5230 CHECK(value->IsTrue());
5231
5232 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5233 CHECK(value->IsTrue());
5234
5235 context1->Exit();
5236 context0->Exit();
5237 context1.Dispose();
5238 context0.Dispose();
5239}
5240
5241
Steve Block8defd9f2010-07-08 12:39:36 +01005242static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5243 v8::Handle<v8::Array> result = v8::Array::New(1);
5244 result->Set(0, v8_str("x"));
5245 return result;
5246}
5247
5248
5249THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5250 v8::HandleScope handle_scope;
5251 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5252
5253 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5254 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5255 NamedPropertyEnumerator);
5256
5257 LocalContext context;
5258 v8::Handle<v8::Object> global = context->Global();
5259 global->Set(v8_str("object"), obj_template->NewInstance());
5260
5261 v8::Handle<Value> value =
5262 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5263 CHECK_EQ(v8_str("x"), value);
5264}
5265
5266
Steve Blocka7e24c12009-10-30 11:49:00 +00005267static v8::Handle<Value> ConstTenGetter(Local<String> name,
5268 const AccessorInfo& info) {
5269 return v8_num(10);
5270}
5271
5272
5273THREADED_TEST(CrossDomainAccessors) {
5274 v8::HandleScope handle_scope;
5275
5276 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5277
5278 v8::Handle<v8::ObjectTemplate> global_template =
5279 func_template->InstanceTemplate();
5280
5281 v8::Handle<v8::ObjectTemplate> proto_template =
5282 func_template->PrototypeTemplate();
5283
5284 // Add an accessor to proto that's accessible by cross-domain JS code.
5285 proto_template->SetAccessor(v8_str("accessible"),
5286 ConstTenGetter, 0,
5287 v8::Handle<Value>(),
5288 v8::ALL_CAN_READ);
5289
5290 // Add an accessor that is not accessible by cross-domain JS code.
5291 global_template->SetAccessor(v8_str("unreachable"),
5292 UnreachableGetter, 0,
5293 v8::Handle<Value>(),
5294 v8::DEFAULT);
5295
5296 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5297 context0->Enter();
5298
5299 Local<v8::Object> global = context0->Global();
5300 // Add a normal property that shadows 'accessible'
5301 global->Set(v8_str("accessible"), v8_num(11));
5302
5303 // Enter a new context.
5304 v8::HandleScope scope1;
5305 v8::Persistent<Context> context1 = Context::New();
5306 context1->Enter();
5307
5308 v8::Handle<v8::Object> global1 = context1->Global();
5309 global1->Set(v8_str("other"), global);
5310
5311 // Should return 10, instead of 11
5312 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5313 CHECK(value->IsNumber());
5314 CHECK_EQ(10, value->Int32Value());
5315
5316 value = v8_compile("other.unreachable")->Run();
5317 CHECK(value->IsUndefined());
5318
5319 context1->Exit();
5320 context0->Exit();
5321 context1.Dispose();
5322 context0.Dispose();
5323}
5324
5325
5326static int named_access_count = 0;
5327static int indexed_access_count = 0;
5328
5329static bool NamedAccessCounter(Local<v8::Object> global,
5330 Local<Value> name,
5331 v8::AccessType type,
5332 Local<Value> data) {
5333 named_access_count++;
5334 return true;
5335}
5336
5337
5338static bool IndexedAccessCounter(Local<v8::Object> global,
5339 uint32_t key,
5340 v8::AccessType type,
5341 Local<Value> data) {
5342 indexed_access_count++;
5343 return true;
5344}
5345
5346
5347// This one is too easily disturbed by other tests.
5348TEST(AccessControlIC) {
5349 named_access_count = 0;
5350 indexed_access_count = 0;
5351
5352 v8::HandleScope handle_scope;
5353
5354 // Create an environment.
5355 v8::Persistent<Context> context0 = Context::New();
5356 context0->Enter();
5357
5358 // Create an object that requires access-check functions to be
5359 // called for cross-domain access.
5360 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5361 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5362 IndexedAccessCounter);
5363 Local<v8::Object> object = object_template->NewInstance();
5364
5365 v8::HandleScope scope1;
5366
5367 // Create another environment.
5368 v8::Persistent<Context> context1 = Context::New();
5369 context1->Enter();
5370
5371 // Make easy access to the object from the other environment.
5372 v8::Handle<v8::Object> global1 = context1->Global();
5373 global1->Set(v8_str("obj"), object);
5374
5375 v8::Handle<Value> value;
5376
5377 // Check that the named access-control function is called every time.
5378 CompileRun("function testProp(obj) {"
5379 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5380 " for (var j = 0; j < 10; j++) obj.prop;"
5381 " return obj.prop"
5382 "}");
5383 value = CompileRun("testProp(obj)");
5384 CHECK(value->IsNumber());
5385 CHECK_EQ(1, value->Int32Value());
5386 CHECK_EQ(21, named_access_count);
5387
5388 // Check that the named access-control function is called every time.
5389 CompileRun("var p = 'prop';"
5390 "function testKeyed(obj) {"
5391 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5392 " for (var j = 0; j < 10; j++) obj[p];"
5393 " return obj[p];"
5394 "}");
5395 // Use obj which requires access checks. No inline caching is used
5396 // in that case.
5397 value = CompileRun("testKeyed(obj)");
5398 CHECK(value->IsNumber());
5399 CHECK_EQ(1, value->Int32Value());
5400 CHECK_EQ(42, named_access_count);
5401 // Force the inline caches into generic state and try again.
5402 CompileRun("testKeyed({ a: 0 })");
5403 CompileRun("testKeyed({ b: 0 })");
5404 value = CompileRun("testKeyed(obj)");
5405 CHECK(value->IsNumber());
5406 CHECK_EQ(1, value->Int32Value());
5407 CHECK_EQ(63, named_access_count);
5408
5409 // Check that the indexed access-control function is called every time.
5410 CompileRun("function testIndexed(obj) {"
5411 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5412 " for (var j = 0; j < 10; j++) obj[0];"
5413 " return obj[0]"
5414 "}");
5415 value = CompileRun("testIndexed(obj)");
5416 CHECK(value->IsNumber());
5417 CHECK_EQ(1, value->Int32Value());
5418 CHECK_EQ(21, indexed_access_count);
5419 // Force the inline caches into generic state.
5420 CompileRun("testIndexed(new Array(1))");
5421 // Test that the indexed access check is called.
5422 value = CompileRun("testIndexed(obj)");
5423 CHECK(value->IsNumber());
5424 CHECK_EQ(1, value->Int32Value());
5425 CHECK_EQ(42, indexed_access_count);
5426
5427 // Check that the named access check is called when invoking
5428 // functions on an object that requires access checks.
5429 CompileRun("obj.f = function() {}");
5430 CompileRun("function testCallNormal(obj) {"
5431 " for (var i = 0; i < 10; i++) obj.f();"
5432 "}");
5433 CompileRun("testCallNormal(obj)");
5434 CHECK_EQ(74, named_access_count);
5435
5436 // Force obj into slow case.
5437 value = CompileRun("delete obj.prop");
5438 CHECK(value->BooleanValue());
5439 // Force inline caches into dictionary probing mode.
5440 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5441 // Test that the named access check is called.
5442 value = CompileRun("testProp(obj);");
5443 CHECK(value->IsNumber());
5444 CHECK_EQ(1, value->Int32Value());
5445 CHECK_EQ(96, named_access_count);
5446
5447 // Force the call inline cache into dictionary probing mode.
5448 CompileRun("o.f = function() {}; testCallNormal(o)");
5449 // Test that the named access check is still called for each
5450 // invocation of the function.
5451 value = CompileRun("testCallNormal(obj)");
5452 CHECK_EQ(106, named_access_count);
5453
5454 context1->Exit();
5455 context0->Exit();
5456 context1.Dispose();
5457 context0.Dispose();
5458}
5459
5460
5461static bool NamedAccessFlatten(Local<v8::Object> global,
5462 Local<Value> name,
5463 v8::AccessType type,
5464 Local<Value> data) {
5465 char buf[100];
5466 int len;
5467
5468 CHECK(name->IsString());
5469
5470 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005471 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005472 CHECK_EQ(4, len);
5473
5474 uint16_t buf2[100];
5475
5476 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005477 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005478 CHECK_EQ(4, len);
5479
5480 return true;
5481}
5482
5483
5484static bool IndexedAccessFlatten(Local<v8::Object> global,
5485 uint32_t key,
5486 v8::AccessType type,
5487 Local<Value> data) {
5488 return true;
5489}
5490
5491
5492// Regression test. In access checks, operations that may cause
5493// garbage collection are not allowed. It used to be the case that
5494// using the Write operation on a string could cause a garbage
5495// collection due to flattening of the string. This is no longer the
5496// case.
5497THREADED_TEST(AccessControlFlatten) {
5498 named_access_count = 0;
5499 indexed_access_count = 0;
5500
5501 v8::HandleScope handle_scope;
5502
5503 // Create an environment.
5504 v8::Persistent<Context> context0 = Context::New();
5505 context0->Enter();
5506
5507 // Create an object that requires access-check functions to be
5508 // called for cross-domain access.
5509 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5510 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5511 IndexedAccessFlatten);
5512 Local<v8::Object> object = object_template->NewInstance();
5513
5514 v8::HandleScope scope1;
5515
5516 // Create another environment.
5517 v8::Persistent<Context> context1 = Context::New();
5518 context1->Enter();
5519
5520 // Make easy access to the object from the other environment.
5521 v8::Handle<v8::Object> global1 = context1->Global();
5522 global1->Set(v8_str("obj"), object);
5523
5524 v8::Handle<Value> value;
5525
5526 value = v8_compile("var p = 'as' + 'df';")->Run();
5527 value = v8_compile("obj[p];")->Run();
5528
5529 context1->Exit();
5530 context0->Exit();
5531 context1.Dispose();
5532 context0.Dispose();
5533}
5534
5535
5536static v8::Handle<Value> AccessControlNamedGetter(
5537 Local<String>, const AccessorInfo&) {
5538 return v8::Integer::New(42);
5539}
5540
5541
5542static v8::Handle<Value> AccessControlNamedSetter(
5543 Local<String>, Local<Value> value, const AccessorInfo&) {
5544 return value;
5545}
5546
5547
5548static v8::Handle<Value> AccessControlIndexedGetter(
5549 uint32_t index,
5550 const AccessorInfo& info) {
5551 return v8_num(42);
5552}
5553
5554
5555static v8::Handle<Value> AccessControlIndexedSetter(
5556 uint32_t, Local<Value> value, const AccessorInfo&) {
5557 return value;
5558}
5559
5560
5561THREADED_TEST(AccessControlInterceptorIC) {
5562 named_access_count = 0;
5563 indexed_access_count = 0;
5564
5565 v8::HandleScope handle_scope;
5566
5567 // Create an environment.
5568 v8::Persistent<Context> context0 = Context::New();
5569 context0->Enter();
5570
5571 // Create an object that requires access-check functions to be
5572 // called for cross-domain access. The object also has interceptors
5573 // interceptor.
5574 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5575 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5576 IndexedAccessCounter);
5577 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5578 AccessControlNamedSetter);
5579 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5580 AccessControlIndexedSetter);
5581 Local<v8::Object> object = object_template->NewInstance();
5582
5583 v8::HandleScope scope1;
5584
5585 // Create another environment.
5586 v8::Persistent<Context> context1 = Context::New();
5587 context1->Enter();
5588
5589 // Make easy access to the object from the other environment.
5590 v8::Handle<v8::Object> global1 = context1->Global();
5591 global1->Set(v8_str("obj"), object);
5592
5593 v8::Handle<Value> value;
5594
5595 // Check that the named access-control function is called every time
5596 // eventhough there is an interceptor on the object.
5597 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5598 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5599 "obj.x")->Run();
5600 CHECK(value->IsNumber());
5601 CHECK_EQ(42, value->Int32Value());
5602 CHECK_EQ(21, named_access_count);
5603
5604 value = v8_compile("var p = 'x';")->Run();
5605 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5606 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5607 "obj[p]")->Run();
5608 CHECK(value->IsNumber());
5609 CHECK_EQ(42, value->Int32Value());
5610 CHECK_EQ(42, named_access_count);
5611
5612 // Check that the indexed access-control function is called every
5613 // time eventhough there is an interceptor on the object.
5614 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5615 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5616 "obj[0]")->Run();
5617 CHECK(value->IsNumber());
5618 CHECK_EQ(42, value->Int32Value());
5619 CHECK_EQ(21, indexed_access_count);
5620
5621 context1->Exit();
5622 context0->Exit();
5623 context1.Dispose();
5624 context0.Dispose();
5625}
5626
5627
5628THREADED_TEST(Version) {
5629 v8::V8::GetVersion();
5630}
5631
5632
5633static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5634 ApiTestFuzzer::Fuzz();
5635 return v8_num(12);
5636}
5637
5638
5639THREADED_TEST(InstanceProperties) {
5640 v8::HandleScope handle_scope;
5641 LocalContext context;
5642
5643 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5644 Local<ObjectTemplate> instance = t->InstanceTemplate();
5645
5646 instance->Set(v8_str("x"), v8_num(42));
5647 instance->Set(v8_str("f"),
5648 v8::FunctionTemplate::New(InstanceFunctionCallback));
5649
5650 Local<Value> o = t->GetFunction()->NewInstance();
5651
5652 context->Global()->Set(v8_str("i"), o);
5653 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5654 CHECK_EQ(42, value->Int32Value());
5655
5656 value = Script::Compile(v8_str("i.f()"))->Run();
5657 CHECK_EQ(12, value->Int32Value());
5658}
5659
5660
5661static v8::Handle<Value>
5662GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5663 ApiTestFuzzer::Fuzz();
5664 return v8::Handle<Value>();
5665}
5666
5667
5668THREADED_TEST(GlobalObjectInstanceProperties) {
5669 v8::HandleScope handle_scope;
5670
5671 Local<Value> global_object;
5672
5673 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5674 t->InstanceTemplate()->SetNamedPropertyHandler(
5675 GlobalObjectInstancePropertiesGet);
5676 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5677 instance_template->Set(v8_str("x"), v8_num(42));
5678 instance_template->Set(v8_str("f"),
5679 v8::FunctionTemplate::New(InstanceFunctionCallback));
5680
5681 {
5682 LocalContext env(NULL, instance_template);
5683 // Hold on to the global object so it can be used again in another
5684 // environment initialization.
5685 global_object = env->Global();
5686
5687 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5688 CHECK_EQ(42, value->Int32Value());
5689 value = Script::Compile(v8_str("f()"))->Run();
5690 CHECK_EQ(12, value->Int32Value());
5691 }
5692
5693 {
5694 // Create new environment reusing the global object.
5695 LocalContext env(NULL, instance_template, global_object);
5696 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5697 CHECK_EQ(42, value->Int32Value());
5698 value = Script::Compile(v8_str("f()"))->Run();
5699 CHECK_EQ(12, value->Int32Value());
5700 }
5701}
5702
5703
5704static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5705 ApiTestFuzzer::Fuzz();
5706 return v8_num(42);
5707}
5708
5709
5710static int shadow_y;
5711static int shadow_y_setter_call_count;
5712static int shadow_y_getter_call_count;
5713
5714
5715static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5716 shadow_y_setter_call_count++;
5717 shadow_y = 42;
5718}
5719
5720
5721static v8::Handle<Value> ShadowYGetter(Local<String> name,
5722 const AccessorInfo& info) {
5723 ApiTestFuzzer::Fuzz();
5724 shadow_y_getter_call_count++;
5725 return v8_num(shadow_y);
5726}
5727
5728
5729static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5730 const AccessorInfo& info) {
5731 return v8::Handle<Value>();
5732}
5733
5734
5735static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5736 const AccessorInfo&) {
5737 return v8::Handle<Value>();
5738}
5739
5740
5741THREADED_TEST(ShadowObject) {
5742 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5743 v8::HandleScope handle_scope;
5744
5745 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5746 LocalContext context(NULL, global_template);
5747
5748 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5749 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5750 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5751 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5752 Local<ObjectTemplate> instance = t->InstanceTemplate();
5753
5754 // Only allow calls of f on instances of t.
5755 Local<v8::Signature> signature = v8::Signature::New(t);
5756 proto->Set(v8_str("f"),
5757 v8::FunctionTemplate::New(ShadowFunctionCallback,
5758 Local<Value>(),
5759 signature));
5760 proto->Set(v8_str("x"), v8_num(12));
5761
5762 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5763
5764 Local<Value> o = t->GetFunction()->NewInstance();
5765 context->Global()->Set(v8_str("__proto__"), o);
5766
5767 Local<Value> value =
5768 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5769 CHECK(value->IsBoolean());
5770 CHECK(!value->BooleanValue());
5771
5772 value = Script::Compile(v8_str("x"))->Run();
5773 CHECK_EQ(12, value->Int32Value());
5774
5775 value = Script::Compile(v8_str("f()"))->Run();
5776 CHECK_EQ(42, value->Int32Value());
5777
5778 Script::Compile(v8_str("y = 42"))->Run();
5779 CHECK_EQ(1, shadow_y_setter_call_count);
5780 value = Script::Compile(v8_str("y"))->Run();
5781 CHECK_EQ(1, shadow_y_getter_call_count);
5782 CHECK_EQ(42, value->Int32Value());
5783}
5784
5785
5786THREADED_TEST(HiddenPrototype) {
5787 v8::HandleScope handle_scope;
5788 LocalContext context;
5789
5790 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5791 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5792 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5793 t1->SetHiddenPrototype(true);
5794 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5795 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5796 t2->SetHiddenPrototype(true);
5797 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5798 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5799 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5800
5801 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5802 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5803 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5804 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5805
5806 // Setting the prototype on an object skips hidden prototypes.
5807 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5808 o0->Set(v8_str("__proto__"), o1);
5809 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5810 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5811 o0->Set(v8_str("__proto__"), o2);
5812 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5813 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5814 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5815 o0->Set(v8_str("__proto__"), o3);
5816 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5817 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5818 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5819 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5820
5821 // Getting the prototype of o0 should get the first visible one
5822 // which is o3. Therefore, z should not be defined on the prototype
5823 // object.
5824 Local<Value> proto = o0->Get(v8_str("__proto__"));
5825 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005826 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005827}
5828
5829
Andrei Popescu402d9372010-02-26 13:31:12 +00005830THREADED_TEST(SetPrototype) {
5831 v8::HandleScope handle_scope;
5832 LocalContext context;
5833
5834 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5835 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5836 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5837 t1->SetHiddenPrototype(true);
5838 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5839 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5840 t2->SetHiddenPrototype(true);
5841 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5842 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5843 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5844
5845 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5846 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5847 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5848 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5849
5850 // Setting the prototype on an object does not skip hidden prototypes.
5851 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5852 CHECK(o0->SetPrototype(o1));
5853 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5854 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5855 CHECK(o1->SetPrototype(o2));
5856 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5857 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5858 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5859 CHECK(o2->SetPrototype(o3));
5860 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5861 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5862 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5863 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5864
5865 // Getting the prototype of o0 should get the first visible one
5866 // which is o3. Therefore, z should not be defined on the prototype
5867 // object.
5868 Local<Value> proto = o0->Get(v8_str("__proto__"));
5869 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005870 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005871
5872 // However, Object::GetPrototype ignores hidden prototype.
5873 Local<Value> proto0 = o0->GetPrototype();
5874 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005875 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005876
5877 Local<Value> proto1 = o1->GetPrototype();
5878 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005879 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005880
5881 Local<Value> proto2 = o2->GetPrototype();
5882 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005883 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005884}
5885
5886
5887THREADED_TEST(SetPrototypeThrows) {
5888 v8::HandleScope handle_scope;
5889 LocalContext context;
5890
5891 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5892
5893 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5894 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5895
5896 CHECK(o0->SetPrototype(o1));
5897 // If setting the prototype leads to the cycle, SetPrototype should
5898 // return false and keep VM in sane state.
5899 v8::TryCatch try_catch;
5900 CHECK(!o1->SetPrototype(o0));
5901 CHECK(!try_catch.HasCaught());
5902 ASSERT(!i::Top::has_pending_exception());
5903
5904 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5905}
5906
5907
Steve Blocka7e24c12009-10-30 11:49:00 +00005908THREADED_TEST(GetterSetterExceptions) {
5909 v8::HandleScope handle_scope;
5910 LocalContext context;
5911 CompileRun(
5912 "function Foo() { };"
5913 "function Throw() { throw 5; };"
5914 "var x = { };"
5915 "x.__defineSetter__('set', Throw);"
5916 "x.__defineGetter__('get', Throw);");
5917 Local<v8::Object> x =
5918 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5919 v8::TryCatch try_catch;
5920 x->Set(v8_str("set"), v8::Integer::New(8));
5921 x->Get(v8_str("get"));
5922 x->Set(v8_str("set"), v8::Integer::New(8));
5923 x->Get(v8_str("get"));
5924 x->Set(v8_str("set"), v8::Integer::New(8));
5925 x->Get(v8_str("get"));
5926 x->Set(v8_str("set"), v8::Integer::New(8));
5927 x->Get(v8_str("get"));
5928}
5929
5930
5931THREADED_TEST(Constructor) {
5932 v8::HandleScope handle_scope;
5933 LocalContext context;
5934 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5935 templ->SetClassName(v8_str("Fun"));
5936 Local<Function> cons = templ->GetFunction();
5937 context->Global()->Set(v8_str("Fun"), cons);
5938 Local<v8::Object> inst = cons->NewInstance();
5939 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5940 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5941 CHECK(value->BooleanValue());
5942}
5943
5944THREADED_TEST(FunctionDescriptorException) {
5945 v8::HandleScope handle_scope;
5946 LocalContext context;
5947 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5948 templ->SetClassName(v8_str("Fun"));
5949 Local<Function> cons = templ->GetFunction();
5950 context->Global()->Set(v8_str("Fun"), cons);
5951 Local<Value> value = CompileRun(
5952 "function test() {"
5953 " try {"
5954 " (new Fun()).blah()"
5955 " } catch (e) {"
5956 " var str = String(e);"
5957 " if (str.indexOf('TypeError') == -1) return 1;"
5958 " if (str.indexOf('[object Fun]') != -1) return 2;"
5959 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5960 " return 0;"
5961 " }"
5962 " return 4;"
5963 "}"
5964 "test();");
5965 CHECK_EQ(0, value->Int32Value());
5966}
5967
5968
5969THREADED_TEST(EvalAliasedDynamic) {
5970 v8::HandleScope scope;
5971 LocalContext current;
5972
5973 // Tests where aliased eval can only be resolved dynamically.
5974 Local<Script> script =
5975 Script::Compile(v8_str("function f(x) { "
5976 " var foo = 2;"
5977 " with (x) { return eval('foo'); }"
5978 "}"
5979 "foo = 0;"
5980 "result1 = f(new Object());"
5981 "result2 = f(this);"
5982 "var x = new Object();"
5983 "x.eval = function(x) { return 1; };"
5984 "result3 = f(x);"));
5985 script->Run();
5986 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5987 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5988 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5989
5990 v8::TryCatch try_catch;
5991 script =
5992 Script::Compile(v8_str("function f(x) { "
5993 " var bar = 2;"
5994 " with (x) { return eval('bar'); }"
5995 "}"
5996 "f(this)"));
5997 script->Run();
5998 CHECK(try_catch.HasCaught());
5999 try_catch.Reset();
6000}
6001
6002
6003THREADED_TEST(CrossEval) {
6004 v8::HandleScope scope;
6005 LocalContext other;
6006 LocalContext current;
6007
6008 Local<String> token = v8_str("<security token>");
6009 other->SetSecurityToken(token);
6010 current->SetSecurityToken(token);
6011
6012 // Setup reference from current to other.
6013 current->Global()->Set(v8_str("other"), other->Global());
6014
6015 // Check that new variables are introduced in other context.
6016 Local<Script> script =
6017 Script::Compile(v8_str("other.eval('var foo = 1234')"));
6018 script->Run();
6019 Local<Value> foo = other->Global()->Get(v8_str("foo"));
6020 CHECK_EQ(1234, foo->Int32Value());
6021 CHECK(!current->Global()->Has(v8_str("foo")));
6022
6023 // Check that writing to non-existing properties introduces them in
6024 // the other context.
6025 script =
6026 Script::Compile(v8_str("other.eval('na = 1234')"));
6027 script->Run();
6028 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
6029 CHECK(!current->Global()->Has(v8_str("na")));
6030
6031 // Check that global variables in current context are not visible in other
6032 // context.
6033 v8::TryCatch try_catch;
6034 script =
6035 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
6036 Local<Value> result = script->Run();
6037 CHECK(try_catch.HasCaught());
6038 try_catch.Reset();
6039
6040 // Check that local variables in current context are not visible in other
6041 // context.
6042 script =
6043 Script::Compile(v8_str("(function() { "
6044 " var baz = 87;"
6045 " return other.eval('baz');"
6046 "})();"));
6047 result = script->Run();
6048 CHECK(try_catch.HasCaught());
6049 try_catch.Reset();
6050
6051 // Check that global variables in the other environment are visible
6052 // when evaluting code.
6053 other->Global()->Set(v8_str("bis"), v8_num(1234));
6054 script = Script::Compile(v8_str("other.eval('bis')"));
6055 CHECK_EQ(1234, script->Run()->Int32Value());
6056 CHECK(!try_catch.HasCaught());
6057
6058 // Check that the 'this' pointer points to the global object evaluating
6059 // code.
6060 other->Global()->Set(v8_str("t"), other->Global());
6061 script = Script::Compile(v8_str("other.eval('this == t')"));
6062 result = script->Run();
6063 CHECK(result->IsTrue());
6064 CHECK(!try_catch.HasCaught());
6065
6066 // Check that variables introduced in with-statement are not visible in
6067 // other context.
6068 script =
6069 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
6070 result = script->Run();
6071 CHECK(try_catch.HasCaught());
6072 try_catch.Reset();
6073
6074 // Check that you cannot use 'eval.call' with another object than the
6075 // current global object.
6076 script =
6077 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
6078 result = script->Run();
6079 CHECK(try_catch.HasCaught());
6080}
6081
6082
6083// Test that calling eval in a context which has been detached from
6084// its global throws an exception. This behavior is consistent with
6085// other JavaScript implementations.
6086THREADED_TEST(EvalInDetachedGlobal) {
6087 v8::HandleScope scope;
6088
6089 v8::Persistent<Context> context0 = Context::New();
6090 v8::Persistent<Context> context1 = Context::New();
6091
6092 // Setup function in context0 that uses eval from context0.
6093 context0->Enter();
6094 v8::Handle<v8::Value> fun =
6095 CompileRun("var x = 42;"
6096 "(function() {"
6097 " var e = eval;"
6098 " return function(s) { return e(s); }"
6099 "})()");
6100 context0->Exit();
6101
6102 // Put the function into context1 and call it before and after
6103 // detaching the global. Before detaching, the call succeeds and
6104 // after detaching and exception is thrown.
6105 context1->Enter();
6106 context1->Global()->Set(v8_str("fun"), fun);
6107 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
6108 CHECK_EQ(42, x_value->Int32Value());
6109 context0->DetachGlobal();
6110 v8::TryCatch catcher;
6111 x_value = CompileRun("fun('x')");
6112 CHECK(x_value.IsEmpty());
6113 CHECK(catcher.HasCaught());
6114 context1->Exit();
6115
6116 context1.Dispose();
6117 context0.Dispose();
6118}
6119
6120
6121THREADED_TEST(CrossLazyLoad) {
6122 v8::HandleScope scope;
6123 LocalContext other;
6124 LocalContext current;
6125
6126 Local<String> token = v8_str("<security token>");
6127 other->SetSecurityToken(token);
6128 current->SetSecurityToken(token);
6129
6130 // Setup reference from current to other.
6131 current->Global()->Set(v8_str("other"), other->Global());
6132
6133 // Trigger lazy loading in other context.
6134 Local<Script> script =
6135 Script::Compile(v8_str("other.eval('new Date(42)')"));
6136 Local<Value> value = script->Run();
6137 CHECK_EQ(42.0, value->NumberValue());
6138}
6139
6140
6141static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006142 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006143 if (args.IsConstructCall()) {
6144 if (args[0]->IsInt32()) {
6145 return v8_num(-args[0]->Int32Value());
6146 }
6147 }
6148
6149 return args[0];
6150}
6151
6152
6153// Test that a call handler can be set for objects which will allow
6154// non-function objects created through the API to be called as
6155// functions.
6156THREADED_TEST(CallAsFunction) {
6157 v8::HandleScope scope;
6158 LocalContext context;
6159
6160 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6161 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6162 instance_template->SetCallAsFunctionHandler(call_as_function);
6163 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6164 context->Global()->Set(v8_str("obj"), instance);
6165 v8::TryCatch try_catch;
6166 Local<Value> value;
6167 CHECK(!try_catch.HasCaught());
6168
6169 value = CompileRun("obj(42)");
6170 CHECK(!try_catch.HasCaught());
6171 CHECK_EQ(42, value->Int32Value());
6172
6173 value = CompileRun("(function(o){return o(49)})(obj)");
6174 CHECK(!try_catch.HasCaught());
6175 CHECK_EQ(49, value->Int32Value());
6176
6177 // test special case of call as function
6178 value = CompileRun("[obj]['0'](45)");
6179 CHECK(!try_catch.HasCaught());
6180 CHECK_EQ(45, value->Int32Value());
6181
6182 value = CompileRun("obj.call = Function.prototype.call;"
6183 "obj.call(null, 87)");
6184 CHECK(!try_catch.HasCaught());
6185 CHECK_EQ(87, value->Int32Value());
6186
6187 // Regression tests for bug #1116356: Calling call through call/apply
6188 // must work for non-function receivers.
6189 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6190 value = CompileRun(apply_99);
6191 CHECK(!try_catch.HasCaught());
6192 CHECK_EQ(99, value->Int32Value());
6193
6194 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6195 value = CompileRun(call_17);
6196 CHECK(!try_catch.HasCaught());
6197 CHECK_EQ(17, value->Int32Value());
6198
6199 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006200 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006201 value = CompileRun("new obj(43)");
6202 CHECK(!try_catch.HasCaught());
6203 CHECK_EQ(-43, value->Int32Value());
6204}
6205
6206
6207static int CountHandles() {
6208 return v8::HandleScope::NumberOfHandles();
6209}
6210
6211
6212static int Recurse(int depth, int iterations) {
6213 v8::HandleScope scope;
6214 if (depth == 0) return CountHandles();
6215 for (int i = 0; i < iterations; i++) {
6216 Local<v8::Number> n = v8::Integer::New(42);
6217 }
6218 return Recurse(depth - 1, iterations);
6219}
6220
6221
6222THREADED_TEST(HandleIteration) {
6223 static const int kIterations = 500;
6224 static const int kNesting = 200;
6225 CHECK_EQ(0, CountHandles());
6226 {
6227 v8::HandleScope scope1;
6228 CHECK_EQ(0, CountHandles());
6229 for (int i = 0; i < kIterations; i++) {
6230 Local<v8::Number> n = v8::Integer::New(42);
6231 CHECK_EQ(i + 1, CountHandles());
6232 }
6233
6234 CHECK_EQ(kIterations, CountHandles());
6235 {
6236 v8::HandleScope scope2;
6237 for (int j = 0; j < kIterations; j++) {
6238 Local<v8::Number> n = v8::Integer::New(42);
6239 CHECK_EQ(j + 1 + kIterations, CountHandles());
6240 }
6241 }
6242 CHECK_EQ(kIterations, CountHandles());
6243 }
6244 CHECK_EQ(0, CountHandles());
6245 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6246}
6247
6248
6249static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6250 Local<String> name,
6251 const AccessorInfo& info) {
6252 ApiTestFuzzer::Fuzz();
6253 return v8::Handle<Value>();
6254}
6255
6256
6257THREADED_TEST(InterceptorHasOwnProperty) {
6258 v8::HandleScope scope;
6259 LocalContext context;
6260 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6261 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6262 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6263 Local<Function> function = fun_templ->GetFunction();
6264 context->Global()->Set(v8_str("constructor"), function);
6265 v8::Handle<Value> value = CompileRun(
6266 "var o = new constructor();"
6267 "o.hasOwnProperty('ostehaps');");
6268 CHECK_EQ(false, value->BooleanValue());
6269 value = CompileRun(
6270 "o.ostehaps = 42;"
6271 "o.hasOwnProperty('ostehaps');");
6272 CHECK_EQ(true, value->BooleanValue());
6273 value = CompileRun(
6274 "var p = new constructor();"
6275 "p.hasOwnProperty('ostehaps');");
6276 CHECK_EQ(false, value->BooleanValue());
6277}
6278
6279
6280static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6281 Local<String> name,
6282 const AccessorInfo& info) {
6283 ApiTestFuzzer::Fuzz();
6284 i::Heap::CollectAllGarbage(false);
6285 return v8::Handle<Value>();
6286}
6287
6288
6289THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6290 v8::HandleScope scope;
6291 LocalContext context;
6292 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6293 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6294 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6295 Local<Function> function = fun_templ->GetFunction();
6296 context->Global()->Set(v8_str("constructor"), function);
6297 // Let's first make some stuff so we can be sure to get a good GC.
6298 CompileRun(
6299 "function makestr(size) {"
6300 " switch (size) {"
6301 " case 1: return 'f';"
6302 " case 2: return 'fo';"
6303 " case 3: return 'foo';"
6304 " }"
6305 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6306 "}"
6307 "var x = makestr(12345);"
6308 "x = makestr(31415);"
6309 "x = makestr(23456);");
6310 v8::Handle<Value> value = CompileRun(
6311 "var o = new constructor();"
6312 "o.__proto__ = new String(x);"
6313 "o.hasOwnProperty('ostehaps');");
6314 CHECK_EQ(false, value->BooleanValue());
6315}
6316
6317
6318typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6319 const AccessorInfo& info);
6320
6321
6322static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6323 const char* source,
6324 int expected) {
6325 v8::HandleScope scope;
6326 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08006327 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00006328 LocalContext context;
6329 context->Global()->Set(v8_str("o"), templ->NewInstance());
6330 v8::Handle<Value> value = CompileRun(source);
6331 CHECK_EQ(expected, value->Int32Value());
6332}
6333
6334
6335static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6336 const AccessorInfo& info) {
6337 ApiTestFuzzer::Fuzz();
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08006338 CHECK_EQ(v8_str("data"), info.Data());
6339 CHECK_EQ(v8_str("x"), name);
Steve Blocka7e24c12009-10-30 11:49:00 +00006340 return v8::Integer::New(42);
6341}
6342
6343
6344// This test should hit the load IC for the interceptor case.
6345THREADED_TEST(InterceptorLoadIC) {
6346 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6347 "var result = 0;"
6348 "for (var i = 0; i < 1000; i++) {"
6349 " result = o.x;"
6350 "}",
6351 42);
6352}
6353
6354
6355// Below go several tests which verify that JITing for various
6356// configurations of interceptor and explicit fields works fine
6357// (those cases are special cased to get better performance).
6358
6359static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6360 const AccessorInfo& info) {
6361 ApiTestFuzzer::Fuzz();
6362 return v8_str("x")->Equals(name)
6363 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6364}
6365
6366
6367THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6368 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6369 "var result = 0;"
6370 "o.y = 239;"
6371 "for (var i = 0; i < 1000; i++) {"
6372 " result = o.y;"
6373 "}",
6374 239);
6375}
6376
6377
6378THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6379 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6380 "var result = 0;"
6381 "o.__proto__ = { 'y': 239 };"
6382 "for (var i = 0; i < 1000; i++) {"
6383 " result = o.y + o.x;"
6384 "}",
6385 239 + 42);
6386}
6387
6388
6389THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6390 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6391 "var result = 0;"
6392 "o.__proto__.y = 239;"
6393 "for (var i = 0; i < 1000; i++) {"
6394 " result = o.y + o.x;"
6395 "}",
6396 239 + 42);
6397}
6398
6399
6400THREADED_TEST(InterceptorLoadICUndefined) {
6401 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6402 "var result = 0;"
6403 "for (var i = 0; i < 1000; i++) {"
6404 " result = (o.y == undefined) ? 239 : 42;"
6405 "}",
6406 239);
6407}
6408
6409
6410THREADED_TEST(InterceptorLoadICWithOverride) {
6411 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6412 "fst = new Object(); fst.__proto__ = o;"
6413 "snd = new Object(); snd.__proto__ = fst;"
6414 "var result1 = 0;"
6415 "for (var i = 0; i < 1000; i++) {"
6416 " result1 = snd.x;"
6417 "}"
6418 "fst.x = 239;"
6419 "var result = 0;"
6420 "for (var i = 0; i < 1000; i++) {"
6421 " result = snd.x;"
6422 "}"
6423 "result + result1",
6424 239 + 42);
6425}
6426
6427
6428// Test the case when we stored field into
6429// a stub, but interceptor produced value on its own.
6430THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6431 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6432 "proto = new Object();"
6433 "o.__proto__ = proto;"
6434 "proto.x = 239;"
6435 "for (var i = 0; i < 1000; i++) {"
6436 " o.x;"
6437 // Now it should be ICed and keep a reference to x defined on proto
6438 "}"
6439 "var result = 0;"
6440 "for (var i = 0; i < 1000; i++) {"
6441 " result += o.x;"
6442 "}"
6443 "result;",
6444 42 * 1000);
6445}
6446
6447
6448// Test the case when we stored field into
6449// a stub, but it got invalidated later on.
6450THREADED_TEST(InterceptorLoadICInvalidatedField) {
6451 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6452 "proto1 = new Object();"
6453 "proto2 = new Object();"
6454 "o.__proto__ = proto1;"
6455 "proto1.__proto__ = proto2;"
6456 "proto2.y = 239;"
6457 "for (var i = 0; i < 1000; i++) {"
6458 " o.y;"
6459 // Now it should be ICed and keep a reference to y defined on proto2
6460 "}"
6461 "proto1.y = 42;"
6462 "var result = 0;"
6463 "for (var i = 0; i < 1000; i++) {"
6464 " result += o.y;"
6465 "}"
6466 "result;",
6467 42 * 1000);
6468}
6469
6470
Steve Block6ded16b2010-05-10 14:33:55 +01006471static int interceptor_load_not_handled_calls = 0;
6472static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6473 const AccessorInfo& info) {
6474 ++interceptor_load_not_handled_calls;
6475 return v8::Handle<v8::Value>();
6476}
6477
6478
6479// Test how post-interceptor lookups are done in the non-cacheable
6480// case: the interceptor should not be invoked during this lookup.
6481THREADED_TEST(InterceptorLoadICPostInterceptor) {
6482 interceptor_load_not_handled_calls = 0;
6483 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6484 "receiver = new Object();"
6485 "receiver.__proto__ = o;"
6486 "proto = new Object();"
6487 "/* Make proto a slow-case object. */"
6488 "for (var i = 0; i < 1000; i++) {"
6489 " proto[\"xxxxxxxx\" + i] = [];"
6490 "}"
6491 "proto.x = 17;"
6492 "o.__proto__ = proto;"
6493 "var result = 0;"
6494 "for (var i = 0; i < 1000; i++) {"
6495 " result += receiver.x;"
6496 "}"
6497 "result;",
6498 17 * 1000);
6499 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6500}
6501
6502
Steve Blocka7e24c12009-10-30 11:49:00 +00006503// Test the case when we stored field into
6504// a stub, but it got invalidated later on due to override on
6505// global object which is between interceptor and fields' holders.
6506THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6507 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6508 "o.__proto__ = this;" // set a global to be a proto of o.
6509 "this.__proto__.y = 239;"
6510 "for (var i = 0; i < 10; i++) {"
6511 " if (o.y != 239) throw 'oops: ' + o.y;"
6512 // Now it should be ICed and keep a reference to y defined on field_holder.
6513 "}"
6514 "this.y = 42;" // Assign on a global.
6515 "var result = 0;"
6516 "for (var i = 0; i < 10; i++) {"
6517 " result += o.y;"
6518 "}"
6519 "result;",
6520 42 * 10);
6521}
6522
6523
Steve Blocka7e24c12009-10-30 11:49:00 +00006524static void SetOnThis(Local<String> name,
6525 Local<Value> value,
6526 const AccessorInfo& info) {
6527 info.This()->ForceSet(name, value);
6528}
6529
6530
6531THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6532 v8::HandleScope scope;
6533 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6534 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6535 templ->SetAccessor(v8_str("y"), Return239);
6536 LocalContext context;
6537 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006538
6539 // Check the case when receiver and interceptor's holder
6540 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006541 v8::Handle<Value> value = CompileRun(
6542 "var result = 0;"
6543 "for (var i = 0; i < 7; i++) {"
6544 " result = o.y;"
6545 "}");
6546 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006547
6548 // Check the case when interceptor's holder is in proto chain
6549 // of receiver.
6550 value = CompileRun(
6551 "r = { __proto__: o };"
6552 "var result = 0;"
6553 "for (var i = 0; i < 7; i++) {"
6554 " result = r.y;"
6555 "}");
6556 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006557}
6558
6559
6560THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6561 v8::HandleScope scope;
6562 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6563 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6564 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6565 templ_p->SetAccessor(v8_str("y"), Return239);
6566
6567 LocalContext context;
6568 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6569 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6570
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006571 // Check the case when receiver and interceptor's holder
6572 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006573 v8::Handle<Value> value = CompileRun(
6574 "o.__proto__ = p;"
6575 "var result = 0;"
6576 "for (var i = 0; i < 7; i++) {"
6577 " result = o.x + o.y;"
6578 "}");
6579 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006580
6581 // Check the case when interceptor's holder is in proto chain
6582 // of receiver.
6583 value = CompileRun(
6584 "r = { __proto__: o };"
6585 "var result = 0;"
6586 "for (var i = 0; i < 7; i++) {"
6587 " result = r.x + r.y;"
6588 "}");
6589 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006590}
6591
6592
6593THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6594 v8::HandleScope scope;
6595 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6596 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6597 templ->SetAccessor(v8_str("y"), Return239);
6598
6599 LocalContext context;
6600 context->Global()->Set(v8_str("o"), templ->NewInstance());
6601
6602 v8::Handle<Value> value = CompileRun(
6603 "fst = new Object(); fst.__proto__ = o;"
6604 "snd = new Object(); snd.__proto__ = fst;"
6605 "var result1 = 0;"
6606 "for (var i = 0; i < 7; i++) {"
6607 " result1 = snd.x;"
6608 "}"
6609 "fst.x = 239;"
6610 "var result = 0;"
6611 "for (var i = 0; i < 7; i++) {"
6612 " result = snd.x;"
6613 "}"
6614 "result + result1");
6615 CHECK_EQ(239 + 42, value->Int32Value());
6616}
6617
6618
6619// Test the case when we stored callback into
6620// a stub, but interceptor produced value on its own.
6621THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6622 v8::HandleScope scope;
6623 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6624 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6625 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6626 templ_p->SetAccessor(v8_str("y"), Return239);
6627
6628 LocalContext context;
6629 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6630 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6631
6632 v8::Handle<Value> value = CompileRun(
6633 "o.__proto__ = p;"
6634 "for (var i = 0; i < 7; i++) {"
6635 " o.x;"
6636 // Now it should be ICed and keep a reference to x defined on p
6637 "}"
6638 "var result = 0;"
6639 "for (var i = 0; i < 7; i++) {"
6640 " result += o.x;"
6641 "}"
6642 "result");
6643 CHECK_EQ(42 * 7, value->Int32Value());
6644}
6645
6646
6647// Test the case when we stored callback into
6648// a stub, but it got invalidated later on.
6649THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6650 v8::HandleScope scope;
6651 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6652 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6653 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6654 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6655
6656 LocalContext context;
6657 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6658 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6659
6660 v8::Handle<Value> value = CompileRun(
6661 "inbetween = new Object();"
6662 "o.__proto__ = inbetween;"
6663 "inbetween.__proto__ = p;"
6664 "for (var i = 0; i < 10; i++) {"
6665 " o.y;"
6666 // Now it should be ICed and keep a reference to y defined on p
6667 "}"
6668 "inbetween.y = 42;"
6669 "var result = 0;"
6670 "for (var i = 0; i < 10; i++) {"
6671 " result += o.y;"
6672 "}"
6673 "result");
6674 CHECK_EQ(42 * 10, value->Int32Value());
6675}
6676
6677
6678// Test the case when we stored callback into
6679// a stub, but it got invalidated later on due to override on
6680// global object which is between interceptor and callbacks' holders.
6681THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6682 v8::HandleScope scope;
6683 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6684 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6685 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6686 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6687
6688 LocalContext context;
6689 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6690 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6691
6692 v8::Handle<Value> value = CompileRun(
6693 "o.__proto__ = this;"
6694 "this.__proto__ = p;"
6695 "for (var i = 0; i < 10; i++) {"
6696 " if (o.y != 239) throw 'oops: ' + o.y;"
6697 // Now it should be ICed and keep a reference to y defined on p
6698 "}"
6699 "this.y = 42;"
6700 "var result = 0;"
6701 "for (var i = 0; i < 10; i++) {"
6702 " result += o.y;"
6703 "}"
6704 "result");
6705 CHECK_EQ(42 * 10, value->Int32Value());
6706}
6707
6708
6709static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6710 const AccessorInfo& info) {
6711 ApiTestFuzzer::Fuzz();
6712 CHECK(v8_str("x")->Equals(name));
6713 return v8::Integer::New(0);
6714}
6715
6716
6717THREADED_TEST(InterceptorReturningZero) {
6718 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6719 "o.x == undefined ? 1 : 0",
6720 0);
6721}
6722
6723
6724static v8::Handle<Value> InterceptorStoreICSetter(
6725 Local<String> key, Local<Value> value, const AccessorInfo&) {
6726 CHECK(v8_str("x")->Equals(key));
6727 CHECK_EQ(42, value->Int32Value());
6728 return value;
6729}
6730
6731
6732// This test should hit the store IC for the interceptor case.
6733THREADED_TEST(InterceptorStoreIC) {
6734 v8::HandleScope scope;
6735 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6736 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08006737 InterceptorStoreICSetter,
6738 0, 0, 0, v8_str("data"));
Steve Blocka7e24c12009-10-30 11:49:00 +00006739 LocalContext context;
6740 context->Global()->Set(v8_str("o"), templ->NewInstance());
6741 v8::Handle<Value> value = CompileRun(
6742 "for (var i = 0; i < 1000; i++) {"
6743 " o.x = 42;"
6744 "}");
6745}
6746
6747
6748THREADED_TEST(InterceptorStoreICWithNoSetter) {
6749 v8::HandleScope scope;
6750 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6751 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6752 LocalContext context;
6753 context->Global()->Set(v8_str("o"), templ->NewInstance());
6754 v8::Handle<Value> value = CompileRun(
6755 "for (var i = 0; i < 1000; i++) {"
6756 " o.y = 239;"
6757 "}"
6758 "42 + o.y");
6759 CHECK_EQ(239 + 42, value->Int32Value());
6760}
6761
6762
6763
6764
6765v8::Handle<Value> call_ic_function;
6766v8::Handle<Value> call_ic_function2;
6767v8::Handle<Value> call_ic_function3;
6768
6769static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6770 const AccessorInfo& info) {
6771 ApiTestFuzzer::Fuzz();
6772 CHECK(v8_str("x")->Equals(name));
6773 return call_ic_function;
6774}
6775
6776
6777// This test should hit the call IC for the interceptor case.
6778THREADED_TEST(InterceptorCallIC) {
6779 v8::HandleScope scope;
6780 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6781 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6782 LocalContext context;
6783 context->Global()->Set(v8_str("o"), templ->NewInstance());
6784 call_ic_function =
6785 v8_compile("function f(x) { return x + 1; }; f")->Run();
6786 v8::Handle<Value> value = CompileRun(
6787 "var result = 0;"
6788 "for (var i = 0; i < 1000; i++) {"
6789 " result = o.x(41);"
6790 "}");
6791 CHECK_EQ(42, value->Int32Value());
6792}
6793
6794
6795// This test checks that if interceptor doesn't provide
6796// a value, we can fetch regular value.
6797THREADED_TEST(InterceptorCallICSeesOthers) {
6798 v8::HandleScope scope;
6799 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6800 templ->SetNamedPropertyHandler(NoBlockGetterX);
6801 LocalContext context;
6802 context->Global()->Set(v8_str("o"), templ->NewInstance());
6803 v8::Handle<Value> value = CompileRun(
6804 "o.x = function f(x) { return x + 1; };"
6805 "var result = 0;"
6806 "for (var i = 0; i < 7; i++) {"
6807 " result = o.x(41);"
6808 "}");
6809 CHECK_EQ(42, value->Int32Value());
6810}
6811
6812
6813static v8::Handle<Value> call_ic_function4;
6814static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6815 const AccessorInfo& info) {
6816 ApiTestFuzzer::Fuzz();
6817 CHECK(v8_str("x")->Equals(name));
6818 return call_ic_function4;
6819}
6820
6821
6822// This test checks that if interceptor provides a function,
6823// even if we cached shadowed variant, interceptor's function
6824// is invoked
6825THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6826 v8::HandleScope scope;
6827 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6828 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6829 LocalContext context;
6830 context->Global()->Set(v8_str("o"), templ->NewInstance());
6831 call_ic_function4 =
6832 v8_compile("function f(x) { return x - 1; }; f")->Run();
6833 v8::Handle<Value> value = CompileRun(
6834 "o.__proto__.x = function(x) { return x + 1; };"
6835 "var result = 0;"
6836 "for (var i = 0; i < 1000; i++) {"
6837 " result = o.x(42);"
6838 "}");
6839 CHECK_EQ(41, value->Int32Value());
6840}
6841
6842
6843// Test the case when we stored cacheable lookup into
6844// a stub, but it got invalidated later on
6845THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6846 v8::HandleScope scope;
6847 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6848 templ->SetNamedPropertyHandler(NoBlockGetterX);
6849 LocalContext context;
6850 context->Global()->Set(v8_str("o"), templ->NewInstance());
6851 v8::Handle<Value> value = CompileRun(
6852 "proto1 = new Object();"
6853 "proto2 = new Object();"
6854 "o.__proto__ = proto1;"
6855 "proto1.__proto__ = proto2;"
6856 "proto2.y = function(x) { return x + 1; };"
6857 // Invoke it many times to compile a stub
6858 "for (var i = 0; i < 7; i++) {"
6859 " o.y(42);"
6860 "}"
6861 "proto1.y = function(x) { return x - 1; };"
6862 "var result = 0;"
6863 "for (var i = 0; i < 7; i++) {"
6864 " result += o.y(42);"
6865 "}");
6866 CHECK_EQ(41 * 7, value->Int32Value());
6867}
6868
6869
6870static v8::Handle<Value> call_ic_function5;
6871static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6872 const AccessorInfo& info) {
6873 ApiTestFuzzer::Fuzz();
6874 if (v8_str("x")->Equals(name))
6875 return call_ic_function5;
6876 else
6877 return Local<Value>();
6878}
6879
6880
6881// This test checks that if interceptor doesn't provide a function,
6882// cached constant function is used
6883THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6884 v8::HandleScope scope;
6885 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6886 templ->SetNamedPropertyHandler(NoBlockGetterX);
6887 LocalContext context;
6888 context->Global()->Set(v8_str("o"), templ->NewInstance());
6889 v8::Handle<Value> value = CompileRun(
6890 "function inc(x) { return x + 1; };"
6891 "inc(1);"
6892 "o.x = inc;"
6893 "var result = 0;"
6894 "for (var i = 0; i < 1000; i++) {"
6895 " result = o.x(42);"
6896 "}");
6897 CHECK_EQ(43, value->Int32Value());
6898}
6899
6900
6901// This test checks that if interceptor provides a function,
6902// even if we cached constant function, interceptor's function
6903// is invoked
6904THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6905 v8::HandleScope scope;
6906 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6907 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6908 LocalContext context;
6909 context->Global()->Set(v8_str("o"), templ->NewInstance());
6910 call_ic_function5 =
6911 v8_compile("function f(x) { return x - 1; }; f")->Run();
6912 v8::Handle<Value> value = CompileRun(
6913 "function inc(x) { return x + 1; };"
6914 "inc(1);"
6915 "o.x = inc;"
6916 "var result = 0;"
6917 "for (var i = 0; i < 1000; i++) {"
6918 " result = o.x(42);"
6919 "}");
6920 CHECK_EQ(41, value->Int32Value());
6921}
6922
6923
6924// Test the case when we stored constant function into
6925// a stub, but it got invalidated later on
6926THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6927 v8::HandleScope scope;
6928 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6929 templ->SetNamedPropertyHandler(NoBlockGetterX);
6930 LocalContext context;
6931 context->Global()->Set(v8_str("o"), templ->NewInstance());
6932 v8::Handle<Value> value = CompileRun(
6933 "function inc(x) { return x + 1; };"
6934 "inc(1);"
6935 "proto1 = new Object();"
6936 "proto2 = new Object();"
6937 "o.__proto__ = proto1;"
6938 "proto1.__proto__ = proto2;"
6939 "proto2.y = inc;"
6940 // Invoke it many times to compile a stub
6941 "for (var i = 0; i < 7; i++) {"
6942 " o.y(42);"
6943 "}"
6944 "proto1.y = function(x) { return x - 1; };"
6945 "var result = 0;"
6946 "for (var i = 0; i < 7; i++) {"
6947 " result += o.y(42);"
6948 "}");
6949 CHECK_EQ(41 * 7, value->Int32Value());
6950}
6951
6952
6953// Test the case when we stored constant function into
6954// a stub, but it got invalidated later on due to override on
6955// global object which is between interceptor and constant function' holders.
6956THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6957 v8::HandleScope scope;
6958 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6959 templ->SetNamedPropertyHandler(NoBlockGetterX);
6960 LocalContext context;
6961 context->Global()->Set(v8_str("o"), templ->NewInstance());
6962 v8::Handle<Value> value = CompileRun(
6963 "function inc(x) { return x + 1; };"
6964 "inc(1);"
6965 "o.__proto__ = this;"
6966 "this.__proto__.y = inc;"
6967 // Invoke it many times to compile a stub
6968 "for (var i = 0; i < 7; i++) {"
6969 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6970 "}"
6971 "this.y = function(x) { return x - 1; };"
6972 "var result = 0;"
6973 "for (var i = 0; i < 7; i++) {"
6974 " result += o.y(42);"
6975 "}");
6976 CHECK_EQ(41 * 7, value->Int32Value());
6977}
6978
6979
Leon Clarke4515c472010-02-03 11:58:03 +00006980// Test the case when actual function to call sits on global object.
6981THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6982 v8::HandleScope scope;
6983 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6984 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6985
6986 LocalContext context;
6987 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6988
6989 v8::Handle<Value> value = CompileRun(
6990 "try {"
6991 " o.__proto__ = this;"
6992 " for (var i = 0; i < 10; i++) {"
6993 " var v = o.parseFloat('239');"
6994 " if (v != 239) throw v;"
6995 // Now it should be ICed and keep a reference to parseFloat.
6996 " }"
6997 " var result = 0;"
6998 " for (var i = 0; i < 10; i++) {"
6999 " result += o.parseFloat('239');"
7000 " }"
7001 " result"
7002 "} catch(e) {"
7003 " e"
7004 "};");
7005 CHECK_EQ(239 * 10, value->Int32Value());
7006}
7007
Andrei Popescu402d9372010-02-26 13:31:12 +00007008static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
7009 const AccessorInfo& info) {
7010 ApiTestFuzzer::Fuzz();
7011 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
7012 ++(*call_count);
7013 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01007014 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00007015 }
7016 return v8::Handle<Value>();
7017}
7018
7019static v8::Handle<Value> FastApiCallback_TrivialSignature(
7020 const v8::Arguments& args) {
7021 ApiTestFuzzer::Fuzz();
7022 CHECK_EQ(args.This(), args.Holder());
7023 CHECK(args.Data()->Equals(v8_str("method_data")));
7024 return v8::Integer::New(args[0]->Int32Value() + 1);
7025}
7026
7027static v8::Handle<Value> FastApiCallback_SimpleSignature(
7028 const v8::Arguments& args) {
7029 ApiTestFuzzer::Fuzz();
7030 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
7031 CHECK(args.Data()->Equals(v8_str("method_data")));
7032 // Note, we're using HasRealNamedProperty instead of Has to avoid
7033 // invoking the interceptor again.
7034 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
7035 return v8::Integer::New(args[0]->Int32Value() + 1);
7036}
7037
7038// Helper to maximize the odds of object moving.
7039static void GenerateSomeGarbage() {
7040 CompileRun(
7041 "var garbage;"
7042 "for (var i = 0; i < 1000; i++) {"
7043 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
7044 "}"
7045 "garbage = undefined;");
7046}
7047
7048THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
7049 int interceptor_call_count = 0;
7050 v8::HandleScope scope;
7051 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7052 v8::Handle<v8::FunctionTemplate> method_templ =
7053 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7054 v8_str("method_data"),
7055 v8::Handle<v8::Signature>());
7056 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7057 proto_templ->Set(v8_str("method"), method_templ);
7058 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7059 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7060 NULL, NULL, NULL, NULL,
7061 v8::External::Wrap(&interceptor_call_count));
7062 LocalContext context;
7063 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7064 GenerateSomeGarbage();
7065 context->Global()->Set(v8_str("o"), fun->NewInstance());
7066 v8::Handle<Value> value = CompileRun(
7067 "var result = 0;"
7068 "for (var i = 0; i < 100; i++) {"
7069 " result = o.method(41);"
7070 "}");
7071 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7072 CHECK_EQ(100, interceptor_call_count);
7073}
7074
7075THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
7076 int interceptor_call_count = 0;
7077 v8::HandleScope scope;
7078 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7079 v8::Handle<v8::FunctionTemplate> method_templ =
7080 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7081 v8_str("method_data"),
7082 v8::Signature::New(fun_templ));
7083 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7084 proto_templ->Set(v8_str("method"), method_templ);
7085 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7086 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7087 NULL, NULL, NULL, NULL,
7088 v8::External::Wrap(&interceptor_call_count));
7089 LocalContext context;
7090 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7091 GenerateSomeGarbage();
7092 context->Global()->Set(v8_str("o"), fun->NewInstance());
7093 v8::Handle<Value> value = CompileRun(
7094 "o.foo = 17;"
7095 "var receiver = {};"
7096 "receiver.__proto__ = o;"
7097 "var result = 0;"
7098 "for (var i = 0; i < 100; i++) {"
7099 " result = receiver.method(41);"
7100 "}");
7101 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7102 CHECK_EQ(100, interceptor_call_count);
7103}
7104
7105THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
7106 int interceptor_call_count = 0;
7107 v8::HandleScope scope;
7108 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7109 v8::Handle<v8::FunctionTemplate> method_templ =
7110 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7111 v8_str("method_data"),
7112 v8::Signature::New(fun_templ));
7113 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7114 proto_templ->Set(v8_str("method"), method_templ);
7115 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7116 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7117 NULL, NULL, NULL, NULL,
7118 v8::External::Wrap(&interceptor_call_count));
7119 LocalContext context;
7120 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7121 GenerateSomeGarbage();
7122 context->Global()->Set(v8_str("o"), fun->NewInstance());
7123 v8::Handle<Value> value = CompileRun(
7124 "o.foo = 17;"
7125 "var receiver = {};"
7126 "receiver.__proto__ = o;"
7127 "var result = 0;"
7128 "var saved_result = 0;"
7129 "for (var i = 0; i < 100; i++) {"
7130 " result = receiver.method(41);"
7131 " if (i == 50) {"
7132 " saved_result = result;"
7133 " receiver = {method: function(x) { return x - 1 }};"
7134 " }"
7135 "}");
7136 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7137 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7138 CHECK_GE(interceptor_call_count, 50);
7139}
7140
7141THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
7142 int interceptor_call_count = 0;
7143 v8::HandleScope scope;
7144 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7145 v8::Handle<v8::FunctionTemplate> method_templ =
7146 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7147 v8_str("method_data"),
7148 v8::Signature::New(fun_templ));
7149 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7150 proto_templ->Set(v8_str("method"), method_templ);
7151 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7152 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7153 NULL, NULL, NULL, NULL,
7154 v8::External::Wrap(&interceptor_call_count));
7155 LocalContext context;
7156 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7157 GenerateSomeGarbage();
7158 context->Global()->Set(v8_str("o"), fun->NewInstance());
7159 v8::Handle<Value> value = CompileRun(
7160 "o.foo = 17;"
7161 "var receiver = {};"
7162 "receiver.__proto__ = o;"
7163 "var result = 0;"
7164 "var saved_result = 0;"
7165 "for (var i = 0; i < 100; i++) {"
7166 " result = receiver.method(41);"
7167 " if (i == 50) {"
7168 " saved_result = result;"
7169 " o.method = function(x) { return x - 1 };"
7170 " }"
7171 "}");
7172 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7173 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7174 CHECK_GE(interceptor_call_count, 50);
7175}
7176
Steve Block6ded16b2010-05-10 14:33:55 +01007177THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7178 int interceptor_call_count = 0;
7179 v8::HandleScope scope;
7180 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7181 v8::Handle<v8::FunctionTemplate> method_templ =
7182 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7183 v8_str("method_data"),
7184 v8::Signature::New(fun_templ));
7185 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7186 proto_templ->Set(v8_str("method"), method_templ);
7187 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7188 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7189 NULL, NULL, NULL, NULL,
7190 v8::External::Wrap(&interceptor_call_count));
7191 LocalContext context;
7192 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7193 GenerateSomeGarbage();
7194 context->Global()->Set(v8_str("o"), fun->NewInstance());
7195 v8::TryCatch try_catch;
7196 v8::Handle<Value> value = CompileRun(
7197 "o.foo = 17;"
7198 "var receiver = {};"
7199 "receiver.__proto__ = o;"
7200 "var result = 0;"
7201 "var saved_result = 0;"
7202 "for (var i = 0; i < 100; i++) {"
7203 " result = receiver.method(41);"
7204 " if (i == 50) {"
7205 " saved_result = result;"
7206 " receiver = 333;"
7207 " }"
7208 "}");
7209 CHECK(try_catch.HasCaught());
7210 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7211 try_catch.Exception()->ToString());
7212 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7213 CHECK_GE(interceptor_call_count, 50);
7214}
7215
Andrei Popescu402d9372010-02-26 13:31:12 +00007216THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
7217 int interceptor_call_count = 0;
7218 v8::HandleScope scope;
7219 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7220 v8::Handle<v8::FunctionTemplate> method_templ =
7221 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7222 v8_str("method_data"),
7223 v8::Signature::New(fun_templ));
7224 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7225 proto_templ->Set(v8_str("method"), method_templ);
7226 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7227 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7228 NULL, NULL, NULL, NULL,
7229 v8::External::Wrap(&interceptor_call_count));
7230 LocalContext context;
7231 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7232 GenerateSomeGarbage();
7233 context->Global()->Set(v8_str("o"), fun->NewInstance());
7234 v8::TryCatch try_catch;
7235 v8::Handle<Value> value = CompileRun(
7236 "o.foo = 17;"
7237 "var receiver = {};"
7238 "receiver.__proto__ = o;"
7239 "var result = 0;"
7240 "var saved_result = 0;"
7241 "for (var i = 0; i < 100; i++) {"
7242 " result = receiver.method(41);"
7243 " if (i == 50) {"
7244 " saved_result = result;"
7245 " receiver = {method: receiver.method};"
7246 " }"
7247 "}");
7248 CHECK(try_catch.HasCaught());
7249 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7250 try_catch.Exception()->ToString());
7251 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7252 CHECK_GE(interceptor_call_count, 50);
7253}
7254
7255THREADED_TEST(CallICFastApi_TrivialSignature) {
7256 v8::HandleScope scope;
7257 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7258 v8::Handle<v8::FunctionTemplate> method_templ =
7259 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7260 v8_str("method_data"),
7261 v8::Handle<v8::Signature>());
7262 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7263 proto_templ->Set(v8_str("method"), method_templ);
7264 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7265 LocalContext context;
7266 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7267 GenerateSomeGarbage();
7268 context->Global()->Set(v8_str("o"), fun->NewInstance());
7269 v8::Handle<Value> value = CompileRun(
7270 "var result = 0;"
7271 "for (var i = 0; i < 100; i++) {"
7272 " result = o.method(41);"
7273 "}");
7274
7275 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7276}
7277
7278THREADED_TEST(CallICFastApi_SimpleSignature) {
7279 v8::HandleScope scope;
7280 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7281 v8::Handle<v8::FunctionTemplate> method_templ =
7282 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7283 v8_str("method_data"),
7284 v8::Signature::New(fun_templ));
7285 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7286 proto_templ->Set(v8_str("method"), method_templ);
7287 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7288 LocalContext context;
7289 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7290 GenerateSomeGarbage();
7291 context->Global()->Set(v8_str("o"), fun->NewInstance());
7292 v8::Handle<Value> value = CompileRun(
7293 "o.foo = 17;"
7294 "var receiver = {};"
7295 "receiver.__proto__ = o;"
7296 "var result = 0;"
7297 "for (var i = 0; i < 100; i++) {"
7298 " result = receiver.method(41);"
7299 "}");
7300
7301 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7302}
7303
Steve Block6ded16b2010-05-10 14:33:55 +01007304THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007305 v8::HandleScope scope;
7306 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7307 v8::Handle<v8::FunctionTemplate> method_templ =
7308 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7309 v8_str("method_data"),
7310 v8::Signature::New(fun_templ));
7311 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7312 proto_templ->Set(v8_str("method"), method_templ);
7313 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7314 LocalContext context;
7315 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7316 GenerateSomeGarbage();
7317 context->Global()->Set(v8_str("o"), fun->NewInstance());
7318 v8::Handle<Value> value = CompileRun(
7319 "o.foo = 17;"
7320 "var receiver = {};"
7321 "receiver.__proto__ = o;"
7322 "var result = 0;"
7323 "var saved_result = 0;"
7324 "for (var i = 0; i < 100; i++) {"
7325 " result = receiver.method(41);"
7326 " if (i == 50) {"
7327 " saved_result = result;"
7328 " receiver = {method: function(x) { return x - 1 }};"
7329 " }"
7330 "}");
7331 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7332 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7333}
7334
Steve Block6ded16b2010-05-10 14:33:55 +01007335THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7336 v8::HandleScope scope;
7337 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7338 v8::Handle<v8::FunctionTemplate> method_templ =
7339 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7340 v8_str("method_data"),
7341 v8::Signature::New(fun_templ));
7342 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7343 proto_templ->Set(v8_str("method"), method_templ);
7344 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7345 LocalContext context;
7346 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7347 GenerateSomeGarbage();
7348 context->Global()->Set(v8_str("o"), fun->NewInstance());
7349 v8::TryCatch try_catch;
7350 v8::Handle<Value> value = CompileRun(
7351 "o.foo = 17;"
7352 "var receiver = {};"
7353 "receiver.__proto__ = o;"
7354 "var result = 0;"
7355 "var saved_result = 0;"
7356 "for (var i = 0; i < 100; i++) {"
7357 " result = receiver.method(41);"
7358 " if (i == 50) {"
7359 " saved_result = result;"
7360 " receiver = 333;"
7361 " }"
7362 "}");
7363 CHECK(try_catch.HasCaught());
7364 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7365 try_catch.Exception()->ToString());
7366 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7367}
7368
Leon Clarke4515c472010-02-03 11:58:03 +00007369
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007370v8::Handle<Value> keyed_call_ic_function;
7371
7372static v8::Handle<Value> InterceptorKeyedCallICGetter(
7373 Local<String> name, const AccessorInfo& info) {
7374 ApiTestFuzzer::Fuzz();
7375 if (v8_str("x")->Equals(name)) {
7376 return keyed_call_ic_function;
7377 }
7378 return v8::Handle<Value>();
7379}
7380
7381
7382// Test the case when we stored cacheable lookup into
7383// a stub, but the function name changed (to another cacheable function).
7384THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7385 v8::HandleScope scope;
7386 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7387 templ->SetNamedPropertyHandler(NoBlockGetterX);
7388 LocalContext context;
7389 context->Global()->Set(v8_str("o"), templ->NewInstance());
7390 v8::Handle<Value> value = CompileRun(
7391 "proto = new Object();"
7392 "proto.y = function(x) { return x + 1; };"
7393 "proto.z = function(x) { return x - 1; };"
7394 "o.__proto__ = proto;"
7395 "var result = 0;"
7396 "var method = 'y';"
7397 "for (var i = 0; i < 10; i++) {"
7398 " if (i == 5) { method = 'z'; };"
7399 " result += o[method](41);"
7400 "}");
7401 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7402}
7403
7404
7405// Test the case when we stored cacheable lookup into
7406// a stub, but the function name changed (and the new function is present
7407// both before and after the interceptor in the prototype chain).
7408THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7409 v8::HandleScope scope;
7410 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7411 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7412 LocalContext context;
7413 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7414 keyed_call_ic_function =
7415 v8_compile("function f(x) { return x - 1; }; f")->Run();
7416 v8::Handle<Value> value = CompileRun(
7417 "o = new Object();"
7418 "proto2 = new Object();"
7419 "o.y = function(x) { return x + 1; };"
7420 "proto2.y = function(x) { return x + 2; };"
7421 "o.__proto__ = proto1;"
7422 "proto1.__proto__ = proto2;"
7423 "var result = 0;"
7424 "var method = 'x';"
7425 "for (var i = 0; i < 10; i++) {"
7426 " if (i == 5) { method = 'y'; };"
7427 " result += o[method](41);"
7428 "}");
7429 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7430}
7431
7432
7433// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7434// on the global object.
7435THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7436 v8::HandleScope scope;
7437 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7438 templ->SetNamedPropertyHandler(NoBlockGetterX);
7439 LocalContext context;
7440 context->Global()->Set(v8_str("o"), templ->NewInstance());
7441 v8::Handle<Value> value = CompileRun(
7442 "function inc(x) { return x + 1; };"
7443 "inc(1);"
7444 "function dec(x) { return x - 1; };"
7445 "dec(1);"
7446 "o.__proto__ = this;"
7447 "this.__proto__.x = inc;"
7448 "this.__proto__.y = dec;"
7449 "var result = 0;"
7450 "var method = 'x';"
7451 "for (var i = 0; i < 10; i++) {"
7452 " if (i == 5) { method = 'y'; };"
7453 " result += o[method](41);"
7454 "}");
7455 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7456}
7457
7458
7459// Test the case when actual function to call sits on global object.
7460THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7461 v8::HandleScope scope;
7462 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7463 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7464 LocalContext context;
7465 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7466
7467 v8::Handle<Value> value = CompileRun(
7468 "function len(x) { return x.length; };"
7469 "o.__proto__ = this;"
7470 "var m = 'parseFloat';"
7471 "var result = 0;"
7472 "for (var i = 0; i < 10; i++) {"
7473 " if (i == 5) {"
7474 " m = 'len';"
7475 " saved_result = result;"
7476 " };"
7477 " result = o[m]('239');"
7478 "}");
7479 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7480 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7481}
7482
7483// Test the map transition before the interceptor.
7484THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7485 v8::HandleScope scope;
7486 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7487 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7488 LocalContext context;
7489 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7490
7491 v8::Handle<Value> value = CompileRun(
7492 "var o = new Object();"
7493 "o.__proto__ = proto;"
7494 "o.method = function(x) { return x + 1; };"
7495 "var m = 'method';"
7496 "var result = 0;"
7497 "for (var i = 0; i < 10; i++) {"
7498 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7499 " result += o[m](41);"
7500 "}");
7501 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7502}
7503
7504
7505// Test the map transition after the interceptor.
7506THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7507 v8::HandleScope scope;
7508 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7509 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7510 LocalContext context;
7511 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7512
7513 v8::Handle<Value> value = CompileRun(
7514 "var proto = new Object();"
7515 "o.__proto__ = proto;"
7516 "proto.method = function(x) { return x + 1; };"
7517 "var m = 'method';"
7518 "var result = 0;"
7519 "for (var i = 0; i < 10; i++) {"
7520 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7521 " result += o[m](41);"
7522 "}");
7523 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7524}
7525
7526
Steve Blocka7e24c12009-10-30 11:49:00 +00007527static int interceptor_call_count = 0;
7528
7529static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7530 const AccessorInfo& info) {
7531 ApiTestFuzzer::Fuzz();
7532 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7533 return call_ic_function2;
7534 }
7535 return v8::Handle<Value>();
7536}
7537
7538
7539// This test should hit load and call ICs for the interceptor case.
7540// Once in a while, the interceptor will reply that a property was not
7541// found in which case we should get a reference error.
7542THREADED_TEST(InterceptorICReferenceErrors) {
7543 v8::HandleScope scope;
7544 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7545 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7546 LocalContext context(0, templ, v8::Handle<Value>());
7547 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7548 v8::Handle<Value> value = CompileRun(
7549 "function f() {"
7550 " for (var i = 0; i < 1000; i++) {"
7551 " try { x; } catch(e) { return true; }"
7552 " }"
7553 " return false;"
7554 "};"
7555 "f();");
7556 CHECK_EQ(true, value->BooleanValue());
7557 interceptor_call_count = 0;
7558 value = CompileRun(
7559 "function g() {"
7560 " for (var i = 0; i < 1000; i++) {"
7561 " try { x(42); } catch(e) { return true; }"
7562 " }"
7563 " return false;"
7564 "};"
7565 "g();");
7566 CHECK_EQ(true, value->BooleanValue());
7567}
7568
7569
7570static int interceptor_ic_exception_get_count = 0;
7571
7572static v8::Handle<Value> InterceptorICExceptionGetter(
7573 Local<String> name,
7574 const AccessorInfo& info) {
7575 ApiTestFuzzer::Fuzz();
7576 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7577 return call_ic_function3;
7578 }
7579 if (interceptor_ic_exception_get_count == 20) {
7580 return v8::ThrowException(v8_num(42));
7581 }
7582 // Do not handle get for properties other than x.
7583 return v8::Handle<Value>();
7584}
7585
7586// Test interceptor load/call IC where the interceptor throws an
7587// exception once in a while.
7588THREADED_TEST(InterceptorICGetterExceptions) {
7589 interceptor_ic_exception_get_count = 0;
7590 v8::HandleScope scope;
7591 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7592 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7593 LocalContext context(0, templ, v8::Handle<Value>());
7594 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7595 v8::Handle<Value> value = CompileRun(
7596 "function f() {"
7597 " for (var i = 0; i < 100; i++) {"
7598 " try { x; } catch(e) { return true; }"
7599 " }"
7600 " return false;"
7601 "};"
7602 "f();");
7603 CHECK_EQ(true, value->BooleanValue());
7604 interceptor_ic_exception_get_count = 0;
7605 value = CompileRun(
7606 "function f() {"
7607 " for (var i = 0; i < 100; i++) {"
7608 " try { x(42); } catch(e) { return true; }"
7609 " }"
7610 " return false;"
7611 "};"
7612 "f();");
7613 CHECK_EQ(true, value->BooleanValue());
7614}
7615
7616
7617static int interceptor_ic_exception_set_count = 0;
7618
7619static v8::Handle<Value> InterceptorICExceptionSetter(
7620 Local<String> key, Local<Value> value, const AccessorInfo&) {
7621 ApiTestFuzzer::Fuzz();
7622 if (++interceptor_ic_exception_set_count > 20) {
7623 return v8::ThrowException(v8_num(42));
7624 }
7625 // Do not actually handle setting.
7626 return v8::Handle<Value>();
7627}
7628
7629// Test interceptor store IC where the interceptor throws an exception
7630// once in a while.
7631THREADED_TEST(InterceptorICSetterExceptions) {
7632 interceptor_ic_exception_set_count = 0;
7633 v8::HandleScope scope;
7634 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7635 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7636 LocalContext context(0, templ, v8::Handle<Value>());
7637 v8::Handle<Value> value = CompileRun(
7638 "function f() {"
7639 " for (var i = 0; i < 100; i++) {"
7640 " try { x = 42; } catch(e) { return true; }"
7641 " }"
7642 " return false;"
7643 "};"
7644 "f();");
7645 CHECK_EQ(true, value->BooleanValue());
7646}
7647
7648
7649// Test that we ignore null interceptors.
7650THREADED_TEST(NullNamedInterceptor) {
7651 v8::HandleScope scope;
7652 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7653 templ->SetNamedPropertyHandler(0);
7654 LocalContext context;
7655 templ->Set("x", v8_num(42));
7656 v8::Handle<v8::Object> obj = templ->NewInstance();
7657 context->Global()->Set(v8_str("obj"), obj);
7658 v8::Handle<Value> value = CompileRun("obj.x");
7659 CHECK(value->IsInt32());
7660 CHECK_EQ(42, value->Int32Value());
7661}
7662
7663
7664// Test that we ignore null interceptors.
7665THREADED_TEST(NullIndexedInterceptor) {
7666 v8::HandleScope scope;
7667 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7668 templ->SetIndexedPropertyHandler(0);
7669 LocalContext context;
7670 templ->Set("42", v8_num(42));
7671 v8::Handle<v8::Object> obj = templ->NewInstance();
7672 context->Global()->Set(v8_str("obj"), obj);
7673 v8::Handle<Value> value = CompileRun("obj[42]");
7674 CHECK(value->IsInt32());
7675 CHECK_EQ(42, value->Int32Value());
7676}
7677
7678
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007679THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7680 v8::HandleScope scope;
7681 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7682 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7683 LocalContext env;
7684 env->Global()->Set(v8_str("obj"),
7685 templ->GetFunction()->NewInstance());
7686 ExpectTrue("obj.x === 42");
7687 ExpectTrue("!obj.propertyIsEnumerable('x')");
7688}
7689
7690
Steve Blocka7e24c12009-10-30 11:49:00 +00007691static v8::Handle<Value> ParentGetter(Local<String> name,
7692 const AccessorInfo& info) {
7693 ApiTestFuzzer::Fuzz();
7694 return v8_num(1);
7695}
7696
7697
7698static v8::Handle<Value> ChildGetter(Local<String> name,
7699 const AccessorInfo& info) {
7700 ApiTestFuzzer::Fuzz();
7701 return v8_num(42);
7702}
7703
7704
7705THREADED_TEST(Overriding) {
7706 v8::HandleScope scope;
7707 LocalContext context;
7708
7709 // Parent template.
7710 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7711 Local<ObjectTemplate> parent_instance_templ =
7712 parent_templ->InstanceTemplate();
7713 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7714
7715 // Template that inherits from the parent template.
7716 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7717 Local<ObjectTemplate> child_instance_templ =
7718 child_templ->InstanceTemplate();
7719 child_templ->Inherit(parent_templ);
7720 // Override 'f'. The child version of 'f' should get called for child
7721 // instances.
7722 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7723 // Add 'g' twice. The 'g' added last should get called for instances.
7724 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7725 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7726
7727 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7728 // so 'h' can be shadowed on the instance object.
7729 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7730 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7731 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7732
7733 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7734 // but the attribute does not have effect because it is duplicated with
7735 // NULL setter.
7736 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7737 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7738
7739
7740
7741 // Instantiate the child template.
7742 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7743
7744 // Check that the child function overrides the parent one.
7745 context->Global()->Set(v8_str("o"), instance);
7746 Local<Value> value = v8_compile("o.f")->Run();
7747 // Check that the 'g' that was added last is hit.
7748 CHECK_EQ(42, value->Int32Value());
7749 value = v8_compile("o.g")->Run();
7750 CHECK_EQ(42, value->Int32Value());
7751
7752 // Check 'h' can be shadowed.
7753 value = v8_compile("o.h = 3; o.h")->Run();
7754 CHECK_EQ(3, value->Int32Value());
7755
7756 // Check 'i' is cannot be shadowed or changed.
7757 value = v8_compile("o.i = 3; o.i")->Run();
7758 CHECK_EQ(42, value->Int32Value());
7759}
7760
7761
7762static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7763 ApiTestFuzzer::Fuzz();
7764 if (args.IsConstructCall()) {
7765 return v8::Boolean::New(true);
7766 }
7767 return v8::Boolean::New(false);
7768}
7769
7770
7771THREADED_TEST(IsConstructCall) {
7772 v8::HandleScope scope;
7773
7774 // Function template with call handler.
7775 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7776 templ->SetCallHandler(IsConstructHandler);
7777
7778 LocalContext context;
7779
7780 context->Global()->Set(v8_str("f"), templ->GetFunction());
7781 Local<Value> value = v8_compile("f()")->Run();
7782 CHECK(!value->BooleanValue());
7783 value = v8_compile("new f()")->Run();
7784 CHECK(value->BooleanValue());
7785}
7786
7787
7788THREADED_TEST(ObjectProtoToString) {
7789 v8::HandleScope scope;
7790 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7791 templ->SetClassName(v8_str("MyClass"));
7792
7793 LocalContext context;
7794
7795 Local<String> customized_tostring = v8_str("customized toString");
7796
7797 // Replace Object.prototype.toString
7798 v8_compile("Object.prototype.toString = function() {"
7799 " return 'customized toString';"
7800 "}")->Run();
7801
7802 // Normal ToString call should call replaced Object.prototype.toString
7803 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7804 Local<String> value = instance->ToString();
7805 CHECK(value->IsString() && value->Equals(customized_tostring));
7806
7807 // ObjectProtoToString should not call replace toString function.
7808 value = instance->ObjectProtoToString();
7809 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7810
7811 // Check global
7812 value = context->Global()->ObjectProtoToString();
7813 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7814
7815 // Check ordinary object
7816 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007817 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007818 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7819}
7820
7821
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08007822THREADED_TEST(ObjectGetConstructorName) {
7823 v8::HandleScope scope;
7824 LocalContext context;
7825 v8_compile("function Parent() {};"
7826 "function Child() {};"
7827 "Child.prototype = new Parent();"
7828 "var outer = { inner: function() { } };"
7829 "var p = new Parent();"
7830 "var c = new Child();"
7831 "var x = new outer.inner();")->Run();
7832
7833 Local<v8::Value> p = context->Global()->Get(v8_str("p"));
7834 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals(
7835 v8_str("Parent")));
7836
7837 Local<v8::Value> c = context->Global()->Get(v8_str("c"));
7838 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals(
7839 v8_str("Child")));
7840
7841 Local<v8::Value> x = context->Global()->Get(v8_str("x"));
7842 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals(
7843 v8_str("outer.inner")));
7844}
7845
7846
Steve Blocka7e24c12009-10-30 11:49:00 +00007847bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01007848i::Semaphore* ApiTestFuzzer::all_tests_done_=
7849 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007850int ApiTestFuzzer::active_tests_;
7851int ApiTestFuzzer::tests_being_run_;
7852int ApiTestFuzzer::current_;
7853
7854
7855// We are in a callback and want to switch to another thread (if we
7856// are currently running the thread fuzzing test).
7857void ApiTestFuzzer::Fuzz() {
7858 if (!fuzzing_) return;
7859 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7860 test->ContextSwitch();
7861}
7862
7863
7864// Let the next thread go. Since it is also waiting on the V8 lock it may
7865// not start immediately.
7866bool ApiTestFuzzer::NextThread() {
7867 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007868 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007869 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007870 if (kLogThreading)
7871 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007872 return false;
7873 }
Steve Blockd0582a62009-12-15 09:54:21 +00007874 if (kLogThreading) {
7875 printf("Switch from %s to %s\n",
7876 test_name,
7877 RegisterThreadedTest::nth(test_position)->name());
7878 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007879 current_ = test_position;
7880 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7881 return true;
7882}
7883
7884
7885void ApiTestFuzzer::Run() {
7886 // When it is our turn...
7887 gate_->Wait();
7888 {
7889 // ... get the V8 lock and start running the test.
7890 v8::Locker locker;
7891 CallTest();
7892 }
7893 // This test finished.
7894 active_ = false;
7895 active_tests_--;
7896 // If it was the last then signal that fact.
7897 if (active_tests_ == 0) {
7898 all_tests_done_->Signal();
7899 } else {
7900 // Otherwise select a new test and start that.
7901 NextThread();
7902 }
7903}
7904
7905
7906static unsigned linear_congruential_generator;
7907
7908
7909void ApiTestFuzzer::Setup(PartOfTest part) {
7910 linear_congruential_generator = i::FLAG_testing_prng_seed;
7911 fuzzing_ = true;
7912 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7913 int end = (part == FIRST_PART)
7914 ? (RegisterThreadedTest::count() >> 1)
7915 : RegisterThreadedTest::count();
7916 active_tests_ = tests_being_run_ = end - start;
7917 for (int i = 0; i < tests_being_run_; i++) {
7918 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7919 }
7920 for (int i = 0; i < active_tests_; i++) {
7921 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7922 }
7923}
7924
7925
7926static void CallTestNumber(int test_number) {
7927 (RegisterThreadedTest::nth(test_number)->callback())();
7928}
7929
7930
7931void ApiTestFuzzer::RunAllTests() {
7932 // Set off the first test.
7933 current_ = -1;
7934 NextThread();
7935 // Wait till they are all done.
7936 all_tests_done_->Wait();
7937}
7938
7939
7940int ApiTestFuzzer::GetNextTestNumber() {
7941 int next_test;
7942 do {
7943 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7944 linear_congruential_generator *= 1664525u;
7945 linear_congruential_generator += 1013904223u;
7946 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7947 return next_test;
7948}
7949
7950
7951void ApiTestFuzzer::ContextSwitch() {
7952 // If the new thread is the same as the current thread there is nothing to do.
7953 if (NextThread()) {
7954 // Now it can start.
7955 v8::Unlocker unlocker;
7956 // Wait till someone starts us again.
7957 gate_->Wait();
7958 // And we're off.
7959 }
7960}
7961
7962
7963void ApiTestFuzzer::TearDown() {
7964 fuzzing_ = false;
7965 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7966 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7967 if (fuzzer != NULL) fuzzer->Join();
7968 }
7969}
7970
7971
7972// Lets not be needlessly self-referential.
7973TEST(Threading) {
7974 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7975 ApiTestFuzzer::RunAllTests();
7976 ApiTestFuzzer::TearDown();
7977}
7978
7979TEST(Threading2) {
7980 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7981 ApiTestFuzzer::RunAllTests();
7982 ApiTestFuzzer::TearDown();
7983}
7984
7985
7986void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007987 if (kLogThreading)
7988 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007989 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007990 if (kLogThreading)
7991 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007992}
7993
7994
7995static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7996 CHECK(v8::Locker::IsLocked());
7997 ApiTestFuzzer::Fuzz();
7998 v8::Unlocker unlocker;
7999 const char* code = "throw 7;";
8000 {
8001 v8::Locker nested_locker;
8002 v8::HandleScope scope;
8003 v8::Handle<Value> exception;
8004 { v8::TryCatch try_catch;
8005 v8::Handle<Value> value = CompileRun(code);
8006 CHECK(value.IsEmpty());
8007 CHECK(try_catch.HasCaught());
8008 // Make sure to wrap the exception in a new handle because
8009 // the handle returned from the TryCatch is destroyed
8010 // when the TryCatch is destroyed.
8011 exception = Local<Value>::New(try_catch.Exception());
8012 }
8013 return v8::ThrowException(exception);
8014 }
8015}
8016
8017
8018static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
8019 CHECK(v8::Locker::IsLocked());
8020 ApiTestFuzzer::Fuzz();
8021 v8::Unlocker unlocker;
8022 const char* code = "throw 7;";
8023 {
8024 v8::Locker nested_locker;
8025 v8::HandleScope scope;
8026 v8::Handle<Value> value = CompileRun(code);
8027 CHECK(value.IsEmpty());
8028 return v8_str("foo");
8029 }
8030}
8031
8032
8033// These are locking tests that don't need to be run again
8034// as part of the locking aggregation tests.
8035TEST(NestedLockers) {
8036 v8::Locker locker;
8037 CHECK(v8::Locker::IsLocked());
8038 v8::HandleScope scope;
8039 LocalContext env;
8040 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
8041 Local<Function> fun = fun_templ->GetFunction();
8042 env->Global()->Set(v8_str("throw_in_js"), fun);
8043 Local<Script> script = v8_compile("(function () {"
8044 " try {"
8045 " throw_in_js();"
8046 " return 42;"
8047 " } catch (e) {"
8048 " return e * 13;"
8049 " }"
8050 "})();");
8051 CHECK_EQ(91, script->Run()->Int32Value());
8052}
8053
8054
8055// These are locking tests that don't need to be run again
8056// as part of the locking aggregation tests.
8057TEST(NestedLockersNoTryCatch) {
8058 v8::Locker locker;
8059 v8::HandleScope scope;
8060 LocalContext env;
8061 Local<v8::FunctionTemplate> fun_templ =
8062 v8::FunctionTemplate::New(ThrowInJSNoCatch);
8063 Local<Function> fun = fun_templ->GetFunction();
8064 env->Global()->Set(v8_str("throw_in_js"), fun);
8065 Local<Script> script = v8_compile("(function () {"
8066 " try {"
8067 " throw_in_js();"
8068 " return 42;"
8069 " } catch (e) {"
8070 " return e * 13;"
8071 " }"
8072 "})();");
8073 CHECK_EQ(91, script->Run()->Int32Value());
8074}
8075
8076
8077THREADED_TEST(RecursiveLocking) {
8078 v8::Locker locker;
8079 {
8080 v8::Locker locker2;
8081 CHECK(v8::Locker::IsLocked());
8082 }
8083}
8084
8085
8086static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
8087 ApiTestFuzzer::Fuzz();
8088 v8::Unlocker unlocker;
8089 return v8::Undefined();
8090}
8091
8092
8093THREADED_TEST(LockUnlockLock) {
8094 {
8095 v8::Locker locker;
8096 v8::HandleScope scope;
8097 LocalContext env;
8098 Local<v8::FunctionTemplate> fun_templ =
8099 v8::FunctionTemplate::New(UnlockForAMoment);
8100 Local<Function> fun = fun_templ->GetFunction();
8101 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8102 Local<Script> script = v8_compile("(function () {"
8103 " unlock_for_a_moment();"
8104 " return 42;"
8105 "})();");
8106 CHECK_EQ(42, script->Run()->Int32Value());
8107 }
8108 {
8109 v8::Locker locker;
8110 v8::HandleScope scope;
8111 LocalContext env;
8112 Local<v8::FunctionTemplate> fun_templ =
8113 v8::FunctionTemplate::New(UnlockForAMoment);
8114 Local<Function> fun = fun_templ->GetFunction();
8115 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8116 Local<Script> script = v8_compile("(function () {"
8117 " unlock_for_a_moment();"
8118 " return 42;"
8119 "})();");
8120 CHECK_EQ(42, script->Run()->Int32Value());
8121 }
8122}
8123
8124
Leon Clarked91b9f72010-01-27 17:25:45 +00008125static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00008126 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01008127 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00008128 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
8129 if (object->IsJSGlobalObject()) count++;
8130 return count;
8131}
8132
8133
Ben Murdochf87a2032010-10-22 12:50:53 +01008134static void CheckSurvivingGlobalObjectsCount(int expected) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008135 // We need to collect all garbage twice to be sure that everything
8136 // has been collected. This is because inline caches are cleared in
8137 // the first garbage collection but some of the maps have already
8138 // been marked at that point. Therefore some of the maps are not
8139 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01008140 i::Heap::CollectAllGarbage(false);
8141 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00008142 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00008143#ifdef DEBUG
Ben Murdochf87a2032010-10-22 12:50:53 +01008144 if (count != expected) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00008145#endif
Ben Murdochf87a2032010-10-22 12:50:53 +01008146 CHECK_EQ(expected, count);
Steve Blocka7e24c12009-10-30 11:49:00 +00008147}
8148
8149
8150TEST(DontLeakGlobalObjects) {
8151 // Regression test for issues 1139850 and 1174891.
8152
8153 v8::V8::Initialize();
8154
Steve Blocka7e24c12009-10-30 11:49:00 +00008155 for (int i = 0; i < 5; i++) {
8156 { v8::HandleScope scope;
8157 LocalContext context;
8158 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008159 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008160
8161 { v8::HandleScope scope;
8162 LocalContext context;
8163 v8_compile("Date")->Run();
8164 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008165 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008166
8167 { v8::HandleScope scope;
8168 LocalContext context;
8169 v8_compile("/aaa/")->Run();
8170 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008171 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008172
8173 { v8::HandleScope scope;
8174 const char* extension_list[] = { "v8/gc" };
8175 v8::ExtensionConfiguration extensions(1, extension_list);
8176 LocalContext context(&extensions);
8177 v8_compile("gc();")->Run();
8178 }
Ben Murdochf87a2032010-10-22 12:50:53 +01008179 CheckSurvivingGlobalObjectsCount(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00008180 }
8181}
8182
8183
8184v8::Persistent<v8::Object> some_object;
8185v8::Persistent<v8::Object> bad_handle;
8186
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008187void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008188 v8::HandleScope scope;
8189 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008190 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008191}
8192
8193
8194THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8195 LocalContext context;
8196
8197 v8::Persistent<v8::Object> handle1, handle2;
8198 {
8199 v8::HandleScope scope;
8200 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8201 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8202 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8203 }
8204 // Note: order is implementation dependent alas: currently
8205 // global handle nodes are processed by PostGarbageCollectionProcessing
8206 // in reverse allocation order, so if second allocated handle is deleted,
8207 // weak callback of the first handle would be able to 'reallocate' it.
8208 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8209 handle2.Dispose();
8210 i::Heap::CollectAllGarbage(false);
8211}
8212
8213
8214v8::Persistent<v8::Object> to_be_disposed;
8215
8216void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8217 to_be_disposed.Dispose();
8218 i::Heap::CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008219 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008220}
8221
8222
8223THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8224 LocalContext context;
8225
8226 v8::Persistent<v8::Object> handle1, handle2;
8227 {
8228 v8::HandleScope scope;
8229 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8230 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8231 }
8232 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8233 to_be_disposed = handle2;
8234 i::Heap::CollectAllGarbage(false);
8235}
8236
Steve Blockd0582a62009-12-15 09:54:21 +00008237void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8238 handle.Dispose();
8239}
8240
8241void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8242 v8::HandleScope scope;
8243 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008244 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00008245}
8246
8247
8248THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8249 LocalContext context;
8250
8251 v8::Persistent<v8::Object> handle1, handle2, handle3;
8252 {
8253 v8::HandleScope scope;
8254 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8255 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8256 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8257 }
8258 handle2.MakeWeak(NULL, DisposingCallback);
8259 handle3.MakeWeak(NULL, HandleCreatingCallback);
8260 i::Heap::CollectAllGarbage(false);
8261}
8262
Steve Blocka7e24c12009-10-30 11:49:00 +00008263
8264THREADED_TEST(CheckForCrossContextObjectLiterals) {
8265 v8::V8::Initialize();
8266
8267 const int nof = 2;
8268 const char* sources[nof] = {
8269 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8270 "Object()"
8271 };
8272
8273 for (int i = 0; i < nof; i++) {
8274 const char* source = sources[i];
8275 { v8::HandleScope scope;
8276 LocalContext context;
8277 CompileRun(source);
8278 }
8279 { v8::HandleScope scope;
8280 LocalContext context;
8281 CompileRun(source);
8282 }
8283 }
8284}
8285
8286
8287static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8288 v8::HandleScope inner;
8289 env->Enter();
8290 v8::Handle<Value> three = v8_num(3);
8291 v8::Handle<Value> value = inner.Close(three);
8292 env->Exit();
8293 return value;
8294}
8295
8296
8297THREADED_TEST(NestedHandleScopeAndContexts) {
8298 v8::HandleScope outer;
8299 v8::Persistent<Context> env = Context::New();
8300 env->Enter();
8301 v8::Handle<Value> value = NestedScope(env);
8302 v8::Handle<String> str = value->ToString();
8303 env->Exit();
8304 env.Dispose();
8305}
8306
8307
8308THREADED_TEST(ExternalAllocatedMemory) {
8309 v8::HandleScope outer;
8310 v8::Persistent<Context> env = Context::New();
8311 const int kSize = 1024*1024;
8312 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8313 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8314}
8315
8316
8317THREADED_TEST(DisposeEnteredContext) {
8318 v8::HandleScope scope;
8319 LocalContext outer;
8320 { v8::Persistent<v8::Context> inner = v8::Context::New();
8321 inner->Enter();
8322 inner.Dispose();
8323 inner.Clear();
8324 inner->Exit();
8325 }
8326}
8327
8328
8329// Regression test for issue 54, object templates with internal fields
8330// but no accessors or interceptors did not get their internal field
8331// count set on instances.
8332THREADED_TEST(Regress54) {
8333 v8::HandleScope outer;
8334 LocalContext context;
8335 static v8::Persistent<v8::ObjectTemplate> templ;
8336 if (templ.IsEmpty()) {
8337 v8::HandleScope inner;
8338 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8339 local->SetInternalFieldCount(1);
8340 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8341 }
8342 v8::Handle<v8::Object> result = templ->NewInstance();
8343 CHECK_EQ(1, result->InternalFieldCount());
8344}
8345
8346
8347// If part of the threaded tests, this test makes ThreadingTest fail
8348// on mac.
8349TEST(CatchStackOverflow) {
8350 v8::HandleScope scope;
8351 LocalContext context;
8352 v8::TryCatch try_catch;
8353 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8354 "function f() {"
8355 " return f();"
8356 "}"
8357 ""
8358 "f();"));
8359 v8::Handle<v8::Value> result = script->Run();
8360 CHECK(result.IsEmpty());
8361}
8362
8363
8364static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8365 const char* resource_name,
8366 int line_offset) {
8367 v8::HandleScope scope;
8368 v8::TryCatch try_catch;
8369 v8::Handle<v8::Value> result = script->Run();
8370 CHECK(result.IsEmpty());
8371 CHECK(try_catch.HasCaught());
8372 v8::Handle<v8::Message> message = try_catch.Message();
8373 CHECK(!message.IsEmpty());
8374 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8375 CHECK_EQ(91, message->GetStartPosition());
8376 CHECK_EQ(92, message->GetEndPosition());
8377 CHECK_EQ(2, message->GetStartColumn());
8378 CHECK_EQ(3, message->GetEndColumn());
8379 v8::String::AsciiValue line(message->GetSourceLine());
8380 CHECK_EQ(" throw 'nirk';", *line);
8381 v8::String::AsciiValue name(message->GetScriptResourceName());
8382 CHECK_EQ(resource_name, *name);
8383}
8384
8385
8386THREADED_TEST(TryCatchSourceInfo) {
8387 v8::HandleScope scope;
8388 LocalContext context;
8389 v8::Handle<v8::String> source = v8::String::New(
8390 "function Foo() {\n"
8391 " return Bar();\n"
8392 "}\n"
8393 "\n"
8394 "function Bar() {\n"
8395 " return Baz();\n"
8396 "}\n"
8397 "\n"
8398 "function Baz() {\n"
8399 " throw 'nirk';\n"
8400 "}\n"
8401 "\n"
8402 "Foo();\n");
8403
8404 const char* resource_name;
8405 v8::Handle<v8::Script> script;
8406 resource_name = "test.js";
8407 script = v8::Script::Compile(source, v8::String::New(resource_name));
8408 CheckTryCatchSourceInfo(script, resource_name, 0);
8409
8410 resource_name = "test1.js";
8411 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8412 script = v8::Script::Compile(source, &origin1);
8413 CheckTryCatchSourceInfo(script, resource_name, 0);
8414
8415 resource_name = "test2.js";
8416 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8417 script = v8::Script::Compile(source, &origin2);
8418 CheckTryCatchSourceInfo(script, resource_name, 7);
8419}
8420
8421
8422THREADED_TEST(CompilationCache) {
8423 v8::HandleScope scope;
8424 LocalContext context;
8425 v8::Handle<v8::String> source0 = v8::String::New("1234");
8426 v8::Handle<v8::String> source1 = v8::String::New("1234");
8427 v8::Handle<v8::Script> script0 =
8428 v8::Script::Compile(source0, v8::String::New("test.js"));
8429 v8::Handle<v8::Script> script1 =
8430 v8::Script::Compile(source1, v8::String::New("test.js"));
8431 v8::Handle<v8::Script> script2 =
8432 v8::Script::Compile(source0); // different origin
8433 CHECK_EQ(1234, script0->Run()->Int32Value());
8434 CHECK_EQ(1234, script1->Run()->Int32Value());
8435 CHECK_EQ(1234, script2->Run()->Int32Value());
8436}
8437
8438
8439static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8440 ApiTestFuzzer::Fuzz();
8441 return v8_num(42);
8442}
8443
8444
8445THREADED_TEST(CallbackFunctionName) {
8446 v8::HandleScope scope;
8447 LocalContext context;
8448 Local<ObjectTemplate> t = ObjectTemplate::New();
8449 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8450 context->Global()->Set(v8_str("obj"), t->NewInstance());
8451 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8452 CHECK(value->IsString());
8453 v8::String::AsciiValue name(value);
8454 CHECK_EQ("asdf", *name);
8455}
8456
8457
8458THREADED_TEST(DateAccess) {
8459 v8::HandleScope scope;
8460 LocalContext context;
8461 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8462 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008463 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008464}
8465
8466
8467void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008468 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008469 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8470 CHECK_EQ(elmc, props->Length());
8471 for (int i = 0; i < elmc; i++) {
8472 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8473 CHECK_EQ(elmv[i], *elm);
8474 }
8475}
8476
8477
8478THREADED_TEST(PropertyEnumeration) {
8479 v8::HandleScope scope;
8480 LocalContext context;
8481 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8482 "var result = [];"
8483 "result[0] = {};"
8484 "result[1] = {a: 1, b: 2};"
8485 "result[2] = [1, 2, 3];"
8486 "var proto = {x: 1, y: 2, z: 3};"
8487 "var x = { __proto__: proto, w: 0, z: 1 };"
8488 "result[3] = x;"
8489 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008490 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008491 CHECK_EQ(4, elms->Length());
8492 int elmc0 = 0;
8493 const char** elmv0 = NULL;
8494 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8495 int elmc1 = 2;
8496 const char* elmv1[] = {"a", "b"};
8497 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8498 int elmc2 = 3;
8499 const char* elmv2[] = {"0", "1", "2"};
8500 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8501 int elmc3 = 4;
8502 const char* elmv3[] = {"w", "z", "x", "y"};
8503 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8504}
8505
8506
Steve Blocka7e24c12009-10-30 11:49:00 +00008507static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8508 Local<Value> name,
8509 v8::AccessType type,
8510 Local<Value> data) {
8511 return type != v8::ACCESS_SET;
8512}
8513
8514
8515static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8516 uint32_t key,
8517 v8::AccessType type,
8518 Local<Value> data) {
8519 return type != v8::ACCESS_SET;
8520}
8521
8522
8523THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8524 v8::HandleScope scope;
8525 LocalContext context;
8526 Local<ObjectTemplate> templ = ObjectTemplate::New();
8527 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8528 IndexedSetAccessBlocker);
8529 templ->Set(v8_str("x"), v8::True());
8530 Local<v8::Object> instance = templ->NewInstance();
8531 context->Global()->Set(v8_str("obj"), instance);
8532 Local<Value> value = CompileRun("obj.x");
8533 CHECK(value->BooleanValue());
8534}
8535
8536
8537static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8538 Local<Value> name,
8539 v8::AccessType type,
8540 Local<Value> data) {
8541 return false;
8542}
8543
8544
8545static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8546 uint32_t key,
8547 v8::AccessType type,
8548 Local<Value> data) {
8549 return false;
8550}
8551
8552
8553
8554THREADED_TEST(AccessChecksReenabledCorrectly) {
8555 v8::HandleScope scope;
8556 LocalContext context;
8557 Local<ObjectTemplate> templ = ObjectTemplate::New();
8558 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8559 IndexedGetAccessBlocker);
8560 templ->Set(v8_str("a"), v8_str("a"));
8561 // Add more than 8 (see kMaxFastProperties) properties
8562 // so that the constructor will force copying map.
8563 // Cannot sprintf, gcc complains unsafety.
8564 char buf[4];
8565 for (char i = '0'; i <= '9' ; i++) {
8566 buf[0] = i;
8567 for (char j = '0'; j <= '9'; j++) {
8568 buf[1] = j;
8569 for (char k = '0'; k <= '9'; k++) {
8570 buf[2] = k;
8571 buf[3] = 0;
8572 templ->Set(v8_str(buf), v8::Number::New(k));
8573 }
8574 }
8575 }
8576
8577 Local<v8::Object> instance_1 = templ->NewInstance();
8578 context->Global()->Set(v8_str("obj_1"), instance_1);
8579
8580 Local<Value> value_1 = CompileRun("obj_1.a");
8581 CHECK(value_1->IsUndefined());
8582
8583 Local<v8::Object> instance_2 = templ->NewInstance();
8584 context->Global()->Set(v8_str("obj_2"), instance_2);
8585
8586 Local<Value> value_2 = CompileRun("obj_2.a");
8587 CHECK(value_2->IsUndefined());
8588}
8589
8590
8591// This tests that access check information remains on the global
8592// object template when creating contexts.
8593THREADED_TEST(AccessControlRepeatedContextCreation) {
8594 v8::HandleScope handle_scope;
8595 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8596 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8597 IndexedSetAccessBlocker);
8598 i::Handle<i::ObjectTemplateInfo> internal_template =
8599 v8::Utils::OpenHandle(*global_template);
8600 CHECK(!internal_template->constructor()->IsUndefined());
8601 i::Handle<i::FunctionTemplateInfo> constructor(
8602 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8603 CHECK(!constructor->access_check_info()->IsUndefined());
8604 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8605 CHECK(!constructor->access_check_info()->IsUndefined());
8606}
8607
8608
8609THREADED_TEST(TurnOnAccessCheck) {
8610 v8::HandleScope handle_scope;
8611
8612 // Create an environment with access check to the global object disabled by
8613 // default.
8614 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8615 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8616 IndexedGetAccessBlocker,
8617 v8::Handle<v8::Value>(),
8618 false);
8619 v8::Persistent<Context> context = Context::New(NULL, global_template);
8620 Context::Scope context_scope(context);
8621
8622 // Set up a property and a number of functions.
8623 context->Global()->Set(v8_str("a"), v8_num(1));
8624 CompileRun("function f1() {return a;}"
8625 "function f2() {return a;}"
8626 "function g1() {return h();}"
8627 "function g2() {return h();}"
8628 "function h() {return 1;}");
8629 Local<Function> f1 =
8630 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8631 Local<Function> f2 =
8632 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8633 Local<Function> g1 =
8634 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8635 Local<Function> g2 =
8636 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8637 Local<Function> h =
8638 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8639
8640 // Get the global object.
8641 v8::Handle<v8::Object> global = context->Global();
8642
8643 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8644 // uses the runtime system to retreive property a whereas f2 uses global load
8645 // inline cache.
8646 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8647 for (int i = 0; i < 4; i++) {
8648 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8649 }
8650
8651 // Same for g1 and g2.
8652 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8653 for (int i = 0; i < 4; i++) {
8654 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8655 }
8656
8657 // Detach the global and turn on access check.
8658 context->DetachGlobal();
8659 context->Global()->TurnOnAccessCheck();
8660
8661 // Failing access check to property get results in undefined.
8662 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8663 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8664
8665 // Failing access check to function call results in exception.
8666 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8667 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8668
8669 // No failing access check when just returning a constant.
8670 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8671}
8672
8673
8674// This test verifies that pre-compilation (aka preparsing) can be called
8675// without initializing the whole VM. Thus we cannot run this test in a
8676// multi-threaded setup.
8677TEST(PreCompile) {
8678 // TODO(155): This test would break without the initialization of V8. This is
8679 // a workaround for now to make this test not fail.
8680 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008681 const char* script = "function foo(a) { return a+1; }";
8682 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008683 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008684 CHECK_NE(sd->Length(), 0);
8685 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008686 CHECK(!sd->HasError());
8687 delete sd;
8688}
8689
8690
8691TEST(PreCompileWithError) {
8692 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008693 const char* script = "function foo(a) { return 1 * * 2; }";
8694 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008695 v8::ScriptData::PreCompile(script, i::StrLength(script));
8696 CHECK(sd->HasError());
8697 delete sd;
8698}
8699
8700
8701TEST(Regress31661) {
8702 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008703 const char* script = " The Definintive Guide";
8704 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008705 v8::ScriptData::PreCompile(script, i::StrLength(script));
8706 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008707 delete sd;
8708}
8709
8710
Leon Clarkef7060e22010-06-03 12:02:55 +01008711// Tests that ScriptData can be serialized and deserialized.
8712TEST(PreCompileSerialization) {
8713 v8::V8::Initialize();
8714 const char* script = "function foo(a) { return a+1; }";
8715 v8::ScriptData* sd =
8716 v8::ScriptData::PreCompile(script, i::StrLength(script));
8717
8718 // Serialize.
8719 int serialized_data_length = sd->Length();
8720 char* serialized_data = i::NewArray<char>(serialized_data_length);
8721 memcpy(serialized_data, sd->Data(), serialized_data_length);
8722
8723 // Deserialize.
8724 v8::ScriptData* deserialized_sd =
8725 v8::ScriptData::New(serialized_data, serialized_data_length);
8726
8727 // Verify that the original is the same as the deserialized.
8728 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8729 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8730 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8731
8732 delete sd;
8733 delete deserialized_sd;
8734}
8735
8736
8737// Attempts to deserialize bad data.
8738TEST(PreCompileDeserializationError) {
8739 v8::V8::Initialize();
8740 const char* data = "DONT CARE";
8741 int invalid_size = 3;
8742 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8743
8744 CHECK_EQ(0, sd->Length());
8745
8746 delete sd;
8747}
8748
8749
Leon Clarkeac952652010-07-15 11:15:24 +01008750// Attempts to deserialize bad data.
8751TEST(PreCompileInvalidPreparseDataError) {
8752 v8::V8::Initialize();
8753 v8::HandleScope scope;
8754 LocalContext context;
8755
8756 const char* script = "function foo(){ return 5;}\n"
8757 "function bar(){ return 6 + 7;} foo();";
8758 v8::ScriptData* sd =
8759 v8::ScriptData::PreCompile(script, i::StrLength(script));
8760 CHECK(!sd->HasError());
8761 // ScriptDataImpl private implementation details
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -08008762 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize;
Iain Merrick9ac36c92010-09-13 15:29:50 +01008763 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +01008764 const int kFunctionEntryStartOffset = 0;
8765 const int kFunctionEntryEndOffset = 1;
8766 unsigned* sd_data =
8767 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01008768
8769 // Overwrite function bar's end position with 0.
8770 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
8771 v8::TryCatch try_catch;
8772
8773 Local<String> source = String::New(script);
8774 Local<Script> compiled_script = Script::New(source, NULL, sd);
8775 CHECK(try_catch.HasCaught());
8776 String::AsciiValue exception_value(try_catch.Message()->Get());
8777 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8778 *exception_value);
8779
8780 try_catch.Reset();
8781 // Overwrite function bar's start position with 200. The function entry
8782 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01008783 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
8784 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01008785 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
8786 200;
8787 compiled_script = Script::New(source, NULL, sd);
8788 CHECK(try_catch.HasCaught());
8789 String::AsciiValue second_exception_value(try_catch.Message()->Get());
8790 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8791 *second_exception_value);
8792
8793 delete sd;
8794}
8795
8796
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008797// Verifies that the Handle<String> and const char* versions of the API produce
8798// the same results (at least for one trivial case).
8799TEST(PreCompileAPIVariationsAreSame) {
8800 v8::V8::Initialize();
8801 v8::HandleScope scope;
8802
8803 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008804
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008805 v8::ScriptData* sd_from_cstring =
8806 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8807
8808 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008809 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008810 v8::String::NewExternal(resource));
8811
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008812 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
8813 v8::String::New(cstring));
8814
8815 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008816 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008817 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008818 sd_from_cstring->Length()));
8819
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008820 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
8821 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8822 sd_from_string->Data(),
8823 sd_from_cstring->Length()));
8824
8825
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008826 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008827 delete sd_from_external_string;
8828 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008829}
8830
8831
Steve Blocka7e24c12009-10-30 11:49:00 +00008832// This tests that we do not allow dictionary load/call inline caches
8833// to use functions that have not yet been compiled. The potential
8834// problem of loading a function that has not yet been compiled can
8835// arise because we share code between contexts via the compilation
8836// cache.
8837THREADED_TEST(DictionaryICLoadedFunction) {
8838 v8::HandleScope scope;
8839 // Test LoadIC.
8840 for (int i = 0; i < 2; i++) {
8841 LocalContext context;
8842 context->Global()->Set(v8_str("tmp"), v8::True());
8843 context->Global()->Delete(v8_str("tmp"));
8844 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8845 }
8846 // Test CallIC.
8847 for (int i = 0; i < 2; i++) {
8848 LocalContext context;
8849 context->Global()->Set(v8_str("tmp"), v8::True());
8850 context->Global()->Delete(v8_str("tmp"));
8851 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8852 }
8853}
8854
8855
8856// Test that cross-context new calls use the context of the callee to
8857// create the new JavaScript object.
8858THREADED_TEST(CrossContextNew) {
8859 v8::HandleScope scope;
8860 v8::Persistent<Context> context0 = Context::New();
8861 v8::Persistent<Context> context1 = Context::New();
8862
8863 // Allow cross-domain access.
8864 Local<String> token = v8_str("<security token>");
8865 context0->SetSecurityToken(token);
8866 context1->SetSecurityToken(token);
8867
8868 // Set an 'x' property on the Object prototype and define a
8869 // constructor function in context0.
8870 context0->Enter();
8871 CompileRun("Object.prototype.x = 42; function C() {};");
8872 context0->Exit();
8873
8874 // Call the constructor function from context0 and check that the
8875 // result has the 'x' property.
8876 context1->Enter();
8877 context1->Global()->Set(v8_str("other"), context0->Global());
8878 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8879 CHECK(value->IsInt32());
8880 CHECK_EQ(42, value->Int32Value());
8881 context1->Exit();
8882
8883 // Dispose the contexts to allow them to be garbage collected.
8884 context0.Dispose();
8885 context1.Dispose();
8886}
8887
8888
8889class RegExpInterruptTest {
8890 public:
8891 RegExpInterruptTest() : block_(NULL) {}
8892 ~RegExpInterruptTest() { delete block_; }
8893 void RunTest() {
8894 block_ = i::OS::CreateSemaphore(0);
8895 gc_count_ = 0;
8896 gc_during_regexp_ = 0;
8897 regexp_success_ = false;
8898 gc_success_ = false;
8899 GCThread gc_thread(this);
8900 gc_thread.Start();
8901 v8::Locker::StartPreemption(1);
8902
8903 LongRunningRegExp();
8904 {
8905 v8::Unlocker unlock;
8906 gc_thread.Join();
8907 }
8908 v8::Locker::StopPreemption();
8909 CHECK(regexp_success_);
8910 CHECK(gc_success_);
8911 }
8912 private:
8913 // Number of garbage collections required.
8914 static const int kRequiredGCs = 5;
8915
8916 class GCThread : public i::Thread {
8917 public:
8918 explicit GCThread(RegExpInterruptTest* test)
8919 : test_(test) {}
8920 virtual void Run() {
8921 test_->CollectGarbage();
8922 }
8923 private:
8924 RegExpInterruptTest* test_;
8925 };
8926
8927 void CollectGarbage() {
8928 block_->Wait();
8929 while (gc_during_regexp_ < kRequiredGCs) {
8930 {
8931 v8::Locker lock;
8932 // TODO(lrn): Perhaps create some garbage before collecting.
8933 i::Heap::CollectAllGarbage(false);
8934 gc_count_++;
8935 }
8936 i::OS::Sleep(1);
8937 }
8938 gc_success_ = true;
8939 }
8940
8941 void LongRunningRegExp() {
8942 block_->Signal(); // Enable garbage collection thread on next preemption.
8943 int rounds = 0;
8944 while (gc_during_regexp_ < kRequiredGCs) {
8945 int gc_before = gc_count_;
8946 {
8947 // Match 15-30 "a"'s against 14 and a "b".
8948 const char* c_source =
8949 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8950 ".exec('aaaaaaaaaaaaaaab') === null";
8951 Local<String> source = String::New(c_source);
8952 Local<Script> script = Script::Compile(source);
8953 Local<Value> result = script->Run();
8954 if (!result->BooleanValue()) {
8955 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8956 return;
8957 }
8958 }
8959 {
8960 // Match 15-30 "a"'s against 15 and a "b".
8961 const char* c_source =
8962 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8963 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8964 Local<String> source = String::New(c_source);
8965 Local<Script> script = Script::Compile(source);
8966 Local<Value> result = script->Run();
8967 if (!result->BooleanValue()) {
8968 gc_during_regexp_ = kRequiredGCs;
8969 return;
8970 }
8971 }
8972 int gc_after = gc_count_;
8973 gc_during_regexp_ += gc_after - gc_before;
8974 rounds++;
8975 i::OS::Sleep(1);
8976 }
8977 regexp_success_ = true;
8978 }
8979
8980 i::Semaphore* block_;
8981 int gc_count_;
8982 int gc_during_regexp_;
8983 bool regexp_success_;
8984 bool gc_success_;
8985};
8986
8987
8988// Test that a regular expression execution can be interrupted and
8989// survive a garbage collection.
8990TEST(RegExpInterruption) {
8991 v8::Locker lock;
8992 v8::V8::Initialize();
8993 v8::HandleScope scope;
8994 Local<Context> local_env;
8995 {
8996 LocalContext env;
8997 local_env = env.local();
8998 }
8999
9000 // Local context should still be live.
9001 CHECK(!local_env.IsEmpty());
9002 local_env->Enter();
9003
9004 // Should complete without problems.
9005 RegExpInterruptTest().RunTest();
9006
9007 local_env->Exit();
9008}
9009
9010
9011class ApplyInterruptTest {
9012 public:
9013 ApplyInterruptTest() : block_(NULL) {}
9014 ~ApplyInterruptTest() { delete block_; }
9015 void RunTest() {
9016 block_ = i::OS::CreateSemaphore(0);
9017 gc_count_ = 0;
9018 gc_during_apply_ = 0;
9019 apply_success_ = false;
9020 gc_success_ = false;
9021 GCThread gc_thread(this);
9022 gc_thread.Start();
9023 v8::Locker::StartPreemption(1);
9024
9025 LongRunningApply();
9026 {
9027 v8::Unlocker unlock;
9028 gc_thread.Join();
9029 }
9030 v8::Locker::StopPreemption();
9031 CHECK(apply_success_);
9032 CHECK(gc_success_);
9033 }
9034 private:
9035 // Number of garbage collections required.
9036 static const int kRequiredGCs = 2;
9037
9038 class GCThread : public i::Thread {
9039 public:
9040 explicit GCThread(ApplyInterruptTest* test)
9041 : test_(test) {}
9042 virtual void Run() {
9043 test_->CollectGarbage();
9044 }
9045 private:
9046 ApplyInterruptTest* test_;
9047 };
9048
9049 void CollectGarbage() {
9050 block_->Wait();
9051 while (gc_during_apply_ < kRequiredGCs) {
9052 {
9053 v8::Locker lock;
9054 i::Heap::CollectAllGarbage(false);
9055 gc_count_++;
9056 }
9057 i::OS::Sleep(1);
9058 }
9059 gc_success_ = true;
9060 }
9061
9062 void LongRunningApply() {
9063 block_->Signal();
9064 int rounds = 0;
9065 while (gc_during_apply_ < kRequiredGCs) {
9066 int gc_before = gc_count_;
9067 {
9068 const char* c_source =
9069 "function do_very_little(bar) {"
9070 " this.foo = bar;"
9071 "}"
9072 "for (var i = 0; i < 100000; i++) {"
9073 " do_very_little.apply(this, ['bar']);"
9074 "}";
9075 Local<String> source = String::New(c_source);
9076 Local<Script> script = Script::Compile(source);
9077 Local<Value> result = script->Run();
9078 // Check that no exception was thrown.
9079 CHECK(!result.IsEmpty());
9080 }
9081 int gc_after = gc_count_;
9082 gc_during_apply_ += gc_after - gc_before;
9083 rounds++;
9084 }
9085 apply_success_ = true;
9086 }
9087
9088 i::Semaphore* block_;
9089 int gc_count_;
9090 int gc_during_apply_;
9091 bool apply_success_;
9092 bool gc_success_;
9093};
9094
9095
9096// Test that nothing bad happens if we get a preemption just when we were
9097// about to do an apply().
9098TEST(ApplyInterruption) {
9099 v8::Locker lock;
9100 v8::V8::Initialize();
9101 v8::HandleScope scope;
9102 Local<Context> local_env;
9103 {
9104 LocalContext env;
9105 local_env = env.local();
9106 }
9107
9108 // Local context should still be live.
9109 CHECK(!local_env.IsEmpty());
9110 local_env->Enter();
9111
9112 // Should complete without problems.
9113 ApplyInterruptTest().RunTest();
9114
9115 local_env->Exit();
9116}
9117
9118
9119// Verify that we can clone an object
9120TEST(ObjectClone) {
9121 v8::HandleScope scope;
9122 LocalContext env;
9123
9124 const char* sample =
9125 "var rv = {};" \
9126 "rv.alpha = 'hello';" \
9127 "rv.beta = 123;" \
9128 "rv;";
9129
9130 // Create an object, verify basics.
9131 Local<Value> val = CompileRun(sample);
9132 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01009133 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009134 obj->Set(v8_str("gamma"), v8_str("cloneme"));
9135
9136 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
9137 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9138 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
9139
9140 // Clone it.
9141 Local<v8::Object> clone = obj->Clone();
9142 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
9143 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
9144 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
9145
9146 // Set a property on the clone, verify each object.
9147 clone->Set(v8_str("beta"), v8::Integer::New(456));
9148 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9149 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
9150}
9151
9152
9153class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
9154 public:
9155 explicit AsciiVectorResource(i::Vector<const char> vector)
9156 : data_(vector) {}
9157 virtual ~AsciiVectorResource() {}
9158 virtual size_t length() const { return data_.length(); }
9159 virtual const char* data() const { return data_.start(); }
9160 private:
9161 i::Vector<const char> data_;
9162};
9163
9164
9165class UC16VectorResource : public v8::String::ExternalStringResource {
9166 public:
9167 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
9168 : data_(vector) {}
9169 virtual ~UC16VectorResource() {}
9170 virtual size_t length() const { return data_.length(); }
9171 virtual const i::uc16* data() const { return data_.start(); }
9172 private:
9173 i::Vector<const i::uc16> data_;
9174};
9175
9176
9177static void MorphAString(i::String* string,
9178 AsciiVectorResource* ascii_resource,
9179 UC16VectorResource* uc16_resource) {
9180 CHECK(i::StringShape(string).IsExternal());
9181 if (string->IsAsciiRepresentation()) {
9182 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009183 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009184 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009185 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009186 i::ExternalTwoByteString* morphed =
9187 i::ExternalTwoByteString::cast(string);
9188 morphed->set_resource(uc16_resource);
9189 } else {
9190 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009191 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009192 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009193 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009194 i::ExternalAsciiString* morphed =
9195 i::ExternalAsciiString::cast(string);
9196 morphed->set_resource(ascii_resource);
9197 }
9198}
9199
9200
9201// Test that we can still flatten a string if the components it is built up
9202// from have been turned into 16 bit strings in the mean time.
9203THREADED_TEST(MorphCompositeStringTest) {
9204 const char* c_string = "Now is the time for all good men"
9205 " to come to the aid of the party";
9206 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9207 {
9208 v8::HandleScope scope;
9209 LocalContext env;
9210 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009211 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009212 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009213 i::Vector<const uint16_t>(two_byte_string,
9214 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009215
9216 Local<String> lhs(v8::Utils::ToLocal(
9217 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9218 Local<String> rhs(v8::Utils::ToLocal(
9219 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9220
9221 env->Global()->Set(v8_str("lhs"), lhs);
9222 env->Global()->Set(v8_str("rhs"), rhs);
9223
9224 CompileRun(
9225 "var cons = lhs + rhs;"
9226 "var slice = lhs.substring(1, lhs.length - 1);"
9227 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
9228
9229 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
9230 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
9231
9232 // Now do some stuff to make sure the strings are flattened, etc.
9233 CompileRun(
9234 "/[^a-z]/.test(cons);"
9235 "/[^a-z]/.test(slice);"
9236 "/[^a-z]/.test(slice_on_cons);");
9237 const char* expected_cons =
9238 "Now is the time for all good men to come to the aid of the party"
9239 "Now is the time for all good men to come to the aid of the party";
9240 const char* expected_slice =
9241 "ow is the time for all good men to come to the aid of the part";
9242 const char* expected_slice_on_cons =
9243 "ow is the time for all good men to come to the aid of the party"
9244 "Now is the time for all good men to come to the aid of the part";
9245 CHECK_EQ(String::New(expected_cons),
9246 env->Global()->Get(v8_str("cons")));
9247 CHECK_EQ(String::New(expected_slice),
9248 env->Global()->Get(v8_str("slice")));
9249 CHECK_EQ(String::New(expected_slice_on_cons),
9250 env->Global()->Get(v8_str("slice_on_cons")));
9251 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009252 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009253}
9254
9255
9256TEST(CompileExternalTwoByteSource) {
9257 v8::HandleScope scope;
9258 LocalContext context;
9259
9260 // This is a very short list of sources, which currently is to check for a
9261 // regression caused by r2703.
9262 const char* ascii_sources[] = {
9263 "0.5",
9264 "-0.5", // This mainly testes PushBack in the Scanner.
9265 "--0.5", // This mainly testes PushBack in the Scanner.
9266 NULL
9267 };
9268
9269 // Compile the sources as external two byte strings.
9270 for (int i = 0; ascii_sources[i] != NULL; i++) {
9271 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9272 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009273 i::Vector<const uint16_t>(two_byte_string,
9274 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009275 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9276 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009277 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009278 }
9279}
9280
9281
9282class RegExpStringModificationTest {
9283 public:
9284 RegExpStringModificationTest()
9285 : block_(i::OS::CreateSemaphore(0)),
9286 morphs_(0),
9287 morphs_during_regexp_(0),
9288 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9289 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9290 ~RegExpStringModificationTest() { delete block_; }
9291 void RunTest() {
9292 regexp_success_ = false;
9293 morph_success_ = false;
9294
9295 // Initialize the contents of two_byte_content_ to be a uc16 representation
9296 // of "aaaaaaaaaaaaaab".
9297 for (int i = 0; i < 14; i++) {
9298 two_byte_content_[i] = 'a';
9299 }
9300 two_byte_content_[14] = 'b';
9301
9302 // Create the input string for the regexp - the one we are going to change
9303 // properties of.
9304 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9305
9306 // Inject the input as a global variable.
9307 i::Handle<i::String> input_name =
9308 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
John Reck59135872010-11-02 12:39:01 -07009309 i::Top::global_context()->global()->SetProperty(*input_name,
9310 *input_,
9311 NONE)->ToObjectChecked();
Steve Blocka7e24c12009-10-30 11:49:00 +00009312
9313
9314 MorphThread morph_thread(this);
9315 morph_thread.Start();
9316 v8::Locker::StartPreemption(1);
9317 LongRunningRegExp();
9318 {
9319 v8::Unlocker unlock;
9320 morph_thread.Join();
9321 }
9322 v8::Locker::StopPreemption();
9323 CHECK(regexp_success_);
9324 CHECK(morph_success_);
9325 }
9326 private:
9327
9328 // Number of string modifications required.
9329 static const int kRequiredModifications = 5;
9330 static const int kMaxModifications = 100;
9331
9332 class MorphThread : public i::Thread {
9333 public:
9334 explicit MorphThread(RegExpStringModificationTest* test)
9335 : test_(test) {}
9336 virtual void Run() {
9337 test_->MorphString();
9338 }
9339 private:
9340 RegExpStringModificationTest* test_;
9341 };
9342
9343 void MorphString() {
9344 block_->Wait();
9345 while (morphs_during_regexp_ < kRequiredModifications &&
9346 morphs_ < kMaxModifications) {
9347 {
9348 v8::Locker lock;
9349 // Swap string between ascii and two-byte representation.
9350 i::String* string = *input_;
9351 MorphAString(string, &ascii_resource_, &uc16_resource_);
9352 morphs_++;
9353 }
9354 i::OS::Sleep(1);
9355 }
9356 morph_success_ = true;
9357 }
9358
9359 void LongRunningRegExp() {
9360 block_->Signal(); // Enable morphing thread on next preemption.
9361 while (morphs_during_regexp_ < kRequiredModifications &&
9362 morphs_ < kMaxModifications) {
9363 int morphs_before = morphs_;
9364 {
Steve Block791712a2010-08-27 10:21:07 +01009365 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +00009366 // Match 15-30 "a"'s against 14 and a "b".
9367 const char* c_source =
9368 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9369 ".exec(input) === null";
9370 Local<String> source = String::New(c_source);
9371 Local<Script> script = Script::Compile(source);
9372 Local<Value> result = script->Run();
9373 CHECK(result->IsTrue());
9374 }
9375 int morphs_after = morphs_;
9376 morphs_during_regexp_ += morphs_after - morphs_before;
9377 }
9378 regexp_success_ = true;
9379 }
9380
9381 i::uc16 two_byte_content_[15];
9382 i::Semaphore* block_;
9383 int morphs_;
9384 int morphs_during_regexp_;
9385 bool regexp_success_;
9386 bool morph_success_;
9387 i::Handle<i::String> input_;
9388 AsciiVectorResource ascii_resource_;
9389 UC16VectorResource uc16_resource_;
9390};
9391
9392
9393// Test that a regular expression execution can be interrupted and
9394// the string changed without failing.
9395TEST(RegExpStringModification) {
9396 v8::Locker lock;
9397 v8::V8::Initialize();
9398 v8::HandleScope scope;
9399 Local<Context> local_env;
9400 {
9401 LocalContext env;
9402 local_env = env.local();
9403 }
9404
9405 // Local context should still be live.
9406 CHECK(!local_env.IsEmpty());
9407 local_env->Enter();
9408
9409 // Should complete without problems.
9410 RegExpStringModificationTest().RunTest();
9411
9412 local_env->Exit();
9413}
9414
9415
9416// Test that we can set a property on the global object even if there
9417// is a read-only property in the prototype chain.
9418TEST(ReadOnlyPropertyInGlobalProto) {
9419 v8::HandleScope scope;
9420 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9421 LocalContext context(0, templ);
9422 v8::Handle<v8::Object> global = context->Global();
9423 v8::Handle<v8::Object> global_proto =
9424 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9425 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9426 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9427 // Check without 'eval' or 'with'.
9428 v8::Handle<v8::Value> res =
9429 CompileRun("function f() { x = 42; return x; }; f()");
9430 // Check with 'eval'.
9431 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9432 CHECK_EQ(v8::Integer::New(42), res);
9433 // Check with 'with'.
9434 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9435 CHECK_EQ(v8::Integer::New(42), res);
9436}
9437
9438static int force_set_set_count = 0;
9439static int force_set_get_count = 0;
9440bool pass_on_get = false;
9441
9442static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9443 const v8::AccessorInfo& info) {
9444 force_set_get_count++;
9445 if (pass_on_get) {
9446 return v8::Handle<v8::Value>();
9447 } else {
9448 return v8::Int32::New(3);
9449 }
9450}
9451
9452static void ForceSetSetter(v8::Local<v8::String> name,
9453 v8::Local<v8::Value> value,
9454 const v8::AccessorInfo& info) {
9455 force_set_set_count++;
9456}
9457
9458static v8::Handle<v8::Value> ForceSetInterceptSetter(
9459 v8::Local<v8::String> name,
9460 v8::Local<v8::Value> value,
9461 const v8::AccessorInfo& info) {
9462 force_set_set_count++;
9463 return v8::Undefined();
9464}
9465
9466TEST(ForceSet) {
9467 force_set_get_count = 0;
9468 force_set_set_count = 0;
9469 pass_on_get = false;
9470
9471 v8::HandleScope scope;
9472 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9473 v8::Handle<v8::String> access_property = v8::String::New("a");
9474 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9475 LocalContext context(NULL, templ);
9476 v8::Handle<v8::Object> global = context->Global();
9477
9478 // Ordinary properties
9479 v8::Handle<v8::String> simple_property = v8::String::New("p");
9480 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9481 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9482 // This should fail because the property is read-only
9483 global->Set(simple_property, v8::Int32::New(5));
9484 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9485 // This should succeed even though the property is read-only
9486 global->ForceSet(simple_property, v8::Int32::New(6));
9487 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9488
9489 // Accessors
9490 CHECK_EQ(0, force_set_set_count);
9491 CHECK_EQ(0, force_set_get_count);
9492 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9493 // CHECK_EQ the property shouldn't override it, just call the setter
9494 // which in this case does nothing.
9495 global->Set(access_property, v8::Int32::New(7));
9496 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9497 CHECK_EQ(1, force_set_set_count);
9498 CHECK_EQ(2, force_set_get_count);
9499 // Forcing the property to be set should override the accessor without
9500 // calling it
9501 global->ForceSet(access_property, v8::Int32::New(8));
9502 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9503 CHECK_EQ(1, force_set_set_count);
9504 CHECK_EQ(2, force_set_get_count);
9505}
9506
9507TEST(ForceSetWithInterceptor) {
9508 force_set_get_count = 0;
9509 force_set_set_count = 0;
9510 pass_on_get = false;
9511
9512 v8::HandleScope scope;
9513 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9514 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9515 LocalContext context(NULL, templ);
9516 v8::Handle<v8::Object> global = context->Global();
9517
9518 v8::Handle<v8::String> some_property = v8::String::New("a");
9519 CHECK_EQ(0, force_set_set_count);
9520 CHECK_EQ(0, force_set_get_count);
9521 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9522 // Setting the property shouldn't override it, just call the setter
9523 // which in this case does nothing.
9524 global->Set(some_property, v8::Int32::New(7));
9525 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9526 CHECK_EQ(1, force_set_set_count);
9527 CHECK_EQ(2, force_set_get_count);
9528 // Getting the property when the interceptor returns an empty handle
9529 // should yield undefined, since the property isn't present on the
9530 // object itself yet.
9531 pass_on_get = true;
9532 CHECK(global->Get(some_property)->IsUndefined());
9533 CHECK_EQ(1, force_set_set_count);
9534 CHECK_EQ(3, force_set_get_count);
9535 // Forcing the property to be set should cause the value to be
9536 // set locally without calling the interceptor.
9537 global->ForceSet(some_property, v8::Int32::New(8));
9538 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9539 CHECK_EQ(1, force_set_set_count);
9540 CHECK_EQ(4, force_set_get_count);
9541 // Reenabling the interceptor should cause it to take precedence over
9542 // the property
9543 pass_on_get = false;
9544 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9545 CHECK_EQ(1, force_set_set_count);
9546 CHECK_EQ(5, force_set_get_count);
9547 // The interceptor should also work for other properties
9548 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9549 CHECK_EQ(1, force_set_set_count);
9550 CHECK_EQ(6, force_set_get_count);
9551}
9552
9553
9554THREADED_TEST(ForceDelete) {
9555 v8::HandleScope scope;
9556 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9557 LocalContext context(NULL, templ);
9558 v8::Handle<v8::Object> global = context->Global();
9559
9560 // Ordinary properties
9561 v8::Handle<v8::String> simple_property = v8::String::New("p");
9562 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9563 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9564 // This should fail because the property is dont-delete.
9565 CHECK(!global->Delete(simple_property));
9566 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9567 // This should succeed even though the property is dont-delete.
9568 CHECK(global->ForceDelete(simple_property));
9569 CHECK(global->Get(simple_property)->IsUndefined());
9570}
9571
9572
9573static int force_delete_interceptor_count = 0;
9574static bool pass_on_delete = false;
9575
9576
9577static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9578 v8::Local<v8::String> name,
9579 const v8::AccessorInfo& info) {
9580 force_delete_interceptor_count++;
9581 if (pass_on_delete) {
9582 return v8::Handle<v8::Boolean>();
9583 } else {
9584 return v8::True();
9585 }
9586}
9587
9588
9589THREADED_TEST(ForceDeleteWithInterceptor) {
9590 force_delete_interceptor_count = 0;
9591 pass_on_delete = false;
9592
9593 v8::HandleScope scope;
9594 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9595 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9596 LocalContext context(NULL, templ);
9597 v8::Handle<v8::Object> global = context->Global();
9598
9599 v8::Handle<v8::String> some_property = v8::String::New("a");
9600 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9601
9602 // Deleting a property should get intercepted and nothing should
9603 // happen.
9604 CHECK_EQ(0, force_delete_interceptor_count);
9605 CHECK(global->Delete(some_property));
9606 CHECK_EQ(1, force_delete_interceptor_count);
9607 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9608 // Deleting the property when the interceptor returns an empty
9609 // handle should not delete the property since it is DontDelete.
9610 pass_on_delete = true;
9611 CHECK(!global->Delete(some_property));
9612 CHECK_EQ(2, force_delete_interceptor_count);
9613 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9614 // Forcing the property to be deleted should delete the value
9615 // without calling the interceptor.
9616 CHECK(global->ForceDelete(some_property));
9617 CHECK(global->Get(some_property)->IsUndefined());
9618 CHECK_EQ(2, force_delete_interceptor_count);
9619}
9620
9621
9622// Make sure that forcing a delete invalidates any IC stubs, so we
9623// don't read the hole value.
9624THREADED_TEST(ForceDeleteIC) {
9625 v8::HandleScope scope;
9626 LocalContext context;
9627 // Create a DontDelete variable on the global object.
9628 CompileRun("this.__proto__ = { foo: 'horse' };"
9629 "var foo = 'fish';"
9630 "function f() { return foo.length; }");
9631 // Initialize the IC for foo in f.
9632 CompileRun("for (var i = 0; i < 4; i++) f();");
9633 // Make sure the value of foo is correct before the deletion.
9634 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9635 // Force the deletion of foo.
9636 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9637 // Make sure the value for foo is read from the prototype, and that
9638 // we don't get in trouble with reading the deleted cell value
9639 // sentinel.
9640 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9641}
9642
9643
9644v8::Persistent<Context> calling_context0;
9645v8::Persistent<Context> calling_context1;
9646v8::Persistent<Context> calling_context2;
9647
9648
9649// Check that the call to the callback is initiated in
9650// calling_context2, the directly calling context is calling_context1
9651// and the callback itself is in calling_context0.
9652static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9653 ApiTestFuzzer::Fuzz();
9654 CHECK(Context::GetCurrent() == calling_context0);
9655 CHECK(Context::GetCalling() == calling_context1);
9656 CHECK(Context::GetEntered() == calling_context2);
9657 return v8::Integer::New(42);
9658}
9659
9660
9661THREADED_TEST(GetCallingContext) {
9662 v8::HandleScope scope;
9663
9664 calling_context0 = Context::New();
9665 calling_context1 = Context::New();
9666 calling_context2 = Context::New();
9667
9668 // Allow cross-domain access.
9669 Local<String> token = v8_str("<security token>");
9670 calling_context0->SetSecurityToken(token);
9671 calling_context1->SetSecurityToken(token);
9672 calling_context2->SetSecurityToken(token);
9673
9674 // Create an object with a C++ callback in context0.
9675 calling_context0->Enter();
9676 Local<v8::FunctionTemplate> callback_templ =
9677 v8::FunctionTemplate::New(GetCallingContextCallback);
9678 calling_context0->Global()->Set(v8_str("callback"),
9679 callback_templ->GetFunction());
9680 calling_context0->Exit();
9681
9682 // Expose context0 in context1 and setup a function that calls the
9683 // callback function.
9684 calling_context1->Enter();
9685 calling_context1->Global()->Set(v8_str("context0"),
9686 calling_context0->Global());
9687 CompileRun("function f() { context0.callback() }");
9688 calling_context1->Exit();
9689
9690 // Expose context1 in context2 and call the callback function in
9691 // context0 indirectly through f in context1.
9692 calling_context2->Enter();
9693 calling_context2->Global()->Set(v8_str("context1"),
9694 calling_context1->Global());
9695 CompileRun("context1.f()");
9696 calling_context2->Exit();
9697
9698 // Dispose the contexts to allow them to be garbage collected.
9699 calling_context0.Dispose();
9700 calling_context1.Dispose();
9701 calling_context2.Dispose();
9702 calling_context0.Clear();
9703 calling_context1.Clear();
9704 calling_context2.Clear();
9705}
9706
9707
9708// Check that a variable declaration with no explicit initialization
9709// value does not shadow an existing property in the prototype chain.
9710//
9711// This is consistent with Firefox and Safari.
9712//
9713// See http://crbug.com/12548.
9714THREADED_TEST(InitGlobalVarInProtoChain) {
9715 v8::HandleScope scope;
9716 LocalContext context;
9717 // Introduce a variable in the prototype chain.
9718 CompileRun("__proto__.x = 42");
9719 v8::Handle<v8::Value> result = CompileRun("var x; x");
9720 CHECK(!result->IsUndefined());
9721 CHECK_EQ(42, result->Int32Value());
9722}
9723
9724
9725// Regression test for issue 398.
9726// If a function is added to an object, creating a constant function
9727// field, and the result is cloned, replacing the constant function on the
9728// original should not affect the clone.
9729// See http://code.google.com/p/v8/issues/detail?id=398
9730THREADED_TEST(ReplaceConstantFunction) {
9731 v8::HandleScope scope;
9732 LocalContext context;
9733 v8::Handle<v8::Object> obj = v8::Object::New();
9734 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9735 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9736 obj->Set(foo_string, func_templ->GetFunction());
9737 v8::Handle<v8::Object> obj_clone = obj->Clone();
9738 obj_clone->Set(foo_string, v8::String::New("Hello"));
9739 CHECK(!obj->Get(foo_string)->IsUndefined());
9740}
9741
9742
9743// Regression test for http://crbug.com/16276.
9744THREADED_TEST(Regress16276) {
9745 v8::HandleScope scope;
9746 LocalContext context;
9747 // Force the IC in f to be a dictionary load IC.
9748 CompileRun("function f(obj) { return obj.x; }\n"
9749 "var obj = { x: { foo: 42 }, y: 87 };\n"
9750 "var x = obj.x;\n"
9751 "delete obj.y;\n"
9752 "for (var i = 0; i < 5; i++) f(obj);");
9753 // Detach the global object to make 'this' refer directly to the
9754 // global object (not the proxy), and make sure that the dictionary
9755 // load IC doesn't mess up loading directly from the global object.
9756 context->DetachGlobal();
9757 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9758}
9759
9760
9761THREADED_TEST(PixelArray) {
9762 v8::HandleScope scope;
9763 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009764 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009765 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9766 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9767 pixel_data);
9768 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9769 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009770 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009771 }
9772 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9773 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009774 CHECK_EQ(i % 256, pixels->get(i));
9775 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009776 }
9777
9778 v8::Handle<v8::Object> obj = v8::Object::New();
9779 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9780 // Set the elements to be the pixels.
9781 // jsobj->set_elements(*pixels);
9782 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
John Reck59135872010-11-02 12:39:01 -07009783 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009784 obj->Set(v8_str("field"), v8::Int32::New(1503));
9785 context->Global()->Set(v8_str("pixels"), obj);
9786 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9787 CHECK_EQ(1503, result->Int32Value());
9788 result = CompileRun("pixels[1]");
9789 CHECK_EQ(1, result->Int32Value());
9790
9791 result = CompileRun("var sum = 0;"
9792 "for (var i = 0; i < 8; i++) {"
9793 " sum += pixels[i] = pixels[i] = -i;"
9794 "}"
9795 "sum;");
9796 CHECK_EQ(-28, result->Int32Value());
9797
9798 result = CompileRun("var sum = 0;"
9799 "for (var i = 0; i < 8; i++) {"
9800 " sum += pixels[i] = pixels[i] = 0;"
9801 "}"
9802 "sum;");
9803 CHECK_EQ(0, result->Int32Value());
9804
9805 result = CompileRun("var sum = 0;"
9806 "for (var i = 0; i < 8; i++) {"
9807 " sum += pixels[i] = pixels[i] = 255;"
9808 "}"
9809 "sum;");
9810 CHECK_EQ(8 * 255, result->Int32Value());
9811
9812 result = CompileRun("var sum = 0;"
9813 "for (var i = 0; i < 8; i++) {"
9814 " sum += pixels[i] = pixels[i] = 256 + i;"
9815 "}"
9816 "sum;");
9817 CHECK_EQ(2076, result->Int32Value());
9818
9819 result = CompileRun("var sum = 0;"
9820 "for (var i = 0; i < 8; i++) {"
9821 " sum += pixels[i] = pixels[i] = i;"
9822 "}"
9823 "sum;");
9824 CHECK_EQ(28, result->Int32Value());
9825
9826 result = CompileRun("var sum = 0;"
9827 "for (var i = 0; i < 8; i++) {"
9828 " sum += pixels[i];"
9829 "}"
9830 "sum;");
9831 CHECK_EQ(28, result->Int32Value());
9832
9833 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9834 i::SetElement(jsobj, 1, value);
John Reck59135872010-11-02 12:39:01 -07009835 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009836 *value.location() = i::Smi::FromInt(256);
9837 i::SetElement(jsobj, 1, value);
John Reck59135872010-11-02 12:39:01 -07009838 CHECK_EQ(255,
9839 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009840 *value.location() = i::Smi::FromInt(-1);
9841 i::SetElement(jsobj, 1, value);
John Reck59135872010-11-02 12:39:01 -07009842 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009843
9844 result = CompileRun("for (var i = 0; i < 8; i++) {"
9845 " pixels[i] = (i * 65) - 109;"
9846 "}"
9847 "pixels[1] + pixels[6];");
9848 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -07009849 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
9850 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
9851 CHECK_EQ(21,
9852 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
9853 CHECK_EQ(86,
9854 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
9855 CHECK_EQ(151,
9856 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
9857 CHECK_EQ(216,
9858 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
9859 CHECK_EQ(255,
9860 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
9861 CHECK_EQ(255,
9862 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009863 result = CompileRun("var sum = 0;"
9864 "for (var i = 0; i < 8; i++) {"
9865 " sum += pixels[i];"
9866 "}"
9867 "sum;");
9868 CHECK_EQ(984, result->Int32Value());
9869
9870 result = CompileRun("for (var i = 0; i < 8; i++) {"
9871 " pixels[i] = (i * 1.1);"
9872 "}"
9873 "pixels[1] + pixels[6];");
9874 CHECK_EQ(8, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -07009875 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value());
9876 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value());
9877 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value());
9878 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value());
9879 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value());
9880 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
9881 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
9882 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009883
9884 result = CompileRun("for (var i = 0; i < 8; i++) {"
9885 " pixels[7] = undefined;"
9886 "}"
9887 "pixels[7];");
9888 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -07009889 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009890
9891 result = CompileRun("for (var i = 0; i < 8; i++) {"
9892 " pixels[6] = '2.3';"
9893 "}"
9894 "pixels[6];");
9895 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -07009896 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009897
9898 result = CompileRun("for (var i = 0; i < 8; i++) {"
9899 " pixels[5] = NaN;"
9900 "}"
9901 "pixels[5];");
9902 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -07009903 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009904
9905 result = CompileRun("for (var i = 0; i < 8; i++) {"
9906 " pixels[8] = Infinity;"
9907 "}"
9908 "pixels[8];");
9909 CHECK_EQ(255, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -07009910 CHECK_EQ(255,
9911 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009912
9913 result = CompileRun("for (var i = 0; i < 8; i++) {"
9914 " pixels[9] = -Infinity;"
9915 "}"
9916 "pixels[9];");
9917 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -07009918 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value());
Steve Blocka7e24c12009-10-30 11:49:00 +00009919
9920 result = CompileRun("pixels[3] = 33;"
9921 "delete pixels[3];"
9922 "pixels[3];");
9923 CHECK_EQ(33, result->Int32Value());
9924
9925 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9926 "pixels[2] = 12; pixels[3] = 13;"
9927 "pixels.__defineGetter__('2',"
9928 "function() { return 120; });"
9929 "pixels[2];");
9930 CHECK_EQ(12, result->Int32Value());
9931
9932 result = CompileRun("var js_array = new Array(40);"
9933 "js_array[0] = 77;"
9934 "js_array;");
9935 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9936
9937 result = CompileRun("pixels[1] = 23;"
9938 "pixels.__proto__ = [];"
9939 "js_array.__proto__ = pixels;"
9940 "js_array.concat(pixels);");
9941 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9942 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9943
9944 result = CompileRun("pixels[1] = 23;");
9945 CHECK_EQ(23, result->Int32Value());
9946
Steve Blockd0582a62009-12-15 09:54:21 +00009947 // Test for index greater than 255. Regression test for:
9948 // http://code.google.com/p/chromium/issues/detail?id=26337.
9949 result = CompileRun("pixels[256] = 255;");
9950 CHECK_EQ(255, result->Int32Value());
9951 result = CompileRun("var i = 0;"
9952 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9953 "i");
9954 CHECK_EQ(255, result->Int32Value());
9955
Steve Blocka7e24c12009-10-30 11:49:00 +00009956 free(pixel_data);
9957}
9958
9959
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009960THREADED_TEST(PixelArrayInfo) {
9961 v8::HandleScope scope;
9962 LocalContext context;
9963 for (int size = 0; size < 100; size += 10) {
9964 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9965 v8::Handle<v8::Object> obj = v8::Object::New();
9966 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9967 CHECK(obj->HasIndexedPropertiesInPixelData());
9968 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9969 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9970 free(pixel_data);
9971 }
9972}
9973
9974
9975static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9976 switch (array_type) {
9977 case v8::kExternalByteArray:
9978 case v8::kExternalUnsignedByteArray:
9979 return 1;
9980 break;
9981 case v8::kExternalShortArray:
9982 case v8::kExternalUnsignedShortArray:
9983 return 2;
9984 break;
9985 case v8::kExternalIntArray:
9986 case v8::kExternalUnsignedIntArray:
9987 case v8::kExternalFloatArray:
9988 return 4;
9989 break;
9990 default:
9991 UNREACHABLE();
9992 return -1;
9993 }
9994 UNREACHABLE();
9995 return -1;
9996}
9997
9998
Steve Block3ce2e202009-11-05 08:53:23 +00009999template <class ExternalArrayClass, class ElementType>
10000static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
10001 int64_t low,
10002 int64_t high) {
10003 v8::HandleScope scope;
10004 LocalContext context;
10005 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010006 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +000010007 ElementType* array_data =
10008 static_cast<ElementType*>(malloc(kElementCount * element_size));
10009 i::Handle<ExternalArrayClass> array =
10010 i::Handle<ExternalArrayClass>::cast(
10011 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
10012 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10013 for (int i = 0; i < kElementCount; i++) {
10014 array->set(i, static_cast<ElementType>(i));
10015 }
10016 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10017 for (int i = 0; i < kElementCount; i++) {
10018 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
10019 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
10020 }
10021
10022 v8::Handle<v8::Object> obj = v8::Object::New();
10023 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
10024 // Set the elements to be the external array.
10025 obj->SetIndexedPropertiesToExternalArrayData(array_data,
10026 array_type,
10027 kElementCount);
John Reck59135872010-11-02 12:39:01 -070010028 CHECK_EQ(
10029 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000010030 obj->Set(v8_str("field"), v8::Int32::New(1503));
10031 context->Global()->Set(v8_str("ext_array"), obj);
10032 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
10033 CHECK_EQ(1503, result->Int32Value());
10034 result = CompileRun("ext_array[1]");
10035 CHECK_EQ(1, result->Int32Value());
10036
10037 // Check pass through of assigned smis
10038 result = CompileRun("var sum = 0;"
10039 "for (var i = 0; i < 8; i++) {"
10040 " sum += ext_array[i] = ext_array[i] = -i;"
10041 "}"
10042 "sum;");
10043 CHECK_EQ(-28, result->Int32Value());
10044
10045 // Check assigned smis
10046 result = CompileRun("for (var i = 0; i < 8; i++) {"
10047 " ext_array[i] = i;"
10048 "}"
10049 "var sum = 0;"
10050 "for (var i = 0; i < 8; i++) {"
10051 " sum += ext_array[i];"
10052 "}"
10053 "sum;");
10054 CHECK_EQ(28, result->Int32Value());
10055
10056 // Check assigned smis in reverse order
10057 result = CompileRun("for (var i = 8; --i >= 0; ) {"
10058 " ext_array[i] = i;"
10059 "}"
10060 "var sum = 0;"
10061 "for (var i = 0; i < 8; i++) {"
10062 " sum += ext_array[i];"
10063 "}"
10064 "sum;");
10065 CHECK_EQ(28, result->Int32Value());
10066
10067 // Check pass through of assigned HeapNumbers
10068 result = CompileRun("var sum = 0;"
10069 "for (var i = 0; i < 16; i+=2) {"
10070 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
10071 "}"
10072 "sum;");
10073 CHECK_EQ(-28, result->Int32Value());
10074
10075 // Check assigned HeapNumbers
10076 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
10077 " ext_array[i] = (i * 0.5);"
10078 "}"
10079 "var sum = 0;"
10080 "for (var i = 0; i < 16; i+=2) {"
10081 " sum += ext_array[i];"
10082 "}"
10083 "sum;");
10084 CHECK_EQ(28, result->Int32Value());
10085
10086 // Check assigned HeapNumbers in reverse order
10087 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
10088 " ext_array[i] = (i * 0.5);"
10089 "}"
10090 "var sum = 0;"
10091 "for (var i = 0; i < 16; i+=2) {"
10092 " sum += ext_array[i];"
10093 "}"
10094 "sum;");
10095 CHECK_EQ(28, result->Int32Value());
10096
10097 i::ScopedVector<char> test_buf(1024);
10098
10099 // Check legal boundary conditions.
10100 // The repeated loads and stores ensure the ICs are exercised.
10101 const char* boundary_program =
10102 "var res = 0;"
10103 "for (var i = 0; i < 16; i++) {"
10104 " ext_array[i] = %lld;"
10105 " if (i > 8) {"
10106 " res = ext_array[i];"
10107 " }"
10108 "}"
10109 "res;";
10110 i::OS::SNPrintF(test_buf,
10111 boundary_program,
10112 low);
10113 result = CompileRun(test_buf.start());
10114 CHECK_EQ(low, result->IntegerValue());
10115
10116 i::OS::SNPrintF(test_buf,
10117 boundary_program,
10118 high);
10119 result = CompileRun(test_buf.start());
10120 CHECK_EQ(high, result->IntegerValue());
10121
10122 // Check misprediction of type in IC.
10123 result = CompileRun("var tmp_array = ext_array;"
10124 "var sum = 0;"
10125 "for (var i = 0; i < 8; i++) {"
10126 " tmp_array[i] = i;"
10127 " sum += tmp_array[i];"
10128 " if (i == 4) {"
10129 " tmp_array = {};"
10130 " }"
10131 "}"
10132 "sum;");
10133 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10134 CHECK_EQ(28, result->Int32Value());
10135
10136 // Make sure out-of-range loads do not throw.
10137 i::OS::SNPrintF(test_buf,
10138 "var caught_exception = false;"
10139 "try {"
10140 " ext_array[%d];"
10141 "} catch (e) {"
10142 " caught_exception = true;"
10143 "}"
10144 "caught_exception;",
10145 kElementCount);
10146 result = CompileRun(test_buf.start());
10147 CHECK_EQ(false, result->BooleanValue());
10148
10149 // Make sure out-of-range stores do not throw.
10150 i::OS::SNPrintF(test_buf,
10151 "var caught_exception = false;"
10152 "try {"
10153 " ext_array[%d] = 1;"
10154 "} catch (e) {"
10155 " caught_exception = true;"
10156 "}"
10157 "caught_exception;",
10158 kElementCount);
10159 result = CompileRun(test_buf.start());
10160 CHECK_EQ(false, result->BooleanValue());
10161
10162 // Check other boundary conditions, values and operations.
10163 result = CompileRun("for (var i = 0; i < 8; i++) {"
10164 " ext_array[7] = undefined;"
10165 "}"
10166 "ext_array[7];");
10167 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010168 CHECK_EQ(
10169 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000010170
10171 result = CompileRun("for (var i = 0; i < 8; i++) {"
10172 " ext_array[6] = '2.3';"
10173 "}"
10174 "ext_array[6];");
10175 CHECK_EQ(2, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010176 CHECK_EQ(
10177 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number()));
Steve Block3ce2e202009-11-05 08:53:23 +000010178
10179 if (array_type != v8::kExternalFloatArray) {
10180 // Though the specification doesn't state it, be explicit about
10181 // converting NaNs and +/-Infinity to zero.
10182 result = CompileRun("for (var i = 0; i < 8; i++) {"
10183 " ext_array[i] = 5;"
10184 "}"
10185 "for (var i = 0; i < 8; i++) {"
10186 " ext_array[i] = NaN;"
10187 "}"
10188 "ext_array[5];");
10189 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010190 CHECK_EQ(0,
10191 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000010192
10193 result = CompileRun("for (var i = 0; i < 8; i++) {"
10194 " ext_array[i] = 5;"
10195 "}"
10196 "for (var i = 0; i < 8; i++) {"
10197 " ext_array[i] = Infinity;"
10198 "}"
10199 "ext_array[5];");
10200 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010201 CHECK_EQ(0,
10202 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000010203
10204 result = CompileRun("for (var i = 0; i < 8; i++) {"
10205 " ext_array[i] = 5;"
10206 "}"
10207 "for (var i = 0; i < 8; i++) {"
10208 " ext_array[i] = -Infinity;"
10209 "}"
10210 "ext_array[5];");
10211 CHECK_EQ(0, result->Int32Value());
John Reck59135872010-11-02 12:39:01 -070010212 CHECK_EQ(0,
10213 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value());
Steve Block3ce2e202009-11-05 08:53:23 +000010214 }
10215
10216 result = CompileRun("ext_array[3] = 33;"
10217 "delete ext_array[3];"
10218 "ext_array[3];");
10219 CHECK_EQ(33, result->Int32Value());
10220
10221 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
10222 "ext_array[2] = 12; ext_array[3] = 13;"
10223 "ext_array.__defineGetter__('2',"
10224 "function() { return 120; });"
10225 "ext_array[2];");
10226 CHECK_EQ(12, result->Int32Value());
10227
10228 result = CompileRun("var js_array = new Array(40);"
10229 "js_array[0] = 77;"
10230 "js_array;");
10231 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10232
10233 result = CompileRun("ext_array[1] = 23;"
10234 "ext_array.__proto__ = [];"
10235 "js_array.__proto__ = ext_array;"
10236 "js_array.concat(ext_array);");
10237 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10238 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10239
10240 result = CompileRun("ext_array[1] = 23;");
10241 CHECK_EQ(23, result->Int32Value());
10242
Steve Blockd0582a62009-12-15 09:54:21 +000010243 // Test more complex manipulations which cause eax to contain values
10244 // that won't be completely overwritten by loads from the arrays.
10245 // This catches bugs in the instructions used for the KeyedLoadIC
10246 // for byte and word types.
10247 {
10248 const int kXSize = 300;
10249 const int kYSize = 300;
10250 const int kLargeElementCount = kXSize * kYSize * 4;
10251 ElementType* large_array_data =
10252 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
10253 i::Handle<ExternalArrayClass> large_array =
10254 i::Handle<ExternalArrayClass>::cast(
10255 i::Factory::NewExternalArray(kLargeElementCount,
10256 array_type,
10257 array_data));
10258 v8::Handle<v8::Object> large_obj = v8::Object::New();
10259 // Set the elements to be the external array.
10260 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
10261 array_type,
10262 kLargeElementCount);
10263 context->Global()->Set(v8_str("large_array"), large_obj);
10264 // Initialize contents of a few rows.
10265 for (int x = 0; x < 300; x++) {
10266 int row = 0;
10267 int offset = row * 300 * 4;
10268 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10269 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10270 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10271 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10272 row = 150;
10273 offset = row * 300 * 4;
10274 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10275 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10276 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10277 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10278 row = 298;
10279 offset = row * 300 * 4;
10280 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10281 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10282 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10283 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10284 }
10285 // The goal of the code below is to make "offset" large enough
10286 // that the computation of the index (which goes into eax) has
10287 // high bits set which will not be overwritten by a byte or short
10288 // load.
10289 result = CompileRun("var failed = false;"
10290 "var offset = 0;"
10291 "for (var i = 0; i < 300; i++) {"
10292 " if (large_array[4 * i] != 127 ||"
10293 " large_array[4 * i + 1] != 0 ||"
10294 " large_array[4 * i + 2] != 0 ||"
10295 " large_array[4 * i + 3] != 127) {"
10296 " failed = true;"
10297 " }"
10298 "}"
10299 "offset = 150 * 300 * 4;"
10300 "for (var i = 0; i < 300; i++) {"
10301 " if (large_array[offset + 4 * i] != 127 ||"
10302 " large_array[offset + 4 * i + 1] != 0 ||"
10303 " large_array[offset + 4 * i + 2] != 0 ||"
10304 " large_array[offset + 4 * i + 3] != 127) {"
10305 " failed = true;"
10306 " }"
10307 "}"
10308 "offset = 298 * 300 * 4;"
10309 "for (var i = 0; i < 300; i++) {"
10310 " if (large_array[offset + 4 * i] != 127 ||"
10311 " large_array[offset + 4 * i + 1] != 0 ||"
10312 " large_array[offset + 4 * i + 2] != 0 ||"
10313 " large_array[offset + 4 * i + 3] != 127) {"
10314 " failed = true;"
10315 " }"
10316 "}"
10317 "!failed;");
10318 CHECK_EQ(true, result->BooleanValue());
10319 free(large_array_data);
10320 }
10321
Steve Block3ce2e202009-11-05 08:53:23 +000010322 free(array_data);
10323}
10324
10325
10326THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010327 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010328 v8::kExternalByteArray,
10329 -128,
10330 127);
10331}
10332
10333
10334THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010335 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010336 v8::kExternalUnsignedByteArray,
10337 0,
10338 255);
10339}
10340
10341
10342THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010343 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010344 v8::kExternalShortArray,
10345 -32768,
10346 32767);
10347}
10348
10349
10350THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010351 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010352 v8::kExternalUnsignedShortArray,
10353 0,
10354 65535);
10355}
10356
10357
10358THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010359 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010360 v8::kExternalIntArray,
10361 INT_MIN, // -2147483648
10362 INT_MAX); // 2147483647
10363}
10364
10365
10366THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010367 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010368 v8::kExternalUnsignedIntArray,
10369 0,
10370 UINT_MAX); // 4294967295
10371}
10372
10373
10374THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010375 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000010376 v8::kExternalFloatArray,
10377 -500,
10378 500);
10379}
10380
10381
10382THREADED_TEST(ExternalArrays) {
10383 TestExternalByteArray();
10384 TestExternalUnsignedByteArray();
10385 TestExternalShortArray();
10386 TestExternalUnsignedShortArray();
10387 TestExternalIntArray();
10388 TestExternalUnsignedIntArray();
10389 TestExternalFloatArray();
10390}
10391
10392
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010393void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10394 v8::HandleScope scope;
10395 LocalContext context;
10396 for (int size = 0; size < 100; size += 10) {
10397 int element_size = ExternalArrayElementSize(array_type);
10398 void* external_data = malloc(size * element_size);
10399 v8::Handle<v8::Object> obj = v8::Object::New();
10400 obj->SetIndexedPropertiesToExternalArrayData(
10401 external_data, array_type, size);
10402 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10403 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10404 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10405 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10406 free(external_data);
10407 }
10408}
10409
10410
10411THREADED_TEST(ExternalArrayInfo) {
10412 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10413 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10414 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10415 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10416 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10417 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10418 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10419}
10420
10421
Steve Blocka7e24c12009-10-30 11:49:00 +000010422THREADED_TEST(ScriptContextDependence) {
10423 v8::HandleScope scope;
10424 LocalContext c1;
10425 const char *source = "foo";
10426 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10427 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10428 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10429 CHECK_EQ(dep->Run()->Int32Value(), 100);
10430 CHECK_EQ(indep->Run()->Int32Value(), 100);
10431 LocalContext c2;
10432 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10433 CHECK_EQ(dep->Run()->Int32Value(), 100);
10434 CHECK_EQ(indep->Run()->Int32Value(), 101);
10435}
10436
10437
10438THREADED_TEST(StackTrace) {
10439 v8::HandleScope scope;
10440 LocalContext context;
10441 v8::TryCatch try_catch;
10442 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10443 v8::Handle<v8::String> src = v8::String::New(source);
10444 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10445 v8::Script::New(src, origin)->Run();
10446 CHECK(try_catch.HasCaught());
10447 v8::String::Utf8Value stack(try_catch.StackTrace());
10448 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10449}
10450
10451
Kristian Monsen25f61362010-05-21 11:50:48 +010010452// Checks that a StackFrame has certain expected values.
10453void checkStackFrame(const char* expected_script_name,
10454 const char* expected_func_name, int expected_line_number,
10455 int expected_column, bool is_eval, bool is_constructor,
10456 v8::Handle<v8::StackFrame> frame) {
10457 v8::HandleScope scope;
10458 v8::String::Utf8Value func_name(frame->GetFunctionName());
10459 v8::String::Utf8Value script_name(frame->GetScriptName());
10460 if (*script_name == NULL) {
10461 // The situation where there is no associated script, like for evals.
10462 CHECK(expected_script_name == NULL);
10463 } else {
10464 CHECK(strstr(*script_name, expected_script_name) != NULL);
10465 }
10466 CHECK(strstr(*func_name, expected_func_name) != NULL);
10467 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10468 CHECK_EQ(expected_column, frame->GetColumn());
10469 CHECK_EQ(is_eval, frame->IsEval());
10470 CHECK_EQ(is_constructor, frame->IsConstructor());
10471}
10472
10473
10474v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10475 v8::HandleScope scope;
10476 const char* origin = "capture-stack-trace-test";
10477 const int kOverviewTest = 1;
10478 const int kDetailedTest = 2;
10479
10480 ASSERT(args.Length() == 1);
10481
10482 int testGroup = args[0]->Int32Value();
10483 if (testGroup == kOverviewTest) {
10484 v8::Handle<v8::StackTrace> stackTrace =
10485 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10486 CHECK_EQ(4, stackTrace->GetFrameCount());
10487 checkStackFrame(origin, "bar", 2, 10, false, false,
10488 stackTrace->GetFrame(0));
10489 checkStackFrame(origin, "foo", 6, 3, false, false,
10490 stackTrace->GetFrame(1));
10491 checkStackFrame(NULL, "", 1, 1, false, false,
10492 stackTrace->GetFrame(2));
10493 // The last frame is an anonymous function that has the initial call.
10494 checkStackFrame(origin, "", 8, 7, false, false,
10495 stackTrace->GetFrame(3));
10496
10497 CHECK(stackTrace->AsArray()->IsArray());
10498 } else if (testGroup == kDetailedTest) {
10499 v8::Handle<v8::StackTrace> stackTrace =
10500 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10501 CHECK_EQ(4, stackTrace->GetFrameCount());
10502 checkStackFrame(origin, "bat", 4, 22, false, false,
10503 stackTrace->GetFrame(0));
10504 checkStackFrame(origin, "baz", 8, 3, false, true,
10505 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010506#ifdef ENABLE_DEBUGGER_SUPPORT
10507 bool is_eval = true;
10508#else // ENABLE_DEBUGGER_SUPPORT
10509 bool is_eval = false;
10510#endif // ENABLE_DEBUGGER_SUPPORT
10511
10512 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010513 stackTrace->GetFrame(2));
10514 // The last frame is an anonymous function that has the initial call to foo.
10515 checkStackFrame(origin, "", 10, 1, false, false,
10516 stackTrace->GetFrame(3));
10517
10518 CHECK(stackTrace->AsArray()->IsArray());
10519 }
10520 return v8::Undefined();
10521}
10522
10523
10524// Tests the C++ StackTrace API.
10525THREADED_TEST(CaptureStackTrace) {
10526 v8::HandleScope scope;
10527 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10528 Local<ObjectTemplate> templ = ObjectTemplate::New();
10529 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10530 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10531 LocalContext context(0, templ);
10532
10533 // Test getting OVERVIEW information. Should ignore information that is not
10534 // script name, function name, line number, and column offset.
10535 const char *overview_source =
10536 "function bar() {\n"
10537 " var y; AnalyzeStackInNativeCode(1);\n"
10538 "}\n"
10539 "function foo() {\n"
10540 "\n"
10541 " bar();\n"
10542 "}\n"
10543 "var x;eval('new foo();');";
10544 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10545 v8::Handle<Value> overview_result =
10546 v8::Script::New(overview_src, origin)->Run();
10547 ASSERT(!overview_result.IsEmpty());
10548 ASSERT(overview_result->IsObject());
10549
10550 // Test getting DETAILED information.
10551 const char *detailed_source =
10552 "function bat() {AnalyzeStackInNativeCode(2);\n"
10553 "}\n"
10554 "\n"
10555 "function baz() {\n"
10556 " bat();\n"
10557 "}\n"
10558 "eval('new baz();');";
10559 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10560 // Make the script using a non-zero line and column offset.
10561 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10562 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10563 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10564 v8::Handle<v8::Script> detailed_script(
10565 v8::Script::New(detailed_src, &detailed_origin));
10566 v8::Handle<Value> detailed_result = detailed_script->Run();
10567 ASSERT(!detailed_result.IsEmpty());
10568 ASSERT(detailed_result->IsObject());
10569}
10570
10571
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010572static void StackTraceForUncaughtExceptionListener(
10573 v8::Handle<v8::Message> message,
10574 v8::Handle<Value>) {
10575 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
10576 CHECK_EQ(2, stack_trace->GetFrameCount());
10577 checkStackFrame("origin", "foo", 2, 3, false, false,
10578 stack_trace->GetFrame(0));
10579 checkStackFrame("origin", "bar", 5, 3, false, false,
10580 stack_trace->GetFrame(1));
10581}
10582
10583TEST(CaptureStackTraceForUncaughtException) {
10584 report_count = 0;
10585 v8::HandleScope scope;
10586 LocalContext env;
10587 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
10588 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
10589
10590 Script::Compile(v8_str("function foo() {\n"
10591 " throw 1;\n"
10592 "};\n"
10593 "function bar() {\n"
10594 " foo();\n"
10595 "};"),
10596 v8_str("origin"))->Run();
10597 v8::Local<v8::Object> global = env->Global();
10598 Local<Value> trouble = global->Get(v8_str("bar"));
10599 CHECK(trouble->IsFunction());
10600 Function::Cast(*trouble)->Call(global, 0, NULL);
10601 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
10602 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
10603}
10604
10605
Ben Murdochf87a2032010-10-22 12:50:53 +010010606v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) {
10607 v8::HandleScope scope;
10608 v8::Handle<v8::StackTrace> stackTrace =
10609 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10610 CHECK_EQ(5, stackTrace->GetFrameCount());
10611 v8::Handle<v8::String> url = v8_str("eval_url");
10612 for (int i = 0; i < 3; i++) {
10613 v8::Handle<v8::String> name =
10614 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL();
10615 CHECK(!name.IsEmpty());
10616 CHECK_EQ(url, name);
10617 }
10618 return v8::Undefined();
10619}
10620
10621
10622TEST(SourceURLInStackTrace) {
10623 v8::HandleScope scope;
10624 Local<ObjectTemplate> templ = ObjectTemplate::New();
10625 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"),
10626 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL));
10627 LocalContext context(0, templ);
10628
10629 const char *source =
10630 "function outer() {\n"
10631 "function bar() {\n"
10632 " AnalyzeStackOfEvalWithSourceURL();\n"
10633 "}\n"
10634 "function foo() {\n"
10635 "\n"
10636 " bar();\n"
10637 "}\n"
10638 "foo();\n"
10639 "}\n"
10640 "eval('(' + outer +')()//@ sourceURL=eval_url');";
10641 CHECK(CompileRun(source)->IsUndefined());
10642}
10643
10644
Steve Block3ce2e202009-11-05 08:53:23 +000010645// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010646THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010647 bool rv = false;
10648 for (int i = 0; i < 100; i++) {
10649 rv = v8::V8::IdleNotification();
10650 if (rv)
10651 break;
10652 }
10653 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010654}
10655
10656
10657static uint32_t* stack_limit;
10658
10659static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -080010660 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::real_climit());
Steve Blocka7e24c12009-10-30 11:49:00 +000010661 return v8::Undefined();
10662}
10663
10664
10665// Uses the address of a local variable to determine the stack top now.
10666// Given a size, returns an address that is that far from the current
10667// top of stack.
10668static uint32_t* ComputeStackLimit(uint32_t size) {
10669 uint32_t* answer = &size - (size / sizeof(size));
10670 // If the size is very large and the stack is very near the bottom of
10671 // memory then the calculation above may wrap around and give an address
10672 // that is above the (downwards-growing) stack. In that case we return
10673 // a very low address.
10674 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10675 return answer;
10676}
10677
10678
10679TEST(SetResourceConstraints) {
10680 static const int K = 1024;
10681 uint32_t* set_limit = ComputeStackLimit(128 * K);
10682
10683 // Set stack limit.
10684 v8::ResourceConstraints constraints;
10685 constraints.set_stack_limit(set_limit);
10686 CHECK(v8::SetResourceConstraints(&constraints));
10687
10688 // Execute a script.
10689 v8::HandleScope scope;
10690 LocalContext env;
10691 Local<v8::FunctionTemplate> fun_templ =
10692 v8::FunctionTemplate::New(GetStackLimitCallback);
10693 Local<Function> fun = fun_templ->GetFunction();
10694 env->Global()->Set(v8_str("get_stack_limit"), fun);
10695 CompileRun("get_stack_limit();");
10696
10697 CHECK(stack_limit == set_limit);
10698}
10699
10700
10701TEST(SetResourceConstraintsInThread) {
10702 uint32_t* set_limit;
10703 {
10704 v8::Locker locker;
10705 static const int K = 1024;
10706 set_limit = ComputeStackLimit(128 * K);
10707
10708 // Set stack limit.
10709 v8::ResourceConstraints constraints;
10710 constraints.set_stack_limit(set_limit);
10711 CHECK(v8::SetResourceConstraints(&constraints));
10712
10713 // Execute a script.
10714 v8::HandleScope scope;
10715 LocalContext env;
10716 Local<v8::FunctionTemplate> fun_templ =
10717 v8::FunctionTemplate::New(GetStackLimitCallback);
10718 Local<Function> fun = fun_templ->GetFunction();
10719 env->Global()->Set(v8_str("get_stack_limit"), fun);
10720 CompileRun("get_stack_limit();");
10721
10722 CHECK(stack_limit == set_limit);
10723 }
10724 {
10725 v8::Locker locker;
10726 CHECK(stack_limit == set_limit);
10727 }
10728}
Steve Block3ce2e202009-11-05 08:53:23 +000010729
10730
10731THREADED_TEST(GetHeapStatistics) {
10732 v8::HandleScope scope;
10733 LocalContext c1;
10734 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010735 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10736 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010737 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010738 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10739 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10740}
10741
10742
10743static double DoubleFromBits(uint64_t value) {
10744 double target;
10745#ifdef BIG_ENDIAN_FLOATING_POINT
10746 const int kIntSize = 4;
10747 // Somebody swapped the lower and higher half of doubles.
10748 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10749 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10750#else
10751 memcpy(&target, &value, sizeof(target));
10752#endif
10753 return target;
10754}
10755
10756
10757static uint64_t DoubleToBits(double value) {
10758 uint64_t target;
10759#ifdef BIG_ENDIAN_FLOATING_POINT
10760 const int kIntSize = 4;
10761 // Somebody swapped the lower and higher half of doubles.
10762 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10763 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10764#else
10765 memcpy(&target, &value, sizeof(target));
10766#endif
10767 return target;
10768}
10769
10770
10771static double DoubleToDateTime(double input) {
10772 double date_limit = 864e13;
10773 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10774 return i::OS::nan_value();
10775 }
10776 return (input < 0) ? -(floor(-input)) : floor(input);
10777}
10778
10779// We don't have a consistent way to write 64-bit constants syntactically, so we
10780// split them into two 32-bit constants and combine them programmatically.
10781static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10782 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10783}
10784
10785
10786THREADED_TEST(QuietSignalingNaNs) {
10787 v8::HandleScope scope;
10788 LocalContext context;
10789 v8::TryCatch try_catch;
10790
10791 // Special double values.
10792 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10793 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10794 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10795 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10796 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10797 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10798 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10799
10800 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10801 // on either side of the epoch.
10802 double date_limit = 864e13;
10803
10804 double test_values[] = {
10805 snan,
10806 qnan,
10807 infinity,
10808 max_normal,
10809 date_limit + 1,
10810 date_limit,
10811 min_normal,
10812 max_denormal,
10813 min_denormal,
10814 0,
10815 -0,
10816 -min_denormal,
10817 -max_denormal,
10818 -min_normal,
10819 -date_limit,
10820 -date_limit - 1,
10821 -max_normal,
10822 -infinity,
10823 -qnan,
10824 -snan
10825 };
10826 int num_test_values = 20;
10827
10828 for (int i = 0; i < num_test_values; i++) {
10829 double test_value = test_values[i];
10830
10831 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10832 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10833 double stored_number = number->NumberValue();
10834 if (!IsNaN(test_value)) {
10835 CHECK_EQ(test_value, stored_number);
10836 } else {
10837 uint64_t stored_bits = DoubleToBits(stored_number);
10838 // Check if quiet nan (bits 51..62 all set).
10839 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10840 }
10841
10842 // Check that Date::New preserves non-NaNs in the date range and
10843 // quiets SNaNs.
10844 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10845 double expected_stored_date = DoubleToDateTime(test_value);
10846 double stored_date = date->NumberValue();
10847 if (!IsNaN(expected_stored_date)) {
10848 CHECK_EQ(expected_stored_date, stored_date);
10849 } else {
10850 uint64_t stored_bits = DoubleToBits(stored_date);
10851 // Check if quiet nan (bits 51..62 all set).
10852 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10853 }
10854 }
10855}
10856
10857
10858static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10859 v8::HandleScope scope;
10860 v8::TryCatch tc;
10861 v8::Handle<v8::String> str = args[0]->ToString();
10862 if (tc.HasCaught())
10863 return tc.ReThrow();
10864 return v8::Undefined();
10865}
10866
10867
10868// Test that an exception can be propagated down through a spaghetti
10869// stack using ReThrow.
10870THREADED_TEST(SpaghettiStackReThrow) {
10871 v8::HandleScope scope;
10872 LocalContext context;
10873 context->Global()->Set(
10874 v8::String::New("s"),
10875 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10876 v8::TryCatch try_catch;
10877 CompileRun(
10878 "var i = 0;"
10879 "var o = {"
10880 " toString: function () {"
10881 " if (i == 10) {"
10882 " throw 'Hey!';"
10883 " } else {"
10884 " i++;"
10885 " return s(o);"
10886 " }"
10887 " }"
10888 "};"
10889 "s(o);");
10890 CHECK(try_catch.HasCaught());
10891 v8::String::Utf8Value value(try_catch.Exception());
10892 CHECK_EQ(0, strcmp(*value, "Hey!"));
10893}
10894
10895
Steve Blockd0582a62009-12-15 09:54:21 +000010896TEST(Regress528) {
10897 v8::V8::Initialize();
10898
10899 v8::HandleScope scope;
10900 v8::Persistent<Context> context;
10901 v8::Persistent<Context> other_context;
10902 int gc_count;
10903
10904 // Create a context used to keep the code from aging in the compilation
10905 // cache.
10906 other_context = Context::New();
10907
10908 // Context-dependent context data creates reference from the compilation
10909 // cache to the global object.
10910 const char* source_simple = "1";
10911 context = Context::New();
10912 {
10913 v8::HandleScope scope;
10914
10915 context->Enter();
10916 Local<v8::String> obj = v8::String::New("");
10917 context->SetData(obj);
10918 CompileRun(source_simple);
10919 context->Exit();
10920 }
10921 context.Dispose();
10922 for (gc_count = 1; gc_count < 10; gc_count++) {
10923 other_context->Enter();
10924 CompileRun(source_simple);
10925 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010926 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010927 if (GetGlobalObjectsCount() == 1) break;
10928 }
10929 CHECK_GE(2, gc_count);
10930 CHECK_EQ(1, GetGlobalObjectsCount());
10931
10932 // Eval in a function creates reference from the compilation cache to the
10933 // global object.
10934 const char* source_eval = "function f(){eval('1')}; f()";
10935 context = Context::New();
10936 {
10937 v8::HandleScope scope;
10938
10939 context->Enter();
10940 CompileRun(source_eval);
10941 context->Exit();
10942 }
10943 context.Dispose();
10944 for (gc_count = 1; gc_count < 10; gc_count++) {
10945 other_context->Enter();
10946 CompileRun(source_eval);
10947 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010948 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010949 if (GetGlobalObjectsCount() == 1) break;
10950 }
10951 CHECK_GE(2, gc_count);
10952 CHECK_EQ(1, GetGlobalObjectsCount());
10953
10954 // Looking up the line number for an exception creates reference from the
10955 // compilation cache to the global object.
10956 const char* source_exception = "function f(){throw 1;} f()";
10957 context = Context::New();
10958 {
10959 v8::HandleScope scope;
10960
10961 context->Enter();
10962 v8::TryCatch try_catch;
10963 CompileRun(source_exception);
10964 CHECK(try_catch.HasCaught());
10965 v8::Handle<v8::Message> message = try_catch.Message();
10966 CHECK(!message.IsEmpty());
10967 CHECK_EQ(1, message->GetLineNumber());
10968 context->Exit();
10969 }
10970 context.Dispose();
10971 for (gc_count = 1; gc_count < 10; gc_count++) {
10972 other_context->Enter();
10973 CompileRun(source_exception);
10974 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010975 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010976 if (GetGlobalObjectsCount() == 1) break;
10977 }
10978 CHECK_GE(2, gc_count);
10979 CHECK_EQ(1, GetGlobalObjectsCount());
10980
10981 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010982}
Andrei Popescu402d9372010-02-26 13:31:12 +000010983
10984
10985THREADED_TEST(ScriptOrigin) {
10986 v8::HandleScope scope;
10987 LocalContext env;
10988 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10989 v8::Handle<v8::String> script = v8::String::New(
10990 "function f() {}\n\nfunction g() {}");
10991 v8::Script::Compile(script, &origin)->Run();
10992 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10993 env->Global()->Get(v8::String::New("f")));
10994 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10995 env->Global()->Get(v8::String::New("g")));
10996
10997 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10998 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10999 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
11000
11001 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
11002 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
11003 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
11004}
11005
11006
11007THREADED_TEST(ScriptLineNumber) {
11008 v8::HandleScope scope;
11009 LocalContext env;
11010 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
11011 v8::Handle<v8::String> script = v8::String::New(
11012 "function f() {}\n\nfunction g() {}");
11013 v8::Script::Compile(script, &origin)->Run();
11014 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
11015 env->Global()->Get(v8::String::New("f")));
11016 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
11017 env->Global()->Get(v8::String::New("g")));
11018 CHECK_EQ(0, f->GetScriptLineNumber());
11019 CHECK_EQ(2, g->GetScriptLineNumber());
11020}
11021
11022
11023static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
11024 const AccessorInfo& info) {
11025 return v8_num(42);
11026}
11027
11028
11029static void SetterWhichSetsYOnThisTo23(Local<String> name,
11030 Local<Value> value,
11031 const AccessorInfo& info) {
11032 info.This()->Set(v8_str("y"), v8_num(23));
11033}
11034
11035
Steve Block6ded16b2010-05-10 14:33:55 +010011036TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000011037 v8::HandleScope scope;
11038 Local<ObjectTemplate> templ = ObjectTemplate::New();
11039 templ->SetAccessor(v8_str("x"),
11040 GetterWhichReturns42,
11041 SetterWhichSetsYOnThisTo23);
11042 LocalContext context;
11043 context->Global()->Set(v8_str("P"), templ->NewInstance());
11044 CompileRun("function C1() {"
11045 " this.x = 23;"
11046 "};"
11047 "C1.prototype = P;"
11048 "function C2() {"
11049 " this.x = 23"
11050 "};"
11051 "C2.prototype = { };"
11052 "C2.prototype.__proto__ = P;");
11053
11054 v8::Local<v8::Script> script;
11055 script = v8::Script::Compile(v8_str("new C1();"));
11056 for (int i = 0; i < 10; i++) {
11057 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11058 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
11059 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
11060 }
11061
11062 script = v8::Script::Compile(v8_str("new C2();"));
11063 for (int i = 0; i < 10; i++) {
11064 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
11065 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
11066 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
11067 }
11068}
11069
11070
11071static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
11072 Local<String> name, const AccessorInfo& info) {
11073 return v8_num(42);
11074}
11075
11076
11077static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
11078 Local<String> name, Local<Value> value, const AccessorInfo& info) {
11079 if (name->Equals(v8_str("x"))) {
11080 info.This()->Set(v8_str("y"), v8_num(23));
11081 }
11082 return v8::Handle<Value>();
11083}
11084
11085
11086THREADED_TEST(InterceptorOnConstructorPrototype) {
11087 v8::HandleScope scope;
11088 Local<ObjectTemplate> templ = ObjectTemplate::New();
11089 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
11090 NamedPropertySetterWhichSetsYOnThisTo23);
11091 LocalContext context;
11092 context->Global()->Set(v8_str("P"), templ->NewInstance());
11093 CompileRun("function C1() {"
11094 " this.x = 23;"
11095 "};"
11096 "C1.prototype = P;"
11097 "function C2() {"
11098 " this.x = 23"
11099 "};"
11100 "C2.prototype = { };"
11101 "C2.prototype.__proto__ = P;");
11102
11103 v8::Local<v8::Script> script;
11104 script = v8::Script::Compile(v8_str("new C1();"));
11105 for (int i = 0; i < 10; i++) {
11106 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11107 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
11108 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
11109 }
11110
11111 script = v8::Script::Compile(v8_str("new C2();"));
11112 for (int i = 0; i < 10; i++) {
11113 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
11114 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
11115 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
11116 }
11117}
Steve Block6ded16b2010-05-10 14:33:55 +010011118
11119
11120TEST(Bug618) {
11121 const char* source = "function C1() {"
11122 " this.x = 23;"
11123 "};"
11124 "C1.prototype = P;";
11125
11126 v8::HandleScope scope;
11127 LocalContext context;
11128 v8::Local<v8::Script> script;
11129
11130 // Use a simple object as prototype.
11131 v8::Local<v8::Object> prototype = v8::Object::New();
11132 prototype->Set(v8_str("y"), v8_num(42));
11133 context->Global()->Set(v8_str("P"), prototype);
11134
11135 // This compile will add the code to the compilation cache.
11136 CompileRun(source);
11137
11138 script = v8::Script::Compile(v8_str("new C1();"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +010011139 // Allow enough iterations for the inobject slack tracking logic
11140 // to finalize instance size and install the fast construct stub.
11141 for (int i = 0; i < 256; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +010011142 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11143 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
11144 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
11145 }
11146
11147 // Use an API object with accessors as prototype.
11148 Local<ObjectTemplate> templ = ObjectTemplate::New();
11149 templ->SetAccessor(v8_str("x"),
11150 GetterWhichReturns42,
11151 SetterWhichSetsYOnThisTo23);
11152 context->Global()->Set(v8_str("P"), templ->NewInstance());
11153
11154 // This compile will get the code from the compilation cache.
11155 CompileRun(source);
11156
11157 script = v8::Script::Compile(v8_str("new C1();"));
11158 for (int i = 0; i < 10; i++) {
11159 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11160 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
11161 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
11162 }
11163}
11164
11165int prologue_call_count = 0;
11166int epilogue_call_count = 0;
11167int prologue_call_count_second = 0;
11168int epilogue_call_count_second = 0;
11169
11170void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
11171 ++prologue_call_count;
11172}
11173
11174void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
11175 ++epilogue_call_count;
11176}
11177
11178void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
11179 ++prologue_call_count_second;
11180}
11181
11182void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
11183 ++epilogue_call_count_second;
11184}
11185
11186TEST(GCCallbacks) {
11187 LocalContext context;
11188
11189 v8::V8::AddGCPrologueCallback(PrologueCallback);
11190 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
11191 CHECK_EQ(0, prologue_call_count);
11192 CHECK_EQ(0, epilogue_call_count);
11193 i::Heap::CollectAllGarbage(false);
11194 CHECK_EQ(1, prologue_call_count);
11195 CHECK_EQ(1, epilogue_call_count);
11196 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
11197 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
11198 i::Heap::CollectAllGarbage(false);
11199 CHECK_EQ(2, prologue_call_count);
11200 CHECK_EQ(2, epilogue_call_count);
11201 CHECK_EQ(1, prologue_call_count_second);
11202 CHECK_EQ(1, epilogue_call_count_second);
11203 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
11204 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
11205 i::Heap::CollectAllGarbage(false);
11206 CHECK_EQ(2, prologue_call_count);
11207 CHECK_EQ(2, epilogue_call_count);
11208 CHECK_EQ(2, prologue_call_count_second);
11209 CHECK_EQ(2, epilogue_call_count_second);
11210 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
11211 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
11212 i::Heap::CollectAllGarbage(false);
11213 CHECK_EQ(2, prologue_call_count);
11214 CHECK_EQ(2, epilogue_call_count);
11215 CHECK_EQ(2, prologue_call_count_second);
11216 CHECK_EQ(2, epilogue_call_count_second);
11217}
Kristian Monsen25f61362010-05-21 11:50:48 +010011218
11219
11220THREADED_TEST(AddToJSFunctionResultCache) {
11221 i::FLAG_allow_natives_syntax = true;
11222 v8::HandleScope scope;
11223
11224 LocalContext context;
11225
11226 const char* code =
11227 "(function() {"
11228 " var key0 = 'a';"
11229 " var key1 = 'b';"
11230 " var r0 = %_GetFromCache(0, key0);"
11231 " var r1 = %_GetFromCache(0, key1);"
11232 " var r0_ = %_GetFromCache(0, key0);"
11233 " if (r0 !== r0_)"
11234 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
11235 " var r1_ = %_GetFromCache(0, key1);"
11236 " if (r1 !== r1_)"
11237 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
11238 " return 'PASSED';"
11239 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011240 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011241 ExpectString(code, "PASSED");
11242}
11243
11244
11245static const int k0CacheSize = 16;
11246
11247THREADED_TEST(FillJSFunctionResultCache) {
11248 i::FLAG_allow_natives_syntax = true;
11249 v8::HandleScope scope;
11250
11251 LocalContext context;
11252
11253 const char* code =
11254 "(function() {"
11255 " var k = 'a';"
11256 " var r = %_GetFromCache(0, k);"
11257 " for (var i = 0; i < 16; i++) {"
11258 " %_GetFromCache(0, 'a' + i);"
11259 " };"
11260 " if (r === %_GetFromCache(0, k))"
11261 " return 'FAILED: k0CacheSize is too small';"
11262 " return 'PASSED';"
11263 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011264 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011265 ExpectString(code, "PASSED");
11266}
11267
11268
11269THREADED_TEST(RoundRobinGetFromCache) {
11270 i::FLAG_allow_natives_syntax = true;
11271 v8::HandleScope scope;
11272
11273 LocalContext context;
11274
11275 const char* code =
11276 "(function() {"
11277 " var keys = [];"
11278 " for (var i = 0; i < 16; i++) keys.push(i);"
11279 " var values = [];"
11280 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11281 " for (var i = 0; i < 16; i++) {"
11282 " var v = %_GetFromCache(0, keys[i]);"
11283 " if (v !== values[i])"
11284 " return 'Wrong value for ' + "
11285 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11286 " };"
11287 " return 'PASSED';"
11288 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011289 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011290 ExpectString(code, "PASSED");
11291}
11292
11293
11294THREADED_TEST(ReverseGetFromCache) {
11295 i::FLAG_allow_natives_syntax = true;
11296 v8::HandleScope scope;
11297
11298 LocalContext context;
11299
11300 const char* code =
11301 "(function() {"
11302 " var keys = [];"
11303 " for (var i = 0; i < 16; i++) keys.push(i);"
11304 " var values = [];"
11305 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11306 " for (var i = 15; i >= 16; i--) {"
11307 " var v = %_GetFromCache(0, keys[i]);"
11308 " if (v !== values[i])"
11309 " return 'Wrong value for ' + "
11310 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11311 " };"
11312 " return 'PASSED';"
11313 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011314 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011315 ExpectString(code, "PASSED");
11316}
11317
11318
11319THREADED_TEST(TestEviction) {
11320 i::FLAG_allow_natives_syntax = true;
11321 v8::HandleScope scope;
11322
11323 LocalContext context;
11324
11325 const char* code =
11326 "(function() {"
11327 " for (var i = 0; i < 2*16; i++) {"
11328 " %_GetFromCache(0, 'a' + i);"
11329 " };"
11330 " return 'PASSED';"
11331 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011332 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011333 ExpectString(code, "PASSED");
11334}
Steve Block8defd9f2010-07-08 12:39:36 +010011335
11336
11337THREADED_TEST(TwoByteStringInAsciiCons) {
11338 // See Chromium issue 47824.
11339 v8::HandleScope scope;
11340
11341 LocalContext context;
11342 const char* init_code =
11343 "var str1 = 'abelspendabel';"
11344 "var str2 = str1 + str1 + str1;"
11345 "str2;";
11346 Local<Value> result = CompileRun(init_code);
11347
11348 CHECK(result->IsString());
11349 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
11350 int length = string->length();
11351 CHECK(string->IsAsciiRepresentation());
11352
11353 FlattenString(string);
11354 i::Handle<i::String> flat_string = FlattenGetString(string);
11355
11356 CHECK(string->IsAsciiRepresentation());
11357 CHECK(flat_string->IsAsciiRepresentation());
11358
11359 // Create external resource.
11360 uint16_t* uc16_buffer = new uint16_t[length + 1];
11361
11362 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
11363 uc16_buffer[length] = 0;
11364
11365 TestResource resource(uc16_buffer);
11366
11367 flat_string->MakeExternal(&resource);
11368
11369 CHECK(flat_string->IsTwoByteRepresentation());
11370
11371 // At this point, we should have a Cons string which is flat and ASCII,
11372 // with a first half that is a two-byte string (although it only contains
11373 // ASCII characters). This is a valid sequence of steps, and it can happen
11374 // in real pages.
11375
11376 CHECK(string->IsAsciiRepresentation());
11377 i::ConsString* cons = i::ConsString::cast(*string);
11378 CHECK_EQ(0, cons->second()->length());
11379 CHECK(cons->first()->IsTwoByteRepresentation());
11380
11381 // Check that some string operations work.
11382
11383 // Atom RegExp.
11384 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
11385 CHECK_EQ(6, reresult->Int32Value());
11386
11387 // Nonatom RegExp.
11388 reresult = CompileRun("str2.match(/abe./g).length;");
11389 CHECK_EQ(6, reresult->Int32Value());
11390
11391 reresult = CompileRun("str2.search(/bel/g);");
11392 CHECK_EQ(1, reresult->Int32Value());
11393
11394 reresult = CompileRun("str2.search(/be./g);");
11395 CHECK_EQ(1, reresult->Int32Value());
11396
11397 ExpectTrue("/bel/g.test(str2);");
11398
11399 ExpectTrue("/be./g.test(str2);");
11400
11401 reresult = CompileRun("/bel/g.exec(str2);");
11402 CHECK(!reresult->IsNull());
11403
11404 reresult = CompileRun("/be./g.exec(str2);");
11405 CHECK(!reresult->IsNull());
11406
11407 ExpectString("str2.substring(2, 10);", "elspenda");
11408
11409 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
11410
11411 ExpectString("str2.charAt(2);", "e");
11412
11413 reresult = CompileRun("str2.charCodeAt(2);");
11414 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
11415}
Iain Merrick75681382010-08-19 15:07:18 +010011416
11417
11418// Failed access check callback that performs a GC on each invocation.
11419void FailedAccessCheckCallbackGC(Local<v8::Object> target,
11420 v8::AccessType type,
11421 Local<v8::Value> data) {
11422 i::Heap::CollectAllGarbage(true);
11423}
11424
11425
11426TEST(GCInFailedAccessCheckCallback) {
11427 // Install a failed access check callback that performs a GC on each
11428 // invocation. Then force the callback to be called from va
11429
11430 v8::V8::Initialize();
11431 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
11432
11433 v8::HandleScope scope;
11434
11435 // Create an ObjectTemplate for global objects and install access
11436 // check callbacks that will block access.
11437 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
11438 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
11439 IndexedGetAccessBlocker,
11440 v8::Handle<v8::Value>(),
11441 false);
11442
11443 // Create a context and set an x property on it's global object.
11444 LocalContext context0(NULL, global_template);
11445 context0->Global()->Set(v8_str("x"), v8_num(42));
11446 v8::Handle<v8::Object> global0 = context0->Global();
11447
11448 // Create a context with a different security token so that the
11449 // failed access check callback will be called on each access.
11450 LocalContext context1(NULL, global_template);
11451 context1->Global()->Set(v8_str("other"), global0);
11452
11453 // Get property with failed access check.
11454 ExpectUndefined("other.x");
11455
11456 // Get element with failed access check.
11457 ExpectUndefined("other[0]");
11458
11459 // Set property with failed access check.
11460 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
11461 CHECK(result->IsObject());
11462
11463 // Set element with failed access check.
11464 result = CompileRun("other[0] = new Object()");
11465 CHECK(result->IsObject());
11466
11467 // Get property attribute with failed access check.
11468 ExpectFalse("\'x\' in other");
11469
11470 // Get property attribute for element with failed access check.
11471 ExpectFalse("0 in other");
11472
11473 // Delete property.
11474 ExpectFalse("delete other.x");
11475
11476 // Delete element.
11477 CHECK_EQ(false, global0->Delete(0));
11478
11479 // DefineAccessor.
11480 CHECK_EQ(false,
11481 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
11482
11483 // Define JavaScript accessor.
11484 ExpectUndefined("Object.prototype.__defineGetter__.call("
11485 " other, \'x\', function() { return 42; })");
11486
11487 // LookupAccessor.
11488 ExpectUndefined("Object.prototype.__lookupGetter__.call("
11489 " other, \'x\')");
11490
11491 // HasLocalElement.
11492 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
11493
11494 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
11495 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
11496 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
11497
11498 // Reset the failed access check callback so it does not influence
11499 // the other tests.
11500 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
11501}
Kristian Monsen0d5e1162010-09-30 15:31:59 +010011502
11503
11504TEST(StringCheckMultipleContexts) {
11505 const char* code =
11506 "(function() { return \"a\".charAt(0); })()";
11507
11508 {
11509 // Run the code twice in the first context to initialize the call IC.
11510 v8::HandleScope scope;
11511 LocalContext context1;
11512 ExpectString(code, "a");
11513 ExpectString(code, "a");
11514 }
11515
11516 {
11517 // Change the String.prototype in the second context and check
11518 // that the right function gets called.
11519 v8::HandleScope scope;
11520 LocalContext context2;
11521 CompileRun("String.prototype.charAt = function() { return \"not a\"; }");
11522 ExpectString(code, "not a");
11523 }
11524}
11525
11526
11527TEST(NumberCheckMultipleContexts) {
11528 const char* code =
11529 "(function() { return (42).toString(); })()";
11530
11531 {
11532 // Run the code twice in the first context to initialize the call IC.
11533 v8::HandleScope scope;
11534 LocalContext context1;
11535 ExpectString(code, "42");
11536 ExpectString(code, "42");
11537 }
11538
11539 {
11540 // Change the Number.prototype in the second context and check
11541 // that the right function gets called.
11542 v8::HandleScope scope;
11543 LocalContext context2;
11544 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }");
11545 ExpectString(code, "not 42");
11546 }
11547}
11548
11549
11550TEST(BooleanCheckMultipleContexts) {
11551 const char* code =
11552 "(function() { return true.toString(); })()";
11553
11554 {
11555 // Run the code twice in the first context to initialize the call IC.
11556 v8::HandleScope scope;
11557 LocalContext context1;
11558 ExpectString(code, "true");
11559 ExpectString(code, "true");
11560 }
11561
11562 {
11563 // Change the Boolean.prototype in the second context and check
11564 // that the right function gets called.
11565 v8::HandleScope scope;
11566 LocalContext context2;
11567 CompileRun("Boolean.prototype.toString = function() { return \"\"; }");
11568 ExpectString(code, "");
11569 }
11570}
Ben Murdochf87a2032010-10-22 12:50:53 +010011571
11572
11573TEST(DontDeleteCellLoadIC) {
11574 const char* function_code =
11575 "function readCell() { while (true) { return cell; } }";
11576
11577 {
11578 // Run the code twice in the first context to initialize the load
11579 // IC for a don't delete cell.
11580 v8::HandleScope scope;
11581 LocalContext context1;
11582 CompileRun("var cell = \"first\";");
11583 ExpectBoolean("delete cell", false);
11584 CompileRun(function_code);
11585 ExpectString("readCell()", "first");
11586 ExpectString("readCell()", "first");
11587 }
11588
11589 {
11590 // Use a deletable cell in the second context.
11591 v8::HandleScope scope;
11592 LocalContext context2;
11593 CompileRun("cell = \"second\";");
11594 CompileRun(function_code);
11595 ExpectString("readCell()", "second");
11596 ExpectBoolean("delete cell", true);
11597 ExpectString("(function() {"
11598 " try {"
11599 " return readCell();"
11600 " } catch(e) {"
11601 " return e.toString();"
11602 " }"
11603 "})()",
11604 "ReferenceError: cell is not defined");
11605 CompileRun("cell = \"new_second\";");
11606 i::Heap::CollectAllGarbage(true);
11607 ExpectString("readCell()", "new_second");
11608 ExpectString("readCell()", "new_second");
11609 }
11610}
11611
11612
11613TEST(DontDeleteCellLoadICForceDelete) {
11614 const char* function_code =
11615 "function readCell() { while (true) { return cell; } }";
11616
11617 // Run the code twice to initialize the load IC for a don't delete
11618 // cell.
11619 v8::HandleScope scope;
11620 LocalContext context;
11621 CompileRun("var cell = \"value\";");
11622 ExpectBoolean("delete cell", false);
11623 CompileRun(function_code);
11624 ExpectString("readCell()", "value");
11625 ExpectString("readCell()", "value");
11626
11627 // Delete the cell using the API and check the inlined code works
11628 // correctly.
11629 CHECK(context->Global()->ForceDelete(v8_str("cell")));
11630 ExpectString("(function() {"
11631 " try {"
11632 " return readCell();"
11633 " } catch(e) {"
11634 " return e.toString();"
11635 " }"
11636 "})()",
11637 "ReferenceError: cell is not defined");
11638}
11639
11640
11641TEST(DontDeleteCellLoadICAPI) {
11642 const char* function_code =
11643 "function readCell() { while (true) { return cell; } }";
11644
11645 // Run the code twice to initialize the load IC for a don't delete
11646 // cell created using the API.
11647 v8::HandleScope scope;
11648 LocalContext context;
11649 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete);
11650 ExpectBoolean("delete cell", false);
11651 CompileRun(function_code);
11652 ExpectString("readCell()", "value");
11653 ExpectString("readCell()", "value");
11654
11655 // Delete the cell using the API and check the inlined code works
11656 // correctly.
11657 CHECK(context->Global()->ForceDelete(v8_str("cell")));
11658 ExpectString("(function() {"
11659 " try {"
11660 " return readCell();"
11661 " } catch(e) {"
11662 " return e.toString();"
11663 " }"
11664 "})()",
11665 "ReferenceError: cell is not defined");
11666}
11667
11668
11669TEST(GlobalLoadICGC) {
11670 const char* function_code =
11671 "function readCell() { while (true) { return cell; } }";
11672
11673 // Check inline load code for a don't delete cell is cleared during
11674 // GC.
11675 {
11676 v8::HandleScope scope;
11677 LocalContext context;
11678 CompileRun("var cell = \"value\";");
11679 ExpectBoolean("delete cell", false);
11680 CompileRun(function_code);
11681 ExpectString("readCell()", "value");
11682 ExpectString("readCell()", "value");
11683 }
11684 {
11685 v8::HandleScope scope;
11686 LocalContext context2;
11687 // Hold the code object in the second context.
11688 CompileRun(function_code);
11689 CheckSurvivingGlobalObjectsCount(1);
11690 }
11691
11692 // Check inline load code for a deletable cell is cleared during GC.
11693 {
11694 v8::HandleScope scope;
11695 LocalContext context;
11696 CompileRun("cell = \"value\";");
11697 CompileRun(function_code);
11698 ExpectString("readCell()", "value");
11699 ExpectString("readCell()", "value");
11700 }
11701 {
11702 v8::HandleScope scope;
11703 LocalContext context2;
11704 // Hold the code object in the second context.
11705 CompileRun(function_code);
11706 CheckSurvivingGlobalObjectsCount(1);
11707 }
11708}
11709
11710
11711TEST(RegExp) {
11712 v8::HandleScope scope;
11713 LocalContext context;
11714
11715 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone);
11716 CHECK(re->IsRegExp());
11717 CHECK(re->GetSource()->Equals(v8_str("foo")));
11718 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
11719
11720 re = v8::RegExp::New(v8_str("bar"),
11721 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
11722 v8::RegExp::kGlobal));
11723 CHECK(re->IsRegExp());
11724 CHECK(re->GetSource()->Equals(v8_str("bar")));
11725 CHECK_EQ(static_cast<int>(re->GetFlags()),
11726 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal);
11727
11728 re = v8::RegExp::New(v8_str("baz"),
11729 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
11730 v8::RegExp::kMultiline));
11731 CHECK(re->IsRegExp());
11732 CHECK(re->GetSource()->Equals(v8_str("baz")));
11733 CHECK_EQ(static_cast<int>(re->GetFlags()),
11734 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
11735
11736 re = CompileRun("/quux/").As<v8::RegExp>();
11737 CHECK(re->IsRegExp());
11738 CHECK(re->GetSource()->Equals(v8_str("quux")));
11739 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
11740
11741 re = CompileRun("/quux/gm").As<v8::RegExp>();
11742 CHECK(re->IsRegExp());
11743 CHECK(re->GetSource()->Equals(v8_str("quux")));
11744 CHECK_EQ(static_cast<int>(re->GetFlags()),
11745 v8::RegExp::kGlobal | v8::RegExp::kMultiline);
11746
11747 // Override the RegExp constructor and check the API constructor
11748 // still works.
11749 CompileRun("RegExp = function() {}");
11750
11751 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone);
11752 CHECK(re->IsRegExp());
11753 CHECK(re->GetSource()->Equals(v8_str("foobar")));
11754 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone);
11755
11756 re = v8::RegExp::New(v8_str("foobarbaz"),
11757 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase |
11758 v8::RegExp::kMultiline));
11759 CHECK(re->IsRegExp());
11760 CHECK(re->GetSource()->Equals(v8_str("foobarbaz")));
11761 CHECK_EQ(static_cast<int>(re->GetFlags()),
11762 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline);
11763
11764 context->Global()->Set(v8_str("re"), re);
11765 ExpectTrue("re.test('FoobarbaZ')");
11766
11767 v8::TryCatch try_catch;
11768 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone);
11769 CHECK(re.IsEmpty());
11770 CHECK(try_catch.HasCaught());
11771 context->Global()->Set(v8_str("ex"), try_catch.Exception());
11772 ExpectTrue("ex instanceof SyntaxError");
11773}
11774
11775
11776static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property,
11777 const v8::AccessorInfo& info ) {
11778 return v8_str("42!");
11779}
11780
11781
11782static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) {
11783 v8::Handle<v8::Array> result = v8::Array::New();
11784 result->Set(0, v8_str("universalAnswer"));
11785 return result;
11786}
11787
11788
11789TEST(NamedEnumeratorAndForIn) {
11790 v8::HandleScope handle_scope;
11791 LocalContext context;
11792 v8::Context::Scope context_scope(context.local());
11793
11794 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New();
11795 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
11796 context->Global()->Set(v8_str("o"), tmpl->NewInstance());
11797 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
11798 "var result = []; for (var k in o) result.push(k); result"));
11799 CHECK_EQ(1, result->Length());
11800 CHECK_EQ(v8_str("universalAnswer"), result->Get(0));
11801}