blob: 1b6359b40d09670145639d0946d27db33bd0bf2a [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
Kristian Monsen0d5e1162010-09-30 15:31:59 +01002940THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
2941 v8::HandleScope scope;
2942
2943 v8::Persistent<Context> context1 = Context::New();
2944
2945 context1->Enter();
2946 Local<ObjectTemplate> templ = ObjectTemplate::New();
2947 templ->SetNamedPropertyHandler(XPropertyGetter);
2948 // Create an object with a named interceptor.
2949 v8::Local<v8::Object> object = templ->NewInstance();
2950 context1->Global()->Set(v8_str("interceptor_obj"), object);
2951
2952 // Force the object into the slow case.
2953 CompileRun("interceptor_obj.y = 0;"
2954 "delete interceptor_obj.y;");
2955 context1->Exit();
2956
2957 {
2958 // Introduce the object into a different context.
2959 // Repeat named loads to exercise ICs.
2960 LocalContext context2;
2961 context2->Global()->Set(v8_str("interceptor_obj"), object);
2962 Local<Value> result =
2963 CompileRun("function get_x(o) { return o.x; }"
2964 "interceptor_obj.x = 42;"
2965 "for (var i=0; i != 10; i++) {"
2966 " get_x(interceptor_obj);"
2967 "}"
2968 "get_x(interceptor_obj)");
2969 // Check that the interceptor was actually invoked.
2970 CHECK_EQ(result, v8_str("x"));
2971 }
2972
2973 // Return to the original context and force some object to the slow case
2974 // to cause the NormalizedMapCache to verify.
2975 context1->Enter();
2976 CompileRun("var obj = { x : 0 }; delete obj.x;");
2977 context1->Exit();
2978
2979 context1.Dispose();
2980}
2981
2982
Andrei Popescu402d9372010-02-26 13:31:12 +00002983static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2984 const AccessorInfo& info) {
2985 // Set x on the prototype object and do not handle the get request.
2986 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002987 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002988 return v8::Handle<Value>();
2989}
2990
2991
2992// This is a regression test for http://crbug.com/20104. Map
2993// transitions should not interfere with post interceptor lookup.
2994THREADED_TEST(NamedInterceptorMapTransitionRead) {
2995 v8::HandleScope scope;
2996 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
2997 Local<v8::ObjectTemplate> instance_template
2998 = function_template->InstanceTemplate();
2999 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
3000 LocalContext context;
3001 context->Global()->Set(v8_str("F"), function_template->GetFunction());
3002 // Create an instance of F and introduce a map transition for x.
3003 CompileRun("var o = new F(); o.x = 23;");
3004 // Create an instance of F and invoke the getter. The result should be 23.
3005 Local<Value> result = CompileRun("o = new F(); o.x");
3006 CHECK_EQ(result->Int32Value(), 23);
3007}
3008
3009
Steve Blocka7e24c12009-10-30 11:49:00 +00003010static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
3011 const AccessorInfo& info) {
3012 ApiTestFuzzer::Fuzz();
3013 if (index == 37) {
3014 return v8::Handle<Value>(v8_num(625));
3015 }
3016 return v8::Handle<Value>();
3017}
3018
3019
3020static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
3021 Local<Value> value,
3022 const AccessorInfo& info) {
3023 ApiTestFuzzer::Fuzz();
3024 if (index == 39) {
3025 return value;
3026 }
3027 return v8::Handle<Value>();
3028}
3029
3030
3031THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
3032 v8::HandleScope scope;
3033 Local<ObjectTemplate> templ = ObjectTemplate::New();
3034 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
3035 IndexedPropertySetter);
3036 LocalContext context;
3037 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3038 Local<Script> getter_script = Script::Compile(v8_str(
3039 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
3040 Local<Script> setter_script = Script::Compile(v8_str(
3041 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
3042 "obj[17] = 23;"
3043 "obj.foo;"));
3044 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3045 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3046 "obj[39] = 47;"
3047 "obj.foo;")); // This setter should not run, due to the interceptor.
3048 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3049 "obj[37];"));
3050 Local<Value> result = getter_script->Run();
3051 CHECK_EQ(v8_num(5), result);
3052 result = setter_script->Run();
3053 CHECK_EQ(v8_num(23), result);
3054 result = interceptor_setter_script->Run();
3055 CHECK_EQ(v8_num(23), result);
3056 result = interceptor_getter_script->Run();
3057 CHECK_EQ(v8_num(625), result);
3058}
3059
3060
Leon Clarked91b9f72010-01-27 17:25:45 +00003061static v8::Handle<Value> IdentityIndexedPropertyGetter(
3062 uint32_t index,
3063 const AccessorInfo& info) {
3064 return v8::Integer::New(index);
3065}
3066
3067
Kristian Monsen0d5e1162010-09-30 15:31:59 +01003068THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
3069 v8::HandleScope scope;
3070 Local<ObjectTemplate> templ = ObjectTemplate::New();
3071 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3072
3073 LocalContext context;
3074 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3075
3076 // Check fast object case.
3077 const char* fast_case_code =
3078 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()";
3079 ExpectString(fast_case_code, "0");
3080
3081 // Check slow case.
3082 const char* slow_case_code =
3083 "obj.x = 1; delete obj.x;"
3084 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()";
3085 ExpectString(slow_case_code, "1");
3086}
3087
3088
Leon Clarked91b9f72010-01-27 17:25:45 +00003089THREADED_TEST(IndexedInterceptorWithNoSetter) {
3090 v8::HandleScope scope;
3091 Local<ObjectTemplate> templ = ObjectTemplate::New();
3092 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3093
3094 LocalContext context;
3095 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3096
3097 const char* code =
3098 "try {"
3099 " obj[0] = 239;"
3100 " for (var i = 0; i < 100; i++) {"
3101 " var v = obj[0];"
3102 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3103 " }"
3104 " 'PASSED'"
3105 "} catch(e) {"
3106 " e"
3107 "}";
3108 ExpectString(code, "PASSED");
3109}
3110
3111
Andrei Popescu402d9372010-02-26 13:31:12 +00003112THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3113 v8::HandleScope scope;
3114 Local<ObjectTemplate> templ = ObjectTemplate::New();
3115 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3116
3117 LocalContext context;
3118 Local<v8::Object> obj = templ->NewInstance();
3119 obj->TurnOnAccessCheck();
3120 context->Global()->Set(v8_str("obj"), obj);
3121
3122 const char* code =
3123 "try {"
3124 " for (var i = 0; i < 100; i++) {"
3125 " var v = obj[0];"
3126 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3127 " }"
3128 " 'PASSED'"
3129 "} catch(e) {"
3130 " e"
3131 "}";
3132 ExpectString(code, "PASSED");
3133}
3134
3135
3136THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3137 i::FLAG_allow_natives_syntax = true;
3138 v8::HandleScope scope;
3139 Local<ObjectTemplate> templ = ObjectTemplate::New();
3140 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3141
3142 LocalContext context;
3143 Local<v8::Object> obj = templ->NewInstance();
3144 context->Global()->Set(v8_str("obj"), obj);
3145
3146 const char* code =
3147 "try {"
3148 " for (var i = 0; i < 100; i++) {"
3149 " var expected = i;"
3150 " if (i == 5) {"
3151 " %EnableAccessChecks(obj);"
3152 " expected = undefined;"
3153 " }"
3154 " var v = obj[i];"
3155 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3156 " if (i == 5) %DisableAccessChecks(obj);"
3157 " }"
3158 " 'PASSED'"
3159 "} catch(e) {"
3160 " e"
3161 "}";
3162 ExpectString(code, "PASSED");
3163}
3164
3165
3166THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3167 v8::HandleScope scope;
3168 Local<ObjectTemplate> templ = ObjectTemplate::New();
3169 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3170
3171 LocalContext context;
3172 Local<v8::Object> obj = templ->NewInstance();
3173 context->Global()->Set(v8_str("obj"), obj);
3174
3175 const char* code =
3176 "try {"
3177 " for (var i = 0; i < 100; i++) {"
3178 " var v = obj[i];"
3179 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3180 " }"
3181 " 'PASSED'"
3182 "} catch(e) {"
3183 " e"
3184 "}";
3185 ExpectString(code, "PASSED");
3186}
3187
3188
3189THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3190 v8::HandleScope scope;
3191 Local<ObjectTemplate> templ = ObjectTemplate::New();
3192 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3193
3194 LocalContext context;
3195 Local<v8::Object> obj = templ->NewInstance();
3196 context->Global()->Set(v8_str("obj"), obj);
3197
3198 const char* code =
3199 "try {"
3200 " for (var i = 0; i < 100; i++) {"
3201 " var expected = i;"
3202 " if (i == 50) {"
3203 " i = 'foobar';"
3204 " expected = undefined;"
3205 " }"
3206 " var v = obj[i];"
3207 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3208 " }"
3209 " 'PASSED'"
3210 "} catch(e) {"
3211 " e"
3212 "}";
3213 ExpectString(code, "PASSED");
3214}
3215
3216
3217THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3218 v8::HandleScope scope;
3219 Local<ObjectTemplate> templ = ObjectTemplate::New();
3220 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3221
3222 LocalContext context;
3223 Local<v8::Object> obj = templ->NewInstance();
3224 context->Global()->Set(v8_str("obj"), obj);
3225
3226 const char* code =
3227 "var original = obj;"
3228 "try {"
3229 " for (var i = 0; i < 100; i++) {"
3230 " var expected = i;"
3231 " if (i == 50) {"
3232 " obj = {50: 'foobar'};"
3233 " expected = 'foobar';"
3234 " }"
3235 " var v = obj[i];"
3236 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3237 " if (i == 50) obj = original;"
3238 " }"
3239 " 'PASSED'"
3240 "} catch(e) {"
3241 " e"
3242 "}";
3243 ExpectString(code, "PASSED");
3244}
3245
3246
3247THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3248 v8::HandleScope scope;
3249 Local<ObjectTemplate> templ = ObjectTemplate::New();
3250 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3251
3252 LocalContext context;
3253 Local<v8::Object> obj = templ->NewInstance();
3254 context->Global()->Set(v8_str("obj"), obj);
3255
3256 const char* code =
3257 "var original = obj;"
3258 "try {"
3259 " for (var i = 0; i < 100; i++) {"
3260 " var expected = i;"
3261 " if (i == 5) {"
3262 " obj = 239;"
3263 " expected = undefined;"
3264 " }"
3265 " var v = obj[i];"
3266 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3267 " if (i == 5) obj = original;"
3268 " }"
3269 " 'PASSED'"
3270 "} catch(e) {"
3271 " e"
3272 "}";
3273 ExpectString(code, "PASSED");
3274}
3275
3276
3277THREADED_TEST(IndexedInterceptorOnProto) {
3278 v8::HandleScope scope;
3279 Local<ObjectTemplate> templ = ObjectTemplate::New();
3280 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3281
3282 LocalContext context;
3283 Local<v8::Object> obj = templ->NewInstance();
3284 context->Global()->Set(v8_str("obj"), obj);
3285
3286 const char* code =
3287 "var o = {__proto__: obj};"
3288 "try {"
3289 " for (var i = 0; i < 100; i++) {"
3290 " var v = o[i];"
3291 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3292 " }"
3293 " 'PASSED'"
3294 "} catch(e) {"
3295 " e"
3296 "}";
3297 ExpectString(code, "PASSED");
3298}
3299
3300
Steve Blocka7e24c12009-10-30 11:49:00 +00003301THREADED_TEST(MultiContexts) {
3302 v8::HandleScope scope;
3303 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3304 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3305
3306 Local<String> password = v8_str("Password");
3307
3308 // Create an environment
3309 LocalContext context0(0, templ);
3310 context0->SetSecurityToken(password);
3311 v8::Handle<v8::Object> global0 = context0->Global();
3312 global0->Set(v8_str("custom"), v8_num(1234));
3313 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3314
3315 // Create an independent environment
3316 LocalContext context1(0, templ);
3317 context1->SetSecurityToken(password);
3318 v8::Handle<v8::Object> global1 = context1->Global();
3319 global1->Set(v8_str("custom"), v8_num(1234));
3320 CHECK_NE(global0, global1);
3321 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3322 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3323
3324 // Now create a new context with the old global
3325 LocalContext context2(0, templ, global1);
3326 context2->SetSecurityToken(password);
3327 v8::Handle<v8::Object> global2 = context2->Global();
3328 CHECK_EQ(global1, global2);
3329 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3330 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3331}
3332
3333
3334THREADED_TEST(FunctionPrototypeAcrossContexts) {
3335 // Make sure that functions created by cloning boilerplates cannot
3336 // communicate through their __proto__ field.
3337
3338 v8::HandleScope scope;
3339
3340 LocalContext env0;
3341 v8::Handle<v8::Object> global0 =
3342 env0->Global();
3343 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003344 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003345 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003346 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003347 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003348 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003349 proto0->Set(v8_str("custom"), v8_num(1234));
3350
3351 LocalContext env1;
3352 v8::Handle<v8::Object> global1 =
3353 env1->Global();
3354 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003355 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003356 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003357 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003358 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003359 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003360 CHECK(!proto1->Has(v8_str("custom")));
3361}
3362
3363
3364THREADED_TEST(Regress892105) {
3365 // Make sure that object and array literals created by cloning
3366 // boilerplates cannot communicate through their __proto__
3367 // field. This is rather difficult to check, but we try to add stuff
3368 // to Object.prototype and Array.prototype and create a new
3369 // environment. This should succeed.
3370
3371 v8::HandleScope scope;
3372
3373 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3374 "Array.prototype.arr = 4567;"
3375 "8901");
3376
3377 LocalContext env0;
3378 Local<Script> script0 = Script::Compile(source);
3379 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3380
3381 LocalContext env1;
3382 Local<Script> script1 = Script::Compile(source);
3383 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3384}
3385
3386
Steve Blocka7e24c12009-10-30 11:49:00 +00003387THREADED_TEST(UndetectableObject) {
3388 v8::HandleScope scope;
3389 LocalContext env;
3390
3391 Local<v8::FunctionTemplate> desc =
3392 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3393 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3394
3395 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3396 env->Global()->Set(v8_str("undetectable"), obj);
3397
3398 ExpectString("undetectable.toString()", "[object Object]");
3399 ExpectString("typeof undetectable", "undefined");
3400 ExpectString("typeof(undetectable)", "undefined");
3401 ExpectBoolean("typeof undetectable == 'undefined'", true);
3402 ExpectBoolean("typeof undetectable == 'object'", false);
3403 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3404 ExpectBoolean("!undetectable", true);
3405
3406 ExpectObject("true&&undetectable", obj);
3407 ExpectBoolean("false&&undetectable", false);
3408 ExpectBoolean("true||undetectable", true);
3409 ExpectObject("false||undetectable", obj);
3410
3411 ExpectObject("undetectable&&true", obj);
3412 ExpectObject("undetectable&&false", obj);
3413 ExpectBoolean("undetectable||true", true);
3414 ExpectBoolean("undetectable||false", false);
3415
3416 ExpectBoolean("undetectable==null", true);
3417 ExpectBoolean("null==undetectable", true);
3418 ExpectBoolean("undetectable==undefined", true);
3419 ExpectBoolean("undefined==undetectable", true);
3420 ExpectBoolean("undetectable==undetectable", true);
3421
3422
3423 ExpectBoolean("undetectable===null", false);
3424 ExpectBoolean("null===undetectable", false);
3425 ExpectBoolean("undetectable===undefined", false);
3426 ExpectBoolean("undefined===undetectable", false);
3427 ExpectBoolean("undetectable===undetectable", true);
3428}
3429
3430
Steve Block8defd9f2010-07-08 12:39:36 +01003431
3432THREADED_TEST(ExtensibleOnUndetectable) {
3433 v8::HandleScope scope;
3434 LocalContext env;
3435
3436 Local<v8::FunctionTemplate> desc =
3437 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3438 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3439
3440 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3441 env->Global()->Set(v8_str("undetectable"), obj);
3442
3443 Local<String> source = v8_str("undetectable.x = 42;"
3444 "undetectable.x");
3445
3446 Local<Script> script = Script::Compile(source);
3447
3448 CHECK_EQ(v8::Integer::New(42), script->Run());
3449
3450 ExpectBoolean("Object.isExtensible(undetectable)", true);
3451
3452 source = v8_str("Object.preventExtensions(undetectable);");
3453 script = Script::Compile(source);
3454 script->Run();
3455 ExpectBoolean("Object.isExtensible(undetectable)", false);
3456
3457 source = v8_str("undetectable.y = 2000;");
3458 script = Script::Compile(source);
3459 v8::TryCatch try_catch;
3460 Local<Value> result = script->Run();
3461 CHECK(result.IsEmpty());
3462 CHECK(try_catch.HasCaught());
3463}
3464
3465
3466
Steve Blocka7e24c12009-10-30 11:49:00 +00003467THREADED_TEST(UndetectableString) {
3468 v8::HandleScope scope;
3469 LocalContext env;
3470
3471 Local<String> obj = String::NewUndetectable("foo");
3472 env->Global()->Set(v8_str("undetectable"), obj);
3473
3474 ExpectString("undetectable", "foo");
3475 ExpectString("typeof undetectable", "undefined");
3476 ExpectString("typeof(undetectable)", "undefined");
3477 ExpectBoolean("typeof undetectable == 'undefined'", true);
3478 ExpectBoolean("typeof undetectable == 'string'", false);
3479 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3480 ExpectBoolean("!undetectable", true);
3481
3482 ExpectObject("true&&undetectable", obj);
3483 ExpectBoolean("false&&undetectable", false);
3484 ExpectBoolean("true||undetectable", true);
3485 ExpectObject("false||undetectable", obj);
3486
3487 ExpectObject("undetectable&&true", obj);
3488 ExpectObject("undetectable&&false", obj);
3489 ExpectBoolean("undetectable||true", true);
3490 ExpectBoolean("undetectable||false", false);
3491
3492 ExpectBoolean("undetectable==null", true);
3493 ExpectBoolean("null==undetectable", true);
3494 ExpectBoolean("undetectable==undefined", true);
3495 ExpectBoolean("undefined==undetectable", true);
3496 ExpectBoolean("undetectable==undetectable", true);
3497
3498
3499 ExpectBoolean("undetectable===null", false);
3500 ExpectBoolean("null===undetectable", false);
3501 ExpectBoolean("undetectable===undefined", false);
3502 ExpectBoolean("undefined===undetectable", false);
3503 ExpectBoolean("undetectable===undetectable", true);
3504}
3505
3506
3507template <typename T> static void USE(T) { }
3508
3509
3510// This test is not intended to be run, just type checked.
3511static void PersistentHandles() {
3512 USE(PersistentHandles);
3513 Local<String> str = v8_str("foo");
3514 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3515 USE(p_str);
3516 Local<Script> scr = Script::Compile(v8_str(""));
3517 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3518 USE(p_scr);
3519 Local<ObjectTemplate> templ = ObjectTemplate::New();
3520 v8::Persistent<ObjectTemplate> p_templ =
3521 v8::Persistent<ObjectTemplate>::New(templ);
3522 USE(p_templ);
3523}
3524
3525
3526static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3527 ApiTestFuzzer::Fuzz();
3528 return v8::Undefined();
3529}
3530
3531
3532THREADED_TEST(GlobalObjectTemplate) {
3533 v8::HandleScope handle_scope;
3534 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3535 global_template->Set(v8_str("JSNI_Log"),
3536 v8::FunctionTemplate::New(HandleLogDelegator));
3537 v8::Persistent<Context> context = Context::New(0, global_template);
3538 Context::Scope context_scope(context);
3539 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3540 context.Dispose();
3541}
3542
3543
3544static const char* kSimpleExtensionSource =
3545 "function Foo() {"
3546 " return 4;"
3547 "}";
3548
3549
3550THREADED_TEST(SimpleExtensions) {
3551 v8::HandleScope handle_scope;
3552 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3553 const char* extension_names[] = { "simpletest" };
3554 v8::ExtensionConfiguration extensions(1, extension_names);
3555 v8::Handle<Context> context = Context::New(&extensions);
3556 Context::Scope lock(context);
3557 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3558 CHECK_EQ(result, v8::Integer::New(4));
3559}
3560
3561
3562static const char* kEvalExtensionSource1 =
3563 "function UseEval1() {"
3564 " var x = 42;"
3565 " return eval('x');"
3566 "}";
3567
3568
3569static const char* kEvalExtensionSource2 =
3570 "(function() {"
3571 " var x = 42;"
3572 " function e() {"
3573 " return eval('x');"
3574 " }"
3575 " this.UseEval2 = e;"
3576 "})()";
3577
3578
3579THREADED_TEST(UseEvalFromExtension) {
3580 v8::HandleScope handle_scope;
3581 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3582 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3583 const char* extension_names[] = { "evaltest1", "evaltest2" };
3584 v8::ExtensionConfiguration extensions(2, extension_names);
3585 v8::Handle<Context> context = Context::New(&extensions);
3586 Context::Scope lock(context);
3587 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3588 CHECK_EQ(result, v8::Integer::New(42));
3589 result = Script::Compile(v8_str("UseEval2()"))->Run();
3590 CHECK_EQ(result, v8::Integer::New(42));
3591}
3592
3593
3594static const char* kWithExtensionSource1 =
3595 "function UseWith1() {"
3596 " var x = 42;"
3597 " with({x:87}) { return x; }"
3598 "}";
3599
3600
3601
3602static const char* kWithExtensionSource2 =
3603 "(function() {"
3604 " var x = 42;"
3605 " function e() {"
3606 " with ({x:87}) { return x; }"
3607 " }"
3608 " this.UseWith2 = e;"
3609 "})()";
3610
3611
3612THREADED_TEST(UseWithFromExtension) {
3613 v8::HandleScope handle_scope;
3614 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3615 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3616 const char* extension_names[] = { "withtest1", "withtest2" };
3617 v8::ExtensionConfiguration extensions(2, extension_names);
3618 v8::Handle<Context> context = Context::New(&extensions);
3619 Context::Scope lock(context);
3620 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3621 CHECK_EQ(result, v8::Integer::New(87));
3622 result = Script::Compile(v8_str("UseWith2()"))->Run();
3623 CHECK_EQ(result, v8::Integer::New(87));
3624}
3625
3626
3627THREADED_TEST(AutoExtensions) {
3628 v8::HandleScope handle_scope;
3629 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3630 extension->set_auto_enable(true);
3631 v8::RegisterExtension(extension);
3632 v8::Handle<Context> context = Context::New();
3633 Context::Scope lock(context);
3634 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3635 CHECK_EQ(result, v8::Integer::New(4));
3636}
3637
3638
Steve Blockd0582a62009-12-15 09:54:21 +00003639static const char* kSyntaxErrorInExtensionSource =
3640 "[";
3641
3642
3643// Test that a syntax error in an extension does not cause a fatal
3644// error but results in an empty context.
3645THREADED_TEST(SyntaxErrorExtensions) {
3646 v8::HandleScope handle_scope;
3647 v8::RegisterExtension(new Extension("syntaxerror",
3648 kSyntaxErrorInExtensionSource));
3649 const char* extension_names[] = { "syntaxerror" };
3650 v8::ExtensionConfiguration extensions(1, extension_names);
3651 v8::Handle<Context> context = Context::New(&extensions);
3652 CHECK(context.IsEmpty());
3653}
3654
3655
3656static const char* kExceptionInExtensionSource =
3657 "throw 42";
3658
3659
3660// Test that an exception when installing an extension does not cause
3661// a fatal error but results in an empty context.
3662THREADED_TEST(ExceptionExtensions) {
3663 v8::HandleScope handle_scope;
3664 v8::RegisterExtension(new Extension("exception",
3665 kExceptionInExtensionSource));
3666 const char* extension_names[] = { "exception" };
3667 v8::ExtensionConfiguration extensions(1, extension_names);
3668 v8::Handle<Context> context = Context::New(&extensions);
3669 CHECK(context.IsEmpty());
3670}
3671
3672
Iain Merrick9ac36c92010-09-13 15:29:50 +01003673static const char* kNativeCallInExtensionSource =
3674 "function call_runtime_last_index_of(x) {"
3675 " return %StringLastIndexOf(x, 'bob', 10);"
3676 "}";
3677
3678
3679static const char* kNativeCallTest =
3680 "call_runtime_last_index_of('bobbobboellebobboellebobbob');";
3681
3682// Test that a native runtime calls are supported in extensions.
3683THREADED_TEST(NativeCallInExtensions) {
3684 v8::HandleScope handle_scope;
3685 v8::RegisterExtension(new Extension("nativecall",
3686 kNativeCallInExtensionSource));
3687 const char* extension_names[] = { "nativecall" };
3688 v8::ExtensionConfiguration extensions(1, extension_names);
3689 v8::Handle<Context> context = Context::New(&extensions);
3690 Context::Scope lock(context);
3691 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run();
3692 CHECK_EQ(result, v8::Integer::New(3));
3693}
3694
3695
Steve Blocka7e24c12009-10-30 11:49:00 +00003696static void CheckDependencies(const char* name, const char* expected) {
3697 v8::HandleScope handle_scope;
3698 v8::ExtensionConfiguration config(1, &name);
3699 LocalContext context(&config);
3700 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3701}
3702
3703
3704/*
3705 * Configuration:
3706 *
3707 * /-- B <--\
3708 * A <- -- D <-- E
3709 * \-- C <--/
3710 */
3711THREADED_TEST(ExtensionDependency) {
3712 static const char* kEDeps[] = { "D" };
3713 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3714 static const char* kDDeps[] = { "B", "C" };
3715 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3716 static const char* kBCDeps[] = { "A" };
3717 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3718 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3719 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3720 CheckDependencies("A", "undefinedA");
3721 CheckDependencies("B", "undefinedAB");
3722 CheckDependencies("C", "undefinedAC");
3723 CheckDependencies("D", "undefinedABCD");
3724 CheckDependencies("E", "undefinedABCDE");
3725 v8::HandleScope handle_scope;
3726 static const char* exts[2] = { "C", "E" };
3727 v8::ExtensionConfiguration config(2, exts);
3728 LocalContext context(&config);
3729 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3730}
3731
3732
3733static const char* kExtensionTestScript =
3734 "native function A();"
3735 "native function B();"
3736 "native function C();"
3737 "function Foo(i) {"
3738 " if (i == 0) return A();"
3739 " if (i == 1) return B();"
3740 " if (i == 2) return C();"
3741 "}";
3742
3743
3744static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3745 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003746 if (args.IsConstructCall()) {
3747 args.This()->Set(v8_str("data"), args.Data());
3748 return v8::Null();
3749 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003750 return args.Data();
3751}
3752
3753
3754class FunctionExtension : public Extension {
3755 public:
3756 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3757 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3758 v8::Handle<String> name);
3759};
3760
3761
3762static int lookup_count = 0;
3763v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3764 v8::Handle<String> name) {
3765 lookup_count++;
3766 if (name->Equals(v8_str("A"))) {
3767 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3768 } else if (name->Equals(v8_str("B"))) {
3769 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3770 } else if (name->Equals(v8_str("C"))) {
3771 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3772 } else {
3773 return v8::Handle<v8::FunctionTemplate>();
3774 }
3775}
3776
3777
3778THREADED_TEST(FunctionLookup) {
3779 v8::RegisterExtension(new FunctionExtension());
3780 v8::HandleScope handle_scope;
3781 static const char* exts[1] = { "functiontest" };
3782 v8::ExtensionConfiguration config(1, exts);
3783 LocalContext context(&config);
3784 CHECK_EQ(3, lookup_count);
3785 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3786 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3787 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3788}
3789
3790
Leon Clarkee46be812010-01-19 14:06:41 +00003791THREADED_TEST(NativeFunctionConstructCall) {
3792 v8::RegisterExtension(new FunctionExtension());
3793 v8::HandleScope handle_scope;
3794 static const char* exts[1] = { "functiontest" };
3795 v8::ExtensionConfiguration config(1, exts);
3796 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003797 for (int i = 0; i < 10; i++) {
3798 // Run a few times to ensure that allocation of objects doesn't
3799 // change behavior of a constructor function.
3800 CHECK_EQ(v8::Integer::New(8),
3801 Script::Compile(v8_str("(new A()).data"))->Run());
3802 CHECK_EQ(v8::Integer::New(7),
3803 Script::Compile(v8_str("(new B()).data"))->Run());
3804 CHECK_EQ(v8::Integer::New(6),
3805 Script::Compile(v8_str("(new C()).data"))->Run());
3806 }
Leon Clarkee46be812010-01-19 14:06:41 +00003807}
3808
3809
Steve Blocka7e24c12009-10-30 11:49:00 +00003810static const char* last_location;
3811static const char* last_message;
3812void StoringErrorCallback(const char* location, const char* message) {
3813 if (last_location == NULL) {
3814 last_location = location;
3815 last_message = message;
3816 }
3817}
3818
3819
3820// ErrorReporting creates a circular extensions configuration and
3821// tests that the fatal error handler gets called. This renders V8
3822// unusable and therefore this test cannot be run in parallel.
3823TEST(ErrorReporting) {
3824 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3825 static const char* aDeps[] = { "B" };
3826 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3827 static const char* bDeps[] = { "A" };
3828 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3829 last_location = NULL;
3830 v8::ExtensionConfiguration config(1, bDeps);
3831 v8::Handle<Context> context = Context::New(&config);
3832 CHECK(context.IsEmpty());
3833 CHECK_NE(last_location, NULL);
3834}
3835
3836
3837static const char* js_code_causing_huge_string_flattening =
3838 "var str = 'X';"
3839 "for (var i = 0; i < 30; i++) {"
3840 " str = str + str;"
3841 "}"
3842 "str.match(/X/);";
3843
3844
3845void OOMCallback(const char* location, const char* message) {
3846 exit(0);
3847}
3848
3849
3850TEST(RegexpOutOfMemory) {
3851 // Execute a script that causes out of memory when flattening a string.
3852 v8::HandleScope scope;
3853 v8::V8::SetFatalErrorHandler(OOMCallback);
3854 LocalContext context;
3855 Local<Script> script =
3856 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3857 last_location = NULL;
3858 Local<Value> result = script->Run();
3859
3860 CHECK(false); // Should not return.
3861}
3862
3863
3864static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3865 v8::Handle<Value> data) {
3866 CHECK_EQ(v8::Undefined(), data);
3867 CHECK(message->GetScriptResourceName()->IsUndefined());
3868 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3869 message->GetLineNumber();
3870 message->GetSourceLine();
3871}
3872
3873
3874THREADED_TEST(ErrorWithMissingScriptInfo) {
3875 v8::HandleScope scope;
3876 LocalContext context;
3877 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3878 Script::Compile(v8_str("throw Error()"))->Run();
3879 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3880}
3881
3882
3883int global_index = 0;
3884
3885class Snorkel {
3886 public:
3887 Snorkel() { index_ = global_index++; }
3888 int index_;
3889};
3890
3891class Whammy {
3892 public:
3893 Whammy() {
3894 cursor_ = 0;
3895 }
3896 ~Whammy() {
3897 script_.Dispose();
3898 }
3899 v8::Handle<Script> getScript() {
3900 if (script_.IsEmpty())
3901 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3902 return Local<Script>(*script_);
3903 }
3904
3905 public:
3906 static const int kObjectCount = 256;
3907 int cursor_;
3908 v8::Persistent<v8::Object> objects_[kObjectCount];
3909 v8::Persistent<Script> script_;
3910};
3911
3912static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3913 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3914 delete snorkel;
3915 obj.ClearWeak();
3916}
3917
3918v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3919 const AccessorInfo& info) {
3920 Whammy* whammy =
3921 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3922
3923 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3924
3925 v8::Handle<v8::Object> obj = v8::Object::New();
3926 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3927 if (!prev.IsEmpty()) {
3928 prev->Set(v8_str("next"), obj);
3929 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3930 whammy->objects_[whammy->cursor_].Clear();
3931 }
3932 whammy->objects_[whammy->cursor_] = global;
3933 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3934 return whammy->getScript()->Run();
3935}
3936
3937THREADED_TEST(WeakReference) {
3938 v8::HandleScope handle_scope;
3939 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003940 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00003941 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3942 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003943 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00003944 const char* extension_list[] = { "v8/gc" };
3945 v8::ExtensionConfiguration extensions(1, extension_list);
3946 v8::Persistent<Context> context = Context::New(&extensions);
3947 Context::Scope context_scope(context);
3948
3949 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3950 context->Global()->Set(v8_str("whammy"), interceptor);
3951 const char* code =
3952 "var last;"
3953 "for (var i = 0; i < 10000; i++) {"
3954 " var obj = whammy.length;"
3955 " if (last) last.next = obj;"
3956 " last = obj;"
3957 "}"
3958 "gc();"
3959 "4";
3960 v8::Handle<Value> result = CompileRun(code);
3961 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003962 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00003963 context.Dispose();
3964}
3965
3966
Steve Blockd0582a62009-12-15 09:54:21 +00003967static bool in_scavenge = false;
3968static int last = -1;
3969
3970static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3971 CHECK_EQ(-1, last);
3972 last = 0;
3973 obj.Dispose();
3974 obj.Clear();
3975 in_scavenge = true;
3976 i::Heap::PerformScavenge();
3977 in_scavenge = false;
3978 *(reinterpret_cast<bool*>(data)) = true;
3979}
3980
3981static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3982 void* data) {
3983 CHECK_EQ(0, last);
3984 last = 1;
3985 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3986 obj.Dispose();
3987 obj.Clear();
3988}
3989
3990THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3991 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3992 // Calling callbacks from scavenges is unsafe as objects held by those
3993 // handlers might have become strongly reachable, but scavenge doesn't
3994 // check that.
3995 v8::Persistent<Context> context = Context::New();
3996 Context::Scope context_scope(context);
3997
3998 v8::Persistent<v8::Object> object_a;
3999 v8::Persistent<v8::Object> object_b;
4000
4001 {
4002 v8::HandleScope handle_scope;
4003 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
4004 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
4005 }
4006
4007 bool object_a_disposed = false;
4008 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
4009 bool released_in_scavenge = false;
4010 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
4011
4012 while (!object_a_disposed) {
4013 i::Heap::CollectAllGarbage(false);
4014 }
4015 CHECK(!released_in_scavenge);
4016}
4017
4018
Steve Blocka7e24c12009-10-30 11:49:00 +00004019v8::Handle<Function> args_fun;
4020
4021
4022static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
4023 ApiTestFuzzer::Fuzz();
4024 CHECK_EQ(args_fun, args.Callee());
4025 CHECK_EQ(3, args.Length());
4026 CHECK_EQ(v8::Integer::New(1), args[0]);
4027 CHECK_EQ(v8::Integer::New(2), args[1]);
4028 CHECK_EQ(v8::Integer::New(3), args[2]);
4029 CHECK_EQ(v8::Undefined(), args[3]);
4030 v8::HandleScope scope;
4031 i::Heap::CollectAllGarbage(false);
4032 return v8::Undefined();
4033}
4034
4035
4036THREADED_TEST(Arguments) {
4037 v8::HandleScope scope;
4038 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
4039 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
4040 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01004041 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00004042 v8_compile("f(1, 2, 3)")->Run();
4043}
4044
4045
Steve Blocka7e24c12009-10-30 11:49:00 +00004046static v8::Handle<Value> NoBlockGetterX(Local<String> name,
4047 const AccessorInfo&) {
4048 return v8::Handle<Value>();
4049}
4050
4051
4052static v8::Handle<Value> NoBlockGetterI(uint32_t index,
4053 const AccessorInfo&) {
4054 return v8::Handle<Value>();
4055}
4056
4057
4058static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
4059 const AccessorInfo&) {
4060 if (!name->Equals(v8_str("foo"))) {
4061 return v8::Handle<v8::Boolean>(); // not intercepted
4062 }
4063
4064 return v8::False(); // intercepted, and don't delete the property
4065}
4066
4067
4068static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
4069 if (index != 2) {
4070 return v8::Handle<v8::Boolean>(); // not intercepted
4071 }
4072
4073 return v8::False(); // intercepted, and don't delete the property
4074}
4075
4076
4077THREADED_TEST(Deleter) {
4078 v8::HandleScope scope;
4079 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4080 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
4081 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
4082 LocalContext context;
4083 context->Global()->Set(v8_str("k"), obj->NewInstance());
4084 CompileRun(
4085 "k.foo = 'foo';"
4086 "k.bar = 'bar';"
4087 "k[2] = 2;"
4088 "k[4] = 4;");
4089 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4090 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4091
4092 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4093 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4094
4095 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4096 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4097
4098 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4099 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4100}
4101
4102
4103static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4104 ApiTestFuzzer::Fuzz();
4105 if (name->Equals(v8_str("foo")) ||
4106 name->Equals(v8_str("bar")) ||
4107 name->Equals(v8_str("baz"))) {
4108 return v8::Undefined();
4109 }
4110 return v8::Handle<Value>();
4111}
4112
4113
4114static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4115 ApiTestFuzzer::Fuzz();
4116 if (index == 0 || index == 1) return v8::Undefined();
4117 return v8::Handle<Value>();
4118}
4119
4120
4121static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4122 ApiTestFuzzer::Fuzz();
4123 v8::Handle<v8::Array> result = v8::Array::New(3);
4124 result->Set(v8::Integer::New(0), v8_str("foo"));
4125 result->Set(v8::Integer::New(1), v8_str("bar"));
4126 result->Set(v8::Integer::New(2), v8_str("baz"));
4127 return result;
4128}
4129
4130
4131static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4132 ApiTestFuzzer::Fuzz();
4133 v8::Handle<v8::Array> result = v8::Array::New(2);
4134 result->Set(v8::Integer::New(0), v8_str("0"));
4135 result->Set(v8::Integer::New(1), v8_str("1"));
4136 return result;
4137}
4138
4139
4140THREADED_TEST(Enumerators) {
4141 v8::HandleScope scope;
4142 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4143 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4144 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4145 LocalContext context;
4146 context->Global()->Set(v8_str("k"), obj->NewInstance());
4147 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4148 "k[10] = 0;"
4149 "k.a = 0;"
4150 "k[5] = 0;"
4151 "k.b = 0;"
4152 "k[4294967295] = 0;"
4153 "k.c = 0;"
4154 "k[4294967296] = 0;"
4155 "k.d = 0;"
4156 "k[140000] = 0;"
4157 "k.e = 0;"
4158 "k[30000000000] = 0;"
4159 "k.f = 0;"
4160 "var result = [];"
4161 "for (var prop in k) {"
4162 " result.push(prop);"
4163 "}"
4164 "result"));
4165 // Check that we get all the property names returned including the
4166 // ones from the enumerators in the right order: indexed properties
4167 // in numerical order, indexed interceptor properties, named
4168 // properties in insertion order, named interceptor properties.
4169 // This order is not mandated by the spec, so this test is just
4170 // documenting our behavior.
4171 CHECK_EQ(17, result->Length());
4172 // Indexed properties in numerical order.
4173 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4174 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4175 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4176 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4177 // Indexed interceptor properties in the order they are returned
4178 // from the enumerator interceptor.
4179 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4180 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4181 // Named properties in insertion order.
4182 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4183 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4184 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4185 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4186 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4187 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4188 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4189 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4190 // Named interceptor properties.
4191 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4192 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4193 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4194}
4195
4196
4197int p_getter_count;
4198int p_getter_count2;
4199
4200
4201static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4202 ApiTestFuzzer::Fuzz();
4203 p_getter_count++;
4204 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4205 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4206 if (name->Equals(v8_str("p1"))) {
4207 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4208 } else if (name->Equals(v8_str("p2"))) {
4209 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4210 } else if (name->Equals(v8_str("p3"))) {
4211 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4212 } else if (name->Equals(v8_str("p4"))) {
4213 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4214 }
4215 return v8::Undefined();
4216}
4217
4218
4219static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4220 ApiTestFuzzer::Fuzz();
4221 LocalContext context;
4222 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4223 CompileRun(
4224 "o1.__proto__ = { };"
4225 "var o2 = { __proto__: o1 };"
4226 "var o3 = { __proto__: o2 };"
4227 "var o4 = { __proto__: o3 };"
4228 "for (var i = 0; i < 10; i++) o4.p4;"
4229 "for (var i = 0; i < 10; i++) o3.p3;"
4230 "for (var i = 0; i < 10; i++) o2.p2;"
4231 "for (var i = 0; i < 10; i++) o1.p1;");
4232}
4233
4234
4235static v8::Handle<Value> PGetter2(Local<String> name,
4236 const AccessorInfo& info) {
4237 ApiTestFuzzer::Fuzz();
4238 p_getter_count2++;
4239 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4240 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4241 if (name->Equals(v8_str("p1"))) {
4242 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4243 } else if (name->Equals(v8_str("p2"))) {
4244 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4245 } else if (name->Equals(v8_str("p3"))) {
4246 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4247 } else if (name->Equals(v8_str("p4"))) {
4248 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4249 }
4250 return v8::Undefined();
4251}
4252
4253
4254THREADED_TEST(GetterHolders) {
4255 v8::HandleScope scope;
4256 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4257 obj->SetAccessor(v8_str("p1"), PGetter);
4258 obj->SetAccessor(v8_str("p2"), PGetter);
4259 obj->SetAccessor(v8_str("p3"), PGetter);
4260 obj->SetAccessor(v8_str("p4"), PGetter);
4261 p_getter_count = 0;
4262 RunHolderTest(obj);
4263 CHECK_EQ(40, p_getter_count);
4264}
4265
4266
4267THREADED_TEST(PreInterceptorHolders) {
4268 v8::HandleScope scope;
4269 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4270 obj->SetNamedPropertyHandler(PGetter2);
4271 p_getter_count2 = 0;
4272 RunHolderTest(obj);
4273 CHECK_EQ(40, p_getter_count2);
4274}
4275
4276
4277THREADED_TEST(ObjectInstantiation) {
4278 v8::HandleScope scope;
4279 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4280 templ->SetAccessor(v8_str("t"), PGetter2);
4281 LocalContext context;
4282 context->Global()->Set(v8_str("o"), templ->NewInstance());
4283 for (int i = 0; i < 100; i++) {
4284 v8::HandleScope inner_scope;
4285 v8::Handle<v8::Object> obj = templ->NewInstance();
4286 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4287 context->Global()->Set(v8_str("o2"), obj);
4288 v8::Handle<Value> value =
4289 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4290 CHECK_EQ(v8::True(), value);
4291 context->Global()->Set(v8_str("o"), obj);
4292 }
4293}
4294
4295
4296THREADED_TEST(StringWrite) {
4297 v8::HandleScope scope;
4298 v8::Handle<String> str = v8_str("abcde");
4299
4300 char buf[100];
4301 int len;
4302
4303 memset(buf, 0x1, sizeof(buf));
4304 len = str->WriteAscii(buf);
4305 CHECK_EQ(len, 5);
4306 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4307
4308 memset(buf, 0x1, sizeof(buf));
4309 len = str->WriteAscii(buf, 0, 4);
4310 CHECK_EQ(len, 4);
4311 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4312
4313 memset(buf, 0x1, sizeof(buf));
4314 len = str->WriteAscii(buf, 0, 5);
4315 CHECK_EQ(len, 5);
4316 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4317
4318 memset(buf, 0x1, sizeof(buf));
4319 len = str->WriteAscii(buf, 0, 6);
4320 CHECK_EQ(len, 5);
4321 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4322
4323 memset(buf, 0x1, sizeof(buf));
4324 len = str->WriteAscii(buf, 4, -1);
4325 CHECK_EQ(len, 1);
4326 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4327
4328 memset(buf, 0x1, sizeof(buf));
4329 len = str->WriteAscii(buf, 4, 6);
4330 CHECK_EQ(len, 1);
4331 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4332
4333 memset(buf, 0x1, sizeof(buf));
4334 len = str->WriteAscii(buf, 4, 1);
4335 CHECK_EQ(len, 1);
4336 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4337}
4338
4339
4340THREADED_TEST(ToArrayIndex) {
4341 v8::HandleScope scope;
4342 LocalContext context;
4343
4344 v8::Handle<String> str = v8_str("42");
4345 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4346 CHECK(!index.IsEmpty());
4347 CHECK_EQ(42.0, index->Uint32Value());
4348 str = v8_str("42asdf");
4349 index = str->ToArrayIndex();
4350 CHECK(index.IsEmpty());
4351 str = v8_str("-42");
4352 index = str->ToArrayIndex();
4353 CHECK(index.IsEmpty());
4354 str = v8_str("4294967295");
4355 index = str->ToArrayIndex();
4356 CHECK(!index.IsEmpty());
4357 CHECK_EQ(4294967295.0, index->Uint32Value());
4358 v8::Handle<v8::Number> num = v8::Number::New(1);
4359 index = num->ToArrayIndex();
4360 CHECK(!index.IsEmpty());
4361 CHECK_EQ(1.0, index->Uint32Value());
4362 num = v8::Number::New(-1);
4363 index = num->ToArrayIndex();
4364 CHECK(index.IsEmpty());
4365 v8::Handle<v8::Object> obj = v8::Object::New();
4366 index = obj->ToArrayIndex();
4367 CHECK(index.IsEmpty());
4368}
4369
4370
4371THREADED_TEST(ErrorConstruction) {
4372 v8::HandleScope scope;
4373 LocalContext context;
4374
4375 v8::Handle<String> foo = v8_str("foo");
4376 v8::Handle<String> message = v8_str("message");
4377 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4378 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004379 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4380 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004381 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4382 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004383 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004384 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4385 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004386 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004387 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4388 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004389 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004390 v8::Handle<Value> error = v8::Exception::Error(foo);
4391 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004392 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004393}
4394
4395
4396static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4397 ApiTestFuzzer::Fuzz();
4398 return v8_num(10);
4399}
4400
4401
4402static void YSetter(Local<String> name,
4403 Local<Value> value,
4404 const AccessorInfo& info) {
4405 if (info.This()->Has(name)) {
4406 info.This()->Delete(name);
4407 }
4408 info.This()->Set(name, value);
4409}
4410
4411
4412THREADED_TEST(DeleteAccessor) {
4413 v8::HandleScope scope;
4414 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4415 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4416 LocalContext context;
4417 v8::Handle<v8::Object> holder = obj->NewInstance();
4418 context->Global()->Set(v8_str("holder"), holder);
4419 v8::Handle<Value> result = CompileRun(
4420 "holder.y = 11; holder.y = 12; holder.y");
4421 CHECK_EQ(12, result->Uint32Value());
4422}
4423
4424
4425THREADED_TEST(TypeSwitch) {
4426 v8::HandleScope scope;
4427 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4428 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4429 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4430 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4431 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4432 LocalContext context;
4433 v8::Handle<v8::Object> obj0 = v8::Object::New();
4434 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4435 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4436 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4437 for (int i = 0; i < 10; i++) {
4438 CHECK_EQ(0, type_switch->match(obj0));
4439 CHECK_EQ(1, type_switch->match(obj1));
4440 CHECK_EQ(2, type_switch->match(obj2));
4441 CHECK_EQ(3, type_switch->match(obj3));
4442 CHECK_EQ(3, type_switch->match(obj3));
4443 CHECK_EQ(2, type_switch->match(obj2));
4444 CHECK_EQ(1, type_switch->match(obj1));
4445 CHECK_EQ(0, type_switch->match(obj0));
4446 }
4447}
4448
4449
4450// For use within the TestSecurityHandler() test.
4451static bool g_security_callback_result = false;
4452static bool NamedSecurityTestCallback(Local<v8::Object> global,
4453 Local<Value> name,
4454 v8::AccessType type,
4455 Local<Value> data) {
4456 // Always allow read access.
4457 if (type == v8::ACCESS_GET)
4458 return true;
4459
4460 // Sometimes allow other access.
4461 return g_security_callback_result;
4462}
4463
4464
4465static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4466 uint32_t key,
4467 v8::AccessType type,
4468 Local<Value> data) {
4469 // Always allow read access.
4470 if (type == v8::ACCESS_GET)
4471 return true;
4472
4473 // Sometimes allow other access.
4474 return g_security_callback_result;
4475}
4476
4477
4478static int trouble_nesting = 0;
4479static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4480 ApiTestFuzzer::Fuzz();
4481 trouble_nesting++;
4482
4483 // Call a JS function that throws an uncaught exception.
4484 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4485 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4486 arg_this->Get(v8_str("trouble_callee")) :
4487 arg_this->Get(v8_str("trouble_caller"));
4488 CHECK(trouble_callee->IsFunction());
4489 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4490}
4491
4492
4493static int report_count = 0;
4494static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4495 v8::Handle<Value>) {
4496 report_count++;
4497}
4498
4499
4500// Counts uncaught exceptions, but other tests running in parallel
4501// also have uncaught exceptions.
4502TEST(ApiUncaughtException) {
4503 report_count = 0;
4504 v8::HandleScope scope;
4505 LocalContext env;
4506 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4507
4508 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4509 v8::Local<v8::Object> global = env->Global();
4510 global->Set(v8_str("trouble"), fun->GetFunction());
4511
4512 Script::Compile(v8_str("function trouble_callee() {"
4513 " var x = null;"
4514 " return x.foo;"
4515 "};"
4516 "function trouble_caller() {"
4517 " trouble();"
4518 "};"))->Run();
4519 Local<Value> trouble = global->Get(v8_str("trouble"));
4520 CHECK(trouble->IsFunction());
4521 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4522 CHECK(trouble_callee->IsFunction());
4523 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4524 CHECK(trouble_caller->IsFunction());
4525 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4526 CHECK_EQ(1, report_count);
4527 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4528}
4529
Leon Clarke4515c472010-02-03 11:58:03 +00004530static const char* script_resource_name = "ExceptionInNativeScript.js";
4531static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4532 v8::Handle<Value>) {
4533 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4534 CHECK(!name_val.IsEmpty() && name_val->IsString());
4535 v8::String::AsciiValue name(message->GetScriptResourceName());
4536 CHECK_EQ(script_resource_name, *name);
4537 CHECK_EQ(3, message->GetLineNumber());
4538 v8::String::AsciiValue source_line(message->GetSourceLine());
4539 CHECK_EQ(" new o.foo();", *source_line);
4540}
4541
4542TEST(ExceptionInNativeScript) {
4543 v8::HandleScope scope;
4544 LocalContext env;
4545 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4546
4547 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4548 v8::Local<v8::Object> global = env->Global();
4549 global->Set(v8_str("trouble"), fun->GetFunction());
4550
4551 Script::Compile(v8_str("function trouble() {\n"
4552 " var o = {};\n"
4553 " new o.foo();\n"
4554 "};"), v8::String::New(script_resource_name))->Run();
4555 Local<Value> trouble = global->Get(v8_str("trouble"));
4556 CHECK(trouble->IsFunction());
4557 Function::Cast(*trouble)->Call(global, 0, NULL);
4558 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4559}
4560
Steve Blocka7e24c12009-10-30 11:49:00 +00004561
4562TEST(CompilationErrorUsingTryCatchHandler) {
4563 v8::HandleScope scope;
4564 LocalContext env;
4565 v8::TryCatch try_catch;
4566 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4567 CHECK_NE(NULL, *try_catch.Exception());
4568 CHECK(try_catch.HasCaught());
4569}
4570
4571
4572TEST(TryCatchFinallyUsingTryCatchHandler) {
4573 v8::HandleScope scope;
4574 LocalContext env;
4575 v8::TryCatch try_catch;
4576 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4577 CHECK(!try_catch.HasCaught());
4578 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4579 CHECK(try_catch.HasCaught());
4580 try_catch.Reset();
4581 Script::Compile(v8_str("(function() {"
4582 "try { throw ''; } finally { return; }"
4583 "})()"))->Run();
4584 CHECK(!try_catch.HasCaught());
4585 Script::Compile(v8_str("(function()"
4586 " { try { throw ''; } finally { throw 0; }"
4587 "})()"))->Run();
4588 CHECK(try_catch.HasCaught());
4589}
4590
4591
4592// SecurityHandler can't be run twice
4593TEST(SecurityHandler) {
4594 v8::HandleScope scope0;
4595 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4596 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4597 IndexedSecurityTestCallback);
4598 // Create an environment
4599 v8::Persistent<Context> context0 =
4600 Context::New(NULL, global_template);
4601 context0->Enter();
4602
4603 v8::Handle<v8::Object> global0 = context0->Global();
4604 v8::Handle<Script> script0 = v8_compile("foo = 111");
4605 script0->Run();
4606 global0->Set(v8_str("0"), v8_num(999));
4607 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4608 CHECK_EQ(111, foo0->Int32Value());
4609 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4610 CHECK_EQ(999, z0->Int32Value());
4611
4612 // Create another environment, should fail security checks.
4613 v8::HandleScope scope1;
4614
4615 v8::Persistent<Context> context1 =
4616 Context::New(NULL, global_template);
4617 context1->Enter();
4618
4619 v8::Handle<v8::Object> global1 = context1->Global();
4620 global1->Set(v8_str("othercontext"), global0);
4621 // This set will fail the security check.
4622 v8::Handle<Script> script1 =
4623 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4624 script1->Run();
4625 // This read will pass the security check.
4626 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4627 CHECK_EQ(111, foo1->Int32Value());
4628 // This read will pass the security check.
4629 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4630 CHECK_EQ(999, z1->Int32Value());
4631
4632 // Create another environment, should pass security checks.
4633 { g_security_callback_result = true; // allow security handler to pass.
4634 v8::HandleScope scope2;
4635 LocalContext context2;
4636 v8::Handle<v8::Object> global2 = context2->Global();
4637 global2->Set(v8_str("othercontext"), global0);
4638 v8::Handle<Script> script2 =
4639 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4640 script2->Run();
4641 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4642 CHECK_EQ(333, foo2->Int32Value());
4643 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4644 CHECK_EQ(888, z2->Int32Value());
4645 }
4646
4647 context1->Exit();
4648 context1.Dispose();
4649
4650 context0->Exit();
4651 context0.Dispose();
4652}
4653
4654
4655THREADED_TEST(SecurityChecks) {
4656 v8::HandleScope handle_scope;
4657 LocalContext env1;
4658 v8::Persistent<Context> env2 = Context::New();
4659
4660 Local<Value> foo = v8_str("foo");
4661 Local<Value> bar = v8_str("bar");
4662
4663 // Set to the same domain.
4664 env1->SetSecurityToken(foo);
4665
4666 // Create a function in env1.
4667 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4668 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4669 CHECK(spy->IsFunction());
4670
4671 // Create another function accessing global objects.
4672 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4673 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4674 CHECK(spy2->IsFunction());
4675
4676 // Switch to env2 in the same domain and invoke spy on env2.
4677 {
4678 env2->SetSecurityToken(foo);
4679 // Enter env2
4680 Context::Scope scope_env2(env2);
4681 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4682 CHECK(result->IsFunction());
4683 }
4684
4685 {
4686 env2->SetSecurityToken(bar);
4687 Context::Scope scope_env2(env2);
4688
4689 // Call cross_domain_call, it should throw an exception
4690 v8::TryCatch try_catch;
4691 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4692 CHECK(try_catch.HasCaught());
4693 }
4694
4695 env2.Dispose();
4696}
4697
4698
4699// Regression test case for issue 1183439.
4700THREADED_TEST(SecurityChecksForPrototypeChain) {
4701 v8::HandleScope scope;
4702 LocalContext current;
4703 v8::Persistent<Context> other = Context::New();
4704
4705 // Change context to be able to get to the Object function in the
4706 // other context without hitting the security checks.
4707 v8::Local<Value> other_object;
4708 { Context::Scope scope(other);
4709 other_object = other->Global()->Get(v8_str("Object"));
4710 other->Global()->Set(v8_num(42), v8_num(87));
4711 }
4712
4713 current->Global()->Set(v8_str("other"), other->Global());
4714 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4715
4716 // Make sure the security check fails here and we get an undefined
4717 // result instead of getting the Object function. Repeat in a loop
4718 // to make sure to exercise the IC code.
4719 v8::Local<Script> access_other0 = v8_compile("other.Object");
4720 v8::Local<Script> access_other1 = v8_compile("other[42]");
4721 for (int i = 0; i < 5; i++) {
4722 CHECK(!access_other0->Run()->Equals(other_object));
4723 CHECK(access_other0->Run()->IsUndefined());
4724 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4725 CHECK(access_other1->Run()->IsUndefined());
4726 }
4727
4728 // Create an object that has 'other' in its prototype chain and make
4729 // sure we cannot access the Object function indirectly through
4730 // that. Repeat in a loop to make sure to exercise the IC code.
4731 v8_compile("function F() { };"
4732 "F.prototype = other;"
4733 "var f = new F();")->Run();
4734 v8::Local<Script> access_f0 = v8_compile("f.Object");
4735 v8::Local<Script> access_f1 = v8_compile("f[42]");
4736 for (int j = 0; j < 5; j++) {
4737 CHECK(!access_f0->Run()->Equals(other_object));
4738 CHECK(access_f0->Run()->IsUndefined());
4739 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4740 CHECK(access_f1->Run()->IsUndefined());
4741 }
4742
4743 // Now it gets hairy: Set the prototype for the other global object
4744 // to be the current global object. The prototype chain for 'f' now
4745 // goes through 'other' but ends up in the current global object.
4746 { Context::Scope scope(other);
4747 other->Global()->Set(v8_str("__proto__"), current->Global());
4748 }
4749 // Set a named and an index property on the current global
4750 // object. To force the lookup to go through the other global object,
4751 // the properties must not exist in the other global object.
4752 current->Global()->Set(v8_str("foo"), v8_num(100));
4753 current->Global()->Set(v8_num(99), v8_num(101));
4754 // Try to read the properties from f and make sure that the access
4755 // gets stopped by the security checks on the other global object.
4756 Local<Script> access_f2 = v8_compile("f.foo");
4757 Local<Script> access_f3 = v8_compile("f[99]");
4758 for (int k = 0; k < 5; k++) {
4759 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4760 CHECK(access_f2->Run()->IsUndefined());
4761 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4762 CHECK(access_f3->Run()->IsUndefined());
4763 }
4764 other.Dispose();
4765}
4766
4767
4768THREADED_TEST(CrossDomainDelete) {
4769 v8::HandleScope handle_scope;
4770 LocalContext env1;
4771 v8::Persistent<Context> env2 = Context::New();
4772
4773 Local<Value> foo = v8_str("foo");
4774 Local<Value> bar = v8_str("bar");
4775
4776 // Set to the same domain.
4777 env1->SetSecurityToken(foo);
4778 env2->SetSecurityToken(foo);
4779
4780 env1->Global()->Set(v8_str("prop"), v8_num(3));
4781 env2->Global()->Set(v8_str("env1"), env1->Global());
4782
4783 // Change env2 to a different domain and delete env1.prop.
4784 env2->SetSecurityToken(bar);
4785 {
4786 Context::Scope scope_env2(env2);
4787 Local<Value> result =
4788 Script::Compile(v8_str("delete env1.prop"))->Run();
4789 CHECK(result->IsFalse());
4790 }
4791
4792 // Check that env1.prop still exists.
4793 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4794 CHECK(v->IsNumber());
4795 CHECK_EQ(3, v->Int32Value());
4796
4797 env2.Dispose();
4798}
4799
4800
4801THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4802 v8::HandleScope handle_scope;
4803 LocalContext env1;
4804 v8::Persistent<Context> env2 = Context::New();
4805
4806 Local<Value> foo = v8_str("foo");
4807 Local<Value> bar = v8_str("bar");
4808
4809 // Set to the same domain.
4810 env1->SetSecurityToken(foo);
4811 env2->SetSecurityToken(foo);
4812
4813 env1->Global()->Set(v8_str("prop"), v8_num(3));
4814 env2->Global()->Set(v8_str("env1"), env1->Global());
4815
4816 // env1.prop is enumerable in env2.
4817 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4818 {
4819 Context::Scope scope_env2(env2);
4820 Local<Value> result = Script::Compile(test)->Run();
4821 CHECK(result->IsTrue());
4822 }
4823
4824 // Change env2 to a different domain and test again.
4825 env2->SetSecurityToken(bar);
4826 {
4827 Context::Scope scope_env2(env2);
4828 Local<Value> result = Script::Compile(test)->Run();
4829 CHECK(result->IsFalse());
4830 }
4831
4832 env2.Dispose();
4833}
4834
4835
4836THREADED_TEST(CrossDomainForIn) {
4837 v8::HandleScope handle_scope;
4838 LocalContext env1;
4839 v8::Persistent<Context> env2 = Context::New();
4840
4841 Local<Value> foo = v8_str("foo");
4842 Local<Value> bar = v8_str("bar");
4843
4844 // Set to the same domain.
4845 env1->SetSecurityToken(foo);
4846 env2->SetSecurityToken(foo);
4847
4848 env1->Global()->Set(v8_str("prop"), v8_num(3));
4849 env2->Global()->Set(v8_str("env1"), env1->Global());
4850
4851 // Change env2 to a different domain and set env1's global object
4852 // as the __proto__ of an object in env2 and enumerate properties
4853 // in for-in. It shouldn't enumerate properties on env1's global
4854 // object.
4855 env2->SetSecurityToken(bar);
4856 {
4857 Context::Scope scope_env2(env2);
4858 Local<Value> result =
4859 CompileRun("(function(){var obj = {'__proto__':env1};"
4860 "for (var p in obj)"
4861 " if (p == 'prop') return false;"
4862 "return true;})()");
4863 CHECK(result->IsTrue());
4864 }
4865 env2.Dispose();
4866}
4867
4868
4869TEST(ContextDetachGlobal) {
4870 v8::HandleScope handle_scope;
4871 LocalContext env1;
4872 v8::Persistent<Context> env2 = Context::New();
4873
4874 Local<v8::Object> global1 = env1->Global();
4875
4876 Local<Value> foo = v8_str("foo");
4877
4878 // Set to the same domain.
4879 env1->SetSecurityToken(foo);
4880 env2->SetSecurityToken(foo);
4881
4882 // Enter env2
4883 env2->Enter();
4884
Andrei Popescu74b3c142010-03-29 12:03:09 +01004885 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004886 Local<v8::Object> global2 = env2->Global();
4887 global2->Set(v8_str("prop"), v8::Integer::New(1));
4888 CompileRun("function getProp() {return prop;}");
4889
4890 env1->Global()->Set(v8_str("getProp"),
4891 global2->Get(v8_str("getProp")));
4892
Andrei Popescu74b3c142010-03-29 12:03:09 +01004893 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004894 env2->Exit();
4895 env2->DetachGlobal();
4896 // env2 has a new global object.
4897 CHECK(!env2->Global()->Equals(global2));
4898
4899 v8::Persistent<Context> env3 =
4900 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4901 env3->SetSecurityToken(v8_str("bar"));
4902 env3->Enter();
4903
4904 Local<v8::Object> global3 = env3->Global();
4905 CHECK_EQ(global2, global3);
4906 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4907 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4908 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4909 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4910 env3->Exit();
4911
4912 // Call getProp in env1, and it should return the value 1
4913 {
4914 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4915 CHECK(get_prop->IsFunction());
4916 v8::TryCatch try_catch;
4917 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4918 CHECK(!try_catch.HasCaught());
4919 CHECK_EQ(1, r->Int32Value());
4920 }
4921
4922 // Check that env3 is not accessible from env1
4923 {
4924 Local<Value> r = global3->Get(v8_str("prop2"));
4925 CHECK(r->IsUndefined());
4926 }
4927
4928 env2.Dispose();
4929 env3.Dispose();
4930}
4931
4932
Andrei Popescu74b3c142010-03-29 12:03:09 +01004933TEST(DetachAndReattachGlobal) {
4934 v8::HandleScope scope;
4935 LocalContext env1;
4936
4937 // Create second environment.
4938 v8::Persistent<Context> env2 = Context::New();
4939
4940 Local<Value> foo = v8_str("foo");
4941
4942 // Set same security token for env1 and env2.
4943 env1->SetSecurityToken(foo);
4944 env2->SetSecurityToken(foo);
4945
4946 // Create a property on the global object in env2.
4947 {
4948 v8::Context::Scope scope(env2);
4949 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4950 }
4951
4952 // Create a reference to env2 global from env1 global.
4953 env1->Global()->Set(v8_str("other"), env2->Global());
4954
4955 // Check that we have access to other.p in env2 from env1.
4956 Local<Value> result = CompileRun("other.p");
4957 CHECK(result->IsInt32());
4958 CHECK_EQ(42, result->Int32Value());
4959
4960 // Hold on to global from env2 and detach global from env2.
4961 Local<v8::Object> global2 = env2->Global();
4962 env2->DetachGlobal();
4963
4964 // Check that the global has been detached. No other.p property can
4965 // be found.
4966 result = CompileRun("other.p");
4967 CHECK(result->IsUndefined());
4968
4969 // Reuse global2 for env3.
4970 v8::Persistent<Context> env3 =
4971 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4972 CHECK_EQ(global2, env3->Global());
4973
4974 // Start by using the same security token for env3 as for env1 and env2.
4975 env3->SetSecurityToken(foo);
4976
4977 // Create a property on the global object in env3.
4978 {
4979 v8::Context::Scope scope(env3);
4980 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4981 }
4982
4983 // Check that other.p is now the property in env3 and that we have access.
4984 result = CompileRun("other.p");
4985 CHECK(result->IsInt32());
4986 CHECK_EQ(24, result->Int32Value());
4987
4988 // Change security token for env3 to something different from env1 and env2.
4989 env3->SetSecurityToken(v8_str("bar"));
4990
4991 // Check that we do not have access to other.p in env1. |other| is now
4992 // the global object for env3 which has a different security token,
4993 // so access should be blocked.
4994 result = CompileRun("other.p");
4995 CHECK(result->IsUndefined());
4996
4997 // Detach the global for env3 and reattach it to env2.
4998 env3->DetachGlobal();
4999 env2->ReattachGlobal(global2);
5000
5001 // Check that we have access to other.p again in env1. |other| is now
5002 // the global object for env2 which has the same security token as env1.
5003 result = CompileRun("other.p");
5004 CHECK(result->IsInt32());
5005 CHECK_EQ(42, result->Int32Value());
5006
5007 env2.Dispose();
5008 env3.Dispose();
5009}
5010
5011
Steve Blocka7e24c12009-10-30 11:49:00 +00005012static bool NamedAccessBlocker(Local<v8::Object> global,
5013 Local<Value> name,
5014 v8::AccessType type,
5015 Local<Value> data) {
5016 return Context::GetCurrent()->Global()->Equals(global);
5017}
5018
5019
5020static bool IndexedAccessBlocker(Local<v8::Object> global,
5021 uint32_t key,
5022 v8::AccessType type,
5023 Local<Value> data) {
5024 return Context::GetCurrent()->Global()->Equals(global);
5025}
5026
5027
5028static int g_echo_value = -1;
5029static v8::Handle<Value> EchoGetter(Local<String> name,
5030 const AccessorInfo& info) {
5031 return v8_num(g_echo_value);
5032}
5033
5034
5035static void EchoSetter(Local<String> name,
5036 Local<Value> value,
5037 const AccessorInfo&) {
5038 if (value->IsNumber())
5039 g_echo_value = value->Int32Value();
5040}
5041
5042
5043static v8::Handle<Value> UnreachableGetter(Local<String> name,
5044 const AccessorInfo& info) {
5045 CHECK(false); // This function should not be called..
5046 return v8::Undefined();
5047}
5048
5049
5050static void UnreachableSetter(Local<String>, Local<Value>,
5051 const AccessorInfo&) {
5052 CHECK(false); // This function should nto be called.
5053}
5054
5055
5056THREADED_TEST(AccessControl) {
5057 v8::HandleScope handle_scope;
5058 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
5059
5060 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
5061 IndexedAccessBlocker);
5062
5063 // Add an accessor accessible by cross-domain JS code.
5064 global_template->SetAccessor(
5065 v8_str("accessible_prop"),
5066 EchoGetter, EchoSetter,
5067 v8::Handle<Value>(),
5068 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
5069
5070 // Add an accessor that is not accessible by cross-domain JS code.
5071 global_template->SetAccessor(v8_str("blocked_prop"),
5072 UnreachableGetter, UnreachableSetter,
5073 v8::Handle<Value>(),
5074 v8::DEFAULT);
5075
5076 // Create an environment
5077 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5078 context0->Enter();
5079
5080 v8::Handle<v8::Object> global0 = context0->Global();
5081
5082 v8::HandleScope scope1;
5083
5084 v8::Persistent<Context> context1 = Context::New();
5085 context1->Enter();
5086
5087 v8::Handle<v8::Object> global1 = context1->Global();
5088 global1->Set(v8_str("other"), global0);
5089
5090 v8::Handle<Value> value;
5091
5092 // Access blocked property
5093 value = v8_compile("other.blocked_prop = 1")->Run();
5094 value = v8_compile("other.blocked_prop")->Run();
5095 CHECK(value->IsUndefined());
5096
5097 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
5098 CHECK(value->IsFalse());
5099
5100 // Access accessible property
5101 value = v8_compile("other.accessible_prop = 3")->Run();
5102 CHECK(value->IsNumber());
5103 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005104 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005105
5106 value = v8_compile("other.accessible_prop")->Run();
5107 CHECK(value->IsNumber());
5108 CHECK_EQ(3, value->Int32Value());
5109
5110 value =
5111 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
5112 CHECK(value->IsTrue());
5113
5114 // Enumeration doesn't enumerate accessors from inaccessible objects in
5115 // the prototype chain even if the accessors are in themselves accessible.
5116 Local<Value> result =
5117 CompileRun("(function(){var obj = {'__proto__':other};"
5118 "for (var p in obj)"
5119 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5120 " return false;"
5121 " }"
5122 "return true;})()");
5123 CHECK(result->IsTrue());
5124
5125 context1->Exit();
5126 context0->Exit();
5127 context1.Dispose();
5128 context0.Dispose();
5129}
5130
5131
Leon Clarke4515c472010-02-03 11:58:03 +00005132static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5133 Local<Value> name,
5134 v8::AccessType type,
5135 Local<Value> data) {
5136 return false;
5137}
5138
5139
5140static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5141 uint32_t key,
5142 v8::AccessType type,
5143 Local<Value> data) {
5144 return false;
5145}
5146
5147
5148THREADED_TEST(AccessControlGetOwnPropertyNames) {
5149 v8::HandleScope handle_scope;
5150 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5151
5152 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5153 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5154 GetOwnPropertyNamesIndexedBlocker);
5155
5156 // Create an environment
5157 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5158 context0->Enter();
5159
5160 v8::Handle<v8::Object> global0 = context0->Global();
5161
5162 v8::HandleScope scope1;
5163
5164 v8::Persistent<Context> context1 = Context::New();
5165 context1->Enter();
5166
5167 v8::Handle<v8::Object> global1 = context1->Global();
5168 global1->Set(v8_str("other"), global0);
5169 global1->Set(v8_str("object"), obj_template->NewInstance());
5170
5171 v8::Handle<Value> value;
5172
5173 // Attempt to get the property names of the other global object and
5174 // of an object that requires access checks. Accessing the other
5175 // global object should be blocked by access checks on the global
5176 // proxy object. Accessing the object that requires access checks
5177 // is blocked by the access checks on the object itself.
5178 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5179 CHECK(value->IsTrue());
5180
5181 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5182 CHECK(value->IsTrue());
5183
5184 context1->Exit();
5185 context0->Exit();
5186 context1.Dispose();
5187 context0.Dispose();
5188}
5189
5190
Steve Block8defd9f2010-07-08 12:39:36 +01005191static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5192 v8::Handle<v8::Array> result = v8::Array::New(1);
5193 result->Set(0, v8_str("x"));
5194 return result;
5195}
5196
5197
5198THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5199 v8::HandleScope handle_scope;
5200 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5201
5202 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5203 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5204 NamedPropertyEnumerator);
5205
5206 LocalContext context;
5207 v8::Handle<v8::Object> global = context->Global();
5208 global->Set(v8_str("object"), obj_template->NewInstance());
5209
5210 v8::Handle<Value> value =
5211 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5212 CHECK_EQ(v8_str("x"), value);
5213}
5214
5215
Steve Blocka7e24c12009-10-30 11:49:00 +00005216static v8::Handle<Value> ConstTenGetter(Local<String> name,
5217 const AccessorInfo& info) {
5218 return v8_num(10);
5219}
5220
5221
5222THREADED_TEST(CrossDomainAccessors) {
5223 v8::HandleScope handle_scope;
5224
5225 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5226
5227 v8::Handle<v8::ObjectTemplate> global_template =
5228 func_template->InstanceTemplate();
5229
5230 v8::Handle<v8::ObjectTemplate> proto_template =
5231 func_template->PrototypeTemplate();
5232
5233 // Add an accessor to proto that's accessible by cross-domain JS code.
5234 proto_template->SetAccessor(v8_str("accessible"),
5235 ConstTenGetter, 0,
5236 v8::Handle<Value>(),
5237 v8::ALL_CAN_READ);
5238
5239 // Add an accessor that is not accessible by cross-domain JS code.
5240 global_template->SetAccessor(v8_str("unreachable"),
5241 UnreachableGetter, 0,
5242 v8::Handle<Value>(),
5243 v8::DEFAULT);
5244
5245 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5246 context0->Enter();
5247
5248 Local<v8::Object> global = context0->Global();
5249 // Add a normal property that shadows 'accessible'
5250 global->Set(v8_str("accessible"), v8_num(11));
5251
5252 // Enter a new context.
5253 v8::HandleScope scope1;
5254 v8::Persistent<Context> context1 = Context::New();
5255 context1->Enter();
5256
5257 v8::Handle<v8::Object> global1 = context1->Global();
5258 global1->Set(v8_str("other"), global);
5259
5260 // Should return 10, instead of 11
5261 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5262 CHECK(value->IsNumber());
5263 CHECK_EQ(10, value->Int32Value());
5264
5265 value = v8_compile("other.unreachable")->Run();
5266 CHECK(value->IsUndefined());
5267
5268 context1->Exit();
5269 context0->Exit();
5270 context1.Dispose();
5271 context0.Dispose();
5272}
5273
5274
5275static int named_access_count = 0;
5276static int indexed_access_count = 0;
5277
5278static bool NamedAccessCounter(Local<v8::Object> global,
5279 Local<Value> name,
5280 v8::AccessType type,
5281 Local<Value> data) {
5282 named_access_count++;
5283 return true;
5284}
5285
5286
5287static bool IndexedAccessCounter(Local<v8::Object> global,
5288 uint32_t key,
5289 v8::AccessType type,
5290 Local<Value> data) {
5291 indexed_access_count++;
5292 return true;
5293}
5294
5295
5296// This one is too easily disturbed by other tests.
5297TEST(AccessControlIC) {
5298 named_access_count = 0;
5299 indexed_access_count = 0;
5300
5301 v8::HandleScope handle_scope;
5302
5303 // Create an environment.
5304 v8::Persistent<Context> context0 = Context::New();
5305 context0->Enter();
5306
5307 // Create an object that requires access-check functions to be
5308 // called for cross-domain access.
5309 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5310 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5311 IndexedAccessCounter);
5312 Local<v8::Object> object = object_template->NewInstance();
5313
5314 v8::HandleScope scope1;
5315
5316 // Create another environment.
5317 v8::Persistent<Context> context1 = Context::New();
5318 context1->Enter();
5319
5320 // Make easy access to the object from the other environment.
5321 v8::Handle<v8::Object> global1 = context1->Global();
5322 global1->Set(v8_str("obj"), object);
5323
5324 v8::Handle<Value> value;
5325
5326 // Check that the named access-control function is called every time.
5327 CompileRun("function testProp(obj) {"
5328 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5329 " for (var j = 0; j < 10; j++) obj.prop;"
5330 " return obj.prop"
5331 "}");
5332 value = CompileRun("testProp(obj)");
5333 CHECK(value->IsNumber());
5334 CHECK_EQ(1, value->Int32Value());
5335 CHECK_EQ(21, named_access_count);
5336
5337 // Check that the named access-control function is called every time.
5338 CompileRun("var p = 'prop';"
5339 "function testKeyed(obj) {"
5340 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5341 " for (var j = 0; j < 10; j++) obj[p];"
5342 " return obj[p];"
5343 "}");
5344 // Use obj which requires access checks. No inline caching is used
5345 // in that case.
5346 value = CompileRun("testKeyed(obj)");
5347 CHECK(value->IsNumber());
5348 CHECK_EQ(1, value->Int32Value());
5349 CHECK_EQ(42, named_access_count);
5350 // Force the inline caches into generic state and try again.
5351 CompileRun("testKeyed({ a: 0 })");
5352 CompileRun("testKeyed({ b: 0 })");
5353 value = CompileRun("testKeyed(obj)");
5354 CHECK(value->IsNumber());
5355 CHECK_EQ(1, value->Int32Value());
5356 CHECK_EQ(63, named_access_count);
5357
5358 // Check that the indexed access-control function is called every time.
5359 CompileRun("function testIndexed(obj) {"
5360 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5361 " for (var j = 0; j < 10; j++) obj[0];"
5362 " return obj[0]"
5363 "}");
5364 value = CompileRun("testIndexed(obj)");
5365 CHECK(value->IsNumber());
5366 CHECK_EQ(1, value->Int32Value());
5367 CHECK_EQ(21, indexed_access_count);
5368 // Force the inline caches into generic state.
5369 CompileRun("testIndexed(new Array(1))");
5370 // Test that the indexed access check is called.
5371 value = CompileRun("testIndexed(obj)");
5372 CHECK(value->IsNumber());
5373 CHECK_EQ(1, value->Int32Value());
5374 CHECK_EQ(42, indexed_access_count);
5375
5376 // Check that the named access check is called when invoking
5377 // functions on an object that requires access checks.
5378 CompileRun("obj.f = function() {}");
5379 CompileRun("function testCallNormal(obj) {"
5380 " for (var i = 0; i < 10; i++) obj.f();"
5381 "}");
5382 CompileRun("testCallNormal(obj)");
5383 CHECK_EQ(74, named_access_count);
5384
5385 // Force obj into slow case.
5386 value = CompileRun("delete obj.prop");
5387 CHECK(value->BooleanValue());
5388 // Force inline caches into dictionary probing mode.
5389 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5390 // Test that the named access check is called.
5391 value = CompileRun("testProp(obj);");
5392 CHECK(value->IsNumber());
5393 CHECK_EQ(1, value->Int32Value());
5394 CHECK_EQ(96, named_access_count);
5395
5396 // Force the call inline cache into dictionary probing mode.
5397 CompileRun("o.f = function() {}; testCallNormal(o)");
5398 // Test that the named access check is still called for each
5399 // invocation of the function.
5400 value = CompileRun("testCallNormal(obj)");
5401 CHECK_EQ(106, named_access_count);
5402
5403 context1->Exit();
5404 context0->Exit();
5405 context1.Dispose();
5406 context0.Dispose();
5407}
5408
5409
5410static bool NamedAccessFlatten(Local<v8::Object> global,
5411 Local<Value> name,
5412 v8::AccessType type,
5413 Local<Value> data) {
5414 char buf[100];
5415 int len;
5416
5417 CHECK(name->IsString());
5418
5419 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005420 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005421 CHECK_EQ(4, len);
5422
5423 uint16_t buf2[100];
5424
5425 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005426 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005427 CHECK_EQ(4, len);
5428
5429 return true;
5430}
5431
5432
5433static bool IndexedAccessFlatten(Local<v8::Object> global,
5434 uint32_t key,
5435 v8::AccessType type,
5436 Local<Value> data) {
5437 return true;
5438}
5439
5440
5441// Regression test. In access checks, operations that may cause
5442// garbage collection are not allowed. It used to be the case that
5443// using the Write operation on a string could cause a garbage
5444// collection due to flattening of the string. This is no longer the
5445// case.
5446THREADED_TEST(AccessControlFlatten) {
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.
5458 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5459 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5460 IndexedAccessFlatten);
5461 Local<v8::Object> object = object_template->NewInstance();
5462
5463 v8::HandleScope scope1;
5464
5465 // Create another environment.
5466 v8::Persistent<Context> context1 = Context::New();
5467 context1->Enter();
5468
5469 // Make easy access to the object from the other environment.
5470 v8::Handle<v8::Object> global1 = context1->Global();
5471 global1->Set(v8_str("obj"), object);
5472
5473 v8::Handle<Value> value;
5474
5475 value = v8_compile("var p = 'as' + 'df';")->Run();
5476 value = v8_compile("obj[p];")->Run();
5477
5478 context1->Exit();
5479 context0->Exit();
5480 context1.Dispose();
5481 context0.Dispose();
5482}
5483
5484
5485static v8::Handle<Value> AccessControlNamedGetter(
5486 Local<String>, const AccessorInfo&) {
5487 return v8::Integer::New(42);
5488}
5489
5490
5491static v8::Handle<Value> AccessControlNamedSetter(
5492 Local<String>, Local<Value> value, const AccessorInfo&) {
5493 return value;
5494}
5495
5496
5497static v8::Handle<Value> AccessControlIndexedGetter(
5498 uint32_t index,
5499 const AccessorInfo& info) {
5500 return v8_num(42);
5501}
5502
5503
5504static v8::Handle<Value> AccessControlIndexedSetter(
5505 uint32_t, Local<Value> value, const AccessorInfo&) {
5506 return value;
5507}
5508
5509
5510THREADED_TEST(AccessControlInterceptorIC) {
5511 named_access_count = 0;
5512 indexed_access_count = 0;
5513
5514 v8::HandleScope handle_scope;
5515
5516 // Create an environment.
5517 v8::Persistent<Context> context0 = Context::New();
5518 context0->Enter();
5519
5520 // Create an object that requires access-check functions to be
5521 // called for cross-domain access. The object also has interceptors
5522 // interceptor.
5523 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5524 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5525 IndexedAccessCounter);
5526 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5527 AccessControlNamedSetter);
5528 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5529 AccessControlIndexedSetter);
5530 Local<v8::Object> object = object_template->NewInstance();
5531
5532 v8::HandleScope scope1;
5533
5534 // Create another environment.
5535 v8::Persistent<Context> context1 = Context::New();
5536 context1->Enter();
5537
5538 // Make easy access to the object from the other environment.
5539 v8::Handle<v8::Object> global1 = context1->Global();
5540 global1->Set(v8_str("obj"), object);
5541
5542 v8::Handle<Value> value;
5543
5544 // Check that the named access-control function is called every time
5545 // eventhough there is an interceptor on the object.
5546 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5547 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5548 "obj.x")->Run();
5549 CHECK(value->IsNumber());
5550 CHECK_EQ(42, value->Int32Value());
5551 CHECK_EQ(21, named_access_count);
5552
5553 value = v8_compile("var p = 'x';")->Run();
5554 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5555 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5556 "obj[p]")->Run();
5557 CHECK(value->IsNumber());
5558 CHECK_EQ(42, value->Int32Value());
5559 CHECK_EQ(42, named_access_count);
5560
5561 // Check that the indexed access-control function is called every
5562 // time eventhough there is an interceptor on the object.
5563 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5564 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5565 "obj[0]")->Run();
5566 CHECK(value->IsNumber());
5567 CHECK_EQ(42, value->Int32Value());
5568 CHECK_EQ(21, indexed_access_count);
5569
5570 context1->Exit();
5571 context0->Exit();
5572 context1.Dispose();
5573 context0.Dispose();
5574}
5575
5576
5577THREADED_TEST(Version) {
5578 v8::V8::GetVersion();
5579}
5580
5581
5582static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5583 ApiTestFuzzer::Fuzz();
5584 return v8_num(12);
5585}
5586
5587
5588THREADED_TEST(InstanceProperties) {
5589 v8::HandleScope handle_scope;
5590 LocalContext context;
5591
5592 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5593 Local<ObjectTemplate> instance = t->InstanceTemplate();
5594
5595 instance->Set(v8_str("x"), v8_num(42));
5596 instance->Set(v8_str("f"),
5597 v8::FunctionTemplate::New(InstanceFunctionCallback));
5598
5599 Local<Value> o = t->GetFunction()->NewInstance();
5600
5601 context->Global()->Set(v8_str("i"), o);
5602 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5603 CHECK_EQ(42, value->Int32Value());
5604
5605 value = Script::Compile(v8_str("i.f()"))->Run();
5606 CHECK_EQ(12, value->Int32Value());
5607}
5608
5609
5610static v8::Handle<Value>
5611GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5612 ApiTestFuzzer::Fuzz();
5613 return v8::Handle<Value>();
5614}
5615
5616
5617THREADED_TEST(GlobalObjectInstanceProperties) {
5618 v8::HandleScope handle_scope;
5619
5620 Local<Value> global_object;
5621
5622 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5623 t->InstanceTemplate()->SetNamedPropertyHandler(
5624 GlobalObjectInstancePropertiesGet);
5625 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5626 instance_template->Set(v8_str("x"), v8_num(42));
5627 instance_template->Set(v8_str("f"),
5628 v8::FunctionTemplate::New(InstanceFunctionCallback));
5629
5630 {
5631 LocalContext env(NULL, instance_template);
5632 // Hold on to the global object so it can be used again in another
5633 // environment initialization.
5634 global_object = env->Global();
5635
5636 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5637 CHECK_EQ(42, value->Int32Value());
5638 value = Script::Compile(v8_str("f()"))->Run();
5639 CHECK_EQ(12, value->Int32Value());
5640 }
5641
5642 {
5643 // Create new environment reusing the global object.
5644 LocalContext env(NULL, instance_template, global_object);
5645 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5646 CHECK_EQ(42, value->Int32Value());
5647 value = Script::Compile(v8_str("f()"))->Run();
5648 CHECK_EQ(12, value->Int32Value());
5649 }
5650}
5651
5652
5653static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5654 ApiTestFuzzer::Fuzz();
5655 return v8_num(42);
5656}
5657
5658
5659static int shadow_y;
5660static int shadow_y_setter_call_count;
5661static int shadow_y_getter_call_count;
5662
5663
5664static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5665 shadow_y_setter_call_count++;
5666 shadow_y = 42;
5667}
5668
5669
5670static v8::Handle<Value> ShadowYGetter(Local<String> name,
5671 const AccessorInfo& info) {
5672 ApiTestFuzzer::Fuzz();
5673 shadow_y_getter_call_count++;
5674 return v8_num(shadow_y);
5675}
5676
5677
5678static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5679 const AccessorInfo& info) {
5680 return v8::Handle<Value>();
5681}
5682
5683
5684static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5685 const AccessorInfo&) {
5686 return v8::Handle<Value>();
5687}
5688
5689
5690THREADED_TEST(ShadowObject) {
5691 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5692 v8::HandleScope handle_scope;
5693
5694 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5695 LocalContext context(NULL, global_template);
5696
5697 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5698 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5699 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5700 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5701 Local<ObjectTemplate> instance = t->InstanceTemplate();
5702
5703 // Only allow calls of f on instances of t.
5704 Local<v8::Signature> signature = v8::Signature::New(t);
5705 proto->Set(v8_str("f"),
5706 v8::FunctionTemplate::New(ShadowFunctionCallback,
5707 Local<Value>(),
5708 signature));
5709 proto->Set(v8_str("x"), v8_num(12));
5710
5711 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5712
5713 Local<Value> o = t->GetFunction()->NewInstance();
5714 context->Global()->Set(v8_str("__proto__"), o);
5715
5716 Local<Value> value =
5717 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5718 CHECK(value->IsBoolean());
5719 CHECK(!value->BooleanValue());
5720
5721 value = Script::Compile(v8_str("x"))->Run();
5722 CHECK_EQ(12, value->Int32Value());
5723
5724 value = Script::Compile(v8_str("f()"))->Run();
5725 CHECK_EQ(42, value->Int32Value());
5726
5727 Script::Compile(v8_str("y = 42"))->Run();
5728 CHECK_EQ(1, shadow_y_setter_call_count);
5729 value = Script::Compile(v8_str("y"))->Run();
5730 CHECK_EQ(1, shadow_y_getter_call_count);
5731 CHECK_EQ(42, value->Int32Value());
5732}
5733
5734
5735THREADED_TEST(HiddenPrototype) {
5736 v8::HandleScope handle_scope;
5737 LocalContext context;
5738
5739 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5740 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5741 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5742 t1->SetHiddenPrototype(true);
5743 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5744 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5745 t2->SetHiddenPrototype(true);
5746 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5747 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5748 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5749
5750 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5751 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5752 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5753 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5754
5755 // Setting the prototype on an object skips hidden prototypes.
5756 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5757 o0->Set(v8_str("__proto__"), o1);
5758 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5759 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5760 o0->Set(v8_str("__proto__"), o2);
5761 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5762 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5763 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5764 o0->Set(v8_str("__proto__"), o3);
5765 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5766 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5767 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5768 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5769
5770 // Getting the prototype of o0 should get the first visible one
5771 // which is o3. Therefore, z should not be defined on the prototype
5772 // object.
5773 Local<Value> proto = o0->Get(v8_str("__proto__"));
5774 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005775 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005776}
5777
5778
Andrei Popescu402d9372010-02-26 13:31:12 +00005779THREADED_TEST(SetPrototype) {
5780 v8::HandleScope handle_scope;
5781 LocalContext context;
5782
5783 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5784 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5785 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5786 t1->SetHiddenPrototype(true);
5787 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5788 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5789 t2->SetHiddenPrototype(true);
5790 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5791 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5792 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5793
5794 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5795 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5796 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5797 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5798
5799 // Setting the prototype on an object does not skip hidden prototypes.
5800 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5801 CHECK(o0->SetPrototype(o1));
5802 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5803 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5804 CHECK(o1->SetPrototype(o2));
5805 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5806 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5807 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5808 CHECK(o2->SetPrototype(o3));
5809 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5810 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5811 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5812 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5813
5814 // Getting the prototype of o0 should get the first visible one
5815 // which is o3. Therefore, z should not be defined on the prototype
5816 // object.
5817 Local<Value> proto = o0->Get(v8_str("__proto__"));
5818 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005819 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005820
5821 // However, Object::GetPrototype ignores hidden prototype.
5822 Local<Value> proto0 = o0->GetPrototype();
5823 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005824 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005825
5826 Local<Value> proto1 = o1->GetPrototype();
5827 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005828 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005829
5830 Local<Value> proto2 = o2->GetPrototype();
5831 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005832 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005833}
5834
5835
5836THREADED_TEST(SetPrototypeThrows) {
5837 v8::HandleScope handle_scope;
5838 LocalContext context;
5839
5840 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5841
5842 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5843 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5844
5845 CHECK(o0->SetPrototype(o1));
5846 // If setting the prototype leads to the cycle, SetPrototype should
5847 // return false and keep VM in sane state.
5848 v8::TryCatch try_catch;
5849 CHECK(!o1->SetPrototype(o0));
5850 CHECK(!try_catch.HasCaught());
5851 ASSERT(!i::Top::has_pending_exception());
5852
5853 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5854}
5855
5856
Steve Blocka7e24c12009-10-30 11:49:00 +00005857THREADED_TEST(GetterSetterExceptions) {
5858 v8::HandleScope handle_scope;
5859 LocalContext context;
5860 CompileRun(
5861 "function Foo() { };"
5862 "function Throw() { throw 5; };"
5863 "var x = { };"
5864 "x.__defineSetter__('set', Throw);"
5865 "x.__defineGetter__('get', Throw);");
5866 Local<v8::Object> x =
5867 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5868 v8::TryCatch try_catch;
5869 x->Set(v8_str("set"), v8::Integer::New(8));
5870 x->Get(v8_str("get"));
5871 x->Set(v8_str("set"), v8::Integer::New(8));
5872 x->Get(v8_str("get"));
5873 x->Set(v8_str("set"), v8::Integer::New(8));
5874 x->Get(v8_str("get"));
5875 x->Set(v8_str("set"), v8::Integer::New(8));
5876 x->Get(v8_str("get"));
5877}
5878
5879
5880THREADED_TEST(Constructor) {
5881 v8::HandleScope handle_scope;
5882 LocalContext context;
5883 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5884 templ->SetClassName(v8_str("Fun"));
5885 Local<Function> cons = templ->GetFunction();
5886 context->Global()->Set(v8_str("Fun"), cons);
5887 Local<v8::Object> inst = cons->NewInstance();
5888 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5889 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5890 CHECK(value->BooleanValue());
5891}
5892
5893THREADED_TEST(FunctionDescriptorException) {
5894 v8::HandleScope handle_scope;
5895 LocalContext context;
5896 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5897 templ->SetClassName(v8_str("Fun"));
5898 Local<Function> cons = templ->GetFunction();
5899 context->Global()->Set(v8_str("Fun"), cons);
5900 Local<Value> value = CompileRun(
5901 "function test() {"
5902 " try {"
5903 " (new Fun()).blah()"
5904 " } catch (e) {"
5905 " var str = String(e);"
5906 " if (str.indexOf('TypeError') == -1) return 1;"
5907 " if (str.indexOf('[object Fun]') != -1) return 2;"
5908 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5909 " return 0;"
5910 " }"
5911 " return 4;"
5912 "}"
5913 "test();");
5914 CHECK_EQ(0, value->Int32Value());
5915}
5916
5917
5918THREADED_TEST(EvalAliasedDynamic) {
5919 v8::HandleScope scope;
5920 LocalContext current;
5921
5922 // Tests where aliased eval can only be resolved dynamically.
5923 Local<Script> script =
5924 Script::Compile(v8_str("function f(x) { "
5925 " var foo = 2;"
5926 " with (x) { return eval('foo'); }"
5927 "}"
5928 "foo = 0;"
5929 "result1 = f(new Object());"
5930 "result2 = f(this);"
5931 "var x = new Object();"
5932 "x.eval = function(x) { return 1; };"
5933 "result3 = f(x);"));
5934 script->Run();
5935 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5936 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5937 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5938
5939 v8::TryCatch try_catch;
5940 script =
5941 Script::Compile(v8_str("function f(x) { "
5942 " var bar = 2;"
5943 " with (x) { return eval('bar'); }"
5944 "}"
5945 "f(this)"));
5946 script->Run();
5947 CHECK(try_catch.HasCaught());
5948 try_catch.Reset();
5949}
5950
5951
5952THREADED_TEST(CrossEval) {
5953 v8::HandleScope scope;
5954 LocalContext other;
5955 LocalContext current;
5956
5957 Local<String> token = v8_str("<security token>");
5958 other->SetSecurityToken(token);
5959 current->SetSecurityToken(token);
5960
5961 // Setup reference from current to other.
5962 current->Global()->Set(v8_str("other"), other->Global());
5963
5964 // Check that new variables are introduced in other context.
5965 Local<Script> script =
5966 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5967 script->Run();
5968 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5969 CHECK_EQ(1234, foo->Int32Value());
5970 CHECK(!current->Global()->Has(v8_str("foo")));
5971
5972 // Check that writing to non-existing properties introduces them in
5973 // the other context.
5974 script =
5975 Script::Compile(v8_str("other.eval('na = 1234')"));
5976 script->Run();
5977 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5978 CHECK(!current->Global()->Has(v8_str("na")));
5979
5980 // Check that global variables in current context are not visible in other
5981 // context.
5982 v8::TryCatch try_catch;
5983 script =
5984 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5985 Local<Value> result = script->Run();
5986 CHECK(try_catch.HasCaught());
5987 try_catch.Reset();
5988
5989 // Check that local variables in current context are not visible in other
5990 // context.
5991 script =
5992 Script::Compile(v8_str("(function() { "
5993 " var baz = 87;"
5994 " return other.eval('baz');"
5995 "})();"));
5996 result = script->Run();
5997 CHECK(try_catch.HasCaught());
5998 try_catch.Reset();
5999
6000 // Check that global variables in the other environment are visible
6001 // when evaluting code.
6002 other->Global()->Set(v8_str("bis"), v8_num(1234));
6003 script = Script::Compile(v8_str("other.eval('bis')"));
6004 CHECK_EQ(1234, script->Run()->Int32Value());
6005 CHECK(!try_catch.HasCaught());
6006
6007 // Check that the 'this' pointer points to the global object evaluating
6008 // code.
6009 other->Global()->Set(v8_str("t"), other->Global());
6010 script = Script::Compile(v8_str("other.eval('this == t')"));
6011 result = script->Run();
6012 CHECK(result->IsTrue());
6013 CHECK(!try_catch.HasCaught());
6014
6015 // Check that variables introduced in with-statement are not visible in
6016 // other context.
6017 script =
6018 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
6019 result = script->Run();
6020 CHECK(try_catch.HasCaught());
6021 try_catch.Reset();
6022
6023 // Check that you cannot use 'eval.call' with another object than the
6024 // current global object.
6025 script =
6026 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
6027 result = script->Run();
6028 CHECK(try_catch.HasCaught());
6029}
6030
6031
6032// Test that calling eval in a context which has been detached from
6033// its global throws an exception. This behavior is consistent with
6034// other JavaScript implementations.
6035THREADED_TEST(EvalInDetachedGlobal) {
6036 v8::HandleScope scope;
6037
6038 v8::Persistent<Context> context0 = Context::New();
6039 v8::Persistent<Context> context1 = Context::New();
6040
6041 // Setup function in context0 that uses eval from context0.
6042 context0->Enter();
6043 v8::Handle<v8::Value> fun =
6044 CompileRun("var x = 42;"
6045 "(function() {"
6046 " var e = eval;"
6047 " return function(s) { return e(s); }"
6048 "})()");
6049 context0->Exit();
6050
6051 // Put the function into context1 and call it before and after
6052 // detaching the global. Before detaching, the call succeeds and
6053 // after detaching and exception is thrown.
6054 context1->Enter();
6055 context1->Global()->Set(v8_str("fun"), fun);
6056 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
6057 CHECK_EQ(42, x_value->Int32Value());
6058 context0->DetachGlobal();
6059 v8::TryCatch catcher;
6060 x_value = CompileRun("fun('x')");
6061 CHECK(x_value.IsEmpty());
6062 CHECK(catcher.HasCaught());
6063 context1->Exit();
6064
6065 context1.Dispose();
6066 context0.Dispose();
6067}
6068
6069
6070THREADED_TEST(CrossLazyLoad) {
6071 v8::HandleScope scope;
6072 LocalContext other;
6073 LocalContext current;
6074
6075 Local<String> token = v8_str("<security token>");
6076 other->SetSecurityToken(token);
6077 current->SetSecurityToken(token);
6078
6079 // Setup reference from current to other.
6080 current->Global()->Set(v8_str("other"), other->Global());
6081
6082 // Trigger lazy loading in other context.
6083 Local<Script> script =
6084 Script::Compile(v8_str("other.eval('new Date(42)')"));
6085 Local<Value> value = script->Run();
6086 CHECK_EQ(42.0, value->NumberValue());
6087}
6088
6089
6090static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006091 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006092 if (args.IsConstructCall()) {
6093 if (args[0]->IsInt32()) {
6094 return v8_num(-args[0]->Int32Value());
6095 }
6096 }
6097
6098 return args[0];
6099}
6100
6101
6102// Test that a call handler can be set for objects which will allow
6103// non-function objects created through the API to be called as
6104// functions.
6105THREADED_TEST(CallAsFunction) {
6106 v8::HandleScope scope;
6107 LocalContext context;
6108
6109 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6110 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6111 instance_template->SetCallAsFunctionHandler(call_as_function);
6112 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6113 context->Global()->Set(v8_str("obj"), instance);
6114 v8::TryCatch try_catch;
6115 Local<Value> value;
6116 CHECK(!try_catch.HasCaught());
6117
6118 value = CompileRun("obj(42)");
6119 CHECK(!try_catch.HasCaught());
6120 CHECK_EQ(42, value->Int32Value());
6121
6122 value = CompileRun("(function(o){return o(49)})(obj)");
6123 CHECK(!try_catch.HasCaught());
6124 CHECK_EQ(49, value->Int32Value());
6125
6126 // test special case of call as function
6127 value = CompileRun("[obj]['0'](45)");
6128 CHECK(!try_catch.HasCaught());
6129 CHECK_EQ(45, value->Int32Value());
6130
6131 value = CompileRun("obj.call = Function.prototype.call;"
6132 "obj.call(null, 87)");
6133 CHECK(!try_catch.HasCaught());
6134 CHECK_EQ(87, value->Int32Value());
6135
6136 // Regression tests for bug #1116356: Calling call through call/apply
6137 // must work for non-function receivers.
6138 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6139 value = CompileRun(apply_99);
6140 CHECK(!try_catch.HasCaught());
6141 CHECK_EQ(99, value->Int32Value());
6142
6143 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6144 value = CompileRun(call_17);
6145 CHECK(!try_catch.HasCaught());
6146 CHECK_EQ(17, value->Int32Value());
6147
6148 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006149 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006150 value = CompileRun("new obj(43)");
6151 CHECK(!try_catch.HasCaught());
6152 CHECK_EQ(-43, value->Int32Value());
6153}
6154
6155
6156static int CountHandles() {
6157 return v8::HandleScope::NumberOfHandles();
6158}
6159
6160
6161static int Recurse(int depth, int iterations) {
6162 v8::HandleScope scope;
6163 if (depth == 0) return CountHandles();
6164 for (int i = 0; i < iterations; i++) {
6165 Local<v8::Number> n = v8::Integer::New(42);
6166 }
6167 return Recurse(depth - 1, iterations);
6168}
6169
6170
6171THREADED_TEST(HandleIteration) {
6172 static const int kIterations = 500;
6173 static const int kNesting = 200;
6174 CHECK_EQ(0, CountHandles());
6175 {
6176 v8::HandleScope scope1;
6177 CHECK_EQ(0, CountHandles());
6178 for (int i = 0; i < kIterations; i++) {
6179 Local<v8::Number> n = v8::Integer::New(42);
6180 CHECK_EQ(i + 1, CountHandles());
6181 }
6182
6183 CHECK_EQ(kIterations, CountHandles());
6184 {
6185 v8::HandleScope scope2;
6186 for (int j = 0; j < kIterations; j++) {
6187 Local<v8::Number> n = v8::Integer::New(42);
6188 CHECK_EQ(j + 1 + kIterations, CountHandles());
6189 }
6190 }
6191 CHECK_EQ(kIterations, CountHandles());
6192 }
6193 CHECK_EQ(0, CountHandles());
6194 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6195}
6196
6197
6198static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6199 Local<String> name,
6200 const AccessorInfo& info) {
6201 ApiTestFuzzer::Fuzz();
6202 return v8::Handle<Value>();
6203}
6204
6205
6206THREADED_TEST(InterceptorHasOwnProperty) {
6207 v8::HandleScope scope;
6208 LocalContext context;
6209 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6210 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6211 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6212 Local<Function> function = fun_templ->GetFunction();
6213 context->Global()->Set(v8_str("constructor"), function);
6214 v8::Handle<Value> value = CompileRun(
6215 "var o = new constructor();"
6216 "o.hasOwnProperty('ostehaps');");
6217 CHECK_EQ(false, value->BooleanValue());
6218 value = CompileRun(
6219 "o.ostehaps = 42;"
6220 "o.hasOwnProperty('ostehaps');");
6221 CHECK_EQ(true, value->BooleanValue());
6222 value = CompileRun(
6223 "var p = new constructor();"
6224 "p.hasOwnProperty('ostehaps');");
6225 CHECK_EQ(false, value->BooleanValue());
6226}
6227
6228
6229static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6230 Local<String> name,
6231 const AccessorInfo& info) {
6232 ApiTestFuzzer::Fuzz();
6233 i::Heap::CollectAllGarbage(false);
6234 return v8::Handle<Value>();
6235}
6236
6237
6238THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6239 v8::HandleScope scope;
6240 LocalContext context;
6241 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6242 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6243 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6244 Local<Function> function = fun_templ->GetFunction();
6245 context->Global()->Set(v8_str("constructor"), function);
6246 // Let's first make some stuff so we can be sure to get a good GC.
6247 CompileRun(
6248 "function makestr(size) {"
6249 " switch (size) {"
6250 " case 1: return 'f';"
6251 " case 2: return 'fo';"
6252 " case 3: return 'foo';"
6253 " }"
6254 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6255 "}"
6256 "var x = makestr(12345);"
6257 "x = makestr(31415);"
6258 "x = makestr(23456);");
6259 v8::Handle<Value> value = CompileRun(
6260 "var o = new constructor();"
6261 "o.__proto__ = new String(x);"
6262 "o.hasOwnProperty('ostehaps');");
6263 CHECK_EQ(false, value->BooleanValue());
6264}
6265
6266
6267typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6268 const AccessorInfo& info);
6269
6270
6271static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6272 const char* source,
6273 int expected) {
6274 v8::HandleScope scope;
6275 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6276 templ->SetNamedPropertyHandler(getter);
6277 LocalContext context;
6278 context->Global()->Set(v8_str("o"), templ->NewInstance());
6279 v8::Handle<Value> value = CompileRun(source);
6280 CHECK_EQ(expected, value->Int32Value());
6281}
6282
6283
6284static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6285 const AccessorInfo& info) {
6286 ApiTestFuzzer::Fuzz();
6287 CHECK(v8_str("x")->Equals(name));
6288 return v8::Integer::New(42);
6289}
6290
6291
6292// This test should hit the load IC for the interceptor case.
6293THREADED_TEST(InterceptorLoadIC) {
6294 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6295 "var result = 0;"
6296 "for (var i = 0; i < 1000; i++) {"
6297 " result = o.x;"
6298 "}",
6299 42);
6300}
6301
6302
6303// Below go several tests which verify that JITing for various
6304// configurations of interceptor and explicit fields works fine
6305// (those cases are special cased to get better performance).
6306
6307static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6308 const AccessorInfo& info) {
6309 ApiTestFuzzer::Fuzz();
6310 return v8_str("x")->Equals(name)
6311 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6312}
6313
6314
6315THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6316 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6317 "var result = 0;"
6318 "o.y = 239;"
6319 "for (var i = 0; i < 1000; i++) {"
6320 " result = o.y;"
6321 "}",
6322 239);
6323}
6324
6325
6326THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6327 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6328 "var result = 0;"
6329 "o.__proto__ = { 'y': 239 };"
6330 "for (var i = 0; i < 1000; i++) {"
6331 " result = o.y + o.x;"
6332 "}",
6333 239 + 42);
6334}
6335
6336
6337THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6338 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6339 "var result = 0;"
6340 "o.__proto__.y = 239;"
6341 "for (var i = 0; i < 1000; i++) {"
6342 " result = o.y + o.x;"
6343 "}",
6344 239 + 42);
6345}
6346
6347
6348THREADED_TEST(InterceptorLoadICUndefined) {
6349 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6350 "var result = 0;"
6351 "for (var i = 0; i < 1000; i++) {"
6352 " result = (o.y == undefined) ? 239 : 42;"
6353 "}",
6354 239);
6355}
6356
6357
6358THREADED_TEST(InterceptorLoadICWithOverride) {
6359 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6360 "fst = new Object(); fst.__proto__ = o;"
6361 "snd = new Object(); snd.__proto__ = fst;"
6362 "var result1 = 0;"
6363 "for (var i = 0; i < 1000; i++) {"
6364 " result1 = snd.x;"
6365 "}"
6366 "fst.x = 239;"
6367 "var result = 0;"
6368 "for (var i = 0; i < 1000; i++) {"
6369 " result = snd.x;"
6370 "}"
6371 "result + result1",
6372 239 + 42);
6373}
6374
6375
6376// Test the case when we stored field into
6377// a stub, but interceptor produced value on its own.
6378THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6379 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6380 "proto = new Object();"
6381 "o.__proto__ = proto;"
6382 "proto.x = 239;"
6383 "for (var i = 0; i < 1000; i++) {"
6384 " o.x;"
6385 // Now it should be ICed and keep a reference to x defined on proto
6386 "}"
6387 "var result = 0;"
6388 "for (var i = 0; i < 1000; i++) {"
6389 " result += o.x;"
6390 "}"
6391 "result;",
6392 42 * 1000);
6393}
6394
6395
6396// Test the case when we stored field into
6397// a stub, but it got invalidated later on.
6398THREADED_TEST(InterceptorLoadICInvalidatedField) {
6399 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6400 "proto1 = new Object();"
6401 "proto2 = new Object();"
6402 "o.__proto__ = proto1;"
6403 "proto1.__proto__ = proto2;"
6404 "proto2.y = 239;"
6405 "for (var i = 0; i < 1000; i++) {"
6406 " o.y;"
6407 // Now it should be ICed and keep a reference to y defined on proto2
6408 "}"
6409 "proto1.y = 42;"
6410 "var result = 0;"
6411 "for (var i = 0; i < 1000; i++) {"
6412 " result += o.y;"
6413 "}"
6414 "result;",
6415 42 * 1000);
6416}
6417
6418
Steve Block6ded16b2010-05-10 14:33:55 +01006419static int interceptor_load_not_handled_calls = 0;
6420static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6421 const AccessorInfo& info) {
6422 ++interceptor_load_not_handled_calls;
6423 return v8::Handle<v8::Value>();
6424}
6425
6426
6427// Test how post-interceptor lookups are done in the non-cacheable
6428// case: the interceptor should not be invoked during this lookup.
6429THREADED_TEST(InterceptorLoadICPostInterceptor) {
6430 interceptor_load_not_handled_calls = 0;
6431 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6432 "receiver = new Object();"
6433 "receiver.__proto__ = o;"
6434 "proto = new Object();"
6435 "/* Make proto a slow-case object. */"
6436 "for (var i = 0; i < 1000; i++) {"
6437 " proto[\"xxxxxxxx\" + i] = [];"
6438 "}"
6439 "proto.x = 17;"
6440 "o.__proto__ = proto;"
6441 "var result = 0;"
6442 "for (var i = 0; i < 1000; i++) {"
6443 " result += receiver.x;"
6444 "}"
6445 "result;",
6446 17 * 1000);
6447 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6448}
6449
6450
Steve Blocka7e24c12009-10-30 11:49:00 +00006451// Test the case when we stored field into
6452// a stub, but it got invalidated later on due to override on
6453// global object which is between interceptor and fields' holders.
6454THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6455 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6456 "o.__proto__ = this;" // set a global to be a proto of o.
6457 "this.__proto__.y = 239;"
6458 "for (var i = 0; i < 10; i++) {"
6459 " if (o.y != 239) throw 'oops: ' + o.y;"
6460 // Now it should be ICed and keep a reference to y defined on field_holder.
6461 "}"
6462 "this.y = 42;" // Assign on a global.
6463 "var result = 0;"
6464 "for (var i = 0; i < 10; i++) {"
6465 " result += o.y;"
6466 "}"
6467 "result;",
6468 42 * 10);
6469}
6470
6471
6472static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
6473 ApiTestFuzzer::Fuzz();
6474 return v8_num(239);
6475}
6476
6477
6478static void SetOnThis(Local<String> name,
6479 Local<Value> value,
6480 const AccessorInfo& info) {
6481 info.This()->ForceSet(name, value);
6482}
6483
6484
6485THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6486 v8::HandleScope scope;
6487 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6488 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6489 templ->SetAccessor(v8_str("y"), Return239);
6490 LocalContext context;
6491 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006492
6493 // Check the case when receiver and interceptor's holder
6494 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006495 v8::Handle<Value> value = CompileRun(
6496 "var result = 0;"
6497 "for (var i = 0; i < 7; i++) {"
6498 " result = o.y;"
6499 "}");
6500 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006501
6502 // Check the case when interceptor's holder is in proto chain
6503 // of receiver.
6504 value = CompileRun(
6505 "r = { __proto__: o };"
6506 "var result = 0;"
6507 "for (var i = 0; i < 7; i++) {"
6508 " result = r.y;"
6509 "}");
6510 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006511}
6512
6513
6514THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6515 v8::HandleScope scope;
6516 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6517 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6518 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6519 templ_p->SetAccessor(v8_str("y"), Return239);
6520
6521 LocalContext context;
6522 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6523 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6524
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006525 // Check the case when receiver and interceptor's holder
6526 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006527 v8::Handle<Value> value = CompileRun(
6528 "o.__proto__ = p;"
6529 "var result = 0;"
6530 "for (var i = 0; i < 7; i++) {"
6531 " result = o.x + o.y;"
6532 "}");
6533 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006534
6535 // Check the case when interceptor's holder is in proto chain
6536 // of receiver.
6537 value = CompileRun(
6538 "r = { __proto__: o };"
6539 "var result = 0;"
6540 "for (var i = 0; i < 7; i++) {"
6541 " result = r.x + r.y;"
6542 "}");
6543 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006544}
6545
6546
6547THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6548 v8::HandleScope scope;
6549 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6550 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6551 templ->SetAccessor(v8_str("y"), Return239);
6552
6553 LocalContext context;
6554 context->Global()->Set(v8_str("o"), templ->NewInstance());
6555
6556 v8::Handle<Value> value = CompileRun(
6557 "fst = new Object(); fst.__proto__ = o;"
6558 "snd = new Object(); snd.__proto__ = fst;"
6559 "var result1 = 0;"
6560 "for (var i = 0; i < 7; i++) {"
6561 " result1 = snd.x;"
6562 "}"
6563 "fst.x = 239;"
6564 "var result = 0;"
6565 "for (var i = 0; i < 7; i++) {"
6566 " result = snd.x;"
6567 "}"
6568 "result + result1");
6569 CHECK_EQ(239 + 42, value->Int32Value());
6570}
6571
6572
6573// Test the case when we stored callback into
6574// a stub, but interceptor produced value on its own.
6575THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6576 v8::HandleScope scope;
6577 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6578 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6579 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6580 templ_p->SetAccessor(v8_str("y"), Return239);
6581
6582 LocalContext context;
6583 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6584 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6585
6586 v8::Handle<Value> value = CompileRun(
6587 "o.__proto__ = p;"
6588 "for (var i = 0; i < 7; i++) {"
6589 " o.x;"
6590 // Now it should be ICed and keep a reference to x defined on p
6591 "}"
6592 "var result = 0;"
6593 "for (var i = 0; i < 7; i++) {"
6594 " result += o.x;"
6595 "}"
6596 "result");
6597 CHECK_EQ(42 * 7, value->Int32Value());
6598}
6599
6600
6601// Test the case when we stored callback into
6602// a stub, but it got invalidated later on.
6603THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6604 v8::HandleScope scope;
6605 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6606 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6607 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6608 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6609
6610 LocalContext context;
6611 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6612 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6613
6614 v8::Handle<Value> value = CompileRun(
6615 "inbetween = new Object();"
6616 "o.__proto__ = inbetween;"
6617 "inbetween.__proto__ = p;"
6618 "for (var i = 0; i < 10; i++) {"
6619 " o.y;"
6620 // Now it should be ICed and keep a reference to y defined on p
6621 "}"
6622 "inbetween.y = 42;"
6623 "var result = 0;"
6624 "for (var i = 0; i < 10; i++) {"
6625 " result += o.y;"
6626 "}"
6627 "result");
6628 CHECK_EQ(42 * 10, value->Int32Value());
6629}
6630
6631
6632// Test the case when we stored callback into
6633// a stub, but it got invalidated later on due to override on
6634// global object which is between interceptor and callbacks' holders.
6635THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6636 v8::HandleScope scope;
6637 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6638 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6639 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6640 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6641
6642 LocalContext context;
6643 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6644 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6645
6646 v8::Handle<Value> value = CompileRun(
6647 "o.__proto__ = this;"
6648 "this.__proto__ = p;"
6649 "for (var i = 0; i < 10; i++) {"
6650 " if (o.y != 239) throw 'oops: ' + o.y;"
6651 // Now it should be ICed and keep a reference to y defined on p
6652 "}"
6653 "this.y = 42;"
6654 "var result = 0;"
6655 "for (var i = 0; i < 10; i++) {"
6656 " result += o.y;"
6657 "}"
6658 "result");
6659 CHECK_EQ(42 * 10, value->Int32Value());
6660}
6661
6662
6663static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6664 const AccessorInfo& info) {
6665 ApiTestFuzzer::Fuzz();
6666 CHECK(v8_str("x")->Equals(name));
6667 return v8::Integer::New(0);
6668}
6669
6670
6671THREADED_TEST(InterceptorReturningZero) {
6672 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6673 "o.x == undefined ? 1 : 0",
6674 0);
6675}
6676
6677
6678static v8::Handle<Value> InterceptorStoreICSetter(
6679 Local<String> key, Local<Value> value, const AccessorInfo&) {
6680 CHECK(v8_str("x")->Equals(key));
6681 CHECK_EQ(42, value->Int32Value());
6682 return value;
6683}
6684
6685
6686// This test should hit the store IC for the interceptor case.
6687THREADED_TEST(InterceptorStoreIC) {
6688 v8::HandleScope scope;
6689 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6690 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6691 InterceptorStoreICSetter);
6692 LocalContext context;
6693 context->Global()->Set(v8_str("o"), templ->NewInstance());
6694 v8::Handle<Value> value = CompileRun(
6695 "for (var i = 0; i < 1000; i++) {"
6696 " o.x = 42;"
6697 "}");
6698}
6699
6700
6701THREADED_TEST(InterceptorStoreICWithNoSetter) {
6702 v8::HandleScope scope;
6703 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6704 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6705 LocalContext context;
6706 context->Global()->Set(v8_str("o"), templ->NewInstance());
6707 v8::Handle<Value> value = CompileRun(
6708 "for (var i = 0; i < 1000; i++) {"
6709 " o.y = 239;"
6710 "}"
6711 "42 + o.y");
6712 CHECK_EQ(239 + 42, value->Int32Value());
6713}
6714
6715
6716
6717
6718v8::Handle<Value> call_ic_function;
6719v8::Handle<Value> call_ic_function2;
6720v8::Handle<Value> call_ic_function3;
6721
6722static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6723 const AccessorInfo& info) {
6724 ApiTestFuzzer::Fuzz();
6725 CHECK(v8_str("x")->Equals(name));
6726 return call_ic_function;
6727}
6728
6729
6730// This test should hit the call IC for the interceptor case.
6731THREADED_TEST(InterceptorCallIC) {
6732 v8::HandleScope scope;
6733 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6734 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6735 LocalContext context;
6736 context->Global()->Set(v8_str("o"), templ->NewInstance());
6737 call_ic_function =
6738 v8_compile("function f(x) { return x + 1; }; f")->Run();
6739 v8::Handle<Value> value = CompileRun(
6740 "var result = 0;"
6741 "for (var i = 0; i < 1000; i++) {"
6742 " result = o.x(41);"
6743 "}");
6744 CHECK_EQ(42, value->Int32Value());
6745}
6746
6747
6748// This test checks that if interceptor doesn't provide
6749// a value, we can fetch regular value.
6750THREADED_TEST(InterceptorCallICSeesOthers) {
6751 v8::HandleScope scope;
6752 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6753 templ->SetNamedPropertyHandler(NoBlockGetterX);
6754 LocalContext context;
6755 context->Global()->Set(v8_str("o"), templ->NewInstance());
6756 v8::Handle<Value> value = CompileRun(
6757 "o.x = function f(x) { return x + 1; };"
6758 "var result = 0;"
6759 "for (var i = 0; i < 7; i++) {"
6760 " result = o.x(41);"
6761 "}");
6762 CHECK_EQ(42, value->Int32Value());
6763}
6764
6765
6766static v8::Handle<Value> call_ic_function4;
6767static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6768 const AccessorInfo& info) {
6769 ApiTestFuzzer::Fuzz();
6770 CHECK(v8_str("x")->Equals(name));
6771 return call_ic_function4;
6772}
6773
6774
6775// This test checks that if interceptor provides a function,
6776// even if we cached shadowed variant, interceptor's function
6777// is invoked
6778THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6779 v8::HandleScope scope;
6780 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6781 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6782 LocalContext context;
6783 context->Global()->Set(v8_str("o"), templ->NewInstance());
6784 call_ic_function4 =
6785 v8_compile("function f(x) { return x - 1; }; f")->Run();
6786 v8::Handle<Value> value = CompileRun(
6787 "o.__proto__.x = function(x) { return x + 1; };"
6788 "var result = 0;"
6789 "for (var i = 0; i < 1000; i++) {"
6790 " result = o.x(42);"
6791 "}");
6792 CHECK_EQ(41, value->Int32Value());
6793}
6794
6795
6796// Test the case when we stored cacheable lookup into
6797// a stub, but it got invalidated later on
6798THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6799 v8::HandleScope scope;
6800 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6801 templ->SetNamedPropertyHandler(NoBlockGetterX);
6802 LocalContext context;
6803 context->Global()->Set(v8_str("o"), templ->NewInstance());
6804 v8::Handle<Value> value = CompileRun(
6805 "proto1 = new Object();"
6806 "proto2 = new Object();"
6807 "o.__proto__ = proto1;"
6808 "proto1.__proto__ = proto2;"
6809 "proto2.y = function(x) { return x + 1; };"
6810 // Invoke it many times to compile a stub
6811 "for (var i = 0; i < 7; i++) {"
6812 " o.y(42);"
6813 "}"
6814 "proto1.y = function(x) { return x - 1; };"
6815 "var result = 0;"
6816 "for (var i = 0; i < 7; i++) {"
6817 " result += o.y(42);"
6818 "}");
6819 CHECK_EQ(41 * 7, value->Int32Value());
6820}
6821
6822
6823static v8::Handle<Value> call_ic_function5;
6824static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6825 const AccessorInfo& info) {
6826 ApiTestFuzzer::Fuzz();
6827 if (v8_str("x")->Equals(name))
6828 return call_ic_function5;
6829 else
6830 return Local<Value>();
6831}
6832
6833
6834// This test checks that if interceptor doesn't provide a function,
6835// cached constant function is used
6836THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6837 v8::HandleScope scope;
6838 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6839 templ->SetNamedPropertyHandler(NoBlockGetterX);
6840 LocalContext context;
6841 context->Global()->Set(v8_str("o"), templ->NewInstance());
6842 v8::Handle<Value> value = CompileRun(
6843 "function inc(x) { return x + 1; };"
6844 "inc(1);"
6845 "o.x = inc;"
6846 "var result = 0;"
6847 "for (var i = 0; i < 1000; i++) {"
6848 " result = o.x(42);"
6849 "}");
6850 CHECK_EQ(43, value->Int32Value());
6851}
6852
6853
6854// This test checks that if interceptor provides a function,
6855// even if we cached constant function, interceptor's function
6856// is invoked
6857THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6858 v8::HandleScope scope;
6859 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6860 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6861 LocalContext context;
6862 context->Global()->Set(v8_str("o"), templ->NewInstance());
6863 call_ic_function5 =
6864 v8_compile("function f(x) { return x - 1; }; f")->Run();
6865 v8::Handle<Value> value = CompileRun(
6866 "function inc(x) { return x + 1; };"
6867 "inc(1);"
6868 "o.x = inc;"
6869 "var result = 0;"
6870 "for (var i = 0; i < 1000; i++) {"
6871 " result = o.x(42);"
6872 "}");
6873 CHECK_EQ(41, value->Int32Value());
6874}
6875
6876
6877// Test the case when we stored constant function into
6878// a stub, but it got invalidated later on
6879THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6880 v8::HandleScope scope;
6881 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6882 templ->SetNamedPropertyHandler(NoBlockGetterX);
6883 LocalContext context;
6884 context->Global()->Set(v8_str("o"), templ->NewInstance());
6885 v8::Handle<Value> value = CompileRun(
6886 "function inc(x) { return x + 1; };"
6887 "inc(1);"
6888 "proto1 = new Object();"
6889 "proto2 = new Object();"
6890 "o.__proto__ = proto1;"
6891 "proto1.__proto__ = proto2;"
6892 "proto2.y = inc;"
6893 // Invoke it many times to compile a stub
6894 "for (var i = 0; i < 7; i++) {"
6895 " o.y(42);"
6896 "}"
6897 "proto1.y = function(x) { return x - 1; };"
6898 "var result = 0;"
6899 "for (var i = 0; i < 7; i++) {"
6900 " result += o.y(42);"
6901 "}");
6902 CHECK_EQ(41 * 7, value->Int32Value());
6903}
6904
6905
6906// Test the case when we stored constant function into
6907// a stub, but it got invalidated later on due to override on
6908// global object which is between interceptor and constant function' holders.
6909THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6910 v8::HandleScope scope;
6911 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6912 templ->SetNamedPropertyHandler(NoBlockGetterX);
6913 LocalContext context;
6914 context->Global()->Set(v8_str("o"), templ->NewInstance());
6915 v8::Handle<Value> value = CompileRun(
6916 "function inc(x) { return x + 1; };"
6917 "inc(1);"
6918 "o.__proto__ = this;"
6919 "this.__proto__.y = inc;"
6920 // Invoke it many times to compile a stub
6921 "for (var i = 0; i < 7; i++) {"
6922 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6923 "}"
6924 "this.y = function(x) { return x - 1; };"
6925 "var result = 0;"
6926 "for (var i = 0; i < 7; i++) {"
6927 " result += o.y(42);"
6928 "}");
6929 CHECK_EQ(41 * 7, value->Int32Value());
6930}
6931
6932
Leon Clarke4515c472010-02-03 11:58:03 +00006933// Test the case when actual function to call sits on global object.
6934THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6935 v8::HandleScope scope;
6936 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6937 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6938
6939 LocalContext context;
6940 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6941
6942 v8::Handle<Value> value = CompileRun(
6943 "try {"
6944 " o.__proto__ = this;"
6945 " for (var i = 0; i < 10; i++) {"
6946 " var v = o.parseFloat('239');"
6947 " if (v != 239) throw v;"
6948 // Now it should be ICed and keep a reference to parseFloat.
6949 " }"
6950 " var result = 0;"
6951 " for (var i = 0; i < 10; i++) {"
6952 " result += o.parseFloat('239');"
6953 " }"
6954 " result"
6955 "} catch(e) {"
6956 " e"
6957 "};");
6958 CHECK_EQ(239 * 10, value->Int32Value());
6959}
6960
Andrei Popescu402d9372010-02-26 13:31:12 +00006961static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6962 const AccessorInfo& info) {
6963 ApiTestFuzzer::Fuzz();
6964 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6965 ++(*call_count);
6966 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01006967 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00006968 }
6969 return v8::Handle<Value>();
6970}
6971
6972static v8::Handle<Value> FastApiCallback_TrivialSignature(
6973 const v8::Arguments& args) {
6974 ApiTestFuzzer::Fuzz();
6975 CHECK_EQ(args.This(), args.Holder());
6976 CHECK(args.Data()->Equals(v8_str("method_data")));
6977 return v8::Integer::New(args[0]->Int32Value() + 1);
6978}
6979
6980static v8::Handle<Value> FastApiCallback_SimpleSignature(
6981 const v8::Arguments& args) {
6982 ApiTestFuzzer::Fuzz();
6983 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6984 CHECK(args.Data()->Equals(v8_str("method_data")));
6985 // Note, we're using HasRealNamedProperty instead of Has to avoid
6986 // invoking the interceptor again.
6987 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6988 return v8::Integer::New(args[0]->Int32Value() + 1);
6989}
6990
6991// Helper to maximize the odds of object moving.
6992static void GenerateSomeGarbage() {
6993 CompileRun(
6994 "var garbage;"
6995 "for (var i = 0; i < 1000; i++) {"
6996 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6997 "}"
6998 "garbage = undefined;");
6999}
7000
7001THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
7002 int interceptor_call_count = 0;
7003 v8::HandleScope scope;
7004 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7005 v8::Handle<v8::FunctionTemplate> method_templ =
7006 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7007 v8_str("method_data"),
7008 v8::Handle<v8::Signature>());
7009 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7010 proto_templ->Set(v8_str("method"), method_templ);
7011 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7012 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7013 NULL, NULL, NULL, NULL,
7014 v8::External::Wrap(&interceptor_call_count));
7015 LocalContext context;
7016 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7017 GenerateSomeGarbage();
7018 context->Global()->Set(v8_str("o"), fun->NewInstance());
7019 v8::Handle<Value> value = CompileRun(
7020 "var result = 0;"
7021 "for (var i = 0; i < 100; i++) {"
7022 " result = o.method(41);"
7023 "}");
7024 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7025 CHECK_EQ(100, interceptor_call_count);
7026}
7027
7028THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
7029 int interceptor_call_count = 0;
7030 v8::HandleScope scope;
7031 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7032 v8::Handle<v8::FunctionTemplate> method_templ =
7033 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7034 v8_str("method_data"),
7035 v8::Signature::New(fun_templ));
7036 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7037 proto_templ->Set(v8_str("method"), method_templ);
7038 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7039 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7040 NULL, NULL, NULL, NULL,
7041 v8::External::Wrap(&interceptor_call_count));
7042 LocalContext context;
7043 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7044 GenerateSomeGarbage();
7045 context->Global()->Set(v8_str("o"), fun->NewInstance());
7046 v8::Handle<Value> value = CompileRun(
7047 "o.foo = 17;"
7048 "var receiver = {};"
7049 "receiver.__proto__ = o;"
7050 "var result = 0;"
7051 "for (var i = 0; i < 100; i++) {"
7052 " result = receiver.method(41);"
7053 "}");
7054 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7055 CHECK_EQ(100, interceptor_call_count);
7056}
7057
7058THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
7059 int interceptor_call_count = 0;
7060 v8::HandleScope scope;
7061 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7062 v8::Handle<v8::FunctionTemplate> method_templ =
7063 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7064 v8_str("method_data"),
7065 v8::Signature::New(fun_templ));
7066 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7067 proto_templ->Set(v8_str("method"), method_templ);
7068 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7069 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7070 NULL, NULL, NULL, NULL,
7071 v8::External::Wrap(&interceptor_call_count));
7072 LocalContext context;
7073 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7074 GenerateSomeGarbage();
7075 context->Global()->Set(v8_str("o"), fun->NewInstance());
7076 v8::Handle<Value> value = CompileRun(
7077 "o.foo = 17;"
7078 "var receiver = {};"
7079 "receiver.__proto__ = o;"
7080 "var result = 0;"
7081 "var saved_result = 0;"
7082 "for (var i = 0; i < 100; i++) {"
7083 " result = receiver.method(41);"
7084 " if (i == 50) {"
7085 " saved_result = result;"
7086 " receiver = {method: function(x) { return x - 1 }};"
7087 " }"
7088 "}");
7089 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7090 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7091 CHECK_GE(interceptor_call_count, 50);
7092}
7093
7094THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
7095 int interceptor_call_count = 0;
7096 v8::HandleScope scope;
7097 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7098 v8::Handle<v8::FunctionTemplate> method_templ =
7099 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7100 v8_str("method_data"),
7101 v8::Signature::New(fun_templ));
7102 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7103 proto_templ->Set(v8_str("method"), method_templ);
7104 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7105 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7106 NULL, NULL, NULL, NULL,
7107 v8::External::Wrap(&interceptor_call_count));
7108 LocalContext context;
7109 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7110 GenerateSomeGarbage();
7111 context->Global()->Set(v8_str("o"), fun->NewInstance());
7112 v8::Handle<Value> value = CompileRun(
7113 "o.foo = 17;"
7114 "var receiver = {};"
7115 "receiver.__proto__ = o;"
7116 "var result = 0;"
7117 "var saved_result = 0;"
7118 "for (var i = 0; i < 100; i++) {"
7119 " result = receiver.method(41);"
7120 " if (i == 50) {"
7121 " saved_result = result;"
7122 " o.method = function(x) { return x - 1 };"
7123 " }"
7124 "}");
7125 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7126 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7127 CHECK_GE(interceptor_call_count, 50);
7128}
7129
Steve Block6ded16b2010-05-10 14:33:55 +01007130THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7131 int interceptor_call_count = 0;
7132 v8::HandleScope scope;
7133 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7134 v8::Handle<v8::FunctionTemplate> method_templ =
7135 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7136 v8_str("method_data"),
7137 v8::Signature::New(fun_templ));
7138 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7139 proto_templ->Set(v8_str("method"), method_templ);
7140 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7141 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7142 NULL, NULL, NULL, NULL,
7143 v8::External::Wrap(&interceptor_call_count));
7144 LocalContext context;
7145 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7146 GenerateSomeGarbage();
7147 context->Global()->Set(v8_str("o"), fun->NewInstance());
7148 v8::TryCatch try_catch;
7149 v8::Handle<Value> value = CompileRun(
7150 "o.foo = 17;"
7151 "var receiver = {};"
7152 "receiver.__proto__ = o;"
7153 "var result = 0;"
7154 "var saved_result = 0;"
7155 "for (var i = 0; i < 100; i++) {"
7156 " result = receiver.method(41);"
7157 " if (i == 50) {"
7158 " saved_result = result;"
7159 " receiver = 333;"
7160 " }"
7161 "}");
7162 CHECK(try_catch.HasCaught());
7163 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7164 try_catch.Exception()->ToString());
7165 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7166 CHECK_GE(interceptor_call_count, 50);
7167}
7168
Andrei Popescu402d9372010-02-26 13:31:12 +00007169THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
7170 int interceptor_call_count = 0;
7171 v8::HandleScope scope;
7172 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7173 v8::Handle<v8::FunctionTemplate> method_templ =
7174 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7175 v8_str("method_data"),
7176 v8::Signature::New(fun_templ));
7177 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7178 proto_templ->Set(v8_str("method"), method_templ);
7179 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7180 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7181 NULL, NULL, NULL, NULL,
7182 v8::External::Wrap(&interceptor_call_count));
7183 LocalContext context;
7184 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7185 GenerateSomeGarbage();
7186 context->Global()->Set(v8_str("o"), fun->NewInstance());
7187 v8::TryCatch try_catch;
7188 v8::Handle<Value> value = CompileRun(
7189 "o.foo = 17;"
7190 "var receiver = {};"
7191 "receiver.__proto__ = o;"
7192 "var result = 0;"
7193 "var saved_result = 0;"
7194 "for (var i = 0; i < 100; i++) {"
7195 " result = receiver.method(41);"
7196 " if (i == 50) {"
7197 " saved_result = result;"
7198 " receiver = {method: receiver.method};"
7199 " }"
7200 "}");
7201 CHECK(try_catch.HasCaught());
7202 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7203 try_catch.Exception()->ToString());
7204 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7205 CHECK_GE(interceptor_call_count, 50);
7206}
7207
7208THREADED_TEST(CallICFastApi_TrivialSignature) {
7209 v8::HandleScope scope;
7210 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7211 v8::Handle<v8::FunctionTemplate> method_templ =
7212 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7213 v8_str("method_data"),
7214 v8::Handle<v8::Signature>());
7215 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7216 proto_templ->Set(v8_str("method"), method_templ);
7217 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7218 LocalContext context;
7219 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7220 GenerateSomeGarbage();
7221 context->Global()->Set(v8_str("o"), fun->NewInstance());
7222 v8::Handle<Value> value = CompileRun(
7223 "var result = 0;"
7224 "for (var i = 0; i < 100; i++) {"
7225 " result = o.method(41);"
7226 "}");
7227
7228 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7229}
7230
7231THREADED_TEST(CallICFastApi_SimpleSignature) {
7232 v8::HandleScope scope;
7233 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7234 v8::Handle<v8::FunctionTemplate> method_templ =
7235 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7236 v8_str("method_data"),
7237 v8::Signature::New(fun_templ));
7238 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7239 proto_templ->Set(v8_str("method"), method_templ);
7240 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7241 LocalContext context;
7242 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7243 GenerateSomeGarbage();
7244 context->Global()->Set(v8_str("o"), fun->NewInstance());
7245 v8::Handle<Value> value = CompileRun(
7246 "o.foo = 17;"
7247 "var receiver = {};"
7248 "receiver.__proto__ = o;"
7249 "var result = 0;"
7250 "for (var i = 0; i < 100; i++) {"
7251 " result = receiver.method(41);"
7252 "}");
7253
7254 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7255}
7256
Steve Block6ded16b2010-05-10 14:33:55 +01007257THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007258 v8::HandleScope scope;
7259 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7260 v8::Handle<v8::FunctionTemplate> method_templ =
7261 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7262 v8_str("method_data"),
7263 v8::Signature::New(fun_templ));
7264 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7265 proto_templ->Set(v8_str("method"), method_templ);
7266 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7267 LocalContext context;
7268 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7269 GenerateSomeGarbage();
7270 context->Global()->Set(v8_str("o"), fun->NewInstance());
7271 v8::Handle<Value> value = CompileRun(
7272 "o.foo = 17;"
7273 "var receiver = {};"
7274 "receiver.__proto__ = o;"
7275 "var result = 0;"
7276 "var saved_result = 0;"
7277 "for (var i = 0; i < 100; i++) {"
7278 " result = receiver.method(41);"
7279 " if (i == 50) {"
7280 " saved_result = result;"
7281 " receiver = {method: function(x) { return x - 1 }};"
7282 " }"
7283 "}");
7284 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7285 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7286}
7287
Steve Block6ded16b2010-05-10 14:33:55 +01007288THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7289 v8::HandleScope scope;
7290 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7291 v8::Handle<v8::FunctionTemplate> method_templ =
7292 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7293 v8_str("method_data"),
7294 v8::Signature::New(fun_templ));
7295 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7296 proto_templ->Set(v8_str("method"), method_templ);
7297 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7298 LocalContext context;
7299 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7300 GenerateSomeGarbage();
7301 context->Global()->Set(v8_str("o"), fun->NewInstance());
7302 v8::TryCatch try_catch;
7303 v8::Handle<Value> value = CompileRun(
7304 "o.foo = 17;"
7305 "var receiver = {};"
7306 "receiver.__proto__ = o;"
7307 "var result = 0;"
7308 "var saved_result = 0;"
7309 "for (var i = 0; i < 100; i++) {"
7310 " result = receiver.method(41);"
7311 " if (i == 50) {"
7312 " saved_result = result;"
7313 " receiver = 333;"
7314 " }"
7315 "}");
7316 CHECK(try_catch.HasCaught());
7317 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7318 try_catch.Exception()->ToString());
7319 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7320}
7321
Leon Clarke4515c472010-02-03 11:58:03 +00007322
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007323v8::Handle<Value> keyed_call_ic_function;
7324
7325static v8::Handle<Value> InterceptorKeyedCallICGetter(
7326 Local<String> name, const AccessorInfo& info) {
7327 ApiTestFuzzer::Fuzz();
7328 if (v8_str("x")->Equals(name)) {
7329 return keyed_call_ic_function;
7330 }
7331 return v8::Handle<Value>();
7332}
7333
7334
7335// Test the case when we stored cacheable lookup into
7336// a stub, but the function name changed (to another cacheable function).
7337THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7338 v8::HandleScope scope;
7339 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7340 templ->SetNamedPropertyHandler(NoBlockGetterX);
7341 LocalContext context;
7342 context->Global()->Set(v8_str("o"), templ->NewInstance());
7343 v8::Handle<Value> value = CompileRun(
7344 "proto = new Object();"
7345 "proto.y = function(x) { return x + 1; };"
7346 "proto.z = function(x) { return x - 1; };"
7347 "o.__proto__ = proto;"
7348 "var result = 0;"
7349 "var method = 'y';"
7350 "for (var i = 0; i < 10; i++) {"
7351 " if (i == 5) { method = 'z'; };"
7352 " result += o[method](41);"
7353 "}");
7354 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7355}
7356
7357
7358// Test the case when we stored cacheable lookup into
7359// a stub, but the function name changed (and the new function is present
7360// both before and after the interceptor in the prototype chain).
7361THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7362 v8::HandleScope scope;
7363 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7364 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7365 LocalContext context;
7366 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7367 keyed_call_ic_function =
7368 v8_compile("function f(x) { return x - 1; }; f")->Run();
7369 v8::Handle<Value> value = CompileRun(
7370 "o = new Object();"
7371 "proto2 = new Object();"
7372 "o.y = function(x) { return x + 1; };"
7373 "proto2.y = function(x) { return x + 2; };"
7374 "o.__proto__ = proto1;"
7375 "proto1.__proto__ = proto2;"
7376 "var result = 0;"
7377 "var method = 'x';"
7378 "for (var i = 0; i < 10; i++) {"
7379 " if (i == 5) { method = 'y'; };"
7380 " result += o[method](41);"
7381 "}");
7382 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7383}
7384
7385
7386// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7387// on the global object.
7388THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7389 v8::HandleScope scope;
7390 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7391 templ->SetNamedPropertyHandler(NoBlockGetterX);
7392 LocalContext context;
7393 context->Global()->Set(v8_str("o"), templ->NewInstance());
7394 v8::Handle<Value> value = CompileRun(
7395 "function inc(x) { return x + 1; };"
7396 "inc(1);"
7397 "function dec(x) { return x - 1; };"
7398 "dec(1);"
7399 "o.__proto__ = this;"
7400 "this.__proto__.x = inc;"
7401 "this.__proto__.y = dec;"
7402 "var result = 0;"
7403 "var method = 'x';"
7404 "for (var i = 0; i < 10; i++) {"
7405 " if (i == 5) { method = 'y'; };"
7406 " result += o[method](41);"
7407 "}");
7408 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7409}
7410
7411
7412// Test the case when actual function to call sits on global object.
7413THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7414 v8::HandleScope scope;
7415 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7416 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7417 LocalContext context;
7418 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7419
7420 v8::Handle<Value> value = CompileRun(
7421 "function len(x) { return x.length; };"
7422 "o.__proto__ = this;"
7423 "var m = 'parseFloat';"
7424 "var result = 0;"
7425 "for (var i = 0; i < 10; i++) {"
7426 " if (i == 5) {"
7427 " m = 'len';"
7428 " saved_result = result;"
7429 " };"
7430 " result = o[m]('239');"
7431 "}");
7432 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7433 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7434}
7435
7436// Test the map transition before the interceptor.
7437THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7438 v8::HandleScope scope;
7439 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7440 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7441 LocalContext context;
7442 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7443
7444 v8::Handle<Value> value = CompileRun(
7445 "var o = new Object();"
7446 "o.__proto__ = proto;"
7447 "o.method = function(x) { return x + 1; };"
7448 "var m = 'method';"
7449 "var result = 0;"
7450 "for (var i = 0; i < 10; i++) {"
7451 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7452 " result += o[m](41);"
7453 "}");
7454 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7455}
7456
7457
7458// Test the map transition after the interceptor.
7459THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7460 v8::HandleScope scope;
7461 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7462 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7463 LocalContext context;
7464 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7465
7466 v8::Handle<Value> value = CompileRun(
7467 "var proto = new Object();"
7468 "o.__proto__ = proto;"
7469 "proto.method = function(x) { return x + 1; };"
7470 "var m = 'method';"
7471 "var result = 0;"
7472 "for (var i = 0; i < 10; i++) {"
7473 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7474 " result += o[m](41);"
7475 "}");
7476 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7477}
7478
7479
Steve Blocka7e24c12009-10-30 11:49:00 +00007480static int interceptor_call_count = 0;
7481
7482static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7483 const AccessorInfo& info) {
7484 ApiTestFuzzer::Fuzz();
7485 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7486 return call_ic_function2;
7487 }
7488 return v8::Handle<Value>();
7489}
7490
7491
7492// This test should hit load and call ICs for the interceptor case.
7493// Once in a while, the interceptor will reply that a property was not
7494// found in which case we should get a reference error.
7495THREADED_TEST(InterceptorICReferenceErrors) {
7496 v8::HandleScope scope;
7497 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7498 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7499 LocalContext context(0, templ, v8::Handle<Value>());
7500 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7501 v8::Handle<Value> value = CompileRun(
7502 "function f() {"
7503 " for (var i = 0; i < 1000; i++) {"
7504 " try { x; } catch(e) { return true; }"
7505 " }"
7506 " return false;"
7507 "};"
7508 "f();");
7509 CHECK_EQ(true, value->BooleanValue());
7510 interceptor_call_count = 0;
7511 value = CompileRun(
7512 "function g() {"
7513 " for (var i = 0; i < 1000; i++) {"
7514 " try { x(42); } catch(e) { return true; }"
7515 " }"
7516 " return false;"
7517 "};"
7518 "g();");
7519 CHECK_EQ(true, value->BooleanValue());
7520}
7521
7522
7523static int interceptor_ic_exception_get_count = 0;
7524
7525static v8::Handle<Value> InterceptorICExceptionGetter(
7526 Local<String> name,
7527 const AccessorInfo& info) {
7528 ApiTestFuzzer::Fuzz();
7529 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7530 return call_ic_function3;
7531 }
7532 if (interceptor_ic_exception_get_count == 20) {
7533 return v8::ThrowException(v8_num(42));
7534 }
7535 // Do not handle get for properties other than x.
7536 return v8::Handle<Value>();
7537}
7538
7539// Test interceptor load/call IC where the interceptor throws an
7540// exception once in a while.
7541THREADED_TEST(InterceptorICGetterExceptions) {
7542 interceptor_ic_exception_get_count = 0;
7543 v8::HandleScope scope;
7544 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7545 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7546 LocalContext context(0, templ, v8::Handle<Value>());
7547 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7548 v8::Handle<Value> value = CompileRun(
7549 "function f() {"
7550 " for (var i = 0; i < 100; i++) {"
7551 " try { x; } catch(e) { return true; }"
7552 " }"
7553 " return false;"
7554 "};"
7555 "f();");
7556 CHECK_EQ(true, value->BooleanValue());
7557 interceptor_ic_exception_get_count = 0;
7558 value = CompileRun(
7559 "function f() {"
7560 " for (var i = 0; i < 100; i++) {"
7561 " try { x(42); } catch(e) { return true; }"
7562 " }"
7563 " return false;"
7564 "};"
7565 "f();");
7566 CHECK_EQ(true, value->BooleanValue());
7567}
7568
7569
7570static int interceptor_ic_exception_set_count = 0;
7571
7572static v8::Handle<Value> InterceptorICExceptionSetter(
7573 Local<String> key, Local<Value> value, const AccessorInfo&) {
7574 ApiTestFuzzer::Fuzz();
7575 if (++interceptor_ic_exception_set_count > 20) {
7576 return v8::ThrowException(v8_num(42));
7577 }
7578 // Do not actually handle setting.
7579 return v8::Handle<Value>();
7580}
7581
7582// Test interceptor store IC where the interceptor throws an exception
7583// once in a while.
7584THREADED_TEST(InterceptorICSetterExceptions) {
7585 interceptor_ic_exception_set_count = 0;
7586 v8::HandleScope scope;
7587 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7588 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7589 LocalContext context(0, templ, v8::Handle<Value>());
7590 v8::Handle<Value> value = CompileRun(
7591 "function f() {"
7592 " for (var i = 0; i < 100; i++) {"
7593 " try { x = 42; } catch(e) { return true; }"
7594 " }"
7595 " return false;"
7596 "};"
7597 "f();");
7598 CHECK_EQ(true, value->BooleanValue());
7599}
7600
7601
7602// Test that we ignore null interceptors.
7603THREADED_TEST(NullNamedInterceptor) {
7604 v8::HandleScope scope;
7605 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7606 templ->SetNamedPropertyHandler(0);
7607 LocalContext context;
7608 templ->Set("x", v8_num(42));
7609 v8::Handle<v8::Object> obj = templ->NewInstance();
7610 context->Global()->Set(v8_str("obj"), obj);
7611 v8::Handle<Value> value = CompileRun("obj.x");
7612 CHECK(value->IsInt32());
7613 CHECK_EQ(42, value->Int32Value());
7614}
7615
7616
7617// Test that we ignore null interceptors.
7618THREADED_TEST(NullIndexedInterceptor) {
7619 v8::HandleScope scope;
7620 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7621 templ->SetIndexedPropertyHandler(0);
7622 LocalContext context;
7623 templ->Set("42", v8_num(42));
7624 v8::Handle<v8::Object> obj = templ->NewInstance();
7625 context->Global()->Set(v8_str("obj"), obj);
7626 v8::Handle<Value> value = CompileRun("obj[42]");
7627 CHECK(value->IsInt32());
7628 CHECK_EQ(42, value->Int32Value());
7629}
7630
7631
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007632THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7633 v8::HandleScope scope;
7634 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7635 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7636 LocalContext env;
7637 env->Global()->Set(v8_str("obj"),
7638 templ->GetFunction()->NewInstance());
7639 ExpectTrue("obj.x === 42");
7640 ExpectTrue("!obj.propertyIsEnumerable('x')");
7641}
7642
7643
Steve Blocka7e24c12009-10-30 11:49:00 +00007644static v8::Handle<Value> ParentGetter(Local<String> name,
7645 const AccessorInfo& info) {
7646 ApiTestFuzzer::Fuzz();
7647 return v8_num(1);
7648}
7649
7650
7651static v8::Handle<Value> ChildGetter(Local<String> name,
7652 const AccessorInfo& info) {
7653 ApiTestFuzzer::Fuzz();
7654 return v8_num(42);
7655}
7656
7657
7658THREADED_TEST(Overriding) {
7659 v8::HandleScope scope;
7660 LocalContext context;
7661
7662 // Parent template.
7663 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7664 Local<ObjectTemplate> parent_instance_templ =
7665 parent_templ->InstanceTemplate();
7666 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7667
7668 // Template that inherits from the parent template.
7669 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7670 Local<ObjectTemplate> child_instance_templ =
7671 child_templ->InstanceTemplate();
7672 child_templ->Inherit(parent_templ);
7673 // Override 'f'. The child version of 'f' should get called for child
7674 // instances.
7675 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7676 // Add 'g' twice. The 'g' added last should get called for instances.
7677 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7678 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7679
7680 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7681 // so 'h' can be shadowed on the instance object.
7682 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7683 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7684 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7685
7686 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7687 // but the attribute does not have effect because it is duplicated with
7688 // NULL setter.
7689 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7690 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7691
7692
7693
7694 // Instantiate the child template.
7695 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7696
7697 // Check that the child function overrides the parent one.
7698 context->Global()->Set(v8_str("o"), instance);
7699 Local<Value> value = v8_compile("o.f")->Run();
7700 // Check that the 'g' that was added last is hit.
7701 CHECK_EQ(42, value->Int32Value());
7702 value = v8_compile("o.g")->Run();
7703 CHECK_EQ(42, value->Int32Value());
7704
7705 // Check 'h' can be shadowed.
7706 value = v8_compile("o.h = 3; o.h")->Run();
7707 CHECK_EQ(3, value->Int32Value());
7708
7709 // Check 'i' is cannot be shadowed or changed.
7710 value = v8_compile("o.i = 3; o.i")->Run();
7711 CHECK_EQ(42, value->Int32Value());
7712}
7713
7714
7715static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7716 ApiTestFuzzer::Fuzz();
7717 if (args.IsConstructCall()) {
7718 return v8::Boolean::New(true);
7719 }
7720 return v8::Boolean::New(false);
7721}
7722
7723
7724THREADED_TEST(IsConstructCall) {
7725 v8::HandleScope scope;
7726
7727 // Function template with call handler.
7728 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7729 templ->SetCallHandler(IsConstructHandler);
7730
7731 LocalContext context;
7732
7733 context->Global()->Set(v8_str("f"), templ->GetFunction());
7734 Local<Value> value = v8_compile("f()")->Run();
7735 CHECK(!value->BooleanValue());
7736 value = v8_compile("new f()")->Run();
7737 CHECK(value->BooleanValue());
7738}
7739
7740
7741THREADED_TEST(ObjectProtoToString) {
7742 v8::HandleScope scope;
7743 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7744 templ->SetClassName(v8_str("MyClass"));
7745
7746 LocalContext context;
7747
7748 Local<String> customized_tostring = v8_str("customized toString");
7749
7750 // Replace Object.prototype.toString
7751 v8_compile("Object.prototype.toString = function() {"
7752 " return 'customized toString';"
7753 "}")->Run();
7754
7755 // Normal ToString call should call replaced Object.prototype.toString
7756 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7757 Local<String> value = instance->ToString();
7758 CHECK(value->IsString() && value->Equals(customized_tostring));
7759
7760 // ObjectProtoToString should not call replace toString function.
7761 value = instance->ObjectProtoToString();
7762 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7763
7764 // Check global
7765 value = context->Global()->ObjectProtoToString();
7766 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7767
7768 // Check ordinary object
7769 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007770 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007771 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7772}
7773
7774
7775bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01007776i::Semaphore* ApiTestFuzzer::all_tests_done_=
7777 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007778int ApiTestFuzzer::active_tests_;
7779int ApiTestFuzzer::tests_being_run_;
7780int ApiTestFuzzer::current_;
7781
7782
7783// We are in a callback and want to switch to another thread (if we
7784// are currently running the thread fuzzing test).
7785void ApiTestFuzzer::Fuzz() {
7786 if (!fuzzing_) return;
7787 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7788 test->ContextSwitch();
7789}
7790
7791
7792// Let the next thread go. Since it is also waiting on the V8 lock it may
7793// not start immediately.
7794bool ApiTestFuzzer::NextThread() {
7795 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007796 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007797 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007798 if (kLogThreading)
7799 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007800 return false;
7801 }
Steve Blockd0582a62009-12-15 09:54:21 +00007802 if (kLogThreading) {
7803 printf("Switch from %s to %s\n",
7804 test_name,
7805 RegisterThreadedTest::nth(test_position)->name());
7806 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007807 current_ = test_position;
7808 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7809 return true;
7810}
7811
7812
7813void ApiTestFuzzer::Run() {
7814 // When it is our turn...
7815 gate_->Wait();
7816 {
7817 // ... get the V8 lock and start running the test.
7818 v8::Locker locker;
7819 CallTest();
7820 }
7821 // This test finished.
7822 active_ = false;
7823 active_tests_--;
7824 // If it was the last then signal that fact.
7825 if (active_tests_ == 0) {
7826 all_tests_done_->Signal();
7827 } else {
7828 // Otherwise select a new test and start that.
7829 NextThread();
7830 }
7831}
7832
7833
7834static unsigned linear_congruential_generator;
7835
7836
7837void ApiTestFuzzer::Setup(PartOfTest part) {
7838 linear_congruential_generator = i::FLAG_testing_prng_seed;
7839 fuzzing_ = true;
7840 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7841 int end = (part == FIRST_PART)
7842 ? (RegisterThreadedTest::count() >> 1)
7843 : RegisterThreadedTest::count();
7844 active_tests_ = tests_being_run_ = end - start;
7845 for (int i = 0; i < tests_being_run_; i++) {
7846 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7847 }
7848 for (int i = 0; i < active_tests_; i++) {
7849 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7850 }
7851}
7852
7853
7854static void CallTestNumber(int test_number) {
7855 (RegisterThreadedTest::nth(test_number)->callback())();
7856}
7857
7858
7859void ApiTestFuzzer::RunAllTests() {
7860 // Set off the first test.
7861 current_ = -1;
7862 NextThread();
7863 // Wait till they are all done.
7864 all_tests_done_->Wait();
7865}
7866
7867
7868int ApiTestFuzzer::GetNextTestNumber() {
7869 int next_test;
7870 do {
7871 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7872 linear_congruential_generator *= 1664525u;
7873 linear_congruential_generator += 1013904223u;
7874 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7875 return next_test;
7876}
7877
7878
7879void ApiTestFuzzer::ContextSwitch() {
7880 // If the new thread is the same as the current thread there is nothing to do.
7881 if (NextThread()) {
7882 // Now it can start.
7883 v8::Unlocker unlocker;
7884 // Wait till someone starts us again.
7885 gate_->Wait();
7886 // And we're off.
7887 }
7888}
7889
7890
7891void ApiTestFuzzer::TearDown() {
7892 fuzzing_ = false;
7893 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7894 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7895 if (fuzzer != NULL) fuzzer->Join();
7896 }
7897}
7898
7899
7900// Lets not be needlessly self-referential.
7901TEST(Threading) {
7902 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7903 ApiTestFuzzer::RunAllTests();
7904 ApiTestFuzzer::TearDown();
7905}
7906
7907TEST(Threading2) {
7908 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7909 ApiTestFuzzer::RunAllTests();
7910 ApiTestFuzzer::TearDown();
7911}
7912
7913
7914void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007915 if (kLogThreading)
7916 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007917 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007918 if (kLogThreading)
7919 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007920}
7921
7922
7923static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7924 CHECK(v8::Locker::IsLocked());
7925 ApiTestFuzzer::Fuzz();
7926 v8::Unlocker unlocker;
7927 const char* code = "throw 7;";
7928 {
7929 v8::Locker nested_locker;
7930 v8::HandleScope scope;
7931 v8::Handle<Value> exception;
7932 { v8::TryCatch try_catch;
7933 v8::Handle<Value> value = CompileRun(code);
7934 CHECK(value.IsEmpty());
7935 CHECK(try_catch.HasCaught());
7936 // Make sure to wrap the exception in a new handle because
7937 // the handle returned from the TryCatch is destroyed
7938 // when the TryCatch is destroyed.
7939 exception = Local<Value>::New(try_catch.Exception());
7940 }
7941 return v8::ThrowException(exception);
7942 }
7943}
7944
7945
7946static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7947 CHECK(v8::Locker::IsLocked());
7948 ApiTestFuzzer::Fuzz();
7949 v8::Unlocker unlocker;
7950 const char* code = "throw 7;";
7951 {
7952 v8::Locker nested_locker;
7953 v8::HandleScope scope;
7954 v8::Handle<Value> value = CompileRun(code);
7955 CHECK(value.IsEmpty());
7956 return v8_str("foo");
7957 }
7958}
7959
7960
7961// These are locking tests that don't need to be run again
7962// as part of the locking aggregation tests.
7963TEST(NestedLockers) {
7964 v8::Locker locker;
7965 CHECK(v8::Locker::IsLocked());
7966 v8::HandleScope scope;
7967 LocalContext env;
7968 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7969 Local<Function> fun = fun_templ->GetFunction();
7970 env->Global()->Set(v8_str("throw_in_js"), fun);
7971 Local<Script> script = v8_compile("(function () {"
7972 " try {"
7973 " throw_in_js();"
7974 " return 42;"
7975 " } catch (e) {"
7976 " return e * 13;"
7977 " }"
7978 "})();");
7979 CHECK_EQ(91, script->Run()->Int32Value());
7980}
7981
7982
7983// These are locking tests that don't need to be run again
7984// as part of the locking aggregation tests.
7985TEST(NestedLockersNoTryCatch) {
7986 v8::Locker locker;
7987 v8::HandleScope scope;
7988 LocalContext env;
7989 Local<v8::FunctionTemplate> fun_templ =
7990 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7991 Local<Function> fun = fun_templ->GetFunction();
7992 env->Global()->Set(v8_str("throw_in_js"), fun);
7993 Local<Script> script = v8_compile("(function () {"
7994 " try {"
7995 " throw_in_js();"
7996 " return 42;"
7997 " } catch (e) {"
7998 " return e * 13;"
7999 " }"
8000 "})();");
8001 CHECK_EQ(91, script->Run()->Int32Value());
8002}
8003
8004
8005THREADED_TEST(RecursiveLocking) {
8006 v8::Locker locker;
8007 {
8008 v8::Locker locker2;
8009 CHECK(v8::Locker::IsLocked());
8010 }
8011}
8012
8013
8014static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
8015 ApiTestFuzzer::Fuzz();
8016 v8::Unlocker unlocker;
8017 return v8::Undefined();
8018}
8019
8020
8021THREADED_TEST(LockUnlockLock) {
8022 {
8023 v8::Locker locker;
8024 v8::HandleScope scope;
8025 LocalContext env;
8026 Local<v8::FunctionTemplate> fun_templ =
8027 v8::FunctionTemplate::New(UnlockForAMoment);
8028 Local<Function> fun = fun_templ->GetFunction();
8029 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8030 Local<Script> script = v8_compile("(function () {"
8031 " unlock_for_a_moment();"
8032 " return 42;"
8033 "})();");
8034 CHECK_EQ(42, script->Run()->Int32Value());
8035 }
8036 {
8037 v8::Locker locker;
8038 v8::HandleScope scope;
8039 LocalContext env;
8040 Local<v8::FunctionTemplate> fun_templ =
8041 v8::FunctionTemplate::New(UnlockForAMoment);
8042 Local<Function> fun = fun_templ->GetFunction();
8043 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
8044 Local<Script> script = v8_compile("(function () {"
8045 " unlock_for_a_moment();"
8046 " return 42;"
8047 "})();");
8048 CHECK_EQ(42, script->Run()->Int32Value());
8049 }
8050}
8051
8052
Leon Clarked91b9f72010-01-27 17:25:45 +00008053static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00008054 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01008055 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00008056 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
8057 if (object->IsJSGlobalObject()) count++;
8058 return count;
8059}
8060
8061
8062static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00008063 // We need to collect all garbage twice to be sure that everything
8064 // has been collected. This is because inline caches are cleared in
8065 // the first garbage collection but some of the maps have already
8066 // been marked at that point. Therefore some of the maps are not
8067 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01008068 i::Heap::CollectAllGarbage(false);
8069 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00008070 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00008071#ifdef DEBUG
Steve Block8defd9f2010-07-08 12:39:36 +01008072 if (count > 0) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00008073#endif
8074 return count;
8075}
8076
8077
8078TEST(DontLeakGlobalObjects) {
8079 // Regression test for issues 1139850 and 1174891.
8080
8081 v8::V8::Initialize();
8082
8083 int count = GetSurvivingGlobalObjectsCount();
8084
8085 for (int i = 0; i < 5; i++) {
8086 { v8::HandleScope scope;
8087 LocalContext context;
8088 }
8089 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8090
8091 { v8::HandleScope scope;
8092 LocalContext context;
8093 v8_compile("Date")->Run();
8094 }
8095 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8096
8097 { v8::HandleScope scope;
8098 LocalContext context;
8099 v8_compile("/aaa/")->Run();
8100 }
8101 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8102
8103 { v8::HandleScope scope;
8104 const char* extension_list[] = { "v8/gc" };
8105 v8::ExtensionConfiguration extensions(1, extension_list);
8106 LocalContext context(&extensions);
8107 v8_compile("gc();")->Run();
8108 }
8109 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8110 }
8111}
8112
8113
8114v8::Persistent<v8::Object> some_object;
8115v8::Persistent<v8::Object> bad_handle;
8116
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008117void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008118 v8::HandleScope scope;
8119 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008120 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008121}
8122
8123
8124THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8125 LocalContext context;
8126
8127 v8::Persistent<v8::Object> handle1, handle2;
8128 {
8129 v8::HandleScope scope;
8130 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8131 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8132 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8133 }
8134 // Note: order is implementation dependent alas: currently
8135 // global handle nodes are processed by PostGarbageCollectionProcessing
8136 // in reverse allocation order, so if second allocated handle is deleted,
8137 // weak callback of the first handle would be able to 'reallocate' it.
8138 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8139 handle2.Dispose();
8140 i::Heap::CollectAllGarbage(false);
8141}
8142
8143
8144v8::Persistent<v8::Object> to_be_disposed;
8145
8146void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8147 to_be_disposed.Dispose();
8148 i::Heap::CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008149 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008150}
8151
8152
8153THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8154 LocalContext context;
8155
8156 v8::Persistent<v8::Object> handle1, handle2;
8157 {
8158 v8::HandleScope scope;
8159 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8160 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8161 }
8162 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8163 to_be_disposed = handle2;
8164 i::Heap::CollectAllGarbage(false);
8165}
8166
Steve Blockd0582a62009-12-15 09:54:21 +00008167void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8168 handle.Dispose();
8169}
8170
8171void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8172 v8::HandleScope scope;
8173 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008174 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00008175}
8176
8177
8178THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8179 LocalContext context;
8180
8181 v8::Persistent<v8::Object> handle1, handle2, handle3;
8182 {
8183 v8::HandleScope scope;
8184 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8185 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8186 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8187 }
8188 handle2.MakeWeak(NULL, DisposingCallback);
8189 handle3.MakeWeak(NULL, HandleCreatingCallback);
8190 i::Heap::CollectAllGarbage(false);
8191}
8192
Steve Blocka7e24c12009-10-30 11:49:00 +00008193
8194THREADED_TEST(CheckForCrossContextObjectLiterals) {
8195 v8::V8::Initialize();
8196
8197 const int nof = 2;
8198 const char* sources[nof] = {
8199 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8200 "Object()"
8201 };
8202
8203 for (int i = 0; i < nof; i++) {
8204 const char* source = sources[i];
8205 { v8::HandleScope scope;
8206 LocalContext context;
8207 CompileRun(source);
8208 }
8209 { v8::HandleScope scope;
8210 LocalContext context;
8211 CompileRun(source);
8212 }
8213 }
8214}
8215
8216
8217static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8218 v8::HandleScope inner;
8219 env->Enter();
8220 v8::Handle<Value> three = v8_num(3);
8221 v8::Handle<Value> value = inner.Close(three);
8222 env->Exit();
8223 return value;
8224}
8225
8226
8227THREADED_TEST(NestedHandleScopeAndContexts) {
8228 v8::HandleScope outer;
8229 v8::Persistent<Context> env = Context::New();
8230 env->Enter();
8231 v8::Handle<Value> value = NestedScope(env);
8232 v8::Handle<String> str = value->ToString();
8233 env->Exit();
8234 env.Dispose();
8235}
8236
8237
8238THREADED_TEST(ExternalAllocatedMemory) {
8239 v8::HandleScope outer;
8240 v8::Persistent<Context> env = Context::New();
8241 const int kSize = 1024*1024;
8242 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8243 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8244}
8245
8246
8247THREADED_TEST(DisposeEnteredContext) {
8248 v8::HandleScope scope;
8249 LocalContext outer;
8250 { v8::Persistent<v8::Context> inner = v8::Context::New();
8251 inner->Enter();
8252 inner.Dispose();
8253 inner.Clear();
8254 inner->Exit();
8255 }
8256}
8257
8258
8259// Regression test for issue 54, object templates with internal fields
8260// but no accessors or interceptors did not get their internal field
8261// count set on instances.
8262THREADED_TEST(Regress54) {
8263 v8::HandleScope outer;
8264 LocalContext context;
8265 static v8::Persistent<v8::ObjectTemplate> templ;
8266 if (templ.IsEmpty()) {
8267 v8::HandleScope inner;
8268 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8269 local->SetInternalFieldCount(1);
8270 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8271 }
8272 v8::Handle<v8::Object> result = templ->NewInstance();
8273 CHECK_EQ(1, result->InternalFieldCount());
8274}
8275
8276
8277// If part of the threaded tests, this test makes ThreadingTest fail
8278// on mac.
8279TEST(CatchStackOverflow) {
8280 v8::HandleScope scope;
8281 LocalContext context;
8282 v8::TryCatch try_catch;
8283 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8284 "function f() {"
8285 " return f();"
8286 "}"
8287 ""
8288 "f();"));
8289 v8::Handle<v8::Value> result = script->Run();
8290 CHECK(result.IsEmpty());
8291}
8292
8293
8294static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8295 const char* resource_name,
8296 int line_offset) {
8297 v8::HandleScope scope;
8298 v8::TryCatch try_catch;
8299 v8::Handle<v8::Value> result = script->Run();
8300 CHECK(result.IsEmpty());
8301 CHECK(try_catch.HasCaught());
8302 v8::Handle<v8::Message> message = try_catch.Message();
8303 CHECK(!message.IsEmpty());
8304 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8305 CHECK_EQ(91, message->GetStartPosition());
8306 CHECK_EQ(92, message->GetEndPosition());
8307 CHECK_EQ(2, message->GetStartColumn());
8308 CHECK_EQ(3, message->GetEndColumn());
8309 v8::String::AsciiValue line(message->GetSourceLine());
8310 CHECK_EQ(" throw 'nirk';", *line);
8311 v8::String::AsciiValue name(message->GetScriptResourceName());
8312 CHECK_EQ(resource_name, *name);
8313}
8314
8315
8316THREADED_TEST(TryCatchSourceInfo) {
8317 v8::HandleScope scope;
8318 LocalContext context;
8319 v8::Handle<v8::String> source = v8::String::New(
8320 "function Foo() {\n"
8321 " return Bar();\n"
8322 "}\n"
8323 "\n"
8324 "function Bar() {\n"
8325 " return Baz();\n"
8326 "}\n"
8327 "\n"
8328 "function Baz() {\n"
8329 " throw 'nirk';\n"
8330 "}\n"
8331 "\n"
8332 "Foo();\n");
8333
8334 const char* resource_name;
8335 v8::Handle<v8::Script> script;
8336 resource_name = "test.js";
8337 script = v8::Script::Compile(source, v8::String::New(resource_name));
8338 CheckTryCatchSourceInfo(script, resource_name, 0);
8339
8340 resource_name = "test1.js";
8341 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8342 script = v8::Script::Compile(source, &origin1);
8343 CheckTryCatchSourceInfo(script, resource_name, 0);
8344
8345 resource_name = "test2.js";
8346 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8347 script = v8::Script::Compile(source, &origin2);
8348 CheckTryCatchSourceInfo(script, resource_name, 7);
8349}
8350
8351
8352THREADED_TEST(CompilationCache) {
8353 v8::HandleScope scope;
8354 LocalContext context;
8355 v8::Handle<v8::String> source0 = v8::String::New("1234");
8356 v8::Handle<v8::String> source1 = v8::String::New("1234");
8357 v8::Handle<v8::Script> script0 =
8358 v8::Script::Compile(source0, v8::String::New("test.js"));
8359 v8::Handle<v8::Script> script1 =
8360 v8::Script::Compile(source1, v8::String::New("test.js"));
8361 v8::Handle<v8::Script> script2 =
8362 v8::Script::Compile(source0); // different origin
8363 CHECK_EQ(1234, script0->Run()->Int32Value());
8364 CHECK_EQ(1234, script1->Run()->Int32Value());
8365 CHECK_EQ(1234, script2->Run()->Int32Value());
8366}
8367
8368
8369static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8370 ApiTestFuzzer::Fuzz();
8371 return v8_num(42);
8372}
8373
8374
8375THREADED_TEST(CallbackFunctionName) {
8376 v8::HandleScope scope;
8377 LocalContext context;
8378 Local<ObjectTemplate> t = ObjectTemplate::New();
8379 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8380 context->Global()->Set(v8_str("obj"), t->NewInstance());
8381 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8382 CHECK(value->IsString());
8383 v8::String::AsciiValue name(value);
8384 CHECK_EQ("asdf", *name);
8385}
8386
8387
8388THREADED_TEST(DateAccess) {
8389 v8::HandleScope scope;
8390 LocalContext context;
8391 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8392 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008393 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008394}
8395
8396
8397void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008398 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008399 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8400 CHECK_EQ(elmc, props->Length());
8401 for (int i = 0; i < elmc; i++) {
8402 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8403 CHECK_EQ(elmv[i], *elm);
8404 }
8405}
8406
8407
8408THREADED_TEST(PropertyEnumeration) {
8409 v8::HandleScope scope;
8410 LocalContext context;
8411 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8412 "var result = [];"
8413 "result[0] = {};"
8414 "result[1] = {a: 1, b: 2};"
8415 "result[2] = [1, 2, 3];"
8416 "var proto = {x: 1, y: 2, z: 3};"
8417 "var x = { __proto__: proto, w: 0, z: 1 };"
8418 "result[3] = x;"
8419 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008420 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008421 CHECK_EQ(4, elms->Length());
8422 int elmc0 = 0;
8423 const char** elmv0 = NULL;
8424 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8425 int elmc1 = 2;
8426 const char* elmv1[] = {"a", "b"};
8427 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8428 int elmc2 = 3;
8429 const char* elmv2[] = {"0", "1", "2"};
8430 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8431 int elmc3 = 4;
8432 const char* elmv3[] = {"w", "z", "x", "y"};
8433 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8434}
8435
8436
Steve Blocka7e24c12009-10-30 11:49:00 +00008437static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8438 Local<Value> name,
8439 v8::AccessType type,
8440 Local<Value> data) {
8441 return type != v8::ACCESS_SET;
8442}
8443
8444
8445static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8446 uint32_t key,
8447 v8::AccessType type,
8448 Local<Value> data) {
8449 return type != v8::ACCESS_SET;
8450}
8451
8452
8453THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8454 v8::HandleScope scope;
8455 LocalContext context;
8456 Local<ObjectTemplate> templ = ObjectTemplate::New();
8457 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8458 IndexedSetAccessBlocker);
8459 templ->Set(v8_str("x"), v8::True());
8460 Local<v8::Object> instance = templ->NewInstance();
8461 context->Global()->Set(v8_str("obj"), instance);
8462 Local<Value> value = CompileRun("obj.x");
8463 CHECK(value->BooleanValue());
8464}
8465
8466
8467static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8468 Local<Value> name,
8469 v8::AccessType type,
8470 Local<Value> data) {
8471 return false;
8472}
8473
8474
8475static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8476 uint32_t key,
8477 v8::AccessType type,
8478 Local<Value> data) {
8479 return false;
8480}
8481
8482
8483
8484THREADED_TEST(AccessChecksReenabledCorrectly) {
8485 v8::HandleScope scope;
8486 LocalContext context;
8487 Local<ObjectTemplate> templ = ObjectTemplate::New();
8488 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8489 IndexedGetAccessBlocker);
8490 templ->Set(v8_str("a"), v8_str("a"));
8491 // Add more than 8 (see kMaxFastProperties) properties
8492 // so that the constructor will force copying map.
8493 // Cannot sprintf, gcc complains unsafety.
8494 char buf[4];
8495 for (char i = '0'; i <= '9' ; i++) {
8496 buf[0] = i;
8497 for (char j = '0'; j <= '9'; j++) {
8498 buf[1] = j;
8499 for (char k = '0'; k <= '9'; k++) {
8500 buf[2] = k;
8501 buf[3] = 0;
8502 templ->Set(v8_str(buf), v8::Number::New(k));
8503 }
8504 }
8505 }
8506
8507 Local<v8::Object> instance_1 = templ->NewInstance();
8508 context->Global()->Set(v8_str("obj_1"), instance_1);
8509
8510 Local<Value> value_1 = CompileRun("obj_1.a");
8511 CHECK(value_1->IsUndefined());
8512
8513 Local<v8::Object> instance_2 = templ->NewInstance();
8514 context->Global()->Set(v8_str("obj_2"), instance_2);
8515
8516 Local<Value> value_2 = CompileRun("obj_2.a");
8517 CHECK(value_2->IsUndefined());
8518}
8519
8520
8521// This tests that access check information remains on the global
8522// object template when creating contexts.
8523THREADED_TEST(AccessControlRepeatedContextCreation) {
8524 v8::HandleScope handle_scope;
8525 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8526 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8527 IndexedSetAccessBlocker);
8528 i::Handle<i::ObjectTemplateInfo> internal_template =
8529 v8::Utils::OpenHandle(*global_template);
8530 CHECK(!internal_template->constructor()->IsUndefined());
8531 i::Handle<i::FunctionTemplateInfo> constructor(
8532 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8533 CHECK(!constructor->access_check_info()->IsUndefined());
8534 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8535 CHECK(!constructor->access_check_info()->IsUndefined());
8536}
8537
8538
8539THREADED_TEST(TurnOnAccessCheck) {
8540 v8::HandleScope handle_scope;
8541
8542 // Create an environment with access check to the global object disabled by
8543 // default.
8544 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8545 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8546 IndexedGetAccessBlocker,
8547 v8::Handle<v8::Value>(),
8548 false);
8549 v8::Persistent<Context> context = Context::New(NULL, global_template);
8550 Context::Scope context_scope(context);
8551
8552 // Set up a property and a number of functions.
8553 context->Global()->Set(v8_str("a"), v8_num(1));
8554 CompileRun("function f1() {return a;}"
8555 "function f2() {return a;}"
8556 "function g1() {return h();}"
8557 "function g2() {return h();}"
8558 "function h() {return 1;}");
8559 Local<Function> f1 =
8560 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8561 Local<Function> f2 =
8562 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8563 Local<Function> g1 =
8564 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8565 Local<Function> g2 =
8566 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8567 Local<Function> h =
8568 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8569
8570 // Get the global object.
8571 v8::Handle<v8::Object> global = context->Global();
8572
8573 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8574 // uses the runtime system to retreive property a whereas f2 uses global load
8575 // inline cache.
8576 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8577 for (int i = 0; i < 4; i++) {
8578 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8579 }
8580
8581 // Same for g1 and g2.
8582 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8583 for (int i = 0; i < 4; i++) {
8584 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8585 }
8586
8587 // Detach the global and turn on access check.
8588 context->DetachGlobal();
8589 context->Global()->TurnOnAccessCheck();
8590
8591 // Failing access check to property get results in undefined.
8592 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8593 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8594
8595 // Failing access check to function call results in exception.
8596 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8597 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8598
8599 // No failing access check when just returning a constant.
8600 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8601}
8602
8603
8604// This test verifies that pre-compilation (aka preparsing) can be called
8605// without initializing the whole VM. Thus we cannot run this test in a
8606// multi-threaded setup.
8607TEST(PreCompile) {
8608 // TODO(155): This test would break without the initialization of V8. This is
8609 // a workaround for now to make this test not fail.
8610 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008611 const char* script = "function foo(a) { return a+1; }";
8612 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008613 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008614 CHECK_NE(sd->Length(), 0);
8615 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008616 CHECK(!sd->HasError());
8617 delete sd;
8618}
8619
8620
8621TEST(PreCompileWithError) {
8622 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008623 const char* script = "function foo(a) { return 1 * * 2; }";
8624 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008625 v8::ScriptData::PreCompile(script, i::StrLength(script));
8626 CHECK(sd->HasError());
8627 delete sd;
8628}
8629
8630
8631TEST(Regress31661) {
8632 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008633 const char* script = " The Definintive Guide";
8634 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008635 v8::ScriptData::PreCompile(script, i::StrLength(script));
8636 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008637 delete sd;
8638}
8639
8640
Leon Clarkef7060e22010-06-03 12:02:55 +01008641// Tests that ScriptData can be serialized and deserialized.
8642TEST(PreCompileSerialization) {
8643 v8::V8::Initialize();
8644 const char* script = "function foo(a) { return a+1; }";
8645 v8::ScriptData* sd =
8646 v8::ScriptData::PreCompile(script, i::StrLength(script));
8647
8648 // Serialize.
8649 int serialized_data_length = sd->Length();
8650 char* serialized_data = i::NewArray<char>(serialized_data_length);
8651 memcpy(serialized_data, sd->Data(), serialized_data_length);
8652
8653 // Deserialize.
8654 v8::ScriptData* deserialized_sd =
8655 v8::ScriptData::New(serialized_data, serialized_data_length);
8656
8657 // Verify that the original is the same as the deserialized.
8658 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8659 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8660 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8661
8662 delete sd;
8663 delete deserialized_sd;
8664}
8665
8666
8667// Attempts to deserialize bad data.
8668TEST(PreCompileDeserializationError) {
8669 v8::V8::Initialize();
8670 const char* data = "DONT CARE";
8671 int invalid_size = 3;
8672 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8673
8674 CHECK_EQ(0, sd->Length());
8675
8676 delete sd;
8677}
8678
8679
Leon Clarkeac952652010-07-15 11:15:24 +01008680// Attempts to deserialize bad data.
8681TEST(PreCompileInvalidPreparseDataError) {
8682 v8::V8::Initialize();
8683 v8::HandleScope scope;
8684 LocalContext context;
8685
8686 const char* script = "function foo(){ return 5;}\n"
8687 "function bar(){ return 6 + 7;} foo();";
8688 v8::ScriptData* sd =
8689 v8::ScriptData::PreCompile(script, i::StrLength(script));
8690 CHECK(!sd->HasError());
8691 // ScriptDataImpl private implementation details
Iain Merrick9ac36c92010-09-13 15:29:50 +01008692 const int kHeaderSize = i::ScriptDataImpl::kHeaderSize;
8693 const int kFunctionEntrySize = i::FunctionEntry::kSize;
Leon Clarkeac952652010-07-15 11:15:24 +01008694 const int kFunctionEntryStartOffset = 0;
8695 const int kFunctionEntryEndOffset = 1;
8696 unsigned* sd_data =
8697 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01008698
8699 // Overwrite function bar's end position with 0.
8700 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
8701 v8::TryCatch try_catch;
8702
8703 Local<String> source = String::New(script);
8704 Local<Script> compiled_script = Script::New(source, NULL, sd);
8705 CHECK(try_catch.HasCaught());
8706 String::AsciiValue exception_value(try_catch.Message()->Get());
8707 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8708 *exception_value);
8709
8710 try_catch.Reset();
8711 // Overwrite function bar's start position with 200. The function entry
8712 // will not be found when searching for it by position.
Kristian Monsen80d68ea2010-09-08 11:05:35 +01008713 sd = v8::ScriptData::PreCompile(script, i::StrLength(script));
8714 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
Leon Clarkeac952652010-07-15 11:15:24 +01008715 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
8716 200;
8717 compiled_script = Script::New(source, NULL, sd);
8718 CHECK(try_catch.HasCaught());
8719 String::AsciiValue second_exception_value(try_catch.Message()->Get());
8720 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8721 *second_exception_value);
8722
8723 delete sd;
8724}
8725
8726
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008727// Verifies that the Handle<String> and const char* versions of the API produce
8728// the same results (at least for one trivial case).
8729TEST(PreCompileAPIVariationsAreSame) {
8730 v8::V8::Initialize();
8731 v8::HandleScope scope;
8732
8733 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008734
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008735 v8::ScriptData* sd_from_cstring =
8736 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8737
8738 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008739 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008740 v8::String::NewExternal(resource));
8741
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008742 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
8743 v8::String::New(cstring));
8744
8745 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008746 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008747 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008748 sd_from_cstring->Length()));
8749
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008750 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
8751 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8752 sd_from_string->Data(),
8753 sd_from_cstring->Length()));
8754
8755
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008756 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008757 delete sd_from_external_string;
8758 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008759}
8760
8761
Steve Blocka7e24c12009-10-30 11:49:00 +00008762// This tests that we do not allow dictionary load/call inline caches
8763// to use functions that have not yet been compiled. The potential
8764// problem of loading a function that has not yet been compiled can
8765// arise because we share code between contexts via the compilation
8766// cache.
8767THREADED_TEST(DictionaryICLoadedFunction) {
8768 v8::HandleScope scope;
8769 // Test LoadIC.
8770 for (int i = 0; i < 2; i++) {
8771 LocalContext context;
8772 context->Global()->Set(v8_str("tmp"), v8::True());
8773 context->Global()->Delete(v8_str("tmp"));
8774 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8775 }
8776 // Test CallIC.
8777 for (int i = 0; i < 2; i++) {
8778 LocalContext context;
8779 context->Global()->Set(v8_str("tmp"), v8::True());
8780 context->Global()->Delete(v8_str("tmp"));
8781 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8782 }
8783}
8784
8785
8786// Test that cross-context new calls use the context of the callee to
8787// create the new JavaScript object.
8788THREADED_TEST(CrossContextNew) {
8789 v8::HandleScope scope;
8790 v8::Persistent<Context> context0 = Context::New();
8791 v8::Persistent<Context> context1 = Context::New();
8792
8793 // Allow cross-domain access.
8794 Local<String> token = v8_str("<security token>");
8795 context0->SetSecurityToken(token);
8796 context1->SetSecurityToken(token);
8797
8798 // Set an 'x' property on the Object prototype and define a
8799 // constructor function in context0.
8800 context0->Enter();
8801 CompileRun("Object.prototype.x = 42; function C() {};");
8802 context0->Exit();
8803
8804 // Call the constructor function from context0 and check that the
8805 // result has the 'x' property.
8806 context1->Enter();
8807 context1->Global()->Set(v8_str("other"), context0->Global());
8808 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8809 CHECK(value->IsInt32());
8810 CHECK_EQ(42, value->Int32Value());
8811 context1->Exit();
8812
8813 // Dispose the contexts to allow them to be garbage collected.
8814 context0.Dispose();
8815 context1.Dispose();
8816}
8817
8818
8819class RegExpInterruptTest {
8820 public:
8821 RegExpInterruptTest() : block_(NULL) {}
8822 ~RegExpInterruptTest() { delete block_; }
8823 void RunTest() {
8824 block_ = i::OS::CreateSemaphore(0);
8825 gc_count_ = 0;
8826 gc_during_regexp_ = 0;
8827 regexp_success_ = false;
8828 gc_success_ = false;
8829 GCThread gc_thread(this);
8830 gc_thread.Start();
8831 v8::Locker::StartPreemption(1);
8832
8833 LongRunningRegExp();
8834 {
8835 v8::Unlocker unlock;
8836 gc_thread.Join();
8837 }
8838 v8::Locker::StopPreemption();
8839 CHECK(regexp_success_);
8840 CHECK(gc_success_);
8841 }
8842 private:
8843 // Number of garbage collections required.
8844 static const int kRequiredGCs = 5;
8845
8846 class GCThread : public i::Thread {
8847 public:
8848 explicit GCThread(RegExpInterruptTest* test)
8849 : test_(test) {}
8850 virtual void Run() {
8851 test_->CollectGarbage();
8852 }
8853 private:
8854 RegExpInterruptTest* test_;
8855 };
8856
8857 void CollectGarbage() {
8858 block_->Wait();
8859 while (gc_during_regexp_ < kRequiredGCs) {
8860 {
8861 v8::Locker lock;
8862 // TODO(lrn): Perhaps create some garbage before collecting.
8863 i::Heap::CollectAllGarbage(false);
8864 gc_count_++;
8865 }
8866 i::OS::Sleep(1);
8867 }
8868 gc_success_ = true;
8869 }
8870
8871 void LongRunningRegExp() {
8872 block_->Signal(); // Enable garbage collection thread on next preemption.
8873 int rounds = 0;
8874 while (gc_during_regexp_ < kRequiredGCs) {
8875 int gc_before = gc_count_;
8876 {
8877 // Match 15-30 "a"'s against 14 and a "b".
8878 const char* c_source =
8879 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8880 ".exec('aaaaaaaaaaaaaaab') === null";
8881 Local<String> source = String::New(c_source);
8882 Local<Script> script = Script::Compile(source);
8883 Local<Value> result = script->Run();
8884 if (!result->BooleanValue()) {
8885 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8886 return;
8887 }
8888 }
8889 {
8890 // Match 15-30 "a"'s against 15 and a "b".
8891 const char* c_source =
8892 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8893 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8894 Local<String> source = String::New(c_source);
8895 Local<Script> script = Script::Compile(source);
8896 Local<Value> result = script->Run();
8897 if (!result->BooleanValue()) {
8898 gc_during_regexp_ = kRequiredGCs;
8899 return;
8900 }
8901 }
8902 int gc_after = gc_count_;
8903 gc_during_regexp_ += gc_after - gc_before;
8904 rounds++;
8905 i::OS::Sleep(1);
8906 }
8907 regexp_success_ = true;
8908 }
8909
8910 i::Semaphore* block_;
8911 int gc_count_;
8912 int gc_during_regexp_;
8913 bool regexp_success_;
8914 bool gc_success_;
8915};
8916
8917
8918// Test that a regular expression execution can be interrupted and
8919// survive a garbage collection.
8920TEST(RegExpInterruption) {
8921 v8::Locker lock;
8922 v8::V8::Initialize();
8923 v8::HandleScope scope;
8924 Local<Context> local_env;
8925 {
8926 LocalContext env;
8927 local_env = env.local();
8928 }
8929
8930 // Local context should still be live.
8931 CHECK(!local_env.IsEmpty());
8932 local_env->Enter();
8933
8934 // Should complete without problems.
8935 RegExpInterruptTest().RunTest();
8936
8937 local_env->Exit();
8938}
8939
8940
8941class ApplyInterruptTest {
8942 public:
8943 ApplyInterruptTest() : block_(NULL) {}
8944 ~ApplyInterruptTest() { delete block_; }
8945 void RunTest() {
8946 block_ = i::OS::CreateSemaphore(0);
8947 gc_count_ = 0;
8948 gc_during_apply_ = 0;
8949 apply_success_ = false;
8950 gc_success_ = false;
8951 GCThread gc_thread(this);
8952 gc_thread.Start();
8953 v8::Locker::StartPreemption(1);
8954
8955 LongRunningApply();
8956 {
8957 v8::Unlocker unlock;
8958 gc_thread.Join();
8959 }
8960 v8::Locker::StopPreemption();
8961 CHECK(apply_success_);
8962 CHECK(gc_success_);
8963 }
8964 private:
8965 // Number of garbage collections required.
8966 static const int kRequiredGCs = 2;
8967
8968 class GCThread : public i::Thread {
8969 public:
8970 explicit GCThread(ApplyInterruptTest* test)
8971 : test_(test) {}
8972 virtual void Run() {
8973 test_->CollectGarbage();
8974 }
8975 private:
8976 ApplyInterruptTest* test_;
8977 };
8978
8979 void CollectGarbage() {
8980 block_->Wait();
8981 while (gc_during_apply_ < kRequiredGCs) {
8982 {
8983 v8::Locker lock;
8984 i::Heap::CollectAllGarbage(false);
8985 gc_count_++;
8986 }
8987 i::OS::Sleep(1);
8988 }
8989 gc_success_ = true;
8990 }
8991
8992 void LongRunningApply() {
8993 block_->Signal();
8994 int rounds = 0;
8995 while (gc_during_apply_ < kRequiredGCs) {
8996 int gc_before = gc_count_;
8997 {
8998 const char* c_source =
8999 "function do_very_little(bar) {"
9000 " this.foo = bar;"
9001 "}"
9002 "for (var i = 0; i < 100000; i++) {"
9003 " do_very_little.apply(this, ['bar']);"
9004 "}";
9005 Local<String> source = String::New(c_source);
9006 Local<Script> script = Script::Compile(source);
9007 Local<Value> result = script->Run();
9008 // Check that no exception was thrown.
9009 CHECK(!result.IsEmpty());
9010 }
9011 int gc_after = gc_count_;
9012 gc_during_apply_ += gc_after - gc_before;
9013 rounds++;
9014 }
9015 apply_success_ = true;
9016 }
9017
9018 i::Semaphore* block_;
9019 int gc_count_;
9020 int gc_during_apply_;
9021 bool apply_success_;
9022 bool gc_success_;
9023};
9024
9025
9026// Test that nothing bad happens if we get a preemption just when we were
9027// about to do an apply().
9028TEST(ApplyInterruption) {
9029 v8::Locker lock;
9030 v8::V8::Initialize();
9031 v8::HandleScope scope;
9032 Local<Context> local_env;
9033 {
9034 LocalContext env;
9035 local_env = env.local();
9036 }
9037
9038 // Local context should still be live.
9039 CHECK(!local_env.IsEmpty());
9040 local_env->Enter();
9041
9042 // Should complete without problems.
9043 ApplyInterruptTest().RunTest();
9044
9045 local_env->Exit();
9046}
9047
9048
9049// Verify that we can clone an object
9050TEST(ObjectClone) {
9051 v8::HandleScope scope;
9052 LocalContext env;
9053
9054 const char* sample =
9055 "var rv = {};" \
9056 "rv.alpha = 'hello';" \
9057 "rv.beta = 123;" \
9058 "rv;";
9059
9060 // Create an object, verify basics.
9061 Local<Value> val = CompileRun(sample);
9062 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01009063 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00009064 obj->Set(v8_str("gamma"), v8_str("cloneme"));
9065
9066 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
9067 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9068 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
9069
9070 // Clone it.
9071 Local<v8::Object> clone = obj->Clone();
9072 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
9073 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
9074 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
9075
9076 // Set a property on the clone, verify each object.
9077 clone->Set(v8_str("beta"), v8::Integer::New(456));
9078 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
9079 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
9080}
9081
9082
9083class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
9084 public:
9085 explicit AsciiVectorResource(i::Vector<const char> vector)
9086 : data_(vector) {}
9087 virtual ~AsciiVectorResource() {}
9088 virtual size_t length() const { return data_.length(); }
9089 virtual const char* data() const { return data_.start(); }
9090 private:
9091 i::Vector<const char> data_;
9092};
9093
9094
9095class UC16VectorResource : public v8::String::ExternalStringResource {
9096 public:
9097 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
9098 : data_(vector) {}
9099 virtual ~UC16VectorResource() {}
9100 virtual size_t length() const { return data_.length(); }
9101 virtual const i::uc16* data() const { return data_.start(); }
9102 private:
9103 i::Vector<const i::uc16> data_;
9104};
9105
9106
9107static void MorphAString(i::String* string,
9108 AsciiVectorResource* ascii_resource,
9109 UC16VectorResource* uc16_resource) {
9110 CHECK(i::StringShape(string).IsExternal());
9111 if (string->IsAsciiRepresentation()) {
9112 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009113 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009114 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009115 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009116 i::ExternalTwoByteString* morphed =
9117 i::ExternalTwoByteString::cast(string);
9118 morphed->set_resource(uc16_resource);
9119 } else {
9120 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009121 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009122 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009123 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009124 i::ExternalAsciiString* morphed =
9125 i::ExternalAsciiString::cast(string);
9126 morphed->set_resource(ascii_resource);
9127 }
9128}
9129
9130
9131// Test that we can still flatten a string if the components it is built up
9132// from have been turned into 16 bit strings in the mean time.
9133THREADED_TEST(MorphCompositeStringTest) {
9134 const char* c_string = "Now is the time for all good men"
9135 " to come to the aid of the party";
9136 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9137 {
9138 v8::HandleScope scope;
9139 LocalContext env;
9140 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009141 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009142 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009143 i::Vector<const uint16_t>(two_byte_string,
9144 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009145
9146 Local<String> lhs(v8::Utils::ToLocal(
9147 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9148 Local<String> rhs(v8::Utils::ToLocal(
9149 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9150
9151 env->Global()->Set(v8_str("lhs"), lhs);
9152 env->Global()->Set(v8_str("rhs"), rhs);
9153
9154 CompileRun(
9155 "var cons = lhs + rhs;"
9156 "var slice = lhs.substring(1, lhs.length - 1);"
9157 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
9158
9159 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
9160 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
9161
9162 // Now do some stuff to make sure the strings are flattened, etc.
9163 CompileRun(
9164 "/[^a-z]/.test(cons);"
9165 "/[^a-z]/.test(slice);"
9166 "/[^a-z]/.test(slice_on_cons);");
9167 const char* expected_cons =
9168 "Now is the time for all good men to come to the aid of the party"
9169 "Now is the time for all good men to come to the aid of the party";
9170 const char* expected_slice =
9171 "ow is the time for all good men to come to the aid of the part";
9172 const char* expected_slice_on_cons =
9173 "ow is the time for all good men to come to the aid of the party"
9174 "Now is the time for all good men to come to the aid of the part";
9175 CHECK_EQ(String::New(expected_cons),
9176 env->Global()->Get(v8_str("cons")));
9177 CHECK_EQ(String::New(expected_slice),
9178 env->Global()->Get(v8_str("slice")));
9179 CHECK_EQ(String::New(expected_slice_on_cons),
9180 env->Global()->Get(v8_str("slice_on_cons")));
9181 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009182 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009183}
9184
9185
9186TEST(CompileExternalTwoByteSource) {
9187 v8::HandleScope scope;
9188 LocalContext context;
9189
9190 // This is a very short list of sources, which currently is to check for a
9191 // regression caused by r2703.
9192 const char* ascii_sources[] = {
9193 "0.5",
9194 "-0.5", // This mainly testes PushBack in the Scanner.
9195 "--0.5", // This mainly testes PushBack in the Scanner.
9196 NULL
9197 };
9198
9199 // Compile the sources as external two byte strings.
9200 for (int i = 0; ascii_sources[i] != NULL; i++) {
9201 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9202 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009203 i::Vector<const uint16_t>(two_byte_string,
9204 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009205 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9206 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009207 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009208 }
9209}
9210
9211
9212class RegExpStringModificationTest {
9213 public:
9214 RegExpStringModificationTest()
9215 : block_(i::OS::CreateSemaphore(0)),
9216 morphs_(0),
9217 morphs_during_regexp_(0),
9218 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9219 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9220 ~RegExpStringModificationTest() { delete block_; }
9221 void RunTest() {
9222 regexp_success_ = false;
9223 morph_success_ = false;
9224
9225 // Initialize the contents of two_byte_content_ to be a uc16 representation
9226 // of "aaaaaaaaaaaaaab".
9227 for (int i = 0; i < 14; i++) {
9228 two_byte_content_[i] = 'a';
9229 }
9230 two_byte_content_[14] = 'b';
9231
9232 // Create the input string for the regexp - the one we are going to change
9233 // properties of.
9234 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9235
9236 // Inject the input as a global variable.
9237 i::Handle<i::String> input_name =
9238 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
9239 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
9240
9241
9242 MorphThread morph_thread(this);
9243 morph_thread.Start();
9244 v8::Locker::StartPreemption(1);
9245 LongRunningRegExp();
9246 {
9247 v8::Unlocker unlock;
9248 morph_thread.Join();
9249 }
9250 v8::Locker::StopPreemption();
9251 CHECK(regexp_success_);
9252 CHECK(morph_success_);
9253 }
9254 private:
9255
9256 // Number of string modifications required.
9257 static const int kRequiredModifications = 5;
9258 static const int kMaxModifications = 100;
9259
9260 class MorphThread : public i::Thread {
9261 public:
9262 explicit MorphThread(RegExpStringModificationTest* test)
9263 : test_(test) {}
9264 virtual void Run() {
9265 test_->MorphString();
9266 }
9267 private:
9268 RegExpStringModificationTest* test_;
9269 };
9270
9271 void MorphString() {
9272 block_->Wait();
9273 while (morphs_during_regexp_ < kRequiredModifications &&
9274 morphs_ < kMaxModifications) {
9275 {
9276 v8::Locker lock;
9277 // Swap string between ascii and two-byte representation.
9278 i::String* string = *input_;
9279 MorphAString(string, &ascii_resource_, &uc16_resource_);
9280 morphs_++;
9281 }
9282 i::OS::Sleep(1);
9283 }
9284 morph_success_ = true;
9285 }
9286
9287 void LongRunningRegExp() {
9288 block_->Signal(); // Enable morphing thread on next preemption.
9289 while (morphs_during_regexp_ < kRequiredModifications &&
9290 morphs_ < kMaxModifications) {
9291 int morphs_before = morphs_;
9292 {
Steve Block791712a2010-08-27 10:21:07 +01009293 v8::HandleScope scope;
Steve Blocka7e24c12009-10-30 11:49:00 +00009294 // Match 15-30 "a"'s against 14 and a "b".
9295 const char* c_source =
9296 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9297 ".exec(input) === null";
9298 Local<String> source = String::New(c_source);
9299 Local<Script> script = Script::Compile(source);
9300 Local<Value> result = script->Run();
9301 CHECK(result->IsTrue());
9302 }
9303 int morphs_after = morphs_;
9304 morphs_during_regexp_ += morphs_after - morphs_before;
9305 }
9306 regexp_success_ = true;
9307 }
9308
9309 i::uc16 two_byte_content_[15];
9310 i::Semaphore* block_;
9311 int morphs_;
9312 int morphs_during_regexp_;
9313 bool regexp_success_;
9314 bool morph_success_;
9315 i::Handle<i::String> input_;
9316 AsciiVectorResource ascii_resource_;
9317 UC16VectorResource uc16_resource_;
9318};
9319
9320
9321// Test that a regular expression execution can be interrupted and
9322// the string changed without failing.
9323TEST(RegExpStringModification) {
9324 v8::Locker lock;
9325 v8::V8::Initialize();
9326 v8::HandleScope scope;
9327 Local<Context> local_env;
9328 {
9329 LocalContext env;
9330 local_env = env.local();
9331 }
9332
9333 // Local context should still be live.
9334 CHECK(!local_env.IsEmpty());
9335 local_env->Enter();
9336
9337 // Should complete without problems.
9338 RegExpStringModificationTest().RunTest();
9339
9340 local_env->Exit();
9341}
9342
9343
9344// Test that we can set a property on the global object even if there
9345// is a read-only property in the prototype chain.
9346TEST(ReadOnlyPropertyInGlobalProto) {
9347 v8::HandleScope scope;
9348 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9349 LocalContext context(0, templ);
9350 v8::Handle<v8::Object> global = context->Global();
9351 v8::Handle<v8::Object> global_proto =
9352 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9353 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9354 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9355 // Check without 'eval' or 'with'.
9356 v8::Handle<v8::Value> res =
9357 CompileRun("function f() { x = 42; return x; }; f()");
9358 // Check with 'eval'.
9359 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9360 CHECK_EQ(v8::Integer::New(42), res);
9361 // Check with 'with'.
9362 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9363 CHECK_EQ(v8::Integer::New(42), res);
9364}
9365
9366static int force_set_set_count = 0;
9367static int force_set_get_count = 0;
9368bool pass_on_get = false;
9369
9370static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9371 const v8::AccessorInfo& info) {
9372 force_set_get_count++;
9373 if (pass_on_get) {
9374 return v8::Handle<v8::Value>();
9375 } else {
9376 return v8::Int32::New(3);
9377 }
9378}
9379
9380static void ForceSetSetter(v8::Local<v8::String> name,
9381 v8::Local<v8::Value> value,
9382 const v8::AccessorInfo& info) {
9383 force_set_set_count++;
9384}
9385
9386static v8::Handle<v8::Value> ForceSetInterceptSetter(
9387 v8::Local<v8::String> name,
9388 v8::Local<v8::Value> value,
9389 const v8::AccessorInfo& info) {
9390 force_set_set_count++;
9391 return v8::Undefined();
9392}
9393
9394TEST(ForceSet) {
9395 force_set_get_count = 0;
9396 force_set_set_count = 0;
9397 pass_on_get = false;
9398
9399 v8::HandleScope scope;
9400 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9401 v8::Handle<v8::String> access_property = v8::String::New("a");
9402 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9403 LocalContext context(NULL, templ);
9404 v8::Handle<v8::Object> global = context->Global();
9405
9406 // Ordinary properties
9407 v8::Handle<v8::String> simple_property = v8::String::New("p");
9408 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9409 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9410 // This should fail because the property is read-only
9411 global->Set(simple_property, v8::Int32::New(5));
9412 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9413 // This should succeed even though the property is read-only
9414 global->ForceSet(simple_property, v8::Int32::New(6));
9415 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9416
9417 // Accessors
9418 CHECK_EQ(0, force_set_set_count);
9419 CHECK_EQ(0, force_set_get_count);
9420 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9421 // CHECK_EQ the property shouldn't override it, just call the setter
9422 // which in this case does nothing.
9423 global->Set(access_property, v8::Int32::New(7));
9424 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9425 CHECK_EQ(1, force_set_set_count);
9426 CHECK_EQ(2, force_set_get_count);
9427 // Forcing the property to be set should override the accessor without
9428 // calling it
9429 global->ForceSet(access_property, v8::Int32::New(8));
9430 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9431 CHECK_EQ(1, force_set_set_count);
9432 CHECK_EQ(2, force_set_get_count);
9433}
9434
9435TEST(ForceSetWithInterceptor) {
9436 force_set_get_count = 0;
9437 force_set_set_count = 0;
9438 pass_on_get = false;
9439
9440 v8::HandleScope scope;
9441 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9442 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9443 LocalContext context(NULL, templ);
9444 v8::Handle<v8::Object> global = context->Global();
9445
9446 v8::Handle<v8::String> some_property = v8::String::New("a");
9447 CHECK_EQ(0, force_set_set_count);
9448 CHECK_EQ(0, force_set_get_count);
9449 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9450 // Setting the property shouldn't override it, just call the setter
9451 // which in this case does nothing.
9452 global->Set(some_property, v8::Int32::New(7));
9453 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9454 CHECK_EQ(1, force_set_set_count);
9455 CHECK_EQ(2, force_set_get_count);
9456 // Getting the property when the interceptor returns an empty handle
9457 // should yield undefined, since the property isn't present on the
9458 // object itself yet.
9459 pass_on_get = true;
9460 CHECK(global->Get(some_property)->IsUndefined());
9461 CHECK_EQ(1, force_set_set_count);
9462 CHECK_EQ(3, force_set_get_count);
9463 // Forcing the property to be set should cause the value to be
9464 // set locally without calling the interceptor.
9465 global->ForceSet(some_property, v8::Int32::New(8));
9466 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9467 CHECK_EQ(1, force_set_set_count);
9468 CHECK_EQ(4, force_set_get_count);
9469 // Reenabling the interceptor should cause it to take precedence over
9470 // the property
9471 pass_on_get = false;
9472 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9473 CHECK_EQ(1, force_set_set_count);
9474 CHECK_EQ(5, force_set_get_count);
9475 // The interceptor should also work for other properties
9476 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9477 CHECK_EQ(1, force_set_set_count);
9478 CHECK_EQ(6, force_set_get_count);
9479}
9480
9481
9482THREADED_TEST(ForceDelete) {
9483 v8::HandleScope scope;
9484 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9485 LocalContext context(NULL, templ);
9486 v8::Handle<v8::Object> global = context->Global();
9487
9488 // Ordinary properties
9489 v8::Handle<v8::String> simple_property = v8::String::New("p");
9490 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9491 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9492 // This should fail because the property is dont-delete.
9493 CHECK(!global->Delete(simple_property));
9494 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9495 // This should succeed even though the property is dont-delete.
9496 CHECK(global->ForceDelete(simple_property));
9497 CHECK(global->Get(simple_property)->IsUndefined());
9498}
9499
9500
9501static int force_delete_interceptor_count = 0;
9502static bool pass_on_delete = false;
9503
9504
9505static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9506 v8::Local<v8::String> name,
9507 const v8::AccessorInfo& info) {
9508 force_delete_interceptor_count++;
9509 if (pass_on_delete) {
9510 return v8::Handle<v8::Boolean>();
9511 } else {
9512 return v8::True();
9513 }
9514}
9515
9516
9517THREADED_TEST(ForceDeleteWithInterceptor) {
9518 force_delete_interceptor_count = 0;
9519 pass_on_delete = false;
9520
9521 v8::HandleScope scope;
9522 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9523 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9524 LocalContext context(NULL, templ);
9525 v8::Handle<v8::Object> global = context->Global();
9526
9527 v8::Handle<v8::String> some_property = v8::String::New("a");
9528 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9529
9530 // Deleting a property should get intercepted and nothing should
9531 // happen.
9532 CHECK_EQ(0, force_delete_interceptor_count);
9533 CHECK(global->Delete(some_property));
9534 CHECK_EQ(1, force_delete_interceptor_count);
9535 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9536 // Deleting the property when the interceptor returns an empty
9537 // handle should not delete the property since it is DontDelete.
9538 pass_on_delete = true;
9539 CHECK(!global->Delete(some_property));
9540 CHECK_EQ(2, force_delete_interceptor_count);
9541 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9542 // Forcing the property to be deleted should delete the value
9543 // without calling the interceptor.
9544 CHECK(global->ForceDelete(some_property));
9545 CHECK(global->Get(some_property)->IsUndefined());
9546 CHECK_EQ(2, force_delete_interceptor_count);
9547}
9548
9549
9550// Make sure that forcing a delete invalidates any IC stubs, so we
9551// don't read the hole value.
9552THREADED_TEST(ForceDeleteIC) {
9553 v8::HandleScope scope;
9554 LocalContext context;
9555 // Create a DontDelete variable on the global object.
9556 CompileRun("this.__proto__ = { foo: 'horse' };"
9557 "var foo = 'fish';"
9558 "function f() { return foo.length; }");
9559 // Initialize the IC for foo in f.
9560 CompileRun("for (var i = 0; i < 4; i++) f();");
9561 // Make sure the value of foo is correct before the deletion.
9562 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9563 // Force the deletion of foo.
9564 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9565 // Make sure the value for foo is read from the prototype, and that
9566 // we don't get in trouble with reading the deleted cell value
9567 // sentinel.
9568 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9569}
9570
9571
9572v8::Persistent<Context> calling_context0;
9573v8::Persistent<Context> calling_context1;
9574v8::Persistent<Context> calling_context2;
9575
9576
9577// Check that the call to the callback is initiated in
9578// calling_context2, the directly calling context is calling_context1
9579// and the callback itself is in calling_context0.
9580static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9581 ApiTestFuzzer::Fuzz();
9582 CHECK(Context::GetCurrent() == calling_context0);
9583 CHECK(Context::GetCalling() == calling_context1);
9584 CHECK(Context::GetEntered() == calling_context2);
9585 return v8::Integer::New(42);
9586}
9587
9588
9589THREADED_TEST(GetCallingContext) {
9590 v8::HandleScope scope;
9591
9592 calling_context0 = Context::New();
9593 calling_context1 = Context::New();
9594 calling_context2 = Context::New();
9595
9596 // Allow cross-domain access.
9597 Local<String> token = v8_str("<security token>");
9598 calling_context0->SetSecurityToken(token);
9599 calling_context1->SetSecurityToken(token);
9600 calling_context2->SetSecurityToken(token);
9601
9602 // Create an object with a C++ callback in context0.
9603 calling_context0->Enter();
9604 Local<v8::FunctionTemplate> callback_templ =
9605 v8::FunctionTemplate::New(GetCallingContextCallback);
9606 calling_context0->Global()->Set(v8_str("callback"),
9607 callback_templ->GetFunction());
9608 calling_context0->Exit();
9609
9610 // Expose context0 in context1 and setup a function that calls the
9611 // callback function.
9612 calling_context1->Enter();
9613 calling_context1->Global()->Set(v8_str("context0"),
9614 calling_context0->Global());
9615 CompileRun("function f() { context0.callback() }");
9616 calling_context1->Exit();
9617
9618 // Expose context1 in context2 and call the callback function in
9619 // context0 indirectly through f in context1.
9620 calling_context2->Enter();
9621 calling_context2->Global()->Set(v8_str("context1"),
9622 calling_context1->Global());
9623 CompileRun("context1.f()");
9624 calling_context2->Exit();
9625
9626 // Dispose the contexts to allow them to be garbage collected.
9627 calling_context0.Dispose();
9628 calling_context1.Dispose();
9629 calling_context2.Dispose();
9630 calling_context0.Clear();
9631 calling_context1.Clear();
9632 calling_context2.Clear();
9633}
9634
9635
9636// Check that a variable declaration with no explicit initialization
9637// value does not shadow an existing property in the prototype chain.
9638//
9639// This is consistent with Firefox and Safari.
9640//
9641// See http://crbug.com/12548.
9642THREADED_TEST(InitGlobalVarInProtoChain) {
9643 v8::HandleScope scope;
9644 LocalContext context;
9645 // Introduce a variable in the prototype chain.
9646 CompileRun("__proto__.x = 42");
9647 v8::Handle<v8::Value> result = CompileRun("var x; x");
9648 CHECK(!result->IsUndefined());
9649 CHECK_EQ(42, result->Int32Value());
9650}
9651
9652
9653// Regression test for issue 398.
9654// If a function is added to an object, creating a constant function
9655// field, and the result is cloned, replacing the constant function on the
9656// original should not affect the clone.
9657// See http://code.google.com/p/v8/issues/detail?id=398
9658THREADED_TEST(ReplaceConstantFunction) {
9659 v8::HandleScope scope;
9660 LocalContext context;
9661 v8::Handle<v8::Object> obj = v8::Object::New();
9662 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9663 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9664 obj->Set(foo_string, func_templ->GetFunction());
9665 v8::Handle<v8::Object> obj_clone = obj->Clone();
9666 obj_clone->Set(foo_string, v8::String::New("Hello"));
9667 CHECK(!obj->Get(foo_string)->IsUndefined());
9668}
9669
9670
9671// Regression test for http://crbug.com/16276.
9672THREADED_TEST(Regress16276) {
9673 v8::HandleScope scope;
9674 LocalContext context;
9675 // Force the IC in f to be a dictionary load IC.
9676 CompileRun("function f(obj) { return obj.x; }\n"
9677 "var obj = { x: { foo: 42 }, y: 87 };\n"
9678 "var x = obj.x;\n"
9679 "delete obj.y;\n"
9680 "for (var i = 0; i < 5; i++) f(obj);");
9681 // Detach the global object to make 'this' refer directly to the
9682 // global object (not the proxy), and make sure that the dictionary
9683 // load IC doesn't mess up loading directly from the global object.
9684 context->DetachGlobal();
9685 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9686}
9687
9688
9689THREADED_TEST(PixelArray) {
9690 v8::HandleScope scope;
9691 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009692 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009693 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9694 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9695 pixel_data);
9696 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9697 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009698 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009699 }
9700 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9701 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009702 CHECK_EQ(i % 256, pixels->get(i));
9703 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009704 }
9705
9706 v8::Handle<v8::Object> obj = v8::Object::New();
9707 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9708 // Set the elements to be the pixels.
9709 // jsobj->set_elements(*pixels);
9710 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9711 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9712 obj->Set(v8_str("field"), v8::Int32::New(1503));
9713 context->Global()->Set(v8_str("pixels"), obj);
9714 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9715 CHECK_EQ(1503, result->Int32Value());
9716 result = CompileRun("pixels[1]");
9717 CHECK_EQ(1, result->Int32Value());
9718
9719 result = CompileRun("var sum = 0;"
9720 "for (var i = 0; i < 8; i++) {"
9721 " sum += pixels[i] = pixels[i] = -i;"
9722 "}"
9723 "sum;");
9724 CHECK_EQ(-28, result->Int32Value());
9725
9726 result = CompileRun("var sum = 0;"
9727 "for (var i = 0; i < 8; i++) {"
9728 " sum += pixels[i] = pixels[i] = 0;"
9729 "}"
9730 "sum;");
9731 CHECK_EQ(0, result->Int32Value());
9732
9733 result = CompileRun("var sum = 0;"
9734 "for (var i = 0; i < 8; i++) {"
9735 " sum += pixels[i] = pixels[i] = 255;"
9736 "}"
9737 "sum;");
9738 CHECK_EQ(8 * 255, result->Int32Value());
9739
9740 result = CompileRun("var sum = 0;"
9741 "for (var i = 0; i < 8; i++) {"
9742 " sum += pixels[i] = pixels[i] = 256 + i;"
9743 "}"
9744 "sum;");
9745 CHECK_EQ(2076, result->Int32Value());
9746
9747 result = CompileRun("var sum = 0;"
9748 "for (var i = 0; i < 8; i++) {"
9749 " sum += pixels[i] = pixels[i] = i;"
9750 "}"
9751 "sum;");
9752 CHECK_EQ(28, result->Int32Value());
9753
9754 result = CompileRun("var sum = 0;"
9755 "for (var i = 0; i < 8; i++) {"
9756 " sum += pixels[i];"
9757 "}"
9758 "sum;");
9759 CHECK_EQ(28, result->Int32Value());
9760
9761 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9762 i::SetElement(jsobj, 1, value);
9763 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9764 *value.location() = i::Smi::FromInt(256);
9765 i::SetElement(jsobj, 1, value);
9766 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9767 *value.location() = i::Smi::FromInt(-1);
9768 i::SetElement(jsobj, 1, value);
9769 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9770
9771 result = CompileRun("for (var i = 0; i < 8; i++) {"
9772 " pixels[i] = (i * 65) - 109;"
9773 "}"
9774 "pixels[1] + pixels[6];");
9775 CHECK_EQ(255, result->Int32Value());
9776 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9777 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9778 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9779 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9780 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9781 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9782 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9783 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9784 result = CompileRun("var sum = 0;"
9785 "for (var i = 0; i < 8; i++) {"
9786 " sum += pixels[i];"
9787 "}"
9788 "sum;");
9789 CHECK_EQ(984, result->Int32Value());
9790
9791 result = CompileRun("for (var i = 0; i < 8; i++) {"
9792 " pixels[i] = (i * 1.1);"
9793 "}"
9794 "pixels[1] + pixels[6];");
9795 CHECK_EQ(8, result->Int32Value());
9796 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9797 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9798 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9799 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9800 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9801 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9802 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9803 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9804
9805 result = CompileRun("for (var i = 0; i < 8; i++) {"
9806 " pixels[7] = undefined;"
9807 "}"
9808 "pixels[7];");
9809 CHECK_EQ(0, result->Int32Value());
9810 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9811
9812 result = CompileRun("for (var i = 0; i < 8; i++) {"
9813 " pixels[6] = '2.3';"
9814 "}"
9815 "pixels[6];");
9816 CHECK_EQ(2, result->Int32Value());
9817 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9818
9819 result = CompileRun("for (var i = 0; i < 8; i++) {"
9820 " pixels[5] = NaN;"
9821 "}"
9822 "pixels[5];");
9823 CHECK_EQ(0, result->Int32Value());
9824 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9825
9826 result = CompileRun("for (var i = 0; i < 8; i++) {"
9827 " pixels[8] = Infinity;"
9828 "}"
9829 "pixels[8];");
9830 CHECK_EQ(255, result->Int32Value());
9831 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9832
9833 result = CompileRun("for (var i = 0; i < 8; i++) {"
9834 " pixels[9] = -Infinity;"
9835 "}"
9836 "pixels[9];");
9837 CHECK_EQ(0, result->Int32Value());
9838 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9839
9840 result = CompileRun("pixels[3] = 33;"
9841 "delete pixels[3];"
9842 "pixels[3];");
9843 CHECK_EQ(33, result->Int32Value());
9844
9845 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9846 "pixels[2] = 12; pixels[3] = 13;"
9847 "pixels.__defineGetter__('2',"
9848 "function() { return 120; });"
9849 "pixels[2];");
9850 CHECK_EQ(12, result->Int32Value());
9851
9852 result = CompileRun("var js_array = new Array(40);"
9853 "js_array[0] = 77;"
9854 "js_array;");
9855 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9856
9857 result = CompileRun("pixels[1] = 23;"
9858 "pixels.__proto__ = [];"
9859 "js_array.__proto__ = pixels;"
9860 "js_array.concat(pixels);");
9861 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9862 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9863
9864 result = CompileRun("pixels[1] = 23;");
9865 CHECK_EQ(23, result->Int32Value());
9866
Steve Blockd0582a62009-12-15 09:54:21 +00009867 // Test for index greater than 255. Regression test for:
9868 // http://code.google.com/p/chromium/issues/detail?id=26337.
9869 result = CompileRun("pixels[256] = 255;");
9870 CHECK_EQ(255, result->Int32Value());
9871 result = CompileRun("var i = 0;"
9872 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9873 "i");
9874 CHECK_EQ(255, result->Int32Value());
9875
Steve Blocka7e24c12009-10-30 11:49:00 +00009876 free(pixel_data);
9877}
9878
9879
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009880THREADED_TEST(PixelArrayInfo) {
9881 v8::HandleScope scope;
9882 LocalContext context;
9883 for (int size = 0; size < 100; size += 10) {
9884 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9885 v8::Handle<v8::Object> obj = v8::Object::New();
9886 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9887 CHECK(obj->HasIndexedPropertiesInPixelData());
9888 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9889 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9890 free(pixel_data);
9891 }
9892}
9893
9894
9895static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9896 switch (array_type) {
9897 case v8::kExternalByteArray:
9898 case v8::kExternalUnsignedByteArray:
9899 return 1;
9900 break;
9901 case v8::kExternalShortArray:
9902 case v8::kExternalUnsignedShortArray:
9903 return 2;
9904 break;
9905 case v8::kExternalIntArray:
9906 case v8::kExternalUnsignedIntArray:
9907 case v8::kExternalFloatArray:
9908 return 4;
9909 break;
9910 default:
9911 UNREACHABLE();
9912 return -1;
9913 }
9914 UNREACHABLE();
9915 return -1;
9916}
9917
9918
Steve Block3ce2e202009-11-05 08:53:23 +00009919template <class ExternalArrayClass, class ElementType>
9920static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9921 int64_t low,
9922 int64_t high) {
9923 v8::HandleScope scope;
9924 LocalContext context;
9925 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009926 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +00009927 ElementType* array_data =
9928 static_cast<ElementType*>(malloc(kElementCount * element_size));
9929 i::Handle<ExternalArrayClass> array =
9930 i::Handle<ExternalArrayClass>::cast(
9931 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9932 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9933 for (int i = 0; i < kElementCount; i++) {
9934 array->set(i, static_cast<ElementType>(i));
9935 }
9936 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9937 for (int i = 0; i < kElementCount; i++) {
9938 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9939 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9940 }
9941
9942 v8::Handle<v8::Object> obj = v8::Object::New();
9943 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9944 // Set the elements to be the external array.
9945 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9946 array_type,
9947 kElementCount);
9948 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9949 obj->Set(v8_str("field"), v8::Int32::New(1503));
9950 context->Global()->Set(v8_str("ext_array"), obj);
9951 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9952 CHECK_EQ(1503, result->Int32Value());
9953 result = CompileRun("ext_array[1]");
9954 CHECK_EQ(1, result->Int32Value());
9955
9956 // Check pass through of assigned smis
9957 result = CompileRun("var sum = 0;"
9958 "for (var i = 0; i < 8; i++) {"
9959 " sum += ext_array[i] = ext_array[i] = -i;"
9960 "}"
9961 "sum;");
9962 CHECK_EQ(-28, result->Int32Value());
9963
9964 // Check assigned smis
9965 result = CompileRun("for (var i = 0; i < 8; i++) {"
9966 " ext_array[i] = i;"
9967 "}"
9968 "var sum = 0;"
9969 "for (var i = 0; i < 8; i++) {"
9970 " sum += ext_array[i];"
9971 "}"
9972 "sum;");
9973 CHECK_EQ(28, result->Int32Value());
9974
9975 // Check assigned smis in reverse order
9976 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9977 " ext_array[i] = i;"
9978 "}"
9979 "var sum = 0;"
9980 "for (var i = 0; i < 8; i++) {"
9981 " sum += ext_array[i];"
9982 "}"
9983 "sum;");
9984 CHECK_EQ(28, result->Int32Value());
9985
9986 // Check pass through of assigned HeapNumbers
9987 result = CompileRun("var sum = 0;"
9988 "for (var i = 0; i < 16; i+=2) {"
9989 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9990 "}"
9991 "sum;");
9992 CHECK_EQ(-28, result->Int32Value());
9993
9994 // Check assigned HeapNumbers
9995 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9996 " ext_array[i] = (i * 0.5);"
9997 "}"
9998 "var sum = 0;"
9999 "for (var i = 0; i < 16; i+=2) {"
10000 " sum += ext_array[i];"
10001 "}"
10002 "sum;");
10003 CHECK_EQ(28, result->Int32Value());
10004
10005 // Check assigned HeapNumbers in reverse order
10006 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
10007 " ext_array[i] = (i * 0.5);"
10008 "}"
10009 "var sum = 0;"
10010 "for (var i = 0; i < 16; i+=2) {"
10011 " sum += ext_array[i];"
10012 "}"
10013 "sum;");
10014 CHECK_EQ(28, result->Int32Value());
10015
10016 i::ScopedVector<char> test_buf(1024);
10017
10018 // Check legal boundary conditions.
10019 // The repeated loads and stores ensure the ICs are exercised.
10020 const char* boundary_program =
10021 "var res = 0;"
10022 "for (var i = 0; i < 16; i++) {"
10023 " ext_array[i] = %lld;"
10024 " if (i > 8) {"
10025 " res = ext_array[i];"
10026 " }"
10027 "}"
10028 "res;";
10029 i::OS::SNPrintF(test_buf,
10030 boundary_program,
10031 low);
10032 result = CompileRun(test_buf.start());
10033 CHECK_EQ(low, result->IntegerValue());
10034
10035 i::OS::SNPrintF(test_buf,
10036 boundary_program,
10037 high);
10038 result = CompileRun(test_buf.start());
10039 CHECK_EQ(high, result->IntegerValue());
10040
10041 // Check misprediction of type in IC.
10042 result = CompileRun("var tmp_array = ext_array;"
10043 "var sum = 0;"
10044 "for (var i = 0; i < 8; i++) {"
10045 " tmp_array[i] = i;"
10046 " sum += tmp_array[i];"
10047 " if (i == 4) {"
10048 " tmp_array = {};"
10049 " }"
10050 "}"
10051 "sum;");
10052 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
10053 CHECK_EQ(28, result->Int32Value());
10054
10055 // Make sure out-of-range loads do not throw.
10056 i::OS::SNPrintF(test_buf,
10057 "var caught_exception = false;"
10058 "try {"
10059 " ext_array[%d];"
10060 "} catch (e) {"
10061 " caught_exception = true;"
10062 "}"
10063 "caught_exception;",
10064 kElementCount);
10065 result = CompileRun(test_buf.start());
10066 CHECK_EQ(false, result->BooleanValue());
10067
10068 // Make sure out-of-range stores do not throw.
10069 i::OS::SNPrintF(test_buf,
10070 "var caught_exception = false;"
10071 "try {"
10072 " ext_array[%d] = 1;"
10073 "} catch (e) {"
10074 " caught_exception = true;"
10075 "}"
10076 "caught_exception;",
10077 kElementCount);
10078 result = CompileRun(test_buf.start());
10079 CHECK_EQ(false, result->BooleanValue());
10080
10081 // Check other boundary conditions, values and operations.
10082 result = CompileRun("for (var i = 0; i < 8; i++) {"
10083 " ext_array[7] = undefined;"
10084 "}"
10085 "ext_array[7];");
10086 CHECK_EQ(0, result->Int32Value());
10087 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
10088
10089 result = CompileRun("for (var i = 0; i < 8; i++) {"
10090 " ext_array[6] = '2.3';"
10091 "}"
10092 "ext_array[6];");
10093 CHECK_EQ(2, result->Int32Value());
10094 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
10095
10096 if (array_type != v8::kExternalFloatArray) {
10097 // Though the specification doesn't state it, be explicit about
10098 // converting NaNs and +/-Infinity to zero.
10099 result = CompileRun("for (var i = 0; i < 8; i++) {"
10100 " ext_array[i] = 5;"
10101 "}"
10102 "for (var i = 0; i < 8; i++) {"
10103 " ext_array[i] = NaN;"
10104 "}"
10105 "ext_array[5];");
10106 CHECK_EQ(0, result->Int32Value());
10107 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10108
10109 result = CompileRun("for (var i = 0; i < 8; i++) {"
10110 " ext_array[i] = 5;"
10111 "}"
10112 "for (var i = 0; i < 8; i++) {"
10113 " ext_array[i] = Infinity;"
10114 "}"
10115 "ext_array[5];");
10116 CHECK_EQ(0, result->Int32Value());
10117 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10118
10119 result = CompileRun("for (var i = 0; i < 8; i++) {"
10120 " ext_array[i] = 5;"
10121 "}"
10122 "for (var i = 0; i < 8; i++) {"
10123 " ext_array[i] = -Infinity;"
10124 "}"
10125 "ext_array[5];");
10126 CHECK_EQ(0, result->Int32Value());
10127 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10128 }
10129
10130 result = CompileRun("ext_array[3] = 33;"
10131 "delete ext_array[3];"
10132 "ext_array[3];");
10133 CHECK_EQ(33, result->Int32Value());
10134
10135 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
10136 "ext_array[2] = 12; ext_array[3] = 13;"
10137 "ext_array.__defineGetter__('2',"
10138 "function() { return 120; });"
10139 "ext_array[2];");
10140 CHECK_EQ(12, result->Int32Value());
10141
10142 result = CompileRun("var js_array = new Array(40);"
10143 "js_array[0] = 77;"
10144 "js_array;");
10145 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10146
10147 result = CompileRun("ext_array[1] = 23;"
10148 "ext_array.__proto__ = [];"
10149 "js_array.__proto__ = ext_array;"
10150 "js_array.concat(ext_array);");
10151 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10152 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10153
10154 result = CompileRun("ext_array[1] = 23;");
10155 CHECK_EQ(23, result->Int32Value());
10156
Steve Blockd0582a62009-12-15 09:54:21 +000010157 // Test more complex manipulations which cause eax to contain values
10158 // that won't be completely overwritten by loads from the arrays.
10159 // This catches bugs in the instructions used for the KeyedLoadIC
10160 // for byte and word types.
10161 {
10162 const int kXSize = 300;
10163 const int kYSize = 300;
10164 const int kLargeElementCount = kXSize * kYSize * 4;
10165 ElementType* large_array_data =
10166 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
10167 i::Handle<ExternalArrayClass> large_array =
10168 i::Handle<ExternalArrayClass>::cast(
10169 i::Factory::NewExternalArray(kLargeElementCount,
10170 array_type,
10171 array_data));
10172 v8::Handle<v8::Object> large_obj = v8::Object::New();
10173 // Set the elements to be the external array.
10174 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
10175 array_type,
10176 kLargeElementCount);
10177 context->Global()->Set(v8_str("large_array"), large_obj);
10178 // Initialize contents of a few rows.
10179 for (int x = 0; x < 300; x++) {
10180 int row = 0;
10181 int offset = row * 300 * 4;
10182 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10183 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10184 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10185 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10186 row = 150;
10187 offset = row * 300 * 4;
10188 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10189 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10190 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10191 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10192 row = 298;
10193 offset = row * 300 * 4;
10194 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10195 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10196 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10197 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10198 }
10199 // The goal of the code below is to make "offset" large enough
10200 // that the computation of the index (which goes into eax) has
10201 // high bits set which will not be overwritten by a byte or short
10202 // load.
10203 result = CompileRun("var failed = false;"
10204 "var offset = 0;"
10205 "for (var i = 0; i < 300; i++) {"
10206 " if (large_array[4 * i] != 127 ||"
10207 " large_array[4 * i + 1] != 0 ||"
10208 " large_array[4 * i + 2] != 0 ||"
10209 " large_array[4 * i + 3] != 127) {"
10210 " failed = true;"
10211 " }"
10212 "}"
10213 "offset = 150 * 300 * 4;"
10214 "for (var i = 0; i < 300; i++) {"
10215 " if (large_array[offset + 4 * i] != 127 ||"
10216 " large_array[offset + 4 * i + 1] != 0 ||"
10217 " large_array[offset + 4 * i + 2] != 0 ||"
10218 " large_array[offset + 4 * i + 3] != 127) {"
10219 " failed = true;"
10220 " }"
10221 "}"
10222 "offset = 298 * 300 * 4;"
10223 "for (var i = 0; i < 300; i++) {"
10224 " if (large_array[offset + 4 * i] != 127 ||"
10225 " large_array[offset + 4 * i + 1] != 0 ||"
10226 " large_array[offset + 4 * i + 2] != 0 ||"
10227 " large_array[offset + 4 * i + 3] != 127) {"
10228 " failed = true;"
10229 " }"
10230 "}"
10231 "!failed;");
10232 CHECK_EQ(true, result->BooleanValue());
10233 free(large_array_data);
10234 }
10235
Steve Block3ce2e202009-11-05 08:53:23 +000010236 free(array_data);
10237}
10238
10239
10240THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010241 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010242 v8::kExternalByteArray,
10243 -128,
10244 127);
10245}
10246
10247
10248THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010249 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010250 v8::kExternalUnsignedByteArray,
10251 0,
10252 255);
10253}
10254
10255
10256THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010257 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010258 v8::kExternalShortArray,
10259 -32768,
10260 32767);
10261}
10262
10263
10264THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010265 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010266 v8::kExternalUnsignedShortArray,
10267 0,
10268 65535);
10269}
10270
10271
10272THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010273 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010274 v8::kExternalIntArray,
10275 INT_MIN, // -2147483648
10276 INT_MAX); // 2147483647
10277}
10278
10279
10280THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010281 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010282 v8::kExternalUnsignedIntArray,
10283 0,
10284 UINT_MAX); // 4294967295
10285}
10286
10287
10288THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010289 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000010290 v8::kExternalFloatArray,
10291 -500,
10292 500);
10293}
10294
10295
10296THREADED_TEST(ExternalArrays) {
10297 TestExternalByteArray();
10298 TestExternalUnsignedByteArray();
10299 TestExternalShortArray();
10300 TestExternalUnsignedShortArray();
10301 TestExternalIntArray();
10302 TestExternalUnsignedIntArray();
10303 TestExternalFloatArray();
10304}
10305
10306
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010307void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10308 v8::HandleScope scope;
10309 LocalContext context;
10310 for (int size = 0; size < 100; size += 10) {
10311 int element_size = ExternalArrayElementSize(array_type);
10312 void* external_data = malloc(size * element_size);
10313 v8::Handle<v8::Object> obj = v8::Object::New();
10314 obj->SetIndexedPropertiesToExternalArrayData(
10315 external_data, array_type, size);
10316 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10317 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10318 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10319 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10320 free(external_data);
10321 }
10322}
10323
10324
10325THREADED_TEST(ExternalArrayInfo) {
10326 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10327 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10328 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10329 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10330 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10331 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10332 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10333}
10334
10335
Steve Blocka7e24c12009-10-30 11:49:00 +000010336THREADED_TEST(ScriptContextDependence) {
10337 v8::HandleScope scope;
10338 LocalContext c1;
10339 const char *source = "foo";
10340 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10341 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10342 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10343 CHECK_EQ(dep->Run()->Int32Value(), 100);
10344 CHECK_EQ(indep->Run()->Int32Value(), 100);
10345 LocalContext c2;
10346 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10347 CHECK_EQ(dep->Run()->Int32Value(), 100);
10348 CHECK_EQ(indep->Run()->Int32Value(), 101);
10349}
10350
10351
10352THREADED_TEST(StackTrace) {
10353 v8::HandleScope scope;
10354 LocalContext context;
10355 v8::TryCatch try_catch;
10356 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10357 v8::Handle<v8::String> src = v8::String::New(source);
10358 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10359 v8::Script::New(src, origin)->Run();
10360 CHECK(try_catch.HasCaught());
10361 v8::String::Utf8Value stack(try_catch.StackTrace());
10362 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10363}
10364
10365
Kristian Monsen25f61362010-05-21 11:50:48 +010010366// Checks that a StackFrame has certain expected values.
10367void checkStackFrame(const char* expected_script_name,
10368 const char* expected_func_name, int expected_line_number,
10369 int expected_column, bool is_eval, bool is_constructor,
10370 v8::Handle<v8::StackFrame> frame) {
10371 v8::HandleScope scope;
10372 v8::String::Utf8Value func_name(frame->GetFunctionName());
10373 v8::String::Utf8Value script_name(frame->GetScriptName());
10374 if (*script_name == NULL) {
10375 // The situation where there is no associated script, like for evals.
10376 CHECK(expected_script_name == NULL);
10377 } else {
10378 CHECK(strstr(*script_name, expected_script_name) != NULL);
10379 }
10380 CHECK(strstr(*func_name, expected_func_name) != NULL);
10381 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10382 CHECK_EQ(expected_column, frame->GetColumn());
10383 CHECK_EQ(is_eval, frame->IsEval());
10384 CHECK_EQ(is_constructor, frame->IsConstructor());
10385}
10386
10387
10388v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10389 v8::HandleScope scope;
10390 const char* origin = "capture-stack-trace-test";
10391 const int kOverviewTest = 1;
10392 const int kDetailedTest = 2;
10393
10394 ASSERT(args.Length() == 1);
10395
10396 int testGroup = args[0]->Int32Value();
10397 if (testGroup == kOverviewTest) {
10398 v8::Handle<v8::StackTrace> stackTrace =
10399 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10400 CHECK_EQ(4, stackTrace->GetFrameCount());
10401 checkStackFrame(origin, "bar", 2, 10, false, false,
10402 stackTrace->GetFrame(0));
10403 checkStackFrame(origin, "foo", 6, 3, false, false,
10404 stackTrace->GetFrame(1));
10405 checkStackFrame(NULL, "", 1, 1, false, false,
10406 stackTrace->GetFrame(2));
10407 // The last frame is an anonymous function that has the initial call.
10408 checkStackFrame(origin, "", 8, 7, false, false,
10409 stackTrace->GetFrame(3));
10410
10411 CHECK(stackTrace->AsArray()->IsArray());
10412 } else if (testGroup == kDetailedTest) {
10413 v8::Handle<v8::StackTrace> stackTrace =
10414 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10415 CHECK_EQ(4, stackTrace->GetFrameCount());
10416 checkStackFrame(origin, "bat", 4, 22, false, false,
10417 stackTrace->GetFrame(0));
10418 checkStackFrame(origin, "baz", 8, 3, false, true,
10419 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010420#ifdef ENABLE_DEBUGGER_SUPPORT
10421 bool is_eval = true;
10422#else // ENABLE_DEBUGGER_SUPPORT
10423 bool is_eval = false;
10424#endif // ENABLE_DEBUGGER_SUPPORT
10425
10426 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010427 stackTrace->GetFrame(2));
10428 // The last frame is an anonymous function that has the initial call to foo.
10429 checkStackFrame(origin, "", 10, 1, false, false,
10430 stackTrace->GetFrame(3));
10431
10432 CHECK(stackTrace->AsArray()->IsArray());
10433 }
10434 return v8::Undefined();
10435}
10436
10437
10438// Tests the C++ StackTrace API.
10439THREADED_TEST(CaptureStackTrace) {
10440 v8::HandleScope scope;
10441 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10442 Local<ObjectTemplate> templ = ObjectTemplate::New();
10443 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10444 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10445 LocalContext context(0, templ);
10446
10447 // Test getting OVERVIEW information. Should ignore information that is not
10448 // script name, function name, line number, and column offset.
10449 const char *overview_source =
10450 "function bar() {\n"
10451 " var y; AnalyzeStackInNativeCode(1);\n"
10452 "}\n"
10453 "function foo() {\n"
10454 "\n"
10455 " bar();\n"
10456 "}\n"
10457 "var x;eval('new foo();');";
10458 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10459 v8::Handle<Value> overview_result =
10460 v8::Script::New(overview_src, origin)->Run();
10461 ASSERT(!overview_result.IsEmpty());
10462 ASSERT(overview_result->IsObject());
10463
10464 // Test getting DETAILED information.
10465 const char *detailed_source =
10466 "function bat() {AnalyzeStackInNativeCode(2);\n"
10467 "}\n"
10468 "\n"
10469 "function baz() {\n"
10470 " bat();\n"
10471 "}\n"
10472 "eval('new baz();');";
10473 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10474 // Make the script using a non-zero line and column offset.
10475 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10476 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10477 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10478 v8::Handle<v8::Script> detailed_script(
10479 v8::Script::New(detailed_src, &detailed_origin));
10480 v8::Handle<Value> detailed_result = detailed_script->Run();
10481 ASSERT(!detailed_result.IsEmpty());
10482 ASSERT(detailed_result->IsObject());
10483}
10484
10485
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010486static void StackTraceForUncaughtExceptionListener(
10487 v8::Handle<v8::Message> message,
10488 v8::Handle<Value>) {
10489 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
10490 CHECK_EQ(2, stack_trace->GetFrameCount());
10491 checkStackFrame("origin", "foo", 2, 3, false, false,
10492 stack_trace->GetFrame(0));
10493 checkStackFrame("origin", "bar", 5, 3, false, false,
10494 stack_trace->GetFrame(1));
10495}
10496
10497TEST(CaptureStackTraceForUncaughtException) {
10498 report_count = 0;
10499 v8::HandleScope scope;
10500 LocalContext env;
10501 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
10502 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
10503
10504 Script::Compile(v8_str("function foo() {\n"
10505 " throw 1;\n"
10506 "};\n"
10507 "function bar() {\n"
10508 " foo();\n"
10509 "};"),
10510 v8_str("origin"))->Run();
10511 v8::Local<v8::Object> global = env->Global();
10512 Local<Value> trouble = global->Get(v8_str("bar"));
10513 CHECK(trouble->IsFunction());
10514 Function::Cast(*trouble)->Call(global, 0, NULL);
10515 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
10516 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
10517}
10518
10519
Steve Block3ce2e202009-11-05 08:53:23 +000010520// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010521THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010522 bool rv = false;
10523 for (int i = 0; i < 100; i++) {
10524 rv = v8::V8::IdleNotification();
10525 if (rv)
10526 break;
10527 }
10528 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010529}
10530
10531
10532static uint32_t* stack_limit;
10533
10534static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10535 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10536 return v8::Undefined();
10537}
10538
10539
10540// Uses the address of a local variable to determine the stack top now.
10541// Given a size, returns an address that is that far from the current
10542// top of stack.
10543static uint32_t* ComputeStackLimit(uint32_t size) {
10544 uint32_t* answer = &size - (size / sizeof(size));
10545 // If the size is very large and the stack is very near the bottom of
10546 // memory then the calculation above may wrap around and give an address
10547 // that is above the (downwards-growing) stack. In that case we return
10548 // a very low address.
10549 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10550 return answer;
10551}
10552
10553
10554TEST(SetResourceConstraints) {
10555 static const int K = 1024;
10556 uint32_t* set_limit = ComputeStackLimit(128 * K);
10557
10558 // Set stack limit.
10559 v8::ResourceConstraints constraints;
10560 constraints.set_stack_limit(set_limit);
10561 CHECK(v8::SetResourceConstraints(&constraints));
10562
10563 // Execute a script.
10564 v8::HandleScope scope;
10565 LocalContext env;
10566 Local<v8::FunctionTemplate> fun_templ =
10567 v8::FunctionTemplate::New(GetStackLimitCallback);
10568 Local<Function> fun = fun_templ->GetFunction();
10569 env->Global()->Set(v8_str("get_stack_limit"), fun);
10570 CompileRun("get_stack_limit();");
10571
10572 CHECK(stack_limit == set_limit);
10573}
10574
10575
10576TEST(SetResourceConstraintsInThread) {
10577 uint32_t* set_limit;
10578 {
10579 v8::Locker locker;
10580 static const int K = 1024;
10581 set_limit = ComputeStackLimit(128 * K);
10582
10583 // Set stack limit.
10584 v8::ResourceConstraints constraints;
10585 constraints.set_stack_limit(set_limit);
10586 CHECK(v8::SetResourceConstraints(&constraints));
10587
10588 // Execute a script.
10589 v8::HandleScope scope;
10590 LocalContext env;
10591 Local<v8::FunctionTemplate> fun_templ =
10592 v8::FunctionTemplate::New(GetStackLimitCallback);
10593 Local<Function> fun = fun_templ->GetFunction();
10594 env->Global()->Set(v8_str("get_stack_limit"), fun);
10595 CompileRun("get_stack_limit();");
10596
10597 CHECK(stack_limit == set_limit);
10598 }
10599 {
10600 v8::Locker locker;
10601 CHECK(stack_limit == set_limit);
10602 }
10603}
Steve Block3ce2e202009-11-05 08:53:23 +000010604
10605
10606THREADED_TEST(GetHeapStatistics) {
10607 v8::HandleScope scope;
10608 LocalContext c1;
10609 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010610 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10611 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010612 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010613 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10614 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10615}
10616
10617
10618static double DoubleFromBits(uint64_t value) {
10619 double target;
10620#ifdef BIG_ENDIAN_FLOATING_POINT
10621 const int kIntSize = 4;
10622 // Somebody swapped the lower and higher half of doubles.
10623 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10624 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10625#else
10626 memcpy(&target, &value, sizeof(target));
10627#endif
10628 return target;
10629}
10630
10631
10632static uint64_t DoubleToBits(double value) {
10633 uint64_t target;
10634#ifdef BIG_ENDIAN_FLOATING_POINT
10635 const int kIntSize = 4;
10636 // Somebody swapped the lower and higher half of doubles.
10637 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10638 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10639#else
10640 memcpy(&target, &value, sizeof(target));
10641#endif
10642 return target;
10643}
10644
10645
10646static double DoubleToDateTime(double input) {
10647 double date_limit = 864e13;
10648 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10649 return i::OS::nan_value();
10650 }
10651 return (input < 0) ? -(floor(-input)) : floor(input);
10652}
10653
10654// We don't have a consistent way to write 64-bit constants syntactically, so we
10655// split them into two 32-bit constants and combine them programmatically.
10656static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10657 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10658}
10659
10660
10661THREADED_TEST(QuietSignalingNaNs) {
10662 v8::HandleScope scope;
10663 LocalContext context;
10664 v8::TryCatch try_catch;
10665
10666 // Special double values.
10667 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10668 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10669 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10670 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10671 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10672 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10673 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10674
10675 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10676 // on either side of the epoch.
10677 double date_limit = 864e13;
10678
10679 double test_values[] = {
10680 snan,
10681 qnan,
10682 infinity,
10683 max_normal,
10684 date_limit + 1,
10685 date_limit,
10686 min_normal,
10687 max_denormal,
10688 min_denormal,
10689 0,
10690 -0,
10691 -min_denormal,
10692 -max_denormal,
10693 -min_normal,
10694 -date_limit,
10695 -date_limit - 1,
10696 -max_normal,
10697 -infinity,
10698 -qnan,
10699 -snan
10700 };
10701 int num_test_values = 20;
10702
10703 for (int i = 0; i < num_test_values; i++) {
10704 double test_value = test_values[i];
10705
10706 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10707 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10708 double stored_number = number->NumberValue();
10709 if (!IsNaN(test_value)) {
10710 CHECK_EQ(test_value, stored_number);
10711 } else {
10712 uint64_t stored_bits = DoubleToBits(stored_number);
10713 // Check if quiet nan (bits 51..62 all set).
10714 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10715 }
10716
10717 // Check that Date::New preserves non-NaNs in the date range and
10718 // quiets SNaNs.
10719 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10720 double expected_stored_date = DoubleToDateTime(test_value);
10721 double stored_date = date->NumberValue();
10722 if (!IsNaN(expected_stored_date)) {
10723 CHECK_EQ(expected_stored_date, stored_date);
10724 } else {
10725 uint64_t stored_bits = DoubleToBits(stored_date);
10726 // Check if quiet nan (bits 51..62 all set).
10727 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10728 }
10729 }
10730}
10731
10732
10733static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10734 v8::HandleScope scope;
10735 v8::TryCatch tc;
10736 v8::Handle<v8::String> str = args[0]->ToString();
10737 if (tc.HasCaught())
10738 return tc.ReThrow();
10739 return v8::Undefined();
10740}
10741
10742
10743// Test that an exception can be propagated down through a spaghetti
10744// stack using ReThrow.
10745THREADED_TEST(SpaghettiStackReThrow) {
10746 v8::HandleScope scope;
10747 LocalContext context;
10748 context->Global()->Set(
10749 v8::String::New("s"),
10750 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10751 v8::TryCatch try_catch;
10752 CompileRun(
10753 "var i = 0;"
10754 "var o = {"
10755 " toString: function () {"
10756 " if (i == 10) {"
10757 " throw 'Hey!';"
10758 " } else {"
10759 " i++;"
10760 " return s(o);"
10761 " }"
10762 " }"
10763 "};"
10764 "s(o);");
10765 CHECK(try_catch.HasCaught());
10766 v8::String::Utf8Value value(try_catch.Exception());
10767 CHECK_EQ(0, strcmp(*value, "Hey!"));
10768}
10769
10770
Steve Blockd0582a62009-12-15 09:54:21 +000010771TEST(Regress528) {
10772 v8::V8::Initialize();
10773
10774 v8::HandleScope scope;
10775 v8::Persistent<Context> context;
10776 v8::Persistent<Context> other_context;
10777 int gc_count;
10778
10779 // Create a context used to keep the code from aging in the compilation
10780 // cache.
10781 other_context = Context::New();
10782
10783 // Context-dependent context data creates reference from the compilation
10784 // cache to the global object.
10785 const char* source_simple = "1";
10786 context = Context::New();
10787 {
10788 v8::HandleScope scope;
10789
10790 context->Enter();
10791 Local<v8::String> obj = v8::String::New("");
10792 context->SetData(obj);
10793 CompileRun(source_simple);
10794 context->Exit();
10795 }
10796 context.Dispose();
10797 for (gc_count = 1; gc_count < 10; gc_count++) {
10798 other_context->Enter();
10799 CompileRun(source_simple);
10800 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010801 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010802 if (GetGlobalObjectsCount() == 1) break;
10803 }
10804 CHECK_GE(2, gc_count);
10805 CHECK_EQ(1, GetGlobalObjectsCount());
10806
10807 // Eval in a function creates reference from the compilation cache to the
10808 // global object.
10809 const char* source_eval = "function f(){eval('1')}; f()";
10810 context = Context::New();
10811 {
10812 v8::HandleScope scope;
10813
10814 context->Enter();
10815 CompileRun(source_eval);
10816 context->Exit();
10817 }
10818 context.Dispose();
10819 for (gc_count = 1; gc_count < 10; gc_count++) {
10820 other_context->Enter();
10821 CompileRun(source_eval);
10822 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010823 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010824 if (GetGlobalObjectsCount() == 1) break;
10825 }
10826 CHECK_GE(2, gc_count);
10827 CHECK_EQ(1, GetGlobalObjectsCount());
10828
10829 // Looking up the line number for an exception creates reference from the
10830 // compilation cache to the global object.
10831 const char* source_exception = "function f(){throw 1;} f()";
10832 context = Context::New();
10833 {
10834 v8::HandleScope scope;
10835
10836 context->Enter();
10837 v8::TryCatch try_catch;
10838 CompileRun(source_exception);
10839 CHECK(try_catch.HasCaught());
10840 v8::Handle<v8::Message> message = try_catch.Message();
10841 CHECK(!message.IsEmpty());
10842 CHECK_EQ(1, message->GetLineNumber());
10843 context->Exit();
10844 }
10845 context.Dispose();
10846 for (gc_count = 1; gc_count < 10; gc_count++) {
10847 other_context->Enter();
10848 CompileRun(source_exception);
10849 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010850 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010851 if (GetGlobalObjectsCount() == 1) break;
10852 }
10853 CHECK_GE(2, gc_count);
10854 CHECK_EQ(1, GetGlobalObjectsCount());
10855
10856 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010857}
Andrei Popescu402d9372010-02-26 13:31:12 +000010858
10859
10860THREADED_TEST(ScriptOrigin) {
10861 v8::HandleScope scope;
10862 LocalContext env;
10863 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10864 v8::Handle<v8::String> script = v8::String::New(
10865 "function f() {}\n\nfunction g() {}");
10866 v8::Script::Compile(script, &origin)->Run();
10867 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10868 env->Global()->Get(v8::String::New("f")));
10869 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10870 env->Global()->Get(v8::String::New("g")));
10871
10872 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10873 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10874 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10875
10876 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10877 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10878 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10879}
10880
10881
10882THREADED_TEST(ScriptLineNumber) {
10883 v8::HandleScope scope;
10884 LocalContext env;
10885 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10886 v8::Handle<v8::String> script = v8::String::New(
10887 "function f() {}\n\nfunction g() {}");
10888 v8::Script::Compile(script, &origin)->Run();
10889 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10890 env->Global()->Get(v8::String::New("f")));
10891 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10892 env->Global()->Get(v8::String::New("g")));
10893 CHECK_EQ(0, f->GetScriptLineNumber());
10894 CHECK_EQ(2, g->GetScriptLineNumber());
10895}
10896
10897
10898static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10899 const AccessorInfo& info) {
10900 return v8_num(42);
10901}
10902
10903
10904static void SetterWhichSetsYOnThisTo23(Local<String> name,
10905 Local<Value> value,
10906 const AccessorInfo& info) {
10907 info.This()->Set(v8_str("y"), v8_num(23));
10908}
10909
10910
Steve Block6ded16b2010-05-10 14:33:55 +010010911TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010912 v8::HandleScope scope;
10913 Local<ObjectTemplate> templ = ObjectTemplate::New();
10914 templ->SetAccessor(v8_str("x"),
10915 GetterWhichReturns42,
10916 SetterWhichSetsYOnThisTo23);
10917 LocalContext context;
10918 context->Global()->Set(v8_str("P"), templ->NewInstance());
10919 CompileRun("function C1() {"
10920 " this.x = 23;"
10921 "};"
10922 "C1.prototype = P;"
10923 "function C2() {"
10924 " this.x = 23"
10925 "};"
10926 "C2.prototype = { };"
10927 "C2.prototype.__proto__ = P;");
10928
10929 v8::Local<v8::Script> script;
10930 script = v8::Script::Compile(v8_str("new C1();"));
10931 for (int i = 0; i < 10; i++) {
10932 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10933 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10934 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10935 }
10936
10937 script = v8::Script::Compile(v8_str("new C2();"));
10938 for (int i = 0; i < 10; i++) {
10939 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10940 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10941 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10942 }
10943}
10944
10945
10946static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10947 Local<String> name, const AccessorInfo& info) {
10948 return v8_num(42);
10949}
10950
10951
10952static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10953 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10954 if (name->Equals(v8_str("x"))) {
10955 info.This()->Set(v8_str("y"), v8_num(23));
10956 }
10957 return v8::Handle<Value>();
10958}
10959
10960
10961THREADED_TEST(InterceptorOnConstructorPrototype) {
10962 v8::HandleScope scope;
10963 Local<ObjectTemplate> templ = ObjectTemplate::New();
10964 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10965 NamedPropertySetterWhichSetsYOnThisTo23);
10966 LocalContext context;
10967 context->Global()->Set(v8_str("P"), templ->NewInstance());
10968 CompileRun("function C1() {"
10969 " this.x = 23;"
10970 "};"
10971 "C1.prototype = P;"
10972 "function C2() {"
10973 " this.x = 23"
10974 "};"
10975 "C2.prototype = { };"
10976 "C2.prototype.__proto__ = P;");
10977
10978 v8::Local<v8::Script> script;
10979 script = v8::Script::Compile(v8_str("new C1();"));
10980 for (int i = 0; i < 10; i++) {
10981 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10982 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10983 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10984 }
10985
10986 script = v8::Script::Compile(v8_str("new C2();"));
10987 for (int i = 0; i < 10; i++) {
10988 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10989 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10990 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10991 }
10992}
Steve Block6ded16b2010-05-10 14:33:55 +010010993
10994
10995TEST(Bug618) {
10996 const char* source = "function C1() {"
10997 " this.x = 23;"
10998 "};"
10999 "C1.prototype = P;";
11000
11001 v8::HandleScope scope;
11002 LocalContext context;
11003 v8::Local<v8::Script> script;
11004
11005 // Use a simple object as prototype.
11006 v8::Local<v8::Object> prototype = v8::Object::New();
11007 prototype->Set(v8_str("y"), v8_num(42));
11008 context->Global()->Set(v8_str("P"), prototype);
11009
11010 // This compile will add the code to the compilation cache.
11011 CompileRun(source);
11012
11013 script = v8::Script::Compile(v8_str("new C1();"));
Kristian Monsen0d5e1162010-09-30 15:31:59 +010011014 // Allow enough iterations for the inobject slack tracking logic
11015 // to finalize instance size and install the fast construct stub.
11016 for (int i = 0; i < 256; i++) {
Steve Block6ded16b2010-05-10 14:33:55 +010011017 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11018 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
11019 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
11020 }
11021
11022 // Use an API object with accessors as prototype.
11023 Local<ObjectTemplate> templ = ObjectTemplate::New();
11024 templ->SetAccessor(v8_str("x"),
11025 GetterWhichReturns42,
11026 SetterWhichSetsYOnThisTo23);
11027 context->Global()->Set(v8_str("P"), templ->NewInstance());
11028
11029 // This compile will get the code from the compilation cache.
11030 CompileRun(source);
11031
11032 script = v8::Script::Compile(v8_str("new C1();"));
11033 for (int i = 0; i < 10; i++) {
11034 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
11035 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
11036 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
11037 }
11038}
11039
11040int prologue_call_count = 0;
11041int epilogue_call_count = 0;
11042int prologue_call_count_second = 0;
11043int epilogue_call_count_second = 0;
11044
11045void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
11046 ++prologue_call_count;
11047}
11048
11049void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
11050 ++epilogue_call_count;
11051}
11052
11053void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
11054 ++prologue_call_count_second;
11055}
11056
11057void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
11058 ++epilogue_call_count_second;
11059}
11060
11061TEST(GCCallbacks) {
11062 LocalContext context;
11063
11064 v8::V8::AddGCPrologueCallback(PrologueCallback);
11065 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
11066 CHECK_EQ(0, prologue_call_count);
11067 CHECK_EQ(0, epilogue_call_count);
11068 i::Heap::CollectAllGarbage(false);
11069 CHECK_EQ(1, prologue_call_count);
11070 CHECK_EQ(1, epilogue_call_count);
11071 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
11072 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
11073 i::Heap::CollectAllGarbage(false);
11074 CHECK_EQ(2, prologue_call_count);
11075 CHECK_EQ(2, epilogue_call_count);
11076 CHECK_EQ(1, prologue_call_count_second);
11077 CHECK_EQ(1, epilogue_call_count_second);
11078 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
11079 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
11080 i::Heap::CollectAllGarbage(false);
11081 CHECK_EQ(2, prologue_call_count);
11082 CHECK_EQ(2, epilogue_call_count);
11083 CHECK_EQ(2, prologue_call_count_second);
11084 CHECK_EQ(2, epilogue_call_count_second);
11085 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
11086 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
11087 i::Heap::CollectAllGarbage(false);
11088 CHECK_EQ(2, prologue_call_count);
11089 CHECK_EQ(2, epilogue_call_count);
11090 CHECK_EQ(2, prologue_call_count_second);
11091 CHECK_EQ(2, epilogue_call_count_second);
11092}
Kristian Monsen25f61362010-05-21 11:50:48 +010011093
11094
11095THREADED_TEST(AddToJSFunctionResultCache) {
11096 i::FLAG_allow_natives_syntax = true;
11097 v8::HandleScope scope;
11098
11099 LocalContext context;
11100
11101 const char* code =
11102 "(function() {"
11103 " var key0 = 'a';"
11104 " var key1 = 'b';"
11105 " var r0 = %_GetFromCache(0, key0);"
11106 " var r1 = %_GetFromCache(0, key1);"
11107 " var r0_ = %_GetFromCache(0, key0);"
11108 " if (r0 !== r0_)"
11109 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
11110 " var r1_ = %_GetFromCache(0, key1);"
11111 " if (r1 !== r1_)"
11112 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
11113 " return 'PASSED';"
11114 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011115 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011116 ExpectString(code, "PASSED");
11117}
11118
11119
11120static const int k0CacheSize = 16;
11121
11122THREADED_TEST(FillJSFunctionResultCache) {
11123 i::FLAG_allow_natives_syntax = true;
11124 v8::HandleScope scope;
11125
11126 LocalContext context;
11127
11128 const char* code =
11129 "(function() {"
11130 " var k = 'a';"
11131 " var r = %_GetFromCache(0, k);"
11132 " for (var i = 0; i < 16; i++) {"
11133 " %_GetFromCache(0, 'a' + i);"
11134 " };"
11135 " if (r === %_GetFromCache(0, k))"
11136 " return 'FAILED: k0CacheSize is too small';"
11137 " return 'PASSED';"
11138 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011139 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011140 ExpectString(code, "PASSED");
11141}
11142
11143
11144THREADED_TEST(RoundRobinGetFromCache) {
11145 i::FLAG_allow_natives_syntax = true;
11146 v8::HandleScope scope;
11147
11148 LocalContext context;
11149
11150 const char* code =
11151 "(function() {"
11152 " var keys = [];"
11153 " for (var i = 0; i < 16; i++) keys.push(i);"
11154 " var values = [];"
11155 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11156 " for (var i = 0; i < 16; i++) {"
11157 " var v = %_GetFromCache(0, keys[i]);"
11158 " if (v !== values[i])"
11159 " return 'Wrong value for ' + "
11160 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11161 " };"
11162 " return 'PASSED';"
11163 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011164 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011165 ExpectString(code, "PASSED");
11166}
11167
11168
11169THREADED_TEST(ReverseGetFromCache) {
11170 i::FLAG_allow_natives_syntax = true;
11171 v8::HandleScope scope;
11172
11173 LocalContext context;
11174
11175 const char* code =
11176 "(function() {"
11177 " var keys = [];"
11178 " for (var i = 0; i < 16; i++) keys.push(i);"
11179 " var values = [];"
11180 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11181 " for (var i = 15; i >= 16; i--) {"
11182 " var v = %_GetFromCache(0, keys[i]);"
11183 " if (v !== values[i])"
11184 " return 'Wrong value for ' + "
11185 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11186 " };"
11187 " return 'PASSED';"
11188 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011189 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011190 ExpectString(code, "PASSED");
11191}
11192
11193
11194THREADED_TEST(TestEviction) {
11195 i::FLAG_allow_natives_syntax = true;
11196 v8::HandleScope scope;
11197
11198 LocalContext context;
11199
11200 const char* code =
11201 "(function() {"
11202 " for (var i = 0; i < 2*16; i++) {"
11203 " %_GetFromCache(0, 'a' + i);"
11204 " };"
11205 " return 'PASSED';"
11206 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011207 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011208 ExpectString(code, "PASSED");
11209}
Steve Block8defd9f2010-07-08 12:39:36 +010011210
11211
11212THREADED_TEST(TwoByteStringInAsciiCons) {
11213 // See Chromium issue 47824.
11214 v8::HandleScope scope;
11215
11216 LocalContext context;
11217 const char* init_code =
11218 "var str1 = 'abelspendabel';"
11219 "var str2 = str1 + str1 + str1;"
11220 "str2;";
11221 Local<Value> result = CompileRun(init_code);
11222
11223 CHECK(result->IsString());
11224 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
11225 int length = string->length();
11226 CHECK(string->IsAsciiRepresentation());
11227
11228 FlattenString(string);
11229 i::Handle<i::String> flat_string = FlattenGetString(string);
11230
11231 CHECK(string->IsAsciiRepresentation());
11232 CHECK(flat_string->IsAsciiRepresentation());
11233
11234 // Create external resource.
11235 uint16_t* uc16_buffer = new uint16_t[length + 1];
11236
11237 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
11238 uc16_buffer[length] = 0;
11239
11240 TestResource resource(uc16_buffer);
11241
11242 flat_string->MakeExternal(&resource);
11243
11244 CHECK(flat_string->IsTwoByteRepresentation());
11245
11246 // At this point, we should have a Cons string which is flat and ASCII,
11247 // with a first half that is a two-byte string (although it only contains
11248 // ASCII characters). This is a valid sequence of steps, and it can happen
11249 // in real pages.
11250
11251 CHECK(string->IsAsciiRepresentation());
11252 i::ConsString* cons = i::ConsString::cast(*string);
11253 CHECK_EQ(0, cons->second()->length());
11254 CHECK(cons->first()->IsTwoByteRepresentation());
11255
11256 // Check that some string operations work.
11257
11258 // Atom RegExp.
11259 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
11260 CHECK_EQ(6, reresult->Int32Value());
11261
11262 // Nonatom RegExp.
11263 reresult = CompileRun("str2.match(/abe./g).length;");
11264 CHECK_EQ(6, reresult->Int32Value());
11265
11266 reresult = CompileRun("str2.search(/bel/g);");
11267 CHECK_EQ(1, reresult->Int32Value());
11268
11269 reresult = CompileRun("str2.search(/be./g);");
11270 CHECK_EQ(1, reresult->Int32Value());
11271
11272 ExpectTrue("/bel/g.test(str2);");
11273
11274 ExpectTrue("/be./g.test(str2);");
11275
11276 reresult = CompileRun("/bel/g.exec(str2);");
11277 CHECK(!reresult->IsNull());
11278
11279 reresult = CompileRun("/be./g.exec(str2);");
11280 CHECK(!reresult->IsNull());
11281
11282 ExpectString("str2.substring(2, 10);", "elspenda");
11283
11284 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
11285
11286 ExpectString("str2.charAt(2);", "e");
11287
11288 reresult = CompileRun("str2.charCodeAt(2);");
11289 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
11290}
Iain Merrick75681382010-08-19 15:07:18 +010011291
11292
11293// Failed access check callback that performs a GC on each invocation.
11294void FailedAccessCheckCallbackGC(Local<v8::Object> target,
11295 v8::AccessType type,
11296 Local<v8::Value> data) {
11297 i::Heap::CollectAllGarbage(true);
11298}
11299
11300
11301TEST(GCInFailedAccessCheckCallback) {
11302 // Install a failed access check callback that performs a GC on each
11303 // invocation. Then force the callback to be called from va
11304
11305 v8::V8::Initialize();
11306 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
11307
11308 v8::HandleScope scope;
11309
11310 // Create an ObjectTemplate for global objects and install access
11311 // check callbacks that will block access.
11312 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
11313 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
11314 IndexedGetAccessBlocker,
11315 v8::Handle<v8::Value>(),
11316 false);
11317
11318 // Create a context and set an x property on it's global object.
11319 LocalContext context0(NULL, global_template);
11320 context0->Global()->Set(v8_str("x"), v8_num(42));
11321 v8::Handle<v8::Object> global0 = context0->Global();
11322
11323 // Create a context with a different security token so that the
11324 // failed access check callback will be called on each access.
11325 LocalContext context1(NULL, global_template);
11326 context1->Global()->Set(v8_str("other"), global0);
11327
11328 // Get property with failed access check.
11329 ExpectUndefined("other.x");
11330
11331 // Get element with failed access check.
11332 ExpectUndefined("other[0]");
11333
11334 // Set property with failed access check.
11335 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
11336 CHECK(result->IsObject());
11337
11338 // Set element with failed access check.
11339 result = CompileRun("other[0] = new Object()");
11340 CHECK(result->IsObject());
11341
11342 // Get property attribute with failed access check.
11343 ExpectFalse("\'x\' in other");
11344
11345 // Get property attribute for element with failed access check.
11346 ExpectFalse("0 in other");
11347
11348 // Delete property.
11349 ExpectFalse("delete other.x");
11350
11351 // Delete element.
11352 CHECK_EQ(false, global0->Delete(0));
11353
11354 // DefineAccessor.
11355 CHECK_EQ(false,
11356 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
11357
11358 // Define JavaScript accessor.
11359 ExpectUndefined("Object.prototype.__defineGetter__.call("
11360 " other, \'x\', function() { return 42; })");
11361
11362 // LookupAccessor.
11363 ExpectUndefined("Object.prototype.__lookupGetter__.call("
11364 " other, \'x\')");
11365
11366 // HasLocalElement.
11367 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
11368
11369 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
11370 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
11371 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
11372
11373 // Reset the failed access check callback so it does not influence
11374 // the other tests.
11375 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
11376}
Kristian Monsen0d5e1162010-09-30 15:31:59 +010011377
11378
11379TEST(StringCheckMultipleContexts) {
11380 const char* code =
11381 "(function() { return \"a\".charAt(0); })()";
11382
11383 {
11384 // Run the code twice in the first context to initialize the call IC.
11385 v8::HandleScope scope;
11386 LocalContext context1;
11387 ExpectString(code, "a");
11388 ExpectString(code, "a");
11389 }
11390
11391 {
11392 // Change the String.prototype in the second context and check
11393 // that the right function gets called.
11394 v8::HandleScope scope;
11395 LocalContext context2;
11396 CompileRun("String.prototype.charAt = function() { return \"not a\"; }");
11397 ExpectString(code, "not a");
11398 }
11399}
11400
11401
11402TEST(NumberCheckMultipleContexts) {
11403 const char* code =
11404 "(function() { return (42).toString(); })()";
11405
11406 {
11407 // Run the code twice in the first context to initialize the call IC.
11408 v8::HandleScope scope;
11409 LocalContext context1;
11410 ExpectString(code, "42");
11411 ExpectString(code, "42");
11412 }
11413
11414 {
11415 // Change the Number.prototype in the second context and check
11416 // that the right function gets called.
11417 v8::HandleScope scope;
11418 LocalContext context2;
11419 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }");
11420 ExpectString(code, "not 42");
11421 }
11422}
11423
11424
11425TEST(BooleanCheckMultipleContexts) {
11426 const char* code =
11427 "(function() { return true.toString(); })()";
11428
11429 {
11430 // Run the code twice in the first context to initialize the call IC.
11431 v8::HandleScope scope;
11432 LocalContext context1;
11433 ExpectString(code, "true");
11434 ExpectString(code, "true");
11435 }
11436
11437 {
11438 // Change the Boolean.prototype in the second context and check
11439 // that the right function gets called.
11440 v8::HandleScope scope;
11441 LocalContext context2;
11442 CompileRun("Boolean.prototype.toString = function() { return \"\"; }");
11443 ExpectString(code, "");
11444 }
11445}