blob: 2b50db7ecdbbdb99d79bd27f2951bb9021d61bcf [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"
Steve Blocka7e24c12009-10-30 11:49:00 +000041
Andrei Popescu31002712010-02-23 13:46:05 +000042static const bool kLogThreading = true;
Steve Blockd0582a62009-12-15 09:54:21 +000043
Steve Blocka7e24c12009-10-30 11:49:00 +000044static bool IsNaN(double x) {
45#ifdef WIN32
46 return _isnan(x);
47#else
48 return isnan(x);
49#endif
50}
51
52using ::v8::ObjectTemplate;
53using ::v8::Value;
54using ::v8::Context;
55using ::v8::Local;
56using ::v8::String;
57using ::v8::Script;
58using ::v8::Function;
59using ::v8::AccessorInfo;
60using ::v8::Extension;
61
Steve Block8defd9f2010-07-08 12:39:36 +010062namespace i = ::i;
Steve Blocka7e24c12009-10-30 11:49:00 +000063
Steve Blocka7e24c12009-10-30 11:49:00 +000064
Leon Clarked91b9f72010-01-27 17:25:45 +000065static void ExpectString(const char* code, const char* expected) {
66 Local<Value> result = CompileRun(code);
67 CHECK(result->IsString());
68 String::AsciiValue ascii(result);
69 CHECK_EQ(expected, *ascii);
70}
71
72
73static void ExpectBoolean(const char* code, bool expected) {
74 Local<Value> result = CompileRun(code);
75 CHECK(result->IsBoolean());
76 CHECK_EQ(expected, result->BooleanValue());
77}
78
79
Leon Clarkef7060e22010-06-03 12:02:55 +010080static void ExpectTrue(const char* code) {
81 ExpectBoolean(code, true);
82}
83
84
Iain Merrick75681382010-08-19 15:07:18 +010085static void ExpectFalse(const char* code) {
86 ExpectBoolean(code, false);
87}
88
89
Leon Clarked91b9f72010-01-27 17:25:45 +000090static void ExpectObject(const char* code, Local<Value> expected) {
91 Local<Value> result = CompileRun(code);
92 CHECK(result->Equals(expected));
93}
94
95
Iain Merrick75681382010-08-19 15:07:18 +010096static void ExpectUndefined(const char* code) {
97 Local<Value> result = CompileRun(code);
98 CHECK(result->IsUndefined());
99}
100
101
Steve Blocka7e24c12009-10-30 11:49:00 +0000102static int signature_callback_count;
103static v8::Handle<Value> IncrementingSignatureCallback(
104 const v8::Arguments& args) {
105 ApiTestFuzzer::Fuzz();
106 signature_callback_count++;
107 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
108 for (int i = 0; i < args.Length(); i++)
109 result->Set(v8::Integer::New(i), args[i]);
110 return result;
111}
112
113
114static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
115 ApiTestFuzzer::Fuzz();
116 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
117 for (int i = 0; i < args.Length(); i++) {
118 result->Set(v8::Integer::New(i), args[i]);
119 }
120 return result;
121}
122
123
124THREADED_TEST(Handles) {
125 v8::HandleScope scope;
126 Local<Context> local_env;
127 {
128 LocalContext env;
129 local_env = env.local();
130 }
131
132 // Local context should still be live.
133 CHECK(!local_env.IsEmpty());
134 local_env->Enter();
135
136 v8::Handle<v8::Primitive> undef = v8::Undefined();
137 CHECK(!undef.IsEmpty());
138 CHECK(undef->IsUndefined());
139
140 const char* c_source = "1 + 2 + 3";
141 Local<String> source = String::New(c_source);
142 Local<Script> script = Script::Compile(source);
143 CHECK_EQ(6, script->Run()->Int32Value());
144
145 local_env->Exit();
146}
147
148
Steve Blocka7e24c12009-10-30 11:49:00 +0000149THREADED_TEST(ReceiverSignature) {
150 v8::HandleScope scope;
151 LocalContext env;
152 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
153 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
154 fun->PrototypeTemplate()->Set(
155 v8_str("m"),
156 v8::FunctionTemplate::New(IncrementingSignatureCallback,
157 v8::Handle<Value>(),
158 sig));
159 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
160 signature_callback_count = 0;
161 CompileRun(
162 "var o = new Fun();"
163 "o.m();");
164 CHECK_EQ(1, signature_callback_count);
165 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
166 sub_fun->Inherit(fun);
167 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
168 CompileRun(
169 "var o = new SubFun();"
170 "o.m();");
171 CHECK_EQ(2, signature_callback_count);
172
173 v8::TryCatch try_catch;
174 CompileRun(
175 "var o = { };"
176 "o.m = Fun.prototype.m;"
177 "o.m();");
178 CHECK_EQ(2, signature_callback_count);
179 CHECK(try_catch.HasCaught());
180 try_catch.Reset();
181 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
182 sub_fun->Inherit(fun);
183 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
184 CompileRun(
185 "var o = new UnrelFun();"
186 "o.m = Fun.prototype.m;"
187 "o.m();");
188 CHECK_EQ(2, signature_callback_count);
189 CHECK(try_catch.HasCaught());
190}
191
192
193
194
195THREADED_TEST(ArgumentSignature) {
196 v8::HandleScope scope;
197 LocalContext env;
198 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
199 cons->SetClassName(v8_str("Cons"));
200 v8::Handle<v8::Signature> sig =
201 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
202 v8::Handle<v8::FunctionTemplate> fun =
203 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
204 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
205 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
206
207 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
208 CHECK(value1->IsTrue());
209
210 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
211 CHECK(value2->IsTrue());
212
213 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
214 CHECK(value3->IsTrue());
215
216 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
217 cons1->SetClassName(v8_str("Cons1"));
218 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
219 cons2->SetClassName(v8_str("Cons2"));
220 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
221 cons3->SetClassName(v8_str("Cons3"));
222
223 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
224 v8::Handle<v8::Signature> wsig =
225 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
226 v8::Handle<v8::FunctionTemplate> fun2 =
227 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
228
229 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
230 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
231 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
232 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
233 v8::Handle<Value> value4 = CompileRun(
234 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
235 "'[object Cons1],[object Cons2],[object Cons3]'");
236 CHECK(value4->IsTrue());
237
238 v8::Handle<Value> value5 = CompileRun(
239 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
240 CHECK(value5->IsTrue());
241
242 v8::Handle<Value> value6 = CompileRun(
243 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
244 CHECK(value6->IsTrue());
245
246 v8::Handle<Value> value7 = CompileRun(
247 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
248 "'[object Cons1],[object Cons2],[object Cons3],d';");
249 CHECK(value7->IsTrue());
250
251 v8::Handle<Value> value8 = CompileRun(
252 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
253 CHECK(value8->IsTrue());
254}
255
256
257THREADED_TEST(HulIgennem) {
258 v8::HandleScope scope;
259 LocalContext env;
260 v8::Handle<v8::Primitive> undef = v8::Undefined();
261 Local<String> undef_str = undef->ToString();
262 char* value = i::NewArray<char>(undef_str->Length() + 1);
263 undef_str->WriteAscii(value);
264 CHECK_EQ(0, strcmp(value, "undefined"));
265 i::DeleteArray(value);
266}
267
268
269THREADED_TEST(Access) {
270 v8::HandleScope scope;
271 LocalContext env;
272 Local<v8::Object> obj = v8::Object::New();
273 Local<Value> foo_before = obj->Get(v8_str("foo"));
274 CHECK(foo_before->IsUndefined());
275 Local<String> bar_str = v8_str("bar");
276 obj->Set(v8_str("foo"), bar_str);
277 Local<Value> foo_after = obj->Get(v8_str("foo"));
278 CHECK(!foo_after->IsUndefined());
279 CHECK(foo_after->IsString());
280 CHECK_EQ(bar_str, foo_after);
281}
282
283
Steve Block6ded16b2010-05-10 14:33:55 +0100284THREADED_TEST(AccessElement) {
285 v8::HandleScope scope;
286 LocalContext env;
287 Local<v8::Object> obj = v8::Object::New();
288 Local<Value> before = obj->Get(1);
289 CHECK(before->IsUndefined());
290 Local<String> bar_str = v8_str("bar");
291 obj->Set(1, bar_str);
292 Local<Value> after = obj->Get(1);
293 CHECK(!after->IsUndefined());
294 CHECK(after->IsString());
295 CHECK_EQ(bar_str, after);
296
297 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
298 CHECK_EQ(v8_str("a"), value->Get(0));
299 CHECK_EQ(v8_str("b"), value->Get(1));
300}
301
302
Steve Blocka7e24c12009-10-30 11:49:00 +0000303THREADED_TEST(Script) {
304 v8::HandleScope scope;
305 LocalContext env;
306 const char* c_source = "1 + 2 + 3";
307 Local<String> source = String::New(c_source);
308 Local<Script> script = Script::Compile(source);
309 CHECK_EQ(6, script->Run()->Int32Value());
310}
311
312
313static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000314 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000315 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000316 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000317 return converted;
318}
319
320
321class TestResource: public String::ExternalStringResource {
322 public:
323 static int dispose_count;
324
325 explicit TestResource(uint16_t* data)
326 : data_(data), length_(0) {
327 while (data[length_]) ++length_;
328 }
329
330 ~TestResource() {
331 i::DeleteArray(data_);
332 ++dispose_count;
333 }
334
335 const uint16_t* data() const {
336 return data_;
337 }
338
339 size_t length() const {
340 return length_;
341 }
342 private:
343 uint16_t* data_;
344 size_t length_;
345};
346
347
348int TestResource::dispose_count = 0;
349
350
351class TestAsciiResource: public String::ExternalAsciiStringResource {
352 public:
353 static int dispose_count;
354
355 explicit TestAsciiResource(const char* data)
356 : data_(data),
357 length_(strlen(data)) { }
358
359 ~TestAsciiResource() {
360 i::DeleteArray(data_);
361 ++dispose_count;
362 }
363
364 const char* data() const {
365 return data_;
366 }
367
368 size_t length() const {
369 return length_;
370 }
371 private:
372 const char* data_;
373 size_t length_;
374};
375
376
377int TestAsciiResource::dispose_count = 0;
378
379
380THREADED_TEST(ScriptUsingStringResource) {
381 TestResource::dispose_count = 0;
382 const char* c_source = "1 + 2 * 3";
383 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
384 {
385 v8::HandleScope scope;
386 LocalContext env;
387 TestResource* resource = new TestResource(two_byte_source);
388 Local<String> source = String::NewExternal(resource);
389 Local<Script> script = Script::Compile(source);
390 Local<Value> value = script->Run();
391 CHECK(value->IsNumber());
392 CHECK_EQ(7, value->Int32Value());
393 CHECK(source->IsExternal());
394 CHECK_EQ(resource,
395 static_cast<TestResource*>(source->GetExternalStringResource()));
Steve Block8defd9f2010-07-08 12:39:36 +0100396 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000397 CHECK_EQ(0, TestResource::dispose_count);
398 }
Steve Block8defd9f2010-07-08 12:39:36 +0100399 i::CompilationCache::Clear();
400 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000401 CHECK_EQ(1, TestResource::dispose_count);
402}
403
404
405THREADED_TEST(ScriptUsingAsciiStringResource) {
406 TestAsciiResource::dispose_count = 0;
407 const char* c_source = "1 + 2 * 3";
408 {
409 v8::HandleScope scope;
410 LocalContext env;
411 Local<String> source =
412 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
413 Local<Script> script = Script::Compile(source);
414 Local<Value> value = script->Run();
415 CHECK(value->IsNumber());
416 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100417 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000418 CHECK_EQ(0, TestAsciiResource::dispose_count);
419 }
Steve Block8defd9f2010-07-08 12:39:36 +0100420 i::CompilationCache::Clear();
421 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000422 CHECK_EQ(1, TestAsciiResource::dispose_count);
423}
424
425
426THREADED_TEST(ScriptMakingExternalString) {
427 TestResource::dispose_count = 0;
428 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
429 {
430 v8::HandleScope scope;
431 LocalContext env;
432 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000433 // Trigger GCs so that the newly allocated string moves to old gen.
434 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
435 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000436 bool success = source->MakeExternal(new TestResource(two_byte_source));
437 CHECK(success);
438 Local<Script> script = Script::Compile(source);
439 Local<Value> value = script->Run();
440 CHECK(value->IsNumber());
441 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100442 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000443 CHECK_EQ(0, TestResource::dispose_count);
444 }
Steve Block8defd9f2010-07-08 12:39:36 +0100445 i::CompilationCache::Clear();
446 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000447 CHECK_EQ(1, TestResource::dispose_count);
448}
449
450
451THREADED_TEST(ScriptMakingExternalAsciiString) {
452 TestAsciiResource::dispose_count = 0;
453 const char* c_source = "1 + 2 * 3";
454 {
455 v8::HandleScope scope;
456 LocalContext env;
457 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000458 // Trigger GCs so that the newly allocated string moves to old gen.
459 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
460 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000461 bool success = source->MakeExternal(
462 new TestAsciiResource(i::StrDup(c_source)));
463 CHECK(success);
464 Local<Script> script = Script::Compile(source);
465 Local<Value> value = script->Run();
466 CHECK(value->IsNumber());
467 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100468 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000469 CHECK_EQ(0, TestAsciiResource::dispose_count);
470 }
Steve Block8defd9f2010-07-08 12:39:36 +0100471 i::CompilationCache::Clear();
472 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000473 CHECK_EQ(1, TestAsciiResource::dispose_count);
474}
475
476
Andrei Popescu402d9372010-02-26 13:31:12 +0000477TEST(MakingExternalStringConditions) {
478 v8::HandleScope scope;
479 LocalContext env;
480
481 // Free some space in the new space so that we can check freshness.
482 i::Heap::CollectGarbage(0, i::NEW_SPACE);
483 i::Heap::CollectGarbage(0, i::NEW_SPACE);
484
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100485 uint16_t* two_byte_string = AsciiToTwoByteString("small");
486 Local<String> small_string = String::New(two_byte_string);
487 i::DeleteArray(two_byte_string);
488
Andrei Popescu402d9372010-02-26 13:31:12 +0000489 // We should refuse to externalize newly created small string.
490 CHECK(!small_string->CanMakeExternal());
491 // Trigger GCs so that the newly allocated string moves to old gen.
492 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
493 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
494 // Old space strings should be accepted.
495 CHECK(small_string->CanMakeExternal());
496
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100497 two_byte_string = AsciiToTwoByteString("small 2");
498 small_string = String::New(two_byte_string);
499 i::DeleteArray(two_byte_string);
500
Andrei Popescu402d9372010-02-26 13:31:12 +0000501 // We should refuse externalizing newly created small string.
502 CHECK(!small_string->CanMakeExternal());
503 for (int i = 0; i < 100; i++) {
504 String::Value value(small_string);
505 }
506 // Frequently used strings should be accepted.
507 CHECK(small_string->CanMakeExternal());
508
509 const int buf_size = 10 * 1024;
510 char* buf = i::NewArray<char>(buf_size);
511 memset(buf, 'a', buf_size);
512 buf[buf_size - 1] = '\0';
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100513
514 two_byte_string = AsciiToTwoByteString(buf);
515 Local<String> large_string = String::New(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000516 i::DeleteArray(buf);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100517 i::DeleteArray(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000518 // Large strings should be immediately accepted.
519 CHECK(large_string->CanMakeExternal());
520}
521
522
523TEST(MakingExternalAsciiStringConditions) {
524 v8::HandleScope scope;
525 LocalContext env;
526
527 // Free some space in the new space so that we can check freshness.
528 i::Heap::CollectGarbage(0, i::NEW_SPACE);
529 i::Heap::CollectGarbage(0, i::NEW_SPACE);
530
531 Local<String> small_string = String::New("small");
532 // We should refuse to externalize newly created small string.
533 CHECK(!small_string->CanMakeExternal());
534 // Trigger GCs so that the newly allocated string moves to old gen.
535 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
536 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
537 // Old space strings should be accepted.
538 CHECK(small_string->CanMakeExternal());
539
540 small_string = String::New("small 2");
541 // We should refuse externalizing newly created small string.
542 CHECK(!small_string->CanMakeExternal());
543 for (int i = 0; i < 100; i++) {
544 String::Value value(small_string);
545 }
546 // Frequently used strings should be accepted.
547 CHECK(small_string->CanMakeExternal());
548
549 const int buf_size = 10 * 1024;
550 char* buf = i::NewArray<char>(buf_size);
551 memset(buf, 'a', buf_size);
552 buf[buf_size - 1] = '\0';
553 Local<String> large_string = String::New(buf);
554 i::DeleteArray(buf);
555 // Large strings should be immediately accepted.
556 CHECK(large_string->CanMakeExternal());
557}
558
559
Steve Blocka7e24c12009-10-30 11:49:00 +0000560THREADED_TEST(UsingExternalString) {
561 {
562 v8::HandleScope scope;
563 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
564 Local<String> string =
565 String::NewExternal(new TestResource(two_byte_string));
566 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
567 // Trigger GCs so that the newly allocated string moves to old gen.
568 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
569 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
570 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
571 CHECK(isymbol->IsSymbol());
572 }
573 i::Heap::CollectAllGarbage(false);
574 i::Heap::CollectAllGarbage(false);
575}
576
577
578THREADED_TEST(UsingExternalAsciiString) {
579 {
580 v8::HandleScope scope;
581 const char* one_byte_string = "test string";
582 Local<String> string = String::NewExternal(
583 new TestAsciiResource(i::StrDup(one_byte_string)));
584 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
585 // Trigger GCs so that the newly allocated string moves to old gen.
586 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
587 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
588 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
589 CHECK(isymbol->IsSymbol());
590 }
591 i::Heap::CollectAllGarbage(false);
592 i::Heap::CollectAllGarbage(false);
593}
594
595
Leon Clarkee46be812010-01-19 14:06:41 +0000596THREADED_TEST(ScavengeExternalString) {
597 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100598 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000599 {
600 v8::HandleScope scope;
601 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
602 Local<String> string =
603 String::NewExternal(new TestResource(two_byte_string));
604 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
605 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100606 in_new_space = i::Heap::InNewSpace(*istring);
607 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000608 CHECK_EQ(0, TestResource::dispose_count);
609 }
Steve Block6ded16b2010-05-10 14:33:55 +0100610 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000611 CHECK_EQ(1, TestResource::dispose_count);
612}
613
614
615THREADED_TEST(ScavengeExternalAsciiString) {
616 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100617 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000618 {
619 v8::HandleScope scope;
620 const char* one_byte_string = "test string";
621 Local<String> string = String::NewExternal(
622 new TestAsciiResource(i::StrDup(one_byte_string)));
623 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
624 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100625 in_new_space = i::Heap::InNewSpace(*istring);
626 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000627 CHECK_EQ(0, TestAsciiResource::dispose_count);
628 }
Steve Block6ded16b2010-05-10 14:33:55 +0100629 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000630 CHECK_EQ(1, TestAsciiResource::dispose_count);
631}
632
633
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100634class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
635 public:
636 static int dispose_calls;
637
638 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
639 : TestAsciiResource(data),
640 dispose_(dispose) { }
641
642 void Dispose() {
643 ++dispose_calls;
644 if (dispose_) delete this;
645 }
646 private:
647 bool dispose_;
648};
649
650
651int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
652
653
654TEST(ExternalStringWithDisposeHandling) {
655 const char* c_source = "1 + 2 * 3";
656
657 // Use a stack allocated external string resource allocated object.
658 TestAsciiResource::dispose_count = 0;
659 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
660 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
661 {
662 v8::HandleScope scope;
663 LocalContext env;
664 Local<String> source = String::NewExternal(&res_stack);
665 Local<Script> script = Script::Compile(source);
666 Local<Value> value = script->Run();
667 CHECK(value->IsNumber());
668 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100669 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100670 CHECK_EQ(0, TestAsciiResource::dispose_count);
671 }
Steve Block8defd9f2010-07-08 12:39:36 +0100672 i::CompilationCache::Clear();
673 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100674 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
675 CHECK_EQ(0, TestAsciiResource::dispose_count);
676
677 // Use a heap allocated external string resource allocated object.
678 TestAsciiResource::dispose_count = 0;
679 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
680 TestAsciiResource* res_heap =
681 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
682 {
683 v8::HandleScope scope;
684 LocalContext env;
685 Local<String> source = String::NewExternal(res_heap);
686 Local<Script> script = Script::Compile(source);
687 Local<Value> value = script->Run();
688 CHECK(value->IsNumber());
689 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100690 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100691 CHECK_EQ(0, TestAsciiResource::dispose_count);
692 }
Steve Block8defd9f2010-07-08 12:39:36 +0100693 i::CompilationCache::Clear();
694 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100695 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
696 CHECK_EQ(1, TestAsciiResource::dispose_count);
697}
698
699
Steve Block3ce2e202009-11-05 08:53:23 +0000700THREADED_TEST(StringConcat) {
701 {
702 v8::HandleScope scope;
703 LocalContext env;
704 const char* one_byte_string_1 = "function a_times_t";
705 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
706 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
707 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
708 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
709 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
710 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
711 Local<String> left = v8_str(one_byte_string_1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100712
713 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
714 Local<String> right = String::New(two_byte_source);
715 i::DeleteArray(two_byte_source);
716
Steve Block3ce2e202009-11-05 08:53:23 +0000717 Local<String> source = String::Concat(left, right);
718 right = String::NewExternal(
719 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
720 source = String::Concat(source, right);
721 right = String::NewExternal(
722 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
723 source = String::Concat(source, right);
724 right = v8_str(one_byte_string_2);
725 source = String::Concat(source, right);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100726
727 two_byte_source = AsciiToTwoByteString(two_byte_string_2);
728 right = String::New(two_byte_source);
729 i::DeleteArray(two_byte_source);
730
Steve Block3ce2e202009-11-05 08:53:23 +0000731 source = String::Concat(source, right);
732 right = String::NewExternal(
733 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
734 source = String::Concat(source, right);
735 Local<Script> script = Script::Compile(source);
736 Local<Value> value = script->Run();
737 CHECK(value->IsNumber());
738 CHECK_EQ(68, value->Int32Value());
739 }
Steve Block8defd9f2010-07-08 12:39:36 +0100740 i::CompilationCache::Clear();
Steve Block3ce2e202009-11-05 08:53:23 +0000741 i::Heap::CollectAllGarbage(false);
742 i::Heap::CollectAllGarbage(false);
743}
744
745
Steve Blocka7e24c12009-10-30 11:49:00 +0000746THREADED_TEST(GlobalProperties) {
747 v8::HandleScope scope;
748 LocalContext env;
749 v8::Handle<v8::Object> global = env->Global();
750 global->Set(v8_str("pi"), v8_num(3.1415926));
751 Local<Value> pi = global->Get(v8_str("pi"));
752 CHECK_EQ(3.1415926, pi->NumberValue());
753}
754
755
756static v8::Handle<Value> handle_call(const v8::Arguments& args) {
757 ApiTestFuzzer::Fuzz();
758 return v8_num(102);
759}
760
761
762static v8::Handle<Value> construct_call(const v8::Arguments& args) {
763 ApiTestFuzzer::Fuzz();
764 args.This()->Set(v8_str("x"), v8_num(1));
765 args.This()->Set(v8_str("y"), v8_num(2));
766 return args.This();
767}
768
769THREADED_TEST(FunctionTemplate) {
770 v8::HandleScope scope;
771 LocalContext env;
772 {
773 Local<v8::FunctionTemplate> fun_templ =
774 v8::FunctionTemplate::New(handle_call);
775 Local<Function> fun = fun_templ->GetFunction();
776 env->Global()->Set(v8_str("obj"), fun);
777 Local<Script> script = v8_compile("obj()");
778 CHECK_EQ(102, script->Run()->Int32Value());
779 }
780 // Use SetCallHandler to initialize a function template, should work like the
781 // previous one.
782 {
783 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
784 fun_templ->SetCallHandler(handle_call);
785 Local<Function> fun = fun_templ->GetFunction();
786 env->Global()->Set(v8_str("obj"), fun);
787 Local<Script> script = v8_compile("obj()");
788 CHECK_EQ(102, script->Run()->Int32Value());
789 }
790 // Test constructor calls.
791 {
792 Local<v8::FunctionTemplate> fun_templ =
793 v8::FunctionTemplate::New(construct_call);
794 fun_templ->SetClassName(v8_str("funky"));
795 Local<Function> fun = fun_templ->GetFunction();
796 env->Global()->Set(v8_str("obj"), fun);
797 Local<Script> script = v8_compile("var s = new obj(); s.x");
798 CHECK_EQ(1, script->Run()->Int32Value());
799
800 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
801 CHECK_EQ(v8_str("[object funky]"), result);
802 }
803}
804
805
806THREADED_TEST(FindInstanceInPrototypeChain) {
807 v8::HandleScope scope;
808 LocalContext env;
809
810 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
811 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
812 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
813 derived->Inherit(base);
814
815 Local<v8::Function> base_function = base->GetFunction();
816 Local<v8::Function> derived_function = derived->GetFunction();
817 Local<v8::Function> other_function = other->GetFunction();
818
819 Local<v8::Object> base_instance = base_function->NewInstance();
820 Local<v8::Object> derived_instance = derived_function->NewInstance();
821 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
822 Local<v8::Object> other_instance = other_function->NewInstance();
823 derived_instance2->Set(v8_str("__proto__"), derived_instance);
824 other_instance->Set(v8_str("__proto__"), derived_instance2);
825
826 // base_instance is only an instance of base.
827 CHECK_EQ(base_instance,
828 base_instance->FindInstanceInPrototypeChain(base));
829 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
830 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
831
832 // derived_instance is an instance of base and derived.
833 CHECK_EQ(derived_instance,
834 derived_instance->FindInstanceInPrototypeChain(base));
835 CHECK_EQ(derived_instance,
836 derived_instance->FindInstanceInPrototypeChain(derived));
837 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
838
839 // other_instance is an instance of other and its immediate
840 // prototype derived_instance2 is an instance of base and derived.
841 // Note, derived_instance is an instance of base and derived too,
842 // but it comes after derived_instance2 in the prototype chain of
843 // other_instance.
844 CHECK_EQ(derived_instance2,
845 other_instance->FindInstanceInPrototypeChain(base));
846 CHECK_EQ(derived_instance2,
847 other_instance->FindInstanceInPrototypeChain(derived));
848 CHECK_EQ(other_instance,
849 other_instance->FindInstanceInPrototypeChain(other));
850}
851
852
Steve Block3ce2e202009-11-05 08:53:23 +0000853THREADED_TEST(TinyInteger) {
854 v8::HandleScope scope;
855 LocalContext env;
856 int32_t value = 239;
857 Local<v8::Integer> value_obj = v8::Integer::New(value);
858 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
859}
860
861
862THREADED_TEST(BigSmiInteger) {
863 v8::HandleScope scope;
864 LocalContext env;
865 int32_t value = i::Smi::kMaxValue;
866 // We cannot add one to a Smi::kMaxValue without wrapping.
867 if (i::kSmiValueSize < 32) {
868 CHECK(i::Smi::IsValid(value));
869 CHECK(!i::Smi::IsValid(value + 1));
870 Local<v8::Integer> value_obj = v8::Integer::New(value);
871 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
872 }
873}
874
875
876THREADED_TEST(BigInteger) {
877 v8::HandleScope scope;
878 LocalContext env;
879 // We cannot add one to a Smi::kMaxValue without wrapping.
880 if (i::kSmiValueSize < 32) {
881 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
882 // The code will not be run in that case, due to the "if" guard.
883 int32_t value =
884 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
885 CHECK(value > i::Smi::kMaxValue);
886 CHECK(!i::Smi::IsValid(value));
887 Local<v8::Integer> value_obj = v8::Integer::New(value);
888 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
889 }
890}
891
892
893THREADED_TEST(TinyUnsignedInteger) {
894 v8::HandleScope scope;
895 LocalContext env;
896 uint32_t value = 239;
897 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
898 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
899}
900
901
902THREADED_TEST(BigUnsignedSmiInteger) {
903 v8::HandleScope scope;
904 LocalContext env;
905 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
906 CHECK(i::Smi::IsValid(value));
907 CHECK(!i::Smi::IsValid(value + 1));
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(BigUnsignedInteger) {
914 v8::HandleScope scope;
915 LocalContext env;
916 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
917 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
918 CHECK(!i::Smi::IsValid(value));
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(OutOfSignedRangeUnsignedInteger) {
925 v8::HandleScope scope;
926 LocalContext env;
927 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
928 uint32_t value = INT32_MAX_AS_UINT + 1;
929 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
930 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
931 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
932}
933
934
Steve Blocka7e24c12009-10-30 11:49:00 +0000935THREADED_TEST(Number) {
936 v8::HandleScope scope;
937 LocalContext env;
938 double PI = 3.1415926;
939 Local<v8::Number> pi_obj = v8::Number::New(PI);
940 CHECK_EQ(PI, pi_obj->NumberValue());
941}
942
943
944THREADED_TEST(ToNumber) {
945 v8::HandleScope scope;
946 LocalContext env;
947 Local<String> str = v8_str("3.1415926");
948 CHECK_EQ(3.1415926, str->NumberValue());
949 v8::Handle<v8::Boolean> t = v8::True();
950 CHECK_EQ(1.0, t->NumberValue());
951 v8::Handle<v8::Boolean> f = v8::False();
952 CHECK_EQ(0.0, f->NumberValue());
953}
954
955
956THREADED_TEST(Date) {
957 v8::HandleScope scope;
958 LocalContext env;
959 double PI = 3.1415926;
960 Local<Value> date_obj = v8::Date::New(PI);
961 CHECK_EQ(3.0, date_obj->NumberValue());
962}
963
964
965THREADED_TEST(Boolean) {
966 v8::HandleScope scope;
967 LocalContext env;
968 v8::Handle<v8::Boolean> t = v8::True();
969 CHECK(t->Value());
970 v8::Handle<v8::Boolean> f = v8::False();
971 CHECK(!f->Value());
972 v8::Handle<v8::Primitive> u = v8::Undefined();
973 CHECK(!u->BooleanValue());
974 v8::Handle<v8::Primitive> n = v8::Null();
975 CHECK(!n->BooleanValue());
976 v8::Handle<String> str1 = v8_str("");
977 CHECK(!str1->BooleanValue());
978 v8::Handle<String> str2 = v8_str("x");
979 CHECK(str2->BooleanValue());
980 CHECK(!v8::Number::New(0)->BooleanValue());
981 CHECK(v8::Number::New(-1)->BooleanValue());
982 CHECK(v8::Number::New(1)->BooleanValue());
983 CHECK(v8::Number::New(42)->BooleanValue());
984 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
985}
986
987
988static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
989 ApiTestFuzzer::Fuzz();
990 return v8_num(13.4);
991}
992
993
994static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
995 ApiTestFuzzer::Fuzz();
996 return v8_num(876);
997}
998
999
1000THREADED_TEST(GlobalPrototype) {
1001 v8::HandleScope scope;
1002 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
1003 func_templ->PrototypeTemplate()->Set(
1004 "dummy",
1005 v8::FunctionTemplate::New(DummyCallHandler));
1006 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
1007 templ->Set("x", v8_num(200));
1008 templ->SetAccessor(v8_str("m"), GetM);
1009 LocalContext env(0, templ);
1010 v8::Handle<v8::Object> obj = env->Global();
1011 v8::Handle<Script> script = v8_compile("dummy()");
1012 v8::Handle<Value> result = script->Run();
1013 CHECK_EQ(13.4, result->NumberValue());
1014 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1015 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1016}
1017
1018
Steve Blocka7e24c12009-10-30 11:49:00 +00001019THREADED_TEST(ObjectTemplate) {
1020 v8::HandleScope scope;
1021 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1022 templ1->Set("x", v8_num(10));
1023 templ1->Set("y", v8_num(13));
1024 LocalContext env;
1025 Local<v8::Object> instance1 = templ1->NewInstance();
1026 env->Global()->Set(v8_str("p"), instance1);
1027 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1028 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1029 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1030 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1031 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1032 templ2->Set("a", v8_num(12));
1033 templ2->Set("b", templ1);
1034 Local<v8::Object> instance2 = templ2->NewInstance();
1035 env->Global()->Set(v8_str("q"), instance2);
1036 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1037 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1038 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1039 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1040}
1041
1042
1043static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1044 ApiTestFuzzer::Fuzz();
1045 return v8_num(17.2);
1046}
1047
1048
1049static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1050 ApiTestFuzzer::Fuzz();
1051 return v8_num(15.2);
1052}
1053
1054
1055THREADED_TEST(DescriptorInheritance) {
1056 v8::HandleScope scope;
1057 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1058 super->PrototypeTemplate()->Set("flabby",
1059 v8::FunctionTemplate::New(GetFlabby));
1060 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1061
1062 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1063
1064 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1065 base1->Inherit(super);
1066 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1067
1068 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1069 base2->Inherit(super);
1070 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1071
1072 LocalContext env;
1073
1074 env->Global()->Set(v8_str("s"), super->GetFunction());
1075 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1076 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1077
1078 // Checks right __proto__ chain.
1079 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1080 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1081
1082 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1083
1084 // Instance accessor should not be visible on function object or its prototype
1085 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1086 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1087 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1088
1089 env->Global()->Set(v8_str("obj"),
1090 base1->GetFunction()->NewInstance());
1091 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1092 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1093 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1094 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1095 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1096
1097 env->Global()->Set(v8_str("obj2"),
1098 base2->GetFunction()->NewInstance());
1099 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1100 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1101 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1102 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1103 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1104
1105 // base1 and base2 cannot cross reference to each's prototype
1106 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1107 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1108}
1109
1110
1111int echo_named_call_count;
1112
1113
1114static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1115 const AccessorInfo& info) {
1116 ApiTestFuzzer::Fuzz();
1117 CHECK_EQ(v8_str("data"), info.Data());
1118 echo_named_call_count++;
1119 return name;
1120}
1121
1122
1123THREADED_TEST(NamedPropertyHandlerGetter) {
1124 echo_named_call_count = 0;
1125 v8::HandleScope scope;
1126 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1127 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1128 0, 0, 0, 0,
1129 v8_str("data"));
1130 LocalContext env;
1131 env->Global()->Set(v8_str("obj"),
1132 templ->GetFunction()->NewInstance());
1133 CHECK_EQ(echo_named_call_count, 0);
1134 v8_compile("obj.x")->Run();
1135 CHECK_EQ(echo_named_call_count, 1);
1136 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1137 v8::Handle<Value> str = CompileRun(code);
1138 String::AsciiValue value(str);
1139 CHECK_EQ(*value, "oddlepoddle");
1140 // Check default behavior
1141 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1142 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1143 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1144}
1145
1146
1147int echo_indexed_call_count = 0;
1148
1149
1150static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1151 const AccessorInfo& info) {
1152 ApiTestFuzzer::Fuzz();
1153 CHECK_EQ(v8_num(637), info.Data());
1154 echo_indexed_call_count++;
1155 return v8_num(index);
1156}
1157
1158
1159THREADED_TEST(IndexedPropertyHandlerGetter) {
1160 v8::HandleScope scope;
1161 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1162 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1163 0, 0, 0, 0,
1164 v8_num(637));
1165 LocalContext env;
1166 env->Global()->Set(v8_str("obj"),
1167 templ->GetFunction()->NewInstance());
1168 Local<Script> script = v8_compile("obj[900]");
1169 CHECK_EQ(script->Run()->Int32Value(), 900);
1170}
1171
1172
1173v8::Handle<v8::Object> bottom;
1174
1175static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1176 uint32_t index,
1177 const AccessorInfo& info) {
1178 ApiTestFuzzer::Fuzz();
1179 CHECK(info.This()->Equals(bottom));
1180 return v8::Handle<Value>();
1181}
1182
1183static v8::Handle<Value> CheckThisNamedPropertyHandler(
1184 Local<String> name,
1185 const AccessorInfo& info) {
1186 ApiTestFuzzer::Fuzz();
1187 CHECK(info.This()->Equals(bottom));
1188 return v8::Handle<Value>();
1189}
1190
1191
1192v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1193 Local<Value> value,
1194 const AccessorInfo& info) {
1195 ApiTestFuzzer::Fuzz();
1196 CHECK(info.This()->Equals(bottom));
1197 return v8::Handle<Value>();
1198}
1199
1200
1201v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1202 Local<Value> value,
1203 const AccessorInfo& info) {
1204 ApiTestFuzzer::Fuzz();
1205 CHECK(info.This()->Equals(bottom));
1206 return v8::Handle<Value>();
1207}
1208
Iain Merrick75681382010-08-19 15:07:18 +01001209v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery(
Steve Blocka7e24c12009-10-30 11:49:00 +00001210 uint32_t index,
1211 const AccessorInfo& info) {
1212 ApiTestFuzzer::Fuzz();
1213 CHECK(info.This()->Equals(bottom));
Iain Merrick75681382010-08-19 15:07:18 +01001214 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001215}
1216
1217
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001218v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001219 const AccessorInfo& info) {
1220 ApiTestFuzzer::Fuzz();
1221 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001222 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001223}
1224
1225
1226v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1227 uint32_t index,
1228 const AccessorInfo& info) {
1229 ApiTestFuzzer::Fuzz();
1230 CHECK(info.This()->Equals(bottom));
1231 return v8::Handle<v8::Boolean>();
1232}
1233
1234
1235v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1236 Local<String> property,
1237 const AccessorInfo& info) {
1238 ApiTestFuzzer::Fuzz();
1239 CHECK(info.This()->Equals(bottom));
1240 return v8::Handle<v8::Boolean>();
1241}
1242
1243
1244v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1245 const AccessorInfo& info) {
1246 ApiTestFuzzer::Fuzz();
1247 CHECK(info.This()->Equals(bottom));
1248 return v8::Handle<v8::Array>();
1249}
1250
1251
1252v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1253 const AccessorInfo& info) {
1254 ApiTestFuzzer::Fuzz();
1255 CHECK(info.This()->Equals(bottom));
1256 return v8::Handle<v8::Array>();
1257}
1258
1259
1260THREADED_TEST(PropertyHandlerInPrototype) {
1261 v8::HandleScope scope;
1262 LocalContext env;
1263
1264 // Set up a prototype chain with three interceptors.
1265 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1266 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1267 CheckThisIndexedPropertyHandler,
1268 CheckThisIndexedPropertySetter,
1269 CheckThisIndexedPropertyQuery,
1270 CheckThisIndexedPropertyDeleter,
1271 CheckThisIndexedPropertyEnumerator);
1272
1273 templ->InstanceTemplate()->SetNamedPropertyHandler(
1274 CheckThisNamedPropertyHandler,
1275 CheckThisNamedPropertySetter,
1276 CheckThisNamedPropertyQuery,
1277 CheckThisNamedPropertyDeleter,
1278 CheckThisNamedPropertyEnumerator);
1279
1280 bottom = templ->GetFunction()->NewInstance();
1281 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1282 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1283
1284 bottom->Set(v8_str("__proto__"), middle);
1285 middle->Set(v8_str("__proto__"), top);
1286 env->Global()->Set(v8_str("obj"), bottom);
1287
1288 // Indexed and named get.
1289 Script::Compile(v8_str("obj[0]"))->Run();
1290 Script::Compile(v8_str("obj.x"))->Run();
1291
1292 // Indexed and named set.
1293 Script::Compile(v8_str("obj[1] = 42"))->Run();
1294 Script::Compile(v8_str("obj.y = 42"))->Run();
1295
1296 // Indexed and named query.
1297 Script::Compile(v8_str("0 in obj"))->Run();
1298 Script::Compile(v8_str("'x' in obj"))->Run();
1299
1300 // Indexed and named deleter.
1301 Script::Compile(v8_str("delete obj[0]"))->Run();
1302 Script::Compile(v8_str("delete obj.x"))->Run();
1303
1304 // Enumerators.
1305 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1306}
1307
1308
1309static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1310 const AccessorInfo& info) {
1311 ApiTestFuzzer::Fuzz();
1312 if (v8_str("pre")->Equals(key)) {
1313 return v8_str("PrePropertyHandler: pre");
1314 }
1315 return v8::Handle<String>();
1316}
1317
1318
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001319static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1320 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001321 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001322 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001323 }
1324
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001325 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001326}
1327
1328
1329THREADED_TEST(PrePropertyHandler) {
1330 v8::HandleScope scope;
1331 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1332 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1333 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001334 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001335 LocalContext env(NULL, desc->InstanceTemplate());
1336 Script::Compile(v8_str(
1337 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1338 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1339 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1340 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1341 CHECK_EQ(v8_str("Object: on"), result_on);
1342 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1343 CHECK(result_post.IsEmpty());
1344}
1345
1346
1347THREADED_TEST(UndefinedIsNotEnumerable) {
1348 v8::HandleScope scope;
1349 LocalContext env;
1350 v8::Handle<Value> result = Script::Compile(v8_str(
1351 "this.propertyIsEnumerable(undefined)"))->Run();
1352 CHECK(result->IsFalse());
1353}
1354
1355
1356v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001357static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001358
1359
1360static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1361 ApiTestFuzzer::Fuzz();
1362 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1363 if (depth == kTargetRecursionDepth) return v8::Undefined();
1364 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1365 return call_recursively_script->Run();
1366}
1367
1368
1369static v8::Handle<Value> CallFunctionRecursivelyCall(
1370 const v8::Arguments& args) {
1371 ApiTestFuzzer::Fuzz();
1372 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1373 if (depth == kTargetRecursionDepth) {
1374 printf("[depth = %d]\n", depth);
1375 return v8::Undefined();
1376 }
1377 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1378 v8::Handle<Value> function =
1379 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001380 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001381}
1382
1383
1384THREADED_TEST(DeepCrossLanguageRecursion) {
1385 v8::HandleScope scope;
1386 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1387 global->Set(v8_str("callScriptRecursively"),
1388 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1389 global->Set(v8_str("callFunctionRecursively"),
1390 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1391 LocalContext env(NULL, global);
1392
1393 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1394 call_recursively_script = v8_compile("callScriptRecursively()");
1395 v8::Handle<Value> result = call_recursively_script->Run();
1396 call_recursively_script = v8::Handle<Script>();
1397
1398 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1399 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1400}
1401
1402
1403static v8::Handle<Value>
1404 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1405 ApiTestFuzzer::Fuzz();
1406 return v8::ThrowException(key);
1407}
1408
1409
1410static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1411 Local<Value>,
1412 const AccessorInfo&) {
1413 v8::ThrowException(key);
1414 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1415}
1416
1417
1418THREADED_TEST(CallbackExceptionRegression) {
1419 v8::HandleScope scope;
1420 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1421 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1422 ThrowingPropertyHandlerSet);
1423 LocalContext env;
1424 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1425 v8::Handle<Value> otto = Script::Compile(v8_str(
1426 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1427 CHECK_EQ(v8_str("otto"), otto);
1428 v8::Handle<Value> netto = Script::Compile(v8_str(
1429 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1430 CHECK_EQ(v8_str("netto"), netto);
1431}
1432
1433
Steve Blocka7e24c12009-10-30 11:49:00 +00001434THREADED_TEST(FunctionPrototype) {
1435 v8::HandleScope scope;
1436 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1437 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1438 LocalContext env;
1439 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1440 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1441 CHECK_EQ(script->Run()->Int32Value(), 321);
1442}
1443
1444
1445THREADED_TEST(InternalFields) {
1446 v8::HandleScope scope;
1447 LocalContext env;
1448
1449 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1450 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1451 instance_templ->SetInternalFieldCount(1);
1452 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1453 CHECK_EQ(1, obj->InternalFieldCount());
1454 CHECK(obj->GetInternalField(0)->IsUndefined());
1455 obj->SetInternalField(0, v8_num(17));
1456 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1457}
1458
1459
Steve Block6ded16b2010-05-10 14:33:55 +01001460THREADED_TEST(GlobalObjectInternalFields) {
1461 v8::HandleScope scope;
1462 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1463 global_template->SetInternalFieldCount(1);
1464 LocalContext env(NULL, global_template);
1465 v8::Handle<v8::Object> global_proxy = env->Global();
1466 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1467 CHECK_EQ(1, global->InternalFieldCount());
1468 CHECK(global->GetInternalField(0)->IsUndefined());
1469 global->SetInternalField(0, v8_num(17));
1470 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1471}
1472
1473
Steve Blocka7e24c12009-10-30 11:49:00 +00001474THREADED_TEST(InternalFieldsNativePointers) {
1475 v8::HandleScope scope;
1476 LocalContext env;
1477
1478 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1479 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1480 instance_templ->SetInternalFieldCount(1);
1481 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1482 CHECK_EQ(1, obj->InternalFieldCount());
1483 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1484
1485 char* data = new char[100];
1486
1487 void* aligned = data;
1488 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1489 void* unaligned = data + 1;
1490 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1491
1492 // Check reading and writing aligned pointers.
1493 obj->SetPointerInInternalField(0, aligned);
1494 i::Heap::CollectAllGarbage(false);
1495 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1496
1497 // Check reading and writing unaligned pointers.
1498 obj->SetPointerInInternalField(0, unaligned);
1499 i::Heap::CollectAllGarbage(false);
1500 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1501
1502 delete[] data;
1503}
1504
1505
Steve Block3ce2e202009-11-05 08:53:23 +00001506THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1507 v8::HandleScope scope;
1508 LocalContext env;
1509
1510 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1511 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1512 instance_templ->SetInternalFieldCount(1);
1513 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1514 CHECK_EQ(1, obj->InternalFieldCount());
1515 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1516
1517 char* data = new char[100];
1518
1519 void* aligned = data;
1520 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1521 void* unaligned = data + 1;
1522 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1523
1524 obj->SetPointerInInternalField(0, aligned);
1525 i::Heap::CollectAllGarbage(false);
1526 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1527
1528 obj->SetPointerInInternalField(0, unaligned);
1529 i::Heap::CollectAllGarbage(false);
1530 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1531
1532 obj->SetInternalField(0, v8::External::Wrap(aligned));
1533 i::Heap::CollectAllGarbage(false);
1534 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1535
1536 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1537 i::Heap::CollectAllGarbage(false);
1538 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1539
1540 delete[] data;
1541}
1542
1543
Steve Blocka7e24c12009-10-30 11:49:00 +00001544THREADED_TEST(IdentityHash) {
1545 v8::HandleScope scope;
1546 LocalContext env;
1547
1548 // Ensure that the test starts with an fresh heap to test whether the hash
1549 // code is based on the address.
1550 i::Heap::CollectAllGarbage(false);
1551 Local<v8::Object> obj = v8::Object::New();
1552 int hash = obj->GetIdentityHash();
1553 int hash1 = obj->GetIdentityHash();
1554 CHECK_EQ(hash, hash1);
1555 int hash2 = v8::Object::New()->GetIdentityHash();
1556 // Since the identity hash is essentially a random number two consecutive
1557 // objects should not be assigned the same hash code. If the test below fails
1558 // the random number generator should be evaluated.
1559 CHECK_NE(hash, hash2);
1560 i::Heap::CollectAllGarbage(false);
1561 int hash3 = v8::Object::New()->GetIdentityHash();
1562 // Make sure that the identity hash is not based on the initial address of
1563 // the object alone. If the test below fails the random number generator
1564 // should be evaluated.
1565 CHECK_NE(hash, hash3);
1566 int hash4 = obj->GetIdentityHash();
1567 CHECK_EQ(hash, hash4);
1568}
1569
1570
1571THREADED_TEST(HiddenProperties) {
1572 v8::HandleScope scope;
1573 LocalContext env;
1574
1575 v8::Local<v8::Object> obj = v8::Object::New();
1576 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1577 v8::Local<v8::String> empty = v8_str("");
1578 v8::Local<v8::String> prop_name = v8_str("prop_name");
1579
1580 i::Heap::CollectAllGarbage(false);
1581
1582 // Make sure delete of a non-existent hidden value works
1583 CHECK(obj->DeleteHiddenValue(key));
1584
1585 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1586 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1587 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1588 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1589
1590 i::Heap::CollectAllGarbage(false);
1591
1592 // Make sure we do not find the hidden property.
1593 CHECK(!obj->Has(empty));
1594 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1595 CHECK(obj->Get(empty)->IsUndefined());
1596 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1597 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1598 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1599 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1600
1601 i::Heap::CollectAllGarbage(false);
1602
1603 // Add another property and delete it afterwards to force the object in
1604 // slow case.
1605 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1606 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1607 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1608 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1609 CHECK(obj->Delete(prop_name));
1610 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1611
1612 i::Heap::CollectAllGarbage(false);
1613
1614 CHECK(obj->DeleteHiddenValue(key));
1615 CHECK(obj->GetHiddenValue(key).IsEmpty());
1616}
1617
1618
Steve Blockd0582a62009-12-15 09:54:21 +00001619static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001620static v8::Handle<Value> InterceptorForHiddenProperties(
1621 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001622 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001623 return v8::Handle<Value>();
1624}
1625
1626
1627THREADED_TEST(HiddenPropertiesWithInterceptors) {
1628 v8::HandleScope scope;
1629 LocalContext context;
1630
Steve Blockd0582a62009-12-15 09:54:21 +00001631 interceptor_for_hidden_properties_called = false;
1632
Steve Blocka7e24c12009-10-30 11:49:00 +00001633 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1634
1635 // Associate an interceptor with an object and start setting hidden values.
1636 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1637 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1638 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1639 Local<v8::Function> function = fun_templ->GetFunction();
1640 Local<v8::Object> obj = function->NewInstance();
1641 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1642 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001643 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001644}
1645
1646
1647THREADED_TEST(External) {
1648 v8::HandleScope scope;
1649 int x = 3;
1650 Local<v8::External> ext = v8::External::New(&x);
1651 LocalContext env;
1652 env->Global()->Set(v8_str("ext"), ext);
1653 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001654 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001655 int* ptr = static_cast<int*>(reext->Value());
1656 CHECK_EQ(x, 3);
1657 *ptr = 10;
1658 CHECK_EQ(x, 10);
1659
1660 // Make sure unaligned pointers are wrapped properly.
1661 char* data = i::StrDup("0123456789");
1662 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1663 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1664 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1665 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1666
1667 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1668 CHECK_EQ('0', *char_ptr);
1669 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1670 CHECK_EQ('1', *char_ptr);
1671 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1672 CHECK_EQ('2', *char_ptr);
1673 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1674 CHECK_EQ('3', *char_ptr);
1675 i::DeleteArray(data);
1676}
1677
1678
1679THREADED_TEST(GlobalHandle) {
1680 v8::Persistent<String> global;
1681 {
1682 v8::HandleScope scope;
1683 Local<String> str = v8_str("str");
1684 global = v8::Persistent<String>::New(str);
1685 }
1686 CHECK_EQ(global->Length(), 3);
1687 global.Dispose();
1688}
1689
1690
1691THREADED_TEST(ScriptException) {
1692 v8::HandleScope scope;
1693 LocalContext env;
1694 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1695 v8::TryCatch try_catch;
1696 Local<Value> result = script->Run();
1697 CHECK(result.IsEmpty());
1698 CHECK(try_catch.HasCaught());
1699 String::AsciiValue exception_value(try_catch.Exception());
1700 CHECK_EQ(*exception_value, "panama!");
1701}
1702
1703
1704bool message_received;
1705
1706
1707static void check_message(v8::Handle<v8::Message> message,
1708 v8::Handle<Value> data) {
1709 CHECK_EQ(5.76, data->NumberValue());
1710 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1711 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1712 message_received = true;
1713}
1714
1715
1716THREADED_TEST(MessageHandlerData) {
1717 message_received = false;
1718 v8::HandleScope scope;
1719 CHECK(!message_received);
1720 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1721 LocalContext context;
1722 v8::ScriptOrigin origin =
1723 v8::ScriptOrigin(v8_str("6.75"));
1724 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1725 &origin);
1726 script->SetData(v8_str("7.56"));
1727 script->Run();
1728 CHECK(message_received);
1729 // clear out the message listener
1730 v8::V8::RemoveMessageListeners(check_message);
1731}
1732
1733
1734THREADED_TEST(GetSetProperty) {
1735 v8::HandleScope scope;
1736 LocalContext context;
1737 context->Global()->Set(v8_str("foo"), v8_num(14));
1738 context->Global()->Set(v8_str("12"), v8_num(92));
1739 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1740 context->Global()->Set(v8_num(13), v8_num(56));
1741 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1742 CHECK_EQ(14, foo->Int32Value());
1743 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1744 CHECK_EQ(92, twelve->Int32Value());
1745 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1746 CHECK_EQ(32, sixteen->Int32Value());
1747 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1748 CHECK_EQ(56, thirteen->Int32Value());
1749 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1750 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1751 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1752 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1753 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1754 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1755 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1756 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1757 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1758}
1759
1760
1761THREADED_TEST(PropertyAttributes) {
1762 v8::HandleScope scope;
1763 LocalContext context;
1764 // read-only
1765 Local<String> prop = v8_str("read_only");
1766 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1767 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1768 Script::Compile(v8_str("read_only = 9"))->Run();
1769 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1770 context->Global()->Set(prop, v8_num(10));
1771 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1772 // dont-delete
1773 prop = v8_str("dont_delete");
1774 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1775 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1776 Script::Compile(v8_str("delete dont_delete"))->Run();
1777 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1778}
1779
1780
1781THREADED_TEST(Array) {
1782 v8::HandleScope scope;
1783 LocalContext context;
1784 Local<v8::Array> array = v8::Array::New();
1785 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001786 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001787 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001788 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001789 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001790 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001791 CHECK_EQ(3, array->Length());
1792 CHECK(!array->Has(0));
1793 CHECK(!array->Has(1));
1794 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001795 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001796 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001797 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001798 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001799 CHECK_EQ(1, arr->Get(0)->Int32Value());
1800 CHECK_EQ(2, arr->Get(1)->Int32Value());
1801 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001802}
1803
1804
1805v8::Handle<Value> HandleF(const v8::Arguments& args) {
1806 v8::HandleScope scope;
1807 ApiTestFuzzer::Fuzz();
1808 Local<v8::Array> result = v8::Array::New(args.Length());
1809 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001810 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001811 return scope.Close(result);
1812}
1813
1814
1815THREADED_TEST(Vector) {
1816 v8::HandleScope scope;
1817 Local<ObjectTemplate> global = ObjectTemplate::New();
1818 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1819 LocalContext context(0, global);
1820
1821 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001822 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001823 CHECK_EQ(0, a0->Length());
1824
1825 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001826 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001827 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001828 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001829
1830 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001831 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001832 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001833 CHECK_EQ(12, a2->Get(0)->Int32Value());
1834 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001835
1836 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001837 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001838 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001839 CHECK_EQ(14, a3->Get(0)->Int32Value());
1840 CHECK_EQ(15, a3->Get(1)->Int32Value());
1841 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001842
1843 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001844 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001845 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001846 CHECK_EQ(17, a4->Get(0)->Int32Value());
1847 CHECK_EQ(18, a4->Get(1)->Int32Value());
1848 CHECK_EQ(19, a4->Get(2)->Int32Value());
1849 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001850}
1851
1852
1853THREADED_TEST(FunctionCall) {
1854 v8::HandleScope scope;
1855 LocalContext context;
1856 CompileRun(
1857 "function Foo() {"
1858 " var result = [];"
1859 " for (var i = 0; i < arguments.length; i++) {"
1860 " result.push(arguments[i]);"
1861 " }"
1862 " return result;"
1863 "}");
1864 Local<Function> Foo =
1865 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1866
1867 v8::Handle<Value>* args0 = NULL;
1868 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1869 CHECK_EQ(0, a0->Length());
1870
1871 v8::Handle<Value> args1[] = { v8_num(1.1) };
1872 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1873 CHECK_EQ(1, a1->Length());
1874 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1875
1876 v8::Handle<Value> args2[] = { v8_num(2.2),
1877 v8_num(3.3) };
1878 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1879 CHECK_EQ(2, a2->Length());
1880 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1881 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1882
1883 v8::Handle<Value> args3[] = { v8_num(4.4),
1884 v8_num(5.5),
1885 v8_num(6.6) };
1886 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1887 CHECK_EQ(3, a3->Length());
1888 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1889 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1890 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1891
1892 v8::Handle<Value> args4[] = { v8_num(7.7),
1893 v8_num(8.8),
1894 v8_num(9.9),
1895 v8_num(10.11) };
1896 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1897 CHECK_EQ(4, a4->Length());
1898 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1899 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1900 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1901 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1902}
1903
1904
1905static const char* js_code_causing_out_of_memory =
1906 "var a = new Array(); while(true) a.push(a);";
1907
1908
1909// These tests run for a long time and prevent us from running tests
1910// that come after them so they cannot run in parallel.
1911TEST(OutOfMemory) {
1912 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001913 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001914 // Set heap limits.
1915 static const int K = 1024;
1916 v8::ResourceConstraints constraints;
1917 constraints.set_max_young_space_size(256 * K);
1918 constraints.set_max_old_space_size(4 * K * K);
1919 v8::SetResourceConstraints(&constraints);
1920
1921 // Execute a script that causes out of memory.
1922 v8::HandleScope scope;
1923 LocalContext context;
1924 v8::V8::IgnoreOutOfMemoryException();
1925 Local<Script> script =
1926 Script::Compile(String::New(js_code_causing_out_of_memory));
1927 Local<Value> result = script->Run();
1928
1929 // Check for out of memory state.
1930 CHECK(result.IsEmpty());
1931 CHECK(context->HasOutOfMemoryException());
1932}
1933
1934
1935v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1936 ApiTestFuzzer::Fuzz();
1937
1938 v8::HandleScope scope;
1939 LocalContext context;
1940 Local<Script> script =
1941 Script::Compile(String::New(js_code_causing_out_of_memory));
1942 Local<Value> result = script->Run();
1943
1944 // Check for out of memory state.
1945 CHECK(result.IsEmpty());
1946 CHECK(context->HasOutOfMemoryException());
1947
1948 return result;
1949}
1950
1951
1952TEST(OutOfMemoryNested) {
1953 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001954 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001955 // Set heap limits.
1956 static const int K = 1024;
1957 v8::ResourceConstraints constraints;
1958 constraints.set_max_young_space_size(256 * K);
1959 constraints.set_max_old_space_size(4 * K * K);
1960 v8::SetResourceConstraints(&constraints);
1961
1962 v8::HandleScope scope;
1963 Local<ObjectTemplate> templ = ObjectTemplate::New();
1964 templ->Set(v8_str("ProvokeOutOfMemory"),
1965 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1966 LocalContext context(0, templ);
1967 v8::V8::IgnoreOutOfMemoryException();
1968 Local<Value> result = CompileRun(
1969 "var thrown = false;"
1970 "try {"
1971 " ProvokeOutOfMemory();"
1972 "} catch (e) {"
1973 " thrown = true;"
1974 "}");
1975 // Check for out of memory state.
1976 CHECK(result.IsEmpty());
1977 CHECK(context->HasOutOfMemoryException());
1978}
1979
1980
1981TEST(HugeConsStringOutOfMemory) {
1982 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001983 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001984 v8::HandleScope scope;
1985 LocalContext context;
1986 // Set heap limits.
1987 static const int K = 1024;
1988 v8::ResourceConstraints constraints;
1989 constraints.set_max_young_space_size(256 * K);
1990 constraints.set_max_old_space_size(2 * K * K);
1991 v8::SetResourceConstraints(&constraints);
1992
1993 // Execute a script that causes out of memory.
1994 v8::V8::IgnoreOutOfMemoryException();
1995
1996 // Build huge string. This should fail with out of memory exception.
1997 Local<Value> result = CompileRun(
1998 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00001999 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00002000
2001 // Check for out of memory state.
2002 CHECK(result.IsEmpty());
2003 CHECK(context->HasOutOfMemoryException());
2004}
2005
2006
2007THREADED_TEST(ConstructCall) {
2008 v8::HandleScope scope;
2009 LocalContext context;
2010 CompileRun(
2011 "function Foo() {"
2012 " var result = [];"
2013 " for (var i = 0; i < arguments.length; i++) {"
2014 " result.push(arguments[i]);"
2015 " }"
2016 " return result;"
2017 "}");
2018 Local<Function> Foo =
2019 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2020
2021 v8::Handle<Value>* args0 = NULL;
2022 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2023 CHECK_EQ(0, a0->Length());
2024
2025 v8::Handle<Value> args1[] = { v8_num(1.1) };
2026 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2027 CHECK_EQ(1, a1->Length());
2028 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2029
2030 v8::Handle<Value> args2[] = { v8_num(2.2),
2031 v8_num(3.3) };
2032 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2033 CHECK_EQ(2, a2->Length());
2034 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2035 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2036
2037 v8::Handle<Value> args3[] = { v8_num(4.4),
2038 v8_num(5.5),
2039 v8_num(6.6) };
2040 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2041 CHECK_EQ(3, a3->Length());
2042 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2043 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2044 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2045
2046 v8::Handle<Value> args4[] = { v8_num(7.7),
2047 v8_num(8.8),
2048 v8_num(9.9),
2049 v8_num(10.11) };
2050 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2051 CHECK_EQ(4, a4->Length());
2052 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2053 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2054 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2055 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2056}
2057
2058
2059static void CheckUncle(v8::TryCatch* try_catch) {
2060 CHECK(try_catch->HasCaught());
2061 String::AsciiValue str_value(try_catch->Exception());
2062 CHECK_EQ(*str_value, "uncle?");
2063 try_catch->Reset();
2064}
2065
2066
Steve Block6ded16b2010-05-10 14:33:55 +01002067THREADED_TEST(ConversionNumber) {
2068 v8::HandleScope scope;
2069 LocalContext env;
2070 // Very large number.
2071 CompileRun("var obj = Math.pow(2,32) * 1237;");
2072 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2073 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2074 CHECK_EQ(0, obj->ToInt32()->Value());
2075 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2076 // Large number.
2077 CompileRun("var obj = -1234567890123;");
2078 obj = env->Global()->Get(v8_str("obj"));
2079 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2080 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2081 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2082 // Small positive integer.
2083 CompileRun("var obj = 42;");
2084 obj = env->Global()->Get(v8_str("obj"));
2085 CHECK_EQ(42.0, obj->ToNumber()->Value());
2086 CHECK_EQ(42, obj->ToInt32()->Value());
2087 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2088 // Negative integer.
2089 CompileRun("var obj = -37;");
2090 obj = env->Global()->Get(v8_str("obj"));
2091 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2092 CHECK_EQ(-37, obj->ToInt32()->Value());
2093 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2094 // Positive non-int32 integer.
2095 CompileRun("var obj = 0x81234567;");
2096 obj = env->Global()->Get(v8_str("obj"));
2097 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2098 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2099 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2100 // Fraction.
2101 CompileRun("var obj = 42.3;");
2102 obj = env->Global()->Get(v8_str("obj"));
2103 CHECK_EQ(42.3, obj->ToNumber()->Value());
2104 CHECK_EQ(42, obj->ToInt32()->Value());
2105 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2106 // Large negative fraction.
2107 CompileRun("var obj = -5726623061.75;");
2108 obj = env->Global()->Get(v8_str("obj"));
2109 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2110 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2111 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2112}
2113
2114
2115THREADED_TEST(isNumberType) {
2116 v8::HandleScope scope;
2117 LocalContext env;
2118 // Very large number.
2119 CompileRun("var obj = Math.pow(2,32) * 1237;");
2120 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2121 CHECK(!obj->IsInt32());
2122 CHECK(!obj->IsUint32());
2123 // Large negative number.
2124 CompileRun("var obj = -1234567890123;");
2125 obj = env->Global()->Get(v8_str("obj"));
2126 CHECK(!obj->IsInt32());
2127 CHECK(!obj->IsUint32());
2128 // Small positive integer.
2129 CompileRun("var obj = 42;");
2130 obj = env->Global()->Get(v8_str("obj"));
2131 CHECK(obj->IsInt32());
2132 CHECK(obj->IsUint32());
2133 // Negative integer.
2134 CompileRun("var obj = -37;");
2135 obj = env->Global()->Get(v8_str("obj"));
2136 CHECK(obj->IsInt32());
2137 CHECK(!obj->IsUint32());
2138 // Positive non-int32 integer.
2139 CompileRun("var obj = 0x81234567;");
2140 obj = env->Global()->Get(v8_str("obj"));
2141 CHECK(!obj->IsInt32());
2142 CHECK(obj->IsUint32());
2143 // Fraction.
2144 CompileRun("var obj = 42.3;");
2145 obj = env->Global()->Get(v8_str("obj"));
2146 CHECK(!obj->IsInt32());
2147 CHECK(!obj->IsUint32());
2148 // Large negative fraction.
2149 CompileRun("var obj = -5726623061.75;");
2150 obj = env->Global()->Get(v8_str("obj"));
2151 CHECK(!obj->IsInt32());
2152 CHECK(!obj->IsUint32());
2153}
2154
2155
Steve Blocka7e24c12009-10-30 11:49:00 +00002156THREADED_TEST(ConversionException) {
2157 v8::HandleScope scope;
2158 LocalContext env;
2159 CompileRun(
2160 "function TestClass() { };"
2161 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2162 "var obj = new TestClass();");
2163 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2164
2165 v8::TryCatch try_catch;
2166
2167 Local<Value> to_string_result = obj->ToString();
2168 CHECK(to_string_result.IsEmpty());
2169 CheckUncle(&try_catch);
2170
2171 Local<Value> to_number_result = obj->ToNumber();
2172 CHECK(to_number_result.IsEmpty());
2173 CheckUncle(&try_catch);
2174
2175 Local<Value> to_integer_result = obj->ToInteger();
2176 CHECK(to_integer_result.IsEmpty());
2177 CheckUncle(&try_catch);
2178
2179 Local<Value> to_uint32_result = obj->ToUint32();
2180 CHECK(to_uint32_result.IsEmpty());
2181 CheckUncle(&try_catch);
2182
2183 Local<Value> to_int32_result = obj->ToInt32();
2184 CHECK(to_int32_result.IsEmpty());
2185 CheckUncle(&try_catch);
2186
2187 Local<Value> to_object_result = v8::Undefined()->ToObject();
2188 CHECK(to_object_result.IsEmpty());
2189 CHECK(try_catch.HasCaught());
2190 try_catch.Reset();
2191
2192 int32_t int32_value = obj->Int32Value();
2193 CHECK_EQ(0, int32_value);
2194 CheckUncle(&try_catch);
2195
2196 uint32_t uint32_value = obj->Uint32Value();
2197 CHECK_EQ(0, uint32_value);
2198 CheckUncle(&try_catch);
2199
2200 double number_value = obj->NumberValue();
2201 CHECK_NE(0, IsNaN(number_value));
2202 CheckUncle(&try_catch);
2203
2204 int64_t integer_value = obj->IntegerValue();
2205 CHECK_EQ(0.0, static_cast<double>(integer_value));
2206 CheckUncle(&try_catch);
2207}
2208
2209
2210v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2211 ApiTestFuzzer::Fuzz();
2212 return v8::ThrowException(v8_str("konto"));
2213}
2214
2215
2216v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2217 if (args.Length() < 1) return v8::Boolean::New(false);
2218 v8::HandleScope scope;
2219 v8::TryCatch try_catch;
2220 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2221 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2222 return v8::Boolean::New(try_catch.HasCaught());
2223}
2224
2225
2226THREADED_TEST(APICatch) {
2227 v8::HandleScope scope;
2228 Local<ObjectTemplate> templ = ObjectTemplate::New();
2229 templ->Set(v8_str("ThrowFromC"),
2230 v8::FunctionTemplate::New(ThrowFromC));
2231 LocalContext context(0, templ);
2232 CompileRun(
2233 "var thrown = false;"
2234 "try {"
2235 " ThrowFromC();"
2236 "} catch (e) {"
2237 " thrown = true;"
2238 "}");
2239 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2240 CHECK(thrown->BooleanValue());
2241}
2242
2243
2244THREADED_TEST(APIThrowTryCatch) {
2245 v8::HandleScope scope;
2246 Local<ObjectTemplate> templ = ObjectTemplate::New();
2247 templ->Set(v8_str("ThrowFromC"),
2248 v8::FunctionTemplate::New(ThrowFromC));
2249 LocalContext context(0, templ);
2250 v8::TryCatch try_catch;
2251 CompileRun("ThrowFromC();");
2252 CHECK(try_catch.HasCaught());
2253}
2254
2255
2256// Test that a try-finally block doesn't shadow a try-catch block
2257// when setting up an external handler.
2258//
2259// BUG(271): Some of the exception propagation does not work on the
2260// ARM simulator because the simulator separates the C++ stack and the
2261// JS stack. This test therefore fails on the simulator. The test is
2262// not threaded to allow the threading tests to run on the simulator.
2263TEST(TryCatchInTryFinally) {
2264 v8::HandleScope scope;
2265 Local<ObjectTemplate> templ = ObjectTemplate::New();
2266 templ->Set(v8_str("CCatcher"),
2267 v8::FunctionTemplate::New(CCatcher));
2268 LocalContext context(0, templ);
2269 Local<Value> result = CompileRun("try {"
2270 " try {"
2271 " CCatcher('throw 7;');"
2272 " } finally {"
2273 " }"
2274 "} catch (e) {"
2275 "}");
2276 CHECK(result->IsTrue());
2277}
2278
2279
2280static void receive_message(v8::Handle<v8::Message> message,
2281 v8::Handle<v8::Value> data) {
2282 message->Get();
2283 message_received = true;
2284}
2285
2286
2287TEST(APIThrowMessage) {
2288 message_received = false;
2289 v8::HandleScope scope;
2290 v8::V8::AddMessageListener(receive_message);
2291 Local<ObjectTemplate> templ = ObjectTemplate::New();
2292 templ->Set(v8_str("ThrowFromC"),
2293 v8::FunctionTemplate::New(ThrowFromC));
2294 LocalContext context(0, templ);
2295 CompileRun("ThrowFromC();");
2296 CHECK(message_received);
2297 v8::V8::RemoveMessageListeners(check_message);
2298}
2299
2300
2301TEST(APIThrowMessageAndVerboseTryCatch) {
2302 message_received = false;
2303 v8::HandleScope scope;
2304 v8::V8::AddMessageListener(receive_message);
2305 Local<ObjectTemplate> templ = ObjectTemplate::New();
2306 templ->Set(v8_str("ThrowFromC"),
2307 v8::FunctionTemplate::New(ThrowFromC));
2308 LocalContext context(0, templ);
2309 v8::TryCatch try_catch;
2310 try_catch.SetVerbose(true);
2311 Local<Value> result = CompileRun("ThrowFromC();");
2312 CHECK(try_catch.HasCaught());
2313 CHECK(result.IsEmpty());
2314 CHECK(message_received);
2315 v8::V8::RemoveMessageListeners(check_message);
2316}
2317
2318
2319THREADED_TEST(ExternalScriptException) {
2320 v8::HandleScope scope;
2321 Local<ObjectTemplate> templ = ObjectTemplate::New();
2322 templ->Set(v8_str("ThrowFromC"),
2323 v8::FunctionTemplate::New(ThrowFromC));
2324 LocalContext context(0, templ);
2325
2326 v8::TryCatch try_catch;
2327 Local<Script> script
2328 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2329 Local<Value> result = script->Run();
2330 CHECK(result.IsEmpty());
2331 CHECK(try_catch.HasCaught());
2332 String::AsciiValue exception_value(try_catch.Exception());
2333 CHECK_EQ("konto", *exception_value);
2334}
2335
2336
2337
2338v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2339 ApiTestFuzzer::Fuzz();
2340 CHECK_EQ(4, args.Length());
2341 int count = args[0]->Int32Value();
2342 int cInterval = args[2]->Int32Value();
2343 if (count == 0) {
2344 return v8::ThrowException(v8_str("FromC"));
2345 } else {
2346 Local<v8::Object> global = Context::GetCurrent()->Global();
2347 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2348 v8::Handle<Value> argv[] = { v8_num(count - 1),
2349 args[1],
2350 args[2],
2351 args[3] };
2352 if (count % cInterval == 0) {
2353 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002354 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002355 int expected = args[3]->Int32Value();
2356 if (try_catch.HasCaught()) {
2357 CHECK_EQ(expected, count);
2358 CHECK(result.IsEmpty());
2359 CHECK(!i::Top::has_scheduled_exception());
2360 } else {
2361 CHECK_NE(expected, count);
2362 }
2363 return result;
2364 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002365 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002366 }
2367 }
2368}
2369
2370
2371v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2372 ApiTestFuzzer::Fuzz();
2373 CHECK_EQ(3, args.Length());
2374 bool equality = args[0]->BooleanValue();
2375 int count = args[1]->Int32Value();
2376 int expected = args[2]->Int32Value();
2377 if (equality) {
2378 CHECK_EQ(count, expected);
2379 } else {
2380 CHECK_NE(count, expected);
2381 }
2382 return v8::Undefined();
2383}
2384
2385
2386THREADED_TEST(EvalInTryFinally) {
2387 v8::HandleScope scope;
2388 LocalContext context;
2389 v8::TryCatch try_catch;
2390 CompileRun("(function() {"
2391 " try {"
2392 " eval('asldkf (*&^&*^');"
2393 " } finally {"
2394 " return;"
2395 " }"
2396 "})()");
2397 CHECK(!try_catch.HasCaught());
2398}
2399
2400
2401// This test works by making a stack of alternating JavaScript and C
2402// activations. These activations set up exception handlers with regular
2403// intervals, one interval for C activations and another for JavaScript
2404// activations. When enough activations have been created an exception is
2405// thrown and we check that the right activation catches the exception and that
2406// no other activations do. The right activation is always the topmost one with
2407// a handler, regardless of whether it is in JavaScript or C.
2408//
2409// The notation used to describe a test case looks like this:
2410//
2411// *JS[4] *C[3] @JS[2] C[1] JS[0]
2412//
2413// Each entry is an activation, either JS or C. The index is the count at that
2414// level. Stars identify activations with exception handlers, the @ identifies
2415// the exception handler that should catch the exception.
2416//
2417// BUG(271): Some of the exception propagation does not work on the
2418// ARM simulator because the simulator separates the C++ stack and the
2419// JS stack. This test therefore fails on the simulator. The test is
2420// not threaded to allow the threading tests to run on the simulator.
2421TEST(ExceptionOrder) {
2422 v8::HandleScope scope;
2423 Local<ObjectTemplate> templ = ObjectTemplate::New();
2424 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2425 templ->Set(v8_str("CThrowCountDown"),
2426 v8::FunctionTemplate::New(CThrowCountDown));
2427 LocalContext context(0, templ);
2428 CompileRun(
2429 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2430 " if (count == 0) throw 'FromJS';"
2431 " if (count % jsInterval == 0) {"
2432 " try {"
2433 " var value = CThrowCountDown(count - 1,"
2434 " jsInterval,"
2435 " cInterval,"
2436 " expected);"
2437 " check(false, count, expected);"
2438 " return value;"
2439 " } catch (e) {"
2440 " check(true, count, expected);"
2441 " }"
2442 " } else {"
2443 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2444 " }"
2445 "}");
2446 Local<Function> fun =
2447 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2448
2449 const int argc = 4;
2450 // count jsInterval cInterval expected
2451
2452 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2453 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2454 fun->Call(fun, argc, a0);
2455
2456 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2457 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2458 fun->Call(fun, argc, a1);
2459
2460 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2461 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2462 fun->Call(fun, argc, a2);
2463
2464 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2465 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2466 fun->Call(fun, argc, a3);
2467
2468 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2469 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2470 fun->Call(fun, argc, a4);
2471
2472 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2473 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2474 fun->Call(fun, argc, a5);
2475}
2476
2477
2478v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2479 ApiTestFuzzer::Fuzz();
2480 CHECK_EQ(1, args.Length());
2481 return v8::ThrowException(args[0]);
2482}
2483
2484
2485THREADED_TEST(ThrowValues) {
2486 v8::HandleScope scope;
2487 Local<ObjectTemplate> templ = ObjectTemplate::New();
2488 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2489 LocalContext context(0, templ);
2490 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2491 "function Run(obj) {"
2492 " try {"
2493 " Throw(obj);"
2494 " } catch (e) {"
2495 " return e;"
2496 " }"
2497 " return 'no exception';"
2498 "}"
2499 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2500 CHECK_EQ(5, result->Length());
2501 CHECK(result->Get(v8::Integer::New(0))->IsString());
2502 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2503 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2504 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2505 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2506 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2507 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2508}
2509
2510
2511THREADED_TEST(CatchZero) {
2512 v8::HandleScope scope;
2513 LocalContext context;
2514 v8::TryCatch try_catch;
2515 CHECK(!try_catch.HasCaught());
2516 Script::Compile(v8_str("throw 10"))->Run();
2517 CHECK(try_catch.HasCaught());
2518 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2519 try_catch.Reset();
2520 CHECK(!try_catch.HasCaught());
2521 Script::Compile(v8_str("throw 0"))->Run();
2522 CHECK(try_catch.HasCaught());
2523 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2524}
2525
2526
2527THREADED_TEST(CatchExceptionFromWith) {
2528 v8::HandleScope scope;
2529 LocalContext context;
2530 v8::TryCatch try_catch;
2531 CHECK(!try_catch.HasCaught());
2532 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2533 CHECK(try_catch.HasCaught());
2534}
2535
2536
2537THREADED_TEST(Equality) {
2538 v8::HandleScope scope;
2539 LocalContext context;
2540 // Check that equality works at all before relying on CHECK_EQ
2541 CHECK(v8_str("a")->Equals(v8_str("a")));
2542 CHECK(!v8_str("a")->Equals(v8_str("b")));
2543
2544 CHECK_EQ(v8_str("a"), v8_str("a"));
2545 CHECK_NE(v8_str("a"), v8_str("b"));
2546 CHECK_EQ(v8_num(1), v8_num(1));
2547 CHECK_EQ(v8_num(1.00), v8_num(1));
2548 CHECK_NE(v8_num(1), v8_num(2));
2549
2550 // Assume String is not symbol.
2551 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2552 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2553 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2554 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2555 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2556 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2557 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2558 CHECK(!not_a_number->StrictEquals(not_a_number));
2559 CHECK(v8::False()->StrictEquals(v8::False()));
2560 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2561
2562 v8::Handle<v8::Object> obj = v8::Object::New();
2563 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2564 CHECK(alias->StrictEquals(obj));
2565 alias.Dispose();
2566}
2567
2568
2569THREADED_TEST(MultiRun) {
2570 v8::HandleScope scope;
2571 LocalContext context;
2572 Local<Script> script = Script::Compile(v8_str("x"));
2573 for (int i = 0; i < 10; i++)
2574 script->Run();
2575}
2576
2577
2578static v8::Handle<Value> GetXValue(Local<String> name,
2579 const AccessorInfo& info) {
2580 ApiTestFuzzer::Fuzz();
2581 CHECK_EQ(info.Data(), v8_str("donut"));
2582 CHECK_EQ(name, v8_str("x"));
2583 return name;
2584}
2585
2586
2587THREADED_TEST(SimplePropertyRead) {
2588 v8::HandleScope scope;
2589 Local<ObjectTemplate> templ = ObjectTemplate::New();
2590 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2591 LocalContext context;
2592 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2593 Local<Script> script = Script::Compile(v8_str("obj.x"));
2594 for (int i = 0; i < 10; i++) {
2595 Local<Value> result = script->Run();
2596 CHECK_EQ(result, v8_str("x"));
2597 }
2598}
2599
Andrei Popescu31002712010-02-23 13:46:05 +00002600THREADED_TEST(DefinePropertyOnAPIAccessor) {
2601 v8::HandleScope scope;
2602 Local<ObjectTemplate> templ = ObjectTemplate::New();
2603 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2604 LocalContext context;
2605 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2606
2607 // Uses getOwnPropertyDescriptor to check the configurable status
2608 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002609 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002610 "obj, 'x');"
2611 "prop.configurable;"));
2612 Local<Value> result = script_desc->Run();
2613 CHECK_EQ(result->BooleanValue(), true);
2614
2615 // Redefine get - but still configurable
2616 Local<Script> script_define
2617 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2618 " configurable: true };"
2619 "Object.defineProperty(obj, 'x', desc);"
2620 "obj.x"));
2621 result = script_define->Run();
2622 CHECK_EQ(result, v8_num(42));
2623
2624 // Check that the accessor is still configurable
2625 result = script_desc->Run();
2626 CHECK_EQ(result->BooleanValue(), true);
2627
2628 // Redefine to a non-configurable
2629 script_define
2630 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2631 " configurable: false };"
2632 "Object.defineProperty(obj, 'x', desc);"
2633 "obj.x"));
2634 result = script_define->Run();
2635 CHECK_EQ(result, v8_num(43));
2636 result = script_desc->Run();
2637 CHECK_EQ(result->BooleanValue(), false);
2638
2639 // Make sure that it is not possible to redefine again
2640 v8::TryCatch try_catch;
2641 result = script_define->Run();
2642 CHECK(try_catch.HasCaught());
2643 String::AsciiValue exception_value(try_catch.Exception());
2644 CHECK_EQ(*exception_value,
2645 "TypeError: Cannot redefine property: defineProperty");
2646}
2647
2648THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2649 v8::HandleScope scope;
2650 Local<ObjectTemplate> templ = ObjectTemplate::New();
2651 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2652 LocalContext context;
2653 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2654
2655 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2656 "Object.getOwnPropertyDescriptor( "
2657 "obj, 'x');"
2658 "prop.configurable;"));
2659 Local<Value> result = script_desc->Run();
2660 CHECK_EQ(result->BooleanValue(), true);
2661
2662 Local<Script> script_define =
2663 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2664 " configurable: true };"
2665 "Object.defineProperty(obj, 'x', desc);"
2666 "obj.x"));
2667 result = script_define->Run();
2668 CHECK_EQ(result, v8_num(42));
2669
2670
2671 result = script_desc->Run();
2672 CHECK_EQ(result->BooleanValue(), true);
2673
2674
2675 script_define =
2676 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2677 " configurable: false };"
2678 "Object.defineProperty(obj, 'x', desc);"
2679 "obj.x"));
2680 result = script_define->Run();
2681 CHECK_EQ(result, v8_num(43));
2682 result = script_desc->Run();
2683
2684 CHECK_EQ(result->BooleanValue(), false);
2685
2686 v8::TryCatch try_catch;
2687 result = script_define->Run();
2688 CHECK(try_catch.HasCaught());
2689 String::AsciiValue exception_value(try_catch.Exception());
2690 CHECK_EQ(*exception_value,
2691 "TypeError: Cannot redefine property: defineProperty");
2692}
2693
2694
Leon Clarkef7060e22010-06-03 12:02:55 +01002695static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2696 char const* name) {
2697 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2698}
Andrei Popescu31002712010-02-23 13:46:05 +00002699
2700
Leon Clarkef7060e22010-06-03 12:02:55 +01002701THREADED_TEST(DefineAPIAccessorOnObject) {
2702 v8::HandleScope scope;
2703 Local<ObjectTemplate> templ = ObjectTemplate::New();
2704 LocalContext context;
2705
2706 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2707 CompileRun("var obj2 = {};");
2708
2709 CHECK(CompileRun("obj1.x")->IsUndefined());
2710 CHECK(CompileRun("obj2.x")->IsUndefined());
2711
2712 CHECK(GetGlobalProperty(&context, "obj1")->
2713 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2714
2715 ExpectString("obj1.x", "x");
2716 CHECK(CompileRun("obj2.x")->IsUndefined());
2717
2718 CHECK(GetGlobalProperty(&context, "obj2")->
2719 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2720
2721 ExpectString("obj1.x", "x");
2722 ExpectString("obj2.x", "x");
2723
2724 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2725 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2726
2727 CompileRun("Object.defineProperty(obj1, 'x',"
2728 "{ get: function() { return 'y'; }, configurable: true })");
2729
2730 ExpectString("obj1.x", "y");
2731 ExpectString("obj2.x", "x");
2732
2733 CompileRun("Object.defineProperty(obj2, 'x',"
2734 "{ get: function() { return 'y'; }, configurable: true })");
2735
2736 ExpectString("obj1.x", "y");
2737 ExpectString("obj2.x", "y");
2738
2739 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2740 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2741
2742 CHECK(GetGlobalProperty(&context, "obj1")->
2743 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2744 CHECK(GetGlobalProperty(&context, "obj2")->
2745 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2746
2747 ExpectString("obj1.x", "x");
2748 ExpectString("obj2.x", "x");
2749
2750 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2751 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2752
2753 // Define getters/setters, but now make them not configurable.
2754 CompileRun("Object.defineProperty(obj1, 'x',"
2755 "{ get: function() { return 'z'; }, configurable: false })");
2756 CompileRun("Object.defineProperty(obj2, 'x',"
2757 "{ get: function() { return 'z'; }, configurable: false })");
2758
2759 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2760 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2761
2762 ExpectString("obj1.x", "z");
2763 ExpectString("obj2.x", "z");
2764
2765 CHECK(!GetGlobalProperty(&context, "obj1")->
2766 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2767 CHECK(!GetGlobalProperty(&context, "obj2")->
2768 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2769
2770 ExpectString("obj1.x", "z");
2771 ExpectString("obj2.x", "z");
2772}
2773
2774
2775THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2776 v8::HandleScope scope;
2777 Local<ObjectTemplate> templ = ObjectTemplate::New();
2778 LocalContext context;
2779
2780 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2781 CompileRun("var obj2 = {};");
2782
2783 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2784 v8_str("x"),
2785 GetXValue, NULL,
2786 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2787 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2788 v8_str("x"),
2789 GetXValue, NULL,
2790 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2791
2792 ExpectString("obj1.x", "x");
2793 ExpectString("obj2.x", "x");
2794
2795 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2796 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2797
2798 CHECK(!GetGlobalProperty(&context, "obj1")->
2799 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2800 CHECK(!GetGlobalProperty(&context, "obj2")->
2801 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2802
2803 {
2804 v8::TryCatch try_catch;
2805 CompileRun("Object.defineProperty(obj1, 'x',"
2806 "{get: function() { return 'func'; }})");
2807 CHECK(try_catch.HasCaught());
2808 String::AsciiValue exception_value(try_catch.Exception());
2809 CHECK_EQ(*exception_value,
2810 "TypeError: Cannot redefine property: defineProperty");
2811 }
2812 {
2813 v8::TryCatch try_catch;
2814 CompileRun("Object.defineProperty(obj2, 'x',"
2815 "{get: function() { return 'func'; }})");
2816 CHECK(try_catch.HasCaught());
2817 String::AsciiValue exception_value(try_catch.Exception());
2818 CHECK_EQ(*exception_value,
2819 "TypeError: Cannot redefine property: defineProperty");
2820 }
2821}
2822
2823
2824static v8::Handle<Value> Get239Value(Local<String> name,
2825 const AccessorInfo& info) {
2826 ApiTestFuzzer::Fuzz();
2827 CHECK_EQ(info.Data(), v8_str("donut"));
2828 CHECK_EQ(name, v8_str("239"));
2829 return name;
2830}
2831
2832
2833THREADED_TEST(ElementAPIAccessor) {
2834 v8::HandleScope scope;
2835 Local<ObjectTemplate> templ = ObjectTemplate::New();
2836 LocalContext context;
2837
2838 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2839 CompileRun("var obj2 = {};");
2840
2841 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2842 v8_str("239"),
2843 Get239Value, NULL,
2844 v8_str("donut")));
2845 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2846 v8_str("239"),
2847 Get239Value, NULL,
2848 v8_str("donut")));
2849
2850 ExpectString("obj1[239]", "239");
2851 ExpectString("obj2[239]", "239");
2852 ExpectString("obj1['239']", "239");
2853 ExpectString("obj2['239']", "239");
2854}
2855
Steve Blocka7e24c12009-10-30 11:49:00 +00002856
2857v8::Persistent<Value> xValue;
2858
2859
2860static void SetXValue(Local<String> name,
2861 Local<Value> value,
2862 const AccessorInfo& info) {
2863 CHECK_EQ(value, v8_num(4));
2864 CHECK_EQ(info.Data(), v8_str("donut"));
2865 CHECK_EQ(name, v8_str("x"));
2866 CHECK(xValue.IsEmpty());
2867 xValue = v8::Persistent<Value>::New(value);
2868}
2869
2870
2871THREADED_TEST(SimplePropertyWrite) {
2872 v8::HandleScope scope;
2873 Local<ObjectTemplate> templ = ObjectTemplate::New();
2874 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2875 LocalContext context;
2876 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2877 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2878 for (int i = 0; i < 10; i++) {
2879 CHECK(xValue.IsEmpty());
2880 script->Run();
2881 CHECK_EQ(v8_num(4), xValue);
2882 xValue.Dispose();
2883 xValue = v8::Persistent<Value>();
2884 }
2885}
2886
2887
2888static v8::Handle<Value> XPropertyGetter(Local<String> property,
2889 const AccessorInfo& info) {
2890 ApiTestFuzzer::Fuzz();
2891 CHECK(info.Data()->IsUndefined());
2892 return property;
2893}
2894
2895
2896THREADED_TEST(NamedInterceptorPropertyRead) {
2897 v8::HandleScope scope;
2898 Local<ObjectTemplate> templ = ObjectTemplate::New();
2899 templ->SetNamedPropertyHandler(XPropertyGetter);
2900 LocalContext context;
2901 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2902 Local<Script> script = Script::Compile(v8_str("obj.x"));
2903 for (int i = 0; i < 10; i++) {
2904 Local<Value> result = script->Run();
2905 CHECK_EQ(result, v8_str("x"));
2906 }
2907}
2908
2909
Steve Block6ded16b2010-05-10 14:33:55 +01002910THREADED_TEST(NamedInterceptorDictionaryIC) {
2911 v8::HandleScope scope;
2912 Local<ObjectTemplate> templ = ObjectTemplate::New();
2913 templ->SetNamedPropertyHandler(XPropertyGetter);
2914 LocalContext context;
2915 // Create an object with a named interceptor.
2916 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2917 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2918 for (int i = 0; i < 10; i++) {
2919 Local<Value> result = script->Run();
2920 CHECK_EQ(result, v8_str("x"));
2921 }
2922 // Create a slow case object and a function accessing a property in
2923 // that slow case object (with dictionary probing in generated
2924 // code). Then force object with a named interceptor into slow-case,
2925 // pass it to the function, and check that the interceptor is called
2926 // instead of accessing the local property.
2927 Local<Value> result =
2928 CompileRun("function get_x(o) { return o.x; };"
2929 "var obj = { x : 42, y : 0 };"
2930 "delete obj.y;"
2931 "for (var i = 0; i < 10; i++) get_x(obj);"
2932 "interceptor_obj.x = 42;"
2933 "interceptor_obj.y = 10;"
2934 "delete interceptor_obj.y;"
2935 "get_x(interceptor_obj)");
2936 CHECK_EQ(result, v8_str("x"));
2937}
2938
2939
Andrei Popescu402d9372010-02-26 13:31:12 +00002940static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2941 const AccessorInfo& info) {
2942 // Set x on the prototype object and do not handle the get request.
2943 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002944 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002945 return v8::Handle<Value>();
2946}
2947
2948
2949// This is a regression test for http://crbug.com/20104. Map
2950// transitions should not interfere with post interceptor lookup.
2951THREADED_TEST(NamedInterceptorMapTransitionRead) {
2952 v8::HandleScope scope;
2953 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
2954 Local<v8::ObjectTemplate> instance_template
2955 = function_template->InstanceTemplate();
2956 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
2957 LocalContext context;
2958 context->Global()->Set(v8_str("F"), function_template->GetFunction());
2959 // Create an instance of F and introduce a map transition for x.
2960 CompileRun("var o = new F(); o.x = 23;");
2961 // Create an instance of F and invoke the getter. The result should be 23.
2962 Local<Value> result = CompileRun("o = new F(); o.x");
2963 CHECK_EQ(result->Int32Value(), 23);
2964}
2965
2966
Steve Blocka7e24c12009-10-30 11:49:00 +00002967static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2968 const AccessorInfo& info) {
2969 ApiTestFuzzer::Fuzz();
2970 if (index == 37) {
2971 return v8::Handle<Value>(v8_num(625));
2972 }
2973 return v8::Handle<Value>();
2974}
2975
2976
2977static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2978 Local<Value> value,
2979 const AccessorInfo& info) {
2980 ApiTestFuzzer::Fuzz();
2981 if (index == 39) {
2982 return value;
2983 }
2984 return v8::Handle<Value>();
2985}
2986
2987
2988THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2989 v8::HandleScope scope;
2990 Local<ObjectTemplate> templ = ObjectTemplate::New();
2991 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2992 IndexedPropertySetter);
2993 LocalContext context;
2994 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2995 Local<Script> getter_script = Script::Compile(v8_str(
2996 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2997 Local<Script> setter_script = Script::Compile(v8_str(
2998 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2999 "obj[17] = 23;"
3000 "obj.foo;"));
3001 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3002 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3003 "obj[39] = 47;"
3004 "obj.foo;")); // This setter should not run, due to the interceptor.
3005 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3006 "obj[37];"));
3007 Local<Value> result = getter_script->Run();
3008 CHECK_EQ(v8_num(5), result);
3009 result = setter_script->Run();
3010 CHECK_EQ(v8_num(23), result);
3011 result = interceptor_setter_script->Run();
3012 CHECK_EQ(v8_num(23), result);
3013 result = interceptor_getter_script->Run();
3014 CHECK_EQ(v8_num(625), result);
3015}
3016
3017
Leon Clarked91b9f72010-01-27 17:25:45 +00003018static v8::Handle<Value> IdentityIndexedPropertyGetter(
3019 uint32_t index,
3020 const AccessorInfo& info) {
3021 return v8::Integer::New(index);
3022}
3023
3024
3025THREADED_TEST(IndexedInterceptorWithNoSetter) {
3026 v8::HandleScope scope;
3027 Local<ObjectTemplate> templ = ObjectTemplate::New();
3028 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3029
3030 LocalContext context;
3031 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3032
3033 const char* code =
3034 "try {"
3035 " obj[0] = 239;"
3036 " for (var i = 0; i < 100; i++) {"
3037 " var v = obj[0];"
3038 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3039 " }"
3040 " 'PASSED'"
3041 "} catch(e) {"
3042 " e"
3043 "}";
3044 ExpectString(code, "PASSED");
3045}
3046
3047
Andrei Popescu402d9372010-02-26 13:31:12 +00003048THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3049 v8::HandleScope scope;
3050 Local<ObjectTemplate> templ = ObjectTemplate::New();
3051 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3052
3053 LocalContext context;
3054 Local<v8::Object> obj = templ->NewInstance();
3055 obj->TurnOnAccessCheck();
3056 context->Global()->Set(v8_str("obj"), obj);
3057
3058 const char* code =
3059 "try {"
3060 " for (var i = 0; i < 100; i++) {"
3061 " var v = obj[0];"
3062 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3063 " }"
3064 " 'PASSED'"
3065 "} catch(e) {"
3066 " e"
3067 "}";
3068 ExpectString(code, "PASSED");
3069}
3070
3071
3072THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3073 i::FLAG_allow_natives_syntax = true;
3074 v8::HandleScope scope;
3075 Local<ObjectTemplate> templ = ObjectTemplate::New();
3076 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3077
3078 LocalContext context;
3079 Local<v8::Object> obj = templ->NewInstance();
3080 context->Global()->Set(v8_str("obj"), obj);
3081
3082 const char* code =
3083 "try {"
3084 " for (var i = 0; i < 100; i++) {"
3085 " var expected = i;"
3086 " if (i == 5) {"
3087 " %EnableAccessChecks(obj);"
3088 " expected = undefined;"
3089 " }"
3090 " var v = obj[i];"
3091 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3092 " if (i == 5) %DisableAccessChecks(obj);"
3093 " }"
3094 " 'PASSED'"
3095 "} catch(e) {"
3096 " e"
3097 "}";
3098 ExpectString(code, "PASSED");
3099}
3100
3101
3102THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3103 v8::HandleScope scope;
3104 Local<ObjectTemplate> templ = ObjectTemplate::New();
3105 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3106
3107 LocalContext context;
3108 Local<v8::Object> obj = templ->NewInstance();
3109 context->Global()->Set(v8_str("obj"), obj);
3110
3111 const char* code =
3112 "try {"
3113 " for (var i = 0; i < 100; i++) {"
3114 " var v = obj[i];"
3115 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3116 " }"
3117 " 'PASSED'"
3118 "} catch(e) {"
3119 " e"
3120 "}";
3121 ExpectString(code, "PASSED");
3122}
3123
3124
3125THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3126 v8::HandleScope scope;
3127 Local<ObjectTemplate> templ = ObjectTemplate::New();
3128 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3129
3130 LocalContext context;
3131 Local<v8::Object> obj = templ->NewInstance();
3132 context->Global()->Set(v8_str("obj"), obj);
3133
3134 const char* code =
3135 "try {"
3136 " for (var i = 0; i < 100; i++) {"
3137 " var expected = i;"
3138 " if (i == 50) {"
3139 " i = 'foobar';"
3140 " expected = undefined;"
3141 " }"
3142 " var v = obj[i];"
3143 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3144 " }"
3145 " 'PASSED'"
3146 "} catch(e) {"
3147 " e"
3148 "}";
3149 ExpectString(code, "PASSED");
3150}
3151
3152
3153THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3154 v8::HandleScope scope;
3155 Local<ObjectTemplate> templ = ObjectTemplate::New();
3156 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3157
3158 LocalContext context;
3159 Local<v8::Object> obj = templ->NewInstance();
3160 context->Global()->Set(v8_str("obj"), obj);
3161
3162 const char* code =
3163 "var original = obj;"
3164 "try {"
3165 " for (var i = 0; i < 100; i++) {"
3166 " var expected = i;"
3167 " if (i == 50) {"
3168 " obj = {50: 'foobar'};"
3169 " expected = 'foobar';"
3170 " }"
3171 " var v = obj[i];"
3172 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3173 " if (i == 50) obj = original;"
3174 " }"
3175 " 'PASSED'"
3176 "} catch(e) {"
3177 " e"
3178 "}";
3179 ExpectString(code, "PASSED");
3180}
3181
3182
3183THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3184 v8::HandleScope scope;
3185 Local<ObjectTemplate> templ = ObjectTemplate::New();
3186 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3187
3188 LocalContext context;
3189 Local<v8::Object> obj = templ->NewInstance();
3190 context->Global()->Set(v8_str("obj"), obj);
3191
3192 const char* code =
3193 "var original = obj;"
3194 "try {"
3195 " for (var i = 0; i < 100; i++) {"
3196 " var expected = i;"
3197 " if (i == 5) {"
3198 " obj = 239;"
3199 " expected = undefined;"
3200 " }"
3201 " var v = obj[i];"
3202 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3203 " if (i == 5) obj = original;"
3204 " }"
3205 " 'PASSED'"
3206 "} catch(e) {"
3207 " e"
3208 "}";
3209 ExpectString(code, "PASSED");
3210}
3211
3212
3213THREADED_TEST(IndexedInterceptorOnProto) {
3214 v8::HandleScope scope;
3215 Local<ObjectTemplate> templ = ObjectTemplate::New();
3216 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3217
3218 LocalContext context;
3219 Local<v8::Object> obj = templ->NewInstance();
3220 context->Global()->Set(v8_str("obj"), obj);
3221
3222 const char* code =
3223 "var o = {__proto__: obj};"
3224 "try {"
3225 " for (var i = 0; i < 100; i++) {"
3226 " var v = o[i];"
3227 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3228 " }"
3229 " 'PASSED'"
3230 "} catch(e) {"
3231 " e"
3232 "}";
3233 ExpectString(code, "PASSED");
3234}
3235
3236
Steve Blocka7e24c12009-10-30 11:49:00 +00003237THREADED_TEST(MultiContexts) {
3238 v8::HandleScope scope;
3239 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3240 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3241
3242 Local<String> password = v8_str("Password");
3243
3244 // Create an environment
3245 LocalContext context0(0, templ);
3246 context0->SetSecurityToken(password);
3247 v8::Handle<v8::Object> global0 = context0->Global();
3248 global0->Set(v8_str("custom"), v8_num(1234));
3249 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3250
3251 // Create an independent environment
3252 LocalContext context1(0, templ);
3253 context1->SetSecurityToken(password);
3254 v8::Handle<v8::Object> global1 = context1->Global();
3255 global1->Set(v8_str("custom"), v8_num(1234));
3256 CHECK_NE(global0, global1);
3257 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3258 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3259
3260 // Now create a new context with the old global
3261 LocalContext context2(0, templ, global1);
3262 context2->SetSecurityToken(password);
3263 v8::Handle<v8::Object> global2 = context2->Global();
3264 CHECK_EQ(global1, global2);
3265 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3266 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3267}
3268
3269
3270THREADED_TEST(FunctionPrototypeAcrossContexts) {
3271 // Make sure that functions created by cloning boilerplates cannot
3272 // communicate through their __proto__ field.
3273
3274 v8::HandleScope scope;
3275
3276 LocalContext env0;
3277 v8::Handle<v8::Object> global0 =
3278 env0->Global();
3279 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003280 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003281 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003282 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003283 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003284 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003285 proto0->Set(v8_str("custom"), v8_num(1234));
3286
3287 LocalContext env1;
3288 v8::Handle<v8::Object> global1 =
3289 env1->Global();
3290 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003291 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003292 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003293 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003294 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003295 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003296 CHECK(!proto1->Has(v8_str("custom")));
3297}
3298
3299
3300THREADED_TEST(Regress892105) {
3301 // Make sure that object and array literals created by cloning
3302 // boilerplates cannot communicate through their __proto__
3303 // field. This is rather difficult to check, but we try to add stuff
3304 // to Object.prototype and Array.prototype and create a new
3305 // environment. This should succeed.
3306
3307 v8::HandleScope scope;
3308
3309 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3310 "Array.prototype.arr = 4567;"
3311 "8901");
3312
3313 LocalContext env0;
3314 Local<Script> script0 = Script::Compile(source);
3315 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3316
3317 LocalContext env1;
3318 Local<Script> script1 = Script::Compile(source);
3319 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3320}
3321
3322
Steve Blocka7e24c12009-10-30 11:49:00 +00003323THREADED_TEST(UndetectableObject) {
3324 v8::HandleScope scope;
3325 LocalContext env;
3326
3327 Local<v8::FunctionTemplate> desc =
3328 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3329 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3330
3331 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3332 env->Global()->Set(v8_str("undetectable"), obj);
3333
3334 ExpectString("undetectable.toString()", "[object Object]");
3335 ExpectString("typeof undetectable", "undefined");
3336 ExpectString("typeof(undetectable)", "undefined");
3337 ExpectBoolean("typeof undetectable == 'undefined'", true);
3338 ExpectBoolean("typeof undetectable == 'object'", false);
3339 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3340 ExpectBoolean("!undetectable", true);
3341
3342 ExpectObject("true&&undetectable", obj);
3343 ExpectBoolean("false&&undetectable", false);
3344 ExpectBoolean("true||undetectable", true);
3345 ExpectObject("false||undetectable", obj);
3346
3347 ExpectObject("undetectable&&true", obj);
3348 ExpectObject("undetectable&&false", obj);
3349 ExpectBoolean("undetectable||true", true);
3350 ExpectBoolean("undetectable||false", false);
3351
3352 ExpectBoolean("undetectable==null", true);
3353 ExpectBoolean("null==undetectable", true);
3354 ExpectBoolean("undetectable==undefined", true);
3355 ExpectBoolean("undefined==undetectable", true);
3356 ExpectBoolean("undetectable==undetectable", true);
3357
3358
3359 ExpectBoolean("undetectable===null", false);
3360 ExpectBoolean("null===undetectable", false);
3361 ExpectBoolean("undetectable===undefined", false);
3362 ExpectBoolean("undefined===undetectable", false);
3363 ExpectBoolean("undetectable===undetectable", true);
3364}
3365
3366
Steve Block8defd9f2010-07-08 12:39:36 +01003367
3368THREADED_TEST(ExtensibleOnUndetectable) {
3369 v8::HandleScope scope;
3370 LocalContext env;
3371
3372 Local<v8::FunctionTemplate> desc =
3373 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3374 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3375
3376 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3377 env->Global()->Set(v8_str("undetectable"), obj);
3378
3379 Local<String> source = v8_str("undetectable.x = 42;"
3380 "undetectable.x");
3381
3382 Local<Script> script = Script::Compile(source);
3383
3384 CHECK_EQ(v8::Integer::New(42), script->Run());
3385
3386 ExpectBoolean("Object.isExtensible(undetectable)", true);
3387
3388 source = v8_str("Object.preventExtensions(undetectable);");
3389 script = Script::Compile(source);
3390 script->Run();
3391 ExpectBoolean("Object.isExtensible(undetectable)", false);
3392
3393 source = v8_str("undetectable.y = 2000;");
3394 script = Script::Compile(source);
3395 v8::TryCatch try_catch;
3396 Local<Value> result = script->Run();
3397 CHECK(result.IsEmpty());
3398 CHECK(try_catch.HasCaught());
3399}
3400
3401
3402
Steve Blocka7e24c12009-10-30 11:49:00 +00003403THREADED_TEST(UndetectableString) {
3404 v8::HandleScope scope;
3405 LocalContext env;
3406
3407 Local<String> obj = String::NewUndetectable("foo");
3408 env->Global()->Set(v8_str("undetectable"), obj);
3409
3410 ExpectString("undetectable", "foo");
3411 ExpectString("typeof undetectable", "undefined");
3412 ExpectString("typeof(undetectable)", "undefined");
3413 ExpectBoolean("typeof undetectable == 'undefined'", true);
3414 ExpectBoolean("typeof undetectable == 'string'", false);
3415 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3416 ExpectBoolean("!undetectable", true);
3417
3418 ExpectObject("true&&undetectable", obj);
3419 ExpectBoolean("false&&undetectable", false);
3420 ExpectBoolean("true||undetectable", true);
3421 ExpectObject("false||undetectable", obj);
3422
3423 ExpectObject("undetectable&&true", obj);
3424 ExpectObject("undetectable&&false", obj);
3425 ExpectBoolean("undetectable||true", true);
3426 ExpectBoolean("undetectable||false", false);
3427
3428 ExpectBoolean("undetectable==null", true);
3429 ExpectBoolean("null==undetectable", true);
3430 ExpectBoolean("undetectable==undefined", true);
3431 ExpectBoolean("undefined==undetectable", true);
3432 ExpectBoolean("undetectable==undetectable", true);
3433
3434
3435 ExpectBoolean("undetectable===null", false);
3436 ExpectBoolean("null===undetectable", false);
3437 ExpectBoolean("undetectable===undefined", false);
3438 ExpectBoolean("undefined===undetectable", false);
3439 ExpectBoolean("undetectable===undetectable", true);
3440}
3441
3442
3443template <typename T> static void USE(T) { }
3444
3445
3446// This test is not intended to be run, just type checked.
3447static void PersistentHandles() {
3448 USE(PersistentHandles);
3449 Local<String> str = v8_str("foo");
3450 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3451 USE(p_str);
3452 Local<Script> scr = Script::Compile(v8_str(""));
3453 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3454 USE(p_scr);
3455 Local<ObjectTemplate> templ = ObjectTemplate::New();
3456 v8::Persistent<ObjectTemplate> p_templ =
3457 v8::Persistent<ObjectTemplate>::New(templ);
3458 USE(p_templ);
3459}
3460
3461
3462static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3463 ApiTestFuzzer::Fuzz();
3464 return v8::Undefined();
3465}
3466
3467
3468THREADED_TEST(GlobalObjectTemplate) {
3469 v8::HandleScope handle_scope;
3470 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3471 global_template->Set(v8_str("JSNI_Log"),
3472 v8::FunctionTemplate::New(HandleLogDelegator));
3473 v8::Persistent<Context> context = Context::New(0, global_template);
3474 Context::Scope context_scope(context);
3475 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3476 context.Dispose();
3477}
3478
3479
3480static const char* kSimpleExtensionSource =
3481 "function Foo() {"
3482 " return 4;"
3483 "}";
3484
3485
3486THREADED_TEST(SimpleExtensions) {
3487 v8::HandleScope handle_scope;
3488 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3489 const char* extension_names[] = { "simpletest" };
3490 v8::ExtensionConfiguration extensions(1, extension_names);
3491 v8::Handle<Context> context = Context::New(&extensions);
3492 Context::Scope lock(context);
3493 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3494 CHECK_EQ(result, v8::Integer::New(4));
3495}
3496
3497
3498static const char* kEvalExtensionSource1 =
3499 "function UseEval1() {"
3500 " var x = 42;"
3501 " return eval('x');"
3502 "}";
3503
3504
3505static const char* kEvalExtensionSource2 =
3506 "(function() {"
3507 " var x = 42;"
3508 " function e() {"
3509 " return eval('x');"
3510 " }"
3511 " this.UseEval2 = e;"
3512 "})()";
3513
3514
3515THREADED_TEST(UseEvalFromExtension) {
3516 v8::HandleScope handle_scope;
3517 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3518 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3519 const char* extension_names[] = { "evaltest1", "evaltest2" };
3520 v8::ExtensionConfiguration extensions(2, extension_names);
3521 v8::Handle<Context> context = Context::New(&extensions);
3522 Context::Scope lock(context);
3523 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3524 CHECK_EQ(result, v8::Integer::New(42));
3525 result = Script::Compile(v8_str("UseEval2()"))->Run();
3526 CHECK_EQ(result, v8::Integer::New(42));
3527}
3528
3529
3530static const char* kWithExtensionSource1 =
3531 "function UseWith1() {"
3532 " var x = 42;"
3533 " with({x:87}) { return x; }"
3534 "}";
3535
3536
3537
3538static const char* kWithExtensionSource2 =
3539 "(function() {"
3540 " var x = 42;"
3541 " function e() {"
3542 " with ({x:87}) { return x; }"
3543 " }"
3544 " this.UseWith2 = e;"
3545 "})()";
3546
3547
3548THREADED_TEST(UseWithFromExtension) {
3549 v8::HandleScope handle_scope;
3550 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3551 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3552 const char* extension_names[] = { "withtest1", "withtest2" };
3553 v8::ExtensionConfiguration extensions(2, extension_names);
3554 v8::Handle<Context> context = Context::New(&extensions);
3555 Context::Scope lock(context);
3556 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3557 CHECK_EQ(result, v8::Integer::New(87));
3558 result = Script::Compile(v8_str("UseWith2()"))->Run();
3559 CHECK_EQ(result, v8::Integer::New(87));
3560}
3561
3562
3563THREADED_TEST(AutoExtensions) {
3564 v8::HandleScope handle_scope;
3565 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3566 extension->set_auto_enable(true);
3567 v8::RegisterExtension(extension);
3568 v8::Handle<Context> context = Context::New();
3569 Context::Scope lock(context);
3570 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3571 CHECK_EQ(result, v8::Integer::New(4));
3572}
3573
3574
Steve Blockd0582a62009-12-15 09:54:21 +00003575static const char* kSyntaxErrorInExtensionSource =
3576 "[";
3577
3578
3579// Test that a syntax error in an extension does not cause a fatal
3580// error but results in an empty context.
3581THREADED_TEST(SyntaxErrorExtensions) {
3582 v8::HandleScope handle_scope;
3583 v8::RegisterExtension(new Extension("syntaxerror",
3584 kSyntaxErrorInExtensionSource));
3585 const char* extension_names[] = { "syntaxerror" };
3586 v8::ExtensionConfiguration extensions(1, extension_names);
3587 v8::Handle<Context> context = Context::New(&extensions);
3588 CHECK(context.IsEmpty());
3589}
3590
3591
3592static const char* kExceptionInExtensionSource =
3593 "throw 42";
3594
3595
3596// Test that an exception when installing an extension does not cause
3597// a fatal error but results in an empty context.
3598THREADED_TEST(ExceptionExtensions) {
3599 v8::HandleScope handle_scope;
3600 v8::RegisterExtension(new Extension("exception",
3601 kExceptionInExtensionSource));
3602 const char* extension_names[] = { "exception" };
3603 v8::ExtensionConfiguration extensions(1, extension_names);
3604 v8::Handle<Context> context = Context::New(&extensions);
3605 CHECK(context.IsEmpty());
3606}
3607
3608
Iain Merrick9ac36c92010-09-13 15:29:50 +01003609static const char* kNativeCallInExtensionSource =
3610 "function call_runtime_last_index_of(x) {"
3611 " return %StringLastIndexOf(x, 'bob', 10);"
3612 "}";
3613
3614
3615static const char* kNativeCallTest =
3616 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
3617
3618// Test that a native runtime calls are supported in extensions.
3619THREADED_TEST(NativeCallInExtensions) {
3620 v8::HandleScope handle_scope;
3621 v8::RegisterExtension(new Extension("nativecall",
3622 kNativeCallInExtensionSource));
3623 const char* extension_names[] = { "nativecall" };
3624 v8::ExtensionConfiguration extensions(1, extension_names);
3625 v8::Handle<Context> context = Context::New(&extensions);
3626 Context::Scope lock(context);
3627 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
3628 CHECK_EQ(result, v8::Integer::New(3));
3629}
3630
3631
Steve Blocka7e24c12009-10-30 11:49:00 +00003632static void CheckDependencies(const char* name, const char* expected) {
3633 v8::HandleScope handle_scope;
3634 v8::ExtensionConfiguration config(1, &name);
3635 LocalContext context(&config);
3636 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3637}
3638
3639
3640/*
3641 * Configuration:
3642 *
3643 * /-- B <--\
3644 * A <- -- D <-- E
3645 * \-- C <--/
3646 */
3647THREADED_TEST(ExtensionDependency) {
3648 static const char* kEDeps[] = { "D" };
3649 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3650 static const char* kDDeps[] = { "B", "C" };
3651 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3652 static const char* kBCDeps[] = { "A" };
3653 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3654 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3655 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3656 CheckDependencies("A", "undefinedA");
3657 CheckDependencies("B", "undefinedAB");
3658 CheckDependencies("C", "undefinedAC");
3659 CheckDependencies("D", "undefinedABCD");
3660 CheckDependencies("E", "undefinedABCDE");
3661 v8::HandleScope handle_scope;
3662 static const char* exts[2] = { "C", "E" };
3663 v8::ExtensionConfiguration config(2, exts);
3664 LocalContext context(&config);
3665 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3666}
3667
3668
3669static const char* kExtensionTestScript =
3670 "native function A();"
3671 "native function B();"
3672 "native function C();"
3673 "function Foo(i) {"
3674 " if (i == 0) return A();"
3675 " if (i == 1) return B();"
3676 " if (i == 2) return C();"
3677 "}";
3678
3679
3680static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3681 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003682 if (args.IsConstructCall()) {
3683 args.This()->Set(v8_str("data"), args.Data());
3684 return v8::Null();
3685 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003686 return args.Data();
3687}
3688
3689
3690class FunctionExtension : public Extension {
3691 public:
3692 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3693 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3694 v8::Handle<String> name);
3695};
3696
3697
3698static int lookup_count = 0;
3699v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3700 v8::Handle<String> name) {
3701 lookup_count++;
3702 if (name->Equals(v8_str("A"))) {
3703 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3704 } else if (name->Equals(v8_str("B"))) {
3705 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3706 } else if (name->Equals(v8_str("C"))) {
3707 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3708 } else {
3709 return v8::Handle<v8::FunctionTemplate>();
3710 }
3711}
3712
3713
3714THREADED_TEST(FunctionLookup) {
3715 v8::RegisterExtension(new FunctionExtension());
3716 v8::HandleScope handle_scope;
3717 static const char* exts[1] = { "functiontest" };
3718 v8::ExtensionConfiguration config(1, exts);
3719 LocalContext context(&config);
3720 CHECK_EQ(3, lookup_count);
3721 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3722 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3723 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3724}
3725
3726
Leon Clarkee46be812010-01-19 14:06:41 +00003727THREADED_TEST(NativeFunctionConstructCall) {
3728 v8::RegisterExtension(new FunctionExtension());
3729 v8::HandleScope handle_scope;
3730 static const char* exts[1] = { "functiontest" };
3731 v8::ExtensionConfiguration config(1, exts);
3732 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003733 for (int i = 0; i < 10; i++) {
3734 // Run a few times to ensure that allocation of objects doesn't
3735 // change behavior of a constructor function.
3736 CHECK_EQ(v8::Integer::New(8),
3737 Script::Compile(v8_str("(new A()).data"))->Run());
3738 CHECK_EQ(v8::Integer::New(7),
3739 Script::Compile(v8_str("(new B()).data"))->Run());
3740 CHECK_EQ(v8::Integer::New(6),
3741 Script::Compile(v8_str("(new C()).data"))->Run());
3742 }
Leon Clarkee46be812010-01-19 14:06:41 +00003743}
3744
3745
Steve Blocka7e24c12009-10-30 11:49:00 +00003746static const char* last_location;
3747static const char* last_message;
3748void StoringErrorCallback(const char* location, const char* message) {
3749 if (last_location == NULL) {
3750 last_location = location;
3751 last_message = message;
3752 }
3753}
3754
3755
3756// ErrorReporting creates a circular extensions configuration and
3757// tests that the fatal error handler gets called. This renders V8
3758// unusable and therefore this test cannot be run in parallel.
3759TEST(ErrorReporting) {
3760 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3761 static const char* aDeps[] = { "B" };
3762 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3763 static const char* bDeps[] = { "A" };
3764 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3765 last_location = NULL;
3766 v8::ExtensionConfiguration config(1, bDeps);
3767 v8::Handle<Context> context = Context::New(&config);
3768 CHECK(context.IsEmpty());
3769 CHECK_NE(last_location, NULL);
3770}
3771
3772
3773static const char* js_code_causing_huge_string_flattening =
3774 "var str = 'X';"
3775 "for (var i = 0; i < 30; i++) {"
3776 " str = str + str;"
3777 "}"
3778 "str.match(/X/);";
3779
3780
3781void OOMCallback(const char* location, const char* message) {
3782 exit(0);
3783}
3784
3785
3786TEST(RegexpOutOfMemory) {
3787 // Execute a script that causes out of memory when flattening a string.
3788 v8::HandleScope scope;
3789 v8::V8::SetFatalErrorHandler(OOMCallback);
3790 LocalContext context;
3791 Local<Script> script =
3792 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3793 last_location = NULL;
3794 Local<Value> result = script->Run();
3795
3796 CHECK(false); // Should not return.
3797}
3798
3799
3800static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3801 v8::Handle<Value> data) {
3802 CHECK_EQ(v8::Undefined(), data);
3803 CHECK(message->GetScriptResourceName()->IsUndefined());
3804 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3805 message->GetLineNumber();
3806 message->GetSourceLine();
3807}
3808
3809
3810THREADED_TEST(ErrorWithMissingScriptInfo) {
3811 v8::HandleScope scope;
3812 LocalContext context;
3813 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3814 Script::Compile(v8_str("throw Error()"))->Run();
3815 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3816}
3817
3818
3819int global_index = 0;
3820
3821class Snorkel {
3822 public:
3823 Snorkel() { index_ = global_index++; }
3824 int index_;
3825};
3826
3827class Whammy {
3828 public:
3829 Whammy() {
3830 cursor_ = 0;
3831 }
3832 ~Whammy() {
3833 script_.Dispose();
3834 }
3835 v8::Handle<Script> getScript() {
3836 if (script_.IsEmpty())
3837 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3838 return Local<Script>(*script_);
3839 }
3840
3841 public:
3842 static const int kObjectCount = 256;
3843 int cursor_;
3844 v8::Persistent<v8::Object> objects_[kObjectCount];
3845 v8::Persistent<Script> script_;
3846};
3847
3848static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3849 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3850 delete snorkel;
3851 obj.ClearWeak();
3852}
3853
3854v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3855 const AccessorInfo& info) {
3856 Whammy* whammy =
3857 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3858
3859 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3860
3861 v8::Handle<v8::Object> obj = v8::Object::New();
3862 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3863 if (!prev.IsEmpty()) {
3864 prev->Set(v8_str("next"), obj);
3865 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3866 whammy->objects_[whammy->cursor_].Clear();
3867 }
3868 whammy->objects_[whammy->cursor_] = global;
3869 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3870 return whammy->getScript()->Run();
3871}
3872
3873THREADED_TEST(WeakReference) {
3874 v8::HandleScope handle_scope;
3875 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003876 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00003877 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3878 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003879 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00003880 const char* extension_list[] = { "v8/gc" };
3881 v8::ExtensionConfiguration extensions(1, extension_list);
3882 v8::Persistent<Context> context = Context::New(&extensions);
3883 Context::Scope context_scope(context);
3884
3885 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3886 context->Global()->Set(v8_str("whammy"), interceptor);
3887 const char* code =
3888 "var last;"
3889 "for (var i = 0; i < 10000; i++) {"
3890 " var obj = whammy.length;"
3891 " if (last) last.next = obj;"
3892 " last = obj;"
3893 "}"
3894 "gc();"
3895 "4";
3896 v8::Handle<Value> result = CompileRun(code);
3897 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003898 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00003899 context.Dispose();
3900}
3901
3902
Steve Blockd0582a62009-12-15 09:54:21 +00003903static bool in_scavenge = false;
3904static int last = -1;
3905
3906static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3907 CHECK_EQ(-1, last);
3908 last = 0;
3909 obj.Dispose();
3910 obj.Clear();
3911 in_scavenge = true;
3912 i::Heap::PerformScavenge();
3913 in_scavenge = false;
3914 *(reinterpret_cast<bool*>(data)) = true;
3915}
3916
3917static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3918 void* data) {
3919 CHECK_EQ(0, last);
3920 last = 1;
3921 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3922 obj.Dispose();
3923 obj.Clear();
3924}
3925
3926THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3927 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3928 // Calling callbacks from scavenges is unsafe as objects held by those
3929 // handlers might have become strongly reachable, but scavenge doesn't
3930 // check that.
3931 v8::Persistent<Context> context = Context::New();
3932 Context::Scope context_scope(context);
3933
3934 v8::Persistent<v8::Object> object_a;
3935 v8::Persistent<v8::Object> object_b;
3936
3937 {
3938 v8::HandleScope handle_scope;
3939 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
3940 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
3941 }
3942
3943 bool object_a_disposed = false;
3944 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
3945 bool released_in_scavenge = false;
3946 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
3947
3948 while (!object_a_disposed) {
3949 i::Heap::CollectAllGarbage(false);
3950 }
3951 CHECK(!released_in_scavenge);
3952}
3953
3954
Steve Blocka7e24c12009-10-30 11:49:00 +00003955v8::Handle<Function> args_fun;
3956
3957
3958static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3959 ApiTestFuzzer::Fuzz();
3960 CHECK_EQ(args_fun, args.Callee());
3961 CHECK_EQ(3, args.Length());
3962 CHECK_EQ(v8::Integer::New(1), args[0]);
3963 CHECK_EQ(v8::Integer::New(2), args[1]);
3964 CHECK_EQ(v8::Integer::New(3), args[2]);
3965 CHECK_EQ(v8::Undefined(), args[3]);
3966 v8::HandleScope scope;
3967 i::Heap::CollectAllGarbage(false);
3968 return v8::Undefined();
3969}
3970
3971
3972THREADED_TEST(Arguments) {
3973 v8::HandleScope scope;
3974 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3975 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3976 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01003977 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003978 v8_compile("f(1, 2, 3)")->Run();
3979}
3980
3981
Steve Blocka7e24c12009-10-30 11:49:00 +00003982static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3983 const AccessorInfo&) {
3984 return v8::Handle<Value>();
3985}
3986
3987
3988static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3989 const AccessorInfo&) {
3990 return v8::Handle<Value>();
3991}
3992
3993
3994static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3995 const AccessorInfo&) {
3996 if (!name->Equals(v8_str("foo"))) {
3997 return v8::Handle<v8::Boolean>(); // not intercepted
3998 }
3999
4000 return v8::False(); // intercepted, and don't delete the property
4001}
4002
4003
4004static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4005 if (index != 2) {
4006 return v8::Handle<v8::Boolean>(); // not intercepted
4007 }
4008
4009 return v8::False(); // intercepted, and don't delete the property
4010}
4011
4012
4013THREADED_TEST(Deleter) {
4014 v8::HandleScope scope;
4015 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4016 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4017 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4018 LocalContext context;
4019 context->Global()->Set(v8_str("k"), obj->NewInstance());
4020 CompileRun(
4021 "k.foo = 'foo';"
4022 "k.bar = 'bar';"
4023 "k[2] = 2;"
4024 "k[4] = 4;");
4025 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4026 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4027
4028 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4029 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4030
4031 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4032 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4033
4034 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4035 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4036}
4037
4038
4039static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4040 ApiTestFuzzer::Fuzz();
4041 if (name->Equals(v8_str("foo")) ||
4042 name->Equals(v8_str("bar")) ||
4043 name->Equals(v8_str("baz"))) {
4044 return v8::Undefined();
4045 }
4046 return v8::Handle<Value>();
4047}
4048
4049
4050static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4051 ApiTestFuzzer::Fuzz();
4052 if (index == 0 || index == 1) return v8::Undefined();
4053 return v8::Handle<Value>();
4054}
4055
4056
4057static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4058 ApiTestFuzzer::Fuzz();
4059 v8::Handle<v8::Array> result = v8::Array::New(3);
4060 result->Set(v8::Integer::New(0), v8_str("foo"));
4061 result->Set(v8::Integer::New(1), v8_str("bar"));
4062 result->Set(v8::Integer::New(2), v8_str("baz"));
4063 return result;
4064}
4065
4066
4067static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4068 ApiTestFuzzer::Fuzz();
4069 v8::Handle<v8::Array> result = v8::Array::New(2);
4070 result->Set(v8::Integer::New(0), v8_str("0"));
4071 result->Set(v8::Integer::New(1), v8_str("1"));
4072 return result;
4073}
4074
4075
4076THREADED_TEST(Enumerators) {
4077 v8::HandleScope scope;
4078 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4079 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4080 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4081 LocalContext context;
4082 context->Global()->Set(v8_str("k"), obj->NewInstance());
4083 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4084 "k[10] = 0;"
4085 "k.a = 0;"
4086 "k[5] = 0;"
4087 "k.b = 0;"
4088 "k[4294967295] = 0;"
4089 "k.c = 0;"
4090 "k[4294967296] = 0;"
4091 "k.d = 0;"
4092 "k[140000] = 0;"
4093 "k.e = 0;"
4094 "k[30000000000] = 0;"
4095 "k.f = 0;"
4096 "var result = [];"
4097 "for (var prop in k) {"
4098 " result.push(prop);"
4099 "}"
4100 "result"));
4101 // Check that we get all the property names returned including the
4102 // ones from the enumerators in the right order: indexed properties
4103 // in numerical order, indexed interceptor properties, named
4104 // properties in insertion order, named interceptor properties.
4105 // This order is not mandated by the spec, so this test is just
4106 // documenting our behavior.
4107 CHECK_EQ(17, result->Length());
4108 // Indexed properties in numerical order.
4109 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4110 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4111 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4112 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4113 // Indexed interceptor properties in the order they are returned
4114 // from the enumerator interceptor.
4115 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4116 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4117 // Named properties in insertion order.
4118 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4119 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4120 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4121 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4122 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4123 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4124 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4125 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4126 // Named interceptor properties.
4127 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4128 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4129 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4130}
4131
4132
4133int p_getter_count;
4134int p_getter_count2;
4135
4136
4137static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4138 ApiTestFuzzer::Fuzz();
4139 p_getter_count++;
4140 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4141 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4142 if (name->Equals(v8_str("p1"))) {
4143 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4144 } else if (name->Equals(v8_str("p2"))) {
4145 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4146 } else if (name->Equals(v8_str("p3"))) {
4147 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4148 } else if (name->Equals(v8_str("p4"))) {
4149 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4150 }
4151 return v8::Undefined();
4152}
4153
4154
4155static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4156 ApiTestFuzzer::Fuzz();
4157 LocalContext context;
4158 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4159 CompileRun(
4160 "o1.__proto__ = { };"
4161 "var o2 = { __proto__: o1 };"
4162 "var o3 = { __proto__: o2 };"
4163 "var o4 = { __proto__: o3 };"
4164 "for (var i = 0; i < 10; i++) o4.p4;"
4165 "for (var i = 0; i < 10; i++) o3.p3;"
4166 "for (var i = 0; i < 10; i++) o2.p2;"
4167 "for (var i = 0; i < 10; i++) o1.p1;");
4168}
4169
4170
4171static v8::Handle<Value> PGetter2(Local<String> name,
4172 const AccessorInfo& info) {
4173 ApiTestFuzzer::Fuzz();
4174 p_getter_count2++;
4175 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4176 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4177 if (name->Equals(v8_str("p1"))) {
4178 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4179 } else if (name->Equals(v8_str("p2"))) {
4180 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4181 } else if (name->Equals(v8_str("p3"))) {
4182 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4183 } else if (name->Equals(v8_str("p4"))) {
4184 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4185 }
4186 return v8::Undefined();
4187}
4188
4189
4190THREADED_TEST(GetterHolders) {
4191 v8::HandleScope scope;
4192 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4193 obj->SetAccessor(v8_str("p1"), PGetter);
4194 obj->SetAccessor(v8_str("p2"), PGetter);
4195 obj->SetAccessor(v8_str("p3"), PGetter);
4196 obj->SetAccessor(v8_str("p4"), PGetter);
4197 p_getter_count = 0;
4198 RunHolderTest(obj);
4199 CHECK_EQ(40, p_getter_count);
4200}
4201
4202
4203THREADED_TEST(PreInterceptorHolders) {
4204 v8::HandleScope scope;
4205 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4206 obj->SetNamedPropertyHandler(PGetter2);
4207 p_getter_count2 = 0;
4208 RunHolderTest(obj);
4209 CHECK_EQ(40, p_getter_count2);
4210}
4211
4212
4213THREADED_TEST(ObjectInstantiation) {
4214 v8::HandleScope scope;
4215 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4216 templ->SetAccessor(v8_str("t"), PGetter2);
4217 LocalContext context;
4218 context->Global()->Set(v8_str("o"), templ->NewInstance());
4219 for (int i = 0; i < 100; i++) {
4220 v8::HandleScope inner_scope;
4221 v8::Handle<v8::Object> obj = templ->NewInstance();
4222 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4223 context->Global()->Set(v8_str("o2"), obj);
4224 v8::Handle<Value> value =
4225 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4226 CHECK_EQ(v8::True(), value);
4227 context->Global()->Set(v8_str("o"), obj);
4228 }
4229}
4230
4231
4232THREADED_TEST(StringWrite) {
4233 v8::HandleScope scope;
4234 v8::Handle<String> str = v8_str("abcde");
4235
4236 char buf[100];
4237 int len;
4238
4239 memset(buf, 0x1, sizeof(buf));
4240 len = str->WriteAscii(buf);
4241 CHECK_EQ(len, 5);
4242 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4243
4244 memset(buf, 0x1, sizeof(buf));
4245 len = str->WriteAscii(buf, 0, 4);
4246 CHECK_EQ(len, 4);
4247 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4248
4249 memset(buf, 0x1, sizeof(buf));
4250 len = str->WriteAscii(buf, 0, 5);
4251 CHECK_EQ(len, 5);
4252 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4253
4254 memset(buf, 0x1, sizeof(buf));
4255 len = str->WriteAscii(buf, 0, 6);
4256 CHECK_EQ(len, 5);
4257 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4258
4259 memset(buf, 0x1, sizeof(buf));
4260 len = str->WriteAscii(buf, 4, -1);
4261 CHECK_EQ(len, 1);
4262 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4263
4264 memset(buf, 0x1, sizeof(buf));
4265 len = str->WriteAscii(buf, 4, 6);
4266 CHECK_EQ(len, 1);
4267 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4268
4269 memset(buf, 0x1, sizeof(buf));
4270 len = str->WriteAscii(buf, 4, 1);
4271 CHECK_EQ(len, 1);
4272 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4273}
4274
4275
4276THREADED_TEST(ToArrayIndex) {
4277 v8::HandleScope scope;
4278 LocalContext context;
4279
4280 v8::Handle<String> str = v8_str("42");
4281 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4282 CHECK(!index.IsEmpty());
4283 CHECK_EQ(42.0, index->Uint32Value());
4284 str = v8_str("42asdf");
4285 index = str->ToArrayIndex();
4286 CHECK(index.IsEmpty());
4287 str = v8_str("-42");
4288 index = str->ToArrayIndex();
4289 CHECK(index.IsEmpty());
4290 str = v8_str("4294967295");
4291 index = str->ToArrayIndex();
4292 CHECK(!index.IsEmpty());
4293 CHECK_EQ(4294967295.0, index->Uint32Value());
4294 v8::Handle<v8::Number> num = v8::Number::New(1);
4295 index = num->ToArrayIndex();
4296 CHECK(!index.IsEmpty());
4297 CHECK_EQ(1.0, index->Uint32Value());
4298 num = v8::Number::New(-1);
4299 index = num->ToArrayIndex();
4300 CHECK(index.IsEmpty());
4301 v8::Handle<v8::Object> obj = v8::Object::New();
4302 index = obj->ToArrayIndex();
4303 CHECK(index.IsEmpty());
4304}
4305
4306
4307THREADED_TEST(ErrorConstruction) {
4308 v8::HandleScope scope;
4309 LocalContext context;
4310
4311 v8::Handle<String> foo = v8_str("foo");
4312 v8::Handle<String> message = v8_str("message");
4313 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4314 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004315 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4316 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004317 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4318 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004319 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004320 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4321 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004322 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004323 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4324 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004325 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004326 v8::Handle<Value> error = v8::Exception::Error(foo);
4327 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004328 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004329}
4330
4331
4332static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4333 ApiTestFuzzer::Fuzz();
4334 return v8_num(10);
4335}
4336
4337
4338static void YSetter(Local<String> name,
4339 Local<Value> value,
4340 const AccessorInfo& info) {
4341 if (info.This()->Has(name)) {
4342 info.This()->Delete(name);
4343 }
4344 info.This()->Set(name, value);
4345}
4346
4347
4348THREADED_TEST(DeleteAccessor) {
4349 v8::HandleScope scope;
4350 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4351 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4352 LocalContext context;
4353 v8::Handle<v8::Object> holder = obj->NewInstance();
4354 context->Global()->Set(v8_str("holder"), holder);
4355 v8::Handle<Value> result = CompileRun(
4356 "holder.y = 11; holder.y = 12; holder.y");
4357 CHECK_EQ(12, result->Uint32Value());
4358}
4359
4360
4361THREADED_TEST(TypeSwitch) {
4362 v8::HandleScope scope;
4363 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4364 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4365 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4366 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4367 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4368 LocalContext context;
4369 v8::Handle<v8::Object> obj0 = v8::Object::New();
4370 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4371 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4372 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4373 for (int i = 0; i < 10; i++) {
4374 CHECK_EQ(0, type_switch->match(obj0));
4375 CHECK_EQ(1, type_switch->match(obj1));
4376 CHECK_EQ(2, type_switch->match(obj2));
4377 CHECK_EQ(3, type_switch->match(obj3));
4378 CHECK_EQ(3, type_switch->match(obj3));
4379 CHECK_EQ(2, type_switch->match(obj2));
4380 CHECK_EQ(1, type_switch->match(obj1));
4381 CHECK_EQ(0, type_switch->match(obj0));
4382 }
4383}
4384
4385
4386// For use within the TestSecurityHandler() test.
4387static bool g_security_callback_result = false;
4388static bool NamedSecurityTestCallback(Local<v8::Object> global,
4389 Local<Value> name,
4390 v8::AccessType type,
4391 Local<Value> data) {
4392 // Always allow read access.
4393 if (type == v8::ACCESS_GET)
4394 return true;
4395
4396 // Sometimes allow other access.
4397 return g_security_callback_result;
4398}
4399
4400
4401static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4402 uint32_t key,
4403 v8::AccessType type,
4404 Local<Value> data) {
4405 // Always allow read access.
4406 if (type == v8::ACCESS_GET)
4407 return true;
4408
4409 // Sometimes allow other access.
4410 return g_security_callback_result;
4411}
4412
4413
4414static int trouble_nesting = 0;
4415static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4416 ApiTestFuzzer::Fuzz();
4417 trouble_nesting++;
4418
4419 // Call a JS function that throws an uncaught exception.
4420 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4421 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4422 arg_this->Get(v8_str("trouble_callee")) :
4423 arg_this->Get(v8_str("trouble_caller"));
4424 CHECK(trouble_callee->IsFunction());
4425 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4426}
4427
4428
4429static int report_count = 0;
4430static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4431 v8::Handle<Value>) {
4432 report_count++;
4433}
4434
4435
4436// Counts uncaught exceptions, but other tests running in parallel
4437// also have uncaught exceptions.
4438TEST(ApiUncaughtException) {
4439 report_count = 0;
4440 v8::HandleScope scope;
4441 LocalContext env;
4442 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4443
4444 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4445 v8::Local<v8::Object> global = env->Global();
4446 global->Set(v8_str("trouble"), fun->GetFunction());
4447
4448 Script::Compile(v8_str("function trouble_callee() {"
4449 " var x = null;"
4450 " return x.foo;"
4451 "};"
4452 "function trouble_caller() {"
4453 " trouble();"
4454 "};"))->Run();
4455 Local<Value> trouble = global->Get(v8_str("trouble"));
4456 CHECK(trouble->IsFunction());
4457 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4458 CHECK(trouble_callee->IsFunction());
4459 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4460 CHECK(trouble_caller->IsFunction());
4461 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4462 CHECK_EQ(1, report_count);
4463 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4464}
4465
Leon Clarke4515c472010-02-03 11:58:03 +00004466static const char* script_resource_name = "ExceptionInNativeScript.js";
4467static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4468 v8::Handle<Value>) {
4469 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4470 CHECK(!name_val.IsEmpty() && name_val->IsString());
4471 v8::String::AsciiValue name(message->GetScriptResourceName());
4472 CHECK_EQ(script_resource_name, *name);
4473 CHECK_EQ(3, message->GetLineNumber());
4474 v8::String::AsciiValue source_line(message->GetSourceLine());
4475 CHECK_EQ(" new o.foo();", *source_line);
4476}
4477
4478TEST(ExceptionInNativeScript) {
4479 v8::HandleScope scope;
4480 LocalContext env;
4481 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4482
4483 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4484 v8::Local<v8::Object> global = env->Global();
4485 global->Set(v8_str("trouble"), fun->GetFunction());
4486
4487 Script::Compile(v8_str("function trouble() {\n"
4488 " var o = {};\n"
4489 " new o.foo();\n"
4490 "};"), v8::String::New(script_resource_name))->Run();
4491 Local<Value> trouble = global->Get(v8_str("trouble"));
4492 CHECK(trouble->IsFunction());
4493 Function::Cast(*trouble)->Call(global, 0, NULL);
4494 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4495}
4496
Steve Blocka7e24c12009-10-30 11:49:00 +00004497
4498TEST(CompilationErrorUsingTryCatchHandler) {
4499 v8::HandleScope scope;
4500 LocalContext env;
4501 v8::TryCatch try_catch;
4502 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4503 CHECK_NE(NULL, *try_catch.Exception());
4504 CHECK(try_catch.HasCaught());
4505}
4506
4507
4508TEST(TryCatchFinallyUsingTryCatchHandler) {
4509 v8::HandleScope scope;
4510 LocalContext env;
4511 v8::TryCatch try_catch;
4512 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4513 CHECK(!try_catch.HasCaught());
4514 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4515 CHECK(try_catch.HasCaught());
4516 try_catch.Reset();
4517 Script::Compile(v8_str("(function() {"
4518 "try { throw ''; } finally { return; }"
4519 "})()"))->Run();
4520 CHECK(!try_catch.HasCaught());
4521 Script::Compile(v8_str("(function()"
4522 " { try { throw ''; } finally { throw 0; }"
4523 "})()"))->Run();
4524 CHECK(try_catch.HasCaught());
4525}
4526
4527
4528// SecurityHandler can't be run twice
4529TEST(SecurityHandler) {
4530 v8::HandleScope scope0;
4531 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4532 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4533 IndexedSecurityTestCallback);
4534 // Create an environment
4535 v8::Persistent<Context> context0 =
4536 Context::New(NULL, global_template);
4537 context0->Enter();
4538
4539 v8::Handle<v8::Object> global0 = context0->Global();
4540 v8::Handle<Script> script0 = v8_compile("foo = 111");
4541 script0->Run();
4542 global0->Set(v8_str("0"), v8_num(999));
4543 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4544 CHECK_EQ(111, foo0->Int32Value());
4545 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4546 CHECK_EQ(999, z0->Int32Value());
4547
4548 // Create another environment, should fail security checks.
4549 v8::HandleScope scope1;
4550
4551 v8::Persistent<Context> context1 =
4552 Context::New(NULL, global_template);
4553 context1->Enter();
4554
4555 v8::Handle<v8::Object> global1 = context1->Global();
4556 global1->Set(v8_str("othercontext"), global0);
4557 // This set will fail the security check.
4558 v8::Handle<Script> script1 =
4559 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4560 script1->Run();
4561 // This read will pass the security check.
4562 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4563 CHECK_EQ(111, foo1->Int32Value());
4564 // This read will pass the security check.
4565 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4566 CHECK_EQ(999, z1->Int32Value());
4567
4568 // Create another environment, should pass security checks.
4569 { g_security_callback_result = true; // allow security handler to pass.
4570 v8::HandleScope scope2;
4571 LocalContext context2;
4572 v8::Handle<v8::Object> global2 = context2->Global();
4573 global2->Set(v8_str("othercontext"), global0);
4574 v8::Handle<Script> script2 =
4575 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4576 script2->Run();
4577 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4578 CHECK_EQ(333, foo2->Int32Value());
4579 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4580 CHECK_EQ(888, z2->Int32Value());
4581 }
4582
4583 context1->Exit();
4584 context1.Dispose();
4585
4586 context0->Exit();
4587 context0.Dispose();
4588}
4589
4590
4591THREADED_TEST(SecurityChecks) {
4592 v8::HandleScope handle_scope;
4593 LocalContext env1;
4594 v8::Persistent<Context> env2 = Context::New();
4595
4596 Local<Value> foo = v8_str("foo");
4597 Local<Value> bar = v8_str("bar");
4598
4599 // Set to the same domain.
4600 env1->SetSecurityToken(foo);
4601
4602 // Create a function in env1.
4603 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4604 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4605 CHECK(spy->IsFunction());
4606
4607 // Create another function accessing global objects.
4608 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4609 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4610 CHECK(spy2->IsFunction());
4611
4612 // Switch to env2 in the same domain and invoke spy on env2.
4613 {
4614 env2->SetSecurityToken(foo);
4615 // Enter env2
4616 Context::Scope scope_env2(env2);
4617 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4618 CHECK(result->IsFunction());
4619 }
4620
4621 {
4622 env2->SetSecurityToken(bar);
4623 Context::Scope scope_env2(env2);
4624
4625 // Call cross_domain_call, it should throw an exception
4626 v8::TryCatch try_catch;
4627 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4628 CHECK(try_catch.HasCaught());
4629 }
4630
4631 env2.Dispose();
4632}
4633
4634
4635// Regression test case for issue 1183439.
4636THREADED_TEST(SecurityChecksForPrototypeChain) {
4637 v8::HandleScope scope;
4638 LocalContext current;
4639 v8::Persistent<Context> other = Context::New();
4640
4641 // Change context to be able to get to the Object function in the
4642 // other context without hitting the security checks.
4643 v8::Local<Value> other_object;
4644 { Context::Scope scope(other);
4645 other_object = other->Global()->Get(v8_str("Object"));
4646 other->Global()->Set(v8_num(42), v8_num(87));
4647 }
4648
4649 current->Global()->Set(v8_str("other"), other->Global());
4650 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4651
4652 // Make sure the security check fails here and we get an undefined
4653 // result instead of getting the Object function. Repeat in a loop
4654 // to make sure to exercise the IC code.
4655 v8::Local<Script> access_other0 = v8_compile("other.Object");
4656 v8::Local<Script> access_other1 = v8_compile("other[42]");
4657 for (int i = 0; i < 5; i++) {
4658 CHECK(!access_other0->Run()->Equals(other_object));
4659 CHECK(access_other0->Run()->IsUndefined());
4660 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4661 CHECK(access_other1->Run()->IsUndefined());
4662 }
4663
4664 // Create an object that has 'other' in its prototype chain and make
4665 // sure we cannot access the Object function indirectly through
4666 // that. Repeat in a loop to make sure to exercise the IC code.
4667 v8_compile("function F() { };"
4668 "F.prototype = other;"
4669 "var f = new F();")->Run();
4670 v8::Local<Script> access_f0 = v8_compile("f.Object");
4671 v8::Local<Script> access_f1 = v8_compile("f[42]");
4672 for (int j = 0; j < 5; j++) {
4673 CHECK(!access_f0->Run()->Equals(other_object));
4674 CHECK(access_f0->Run()->IsUndefined());
4675 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4676 CHECK(access_f1->Run()->IsUndefined());
4677 }
4678
4679 // Now it gets hairy: Set the prototype for the other global object
4680 // to be the current global object. The prototype chain for 'f' now
4681 // goes through 'other' but ends up in the current global object.
4682 { Context::Scope scope(other);
4683 other->Global()->Set(v8_str("__proto__"), current->Global());
4684 }
4685 // Set a named and an index property on the current global
4686 // object. To force the lookup to go through the other global object,
4687 // the properties must not exist in the other global object.
4688 current->Global()->Set(v8_str("foo"), v8_num(100));
4689 current->Global()->Set(v8_num(99), v8_num(101));
4690 // Try to read the properties from f and make sure that the access
4691 // gets stopped by the security checks on the other global object.
4692 Local<Script> access_f2 = v8_compile("f.foo");
4693 Local<Script> access_f3 = v8_compile("f[99]");
4694 for (int k = 0; k < 5; k++) {
4695 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4696 CHECK(access_f2->Run()->IsUndefined());
4697 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4698 CHECK(access_f3->Run()->IsUndefined());
4699 }
4700 other.Dispose();
4701}
4702
4703
4704THREADED_TEST(CrossDomainDelete) {
4705 v8::HandleScope handle_scope;
4706 LocalContext env1;
4707 v8::Persistent<Context> env2 = Context::New();
4708
4709 Local<Value> foo = v8_str("foo");
4710 Local<Value> bar = v8_str("bar");
4711
4712 // Set to the same domain.
4713 env1->SetSecurityToken(foo);
4714 env2->SetSecurityToken(foo);
4715
4716 env1->Global()->Set(v8_str("prop"), v8_num(3));
4717 env2->Global()->Set(v8_str("env1"), env1->Global());
4718
4719 // Change env2 to a different domain and delete env1.prop.
4720 env2->SetSecurityToken(bar);
4721 {
4722 Context::Scope scope_env2(env2);
4723 Local<Value> result =
4724 Script::Compile(v8_str("delete env1.prop"))->Run();
4725 CHECK(result->IsFalse());
4726 }
4727
4728 // Check that env1.prop still exists.
4729 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4730 CHECK(v->IsNumber());
4731 CHECK_EQ(3, v->Int32Value());
4732
4733 env2.Dispose();
4734}
4735
4736
4737THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4738 v8::HandleScope handle_scope;
4739 LocalContext env1;
4740 v8::Persistent<Context> env2 = Context::New();
4741
4742 Local<Value> foo = v8_str("foo");
4743 Local<Value> bar = v8_str("bar");
4744
4745 // Set to the same domain.
4746 env1->SetSecurityToken(foo);
4747 env2->SetSecurityToken(foo);
4748
4749 env1->Global()->Set(v8_str("prop"), v8_num(3));
4750 env2->Global()->Set(v8_str("env1"), env1->Global());
4751
4752 // env1.prop is enumerable in env2.
4753 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4754 {
4755 Context::Scope scope_env2(env2);
4756 Local<Value> result = Script::Compile(test)->Run();
4757 CHECK(result->IsTrue());
4758 }
4759
4760 // Change env2 to a different domain and test again.
4761 env2->SetSecurityToken(bar);
4762 {
4763 Context::Scope scope_env2(env2);
4764 Local<Value> result = Script::Compile(test)->Run();
4765 CHECK(result->IsFalse());
4766 }
4767
4768 env2.Dispose();
4769}
4770
4771
4772THREADED_TEST(CrossDomainForIn) {
4773 v8::HandleScope handle_scope;
4774 LocalContext env1;
4775 v8::Persistent<Context> env2 = Context::New();
4776
4777 Local<Value> foo = v8_str("foo");
4778 Local<Value> bar = v8_str("bar");
4779
4780 // Set to the same domain.
4781 env1->SetSecurityToken(foo);
4782 env2->SetSecurityToken(foo);
4783
4784 env1->Global()->Set(v8_str("prop"), v8_num(3));
4785 env2->Global()->Set(v8_str("env1"), env1->Global());
4786
4787 // Change env2 to a different domain and set env1's global object
4788 // as the __proto__ of an object in env2 and enumerate properties
4789 // in for-in. It shouldn't enumerate properties on env1's global
4790 // object.
4791 env2->SetSecurityToken(bar);
4792 {
4793 Context::Scope scope_env2(env2);
4794 Local<Value> result =
4795 CompileRun("(function(){var obj = {'__proto__':env1};"
4796 "for (var p in obj)"
4797 " if (p == 'prop') return false;"
4798 "return true;})()");
4799 CHECK(result->IsTrue());
4800 }
4801 env2.Dispose();
4802}
4803
4804
4805TEST(ContextDetachGlobal) {
4806 v8::HandleScope handle_scope;
4807 LocalContext env1;
4808 v8::Persistent<Context> env2 = Context::New();
4809
4810 Local<v8::Object> global1 = env1->Global();
4811
4812 Local<Value> foo = v8_str("foo");
4813
4814 // Set to the same domain.
4815 env1->SetSecurityToken(foo);
4816 env2->SetSecurityToken(foo);
4817
4818 // Enter env2
4819 env2->Enter();
4820
Andrei Popescu74b3c142010-03-29 12:03:09 +01004821 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004822 Local<v8::Object> global2 = env2->Global();
4823 global2->Set(v8_str("prop"), v8::Integer::New(1));
4824 CompileRun("function getProp() {return prop;}");
4825
4826 env1->Global()->Set(v8_str("getProp"),
4827 global2->Get(v8_str("getProp")));
4828
Andrei Popescu74b3c142010-03-29 12:03:09 +01004829 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004830 env2->Exit();
4831 env2->DetachGlobal();
4832 // env2 has a new global object.
4833 CHECK(!env2->Global()->Equals(global2));
4834
4835 v8::Persistent<Context> env3 =
4836 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4837 env3->SetSecurityToken(v8_str("bar"));
4838 env3->Enter();
4839
4840 Local<v8::Object> global3 = env3->Global();
4841 CHECK_EQ(global2, global3);
4842 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4843 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4844 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4845 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4846 env3->Exit();
4847
4848 // Call getProp in env1, and it should return the value 1
4849 {
4850 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4851 CHECK(get_prop->IsFunction());
4852 v8::TryCatch try_catch;
4853 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4854 CHECK(!try_catch.HasCaught());
4855 CHECK_EQ(1, r->Int32Value());
4856 }
4857
4858 // Check that env3 is not accessible from env1
4859 {
4860 Local<Value> r = global3->Get(v8_str("prop2"));
4861 CHECK(r->IsUndefined());
4862 }
4863
4864 env2.Dispose();
4865 env3.Dispose();
4866}
4867
4868
Andrei Popescu74b3c142010-03-29 12:03:09 +01004869TEST(DetachAndReattachGlobal) {
4870 v8::HandleScope scope;
4871 LocalContext env1;
4872
4873 // Create second environment.
4874 v8::Persistent<Context> env2 = Context::New();
4875
4876 Local<Value> foo = v8_str("foo");
4877
4878 // Set same security token for env1 and env2.
4879 env1->SetSecurityToken(foo);
4880 env2->SetSecurityToken(foo);
4881
4882 // Create a property on the global object in env2.
4883 {
4884 v8::Context::Scope scope(env2);
4885 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4886 }
4887
4888 // Create a reference to env2 global from env1 global.
4889 env1->Global()->Set(v8_str("other"), env2->Global());
4890
4891 // Check that we have access to other.p in env2 from env1.
4892 Local<Value> result = CompileRun("other.p");
4893 CHECK(result->IsInt32());
4894 CHECK_EQ(42, result->Int32Value());
4895
4896 // Hold on to global from env2 and detach global from env2.
4897 Local<v8::Object> global2 = env2->Global();
4898 env2->DetachGlobal();
4899
4900 // Check that the global has been detached. No other.p property can
4901 // be found.
4902 result = CompileRun("other.p");
4903 CHECK(result->IsUndefined());
4904
4905 // Reuse global2 for env3.
4906 v8::Persistent<Context> env3 =
4907 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4908 CHECK_EQ(global2, env3->Global());
4909
4910 // Start by using the same security token for env3 as for env1 and env2.
4911 env3->SetSecurityToken(foo);
4912
4913 // Create a property on the global object in env3.
4914 {
4915 v8::Context::Scope scope(env3);
4916 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4917 }
4918
4919 // Check that other.p is now the property in env3 and that we have access.
4920 result = CompileRun("other.p");
4921 CHECK(result->IsInt32());
4922 CHECK_EQ(24, result->Int32Value());
4923
4924 // Change security token for env3 to something different from env1 and env2.
4925 env3->SetSecurityToken(v8_str("bar"));
4926
4927 // Check that we do not have access to other.p in env1. |other| is now
4928 // the global object for env3 which has a different security token,
4929 // so access should be blocked.
4930 result = CompileRun("other.p");
4931 CHECK(result->IsUndefined());
4932
4933 // Detach the global for env3 and reattach it to env2.
4934 env3->DetachGlobal();
4935 env2->ReattachGlobal(global2);
4936
4937 // Check that we have access to other.p again in env1. |other| is now
4938 // the global object for env2 which has the same security token as env1.
4939 result = CompileRun("other.p");
4940 CHECK(result->IsInt32());
4941 CHECK_EQ(42, result->Int32Value());
4942
4943 env2.Dispose();
4944 env3.Dispose();
4945}
4946
4947
Steve Blocka7e24c12009-10-30 11:49:00 +00004948static bool NamedAccessBlocker(Local<v8::Object> global,
4949 Local<Value> name,
4950 v8::AccessType type,
4951 Local<Value> data) {
4952 return Context::GetCurrent()->Global()->Equals(global);
4953}
4954
4955
4956static bool IndexedAccessBlocker(Local<v8::Object> global,
4957 uint32_t key,
4958 v8::AccessType type,
4959 Local<Value> data) {
4960 return Context::GetCurrent()->Global()->Equals(global);
4961}
4962
4963
4964static int g_echo_value = -1;
4965static v8::Handle<Value> EchoGetter(Local<String> name,
4966 const AccessorInfo& info) {
4967 return v8_num(g_echo_value);
4968}
4969
4970
4971static void EchoSetter(Local<String> name,
4972 Local<Value> value,
4973 const AccessorInfo&) {
4974 if (value->IsNumber())
4975 g_echo_value = value->Int32Value();
4976}
4977
4978
4979static v8::Handle<Value> UnreachableGetter(Local<String> name,
4980 const AccessorInfo& info) {
4981 CHECK(false); // This function should not be called..
4982 return v8::Undefined();
4983}
4984
4985
4986static void UnreachableSetter(Local<String>, Local<Value>,
4987 const AccessorInfo&) {
4988 CHECK(false); // This function should nto be called.
4989}
4990
4991
4992THREADED_TEST(AccessControl) {
4993 v8::HandleScope handle_scope;
4994 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4995
4996 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4997 IndexedAccessBlocker);
4998
4999 // Add an accessor accessible by cross-domain JS code.
5000 global_template->SetAccessor(
5001 v8_str("accessible_prop"),
5002 EchoGetter, EchoSetter,
5003 v8::Handle<Value>(),
5004 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5005
5006 // Add an accessor that is not accessible by cross-domain JS code.
5007 global_template->SetAccessor(v8_str("blocked_prop"),
5008 UnreachableGetter, UnreachableSetter,
5009 v8::Handle<Value>(),
5010 v8::DEFAULT);
5011
5012 // Create an environment
5013 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5014 context0->Enter();
5015
5016 v8::Handle<v8::Object> global0 = context0->Global();
5017
5018 v8::HandleScope scope1;
5019
5020 v8::Persistent<Context> context1 = Context::New();
5021 context1->Enter();
5022
5023 v8::Handle<v8::Object> global1 = context1->Global();
5024 global1->Set(v8_str("other"), global0);
5025
5026 v8::Handle<Value> value;
5027
5028 // Access blocked property
5029 value = v8_compile("other.blocked_prop = 1")->Run();
5030 value = v8_compile("other.blocked_prop")->Run();
5031 CHECK(value->IsUndefined());
5032
5033 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
5034 CHECK(value->IsFalse());
5035
5036 // Access accessible property
5037 value = v8_compile("other.accessible_prop = 3")->Run();
5038 CHECK(value->IsNumber());
5039 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005040 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005041
5042 value = v8_compile("other.accessible_prop")->Run();
5043 CHECK(value->IsNumber());
5044 CHECK_EQ(3, value->Int32Value());
5045
5046 value =
5047 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
5048 CHECK(value->IsTrue());
5049
5050 // Enumeration doesn't enumerate accessors from inaccessible objects in
5051 // the prototype chain even if the accessors are in themselves accessible.
5052 Local<Value> result =
5053 CompileRun("(function(){var obj = {'__proto__':other};"
5054 "for (var p in obj)"
5055 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5056 " return false;"
5057 " }"
5058 "return true;})()");
5059 CHECK(result->IsTrue());
5060
5061 context1->Exit();
5062 context0->Exit();
5063 context1.Dispose();
5064 context0.Dispose();
5065}
5066
5067
Leon Clarke4515c472010-02-03 11:58:03 +00005068static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5069 Local<Value> name,
5070 v8::AccessType type,
5071 Local<Value> data) {
5072 return false;
5073}
5074
5075
5076static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5077 uint32_t key,
5078 v8::AccessType type,
5079 Local<Value> data) {
5080 return false;
5081}
5082
5083
5084THREADED_TEST(AccessControlGetOwnPropertyNames) {
5085 v8::HandleScope handle_scope;
5086 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5087
5088 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5089 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5090 GetOwnPropertyNamesIndexedBlocker);
5091
5092 // Create an environment
5093 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5094 context0->Enter();
5095
5096 v8::Handle<v8::Object> global0 = context0->Global();
5097
5098 v8::HandleScope scope1;
5099
5100 v8::Persistent<Context> context1 = Context::New();
5101 context1->Enter();
5102
5103 v8::Handle<v8::Object> global1 = context1->Global();
5104 global1->Set(v8_str("other"), global0);
5105 global1->Set(v8_str("object"), obj_template->NewInstance());
5106
5107 v8::Handle<Value> value;
5108
5109 // Attempt to get the property names of the other global object and
5110 // of an object that requires access checks. Accessing the other
5111 // global object should be blocked by access checks on the global
5112 // proxy object. Accessing the object that requires access checks
5113 // is blocked by the access checks on the object itself.
5114 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5115 CHECK(value->IsTrue());
5116
5117 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5118 CHECK(value->IsTrue());
5119
5120 context1->Exit();
5121 context0->Exit();
5122 context1.Dispose();
5123 context0.Dispose();
5124}
5125
5126
Steve Block8defd9f2010-07-08 12:39:36 +01005127static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5128 v8::Handle<v8::Array> result = v8::Array::New(1);
5129 result->Set(0, v8_str("x"));
5130 return result;
5131}
5132
5133
5134THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5135 v8::HandleScope handle_scope;
5136 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5137
5138 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5139 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5140 NamedPropertyEnumerator);
5141
5142 LocalContext context;
5143 v8::Handle<v8::Object> global = context->Global();
5144 global->Set(v8_str("object"), obj_template->NewInstance());
5145
5146 v8::Handle<Value> value =
5147 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5148 CHECK_EQ(v8_str("x"), value);
5149}
5150
5151
Steve Blocka7e24c12009-10-30 11:49:00 +00005152static v8::Handle<Value> ConstTenGetter(Local<String> name,
5153 const AccessorInfo& info) {
5154 return v8_num(10);
5155}
5156
5157
5158THREADED_TEST(CrossDomainAccessors) {
5159 v8::HandleScope handle_scope;
5160
5161 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5162
5163 v8::Handle<v8::ObjectTemplate> global_template =
5164 func_template->InstanceTemplate();
5165
5166 v8::Handle<v8::ObjectTemplate> proto_template =
5167 func_template->PrototypeTemplate();
5168
5169 // Add an accessor to proto that's accessible by cross-domain JS code.
5170 proto_template->SetAccessor(v8_str("accessible"),
5171 ConstTenGetter, 0,
5172 v8::Handle<Value>(),
5173 v8::ALL_CAN_READ);
5174
5175 // Add an accessor that is not accessible by cross-domain JS code.
5176 global_template->SetAccessor(v8_str("unreachable"),
5177 UnreachableGetter, 0,
5178 v8::Handle<Value>(),
5179 v8::DEFAULT);
5180
5181 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5182 context0->Enter();
5183
5184 Local<v8::Object> global = context0->Global();
5185 // Add a normal property that shadows 'accessible'
5186 global->Set(v8_str("accessible"), v8_num(11));
5187
5188 // Enter a new context.
5189 v8::HandleScope scope1;
5190 v8::Persistent<Context> context1 = Context::New();
5191 context1->Enter();
5192
5193 v8::Handle<v8::Object> global1 = context1->Global();
5194 global1->Set(v8_str("other"), global);
5195
5196 // Should return 10, instead of 11
5197 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5198 CHECK(value->IsNumber());
5199 CHECK_EQ(10, value->Int32Value());
5200
5201 value = v8_compile("other.unreachable")->Run();
5202 CHECK(value->IsUndefined());
5203
5204 context1->Exit();
5205 context0->Exit();
5206 context1.Dispose();
5207 context0.Dispose();
5208}
5209
5210
5211static int named_access_count = 0;
5212static int indexed_access_count = 0;
5213
5214static bool NamedAccessCounter(Local<v8::Object> global,
5215 Local<Value> name,
5216 v8::AccessType type,
5217 Local<Value> data) {
5218 named_access_count++;
5219 return true;
5220}
5221
5222
5223static bool IndexedAccessCounter(Local<v8::Object> global,
5224 uint32_t key,
5225 v8::AccessType type,
5226 Local<Value> data) {
5227 indexed_access_count++;
5228 return true;
5229}
5230
5231
5232// This one is too easily disturbed by other tests.
5233TEST(AccessControlIC) {
5234 named_access_count = 0;
5235 indexed_access_count = 0;
5236
5237 v8::HandleScope handle_scope;
5238
5239 // Create an environment.
5240 v8::Persistent<Context> context0 = Context::New();
5241 context0->Enter();
5242
5243 // Create an object that requires access-check functions to be
5244 // called for cross-domain access.
5245 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5246 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5247 IndexedAccessCounter);
5248 Local<v8::Object> object = object_template->NewInstance();
5249
5250 v8::HandleScope scope1;
5251
5252 // Create another environment.
5253 v8::Persistent<Context> context1 = Context::New();
5254 context1->Enter();
5255
5256 // Make easy access to the object from the other environment.
5257 v8::Handle<v8::Object> global1 = context1->Global();
5258 global1->Set(v8_str("obj"), object);
5259
5260 v8::Handle<Value> value;
5261
5262 // Check that the named access-control function is called every time.
5263 CompileRun("function testProp(obj) {"
5264 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5265 " for (var j = 0; j < 10; j++) obj.prop;"
5266 " return obj.prop"
5267 "}");
5268 value = CompileRun("testProp(obj)");
5269 CHECK(value->IsNumber());
5270 CHECK_EQ(1, value->Int32Value());
5271 CHECK_EQ(21, named_access_count);
5272
5273 // Check that the named access-control function is called every time.
5274 CompileRun("var p = 'prop';"
5275 "function testKeyed(obj) {"
5276 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5277 " for (var j = 0; j < 10; j++) obj[p];"
5278 " return obj[p];"
5279 "}");
5280 // Use obj which requires access checks. No inline caching is used
5281 // in that case.
5282 value = CompileRun("testKeyed(obj)");
5283 CHECK(value->IsNumber());
5284 CHECK_EQ(1, value->Int32Value());
5285 CHECK_EQ(42, named_access_count);
5286 // Force the inline caches into generic state and try again.
5287 CompileRun("testKeyed({ a: 0 })");
5288 CompileRun("testKeyed({ b: 0 })");
5289 value = CompileRun("testKeyed(obj)");
5290 CHECK(value->IsNumber());
5291 CHECK_EQ(1, value->Int32Value());
5292 CHECK_EQ(63, named_access_count);
5293
5294 // Check that the indexed access-control function is called every time.
5295 CompileRun("function testIndexed(obj) {"
5296 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5297 " for (var j = 0; j < 10; j++) obj[0];"
5298 " return obj[0]"
5299 "}");
5300 value = CompileRun("testIndexed(obj)");
5301 CHECK(value->IsNumber());
5302 CHECK_EQ(1, value->Int32Value());
5303 CHECK_EQ(21, indexed_access_count);
5304 // Force the inline caches into generic state.
5305 CompileRun("testIndexed(new Array(1))");
5306 // Test that the indexed access check is called.
5307 value = CompileRun("testIndexed(obj)");
5308 CHECK(value->IsNumber());
5309 CHECK_EQ(1, value->Int32Value());
5310 CHECK_EQ(42, indexed_access_count);
5311
5312 // Check that the named access check is called when invoking
5313 // functions on an object that requires access checks.
5314 CompileRun("obj.f = function() {}");
5315 CompileRun("function testCallNormal(obj) {"
5316 " for (var i = 0; i < 10; i++) obj.f();"
5317 "}");
5318 CompileRun("testCallNormal(obj)");
5319 CHECK_EQ(74, named_access_count);
5320
5321 // Force obj into slow case.
5322 value = CompileRun("delete obj.prop");
5323 CHECK(value->BooleanValue());
5324 // Force inline caches into dictionary probing mode.
5325 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5326 // Test that the named access check is called.
5327 value = CompileRun("testProp(obj);");
5328 CHECK(value->IsNumber());
5329 CHECK_EQ(1, value->Int32Value());
5330 CHECK_EQ(96, named_access_count);
5331
5332 // Force the call inline cache into dictionary probing mode.
5333 CompileRun("o.f = function() {}; testCallNormal(o)");
5334 // Test that the named access check is still called for each
5335 // invocation of the function.
5336 value = CompileRun("testCallNormal(obj)");
5337 CHECK_EQ(106, named_access_count);
5338
5339 context1->Exit();
5340 context0->Exit();
5341 context1.Dispose();
5342 context0.Dispose();
5343}
5344
5345
5346static bool NamedAccessFlatten(Local<v8::Object> global,
5347 Local<Value> name,
5348 v8::AccessType type,
5349 Local<Value> data) {
5350 char buf[100];
5351 int len;
5352
5353 CHECK(name->IsString());
5354
5355 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005356 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005357 CHECK_EQ(4, len);
5358
5359 uint16_t buf2[100];
5360
5361 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005362 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005363 CHECK_EQ(4, len);
5364
5365 return true;
5366}
5367
5368
5369static bool IndexedAccessFlatten(Local<v8::Object> global,
5370 uint32_t key,
5371 v8::AccessType type,
5372 Local<Value> data) {
5373 return true;
5374}
5375
5376
5377// Regression test. In access checks, operations that may cause
5378// garbage collection are not allowed. It used to be the case that
5379// using the Write operation on a string could cause a garbage
5380// collection due to flattening of the string. This is no longer the
5381// case.
5382THREADED_TEST(AccessControlFlatten) {
5383 named_access_count = 0;
5384 indexed_access_count = 0;
5385
5386 v8::HandleScope handle_scope;
5387
5388 // Create an environment.
5389 v8::Persistent<Context> context0 = Context::New();
5390 context0->Enter();
5391
5392 // Create an object that requires access-check functions to be
5393 // called for cross-domain access.
5394 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5395 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5396 IndexedAccessFlatten);
5397 Local<v8::Object> object = object_template->NewInstance();
5398
5399 v8::HandleScope scope1;
5400
5401 // Create another environment.
5402 v8::Persistent<Context> context1 = Context::New();
5403 context1->Enter();
5404
5405 // Make easy access to the object from the other environment.
5406 v8::Handle<v8::Object> global1 = context1->Global();
5407 global1->Set(v8_str("obj"), object);
5408
5409 v8::Handle<Value> value;
5410
5411 value = v8_compile("var p = 'as' + 'df';")->Run();
5412 value = v8_compile("obj[p];")->Run();
5413
5414 context1->Exit();
5415 context0->Exit();
5416 context1.Dispose();
5417 context0.Dispose();
5418}
5419
5420
5421static v8::Handle<Value> AccessControlNamedGetter(
5422 Local<String>, const AccessorInfo&) {
5423 return v8::Integer::New(42);
5424}
5425
5426
5427static v8::Handle<Value> AccessControlNamedSetter(
5428 Local<String>, Local<Value> value, const AccessorInfo&) {
5429 return value;
5430}
5431
5432
5433static v8::Handle<Value> AccessControlIndexedGetter(
5434 uint32_t index,
5435 const AccessorInfo& info) {
5436 return v8_num(42);
5437}
5438
5439
5440static v8::Handle<Value> AccessControlIndexedSetter(
5441 uint32_t, Local<Value> value, const AccessorInfo&) {
5442 return value;
5443}
5444
5445
5446THREADED_TEST(AccessControlInterceptorIC) {
5447 named_access_count = 0;
5448 indexed_access_count = 0;
5449
5450 v8::HandleScope handle_scope;
5451
5452 // Create an environment.
5453 v8::Persistent<Context> context0 = Context::New();
5454 context0->Enter();
5455
5456 // Create an object that requires access-check functions to be
5457 // called for cross-domain access. The object also has interceptors
5458 // interceptor.
5459 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5460 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5461 IndexedAccessCounter);
5462 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5463 AccessControlNamedSetter);
5464 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5465 AccessControlIndexedSetter);
5466 Local<v8::Object> object = object_template->NewInstance();
5467
5468 v8::HandleScope scope1;
5469
5470 // Create another environment.
5471 v8::Persistent<Context> context1 = Context::New();
5472 context1->Enter();
5473
5474 // Make easy access to the object from the other environment.
5475 v8::Handle<v8::Object> global1 = context1->Global();
5476 global1->Set(v8_str("obj"), object);
5477
5478 v8::Handle<Value> value;
5479
5480 // Check that the named access-control function is called every time
5481 // eventhough there is an interceptor on the object.
5482 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5483 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5484 "obj.x")->Run();
5485 CHECK(value->IsNumber());
5486 CHECK_EQ(42, value->Int32Value());
5487 CHECK_EQ(21, named_access_count);
5488
5489 value = v8_compile("var p = 'x';")->Run();
5490 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5491 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5492 "obj[p]")->Run();
5493 CHECK(value->IsNumber());
5494 CHECK_EQ(42, value->Int32Value());
5495 CHECK_EQ(42, named_access_count);
5496
5497 // Check that the indexed access-control function is called every
5498 // time eventhough there is an interceptor on the object.
5499 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5500 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5501 "obj[0]")->Run();
5502 CHECK(value->IsNumber());
5503 CHECK_EQ(42, value->Int32Value());
5504 CHECK_EQ(21, indexed_access_count);
5505
5506 context1->Exit();
5507 context0->Exit();
5508 context1.Dispose();
5509 context0.Dispose();
5510}
5511
5512
5513THREADED_TEST(Version) {
5514 v8::V8::GetVersion();
5515}
5516
5517
5518static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5519 ApiTestFuzzer::Fuzz();
5520 return v8_num(12);
5521}
5522
5523
5524THREADED_TEST(InstanceProperties) {
5525 v8::HandleScope handle_scope;
5526 LocalContext context;
5527
5528 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5529 Local<ObjectTemplate> instance = t->InstanceTemplate();
5530
5531 instance->Set(v8_str("x"), v8_num(42));
5532 instance->Set(v8_str("f"),
5533 v8::FunctionTemplate::New(InstanceFunctionCallback));
5534
5535 Local<Value> o = t->GetFunction()->NewInstance();
5536
5537 context->Global()->Set(v8_str("i"), o);
5538 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5539 CHECK_EQ(42, value->Int32Value());
5540
5541 value = Script::Compile(v8_str("i.f()"))->Run();
5542 CHECK_EQ(12, value->Int32Value());
5543}
5544
5545
5546static v8::Handle<Value>
5547GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5548 ApiTestFuzzer::Fuzz();
5549 return v8::Handle<Value>();
5550}
5551
5552
5553THREADED_TEST(GlobalObjectInstanceProperties) {
5554 v8::HandleScope handle_scope;
5555
5556 Local<Value> global_object;
5557
5558 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5559 t->InstanceTemplate()->SetNamedPropertyHandler(
5560 GlobalObjectInstancePropertiesGet);
5561 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5562 instance_template->Set(v8_str("x"), v8_num(42));
5563 instance_template->Set(v8_str("f"),
5564 v8::FunctionTemplate::New(InstanceFunctionCallback));
5565
5566 {
5567 LocalContext env(NULL, instance_template);
5568 // Hold on to the global object so it can be used again in another
5569 // environment initialization.
5570 global_object = env->Global();
5571
5572 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5573 CHECK_EQ(42, value->Int32Value());
5574 value = Script::Compile(v8_str("f()"))->Run();
5575 CHECK_EQ(12, value->Int32Value());
5576 }
5577
5578 {
5579 // Create new environment reusing the global object.
5580 LocalContext env(NULL, instance_template, global_object);
5581 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5582 CHECK_EQ(42, value->Int32Value());
5583 value = Script::Compile(v8_str("f()"))->Run();
5584 CHECK_EQ(12, value->Int32Value());
5585 }
5586}
5587
5588
5589static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5590 ApiTestFuzzer::Fuzz();
5591 return v8_num(42);
5592}
5593
5594
5595static int shadow_y;
5596static int shadow_y_setter_call_count;
5597static int shadow_y_getter_call_count;
5598
5599
5600static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5601 shadow_y_setter_call_count++;
5602 shadow_y = 42;
5603}
5604
5605
5606static v8::Handle<Value> ShadowYGetter(Local<String> name,
5607 const AccessorInfo& info) {
5608 ApiTestFuzzer::Fuzz();
5609 shadow_y_getter_call_count++;
5610 return v8_num(shadow_y);
5611}
5612
5613
5614static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5615 const AccessorInfo& info) {
5616 return v8::Handle<Value>();
5617}
5618
5619
5620static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5621 const AccessorInfo&) {
5622 return v8::Handle<Value>();
5623}
5624
5625
5626THREADED_TEST(ShadowObject) {
5627 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5628 v8::HandleScope handle_scope;
5629
5630 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5631 LocalContext context(NULL, global_template);
5632
5633 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5634 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5635 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5636 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5637 Local<ObjectTemplate> instance = t->InstanceTemplate();
5638
5639 // Only allow calls of f on instances of t.
5640 Local<v8::Signature> signature = v8::Signature::New(t);
5641 proto->Set(v8_str("f"),
5642 v8::FunctionTemplate::New(ShadowFunctionCallback,
5643 Local<Value>(),
5644 signature));
5645 proto->Set(v8_str("x"), v8_num(12));
5646
5647 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5648
5649 Local<Value> o = t->GetFunction()->NewInstance();
5650 context->Global()->Set(v8_str("__proto__"), o);
5651
5652 Local<Value> value =
5653 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5654 CHECK(value->IsBoolean());
5655 CHECK(!value->BooleanValue());
5656
5657 value = Script::Compile(v8_str("x"))->Run();
5658 CHECK_EQ(12, value->Int32Value());
5659
5660 value = Script::Compile(v8_str("f()"))->Run();
5661 CHECK_EQ(42, value->Int32Value());
5662
5663 Script::Compile(v8_str("y = 42"))->Run();
5664 CHECK_EQ(1, shadow_y_setter_call_count);
5665 value = Script::Compile(v8_str("y"))->Run();
5666 CHECK_EQ(1, shadow_y_getter_call_count);
5667 CHECK_EQ(42, value->Int32Value());
5668}
5669
5670
5671THREADED_TEST(HiddenPrototype) {
5672 v8::HandleScope handle_scope;
5673 LocalContext context;
5674
5675 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5676 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5677 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5678 t1->SetHiddenPrototype(true);
5679 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5680 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5681 t2->SetHiddenPrototype(true);
5682 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5683 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5684 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5685
5686 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5687 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5688 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5689 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5690
5691 // Setting the prototype on an object skips hidden prototypes.
5692 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5693 o0->Set(v8_str("__proto__"), o1);
5694 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5695 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5696 o0->Set(v8_str("__proto__"), o2);
5697 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5698 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5699 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5700 o0->Set(v8_str("__proto__"), o3);
5701 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5702 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5703 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5704 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5705
5706 // Getting the prototype of o0 should get the first visible one
5707 // which is o3. Therefore, z should not be defined on the prototype
5708 // object.
5709 Local<Value> proto = o0->Get(v8_str("__proto__"));
5710 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005711 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005712}
5713
5714
Andrei Popescu402d9372010-02-26 13:31:12 +00005715THREADED_TEST(SetPrototype) {
5716 v8::HandleScope handle_scope;
5717 LocalContext context;
5718
5719 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5720 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5721 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5722 t1->SetHiddenPrototype(true);
5723 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5724 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5725 t2->SetHiddenPrototype(true);
5726 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5727 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5728 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5729
5730 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5731 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5732 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5733 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5734
5735 // Setting the prototype on an object does not skip hidden prototypes.
5736 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5737 CHECK(o0->SetPrototype(o1));
5738 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5739 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5740 CHECK(o1->SetPrototype(o2));
5741 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5742 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5743 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5744 CHECK(o2->SetPrototype(o3));
5745 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5746 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5747 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5748 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5749
5750 // Getting the prototype of o0 should get the first visible one
5751 // which is o3. Therefore, z should not be defined on the prototype
5752 // object.
5753 Local<Value> proto = o0->Get(v8_str("__proto__"));
5754 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005755 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005756
5757 // However, Object::GetPrototype ignores hidden prototype.
5758 Local<Value> proto0 = o0->GetPrototype();
5759 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005760 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005761
5762 Local<Value> proto1 = o1->GetPrototype();
5763 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005764 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005765
5766 Local<Value> proto2 = o2->GetPrototype();
5767 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005768 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005769}
5770
5771
5772THREADED_TEST(SetPrototypeThrows) {
5773 v8::HandleScope handle_scope;
5774 LocalContext context;
5775
5776 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5777
5778 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5779 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5780
5781 CHECK(o0->SetPrototype(o1));
5782 // If setting the prototype leads to the cycle, SetPrototype should
5783 // return false and keep VM in sane state.
5784 v8::TryCatch try_catch;
5785 CHECK(!o1->SetPrototype(o0));
5786 CHECK(!try_catch.HasCaught());
5787 ASSERT(!i::Top::has_pending_exception());
5788
5789 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5790}
5791
5792
Steve Blocka7e24c12009-10-30 11:49:00 +00005793THREADED_TEST(GetterSetterExceptions) {
5794 v8::HandleScope handle_scope;
5795 LocalContext context;
5796 CompileRun(
5797 "function Foo() { };"
5798 "function Throw() { throw 5; };"
5799 "var x = { };"
5800 "x.__defineSetter__('set', Throw);"
5801 "x.__defineGetter__('get', Throw);");
5802 Local<v8::Object> x =
5803 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5804 v8::TryCatch try_catch;
5805 x->Set(v8_str("set"), v8::Integer::New(8));
5806 x->Get(v8_str("get"));
5807 x->Set(v8_str("set"), v8::Integer::New(8));
5808 x->Get(v8_str("get"));
5809 x->Set(v8_str("set"), v8::Integer::New(8));
5810 x->Get(v8_str("get"));
5811 x->Set(v8_str("set"), v8::Integer::New(8));
5812 x->Get(v8_str("get"));
5813}
5814
5815
5816THREADED_TEST(Constructor) {
5817 v8::HandleScope handle_scope;
5818 LocalContext context;
5819 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5820 templ->SetClassName(v8_str("Fun"));
5821 Local<Function> cons = templ->GetFunction();
5822 context->Global()->Set(v8_str("Fun"), cons);
5823 Local<v8::Object> inst = cons->NewInstance();
5824 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5825 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5826 CHECK(value->BooleanValue());
5827}
5828
5829THREADED_TEST(FunctionDescriptorException) {
5830 v8::HandleScope handle_scope;
5831 LocalContext context;
5832 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5833 templ->SetClassName(v8_str("Fun"));
5834 Local<Function> cons = templ->GetFunction();
5835 context->Global()->Set(v8_str("Fun"), cons);
5836 Local<Value> value = CompileRun(
5837 "function test() {"
5838 " try {"
5839 " (new Fun()).blah()"
5840 " } catch (e) {"
5841 " var str = String(e);"
5842 " if (str.indexOf('TypeError') == -1) return 1;"
5843 " if (str.indexOf('[object Fun]') != -1) return 2;"
5844 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5845 " return 0;"
5846 " }"
5847 " return 4;"
5848 "}"
5849 "test();");
5850 CHECK_EQ(0, value->Int32Value());
5851}
5852
5853
5854THREADED_TEST(EvalAliasedDynamic) {
5855 v8::HandleScope scope;
5856 LocalContext current;
5857
5858 // Tests where aliased eval can only be resolved dynamically.
5859 Local<Script> script =
5860 Script::Compile(v8_str("function f(x) { "
5861 " var foo = 2;"
5862 " with (x) { return eval('foo'); }"
5863 "}"
5864 "foo = 0;"
5865 "result1 = f(new Object());"
5866 "result2 = f(this);"
5867 "var x = new Object();"
5868 "x.eval = function(x) { return 1; };"
5869 "result3 = f(x);"));
5870 script->Run();
5871 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5872 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5873 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5874
5875 v8::TryCatch try_catch;
5876 script =
5877 Script::Compile(v8_str("function f(x) { "
5878 " var bar = 2;"
5879 " with (x) { return eval('bar'); }"
5880 "}"
5881 "f(this)"));
5882 script->Run();
5883 CHECK(try_catch.HasCaught());
5884 try_catch.Reset();
5885}
5886
5887
5888THREADED_TEST(CrossEval) {
5889 v8::HandleScope scope;
5890 LocalContext other;
5891 LocalContext current;
5892
5893 Local<String> token = v8_str("<security token>");
5894 other->SetSecurityToken(token);
5895 current->SetSecurityToken(token);
5896
5897 // Setup reference from current to other.
5898 current->Global()->Set(v8_str("other"), other->Global());
5899
5900 // Check that new variables are introduced in other context.
5901 Local<Script> script =
5902 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5903 script->Run();
5904 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5905 CHECK_EQ(1234, foo->Int32Value());
5906 CHECK(!current->Global()->Has(v8_str("foo")));
5907
5908 // Check that writing to non-existing properties introduces them in
5909 // the other context.
5910 script =
5911 Script::Compile(v8_str("other.eval('na = 1234')"));
5912 script->Run();
5913 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5914 CHECK(!current->Global()->Has(v8_str("na")));
5915
5916 // Check that global variables in current context are not visible in other
5917 // context.
5918 v8::TryCatch try_catch;
5919 script =
5920 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5921 Local<Value> result = script->Run();
5922 CHECK(try_catch.HasCaught());
5923 try_catch.Reset();
5924
5925 // Check that local variables in current context are not visible in other
5926 // context.
5927 script =
5928 Script::Compile(v8_str("(function() { "
5929 " var baz = 87;"
5930 " return other.eval('baz');"
5931 "})();"));
5932 result = script->Run();
5933 CHECK(try_catch.HasCaught());
5934 try_catch.Reset();
5935
5936 // Check that global variables in the other environment are visible
5937 // when evaluting code.
5938 other->Global()->Set(v8_str("bis"), v8_num(1234));
5939 script = Script::Compile(v8_str("other.eval('bis')"));
5940 CHECK_EQ(1234, script->Run()->Int32Value());
5941 CHECK(!try_catch.HasCaught());
5942
5943 // Check that the 'this' pointer points to the global object evaluating
5944 // code.
5945 other->Global()->Set(v8_str("t"), other->Global());
5946 script = Script::Compile(v8_str("other.eval('this == t')"));
5947 result = script->Run();
5948 CHECK(result->IsTrue());
5949 CHECK(!try_catch.HasCaught());
5950
5951 // Check that variables introduced in with-statement are not visible in
5952 // other context.
5953 script =
5954 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
5955 result = script->Run();
5956 CHECK(try_catch.HasCaught());
5957 try_catch.Reset();
5958
5959 // Check that you cannot use 'eval.call' with another object than the
5960 // current global object.
5961 script =
5962 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
5963 result = script->Run();
5964 CHECK(try_catch.HasCaught());
5965}
5966
5967
5968// Test that calling eval in a context which has been detached from
5969// its global throws an exception. This behavior is consistent with
5970// other JavaScript implementations.
5971THREADED_TEST(EvalInDetachedGlobal) {
5972 v8::HandleScope scope;
5973
5974 v8::Persistent<Context> context0 = Context::New();
5975 v8::Persistent<Context> context1 = Context::New();
5976
5977 // Setup function in context0 that uses eval from context0.
5978 context0->Enter();
5979 v8::Handle<v8::Value> fun =
5980 CompileRun("var x = 42;"
5981 "(function() {"
5982 " var e = eval;"
5983 " return function(s) { return e(s); }"
5984 "})()");
5985 context0->Exit();
5986
5987 // Put the function into context1 and call it before and after
5988 // detaching the global. Before detaching, the call succeeds and
5989 // after detaching and exception is thrown.
5990 context1->Enter();
5991 context1->Global()->Set(v8_str("fun"), fun);
5992 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
5993 CHECK_EQ(42, x_value->Int32Value());
5994 context0->DetachGlobal();
5995 v8::TryCatch catcher;
5996 x_value = CompileRun("fun('x')");
5997 CHECK(x_value.IsEmpty());
5998 CHECK(catcher.HasCaught());
5999 context1->Exit();
6000
6001 context1.Dispose();
6002 context0.Dispose();
6003}
6004
6005
6006THREADED_TEST(CrossLazyLoad) {
6007 v8::HandleScope scope;
6008 LocalContext other;
6009 LocalContext current;
6010
6011 Local<String> token = v8_str("<security token>");
6012 other->SetSecurityToken(token);
6013 current->SetSecurityToken(token);
6014
6015 // Setup reference from current to other.
6016 current->Global()->Set(v8_str("other"), other->Global());
6017
6018 // Trigger lazy loading in other context.
6019 Local<Script> script =
6020 Script::Compile(v8_str("other.eval('new Date(42)')"));
6021 Local<Value> value = script->Run();
6022 CHECK_EQ(42.0, value->NumberValue());
6023}
6024
6025
6026static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006027 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006028 if (args.IsConstructCall()) {
6029 if (args[0]->IsInt32()) {
6030 return v8_num(-args[0]->Int32Value());
6031 }
6032 }
6033
6034 return args[0];
6035}
6036
6037
6038// Test that a call handler can be set for objects which will allow
6039// non-function objects created through the API to be called as
6040// functions.
6041THREADED_TEST(CallAsFunction) {
6042 v8::HandleScope scope;
6043 LocalContext context;
6044
6045 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6046 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6047 instance_template->SetCallAsFunctionHandler(call_as_function);
6048 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6049 context->Global()->Set(v8_str("obj"), instance);
6050 v8::TryCatch try_catch;
6051 Local<Value> value;
6052 CHECK(!try_catch.HasCaught());
6053
6054 value = CompileRun("obj(42)");
6055 CHECK(!try_catch.HasCaught());
6056 CHECK_EQ(42, value->Int32Value());
6057
6058 value = CompileRun("(function(o){return o(49)})(obj)");
6059 CHECK(!try_catch.HasCaught());
6060 CHECK_EQ(49, value->Int32Value());
6061
6062 // test special case of call as function
6063 value = CompileRun("[obj]['0'](45)");
6064 CHECK(!try_catch.HasCaught());
6065 CHECK_EQ(45, value->Int32Value());
6066
6067 value = CompileRun("obj.call = Function.prototype.call;"
6068 "obj.call(null, 87)");
6069 CHECK(!try_catch.HasCaught());
6070 CHECK_EQ(87, value->Int32Value());
6071
6072 // Regression tests for bug #1116356: Calling call through call/apply
6073 // must work for non-function receivers.
6074 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6075 value = CompileRun(apply_99);
6076 CHECK(!try_catch.HasCaught());
6077 CHECK_EQ(99, value->Int32Value());
6078
6079 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6080 value = CompileRun(call_17);
6081 CHECK(!try_catch.HasCaught());
6082 CHECK_EQ(17, value->Int32Value());
6083
6084 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006085 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006086 value = CompileRun("new obj(43)");
6087 CHECK(!try_catch.HasCaught());
6088 CHECK_EQ(-43, value->Int32Value());
6089}
6090
6091
6092static int CountHandles() {
6093 return v8::HandleScope::NumberOfHandles();
6094}
6095
6096
6097static int Recurse(int depth, int iterations) {
6098 v8::HandleScope scope;
6099 if (depth == 0) return CountHandles();
6100 for (int i = 0; i < iterations; i++) {
6101 Local<v8::Number> n = v8::Integer::New(42);
6102 }
6103 return Recurse(depth - 1, iterations);
6104}
6105
6106
6107THREADED_TEST(HandleIteration) {
6108 static const int kIterations = 500;
6109 static const int kNesting = 200;
6110 CHECK_EQ(0, CountHandles());
6111 {
6112 v8::HandleScope scope1;
6113 CHECK_EQ(0, CountHandles());
6114 for (int i = 0; i < kIterations; i++) {
6115 Local<v8::Number> n = v8::Integer::New(42);
6116 CHECK_EQ(i + 1, CountHandles());
6117 }
6118
6119 CHECK_EQ(kIterations, CountHandles());
6120 {
6121 v8::HandleScope scope2;
6122 for (int j = 0; j < kIterations; j++) {
6123 Local<v8::Number> n = v8::Integer::New(42);
6124 CHECK_EQ(j + 1 + kIterations, CountHandles());
6125 }
6126 }
6127 CHECK_EQ(kIterations, CountHandles());
6128 }
6129 CHECK_EQ(0, CountHandles());
6130 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6131}
6132
6133
6134static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6135 Local<String> name,
6136 const AccessorInfo& info) {
6137 ApiTestFuzzer::Fuzz();
6138 return v8::Handle<Value>();
6139}
6140
6141
6142THREADED_TEST(InterceptorHasOwnProperty) {
6143 v8::HandleScope scope;
6144 LocalContext context;
6145 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6146 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6147 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6148 Local<Function> function = fun_templ->GetFunction();
6149 context->Global()->Set(v8_str("constructor"), function);
6150 v8::Handle<Value> value = CompileRun(
6151 "var o = new constructor();"
6152 "o.hasOwnProperty('ostehaps');");
6153 CHECK_EQ(false, value->BooleanValue());
6154 value = CompileRun(
6155 "o.ostehaps = 42;"
6156 "o.hasOwnProperty('ostehaps');");
6157 CHECK_EQ(true, value->BooleanValue());
6158 value = CompileRun(
6159 "var p = new constructor();"
6160 "p.hasOwnProperty('ostehaps');");
6161 CHECK_EQ(false, value->BooleanValue());
6162}
6163
6164
6165static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6166 Local<String> name,
6167 const AccessorInfo& info) {
6168 ApiTestFuzzer::Fuzz();
6169 i::Heap::CollectAllGarbage(false);
6170 return v8::Handle<Value>();
6171}
6172
6173
6174THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6175 v8::HandleScope scope;
6176 LocalContext context;
6177 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6178 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6179 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6180 Local<Function> function = fun_templ->GetFunction();
6181 context->Global()->Set(v8_str("constructor"), function);
6182 // Let's first make some stuff so we can be sure to get a good GC.
6183 CompileRun(
6184 "function makestr(size) {"
6185 " switch (size) {"
6186 " case 1: return 'f';"
6187 " case 2: return 'fo';"
6188 " case 3: return 'foo';"
6189 " }"
6190 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6191 "}"
6192 "var x = makestr(12345);"
6193 "x = makestr(31415);"
6194 "x = makestr(23456);");
6195 v8::Handle<Value> value = CompileRun(
6196 "var o = new constructor();"
6197 "o.__proto__ = new String(x);"
6198 "o.hasOwnProperty('ostehaps');");
6199 CHECK_EQ(false, value->BooleanValue());
6200}
6201
6202
6203typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6204 const AccessorInfo& info);
6205
6206
6207static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6208 const char* source,
6209 int expected) {
6210 v8::HandleScope scope;
6211 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6212 templ->SetNamedPropertyHandler(getter);
6213 LocalContext context;
6214 context->Global()->Set(v8_str("o"), templ->NewInstance());
6215 v8::Handle<Value> value = CompileRun(source);
6216 CHECK_EQ(expected, value->Int32Value());
6217}
6218
6219
6220static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6221 const AccessorInfo& info) {
6222 ApiTestFuzzer::Fuzz();
6223 CHECK(v8_str("x")->Equals(name));
6224 return v8::Integer::New(42);
6225}
6226
6227
6228// This test should hit the load IC for the interceptor case.
6229THREADED_TEST(InterceptorLoadIC) {
6230 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6231 "var result = 0;"
6232 "for (var i = 0; i < 1000; i++) {"
6233 " result = o.x;"
6234 "}",
6235 42);
6236}
6237
6238
6239// Below go several tests which verify that JITing for various
6240// configurations of interceptor and explicit fields works fine
6241// (those cases are special cased to get better performance).
6242
6243static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6244 const AccessorInfo& info) {
6245 ApiTestFuzzer::Fuzz();
6246 return v8_str("x")->Equals(name)
6247 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6248}
6249
6250
6251THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6252 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6253 "var result = 0;"
6254 "o.y = 239;"
6255 "for (var i = 0; i < 1000; i++) {"
6256 " result = o.y;"
6257 "}",
6258 239);
6259}
6260
6261
6262THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6263 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6264 "var result = 0;"
6265 "o.__proto__ = { 'y': 239 };"
6266 "for (var i = 0; i < 1000; i++) {"
6267 " result = o.y + o.x;"
6268 "}",
6269 239 + 42);
6270}
6271
6272
6273THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6274 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6275 "var result = 0;"
6276 "o.__proto__.y = 239;"
6277 "for (var i = 0; i < 1000; i++) {"
6278 " result = o.y + o.x;"
6279 "}",
6280 239 + 42);
6281}
6282
6283
6284THREADED_TEST(InterceptorLoadICUndefined) {
6285 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6286 "var result = 0;"
6287 "for (var i = 0; i < 1000; i++) {"
6288 " result = (o.y == undefined) ? 239 : 42;"
6289 "}",
6290 239);
6291}
6292
6293
6294THREADED_TEST(InterceptorLoadICWithOverride) {
6295 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6296 "fst = new Object(); fst.__proto__ = o;"
6297 "snd = new Object(); snd.__proto__ = fst;"
6298 "var result1 = 0;"
6299 "for (var i = 0; i < 1000; i++) {"
6300 " result1 = snd.x;"
6301 "}"
6302 "fst.x = 239;"
6303 "var result = 0;"
6304 "for (var i = 0; i < 1000; i++) {"
6305 " result = snd.x;"
6306 "}"
6307 "result + result1",
6308 239 + 42);
6309}
6310
6311
6312// Test the case when we stored field into
6313// a stub, but interceptor produced value on its own.
6314THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6315 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6316 "proto = new Object();"
6317 "o.__proto__ = proto;"
6318 "proto.x = 239;"
6319 "for (var i = 0; i < 1000; i++) {"
6320 " o.x;"
6321 // Now it should be ICed and keep a reference to x defined on proto
6322 "}"
6323 "var result = 0;"
6324 "for (var i = 0; i < 1000; i++) {"
6325 " result += o.x;"
6326 "}"
6327 "result;",
6328 42 * 1000);
6329}
6330
6331
6332// Test the case when we stored field into
6333// a stub, but it got invalidated later on.
6334THREADED_TEST(InterceptorLoadICInvalidatedField) {
6335 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6336 "proto1 = new Object();"
6337 "proto2 = new Object();"
6338 "o.__proto__ = proto1;"
6339 "proto1.__proto__ = proto2;"
6340 "proto2.y = 239;"
6341 "for (var i = 0; i < 1000; i++) {"
6342 " o.y;"
6343 // Now it should be ICed and keep a reference to y defined on proto2
6344 "}"
6345 "proto1.y = 42;"
6346 "var result = 0;"
6347 "for (var i = 0; i < 1000; i++) {"
6348 " result += o.y;"
6349 "}"
6350 "result;",
6351 42 * 1000);
6352}
6353
6354
Steve Block6ded16b2010-05-10 14:33:55 +01006355static int interceptor_load_not_handled_calls = 0;
6356static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6357 const AccessorInfo& info) {
6358 ++interceptor_load_not_handled_calls;
6359 return v8::Handle<v8::Value>();
6360}
6361
6362
6363// Test how post-interceptor lookups are done in the non-cacheable
6364// case: the interceptor should not be invoked during this lookup.
6365THREADED_TEST(InterceptorLoadICPostInterceptor) {
6366 interceptor_load_not_handled_calls = 0;
6367 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6368 "receiver = new Object();"
6369 "receiver.__proto__ = o;"
6370 "proto = new Object();"
6371 "/* Make proto a slow-case object. */"
6372 "for (var i = 0; i < 1000; i++) {"
6373 " proto[\"xxxxxxxx\" + i] = [];"
6374 "}"
6375 "proto.x = 17;"
6376 "o.__proto__ = proto;"
6377 "var result = 0;"
6378 "for (var i = 0; i < 1000; i++) {"
6379 " result += receiver.x;"
6380 "}"
6381 "result;",
6382 17 * 1000);
6383 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6384}
6385
6386
Steve Blocka7e24c12009-10-30 11:49:00 +00006387// Test the case when we stored field into
6388// a stub, but it got invalidated later on due to override on
6389// global object which is between interceptor and fields' holders.
6390THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6391 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6392 "o.__proto__ = this;" // set a global to be a proto of o.
6393 "this.__proto__.y = 239;"
6394 "for (var i = 0; i < 10; i++) {"
6395 " if (o.y != 239) throw 'oops: ' + o.y;"
6396 // Now it should be ICed and keep a reference to y defined on field_holder.
6397 "}"
6398 "this.y = 42;" // Assign on a global.
6399 "var result = 0;"
6400 "for (var i = 0; i < 10; i++) {"
6401 " result += o.y;"
6402 "}"
6403 "result;",
6404 42 * 10);
6405}
6406
6407
6408static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
6409 ApiTestFuzzer::Fuzz();
6410 return v8_num(239);
6411}
6412
6413
6414static void SetOnThis(Local<String> name,
6415 Local<Value> value,
6416 const AccessorInfo& info) {
6417 info.This()->ForceSet(name, value);
6418}
6419
6420
6421THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6422 v8::HandleScope scope;
6423 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6424 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6425 templ->SetAccessor(v8_str("y"), Return239);
6426 LocalContext context;
6427 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006428
6429 // Check the case when receiver and interceptor's holder
6430 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006431 v8::Handle<Value> value = CompileRun(
6432 "var result = 0;"
6433 "for (var i = 0; i < 7; i++) {"
6434 " result = o.y;"
6435 "}");
6436 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006437
6438 // Check the case when interceptor's holder is in proto chain
6439 // of receiver.
6440 value = CompileRun(
6441 "r = { __proto__: o };"
6442 "var result = 0;"
6443 "for (var i = 0; i < 7; i++) {"
6444 " result = r.y;"
6445 "}");
6446 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006447}
6448
6449
6450THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6451 v8::HandleScope scope;
6452 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6453 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6454 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6455 templ_p->SetAccessor(v8_str("y"), Return239);
6456
6457 LocalContext context;
6458 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6459 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6460
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006461 // Check the case when receiver and interceptor's holder
6462 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006463 v8::Handle<Value> value = CompileRun(
6464 "o.__proto__ = p;"
6465 "var result = 0;"
6466 "for (var i = 0; i < 7; i++) {"
6467 " result = o.x + o.y;"
6468 "}");
6469 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006470
6471 // Check the case when interceptor's holder is in proto chain
6472 // of receiver.
6473 value = CompileRun(
6474 "r = { __proto__: o };"
6475 "var result = 0;"
6476 "for (var i = 0; i < 7; i++) {"
6477 " result = r.x + r.y;"
6478 "}");
6479 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006480}
6481
6482
6483THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6484 v8::HandleScope scope;
6485 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6486 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6487 templ->SetAccessor(v8_str("y"), Return239);
6488
6489 LocalContext context;
6490 context->Global()->Set(v8_str("o"), templ->NewInstance());
6491
6492 v8::Handle<Value> value = CompileRun(
6493 "fst = new Object(); fst.__proto__ = o;"
6494 "snd = new Object(); snd.__proto__ = fst;"
6495 "var result1 = 0;"
6496 "for (var i = 0; i < 7; i++) {"
6497 " result1 = snd.x;"
6498 "}"
6499 "fst.x = 239;"
6500 "var result = 0;"
6501 "for (var i = 0; i < 7; i++) {"
6502 " result = snd.x;"
6503 "}"
6504 "result + result1");
6505 CHECK_EQ(239 + 42, value->Int32Value());
6506}
6507
6508
6509// Test the case when we stored callback into
6510// a stub, but interceptor produced value on its own.
6511THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6512 v8::HandleScope scope;
6513 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6514 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6515 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6516 templ_p->SetAccessor(v8_str("y"), Return239);
6517
6518 LocalContext context;
6519 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6520 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6521
6522 v8::Handle<Value> value = CompileRun(
6523 "o.__proto__ = p;"
6524 "for (var i = 0; i < 7; i++) {"
6525 " o.x;"
6526 // Now it should be ICed and keep a reference to x defined on p
6527 "}"
6528 "var result = 0;"
6529 "for (var i = 0; i < 7; i++) {"
6530 " result += o.x;"
6531 "}"
6532 "result");
6533 CHECK_EQ(42 * 7, value->Int32Value());
6534}
6535
6536
6537// Test the case when we stored callback into
6538// a stub, but it got invalidated later on.
6539THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6540 v8::HandleScope scope;
6541 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6542 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6543 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6544 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6545
6546 LocalContext context;
6547 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6548 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6549
6550 v8::Handle<Value> value = CompileRun(
6551 "inbetween = new Object();"
6552 "o.__proto__ = inbetween;"
6553 "inbetween.__proto__ = p;"
6554 "for (var i = 0; i < 10; i++) {"
6555 " o.y;"
6556 // Now it should be ICed and keep a reference to y defined on p
6557 "}"
6558 "inbetween.y = 42;"
6559 "var result = 0;"
6560 "for (var i = 0; i < 10; i++) {"
6561 " result += o.y;"
6562 "}"
6563 "result");
6564 CHECK_EQ(42 * 10, value->Int32Value());
6565}
6566
6567
6568// Test the case when we stored callback into
6569// a stub, but it got invalidated later on due to override on
6570// global object which is between interceptor and callbacks' holders.
6571THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6572 v8::HandleScope scope;
6573 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6574 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6575 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6576 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6577
6578 LocalContext context;
6579 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6580 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6581
6582 v8::Handle<Value> value = CompileRun(
6583 "o.__proto__ = this;"
6584 "this.__proto__ = p;"
6585 "for (var i = 0; i < 10; i++) {"
6586 " if (o.y != 239) throw 'oops: ' + o.y;"
6587 // Now it should be ICed and keep a reference to y defined on p
6588 "}"
6589 "this.y = 42;"
6590 "var result = 0;"
6591 "for (var i = 0; i < 10; i++) {"
6592 " result += o.y;"
6593 "}"
6594 "result");
6595 CHECK_EQ(42 * 10, value->Int32Value());
6596}
6597
6598
6599static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6600 const AccessorInfo& info) {
6601 ApiTestFuzzer::Fuzz();
6602 CHECK(v8_str("x")->Equals(name));
6603 return v8::Integer::New(0);
6604}
6605
6606
6607THREADED_TEST(InterceptorReturningZero) {
6608 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6609 "o.x == undefined ? 1 : 0",
6610 0);
6611}
6612
6613
6614static v8::Handle<Value> InterceptorStoreICSetter(
6615 Local<String> key, Local<Value> value, const AccessorInfo&) {
6616 CHECK(v8_str("x")->Equals(key));
6617 CHECK_EQ(42, value->Int32Value());
6618 return value;
6619}
6620
6621
6622// This test should hit the store IC for the interceptor case.
6623THREADED_TEST(InterceptorStoreIC) {
6624 v8::HandleScope scope;
6625 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6626 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6627 InterceptorStoreICSetter);
6628 LocalContext context;
6629 context->Global()->Set(v8_str("o"), templ->NewInstance());
6630 v8::Handle<Value> value = CompileRun(
6631 "for (var i = 0; i < 1000; i++) {"
6632 " o.x = 42;"
6633 "}");
6634}
6635
6636
6637THREADED_TEST(InterceptorStoreICWithNoSetter) {
6638 v8::HandleScope scope;
6639 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6640 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6641 LocalContext context;
6642 context->Global()->Set(v8_str("o"), templ->NewInstance());
6643 v8::Handle<Value> value = CompileRun(
6644 "for (var i = 0; i < 1000; i++) {"
6645 " o.y = 239;"
6646 "}"
6647 "42 + o.y");
6648 CHECK_EQ(239 + 42, value->Int32Value());
6649}
6650
6651
6652
6653
6654v8::Handle<Value> call_ic_function;
6655v8::Handle<Value> call_ic_function2;
6656v8::Handle<Value> call_ic_function3;
6657
6658static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6659 const AccessorInfo& info) {
6660 ApiTestFuzzer::Fuzz();
6661 CHECK(v8_str("x")->Equals(name));
6662 return call_ic_function;
6663}
6664
6665
6666// This test should hit the call IC for the interceptor case.
6667THREADED_TEST(InterceptorCallIC) {
6668 v8::HandleScope scope;
6669 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6670 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6671 LocalContext context;
6672 context->Global()->Set(v8_str("o"), templ->NewInstance());
6673 call_ic_function =
6674 v8_compile("function f(x) { return x + 1; }; f")->Run();
6675 v8::Handle<Value> value = CompileRun(
6676 "var result = 0;"
6677 "for (var i = 0; i < 1000; i++) {"
6678 " result = o.x(41);"
6679 "}");
6680 CHECK_EQ(42, value->Int32Value());
6681}
6682
6683
6684// This test checks that if interceptor doesn't provide
6685// a value, we can fetch regular value.
6686THREADED_TEST(InterceptorCallICSeesOthers) {
6687 v8::HandleScope scope;
6688 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6689 templ->SetNamedPropertyHandler(NoBlockGetterX);
6690 LocalContext context;
6691 context->Global()->Set(v8_str("o"), templ->NewInstance());
6692 v8::Handle<Value> value = CompileRun(
6693 "o.x = function f(x) { return x + 1; };"
6694 "var result = 0;"
6695 "for (var i = 0; i < 7; i++) {"
6696 " result = o.x(41);"
6697 "}");
6698 CHECK_EQ(42, value->Int32Value());
6699}
6700
6701
6702static v8::Handle<Value> call_ic_function4;
6703static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6704 const AccessorInfo& info) {
6705 ApiTestFuzzer::Fuzz();
6706 CHECK(v8_str("x")->Equals(name));
6707 return call_ic_function4;
6708}
6709
6710
6711// This test checks that if interceptor provides a function,
6712// even if we cached shadowed variant, interceptor's function
6713// is invoked
6714THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6715 v8::HandleScope scope;
6716 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6717 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6718 LocalContext context;
6719 context->Global()->Set(v8_str("o"), templ->NewInstance());
6720 call_ic_function4 =
6721 v8_compile("function f(x) { return x - 1; }; f")->Run();
6722 v8::Handle<Value> value = CompileRun(
6723 "o.__proto__.x = function(x) { return x + 1; };"
6724 "var result = 0;"
6725 "for (var i = 0; i < 1000; i++) {"
6726 " result = o.x(42);"
6727 "}");
6728 CHECK_EQ(41, value->Int32Value());
6729}
6730
6731
6732// Test the case when we stored cacheable lookup into
6733// a stub, but it got invalidated later on
6734THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6735 v8::HandleScope scope;
6736 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6737 templ->SetNamedPropertyHandler(NoBlockGetterX);
6738 LocalContext context;
6739 context->Global()->Set(v8_str("o"), templ->NewInstance());
6740 v8::Handle<Value> value = CompileRun(
6741 "proto1 = new Object();"
6742 "proto2 = new Object();"
6743 "o.__proto__ = proto1;"
6744 "proto1.__proto__ = proto2;"
6745 "proto2.y = function(x) { return x + 1; };"
6746 // Invoke it many times to compile a stub
6747 "for (var i = 0; i < 7; i++) {"
6748 " o.y(42);"
6749 "}"
6750 "proto1.y = function(x) { return x - 1; };"
6751 "var result = 0;"
6752 "for (var i = 0; i < 7; i++) {"
6753 " result += o.y(42);"
6754 "}");
6755 CHECK_EQ(41 * 7, value->Int32Value());
6756}
6757
6758
6759static v8::Handle<Value> call_ic_function5;
6760static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6761 const AccessorInfo& info) {
6762 ApiTestFuzzer::Fuzz();
6763 if (v8_str("x")->Equals(name))
6764 return call_ic_function5;
6765 else
6766 return Local<Value>();
6767}
6768
6769
6770// This test checks that if interceptor doesn't provide a function,
6771// cached constant function is used
6772THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6773 v8::HandleScope scope;
6774 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6775 templ->SetNamedPropertyHandler(NoBlockGetterX);
6776 LocalContext context;
6777 context->Global()->Set(v8_str("o"), templ->NewInstance());
6778 v8::Handle<Value> value = CompileRun(
6779 "function inc(x) { return x + 1; };"
6780 "inc(1);"
6781 "o.x = inc;"
6782 "var result = 0;"
6783 "for (var i = 0; i < 1000; i++) {"
6784 " result = o.x(42);"
6785 "}");
6786 CHECK_EQ(43, value->Int32Value());
6787}
6788
6789
6790// This test checks that if interceptor provides a function,
6791// even if we cached constant function, interceptor's function
6792// is invoked
6793THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6794 v8::HandleScope scope;
6795 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6796 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6797 LocalContext context;
6798 context->Global()->Set(v8_str("o"), templ->NewInstance());
6799 call_ic_function5 =
6800 v8_compile("function f(x) { return x - 1; }; f")->Run();
6801 v8::Handle<Value> value = CompileRun(
6802 "function inc(x) { return x + 1; };"
6803 "inc(1);"
6804 "o.x = inc;"
6805 "var result = 0;"
6806 "for (var i = 0; i < 1000; i++) {"
6807 " result = o.x(42);"
6808 "}");
6809 CHECK_EQ(41, value->Int32Value());
6810}
6811
6812
6813// Test the case when we stored constant function into
6814// a stub, but it got invalidated later on
6815THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6816 v8::HandleScope scope;
6817 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6818 templ->SetNamedPropertyHandler(NoBlockGetterX);
6819 LocalContext context;
6820 context->Global()->Set(v8_str("o"), templ->NewInstance());
6821 v8::Handle<Value> value = CompileRun(
6822 "function inc(x) { return x + 1; };"
6823 "inc(1);"
6824 "proto1 = new Object();"
6825 "proto2 = new Object();"
6826 "o.__proto__ = proto1;"
6827 "proto1.__proto__ = proto2;"
6828 "proto2.y = inc;"
6829 // Invoke it many times to compile a stub
6830 "for (var i = 0; i < 7; i++) {"
6831 " o.y(42);"
6832 "}"
6833 "proto1.y = function(x) { return x - 1; };"
6834 "var result = 0;"
6835 "for (var i = 0; i < 7; i++) {"
6836 " result += o.y(42);"
6837 "}");
6838 CHECK_EQ(41 * 7, value->Int32Value());
6839}
6840
6841
6842// Test the case when we stored constant function into
6843// a stub, but it got invalidated later on due to override on
6844// global object which is between interceptor and constant function' holders.
6845THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
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 "function inc(x) { return x + 1; };"
6853 "inc(1);"
6854 "o.__proto__ = this;"
6855 "this.__proto__.y = inc;"
6856 // Invoke it many times to compile a stub
6857 "for (var i = 0; i < 7; i++) {"
6858 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6859 "}"
6860 "this.y = function(x) { return x - 1; };"
6861 "var result = 0;"
6862 "for (var i = 0; i < 7; i++) {"
6863 " result += o.y(42);"
6864 "}");
6865 CHECK_EQ(41 * 7, value->Int32Value());
6866}
6867
6868
Leon Clarke4515c472010-02-03 11:58:03 +00006869// Test the case when actual function to call sits on global object.
6870THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6871 v8::HandleScope scope;
6872 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6873 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6874
6875 LocalContext context;
6876 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6877
6878 v8::Handle<Value> value = CompileRun(
6879 "try {"
6880 " o.__proto__ = this;"
6881 " for (var i = 0; i < 10; i++) {"
6882 " var v = o.parseFloat('239');"
6883 " if (v != 239) throw v;"
6884 // Now it should be ICed and keep a reference to parseFloat.
6885 " }"
6886 " var result = 0;"
6887 " for (var i = 0; i < 10; i++) {"
6888 " result += o.parseFloat('239');"
6889 " }"
6890 " result"
6891 "} catch(e) {"
6892 " e"
6893 "};");
6894 CHECK_EQ(239 * 10, value->Int32Value());
6895}
6896
Andrei Popescu402d9372010-02-26 13:31:12 +00006897static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6898 const AccessorInfo& info) {
6899 ApiTestFuzzer::Fuzz();
6900 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6901 ++(*call_count);
6902 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01006903 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00006904 }
6905 return v8::Handle<Value>();
6906}
6907
6908static v8::Handle<Value> FastApiCallback_TrivialSignature(
6909 const v8::Arguments& args) {
6910 ApiTestFuzzer::Fuzz();
6911 CHECK_EQ(args.This(), args.Holder());
6912 CHECK(args.Data()->Equals(v8_str("method_data")));
6913 return v8::Integer::New(args[0]->Int32Value() + 1);
6914}
6915
6916static v8::Handle<Value> FastApiCallback_SimpleSignature(
6917 const v8::Arguments& args) {
6918 ApiTestFuzzer::Fuzz();
6919 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6920 CHECK(args.Data()->Equals(v8_str("method_data")));
6921 // Note, we're using HasRealNamedProperty instead of Has to avoid
6922 // invoking the interceptor again.
6923 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6924 return v8::Integer::New(args[0]->Int32Value() + 1);
6925}
6926
6927// Helper to maximize the odds of object moving.
6928static void GenerateSomeGarbage() {
6929 CompileRun(
6930 "var garbage;"
6931 "for (var i = 0; i < 1000; i++) {"
6932 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6933 "}"
6934 "garbage = undefined;");
6935}
6936
6937THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
6938 int interceptor_call_count = 0;
6939 v8::HandleScope scope;
6940 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6941 v8::Handle<v8::FunctionTemplate> method_templ =
6942 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6943 v8_str("method_data"),
6944 v8::Handle<v8::Signature>());
6945 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6946 proto_templ->Set(v8_str("method"), method_templ);
6947 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6948 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6949 NULL, NULL, NULL, NULL,
6950 v8::External::Wrap(&interceptor_call_count));
6951 LocalContext context;
6952 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6953 GenerateSomeGarbage();
6954 context->Global()->Set(v8_str("o"), fun->NewInstance());
6955 v8::Handle<Value> value = CompileRun(
6956 "var result = 0;"
6957 "for (var i = 0; i < 100; i++) {"
6958 " result = o.method(41);"
6959 "}");
6960 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6961 CHECK_EQ(100, interceptor_call_count);
6962}
6963
6964THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
6965 int interceptor_call_count = 0;
6966 v8::HandleScope scope;
6967 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6968 v8::Handle<v8::FunctionTemplate> method_templ =
6969 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6970 v8_str("method_data"),
6971 v8::Signature::New(fun_templ));
6972 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6973 proto_templ->Set(v8_str("method"), method_templ);
6974 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6975 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6976 NULL, NULL, NULL, NULL,
6977 v8::External::Wrap(&interceptor_call_count));
6978 LocalContext context;
6979 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6980 GenerateSomeGarbage();
6981 context->Global()->Set(v8_str("o"), fun->NewInstance());
6982 v8::Handle<Value> value = CompileRun(
6983 "o.foo = 17;"
6984 "var receiver = {};"
6985 "receiver.__proto__ = o;"
6986 "var result = 0;"
6987 "for (var i = 0; i < 100; i++) {"
6988 " result = receiver.method(41);"
6989 "}");
6990 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6991 CHECK_EQ(100, interceptor_call_count);
6992}
6993
6994THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
6995 int interceptor_call_count = 0;
6996 v8::HandleScope scope;
6997 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6998 v8::Handle<v8::FunctionTemplate> method_templ =
6999 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7000 v8_str("method_data"),
7001 v8::Signature::New(fun_templ));
7002 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7003 proto_templ->Set(v8_str("method"), method_templ);
7004 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7005 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7006 NULL, NULL, NULL, NULL,
7007 v8::External::Wrap(&interceptor_call_count));
7008 LocalContext context;
7009 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7010 GenerateSomeGarbage();
7011 context->Global()->Set(v8_str("o"), fun->NewInstance());
7012 v8::Handle<Value> value = CompileRun(
7013 "o.foo = 17;"
7014 "var receiver = {};"
7015 "receiver.__proto__ = o;"
7016 "var result = 0;"
7017 "var saved_result = 0;"
7018 "for (var i = 0; i < 100; i++) {"
7019 " result = receiver.method(41);"
7020 " if (i == 50) {"
7021 " saved_result = result;"
7022 " receiver = {method: function(x) { return x - 1 }};"
7023 " }"
7024 "}");
7025 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7026 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7027 CHECK_GE(interceptor_call_count, 50);
7028}
7029
7030THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
7031 int interceptor_call_count = 0;
7032 v8::HandleScope scope;
7033 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7034 v8::Handle<v8::FunctionTemplate> method_templ =
7035 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7036 v8_str("method_data"),
7037 v8::Signature::New(fun_templ));
7038 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7039 proto_templ->Set(v8_str("method"), method_templ);
7040 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7041 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7042 NULL, NULL, NULL, NULL,
7043 v8::External::Wrap(&interceptor_call_count));
7044 LocalContext context;
7045 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7046 GenerateSomeGarbage();
7047 context->Global()->Set(v8_str("o"), fun->NewInstance());
7048 v8::Handle<Value> value = CompileRun(
7049 "o.foo = 17;"
7050 "var receiver = {};"
7051 "receiver.__proto__ = o;"
7052 "var result = 0;"
7053 "var saved_result = 0;"
7054 "for (var i = 0; i < 100; i++) {"
7055 " result = receiver.method(41);"
7056 " if (i == 50) {"
7057 " saved_result = result;"
7058 " o.method = function(x) { return x - 1 };"
7059 " }"
7060 "}");
7061 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7062 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7063 CHECK_GE(interceptor_call_count, 50);
7064}
7065
Steve Block6ded16b2010-05-10 14:33:55 +01007066THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7067 int interceptor_call_count = 0;
7068 v8::HandleScope scope;
7069 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7070 v8::Handle<v8::FunctionTemplate> method_templ =
7071 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7072 v8_str("method_data"),
7073 v8::Signature::New(fun_templ));
7074 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7075 proto_templ->Set(v8_str("method"), method_templ);
7076 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7077 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7078 NULL, NULL, NULL, NULL,
7079 v8::External::Wrap(&interceptor_call_count));
7080 LocalContext context;
7081 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7082 GenerateSomeGarbage();
7083 context->Global()->Set(v8_str("o"), fun->NewInstance());
7084 v8::TryCatch try_catch;
7085 v8::Handle<Value> value = CompileRun(
7086 "o.foo = 17;"
7087 "var receiver = {};"
7088 "receiver.__proto__ = o;"
7089 "var result = 0;"
7090 "var saved_result = 0;"
7091 "for (var i = 0; i < 100; i++) {"
7092 " result = receiver.method(41);"
7093 " if (i == 50) {"
7094 " saved_result = result;"
7095 " receiver = 333;"
7096 " }"
7097 "}");
7098 CHECK(try_catch.HasCaught());
7099 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7100 try_catch.Exception()->ToString());
7101 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7102 CHECK_GE(interceptor_call_count, 50);
7103}
7104
Andrei Popescu402d9372010-02-26 13:31:12 +00007105THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
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::TryCatch try_catch;
7124 v8::Handle<Value> value = CompileRun(
7125 "o.foo = 17;"
7126 "var receiver = {};"
7127 "receiver.__proto__ = o;"
7128 "var result = 0;"
7129 "var saved_result = 0;"
7130 "for (var i = 0; i < 100; i++) {"
7131 " result = receiver.method(41);"
7132 " if (i == 50) {"
7133 " saved_result = result;"
7134 " receiver = {method: receiver.method};"
7135 " }"
7136 "}");
7137 CHECK(try_catch.HasCaught());
7138 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7139 try_catch.Exception()->ToString());
7140 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7141 CHECK_GE(interceptor_call_count, 50);
7142}
7143
7144THREADED_TEST(CallICFastApi_TrivialSignature) {
7145 v8::HandleScope scope;
7146 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7147 v8::Handle<v8::FunctionTemplate> method_templ =
7148 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7149 v8_str("method_data"),
7150 v8::Handle<v8::Signature>());
7151 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7152 proto_templ->Set(v8_str("method"), method_templ);
7153 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7154 LocalContext context;
7155 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7156 GenerateSomeGarbage();
7157 context->Global()->Set(v8_str("o"), fun->NewInstance());
7158 v8::Handle<Value> value = CompileRun(
7159 "var result = 0;"
7160 "for (var i = 0; i < 100; i++) {"
7161 " result = o.method(41);"
7162 "}");
7163
7164 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7165}
7166
7167THREADED_TEST(CallICFastApi_SimpleSignature) {
7168 v8::HandleScope scope;
7169 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7170 v8::Handle<v8::FunctionTemplate> method_templ =
7171 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7172 v8_str("method_data"),
7173 v8::Signature::New(fun_templ));
7174 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7175 proto_templ->Set(v8_str("method"), method_templ);
7176 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7177 LocalContext context;
7178 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7179 GenerateSomeGarbage();
7180 context->Global()->Set(v8_str("o"), fun->NewInstance());
7181 v8::Handle<Value> value = CompileRun(
7182 "o.foo = 17;"
7183 "var receiver = {};"
7184 "receiver.__proto__ = o;"
7185 "var result = 0;"
7186 "for (var i = 0; i < 100; i++) {"
7187 " result = receiver.method(41);"
7188 "}");
7189
7190 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7191}
7192
Steve Block6ded16b2010-05-10 14:33:55 +01007193THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007194 v8::HandleScope scope;
7195 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7196 v8::Handle<v8::FunctionTemplate> method_templ =
7197 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7198 v8_str("method_data"),
7199 v8::Signature::New(fun_templ));
7200 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7201 proto_templ->Set(v8_str("method"), method_templ);
7202 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7203 LocalContext context;
7204 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7205 GenerateSomeGarbage();
7206 context->Global()->Set(v8_str("o"), fun->NewInstance());
7207 v8::Handle<Value> value = CompileRun(
7208 "o.foo = 17;"
7209 "var receiver = {};"
7210 "receiver.__proto__ = o;"
7211 "var result = 0;"
7212 "var saved_result = 0;"
7213 "for (var i = 0; i < 100; i++) {"
7214 " result = receiver.method(41);"
7215 " if (i == 50) {"
7216 " saved_result = result;"
7217 " receiver = {method: function(x) { return x - 1 }};"
7218 " }"
7219 "}");
7220 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7221 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7222}
7223
Steve Block6ded16b2010-05-10 14:33:55 +01007224THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7225 v8::HandleScope scope;
7226 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7227 v8::Handle<v8::FunctionTemplate> method_templ =
7228 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7229 v8_str("method_data"),
7230 v8::Signature::New(fun_templ));
7231 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7232 proto_templ->Set(v8_str("method"), method_templ);
7233 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7234 LocalContext context;
7235 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7236 GenerateSomeGarbage();
7237 context->Global()->Set(v8_str("o"), fun->NewInstance());
7238 v8::TryCatch try_catch;
7239 v8::Handle<Value> value = CompileRun(
7240 "o.foo = 17;"
7241 "var receiver = {};"
7242 "receiver.__proto__ = o;"
7243 "var result = 0;"
7244 "var saved_result = 0;"
7245 "for (var i = 0; i < 100; i++) {"
7246 " result = receiver.method(41);"
7247 " if (i == 50) {"
7248 " saved_result = result;"
7249 " receiver = 333;"
7250 " }"
7251 "}");
7252 CHECK(try_catch.HasCaught());
7253 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7254 try_catch.Exception()->ToString());
7255 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7256}
7257
Leon Clarke4515c472010-02-03 11:58:03 +00007258
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007259v8::Handle<Value> keyed_call_ic_function;
7260
7261static v8::Handle<Value> InterceptorKeyedCallICGetter(
7262 Local<String> name, const AccessorInfo& info) {
7263 ApiTestFuzzer::Fuzz();
7264 if (v8_str("x")->Equals(name)) {
7265 return keyed_call_ic_function;
7266 }
7267 return v8::Handle<Value>();
7268}
7269
7270
7271// Test the case when we stored cacheable lookup into
7272// a stub, but the function name changed (to another cacheable function).
7273THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7274 v8::HandleScope scope;
7275 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7276 templ->SetNamedPropertyHandler(NoBlockGetterX);
7277 LocalContext context;
7278 context->Global()->Set(v8_str("o"), templ->NewInstance());
7279 v8::Handle<Value> value = CompileRun(
7280 "proto = new Object();"
7281 "proto.y = function(x) { return x + 1; };"
7282 "proto.z = function(x) { return x - 1; };"
7283 "o.__proto__ = proto;"
7284 "var result = 0;"
7285 "var method = 'y';"
7286 "for (var i = 0; i < 10; i++) {"
7287 " if (i == 5) { method = 'z'; };"
7288 " result += o[method](41);"
7289 "}");
7290 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7291}
7292
7293
7294// Test the case when we stored cacheable lookup into
7295// a stub, but the function name changed (and the new function is present
7296// both before and after the interceptor in the prototype chain).
7297THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7298 v8::HandleScope scope;
7299 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7300 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7301 LocalContext context;
7302 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7303 keyed_call_ic_function =
7304 v8_compile("function f(x) { return x - 1; }; f")->Run();
7305 v8::Handle<Value> value = CompileRun(
7306 "o = new Object();"
7307 "proto2 = new Object();"
7308 "o.y = function(x) { return x + 1; };"
7309 "proto2.y = function(x) { return x + 2; };"
7310 "o.__proto__ = proto1;"
7311 "proto1.__proto__ = proto2;"
7312 "var result = 0;"
7313 "var method = 'x';"
7314 "for (var i = 0; i < 10; i++) {"
7315 " if (i == 5) { method = 'y'; };"
7316 " result += o[method](41);"
7317 "}");
7318 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7319}
7320
7321
7322// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7323// on the global object.
7324THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7325 v8::HandleScope scope;
7326 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7327 templ->SetNamedPropertyHandler(NoBlockGetterX);
7328 LocalContext context;
7329 context->Global()->Set(v8_str("o"), templ->NewInstance());
7330 v8::Handle<Value> value = CompileRun(
7331 "function inc(x) { return x + 1; };"
7332 "inc(1);"
7333 "function dec(x) { return x - 1; };"
7334 "dec(1);"
7335 "o.__proto__ = this;"
7336 "this.__proto__.x = inc;"
7337 "this.__proto__.y = dec;"
7338 "var result = 0;"
7339 "var method = 'x';"
7340 "for (var i = 0; i < 10; i++) {"
7341 " if (i == 5) { method = 'y'; };"
7342 " result += o[method](41);"
7343 "}");
7344 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7345}
7346
7347
7348// Test the case when actual function to call sits on global object.
7349THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7350 v8::HandleScope scope;
7351 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7352 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7353 LocalContext context;
7354 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7355
7356 v8::Handle<Value> value = CompileRun(
7357 "function len(x) { return x.length; };"
7358 "o.__proto__ = this;"
7359 "var m = 'parseFloat';"
7360 "var result = 0;"
7361 "for (var i = 0; i < 10; i++) {"
7362 " if (i == 5) {"
7363 " m = 'len';"
7364 " saved_result = result;"
7365 " };"
7366 " result = o[m]('239');"
7367 "}");
7368 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7369 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7370}
7371
7372// Test the map transition before the interceptor.
7373THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7374 v8::HandleScope scope;
7375 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7376 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7377 LocalContext context;
7378 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7379
7380 v8::Handle<Value> value = CompileRun(
7381 "var o = new Object();"
7382 "o.__proto__ = proto;"
7383 "o.method = function(x) { return x + 1; };"
7384 "var m = 'method';"
7385 "var result = 0;"
7386 "for (var i = 0; i < 10; i++) {"
7387 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7388 " result += o[m](41);"
7389 "}");
7390 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7391}
7392
7393
7394// Test the map transition after the interceptor.
7395THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7396 v8::HandleScope scope;
7397 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7398 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7399 LocalContext context;
7400 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7401
7402 v8::Handle<Value> value = CompileRun(
7403 "var proto = new Object();"
7404 "o.__proto__ = proto;"
7405 "proto.method = function(x) { return x + 1; };"
7406 "var m = 'method';"
7407 "var result = 0;"
7408 "for (var i = 0; i < 10; i++) {"
7409 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7410 " result += o[m](41);"
7411 "}");
7412 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7413}
7414
7415
Steve Blocka7e24c12009-10-30 11:49:00 +00007416static int interceptor_call_count = 0;
7417
7418static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7419 const AccessorInfo& info) {
7420 ApiTestFuzzer::Fuzz();
7421 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7422 return call_ic_function2;
7423 }
7424 return v8::Handle<Value>();
7425}
7426
7427
7428// This test should hit load and call ICs for the interceptor case.
7429// Once in a while, the interceptor will reply that a property was not
7430// found in which case we should get a reference error.
7431THREADED_TEST(InterceptorICReferenceErrors) {
7432 v8::HandleScope scope;
7433 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7434 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7435 LocalContext context(0, templ, v8::Handle<Value>());
7436 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7437 v8::Handle<Value> value = CompileRun(
7438 "function f() {"
7439 " for (var i = 0; i < 1000; i++) {"
7440 " try { x; } catch(e) { return true; }"
7441 " }"
7442 " return false;"
7443 "};"
7444 "f();");
7445 CHECK_EQ(true, value->BooleanValue());
7446 interceptor_call_count = 0;
7447 value = CompileRun(
7448 "function g() {"
7449 " for (var i = 0; i < 1000; i++) {"
7450 " try { x(42); } catch(e) { return true; }"
7451 " }"
7452 " return false;"
7453 "};"
7454 "g();");
7455 CHECK_EQ(true, value->BooleanValue());
7456}
7457
7458
7459static int interceptor_ic_exception_get_count = 0;
7460
7461static v8::Handle<Value> InterceptorICExceptionGetter(
7462 Local<String> name,
7463 const AccessorInfo& info) {
7464 ApiTestFuzzer::Fuzz();
7465 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7466 return call_ic_function3;
7467 }
7468 if (interceptor_ic_exception_get_count == 20) {
7469 return v8::ThrowException(v8_num(42));
7470 }
7471 // Do not handle get for properties other than x.
7472 return v8::Handle<Value>();
7473}
7474
7475// Test interceptor load/call IC where the interceptor throws an
7476// exception once in a while.
7477THREADED_TEST(InterceptorICGetterExceptions) {
7478 interceptor_ic_exception_get_count = 0;
7479 v8::HandleScope scope;
7480 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7481 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7482 LocalContext context(0, templ, v8::Handle<Value>());
7483 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7484 v8::Handle<Value> value = CompileRun(
7485 "function f() {"
7486 " for (var i = 0; i < 100; i++) {"
7487 " try { x; } catch(e) { return true; }"
7488 " }"
7489 " return false;"
7490 "};"
7491 "f();");
7492 CHECK_EQ(true, value->BooleanValue());
7493 interceptor_ic_exception_get_count = 0;
7494 value = CompileRun(
7495 "function f() {"
7496 " for (var i = 0; i < 100; i++) {"
7497 " try { x(42); } catch(e) { return true; }"
7498 " }"
7499 " return false;"
7500 "};"
7501 "f();");
7502 CHECK_EQ(true, value->BooleanValue());
7503}
7504
7505
7506static int interceptor_ic_exception_set_count = 0;
7507
7508static v8::Handle<Value> InterceptorICExceptionSetter(
7509 Local<String> key, Local<Value> value, const AccessorInfo&) {
7510 ApiTestFuzzer::Fuzz();
7511 if (++interceptor_ic_exception_set_count > 20) {
7512 return v8::ThrowException(v8_num(42));
7513 }
7514 // Do not actually handle setting.
7515 return v8::Handle<Value>();
7516}
7517
7518// Test interceptor store IC where the interceptor throws an exception
7519// once in a while.
7520THREADED_TEST(InterceptorICSetterExceptions) {
7521 interceptor_ic_exception_set_count = 0;
7522 v8::HandleScope scope;
7523 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7524 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7525 LocalContext context(0, templ, v8::Handle<Value>());
7526 v8::Handle<Value> value = CompileRun(
7527 "function f() {"
7528 " for (var i = 0; i < 100; i++) {"
7529 " try { x = 42; } catch(e) { return true; }"
7530 " }"
7531 " return false;"
7532 "};"
7533 "f();");
7534 CHECK_EQ(true, value->BooleanValue());
7535}
7536
7537
7538// Test that we ignore null interceptors.
7539THREADED_TEST(NullNamedInterceptor) {
7540 v8::HandleScope scope;
7541 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7542 templ->SetNamedPropertyHandler(0);
7543 LocalContext context;
7544 templ->Set("x", v8_num(42));
7545 v8::Handle<v8::Object> obj = templ->NewInstance();
7546 context->Global()->Set(v8_str("obj"), obj);
7547 v8::Handle<Value> value = CompileRun("obj.x");
7548 CHECK(value->IsInt32());
7549 CHECK_EQ(42, value->Int32Value());
7550}
7551
7552
7553// Test that we ignore null interceptors.
7554THREADED_TEST(NullIndexedInterceptor) {
7555 v8::HandleScope scope;
7556 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7557 templ->SetIndexedPropertyHandler(0);
7558 LocalContext context;
7559 templ->Set("42", v8_num(42));
7560 v8::Handle<v8::Object> obj = templ->NewInstance();
7561 context->Global()->Set(v8_str("obj"), obj);
7562 v8::Handle<Value> value = CompileRun("obj[42]");
7563 CHECK(value->IsInt32());
7564 CHECK_EQ(42, value->Int32Value());
7565}
7566
7567
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007568THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7569 v8::HandleScope scope;
7570 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7571 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7572 LocalContext env;
7573 env->Global()->Set(v8_str("obj"),
7574 templ->GetFunction()->NewInstance());
7575 ExpectTrue("obj.x === 42");
7576 ExpectTrue("!obj.propertyIsEnumerable('x')");
7577}
7578
7579
Steve Blocka7e24c12009-10-30 11:49:00 +00007580static v8::Handle<Value> ParentGetter(Local<String> name,
7581 const AccessorInfo& info) {
7582 ApiTestFuzzer::Fuzz();
7583 return v8_num(1);
7584}
7585
7586
7587static v8::Handle<Value> ChildGetter(Local<String> name,
7588 const AccessorInfo& info) {
7589 ApiTestFuzzer::Fuzz();
7590 return v8_num(42);
7591}
7592
7593
7594THREADED_TEST(Overriding) {
7595 v8::HandleScope scope;
7596 LocalContext context;
7597
7598 // Parent template.
7599 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7600 Local<ObjectTemplate> parent_instance_templ =
7601 parent_templ->InstanceTemplate();
7602 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7603
7604 // Template that inherits from the parent template.
7605 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7606 Local<ObjectTemplate> child_instance_templ =
7607 child_templ->InstanceTemplate();
7608 child_templ->Inherit(parent_templ);
7609 // Override 'f'. The child version of 'f' should get called for child
7610 // instances.
7611 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7612 // Add 'g' twice. The 'g' added last should get called for instances.
7613 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7614 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7615
7616 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7617 // so 'h' can be shadowed on the instance object.
7618 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7619 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7620 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7621
7622 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7623 // but the attribute does not have effect because it is duplicated with
7624 // NULL setter.
7625 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7626 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7627
7628
7629
7630 // Instantiate the child template.
7631 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7632
7633 // Check that the child function overrides the parent one.
7634 context->Global()->Set(v8_str("o"), instance);
7635 Local<Value> value = v8_compile("o.f")->Run();
7636 // Check that the 'g' that was added last is hit.
7637 CHECK_EQ(42, value->Int32Value());
7638 value = v8_compile("o.g")->Run();
7639 CHECK_EQ(42, value->Int32Value());
7640
7641 // Check 'h' can be shadowed.
7642 value = v8_compile("o.h = 3; o.h")->Run();
7643 CHECK_EQ(3, value->Int32Value());
7644
7645 // Check 'i' is cannot be shadowed or changed.
7646 value = v8_compile("o.i = 3; o.i")->Run();
7647 CHECK_EQ(42, value->Int32Value());
7648}
7649
7650
7651static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7652 ApiTestFuzzer::Fuzz();
7653 if (args.IsConstructCall()) {
7654 return v8::Boolean::New(true);
7655 }
7656 return v8::Boolean::New(false);
7657}
7658
7659
7660THREADED_TEST(IsConstructCall) {
7661 v8::HandleScope scope;
7662
7663 // Function template with call handler.
7664 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7665 templ->SetCallHandler(IsConstructHandler);
7666
7667 LocalContext context;
7668
7669 context->Global()->Set(v8_str("f"), templ->GetFunction());
7670 Local<Value> value = v8_compile("f()")->Run();
7671 CHECK(!value->BooleanValue());
7672 value = v8_compile("new f()")->Run();
7673 CHECK(value->BooleanValue());
7674}
7675
7676
7677THREADED_TEST(ObjectProtoToString) {
7678 v8::HandleScope scope;
7679 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7680 templ->SetClassName(v8_str("MyClass"));
7681
7682 LocalContext context;
7683
7684 Local<String> customized_tostring = v8_str("customized toString");
7685
7686 // Replace Object.prototype.toString
7687 v8_compile("Object.prototype.toString = function() {"
7688 " return 'customized toString';"
7689 "}")->Run();
7690
7691 // Normal ToString call should call replaced Object.prototype.toString
7692 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7693 Local<String> value = instance->ToString();
7694 CHECK(value->IsString() && value->Equals(customized_tostring));
7695
7696 // ObjectProtoToString should not call replace toString function.
7697 value = instance->ObjectProtoToString();
7698 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7699
7700 // Check global
7701 value = context->Global()->ObjectProtoToString();
7702 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7703
7704 // Check ordinary object
7705 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007706 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007707 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7708}
7709
7710
7711bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01007712i::Semaphore* ApiTestFuzzer::all_tests_done_=
7713 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007714int ApiTestFuzzer::active_tests_;
7715int ApiTestFuzzer::tests_being_run_;
7716int ApiTestFuzzer::current_;
7717
7718
7719// We are in a callback and want to switch to another thread (if we
7720// are currently running the thread fuzzing test).
7721void ApiTestFuzzer::Fuzz() {
7722 if (!fuzzing_) return;
7723 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7724 test->ContextSwitch();
7725}
7726
7727
7728// Let the next thread go. Since it is also waiting on the V8 lock it may
7729// not start immediately.
7730bool ApiTestFuzzer::NextThread() {
7731 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007732 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007733 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007734 if (kLogThreading)
7735 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007736 return false;
7737 }
Steve Blockd0582a62009-12-15 09:54:21 +00007738 if (kLogThreading) {
7739 printf("Switch from %s to %s\n",
7740 test_name,
7741 RegisterThreadedTest::nth(test_position)->name());
7742 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007743 current_ = test_position;
7744 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7745 return true;
7746}
7747
7748
7749void ApiTestFuzzer::Run() {
7750 // When it is our turn...
7751 gate_->Wait();
7752 {
7753 // ... get the V8 lock and start running the test.
7754 v8::Locker locker;
7755 CallTest();
7756 }
7757 // This test finished.
7758 active_ = false;
7759 active_tests_--;
7760 // If it was the last then signal that fact.
7761 if (active_tests_ == 0) {
7762 all_tests_done_->Signal();
7763 } else {
7764 // Otherwise select a new test and start that.
7765 NextThread();
7766 }
7767}
7768
7769
7770static unsigned linear_congruential_generator;
7771
7772
7773void ApiTestFuzzer::Setup(PartOfTest part) {
7774 linear_congruential_generator = i::FLAG_testing_prng_seed;
7775 fuzzing_ = true;
7776 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7777 int end = (part == FIRST_PART)
7778 ? (RegisterThreadedTest::count() >> 1)
7779 : RegisterThreadedTest::count();
7780 active_tests_ = tests_being_run_ = end - start;
7781 for (int i = 0; i < tests_being_run_; i++) {
7782 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7783 }
7784 for (int i = 0; i < active_tests_; i++) {
7785 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7786 }
7787}
7788
7789
7790static void CallTestNumber(int test_number) {
7791 (RegisterThreadedTest::nth(test_number)->callback())();
7792}
7793
7794
7795void ApiTestFuzzer::RunAllTests() {
7796 // Set off the first test.
7797 current_ = -1;
7798 NextThread();
7799 // Wait till they are all done.
7800 all_tests_done_->Wait();
7801}
7802
7803
7804int ApiTestFuzzer::GetNextTestNumber() {
7805 int next_test;
7806 do {
7807 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7808 linear_congruential_generator *= 1664525u;
7809 linear_congruential_generator += 1013904223u;
7810 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7811 return next_test;
7812}
7813
7814
7815void ApiTestFuzzer::ContextSwitch() {
7816 // If the new thread is the same as the current thread there is nothing to do.
7817 if (NextThread()) {
7818 // Now it can start.
7819 v8::Unlocker unlocker;
7820 // Wait till someone starts us again.
7821 gate_->Wait();
7822 // And we're off.
7823 }
7824}
7825
7826
7827void ApiTestFuzzer::TearDown() {
7828 fuzzing_ = false;
7829 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7830 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7831 if (fuzzer != NULL) fuzzer->Join();
7832 }
7833}
7834
7835
7836// Lets not be needlessly self-referential.
7837TEST(Threading) {
7838 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7839 ApiTestFuzzer::RunAllTests();
7840 ApiTestFuzzer::TearDown();
7841}
7842
7843TEST(Threading2) {
7844 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7845 ApiTestFuzzer::RunAllTests();
7846 ApiTestFuzzer::TearDown();
7847}
7848
7849
7850void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007851 if (kLogThreading)
7852 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007853 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007854 if (kLogThreading)
7855 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007856}
7857
7858
7859static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7860 CHECK(v8::Locker::IsLocked());
7861 ApiTestFuzzer::Fuzz();
7862 v8::Unlocker unlocker;
7863 const char* code = "throw 7;";
7864 {
7865 v8::Locker nested_locker;
7866 v8::HandleScope scope;
7867 v8::Handle<Value> exception;
7868 { v8::TryCatch try_catch;
7869 v8::Handle<Value> value = CompileRun(code);
7870 CHECK(value.IsEmpty());
7871 CHECK(try_catch.HasCaught());
7872 // Make sure to wrap the exception in a new handle because
7873 // the handle returned from the TryCatch is destroyed
7874 // when the TryCatch is destroyed.
7875 exception = Local<Value>::New(try_catch.Exception());
7876 }
7877 return v8::ThrowException(exception);
7878 }
7879}
7880
7881
7882static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7883 CHECK(v8::Locker::IsLocked());
7884 ApiTestFuzzer::Fuzz();
7885 v8::Unlocker unlocker;
7886 const char* code = "throw 7;";
7887 {
7888 v8::Locker nested_locker;
7889 v8::HandleScope scope;
7890 v8::Handle<Value> value = CompileRun(code);
7891 CHECK(value.IsEmpty());
7892 return v8_str("foo");
7893 }
7894}
7895
7896
7897// These are locking tests that don't need to be run again
7898// as part of the locking aggregation tests.
7899TEST(NestedLockers) {
7900 v8::Locker locker;
7901 CHECK(v8::Locker::IsLocked());
7902 v8::HandleScope scope;
7903 LocalContext env;
7904 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7905 Local<Function> fun = fun_templ->GetFunction();
7906 env->Global()->Set(v8_str("throw_in_js"), fun);
7907 Local<Script> script = v8_compile("(function () {"
7908 " try {"
7909 " throw_in_js();"
7910 " return 42;"
7911 " } catch (e) {"
7912 " return e * 13;"
7913 " }"
7914 "})();");
7915 CHECK_EQ(91, script->Run()->Int32Value());
7916}
7917
7918
7919// These are locking tests that don't need to be run again
7920// as part of the locking aggregation tests.
7921TEST(NestedLockersNoTryCatch) {
7922 v8::Locker locker;
7923 v8::HandleScope scope;
7924 LocalContext env;
7925 Local<v8::FunctionTemplate> fun_templ =
7926 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7927 Local<Function> fun = fun_templ->GetFunction();
7928 env->Global()->Set(v8_str("throw_in_js"), fun);
7929 Local<Script> script = v8_compile("(function () {"
7930 " try {"
7931 " throw_in_js();"
7932 " return 42;"
7933 " } catch (e) {"
7934 " return e * 13;"
7935 " }"
7936 "})();");
7937 CHECK_EQ(91, script->Run()->Int32Value());
7938}
7939
7940
7941THREADED_TEST(RecursiveLocking) {
7942 v8::Locker locker;
7943 {
7944 v8::Locker locker2;
7945 CHECK(v8::Locker::IsLocked());
7946 }
7947}
7948
7949
7950static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
7951 ApiTestFuzzer::Fuzz();
7952 v8::Unlocker unlocker;
7953 return v8::Undefined();
7954}
7955
7956
7957THREADED_TEST(LockUnlockLock) {
7958 {
7959 v8::Locker locker;
7960 v8::HandleScope scope;
7961 LocalContext env;
7962 Local<v8::FunctionTemplate> fun_templ =
7963 v8::FunctionTemplate::New(UnlockForAMoment);
7964 Local<Function> fun = fun_templ->GetFunction();
7965 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7966 Local<Script> script = v8_compile("(function () {"
7967 " unlock_for_a_moment();"
7968 " return 42;"
7969 "})();");
7970 CHECK_EQ(42, script->Run()->Int32Value());
7971 }
7972 {
7973 v8::Locker locker;
7974 v8::HandleScope scope;
7975 LocalContext env;
7976 Local<v8::FunctionTemplate> fun_templ =
7977 v8::FunctionTemplate::New(UnlockForAMoment);
7978 Local<Function> fun = fun_templ->GetFunction();
7979 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7980 Local<Script> script = v8_compile("(function () {"
7981 " unlock_for_a_moment();"
7982 " return 42;"
7983 "})();");
7984 CHECK_EQ(42, script->Run()->Int32Value());
7985 }
7986}
7987
7988
Leon Clarked91b9f72010-01-27 17:25:45 +00007989static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00007990 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01007991 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00007992 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
7993 if (object->IsJSGlobalObject()) count++;
7994 return count;
7995}
7996
7997
7998static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00007999 // We need to collect all garbage twice to be sure that everything
8000 // has been collected. This is because inline caches are cleared in
8001 // the first garbage collection but some of the maps have already
8002 // been marked at that point. Therefore some of the maps are not
8003 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01008004 i::Heap::CollectAllGarbage(false);
8005 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00008006 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00008007#ifdef DEBUG
Steve Block8defd9f2010-07-08 12:39:36 +01008008 if (count > 0) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00008009#endif
8010 return count;
8011}
8012
8013
8014TEST(DontLeakGlobalObjects) {
8015 // Regression test for issues 1139850 and 1174891.
8016
8017 v8::V8::Initialize();
8018
8019 int count = GetSurvivingGlobalObjectsCount();
8020
8021 for (int i = 0; i < 5; i++) {
8022 { v8::HandleScope scope;
8023 LocalContext context;
8024 }
8025 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8026
8027 { v8::HandleScope scope;
8028 LocalContext context;
8029 v8_compile("Date")->Run();
8030 }
8031 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8032
8033 { v8::HandleScope scope;
8034 LocalContext context;
8035 v8_compile("/aaa/")->Run();
8036 }
8037 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8038
8039 { v8::HandleScope scope;
8040 const char* extension_list[] = { "v8/gc" };
8041 v8::ExtensionConfiguration extensions(1, extension_list);
8042 LocalContext context(&extensions);
8043 v8_compile("gc();")->Run();
8044 }
8045 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8046 }
8047}
8048
8049
8050v8::Persistent<v8::Object> some_object;
8051v8::Persistent<v8::Object> bad_handle;
8052
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008053void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008054 v8::HandleScope scope;
8055 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008056 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008057}
8058
8059
8060THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8061 LocalContext context;
8062
8063 v8::Persistent<v8::Object> handle1, handle2;
8064 {
8065 v8::HandleScope scope;
8066 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8067 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8068 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8069 }
8070 // Note: order is implementation dependent alas: currently
8071 // global handle nodes are processed by PostGarbageCollectionProcessing
8072 // in reverse allocation order, so if second allocated handle is deleted,
8073 // weak callback of the first handle would be able to 'reallocate' it.
8074 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8075 handle2.Dispose();
8076 i::Heap::CollectAllGarbage(false);
8077}
8078
8079
8080v8::Persistent<v8::Object> to_be_disposed;
8081
8082void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8083 to_be_disposed.Dispose();
8084 i::Heap::CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008085 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008086}
8087
8088
8089THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8090 LocalContext context;
8091
8092 v8::Persistent<v8::Object> handle1, handle2;
8093 {
8094 v8::HandleScope scope;
8095 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8096 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8097 }
8098 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8099 to_be_disposed = handle2;
8100 i::Heap::CollectAllGarbage(false);
8101}
8102
Steve Blockd0582a62009-12-15 09:54:21 +00008103void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8104 handle.Dispose();
8105}
8106
8107void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8108 v8::HandleScope scope;
8109 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008110 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00008111}
8112
8113
8114THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8115 LocalContext context;
8116
8117 v8::Persistent<v8::Object> handle1, handle2, handle3;
8118 {
8119 v8::HandleScope scope;
8120 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8121 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8122 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8123 }
8124 handle2.MakeWeak(NULL, DisposingCallback);
8125 handle3.MakeWeak(NULL, HandleCreatingCallback);
8126 i::Heap::CollectAllGarbage(false);
8127}
8128
Steve Blocka7e24c12009-10-30 11:49:00 +00008129
8130THREADED_TEST(CheckForCrossContextObjectLiterals) {
8131 v8::V8::Initialize();
8132
8133 const int nof = 2;
8134 const char* sources[nof] = {
8135 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8136 "Object()"
8137 };
8138
8139 for (int i = 0; i < nof; i++) {
8140 const char* source = sources[i];
8141 { v8::HandleScope scope;
8142 LocalContext context;
8143 CompileRun(source);
8144 }
8145 { v8::HandleScope scope;
8146 LocalContext context;
8147 CompileRun(source);
8148 }
8149 }
8150}
8151
8152
8153static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8154 v8::HandleScope inner;
8155 env->Enter();
8156 v8::Handle<Value> three = v8_num(3);
8157 v8::Handle<Value> value = inner.Close(three);
8158 env->Exit();
8159 return value;
8160}
8161
8162
8163THREADED_TEST(NestedHandleScopeAndContexts) {
8164 v8::HandleScope outer;
8165 v8::Persistent<Context> env = Context::New();
8166 env->Enter();
8167 v8::Handle<Value> value = NestedScope(env);
8168 v8::Handle<String> str = value->ToString();
8169 env->Exit();
8170 env.Dispose();
8171}
8172
8173
8174THREADED_TEST(ExternalAllocatedMemory) {
8175 v8::HandleScope outer;
8176 v8::Persistent<Context> env = Context::New();
8177 const int kSize = 1024*1024;
8178 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8179 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8180}
8181
8182
8183THREADED_TEST(DisposeEnteredContext) {
8184 v8::HandleScope scope;
8185 LocalContext outer;
8186 { v8::Persistent<v8::Context> inner = v8::Context::New();
8187 inner->Enter();
8188 inner.Dispose();
8189 inner.Clear();
8190 inner->Exit();
8191 }
8192}
8193
8194
8195// Regression test for issue 54, object templates with internal fields
8196// but no accessors or interceptors did not get their internal field
8197// count set on instances.
8198THREADED_TEST(Regress54) {
8199 v8::HandleScope outer;
8200 LocalContext context;
8201 static v8::Persistent<v8::ObjectTemplate> templ;
8202 if (templ.IsEmpty()) {
8203 v8::HandleScope inner;
8204 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8205 local->SetInternalFieldCount(1);
8206 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8207 }
8208 v8::Handle<v8::Object> result = templ->NewInstance();
8209 CHECK_EQ(1, result->InternalFieldCount());
8210}
8211
8212
8213// If part of the threaded tests, this test makes ThreadingTest fail
8214// on mac.
8215TEST(CatchStackOverflow) {
8216 v8::HandleScope scope;
8217 LocalContext context;
8218 v8::TryCatch try_catch;
8219 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8220 "function f() {"
8221 " return f();"
8222 "}"
8223 ""
8224 "f();"));
8225 v8::Handle<v8::Value> result = script->Run();
8226 CHECK(result.IsEmpty());
8227}
8228
8229
8230static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8231 const char* resource_name,
8232 int line_offset) {
8233 v8::HandleScope scope;
8234 v8::TryCatch try_catch;
8235 v8::Handle<v8::Value> result = script->Run();
8236 CHECK(result.IsEmpty());
8237 CHECK(try_catch.HasCaught());
8238 v8::Handle<v8::Message> message = try_catch.Message();
8239 CHECK(!message.IsEmpty());
8240 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8241 CHECK_EQ(91, message->GetStartPosition());
8242 CHECK_EQ(92, message->GetEndPosition());
8243 CHECK_EQ(2, message->GetStartColumn());
8244 CHECK_EQ(3, message->GetEndColumn());
8245 v8::String::AsciiValue line(message->GetSourceLine());
8246 CHECK_EQ(" throw 'nirk';", *line);
8247 v8::String::AsciiValue name(message->GetScriptResourceName());
8248 CHECK_EQ(resource_name, *name);
8249}
8250
8251
8252THREADED_TEST(TryCatchSourceInfo) {
8253 v8::HandleScope scope;
8254 LocalContext context;
8255 v8::Handle<v8::String> source = v8::String::New(
8256 "function Foo() {\n"
8257 " return Bar();\n"
8258 "}\n"
8259 "\n"
8260 "function Bar() {\n"
8261 " return Baz();\n"
8262 "}\n"
8263 "\n"
8264 "function Baz() {\n"
8265 " throw 'nirk';\n"
8266 "}\n"
8267 "\n"
8268 "Foo();\n");
8269
8270 const char* resource_name;
8271 v8::Handle<v8::Script> script;
8272 resource_name = "test.js";
8273 script = v8::Script::Compile(source, v8::String::New(resource_name));
8274 CheckTryCatchSourceInfo(script, resource_name, 0);
8275
8276 resource_name = "test1.js";
8277 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8278 script = v8::Script::Compile(source, &origin1);
8279 CheckTryCatchSourceInfo(script, resource_name, 0);
8280
8281 resource_name = "test2.js";
8282 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8283 script = v8::Script::Compile(source, &origin2);
8284 CheckTryCatchSourceInfo(script, resource_name, 7);
8285}
8286
8287
8288THREADED_TEST(CompilationCache) {
8289 v8::HandleScope scope;
8290 LocalContext context;
8291 v8::Handle<v8::String> source0 = v8::String::New("1234");
8292 v8::Handle<v8::String> source1 = v8::String::New("1234");
8293 v8::Handle<v8::Script> script0 =
8294 v8::Script::Compile(source0, v8::String::New("test.js"));
8295 v8::Handle<v8::Script> script1 =
8296 v8::Script::Compile(source1, v8::String::New("test.js"));
8297 v8::Handle<v8::Script> script2 =
8298 v8::Script::Compile(source0); // different origin
8299 CHECK_EQ(1234, script0->Run()->Int32Value());
8300 CHECK_EQ(1234, script1->Run()->Int32Value());
8301 CHECK_EQ(1234, script2->Run()->Int32Value());
8302}
8303
8304
8305static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8306 ApiTestFuzzer::Fuzz();
8307 return v8_num(42);
8308}
8309
8310
8311THREADED_TEST(CallbackFunctionName) {
8312 v8::HandleScope scope;
8313 LocalContext context;
8314 Local<ObjectTemplate> t = ObjectTemplate::New();
8315 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8316 context->Global()->Set(v8_str("obj"), t->NewInstance());
8317 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8318 CHECK(value->IsString());
8319 v8::String::AsciiValue name(value);
8320 CHECK_EQ("asdf", *name);
8321}
8322
8323
8324THREADED_TEST(DateAccess) {
8325 v8::HandleScope scope;
8326 LocalContext context;
8327 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8328 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008329 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008330}
8331
8332
8333void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008334 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008335 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8336 CHECK_EQ(elmc, props->Length());
8337 for (int i = 0; i < elmc; i++) {
8338 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8339 CHECK_EQ(elmv[i], *elm);
8340 }
8341}
8342
8343
8344THREADED_TEST(PropertyEnumeration) {
8345 v8::HandleScope scope;
8346 LocalContext context;
8347 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8348 "var result = [];"
8349 "result[0] = {};"
8350 "result[1] = {a: 1, b: 2};"
8351 "result[2] = [1, 2, 3];"
8352 "var proto = {x: 1, y: 2, z: 3};"
8353 "var x = { __proto__: proto, w: 0, z: 1 };"
8354 "result[3] = x;"
8355 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008356 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008357 CHECK_EQ(4, elms->Length());
8358 int elmc0 = 0;
8359 const char** elmv0 = NULL;
8360 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8361 int elmc1 = 2;
8362 const char* elmv1[] = {"a", "b"};
8363 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8364 int elmc2 = 3;
8365 const char* elmv2[] = {"0", "1", "2"};
8366 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8367 int elmc3 = 4;
8368 const char* elmv3[] = {"w", "z", "x", "y"};
8369 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8370}
8371
8372
Steve Blocka7e24c12009-10-30 11:49:00 +00008373static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8374 Local<Value> name,
8375 v8::AccessType type,
8376 Local<Value> data) {
8377 return type != v8::ACCESS_SET;
8378}
8379
8380
8381static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8382 uint32_t key,
8383 v8::AccessType type,
8384 Local<Value> data) {
8385 return type != v8::ACCESS_SET;
8386}
8387
8388
8389THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8390 v8::HandleScope scope;
8391 LocalContext context;
8392 Local<ObjectTemplate> templ = ObjectTemplate::New();
8393 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8394 IndexedSetAccessBlocker);
8395 templ->Set(v8_str("x"), v8::True());
8396 Local<v8::Object> instance = templ->NewInstance();
8397 context->Global()->Set(v8_str("obj"), instance);
8398 Local<Value> value = CompileRun("obj.x");
8399 CHECK(value->BooleanValue());
8400}
8401
8402
8403static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8404 Local<Value> name,
8405 v8::AccessType type,
8406 Local<Value> data) {
8407 return false;
8408}
8409
8410
8411static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8412 uint32_t key,
8413 v8::AccessType type,
8414 Local<Value> data) {
8415 return false;
8416}
8417
8418
8419
8420THREADED_TEST(AccessChecksReenabledCorrectly) {
8421 v8::HandleScope scope;
8422 LocalContext context;
8423 Local<ObjectTemplate> templ = ObjectTemplate::New();
8424 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8425 IndexedGetAccessBlocker);
8426 templ->Set(v8_str("a"), v8_str("a"));
8427 // Add more than 8 (see kMaxFastProperties) properties
8428 // so that the constructor will force copying map.
8429 // Cannot sprintf, gcc complains unsafety.
8430 char buf[4];
8431 for (char i = '0'; i <= '9' ; i++) {
8432 buf[0] = i;
8433 for (char j = '0'; j <= '9'; j++) {
8434 buf[1] = j;
8435 for (char k = '0'; k <= '9'; k++) {
8436 buf[2] = k;
8437 buf[3] = 0;
8438 templ->Set(v8_str(buf), v8::Number::New(k));
8439 }
8440 }
8441 }
8442
8443 Local<v8::Object> instance_1 = templ->NewInstance();
8444 context->Global()->Set(v8_str("obj_1"), instance_1);
8445
8446 Local<Value> value_1 = CompileRun("obj_1.a");
8447 CHECK(value_1->IsUndefined());
8448
8449 Local<v8::Object> instance_2 = templ->NewInstance();
8450 context->Global()->Set(v8_str("obj_2"), instance_2);
8451
8452 Local<Value> value_2 = CompileRun("obj_2.a");
8453 CHECK(value_2->IsUndefined());
8454}
8455
8456
8457// This tests that access check information remains on the global
8458// object template when creating contexts.
8459THREADED_TEST(AccessControlRepeatedContextCreation) {
8460 v8::HandleScope handle_scope;
8461 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8462 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8463 IndexedSetAccessBlocker);
8464 i::Handle<i::ObjectTemplateInfo> internal_template =
8465 v8::Utils::OpenHandle(*global_template);
8466 CHECK(!internal_template->constructor()->IsUndefined());
8467 i::Handle<i::FunctionTemplateInfo> constructor(
8468 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8469 CHECK(!constructor->access_check_info()->IsUndefined());
8470 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8471 CHECK(!constructor->access_check_info()->IsUndefined());
8472}
8473
8474
8475THREADED_TEST(TurnOnAccessCheck) {
8476 v8::HandleScope handle_scope;
8477
8478 // Create an environment with access check to the global object disabled by
8479 // default.
8480 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8481 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8482 IndexedGetAccessBlocker,
8483 v8::Handle<v8::Value>(),
8484 false);
8485 v8::Persistent<Context> context = Context::New(NULL, global_template);
8486 Context::Scope context_scope(context);
8487
8488 // Set up a property and a number of functions.
8489 context->Global()->Set(v8_str("a"), v8_num(1));
8490 CompileRun("function f1() {return a;}"
8491 "function f2() {return a;}"
8492 "function g1() {return h();}"
8493 "function g2() {return h();}"
8494 "function h() {return 1;}");
8495 Local<Function> f1 =
8496 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8497 Local<Function> f2 =
8498 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8499 Local<Function> g1 =
8500 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8501 Local<Function> g2 =
8502 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8503 Local<Function> h =
8504 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8505
8506 // Get the global object.
8507 v8::Handle<v8::Object> global = context->Global();
8508
8509 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8510 // uses the runtime system to retreive property a whereas f2 uses global load
8511 // inline cache.
8512 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8513 for (int i = 0; i < 4; i++) {
8514 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8515 }
8516
8517 // Same for g1 and g2.
8518 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8519 for (int i = 0; i < 4; i++) {
8520 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8521 }
8522
8523 // Detach the global and turn on access check.
8524 context->DetachGlobal();
8525 context->Global()->TurnOnAccessCheck();
8526
8527 // Failing access check to property get results in undefined.
8528 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8529 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8530
8531 // Failing access check to function call results in exception.
8532 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8533 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8534
8535 // No failing access check when just returning a constant.
8536 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8537}
8538
8539
8540// This test verifies that pre-compilation (aka preparsing) can be called
8541// without initializing the whole VM. Thus we cannot run this test in a
8542// multi-threaded setup.
8543TEST(PreCompile) {
8544 // TODO(155): This test would break without the initialization of V8. This is
8545 // a workaround for now to make this test not fail.
8546 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008547 const char* script = "function foo(a) { return a+1; }";
8548 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008549 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008550 CHECK_NE(sd->Length(), 0);
8551 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008552 CHECK(!sd->HasError());
8553 delete sd;
8554}
8555
8556
8557TEST(PreCompileWithError) {
8558 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008559 const char* script = "function foo(a) { return 1 * * 2; }";
8560 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008561 v8::ScriptData::PreCompile(script, i::StrLength(script));
8562 CHECK(sd->HasError());
8563 delete sd;
8564}
8565
8566
8567TEST(Regress31661) {
8568 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008569 const char* script = " The Definintive Guide";
8570 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008571 v8::ScriptData::PreCompile(script, i::StrLength(script));
8572 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008573 delete sd;
8574}
8575
8576
Leon Clarkef7060e22010-06-03 12:02:55 +01008577// Tests that ScriptData can be serialized and deserialized.
8578TEST(PreCompileSerialization) {
8579 v8::V8::Initialize();
8580 const char* script = "function foo(a) { return a+1; }";
8581 v8::ScriptData* sd =
8582 v8::ScriptData::PreCompile(script, i::StrLength(script));
8583
8584 // Serialize.
8585 int serialized_data_length = sd->Length();
8586 char* serialized_data = i::NewArray<char>(serialized_data_length);
8587 memcpy(serialized_data, sd->Data(), serialized_data_length);
8588
8589 // Deserialize.
8590 v8::ScriptData* deserialized_sd =
8591 v8::ScriptData::New(serialized_data, serialized_data_length);
8592
8593 // Verify that the original is the same as the deserialized.
8594 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8595 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8596 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8597
8598 delete sd;
8599 delete deserialized_sd;
8600}
8601
8602
8603// Attempts to deserialize bad data.
8604TEST(PreCompileDeserializationError) {
8605 v8::V8::Initialize();
8606 const char* data = "DONT CARE";
8607 int invalid_size = 3;
8608 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8609
8610 CHECK_EQ(0, sd->Length());
8611
8612 delete sd;
8613}
8614
8615
Leon Clarkeac952652010-07-15 11:15:24 +01008616// Attempts to deserialize bad data.
8617TEST(PreCompileInvalidPreparseDataError) {
8618 v8::V8::Initialize();
8619 v8::HandleScope scope;
8620 LocalContext context;
8621
8622 const char* script = "function foo(){ return 5;}\n"
8623 "function bar(){ return 6 + 7;} foo();";
8624 v8::ScriptData* sd =
8625 v8::ScriptData::PreCompile(script, i::StrLength(script));
8626 CHECK(!sd->HasError());
8627 // ScriptDataImpl private implementation details
Iain Merrick9ac36c92010-09-13 15:29:50 +01008628 const int kHeaderSize = i::ScriptDataImpl::kHeaderSize;
8629 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +01008630 const int kFunctionEntryStartOffset = 0;
8631 const int kFunctionEntryEndOffset = 1;
8632 unsigned* sd_data =
8633 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01008634
8635 // Overwrite function bar's end position with 0.
8636 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
8637 v8::TryCatch try_catch;
8638
8639 Local<String> source = String::New(script);
8640 Local<Script> compiled_script = Script::New(source, NULL, sd);
8641 CHECK(try_catch.HasCaught());
8642 String::AsciiValue exception_value(try_catch.Message()->Get());
8643 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8644 *exception_value);
8645
8646 try_catch.Reset();
8647 // Overwrite function bar's start position with 200. The function entry
8648 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01008649 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
8650 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01008651 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
8652 200;
8653 compiled_script = Script::New(source, NULL, sd);
8654 CHECK(try_catch.HasCaught());
8655 String::AsciiValue second_exception_value(try_catch.Message()->Get());
8656 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8657 *second_exception_value);
8658
8659 delete sd;
8660}
8661
8662
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008663// Verifies that the Handle<String> and const char* versions of the API produce
8664// the same results (at least for one trivial case).
8665TEST(PreCompileAPIVariationsAreSame) {
8666 v8::V8::Initialize();
8667 v8::HandleScope scope;
8668
8669 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008670
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008671 v8::ScriptData* sd_from_cstring =
8672 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8673
8674 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008675 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008676 v8::String::NewExternal(resource));
8677
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008678 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
8679 v8::String::New(cstring));
8680
8681 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008682 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008683 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008684 sd_from_cstring->Length()));
8685
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008686 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
8687 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8688 sd_from_string->Data(),
8689 sd_from_cstring->Length()));
8690
8691
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008692 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008693 delete sd_from_external_string;
8694 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008695}
8696
8697
Steve Blocka7e24c12009-10-30 11:49:00 +00008698// This tests that we do not allow dictionary load/call inline caches
8699// to use functions that have not yet been compiled. The potential
8700// problem of loading a function that has not yet been compiled can
8701// arise because we share code between contexts via the compilation
8702// cache.
8703THREADED_TEST(DictionaryICLoadedFunction) {
8704 v8::HandleScope scope;
8705 // Test LoadIC.
8706 for (int i = 0; i < 2; i++) {
8707 LocalContext context;
8708 context->Global()->Set(v8_str("tmp"), v8::True());
8709 context->Global()->Delete(v8_str("tmp"));
8710 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8711 }
8712 // Test CallIC.
8713 for (int i = 0; i < 2; i++) {
8714 LocalContext context;
8715 context->Global()->Set(v8_str("tmp"), v8::True());
8716 context->Global()->Delete(v8_str("tmp"));
8717 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8718 }
8719}
8720
8721
8722// Test that cross-context new calls use the context of the callee to
8723// create the new JavaScript object.
8724THREADED_TEST(CrossContextNew) {
8725 v8::HandleScope scope;
8726 v8::Persistent<Context> context0 = Context::New();
8727 v8::Persistent<Context> context1 = Context::New();
8728
8729 // Allow cross-domain access.
8730 Local<String> token = v8_str("<security token>");
8731 context0->SetSecurityToken(token);
8732 context1->SetSecurityToken(token);
8733
8734 // Set an 'x' property on the Object prototype and define a
8735 // constructor function in context0.
8736 context0->Enter();
8737 CompileRun("Object.prototype.x = 42; function C() {};");
8738 context0->Exit();
8739
8740 // Call the constructor function from context0 and check that the
8741 // result has the 'x' property.
8742 context1->Enter();
8743 context1->Global()->Set(v8_str("other"), context0->Global());
8744 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8745 CHECK(value->IsInt32());
8746 CHECK_EQ(42, value->Int32Value());
8747 context1->Exit();
8748
8749 // Dispose the contexts to allow them to be garbage collected.
8750 context0.Dispose();
8751 context1.Dispose();
8752}
8753
8754
8755class RegExpInterruptTest {
8756 public:
8757 RegExpInterruptTest() : block_(NULL) {}
8758 ~RegExpInterruptTest() { delete block_; }
8759 void RunTest() {
8760 block_ = i::OS::CreateSemaphore(0);
8761 gc_count_ = 0;
8762 gc_during_regexp_ = 0;
8763 regexp_success_ = false;
8764 gc_success_ = false;
8765 GCThread gc_thread(this);
8766 gc_thread.Start();
8767 v8::Locker::StartPreemption(1);
8768
8769 LongRunningRegExp();
8770 {
8771 v8::Unlocker unlock;
8772 gc_thread.Join();
8773 }
8774 v8::Locker::StopPreemption();
8775 CHECK(regexp_success_);
8776 CHECK(gc_success_);
8777 }
8778 private:
8779 // Number of garbage collections required.
8780 static const int kRequiredGCs = 5;
8781
8782 class GCThread : public i::Thread {
8783 public:
8784 explicit GCThread(RegExpInterruptTest* test)
8785 : test_(test) {}
8786 virtual void Run() {
8787 test_->CollectGarbage();
8788 }
8789 private:
8790 RegExpInterruptTest* test_;
8791 };
8792
8793 void CollectGarbage() {
8794 block_->Wait();
8795 while (gc_during_regexp_ < kRequiredGCs) {
8796 {
8797 v8::Locker lock;
8798 // TODO(lrn): Perhaps create some garbage before collecting.
8799 i::Heap::CollectAllGarbage(false);
8800 gc_count_++;
8801 }
8802 i::OS::Sleep(1);
8803 }
8804 gc_success_ = true;
8805 }
8806
8807 void LongRunningRegExp() {
8808 block_->Signal(); // Enable garbage collection thread on next preemption.
8809 int rounds = 0;
8810 while (gc_during_regexp_ < kRequiredGCs) {
8811 int gc_before = gc_count_;
8812 {
8813 // Match 15-30 "a"'s against 14 and a "b".
8814 const char* c_source =
8815 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8816 ".exec('aaaaaaaaaaaaaaab') === null";
8817 Local<String> source = String::New(c_source);
8818 Local<Script> script = Script::Compile(source);
8819 Local<Value> result = script->Run();
8820 if (!result->BooleanValue()) {
8821 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8822 return;
8823 }
8824 }
8825 {
8826 // Match 15-30 "a"'s against 15 and a "b".
8827 const char* c_source =
8828 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8829 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8830 Local<String> source = String::New(c_source);
8831 Local<Script> script = Script::Compile(source);
8832 Local<Value> result = script->Run();
8833 if (!result->BooleanValue()) {
8834 gc_during_regexp_ = kRequiredGCs;
8835 return;
8836 }
8837 }
8838 int gc_after = gc_count_;
8839 gc_during_regexp_ += gc_after - gc_before;
8840 rounds++;
8841 i::OS::Sleep(1);
8842 }
8843 regexp_success_ = true;
8844 }
8845
8846 i::Semaphore* block_;
8847 int gc_count_;
8848 int gc_during_regexp_;
8849 bool regexp_success_;
8850 bool gc_success_;
8851};
8852
8853
8854// Test that a regular expression execution can be interrupted and
8855// survive a garbage collection.
8856TEST(RegExpInterruption) {
8857 v8::Locker lock;
8858 v8::V8::Initialize();
8859 v8::HandleScope scope;
8860 Local<Context> local_env;
8861 {
8862 LocalContext env;
8863 local_env = env.local();
8864 }
8865
8866 // Local context should still be live.
8867 CHECK(!local_env.IsEmpty());
8868 local_env->Enter();
8869
8870 // Should complete without problems.
8871 RegExpInterruptTest().RunTest();
8872
8873 local_env->Exit();
8874}
8875
8876
8877class ApplyInterruptTest {
8878 public:
8879 ApplyInterruptTest() : block_(NULL) {}
8880 ~ApplyInterruptTest() { delete block_; }
8881 void RunTest() {
8882 block_ = i::OS::CreateSemaphore(0);
8883 gc_count_ = 0;
8884 gc_during_apply_ = 0;
8885 apply_success_ = false;
8886 gc_success_ = false;
8887 GCThread gc_thread(this);
8888 gc_thread.Start();
8889 v8::Locker::StartPreemption(1);
8890
8891 LongRunningApply();
8892 {
8893 v8::Unlocker unlock;
8894 gc_thread.Join();
8895 }
8896 v8::Locker::StopPreemption();
8897 CHECK(apply_success_);
8898 CHECK(gc_success_);
8899 }
8900 private:
8901 // Number of garbage collections required.
8902 static const int kRequiredGCs = 2;
8903
8904 class GCThread : public i::Thread {
8905 public:
8906 explicit GCThread(ApplyInterruptTest* test)
8907 : test_(test) {}
8908 virtual void Run() {
8909 test_->CollectGarbage();
8910 }
8911 private:
8912 ApplyInterruptTest* test_;
8913 };
8914
8915 void CollectGarbage() {
8916 block_->Wait();
8917 while (gc_during_apply_ < kRequiredGCs) {
8918 {
8919 v8::Locker lock;
8920 i::Heap::CollectAllGarbage(false);
8921 gc_count_++;
8922 }
8923 i::OS::Sleep(1);
8924 }
8925 gc_success_ = true;
8926 }
8927
8928 void LongRunningApply() {
8929 block_->Signal();
8930 int rounds = 0;
8931 while (gc_during_apply_ < kRequiredGCs) {
8932 int gc_before = gc_count_;
8933 {
8934 const char* c_source =
8935 "function do_very_little(bar) {"
8936 " this.foo = bar;"
8937 "}"
8938 "for (var i = 0; i < 100000; i++) {"
8939 " do_very_little.apply(this, ['bar']);"
8940 "}";
8941 Local<String> source = String::New(c_source);
8942 Local<Script> script = Script::Compile(source);
8943 Local<Value> result = script->Run();
8944 // Check that no exception was thrown.
8945 CHECK(!result.IsEmpty());
8946 }
8947 int gc_after = gc_count_;
8948 gc_during_apply_ += gc_after - gc_before;
8949 rounds++;
8950 }
8951 apply_success_ = true;
8952 }
8953
8954 i::Semaphore* block_;
8955 int gc_count_;
8956 int gc_during_apply_;
8957 bool apply_success_;
8958 bool gc_success_;
8959};
8960
8961
8962// Test that nothing bad happens if we get a preemption just when we were
8963// about to do an apply().
8964TEST(ApplyInterruption) {
8965 v8::Locker lock;
8966 v8::V8::Initialize();
8967 v8::HandleScope scope;
8968 Local<Context> local_env;
8969 {
8970 LocalContext env;
8971 local_env = env.local();
8972 }
8973
8974 // Local context should still be live.
8975 CHECK(!local_env.IsEmpty());
8976 local_env->Enter();
8977
8978 // Should complete without problems.
8979 ApplyInterruptTest().RunTest();
8980
8981 local_env->Exit();
8982}
8983
8984
8985// Verify that we can clone an object
8986TEST(ObjectClone) {
8987 v8::HandleScope scope;
8988 LocalContext env;
8989
8990 const char* sample =
8991 "var rv = {};" \
8992 "rv.alpha = 'hello';" \
8993 "rv.beta = 123;" \
8994 "rv;";
8995
8996 // Create an object, verify basics.
8997 Local<Value> val = CompileRun(sample);
8998 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01008999 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009000 obj->Set(v8_str("gamma"), v8_str("cloneme"));
9001
9002 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
9003 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9004 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
9005
9006 // Clone it.
9007 Local<v8::Object> clone = obj->Clone();
9008 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
9009 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
9010 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
9011
9012 // Set a property on the clone, verify each object.
9013 clone->Set(v8_str("beta"), v8::Integer::New(456));
9014 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9015 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
9016}
9017
9018
9019class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
9020 public:
9021 explicit AsciiVectorResource(i::Vector<const char> vector)
9022 : data_(vector) {}
9023 virtual ~AsciiVectorResource() {}
9024 virtual size_t length() const { return data_.length(); }
9025 virtual const char* data() const { return data_.start(); }
9026 private:
9027 i::Vector<const char> data_;
9028};
9029
9030
9031class UC16VectorResource : public v8::String::ExternalStringResource {
9032 public:
9033 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
9034 : data_(vector) {}
9035 virtual ~UC16VectorResource() {}
9036 virtual size_t length() const { return data_.length(); }
9037 virtual const i::uc16* data() const { return data_.start(); }
9038 private:
9039 i::Vector<const i::uc16> data_;
9040};
9041
9042
9043static void MorphAString(i::String* string,
9044 AsciiVectorResource* ascii_resource,
9045 UC16VectorResource* uc16_resource) {
9046 CHECK(i::StringShape(string).IsExternal());
9047 if (string->IsAsciiRepresentation()) {
9048 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009049 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009050 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009051 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009052 i::ExternalTwoByteString* morphed =
9053 i::ExternalTwoByteString::cast(string);
9054 morphed->set_resource(uc16_resource);
9055 } else {
9056 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009057 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009058 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009059 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009060 i::ExternalAsciiString* morphed =
9061 i::ExternalAsciiString::cast(string);
9062 morphed->set_resource(ascii_resource);
9063 }
9064}
9065
9066
9067// Test that we can still flatten a string if the components it is built up
9068// from have been turned into 16 bit strings in the mean time.
9069THREADED_TEST(MorphCompositeStringTest) {
9070 const char* c_string = "Now is the time for all good men"
9071 " to come to the aid of the party";
9072 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9073 {
9074 v8::HandleScope scope;
9075 LocalContext env;
9076 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009077 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009078 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009079 i::Vector<const uint16_t>(two_byte_string,
9080 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009081
9082 Local<String> lhs(v8::Utils::ToLocal(
9083 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9084 Local<String> rhs(v8::Utils::ToLocal(
9085 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9086
9087 env->Global()->Set(v8_str("lhs"), lhs);
9088 env->Global()->Set(v8_str("rhs"), rhs);
9089
9090 CompileRun(
9091 "var cons = lhs + rhs;"
9092 "var slice = lhs.substring(1, lhs.length - 1);"
9093 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
9094
9095 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
9096 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
9097
9098 // Now do some stuff to make sure the strings are flattened, etc.
9099 CompileRun(
9100 "/[^a-z]/.test(cons);"
9101 "/[^a-z]/.test(slice);"
9102 "/[^a-z]/.test(slice_on_cons);");
9103 const char* expected_cons =
9104 "Now is the time for all good men to come to the aid of the party"
9105 "Now is the time for all good men to come to the aid of the party";
9106 const char* expected_slice =
9107 "ow is the time for all good men to come to the aid of the part";
9108 const char* expected_slice_on_cons =
9109 "ow is the time for all good men to come to the aid of the party"
9110 "Now is the time for all good men to come to the aid of the part";
9111 CHECK_EQ(String::New(expected_cons),
9112 env->Global()->Get(v8_str("cons")));
9113 CHECK_EQ(String::New(expected_slice),
9114 env->Global()->Get(v8_str("slice")));
9115 CHECK_EQ(String::New(expected_slice_on_cons),
9116 env->Global()->Get(v8_str("slice_on_cons")));
9117 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009118 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009119}
9120
9121
9122TEST(CompileExternalTwoByteSource) {
9123 v8::HandleScope scope;
9124 LocalContext context;
9125
9126 // This is a very short list of sources, which currently is to check for a
9127 // regression caused by r2703.
9128 const char* ascii_sources[] = {
9129 "0.5",
9130 "-0.5", // This mainly testes PushBack in the Scanner.
9131 "--0.5", // This mainly testes PushBack in the Scanner.
9132 NULL
9133 };
9134
9135 // Compile the sources as external two byte strings.
9136 for (int i = 0; ascii_sources[i] != NULL; i++) {
9137 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9138 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009139 i::Vector<const uint16_t>(two_byte_string,
9140 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009141 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9142 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009143 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009144 }
9145}
9146
9147
9148class RegExpStringModificationTest {
9149 public:
9150 RegExpStringModificationTest()
9151 : block_(i::OS::CreateSemaphore(0)),
9152 morphs_(0),
9153 morphs_during_regexp_(0),
9154 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9155 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9156 ~RegExpStringModificationTest() { delete block_; }
9157 void RunTest() {
9158 regexp_success_ = false;
9159 morph_success_ = false;
9160
9161 // Initialize the contents of two_byte_content_ to be a uc16 representation
9162 // of "aaaaaaaaaaaaaab".
9163 for (int i = 0; i < 14; i++) {
9164 two_byte_content_[i] = 'a';
9165 }
9166 two_byte_content_[14] = 'b';
9167
9168 // Create the input string for the regexp - the one we are going to change
9169 // properties of.
9170 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9171
9172 // Inject the input as a global variable.
9173 i::Handle<i::String> input_name =
9174 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
9175 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
9176
9177
9178 MorphThread morph_thread(this);
9179 morph_thread.Start();
9180 v8::Locker::StartPreemption(1);
9181 LongRunningRegExp();
9182 {
9183 v8::Unlocker unlock;
9184 morph_thread.Join();
9185 }
9186 v8::Locker::StopPreemption();
9187 CHECK(regexp_success_);
9188 CHECK(morph_success_);
9189 }
9190 private:
9191
9192 // Number of string modifications required.
9193 static const int kRequiredModifications = 5;
9194 static const int kMaxModifications = 100;
9195
9196 class MorphThread : public i::Thread {
9197 public:
9198 explicit MorphThread(RegExpStringModificationTest* test)
9199 : test_(test) {}
9200 virtual void Run() {
9201 test_->MorphString();
9202 }
9203 private:
9204 RegExpStringModificationTest* test_;
9205 };
9206
9207 void MorphString() {
9208 block_->Wait();
9209 while (morphs_during_regexp_ < kRequiredModifications &&
9210 morphs_ < kMaxModifications) {
9211 {
9212 v8::Locker lock;
9213 // Swap string between ascii and two-byte representation.
9214 i::String* string = *input_;
9215 MorphAString(string, &ascii_resource_, &uc16_resource_);
9216 morphs_++;
9217 }
9218 i::OS::Sleep(1);
9219 }
9220 morph_success_ = true;
9221 }
9222
9223 void LongRunningRegExp() {
9224 block_->Signal(); // Enable morphing thread on next preemption.
9225 while (morphs_during_regexp_ < kRequiredModifications &&
9226 morphs_ < kMaxModifications) {
9227 int morphs_before = morphs_;
9228 {
Steve Block791712a2010-08-27 10:21:07 +01009229 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +00009230 // Match 15-30 "a"'s against 14 and a "b".
9231 const char* c_source =
9232 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9233 ".exec(input) === null";
9234 Local<String> source = String::New(c_source);
9235 Local<Script> script = Script::Compile(source);
9236 Local<Value> result = script->Run();
9237 CHECK(result->IsTrue());
9238 }
9239 int morphs_after = morphs_;
9240 morphs_during_regexp_ += morphs_after - morphs_before;
9241 }
9242 regexp_success_ = true;
9243 }
9244
9245 i::uc16 two_byte_content_[15];
9246 i::Semaphore* block_;
9247 int morphs_;
9248 int morphs_during_regexp_;
9249 bool regexp_success_;
9250 bool morph_success_;
9251 i::Handle<i::String> input_;
9252 AsciiVectorResource ascii_resource_;
9253 UC16VectorResource uc16_resource_;
9254};
9255
9256
9257// Test that a regular expression execution can be interrupted and
9258// the string changed without failing.
9259TEST(RegExpStringModification) {
9260 v8::Locker lock;
9261 v8::V8::Initialize();
9262 v8::HandleScope scope;
9263 Local<Context> local_env;
9264 {
9265 LocalContext env;
9266 local_env = env.local();
9267 }
9268
9269 // Local context should still be live.
9270 CHECK(!local_env.IsEmpty());
9271 local_env->Enter();
9272
9273 // Should complete without problems.
9274 RegExpStringModificationTest().RunTest();
9275
9276 local_env->Exit();
9277}
9278
9279
9280// Test that we can set a property on the global object even if there
9281// is a read-only property in the prototype chain.
9282TEST(ReadOnlyPropertyInGlobalProto) {
9283 v8::HandleScope scope;
9284 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9285 LocalContext context(0, templ);
9286 v8::Handle<v8::Object> global = context->Global();
9287 v8::Handle<v8::Object> global_proto =
9288 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9289 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9290 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9291 // Check without 'eval' or 'with'.
9292 v8::Handle<v8::Value> res =
9293 CompileRun("function f() { x = 42; return x; }; f()");
9294 // Check with 'eval'.
9295 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9296 CHECK_EQ(v8::Integer::New(42), res);
9297 // Check with 'with'.
9298 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9299 CHECK_EQ(v8::Integer::New(42), res);
9300}
9301
9302static int force_set_set_count = 0;
9303static int force_set_get_count = 0;
9304bool pass_on_get = false;
9305
9306static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9307 const v8::AccessorInfo& info) {
9308 force_set_get_count++;
9309 if (pass_on_get) {
9310 return v8::Handle<v8::Value>();
9311 } else {
9312 return v8::Int32::New(3);
9313 }
9314}
9315
9316static void ForceSetSetter(v8::Local<v8::String> name,
9317 v8::Local<v8::Value> value,
9318 const v8::AccessorInfo& info) {
9319 force_set_set_count++;
9320}
9321
9322static v8::Handle<v8::Value> ForceSetInterceptSetter(
9323 v8::Local<v8::String> name,
9324 v8::Local<v8::Value> value,
9325 const v8::AccessorInfo& info) {
9326 force_set_set_count++;
9327 return v8::Undefined();
9328}
9329
9330TEST(ForceSet) {
9331 force_set_get_count = 0;
9332 force_set_set_count = 0;
9333 pass_on_get = false;
9334
9335 v8::HandleScope scope;
9336 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9337 v8::Handle<v8::String> access_property = v8::String::New("a");
9338 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9339 LocalContext context(NULL, templ);
9340 v8::Handle<v8::Object> global = context->Global();
9341
9342 // Ordinary properties
9343 v8::Handle<v8::String> simple_property = v8::String::New("p");
9344 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9345 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9346 // This should fail because the property is read-only
9347 global->Set(simple_property, v8::Int32::New(5));
9348 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9349 // This should succeed even though the property is read-only
9350 global->ForceSet(simple_property, v8::Int32::New(6));
9351 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9352
9353 // Accessors
9354 CHECK_EQ(0, force_set_set_count);
9355 CHECK_EQ(0, force_set_get_count);
9356 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9357 // CHECK_EQ the property shouldn't override it, just call the setter
9358 // which in this case does nothing.
9359 global->Set(access_property, v8::Int32::New(7));
9360 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9361 CHECK_EQ(1, force_set_set_count);
9362 CHECK_EQ(2, force_set_get_count);
9363 // Forcing the property to be set should override the accessor without
9364 // calling it
9365 global->ForceSet(access_property, v8::Int32::New(8));
9366 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9367 CHECK_EQ(1, force_set_set_count);
9368 CHECK_EQ(2, force_set_get_count);
9369}
9370
9371TEST(ForceSetWithInterceptor) {
9372 force_set_get_count = 0;
9373 force_set_set_count = 0;
9374 pass_on_get = false;
9375
9376 v8::HandleScope scope;
9377 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9378 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9379 LocalContext context(NULL, templ);
9380 v8::Handle<v8::Object> global = context->Global();
9381
9382 v8::Handle<v8::String> some_property = v8::String::New("a");
9383 CHECK_EQ(0, force_set_set_count);
9384 CHECK_EQ(0, force_set_get_count);
9385 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9386 // Setting the property shouldn't override it, just call the setter
9387 // which in this case does nothing.
9388 global->Set(some_property, v8::Int32::New(7));
9389 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9390 CHECK_EQ(1, force_set_set_count);
9391 CHECK_EQ(2, force_set_get_count);
9392 // Getting the property when the interceptor returns an empty handle
9393 // should yield undefined, since the property isn't present on the
9394 // object itself yet.
9395 pass_on_get = true;
9396 CHECK(global->Get(some_property)->IsUndefined());
9397 CHECK_EQ(1, force_set_set_count);
9398 CHECK_EQ(3, force_set_get_count);
9399 // Forcing the property to be set should cause the value to be
9400 // set locally without calling the interceptor.
9401 global->ForceSet(some_property, v8::Int32::New(8));
9402 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9403 CHECK_EQ(1, force_set_set_count);
9404 CHECK_EQ(4, force_set_get_count);
9405 // Reenabling the interceptor should cause it to take precedence over
9406 // the property
9407 pass_on_get = false;
9408 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9409 CHECK_EQ(1, force_set_set_count);
9410 CHECK_EQ(5, force_set_get_count);
9411 // The interceptor should also work for other properties
9412 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9413 CHECK_EQ(1, force_set_set_count);
9414 CHECK_EQ(6, force_set_get_count);
9415}
9416
9417
9418THREADED_TEST(ForceDelete) {
9419 v8::HandleScope scope;
9420 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9421 LocalContext context(NULL, templ);
9422 v8::Handle<v8::Object> global = context->Global();
9423
9424 // Ordinary properties
9425 v8::Handle<v8::String> simple_property = v8::String::New("p");
9426 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9427 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9428 // This should fail because the property is dont-delete.
9429 CHECK(!global->Delete(simple_property));
9430 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9431 // This should succeed even though the property is dont-delete.
9432 CHECK(global->ForceDelete(simple_property));
9433 CHECK(global->Get(simple_property)->IsUndefined());
9434}
9435
9436
9437static int force_delete_interceptor_count = 0;
9438static bool pass_on_delete = false;
9439
9440
9441static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9442 v8::Local<v8::String> name,
9443 const v8::AccessorInfo& info) {
9444 force_delete_interceptor_count++;
9445 if (pass_on_delete) {
9446 return v8::Handle<v8::Boolean>();
9447 } else {
9448 return v8::True();
9449 }
9450}
9451
9452
9453THREADED_TEST(ForceDeleteWithInterceptor) {
9454 force_delete_interceptor_count = 0;
9455 pass_on_delete = false;
9456
9457 v8::HandleScope scope;
9458 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9459 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9460 LocalContext context(NULL, templ);
9461 v8::Handle<v8::Object> global = context->Global();
9462
9463 v8::Handle<v8::String> some_property = v8::String::New("a");
9464 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9465
9466 // Deleting a property should get intercepted and nothing should
9467 // happen.
9468 CHECK_EQ(0, force_delete_interceptor_count);
9469 CHECK(global->Delete(some_property));
9470 CHECK_EQ(1, force_delete_interceptor_count);
9471 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9472 // Deleting the property when the interceptor returns an empty
9473 // handle should not delete the property since it is DontDelete.
9474 pass_on_delete = true;
9475 CHECK(!global->Delete(some_property));
9476 CHECK_EQ(2, force_delete_interceptor_count);
9477 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9478 // Forcing the property to be deleted should delete the value
9479 // without calling the interceptor.
9480 CHECK(global->ForceDelete(some_property));
9481 CHECK(global->Get(some_property)->IsUndefined());
9482 CHECK_EQ(2, force_delete_interceptor_count);
9483}
9484
9485
9486// Make sure that forcing a delete invalidates any IC stubs, so we
9487// don't read the hole value.
9488THREADED_TEST(ForceDeleteIC) {
9489 v8::HandleScope scope;
9490 LocalContext context;
9491 // Create a DontDelete variable on the global object.
9492 CompileRun("this.__proto__ = { foo: 'horse' };"
9493 "var foo = 'fish';"
9494 "function f() { return foo.length; }");
9495 // Initialize the IC for foo in f.
9496 CompileRun("for (var i = 0; i < 4; i++) f();");
9497 // Make sure the value of foo is correct before the deletion.
9498 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9499 // Force the deletion of foo.
9500 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9501 // Make sure the value for foo is read from the prototype, and that
9502 // we don't get in trouble with reading the deleted cell value
9503 // sentinel.
9504 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9505}
9506
9507
9508v8::Persistent<Context> calling_context0;
9509v8::Persistent<Context> calling_context1;
9510v8::Persistent<Context> calling_context2;
9511
9512
9513// Check that the call to the callback is initiated in
9514// calling_context2, the directly calling context is calling_context1
9515// and the callback itself is in calling_context0.
9516static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9517 ApiTestFuzzer::Fuzz();
9518 CHECK(Context::GetCurrent() == calling_context0);
9519 CHECK(Context::GetCalling() == calling_context1);
9520 CHECK(Context::GetEntered() == calling_context2);
9521 return v8::Integer::New(42);
9522}
9523
9524
9525THREADED_TEST(GetCallingContext) {
9526 v8::HandleScope scope;
9527
9528 calling_context0 = Context::New();
9529 calling_context1 = Context::New();
9530 calling_context2 = Context::New();
9531
9532 // Allow cross-domain access.
9533 Local<String> token = v8_str("<security token>");
9534 calling_context0->SetSecurityToken(token);
9535 calling_context1->SetSecurityToken(token);
9536 calling_context2->SetSecurityToken(token);
9537
9538 // Create an object with a C++ callback in context0.
9539 calling_context0->Enter();
9540 Local<v8::FunctionTemplate> callback_templ =
9541 v8::FunctionTemplate::New(GetCallingContextCallback);
9542 calling_context0->Global()->Set(v8_str("callback"),
9543 callback_templ->GetFunction());
9544 calling_context0->Exit();
9545
9546 // Expose context0 in context1 and setup a function that calls the
9547 // callback function.
9548 calling_context1->Enter();
9549 calling_context1->Global()->Set(v8_str("context0"),
9550 calling_context0->Global());
9551 CompileRun("function f() { context0.callback() }");
9552 calling_context1->Exit();
9553
9554 // Expose context1 in context2 and call the callback function in
9555 // context0 indirectly through f in context1.
9556 calling_context2->Enter();
9557 calling_context2->Global()->Set(v8_str("context1"),
9558 calling_context1->Global());
9559 CompileRun("context1.f()");
9560 calling_context2->Exit();
9561
9562 // Dispose the contexts to allow them to be garbage collected.
9563 calling_context0.Dispose();
9564 calling_context1.Dispose();
9565 calling_context2.Dispose();
9566 calling_context0.Clear();
9567 calling_context1.Clear();
9568 calling_context2.Clear();
9569}
9570
9571
9572// Check that a variable declaration with no explicit initialization
9573// value does not shadow an existing property in the prototype chain.
9574//
9575// This is consistent with Firefox and Safari.
9576//
9577// See http://crbug.com/12548.
9578THREADED_TEST(InitGlobalVarInProtoChain) {
9579 v8::HandleScope scope;
9580 LocalContext context;
9581 // Introduce a variable in the prototype chain.
9582 CompileRun("__proto__.x = 42");
9583 v8::Handle<v8::Value> result = CompileRun("var x; x");
9584 CHECK(!result->IsUndefined());
9585 CHECK_EQ(42, result->Int32Value());
9586}
9587
9588
9589// Regression test for issue 398.
9590// If a function is added to an object, creating a constant function
9591// field, and the result is cloned, replacing the constant function on the
9592// original should not affect the clone.
9593// See http://code.google.com/p/v8/issues/detail?id=398
9594THREADED_TEST(ReplaceConstantFunction) {
9595 v8::HandleScope scope;
9596 LocalContext context;
9597 v8::Handle<v8::Object> obj = v8::Object::New();
9598 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9599 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9600 obj->Set(foo_string, func_templ->GetFunction());
9601 v8::Handle<v8::Object> obj_clone = obj->Clone();
9602 obj_clone->Set(foo_string, v8::String::New("Hello"));
9603 CHECK(!obj->Get(foo_string)->IsUndefined());
9604}
9605
9606
9607// Regression test for http://crbug.com/16276.
9608THREADED_TEST(Regress16276) {
9609 v8::HandleScope scope;
9610 LocalContext context;
9611 // Force the IC in f to be a dictionary load IC.
9612 CompileRun("function f(obj) { return obj.x; }\n"
9613 "var obj = { x: { foo: 42 }, y: 87 };\n"
9614 "var x = obj.x;\n"
9615 "delete obj.y;\n"
9616 "for (var i = 0; i < 5; i++) f(obj);");
9617 // Detach the global object to make 'this' refer directly to the
9618 // global object (not the proxy), and make sure that the dictionary
9619 // load IC doesn't mess up loading directly from the global object.
9620 context->DetachGlobal();
9621 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9622}
9623
9624
9625THREADED_TEST(PixelArray) {
9626 v8::HandleScope scope;
9627 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009628 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009629 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9630 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9631 pixel_data);
9632 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9633 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009634 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009635 }
9636 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9637 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009638 CHECK_EQ(i % 256, pixels->get(i));
9639 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009640 }
9641
9642 v8::Handle<v8::Object> obj = v8::Object::New();
9643 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9644 // Set the elements to be the pixels.
9645 // jsobj->set_elements(*pixels);
9646 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9647 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9648 obj->Set(v8_str("field"), v8::Int32::New(1503));
9649 context->Global()->Set(v8_str("pixels"), obj);
9650 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9651 CHECK_EQ(1503, result->Int32Value());
9652 result = CompileRun("pixels[1]");
9653 CHECK_EQ(1, result->Int32Value());
9654
9655 result = CompileRun("var sum = 0;"
9656 "for (var i = 0; i < 8; i++) {"
9657 " sum += pixels[i] = pixels[i] = -i;"
9658 "}"
9659 "sum;");
9660 CHECK_EQ(-28, result->Int32Value());
9661
9662 result = CompileRun("var sum = 0;"
9663 "for (var i = 0; i < 8; i++) {"
9664 " sum += pixels[i] = pixels[i] = 0;"
9665 "}"
9666 "sum;");
9667 CHECK_EQ(0, result->Int32Value());
9668
9669 result = CompileRun("var sum = 0;"
9670 "for (var i = 0; i < 8; i++) {"
9671 " sum += pixels[i] = pixels[i] = 255;"
9672 "}"
9673 "sum;");
9674 CHECK_EQ(8 * 255, result->Int32Value());
9675
9676 result = CompileRun("var sum = 0;"
9677 "for (var i = 0; i < 8; i++) {"
9678 " sum += pixels[i] = pixels[i] = 256 + i;"
9679 "}"
9680 "sum;");
9681 CHECK_EQ(2076, result->Int32Value());
9682
9683 result = CompileRun("var sum = 0;"
9684 "for (var i = 0; i < 8; i++) {"
9685 " sum += pixels[i] = pixels[i] = i;"
9686 "}"
9687 "sum;");
9688 CHECK_EQ(28, result->Int32Value());
9689
9690 result = CompileRun("var sum = 0;"
9691 "for (var i = 0; i < 8; i++) {"
9692 " sum += pixels[i];"
9693 "}"
9694 "sum;");
9695 CHECK_EQ(28, result->Int32Value());
9696
9697 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9698 i::SetElement(jsobj, 1, value);
9699 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9700 *value.location() = i::Smi::FromInt(256);
9701 i::SetElement(jsobj, 1, value);
9702 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9703 *value.location() = i::Smi::FromInt(-1);
9704 i::SetElement(jsobj, 1, value);
9705 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9706
9707 result = CompileRun("for (var i = 0; i < 8; i++) {"
9708 " pixels[i] = (i * 65) - 109;"
9709 "}"
9710 "pixels[1] + pixels[6];");
9711 CHECK_EQ(255, result->Int32Value());
9712 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9713 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9714 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9715 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9716 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9717 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9718 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9719 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9720 result = CompileRun("var sum = 0;"
9721 "for (var i = 0; i < 8; i++) {"
9722 " sum += pixels[i];"
9723 "}"
9724 "sum;");
9725 CHECK_EQ(984, result->Int32Value());
9726
9727 result = CompileRun("for (var i = 0; i < 8; i++) {"
9728 " pixels[i] = (i * 1.1);"
9729 "}"
9730 "pixels[1] + pixels[6];");
9731 CHECK_EQ(8, result->Int32Value());
9732 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9733 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9734 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9735 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9736 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9737 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9738 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9739 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9740
9741 result = CompileRun("for (var i = 0; i < 8; i++) {"
9742 " pixels[7] = undefined;"
9743 "}"
9744 "pixels[7];");
9745 CHECK_EQ(0, result->Int32Value());
9746 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9747
9748 result = CompileRun("for (var i = 0; i < 8; i++) {"
9749 " pixels[6] = '2.3';"
9750 "}"
9751 "pixels[6];");
9752 CHECK_EQ(2, result->Int32Value());
9753 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9754
9755 result = CompileRun("for (var i = 0; i < 8; i++) {"
9756 " pixels[5] = NaN;"
9757 "}"
9758 "pixels[5];");
9759 CHECK_EQ(0, result->Int32Value());
9760 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9761
9762 result = CompileRun("for (var i = 0; i < 8; i++) {"
9763 " pixels[8] = Infinity;"
9764 "}"
9765 "pixels[8];");
9766 CHECK_EQ(255, result->Int32Value());
9767 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9768
9769 result = CompileRun("for (var i = 0; i < 8; i++) {"
9770 " pixels[9] = -Infinity;"
9771 "}"
9772 "pixels[9];");
9773 CHECK_EQ(0, result->Int32Value());
9774 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9775
9776 result = CompileRun("pixels[3] = 33;"
9777 "delete pixels[3];"
9778 "pixels[3];");
9779 CHECK_EQ(33, result->Int32Value());
9780
9781 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9782 "pixels[2] = 12; pixels[3] = 13;"
9783 "pixels.__defineGetter__('2',"
9784 "function() { return 120; });"
9785 "pixels[2];");
9786 CHECK_EQ(12, result->Int32Value());
9787
9788 result = CompileRun("var js_array = new Array(40);"
9789 "js_array[0] = 77;"
9790 "js_array;");
9791 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9792
9793 result = CompileRun("pixels[1] = 23;"
9794 "pixels.__proto__ = [];"
9795 "js_array.__proto__ = pixels;"
9796 "js_array.concat(pixels);");
9797 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9798 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9799
9800 result = CompileRun("pixels[1] = 23;");
9801 CHECK_EQ(23, result->Int32Value());
9802
Steve Blockd0582a62009-12-15 09:54:21 +00009803 // Test for index greater than 255. Regression test for:
9804 // http://code.google.com/p/chromium/issues/detail?id=26337.
9805 result = CompileRun("pixels[256] = 255;");
9806 CHECK_EQ(255, result->Int32Value());
9807 result = CompileRun("var i = 0;"
9808 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9809 "i");
9810 CHECK_EQ(255, result->Int32Value());
9811
Steve Blocka7e24c12009-10-30 11:49:00 +00009812 free(pixel_data);
9813}
9814
9815
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009816THREADED_TEST(PixelArrayInfo) {
9817 v8::HandleScope scope;
9818 LocalContext context;
9819 for (int size = 0; size < 100; size += 10) {
9820 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9821 v8::Handle<v8::Object> obj = v8::Object::New();
9822 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9823 CHECK(obj->HasIndexedPropertiesInPixelData());
9824 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9825 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9826 free(pixel_data);
9827 }
9828}
9829
9830
9831static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9832 switch (array_type) {
9833 case v8::kExternalByteArray:
9834 case v8::kExternalUnsignedByteArray:
9835 return 1;
9836 break;
9837 case v8::kExternalShortArray:
9838 case v8::kExternalUnsignedShortArray:
9839 return 2;
9840 break;
9841 case v8::kExternalIntArray:
9842 case v8::kExternalUnsignedIntArray:
9843 case v8::kExternalFloatArray:
9844 return 4;
9845 break;
9846 default:
9847 UNREACHABLE();
9848 return -1;
9849 }
9850 UNREACHABLE();
9851 return -1;
9852}
9853
9854
Steve Block3ce2e202009-11-05 08:53:23 +00009855template <class ExternalArrayClass, class ElementType>
9856static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9857 int64_t low,
9858 int64_t high) {
9859 v8::HandleScope scope;
9860 LocalContext context;
9861 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009862 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +00009863 ElementType* array_data =
9864 static_cast<ElementType*>(malloc(kElementCount * element_size));
9865 i::Handle<ExternalArrayClass> array =
9866 i::Handle<ExternalArrayClass>::cast(
9867 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9868 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9869 for (int i = 0; i < kElementCount; i++) {
9870 array->set(i, static_cast<ElementType>(i));
9871 }
9872 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9873 for (int i = 0; i < kElementCount; i++) {
9874 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9875 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9876 }
9877
9878 v8::Handle<v8::Object> obj = v8::Object::New();
9879 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9880 // Set the elements to be the external array.
9881 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9882 array_type,
9883 kElementCount);
9884 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9885 obj->Set(v8_str("field"), v8::Int32::New(1503));
9886 context->Global()->Set(v8_str("ext_array"), obj);
9887 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9888 CHECK_EQ(1503, result->Int32Value());
9889 result = CompileRun("ext_array[1]");
9890 CHECK_EQ(1, result->Int32Value());
9891
9892 // Check pass through of assigned smis
9893 result = CompileRun("var sum = 0;"
9894 "for (var i = 0; i < 8; i++) {"
9895 " sum += ext_array[i] = ext_array[i] = -i;"
9896 "}"
9897 "sum;");
9898 CHECK_EQ(-28, result->Int32Value());
9899
9900 // Check assigned smis
9901 result = CompileRun("for (var i = 0; i < 8; i++) {"
9902 " ext_array[i] = i;"
9903 "}"
9904 "var sum = 0;"
9905 "for (var i = 0; i < 8; i++) {"
9906 " sum += ext_array[i];"
9907 "}"
9908 "sum;");
9909 CHECK_EQ(28, result->Int32Value());
9910
9911 // Check assigned smis in reverse order
9912 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9913 " ext_array[i] = i;"
9914 "}"
9915 "var sum = 0;"
9916 "for (var i = 0; i < 8; i++) {"
9917 " sum += ext_array[i];"
9918 "}"
9919 "sum;");
9920 CHECK_EQ(28, result->Int32Value());
9921
9922 // Check pass through of assigned HeapNumbers
9923 result = CompileRun("var sum = 0;"
9924 "for (var i = 0; i < 16; i+=2) {"
9925 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9926 "}"
9927 "sum;");
9928 CHECK_EQ(-28, result->Int32Value());
9929
9930 // Check assigned HeapNumbers
9931 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9932 " ext_array[i] = (i * 0.5);"
9933 "}"
9934 "var sum = 0;"
9935 "for (var i = 0; i < 16; i+=2) {"
9936 " sum += ext_array[i];"
9937 "}"
9938 "sum;");
9939 CHECK_EQ(28, result->Int32Value());
9940
9941 // Check assigned HeapNumbers in reverse order
9942 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9943 " ext_array[i] = (i * 0.5);"
9944 "}"
9945 "var sum = 0;"
9946 "for (var i = 0; i < 16; i+=2) {"
9947 " sum += ext_array[i];"
9948 "}"
9949 "sum;");
9950 CHECK_EQ(28, result->Int32Value());
9951
9952 i::ScopedVector<char> test_buf(1024);
9953
9954 // Check legal boundary conditions.
9955 // The repeated loads and stores ensure the ICs are exercised.
9956 const char* boundary_program =
9957 "var res = 0;"
9958 "for (var i = 0; i < 16; i++) {"
9959 " ext_array[i] = %lld;"
9960 " if (i > 8) {"
9961 " res = ext_array[i];"
9962 " }"
9963 "}"
9964 "res;";
9965 i::OS::SNPrintF(test_buf,
9966 boundary_program,
9967 low);
9968 result = CompileRun(test_buf.start());
9969 CHECK_EQ(low, result->IntegerValue());
9970
9971 i::OS::SNPrintF(test_buf,
9972 boundary_program,
9973 high);
9974 result = CompileRun(test_buf.start());
9975 CHECK_EQ(high, result->IntegerValue());
9976
9977 // Check misprediction of type in IC.
9978 result = CompileRun("var tmp_array = ext_array;"
9979 "var sum = 0;"
9980 "for (var i = 0; i < 8; i++) {"
9981 " tmp_array[i] = i;"
9982 " sum += tmp_array[i];"
9983 " if (i == 4) {"
9984 " tmp_array = {};"
9985 " }"
9986 "}"
9987 "sum;");
9988 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9989 CHECK_EQ(28, result->Int32Value());
9990
9991 // Make sure out-of-range loads do not throw.
9992 i::OS::SNPrintF(test_buf,
9993 "var caught_exception = false;"
9994 "try {"
9995 " ext_array[%d];"
9996 "} catch (e) {"
9997 " caught_exception = true;"
9998 "}"
9999 "caught_exception;",
10000 kElementCount);
10001 result = CompileRun(test_buf.start());
10002 CHECK_EQ(false, result->BooleanValue());
10003
10004 // Make sure out-of-range stores do not throw.
10005 i::OS::SNPrintF(test_buf,
10006 "var caught_exception = false;"
10007 "try {"
10008 " ext_array[%d] = 1;"
10009 "} catch (e) {"
10010 " caught_exception = true;"
10011 "}"
10012 "caught_exception;",
10013 kElementCount);
10014 result = CompileRun(test_buf.start());
10015 CHECK_EQ(false, result->BooleanValue());
10016
10017 // Check other boundary conditions, values and operations.
10018 result = CompileRun("for (var i = 0; i < 8; i++) {"
10019 " ext_array[7] = undefined;"
10020 "}"
10021 "ext_array[7];");
10022 CHECK_EQ(0, result->Int32Value());
10023 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
10024
10025 result = CompileRun("for (var i = 0; i < 8; i++) {"
10026 " ext_array[6] = '2.3';"
10027 "}"
10028 "ext_array[6];");
10029 CHECK_EQ(2, result->Int32Value());
10030 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
10031
10032 if (array_type != v8::kExternalFloatArray) {
10033 // Though the specification doesn't state it, be explicit about
10034 // converting NaNs and +/-Infinity to zero.
10035 result = CompileRun("for (var i = 0; i < 8; i++) {"
10036 " ext_array[i] = 5;"
10037 "}"
10038 "for (var i = 0; i < 8; i++) {"
10039 " ext_array[i] = NaN;"
10040 "}"
10041 "ext_array[5];");
10042 CHECK_EQ(0, result->Int32Value());
10043 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10044
10045 result = CompileRun("for (var i = 0; i < 8; i++) {"
10046 " ext_array[i] = 5;"
10047 "}"
10048 "for (var i = 0; i < 8; i++) {"
10049 " ext_array[i] = Infinity;"
10050 "}"
10051 "ext_array[5];");
10052 CHECK_EQ(0, result->Int32Value());
10053 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10054
10055 result = CompileRun("for (var i = 0; i < 8; i++) {"
10056 " ext_array[i] = 5;"
10057 "}"
10058 "for (var i = 0; i < 8; i++) {"
10059 " ext_array[i] = -Infinity;"
10060 "}"
10061 "ext_array[5];");
10062 CHECK_EQ(0, result->Int32Value());
10063 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10064 }
10065
10066 result = CompileRun("ext_array[3] = 33;"
10067 "delete ext_array[3];"
10068 "ext_array[3];");
10069 CHECK_EQ(33, result->Int32Value());
10070
10071 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
10072 "ext_array[2] = 12; ext_array[3] = 13;"
10073 "ext_array.__defineGetter__('2',"
10074 "function() { return 120; });"
10075 "ext_array[2];");
10076 CHECK_EQ(12, result->Int32Value());
10077
10078 result = CompileRun("var js_array = new Array(40);"
10079 "js_array[0] = 77;"
10080 "js_array;");
10081 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10082
10083 result = CompileRun("ext_array[1] = 23;"
10084 "ext_array.__proto__ = [];"
10085 "js_array.__proto__ = ext_array;"
10086 "js_array.concat(ext_array);");
10087 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10088 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10089
10090 result = CompileRun("ext_array[1] = 23;");
10091 CHECK_EQ(23, result->Int32Value());
10092
Steve Blockd0582a62009-12-15 09:54:21 +000010093 // Test more complex manipulations which cause eax to contain values
10094 // that won't be completely overwritten by loads from the arrays.
10095 // This catches bugs in the instructions used for the KeyedLoadIC
10096 // for byte and word types.
10097 {
10098 const int kXSize = 300;
10099 const int kYSize = 300;
10100 const int kLargeElementCount = kXSize * kYSize * 4;
10101 ElementType* large_array_data =
10102 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
10103 i::Handle<ExternalArrayClass> large_array =
10104 i::Handle<ExternalArrayClass>::cast(
10105 i::Factory::NewExternalArray(kLargeElementCount,
10106 array_type,
10107 array_data));
10108 v8::Handle<v8::Object> large_obj = v8::Object::New();
10109 // Set the elements to be the external array.
10110 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
10111 array_type,
10112 kLargeElementCount);
10113 context->Global()->Set(v8_str("large_array"), large_obj);
10114 // Initialize contents of a few rows.
10115 for (int x = 0; x < 300; x++) {
10116 int row = 0;
10117 int offset = row * 300 * 4;
10118 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10119 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10120 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10121 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10122 row = 150;
10123 offset = row * 300 * 4;
10124 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10125 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10126 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10127 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10128 row = 298;
10129 offset = row * 300 * 4;
10130 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10131 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10132 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10133 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10134 }
10135 // The goal of the code below is to make "offset" large enough
10136 // that the computation of the index (which goes into eax) has
10137 // high bits set which will not be overwritten by a byte or short
10138 // load.
10139 result = CompileRun("var failed = false;"
10140 "var offset = 0;"
10141 "for (var i = 0; i < 300; i++) {"
10142 " if (large_array[4 * i] != 127 ||"
10143 " large_array[4 * i + 1] != 0 ||"
10144 " large_array[4 * i + 2] != 0 ||"
10145 " large_array[4 * i + 3] != 127) {"
10146 " failed = true;"
10147 " }"
10148 "}"
10149 "offset = 150 * 300 * 4;"
10150 "for (var i = 0; i < 300; i++) {"
10151 " if (large_array[offset + 4 * i] != 127 ||"
10152 " large_array[offset + 4 * i + 1] != 0 ||"
10153 " large_array[offset + 4 * i + 2] != 0 ||"
10154 " large_array[offset + 4 * i + 3] != 127) {"
10155 " failed = true;"
10156 " }"
10157 "}"
10158 "offset = 298 * 300 * 4;"
10159 "for (var i = 0; i < 300; i++) {"
10160 " if (large_array[offset + 4 * i] != 127 ||"
10161 " large_array[offset + 4 * i + 1] != 0 ||"
10162 " large_array[offset + 4 * i + 2] != 0 ||"
10163 " large_array[offset + 4 * i + 3] != 127) {"
10164 " failed = true;"
10165 " }"
10166 "}"
10167 "!failed;");
10168 CHECK_EQ(true, result->BooleanValue());
10169 free(large_array_data);
10170 }
10171
Steve Block3ce2e202009-11-05 08:53:23 +000010172 free(array_data);
10173}
10174
10175
10176THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010177 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010178 v8::kExternalByteArray,
10179 -128,
10180 127);
10181}
10182
10183
10184THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010185 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010186 v8::kExternalUnsignedByteArray,
10187 0,
10188 255);
10189}
10190
10191
10192THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010193 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010194 v8::kExternalShortArray,
10195 -32768,
10196 32767);
10197}
10198
10199
10200THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010201 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010202 v8::kExternalUnsignedShortArray,
10203 0,
10204 65535);
10205}
10206
10207
10208THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010209 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010210 v8::kExternalIntArray,
10211 INT_MIN, // -2147483648
10212 INT_MAX); // 2147483647
10213}
10214
10215
10216THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010217 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010218 v8::kExternalUnsignedIntArray,
10219 0,
10220 UINT_MAX); // 4294967295
10221}
10222
10223
10224THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010225 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000010226 v8::kExternalFloatArray,
10227 -500,
10228 500);
10229}
10230
10231
10232THREADED_TEST(ExternalArrays) {
10233 TestExternalByteArray();
10234 TestExternalUnsignedByteArray();
10235 TestExternalShortArray();
10236 TestExternalUnsignedShortArray();
10237 TestExternalIntArray();
10238 TestExternalUnsignedIntArray();
10239 TestExternalFloatArray();
10240}
10241
10242
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010243void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10244 v8::HandleScope scope;
10245 LocalContext context;
10246 for (int size = 0; size < 100; size += 10) {
10247 int element_size = ExternalArrayElementSize(array_type);
10248 void* external_data = malloc(size * element_size);
10249 v8::Handle<v8::Object> obj = v8::Object::New();
10250 obj->SetIndexedPropertiesToExternalArrayData(
10251 external_data, array_type, size);
10252 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10253 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10254 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10255 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10256 free(external_data);
10257 }
10258}
10259
10260
10261THREADED_TEST(ExternalArrayInfo) {
10262 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10263 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10264 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10265 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10266 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10267 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10268 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10269}
10270
10271
Steve Blocka7e24c12009-10-30 11:49:00 +000010272THREADED_TEST(ScriptContextDependence) {
10273 v8::HandleScope scope;
10274 LocalContext c1;
10275 const char *source = "foo";
10276 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10277 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10278 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10279 CHECK_EQ(dep->Run()->Int32Value(), 100);
10280 CHECK_EQ(indep->Run()->Int32Value(), 100);
10281 LocalContext c2;
10282 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10283 CHECK_EQ(dep->Run()->Int32Value(), 100);
10284 CHECK_EQ(indep->Run()->Int32Value(), 101);
10285}
10286
10287
10288THREADED_TEST(StackTrace) {
10289 v8::HandleScope scope;
10290 LocalContext context;
10291 v8::TryCatch try_catch;
10292 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10293 v8::Handle<v8::String> src = v8::String::New(source);
10294 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10295 v8::Script::New(src, origin)->Run();
10296 CHECK(try_catch.HasCaught());
10297 v8::String::Utf8Value stack(try_catch.StackTrace());
10298 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10299}
10300
10301
Kristian Monsen25f61362010-05-21 11:50:48 +010010302// Checks that a StackFrame has certain expected values.
10303void checkStackFrame(const char* expected_script_name,
10304 const char* expected_func_name, int expected_line_number,
10305 int expected_column, bool is_eval, bool is_constructor,
10306 v8::Handle<v8::StackFrame> frame) {
10307 v8::HandleScope scope;
10308 v8::String::Utf8Value func_name(frame->GetFunctionName());
10309 v8::String::Utf8Value script_name(frame->GetScriptName());
10310 if (*script_name == NULL) {
10311 // The situation where there is no associated script, like for evals.
10312 CHECK(expected_script_name == NULL);
10313 } else {
10314 CHECK(strstr(*script_name, expected_script_name) != NULL);
10315 }
10316 CHECK(strstr(*func_name, expected_func_name) != NULL);
10317 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10318 CHECK_EQ(expected_column, frame->GetColumn());
10319 CHECK_EQ(is_eval, frame->IsEval());
10320 CHECK_EQ(is_constructor, frame->IsConstructor());
10321}
10322
10323
10324v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10325 v8::HandleScope scope;
10326 const char* origin = "capture-stack-trace-test";
10327 const int kOverviewTest = 1;
10328 const int kDetailedTest = 2;
10329
10330 ASSERT(args.Length() == 1);
10331
10332 int testGroup = args[0]->Int32Value();
10333 if (testGroup == kOverviewTest) {
10334 v8::Handle<v8::StackTrace> stackTrace =
10335 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10336 CHECK_EQ(4, stackTrace->GetFrameCount());
10337 checkStackFrame(origin, "bar", 2, 10, false, false,
10338 stackTrace->GetFrame(0));
10339 checkStackFrame(origin, "foo", 6, 3, false, false,
10340 stackTrace->GetFrame(1));
10341 checkStackFrame(NULL, "", 1, 1, false, false,
10342 stackTrace->GetFrame(2));
10343 // The last frame is an anonymous function that has the initial call.
10344 checkStackFrame(origin, "", 8, 7, false, false,
10345 stackTrace->GetFrame(3));
10346
10347 CHECK(stackTrace->AsArray()->IsArray());
10348 } else if (testGroup == kDetailedTest) {
10349 v8::Handle<v8::StackTrace> stackTrace =
10350 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10351 CHECK_EQ(4, stackTrace->GetFrameCount());
10352 checkStackFrame(origin, "bat", 4, 22, false, false,
10353 stackTrace->GetFrame(0));
10354 checkStackFrame(origin, "baz", 8, 3, false, true,
10355 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010356#ifdef ENABLE_DEBUGGER_SUPPORT
10357 bool is_eval = true;
10358#else // ENABLE_DEBUGGER_SUPPORT
10359 bool is_eval = false;
10360#endif // ENABLE_DEBUGGER_SUPPORT
10361
10362 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010363 stackTrace->GetFrame(2));
10364 // The last frame is an anonymous function that has the initial call to foo.
10365 checkStackFrame(origin, "", 10, 1, false, false,
10366 stackTrace->GetFrame(3));
10367
10368 CHECK(stackTrace->AsArray()->IsArray());
10369 }
10370 return v8::Undefined();
10371}
10372
10373
10374// Tests the C++ StackTrace API.
10375THREADED_TEST(CaptureStackTrace) {
10376 v8::HandleScope scope;
10377 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10378 Local<ObjectTemplate> templ = ObjectTemplate::New();
10379 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10380 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10381 LocalContext context(0, templ);
10382
10383 // Test getting OVERVIEW information. Should ignore information that is not
10384 // script name, function name, line number, and column offset.
10385 const char *overview_source =
10386 "function bar() {\n"
10387 " var y; AnalyzeStackInNativeCode(1);\n"
10388 "}\n"
10389 "function foo() {\n"
10390 "\n"
10391 " bar();\n"
10392 "}\n"
10393 "var x;eval('new foo();');";
10394 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10395 v8::Handle<Value> overview_result =
10396 v8::Script::New(overview_src, origin)->Run();
10397 ASSERT(!overview_result.IsEmpty());
10398 ASSERT(overview_result->IsObject());
10399
10400 // Test getting DETAILED information.
10401 const char *detailed_source =
10402 "function bat() {AnalyzeStackInNativeCode(2);\n"
10403 "}\n"
10404 "\n"
10405 "function baz() {\n"
10406 " bat();\n"
10407 "}\n"
10408 "eval('new baz();');";
10409 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10410 // Make the script using a non-zero line and column offset.
10411 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10412 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10413 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10414 v8::Handle<v8::Script> detailed_script(
10415 v8::Script::New(detailed_src, &detailed_origin));
10416 v8::Handle<Value> detailed_result = detailed_script->Run();
10417 ASSERT(!detailed_result.IsEmpty());
10418 ASSERT(detailed_result->IsObject());
10419}
10420
10421
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010422static void StackTraceForUncaughtExceptionListener(
10423 v8::Handle<v8::Message> message,
10424 v8::Handle<Value>) {
10425 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
10426 CHECK_EQ(2, stack_trace->GetFrameCount());
10427 checkStackFrame("origin", "foo", 2, 3, false, false,
10428 stack_trace->GetFrame(0));
10429 checkStackFrame("origin", "bar", 5, 3, false, false,
10430 stack_trace->GetFrame(1));
10431}
10432
10433TEST(CaptureStackTraceForUncaughtException) {
10434 report_count = 0;
10435 v8::HandleScope scope;
10436 LocalContext env;
10437 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
10438 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
10439
10440 Script::Compile(v8_str("function foo() {\n"
10441 " throw 1;\n"
10442 "};\n"
10443 "function bar() {\n"
10444 " foo();\n"
10445 "};"),
10446 v8_str("origin"))->Run();
10447 v8::Local<v8::Object> global = env->Global();
10448 Local<Value> trouble = global->Get(v8_str("bar"));
10449 CHECK(trouble->IsFunction());
10450 Function::Cast(*trouble)->Call(global, 0, NULL);
10451 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
10452 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
10453}
10454
10455
Steve Block3ce2e202009-11-05 08:53:23 +000010456// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010457THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010458 bool rv = false;
10459 for (int i = 0; i < 100; i++) {
10460 rv = v8::V8::IdleNotification();
10461 if (rv)
10462 break;
10463 }
10464 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010465}
10466
10467
10468static uint32_t* stack_limit;
10469
10470static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10471 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10472 return v8::Undefined();
10473}
10474
10475
10476// Uses the address of a local variable to determine the stack top now.
10477// Given a size, returns an address that is that far from the current
10478// top of stack.
10479static uint32_t* ComputeStackLimit(uint32_t size) {
10480 uint32_t* answer = &size - (size / sizeof(size));
10481 // If the size is very large and the stack is very near the bottom of
10482 // memory then the calculation above may wrap around and give an address
10483 // that is above the (downwards-growing) stack. In that case we return
10484 // a very low address.
10485 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10486 return answer;
10487}
10488
10489
10490TEST(SetResourceConstraints) {
10491 static const int K = 1024;
10492 uint32_t* set_limit = ComputeStackLimit(128 * K);
10493
10494 // Set stack limit.
10495 v8::ResourceConstraints constraints;
10496 constraints.set_stack_limit(set_limit);
10497 CHECK(v8::SetResourceConstraints(&constraints));
10498
10499 // Execute a script.
10500 v8::HandleScope scope;
10501 LocalContext env;
10502 Local<v8::FunctionTemplate> fun_templ =
10503 v8::FunctionTemplate::New(GetStackLimitCallback);
10504 Local<Function> fun = fun_templ->GetFunction();
10505 env->Global()->Set(v8_str("get_stack_limit"), fun);
10506 CompileRun("get_stack_limit();");
10507
10508 CHECK(stack_limit == set_limit);
10509}
10510
10511
10512TEST(SetResourceConstraintsInThread) {
10513 uint32_t* set_limit;
10514 {
10515 v8::Locker locker;
10516 static const int K = 1024;
10517 set_limit = ComputeStackLimit(128 * K);
10518
10519 // Set stack limit.
10520 v8::ResourceConstraints constraints;
10521 constraints.set_stack_limit(set_limit);
10522 CHECK(v8::SetResourceConstraints(&constraints));
10523
10524 // Execute a script.
10525 v8::HandleScope scope;
10526 LocalContext env;
10527 Local<v8::FunctionTemplate> fun_templ =
10528 v8::FunctionTemplate::New(GetStackLimitCallback);
10529 Local<Function> fun = fun_templ->GetFunction();
10530 env->Global()->Set(v8_str("get_stack_limit"), fun);
10531 CompileRun("get_stack_limit();");
10532
10533 CHECK(stack_limit == set_limit);
10534 }
10535 {
10536 v8::Locker locker;
10537 CHECK(stack_limit == set_limit);
10538 }
10539}
Steve Block3ce2e202009-11-05 08:53:23 +000010540
10541
10542THREADED_TEST(GetHeapStatistics) {
10543 v8::HandleScope scope;
10544 LocalContext c1;
10545 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010546 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10547 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010548 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010549 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10550 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10551}
10552
10553
10554static double DoubleFromBits(uint64_t value) {
10555 double target;
10556#ifdef BIG_ENDIAN_FLOATING_POINT
10557 const int kIntSize = 4;
10558 // Somebody swapped the lower and higher half of doubles.
10559 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10560 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10561#else
10562 memcpy(&target, &value, sizeof(target));
10563#endif
10564 return target;
10565}
10566
10567
10568static uint64_t DoubleToBits(double value) {
10569 uint64_t target;
10570#ifdef BIG_ENDIAN_FLOATING_POINT
10571 const int kIntSize = 4;
10572 // Somebody swapped the lower and higher half of doubles.
10573 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10574 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10575#else
10576 memcpy(&target, &value, sizeof(target));
10577#endif
10578 return target;
10579}
10580
10581
10582static double DoubleToDateTime(double input) {
10583 double date_limit = 864e13;
10584 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10585 return i::OS::nan_value();
10586 }
10587 return (input < 0) ? -(floor(-input)) : floor(input);
10588}
10589
10590// We don't have a consistent way to write 64-bit constants syntactically, so we
10591// split them into two 32-bit constants and combine them programmatically.
10592static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10593 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10594}
10595
10596
10597THREADED_TEST(QuietSignalingNaNs) {
10598 v8::HandleScope scope;
10599 LocalContext context;
10600 v8::TryCatch try_catch;
10601
10602 // Special double values.
10603 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10604 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10605 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10606 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10607 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10608 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10609 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10610
10611 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10612 // on either side of the epoch.
10613 double date_limit = 864e13;
10614
10615 double test_values[] = {
10616 snan,
10617 qnan,
10618 infinity,
10619 max_normal,
10620 date_limit + 1,
10621 date_limit,
10622 min_normal,
10623 max_denormal,
10624 min_denormal,
10625 0,
10626 -0,
10627 -min_denormal,
10628 -max_denormal,
10629 -min_normal,
10630 -date_limit,
10631 -date_limit - 1,
10632 -max_normal,
10633 -infinity,
10634 -qnan,
10635 -snan
10636 };
10637 int num_test_values = 20;
10638
10639 for (int i = 0; i < num_test_values; i++) {
10640 double test_value = test_values[i];
10641
10642 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10643 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10644 double stored_number = number->NumberValue();
10645 if (!IsNaN(test_value)) {
10646 CHECK_EQ(test_value, stored_number);
10647 } else {
10648 uint64_t stored_bits = DoubleToBits(stored_number);
10649 // Check if quiet nan (bits 51..62 all set).
10650 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10651 }
10652
10653 // Check that Date::New preserves non-NaNs in the date range and
10654 // quiets SNaNs.
10655 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10656 double expected_stored_date = DoubleToDateTime(test_value);
10657 double stored_date = date->NumberValue();
10658 if (!IsNaN(expected_stored_date)) {
10659 CHECK_EQ(expected_stored_date, stored_date);
10660 } else {
10661 uint64_t stored_bits = DoubleToBits(stored_date);
10662 // Check if quiet nan (bits 51..62 all set).
10663 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10664 }
10665 }
10666}
10667
10668
10669static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10670 v8::HandleScope scope;
10671 v8::TryCatch tc;
10672 v8::Handle<v8::String> str = args[0]->ToString();
10673 if (tc.HasCaught())
10674 return tc.ReThrow();
10675 return v8::Undefined();
10676}
10677
10678
10679// Test that an exception can be propagated down through a spaghetti
10680// stack using ReThrow.
10681THREADED_TEST(SpaghettiStackReThrow) {
10682 v8::HandleScope scope;
10683 LocalContext context;
10684 context->Global()->Set(
10685 v8::String::New("s"),
10686 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10687 v8::TryCatch try_catch;
10688 CompileRun(
10689 "var i = 0;"
10690 "var o = {"
10691 " toString: function () {"
10692 " if (i == 10) {"
10693 " throw 'Hey!';"
10694 " } else {"
10695 " i++;"
10696 " return s(o);"
10697 " }"
10698 " }"
10699 "};"
10700 "s(o);");
10701 CHECK(try_catch.HasCaught());
10702 v8::String::Utf8Value value(try_catch.Exception());
10703 CHECK_EQ(0, strcmp(*value, "Hey!"));
10704}
10705
10706
Steve Blockd0582a62009-12-15 09:54:21 +000010707TEST(Regress528) {
10708 v8::V8::Initialize();
10709
10710 v8::HandleScope scope;
10711 v8::Persistent<Context> context;
10712 v8::Persistent<Context> other_context;
10713 int gc_count;
10714
10715 // Create a context used to keep the code from aging in the compilation
10716 // cache.
10717 other_context = Context::New();
10718
10719 // Context-dependent context data creates reference from the compilation
10720 // cache to the global object.
10721 const char* source_simple = "1";
10722 context = Context::New();
10723 {
10724 v8::HandleScope scope;
10725
10726 context->Enter();
10727 Local<v8::String> obj = v8::String::New("");
10728 context->SetData(obj);
10729 CompileRun(source_simple);
10730 context->Exit();
10731 }
10732 context.Dispose();
10733 for (gc_count = 1; gc_count < 10; gc_count++) {
10734 other_context->Enter();
10735 CompileRun(source_simple);
10736 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010737 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010738 if (GetGlobalObjectsCount() == 1) break;
10739 }
10740 CHECK_GE(2, gc_count);
10741 CHECK_EQ(1, GetGlobalObjectsCount());
10742
10743 // Eval in a function creates reference from the compilation cache to the
10744 // global object.
10745 const char* source_eval = "function f(){eval('1')}; f()";
10746 context = Context::New();
10747 {
10748 v8::HandleScope scope;
10749
10750 context->Enter();
10751 CompileRun(source_eval);
10752 context->Exit();
10753 }
10754 context.Dispose();
10755 for (gc_count = 1; gc_count < 10; gc_count++) {
10756 other_context->Enter();
10757 CompileRun(source_eval);
10758 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010759 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010760 if (GetGlobalObjectsCount() == 1) break;
10761 }
10762 CHECK_GE(2, gc_count);
10763 CHECK_EQ(1, GetGlobalObjectsCount());
10764
10765 // Looking up the line number for an exception creates reference from the
10766 // compilation cache to the global object.
10767 const char* source_exception = "function f(){throw 1;} f()";
10768 context = Context::New();
10769 {
10770 v8::HandleScope scope;
10771
10772 context->Enter();
10773 v8::TryCatch try_catch;
10774 CompileRun(source_exception);
10775 CHECK(try_catch.HasCaught());
10776 v8::Handle<v8::Message> message = try_catch.Message();
10777 CHECK(!message.IsEmpty());
10778 CHECK_EQ(1, message->GetLineNumber());
10779 context->Exit();
10780 }
10781 context.Dispose();
10782 for (gc_count = 1; gc_count < 10; gc_count++) {
10783 other_context->Enter();
10784 CompileRun(source_exception);
10785 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010786 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010787 if (GetGlobalObjectsCount() == 1) break;
10788 }
10789 CHECK_GE(2, gc_count);
10790 CHECK_EQ(1, GetGlobalObjectsCount());
10791
10792 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010793}
Andrei Popescu402d9372010-02-26 13:31:12 +000010794
10795
10796THREADED_TEST(ScriptOrigin) {
10797 v8::HandleScope scope;
10798 LocalContext env;
10799 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10800 v8::Handle<v8::String> script = v8::String::New(
10801 "function f() {}\n\nfunction g() {}");
10802 v8::Script::Compile(script, &origin)->Run();
10803 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10804 env->Global()->Get(v8::String::New("f")));
10805 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10806 env->Global()->Get(v8::String::New("g")));
10807
10808 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10809 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10810 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10811
10812 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10813 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10814 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10815}
10816
10817
10818THREADED_TEST(ScriptLineNumber) {
10819 v8::HandleScope scope;
10820 LocalContext env;
10821 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10822 v8::Handle<v8::String> script = v8::String::New(
10823 "function f() {}\n\nfunction g() {}");
10824 v8::Script::Compile(script, &origin)->Run();
10825 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10826 env->Global()->Get(v8::String::New("f")));
10827 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10828 env->Global()->Get(v8::String::New("g")));
10829 CHECK_EQ(0, f->GetScriptLineNumber());
10830 CHECK_EQ(2, g->GetScriptLineNumber());
10831}
10832
10833
10834static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10835 const AccessorInfo& info) {
10836 return v8_num(42);
10837}
10838
10839
10840static void SetterWhichSetsYOnThisTo23(Local<String> name,
10841 Local<Value> value,
10842 const AccessorInfo& info) {
10843 info.This()->Set(v8_str("y"), v8_num(23));
10844}
10845
10846
Steve Block6ded16b2010-05-10 14:33:55 +010010847TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010848 v8::HandleScope scope;
10849 Local<ObjectTemplate> templ = ObjectTemplate::New();
10850 templ->SetAccessor(v8_str("x"),
10851 GetterWhichReturns42,
10852 SetterWhichSetsYOnThisTo23);
10853 LocalContext context;
10854 context->Global()->Set(v8_str("P"), templ->NewInstance());
10855 CompileRun("function C1() {"
10856 " this.x = 23;"
10857 "};"
10858 "C1.prototype = P;"
10859 "function C2() {"
10860 " this.x = 23"
10861 "};"
10862 "C2.prototype = { };"
10863 "C2.prototype.__proto__ = P;");
10864
10865 v8::Local<v8::Script> script;
10866 script = v8::Script::Compile(v8_str("new C1();"));
10867 for (int i = 0; i < 10; i++) {
10868 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10869 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10870 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10871 }
10872
10873 script = v8::Script::Compile(v8_str("new C2();"));
10874 for (int i = 0; i < 10; i++) {
10875 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10876 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10877 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10878 }
10879}
10880
10881
10882static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10883 Local<String> name, const AccessorInfo& info) {
10884 return v8_num(42);
10885}
10886
10887
10888static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10889 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10890 if (name->Equals(v8_str("x"))) {
10891 info.This()->Set(v8_str("y"), v8_num(23));
10892 }
10893 return v8::Handle<Value>();
10894}
10895
10896
10897THREADED_TEST(InterceptorOnConstructorPrototype) {
10898 v8::HandleScope scope;
10899 Local<ObjectTemplate> templ = ObjectTemplate::New();
10900 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10901 NamedPropertySetterWhichSetsYOnThisTo23);
10902 LocalContext context;
10903 context->Global()->Set(v8_str("P"), templ->NewInstance());
10904 CompileRun("function C1() {"
10905 " this.x = 23;"
10906 "};"
10907 "C1.prototype = P;"
10908 "function C2() {"
10909 " this.x = 23"
10910 "};"
10911 "C2.prototype = { };"
10912 "C2.prototype.__proto__ = P;");
10913
10914 v8::Local<v8::Script> script;
10915 script = v8::Script::Compile(v8_str("new C1();"));
10916 for (int i = 0; i < 10; i++) {
10917 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10918 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10919 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10920 }
10921
10922 script = v8::Script::Compile(v8_str("new C2();"));
10923 for (int i = 0; i < 10; i++) {
10924 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10925 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10926 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10927 }
10928}
Steve Block6ded16b2010-05-10 14:33:55 +010010929
10930
10931TEST(Bug618) {
10932 const char* source = "function C1() {"
10933 " this.x = 23;"
10934 "};"
10935 "C1.prototype = P;";
10936
10937 v8::HandleScope scope;
10938 LocalContext context;
10939 v8::Local<v8::Script> script;
10940
10941 // Use a simple object as prototype.
10942 v8::Local<v8::Object> prototype = v8::Object::New();
10943 prototype->Set(v8_str("y"), v8_num(42));
10944 context->Global()->Set(v8_str("P"), prototype);
10945
10946 // This compile will add the code to the compilation cache.
10947 CompileRun(source);
10948
10949 script = v8::Script::Compile(v8_str("new C1();"));
10950 for (int i = 0; i < 10; i++) {
10951 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10952 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10953 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10954 }
10955
10956 // Use an API object with accessors as prototype.
10957 Local<ObjectTemplate> templ = ObjectTemplate::New();
10958 templ->SetAccessor(v8_str("x"),
10959 GetterWhichReturns42,
10960 SetterWhichSetsYOnThisTo23);
10961 context->Global()->Set(v8_str("P"), templ->NewInstance());
10962
10963 // This compile will get the code from the compilation cache.
10964 CompileRun(source);
10965
10966 script = v8::Script::Compile(v8_str("new C1();"));
10967 for (int i = 0; i < 10; i++) {
10968 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10969 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10970 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10971 }
10972}
10973
10974int prologue_call_count = 0;
10975int epilogue_call_count = 0;
10976int prologue_call_count_second = 0;
10977int epilogue_call_count_second = 0;
10978
10979void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
10980 ++prologue_call_count;
10981}
10982
10983void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
10984 ++epilogue_call_count;
10985}
10986
10987void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10988 ++prologue_call_count_second;
10989}
10990
10991void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10992 ++epilogue_call_count_second;
10993}
10994
10995TEST(GCCallbacks) {
10996 LocalContext context;
10997
10998 v8::V8::AddGCPrologueCallback(PrologueCallback);
10999 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
11000 CHECK_EQ(0, prologue_call_count);
11001 CHECK_EQ(0, epilogue_call_count);
11002 i::Heap::CollectAllGarbage(false);
11003 CHECK_EQ(1, prologue_call_count);
11004 CHECK_EQ(1, epilogue_call_count);
11005 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
11006 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
11007 i::Heap::CollectAllGarbage(false);
11008 CHECK_EQ(2, prologue_call_count);
11009 CHECK_EQ(2, epilogue_call_count);
11010 CHECK_EQ(1, prologue_call_count_second);
11011 CHECK_EQ(1, epilogue_call_count_second);
11012 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
11013 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
11014 i::Heap::CollectAllGarbage(false);
11015 CHECK_EQ(2, prologue_call_count);
11016 CHECK_EQ(2, epilogue_call_count);
11017 CHECK_EQ(2, prologue_call_count_second);
11018 CHECK_EQ(2, epilogue_call_count_second);
11019 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
11020 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
11021 i::Heap::CollectAllGarbage(false);
11022 CHECK_EQ(2, prologue_call_count);
11023 CHECK_EQ(2, epilogue_call_count);
11024 CHECK_EQ(2, prologue_call_count_second);
11025 CHECK_EQ(2, epilogue_call_count_second);
11026}
Kristian Monsen25f61362010-05-21 11:50:48 +010011027
11028
11029THREADED_TEST(AddToJSFunctionResultCache) {
11030 i::FLAG_allow_natives_syntax = true;
11031 v8::HandleScope scope;
11032
11033 LocalContext context;
11034
11035 const char* code =
11036 "(function() {"
11037 " var key0 = 'a';"
11038 " var key1 = 'b';"
11039 " var r0 = %_GetFromCache(0, key0);"
11040 " var r1 = %_GetFromCache(0, key1);"
11041 " var r0_ = %_GetFromCache(0, key0);"
11042 " if (r0 !== r0_)"
11043 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
11044 " var r1_ = %_GetFromCache(0, key1);"
11045 " if (r1 !== r1_)"
11046 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
11047 " return 'PASSED';"
11048 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011049 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011050 ExpectString(code, "PASSED");
11051}
11052
11053
11054static const int k0CacheSize = 16;
11055
11056THREADED_TEST(FillJSFunctionResultCache) {
11057 i::FLAG_allow_natives_syntax = true;
11058 v8::HandleScope scope;
11059
11060 LocalContext context;
11061
11062 const char* code =
11063 "(function() {"
11064 " var k = 'a';"
11065 " var r = %_GetFromCache(0, k);"
11066 " for (var i = 0; i < 16; i++) {"
11067 " %_GetFromCache(0, 'a' + i);"
11068 " };"
11069 " if (r === %_GetFromCache(0, k))"
11070 " return 'FAILED: k0CacheSize is too small';"
11071 " return 'PASSED';"
11072 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011073 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011074 ExpectString(code, "PASSED");
11075}
11076
11077
11078THREADED_TEST(RoundRobinGetFromCache) {
11079 i::FLAG_allow_natives_syntax = true;
11080 v8::HandleScope scope;
11081
11082 LocalContext context;
11083
11084 const char* code =
11085 "(function() {"
11086 " var keys = [];"
11087 " for (var i = 0; i < 16; i++) keys.push(i);"
11088 " var values = [];"
11089 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11090 " for (var i = 0; i < 16; i++) {"
11091 " var v = %_GetFromCache(0, keys[i]);"
11092 " if (v !== values[i])"
11093 " return 'Wrong value for ' + "
11094 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11095 " };"
11096 " return 'PASSED';"
11097 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011098 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011099 ExpectString(code, "PASSED");
11100}
11101
11102
11103THREADED_TEST(ReverseGetFromCache) {
11104 i::FLAG_allow_natives_syntax = true;
11105 v8::HandleScope scope;
11106
11107 LocalContext context;
11108
11109 const char* code =
11110 "(function() {"
11111 " var keys = [];"
11112 " for (var i = 0; i < 16; i++) keys.push(i);"
11113 " var values = [];"
11114 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11115 " for (var i = 15; i >= 16; i--) {"
11116 " var v = %_GetFromCache(0, keys[i]);"
11117 " if (v !== values[i])"
11118 " return 'Wrong value for ' + "
11119 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11120 " };"
11121 " return 'PASSED';"
11122 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011123 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011124 ExpectString(code, "PASSED");
11125}
11126
11127
11128THREADED_TEST(TestEviction) {
11129 i::FLAG_allow_natives_syntax = true;
11130 v8::HandleScope scope;
11131
11132 LocalContext context;
11133
11134 const char* code =
11135 "(function() {"
11136 " for (var i = 0; i < 2*16; i++) {"
11137 " %_GetFromCache(0, 'a' + i);"
11138 " };"
11139 " return 'PASSED';"
11140 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011141 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011142 ExpectString(code, "PASSED");
11143}
Steve Block8defd9f2010-07-08 12:39:36 +010011144
11145
11146THREADED_TEST(TwoByteStringInAsciiCons) {
11147 // See Chromium issue 47824.
11148 v8::HandleScope scope;
11149
11150 LocalContext context;
11151 const char* init_code =
11152 "var str1 = 'abelspendabel';"
11153 "var str2 = str1 + str1 + str1;"
11154 "str2;";
11155 Local<Value> result = CompileRun(init_code);
11156
11157 CHECK(result->IsString());
11158 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
11159 int length = string->length();
11160 CHECK(string->IsAsciiRepresentation());
11161
11162 FlattenString(string);
11163 i::Handle<i::String> flat_string = FlattenGetString(string);
11164
11165 CHECK(string->IsAsciiRepresentation());
11166 CHECK(flat_string->IsAsciiRepresentation());
11167
11168 // Create external resource.
11169 uint16_t* uc16_buffer = new uint16_t[length + 1];
11170
11171 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
11172 uc16_buffer[length] = 0;
11173
11174 TestResource resource(uc16_buffer);
11175
11176 flat_string->MakeExternal(&resource);
11177
11178 CHECK(flat_string->IsTwoByteRepresentation());
11179
11180 // At this point, we should have a Cons string which is flat and ASCII,
11181 // with a first half that is a two-byte string (although it only contains
11182 // ASCII characters). This is a valid sequence of steps, and it can happen
11183 // in real pages.
11184
11185 CHECK(string->IsAsciiRepresentation());
11186 i::ConsString* cons = i::ConsString::cast(*string);
11187 CHECK_EQ(0, cons->second()->length());
11188 CHECK(cons->first()->IsTwoByteRepresentation());
11189
11190 // Check that some string operations work.
11191
11192 // Atom RegExp.
11193 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
11194 CHECK_EQ(6, reresult->Int32Value());
11195
11196 // Nonatom RegExp.
11197 reresult = CompileRun("str2.match(/abe./g).length;");
11198 CHECK_EQ(6, reresult->Int32Value());
11199
11200 reresult = CompileRun("str2.search(/bel/g);");
11201 CHECK_EQ(1, reresult->Int32Value());
11202
11203 reresult = CompileRun("str2.search(/be./g);");
11204 CHECK_EQ(1, reresult->Int32Value());
11205
11206 ExpectTrue("/bel/g.test(str2);");
11207
11208 ExpectTrue("/be./g.test(str2);");
11209
11210 reresult = CompileRun("/bel/g.exec(str2);");
11211 CHECK(!reresult->IsNull());
11212
11213 reresult = CompileRun("/be./g.exec(str2);");
11214 CHECK(!reresult->IsNull());
11215
11216 ExpectString("str2.substring(2, 10);", "elspenda");
11217
11218 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
11219
11220 ExpectString("str2.charAt(2);", "e");
11221
11222 reresult = CompileRun("str2.charCodeAt(2);");
11223 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
11224}
Iain Merrick75681382010-08-19 15:07:18 +010011225
11226
11227// Failed access check callback that performs a GC on each invocation.
11228void FailedAccessCheckCallbackGC(Local<v8::Object> target,
11229 v8::AccessType type,
11230 Local<v8::Value> data) {
11231 i::Heap::CollectAllGarbage(true);
11232}
11233
11234
11235TEST(GCInFailedAccessCheckCallback) {
11236 // Install a failed access check callback that performs a GC on each
11237 // invocation. Then force the callback to be called from va
11238
11239 v8::V8::Initialize();
11240 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
11241
11242 v8::HandleScope scope;
11243
11244 // Create an ObjectTemplate for global objects and install access
11245 // check callbacks that will block access.
11246 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
11247 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
11248 IndexedGetAccessBlocker,
11249 v8::Handle<v8::Value>(),
11250 false);
11251
11252 // Create a context and set an x property on it's global object.
11253 LocalContext context0(NULL, global_template);
11254 context0->Global()->Set(v8_str("x"), v8_num(42));
11255 v8::Handle<v8::Object> global0 = context0->Global();
11256
11257 // Create a context with a different security token so that the
11258 // failed access check callback will be called on each access.
11259 LocalContext context1(NULL, global_template);
11260 context1->Global()->Set(v8_str("other"), global0);
11261
11262 // Get property with failed access check.
11263 ExpectUndefined("other.x");
11264
11265 // Get element with failed access check.
11266 ExpectUndefined("other[0]");
11267
11268 // Set property with failed access check.
11269 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
11270 CHECK(result->IsObject());
11271
11272 // Set element with failed access check.
11273 result = CompileRun("other[0] = new Object()");
11274 CHECK(result->IsObject());
11275
11276 // Get property attribute with failed access check.
11277 ExpectFalse("\'x\' in other");
11278
11279 // Get property attribute for element with failed access check.
11280 ExpectFalse("0 in other");
11281
11282 // Delete property.
11283 ExpectFalse("delete other.x");
11284
11285 // Delete element.
11286 CHECK_EQ(false, global0->Delete(0));
11287
11288 // DefineAccessor.
11289 CHECK_EQ(false,
11290 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
11291
11292 // Define JavaScript accessor.
11293 ExpectUndefined("Object.prototype.__defineGetter__.call("
11294 " other, \'x\', function() { return 42; })");
11295
11296 // LookupAccessor.
11297 ExpectUndefined("Object.prototype.__lookupGetter__.call("
11298 " other, \'x\')");
11299
11300 // HasLocalElement.
11301 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
11302
11303 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
11304 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
11305 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
11306
11307 // Reset the failed access check callback so it does not influence
11308 // the other tests.
11309 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
11310}