blob: 8bfa51c60a9ea81543ea20200eac52581241063f [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"
40
Andrei Popescu31002712010-02-23 13:46:05 +000041static const bool kLogThreading = true;
Steve Blockd0582a62009-12-15 09:54:21 +000042
Steve Blocka7e24c12009-10-30 11:49:00 +000043static bool IsNaN(double x) {
44#ifdef WIN32
45 return _isnan(x);
46#else
47 return isnan(x);
48#endif
49}
50
51using ::v8::ObjectTemplate;
52using ::v8::Value;
53using ::v8::Context;
54using ::v8::Local;
55using ::v8::String;
56using ::v8::Script;
57using ::v8::Function;
58using ::v8::AccessorInfo;
59using ::v8::Extension;
60
Steve Block8defd9f2010-07-08 12:39:36 +010061namespace i = ::i;
Steve Blocka7e24c12009-10-30 11:49:00 +000062
Steve Blocka7e24c12009-10-30 11:49:00 +000063
Leon Clarked91b9f72010-01-27 17:25:45 +000064static void ExpectString(const char* code, const char* expected) {
65 Local<Value> result = CompileRun(code);
66 CHECK(result->IsString());
67 String::AsciiValue ascii(result);
68 CHECK_EQ(expected, *ascii);
69}
70
71
72static void ExpectBoolean(const char* code, bool expected) {
73 Local<Value> result = CompileRun(code);
74 CHECK(result->IsBoolean());
75 CHECK_EQ(expected, result->BooleanValue());
76}
77
78
Leon Clarkef7060e22010-06-03 12:02:55 +010079static void ExpectTrue(const char* code) {
80 ExpectBoolean(code, true);
81}
82
83
Iain Merrick75681382010-08-19 15:07:18 +010084static void ExpectFalse(const char* code) {
85 ExpectBoolean(code, false);
86}
87
88
Leon Clarked91b9f72010-01-27 17:25:45 +000089static void ExpectObject(const char* code, Local<Value> expected) {
90 Local<Value> result = CompileRun(code);
91 CHECK(result->Equals(expected));
92}
93
94
Iain Merrick75681382010-08-19 15:07:18 +010095static void ExpectUndefined(const char* code) {
96 Local<Value> result = CompileRun(code);
97 CHECK(result->IsUndefined());
98}
99
100
Steve Blocka7e24c12009-10-30 11:49:00 +0000101static int signature_callback_count;
102static v8::Handle<Value> IncrementingSignatureCallback(
103 const v8::Arguments& args) {
104 ApiTestFuzzer::Fuzz();
105 signature_callback_count++;
106 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
107 for (int i = 0; i < args.Length(); i++)
108 result->Set(v8::Integer::New(i), args[i]);
109 return result;
110}
111
112
113static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
114 ApiTestFuzzer::Fuzz();
115 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
116 for (int i = 0; i < args.Length(); i++) {
117 result->Set(v8::Integer::New(i), args[i]);
118 }
119 return result;
120}
121
122
123THREADED_TEST(Handles) {
124 v8::HandleScope scope;
125 Local<Context> local_env;
126 {
127 LocalContext env;
128 local_env = env.local();
129 }
130
131 // Local context should still be live.
132 CHECK(!local_env.IsEmpty());
133 local_env->Enter();
134
135 v8::Handle<v8::Primitive> undef = v8::Undefined();
136 CHECK(!undef.IsEmpty());
137 CHECK(undef->IsUndefined());
138
139 const char* c_source = "1 + 2 + 3";
140 Local<String> source = String::New(c_source);
141 Local<Script> script = Script::Compile(source);
142 CHECK_EQ(6, script->Run()->Int32Value());
143
144 local_env->Exit();
145}
146
147
Steve Blocka7e24c12009-10-30 11:49:00 +0000148THREADED_TEST(ReceiverSignature) {
149 v8::HandleScope scope;
150 LocalContext env;
151 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
152 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
153 fun->PrototypeTemplate()->Set(
154 v8_str("m"),
155 v8::FunctionTemplate::New(IncrementingSignatureCallback,
156 v8::Handle<Value>(),
157 sig));
158 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
159 signature_callback_count = 0;
160 CompileRun(
161 "var o = new Fun();"
162 "o.m();");
163 CHECK_EQ(1, signature_callback_count);
164 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
165 sub_fun->Inherit(fun);
166 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
167 CompileRun(
168 "var o = new SubFun();"
169 "o.m();");
170 CHECK_EQ(2, signature_callback_count);
171
172 v8::TryCatch try_catch;
173 CompileRun(
174 "var o = { };"
175 "o.m = Fun.prototype.m;"
176 "o.m();");
177 CHECK_EQ(2, signature_callback_count);
178 CHECK(try_catch.HasCaught());
179 try_catch.Reset();
180 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
181 sub_fun->Inherit(fun);
182 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
183 CompileRun(
184 "var o = new UnrelFun();"
185 "o.m = Fun.prototype.m;"
186 "o.m();");
187 CHECK_EQ(2, signature_callback_count);
188 CHECK(try_catch.HasCaught());
189}
190
191
192
193
194THREADED_TEST(ArgumentSignature) {
195 v8::HandleScope scope;
196 LocalContext env;
197 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
198 cons->SetClassName(v8_str("Cons"));
199 v8::Handle<v8::Signature> sig =
200 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
201 v8::Handle<v8::FunctionTemplate> fun =
202 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
203 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
204 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
205
206 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
207 CHECK(value1->IsTrue());
208
209 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
210 CHECK(value2->IsTrue());
211
212 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
213 CHECK(value3->IsTrue());
214
215 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
216 cons1->SetClassName(v8_str("Cons1"));
217 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
218 cons2->SetClassName(v8_str("Cons2"));
219 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
220 cons3->SetClassName(v8_str("Cons3"));
221
222 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
223 v8::Handle<v8::Signature> wsig =
224 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
225 v8::Handle<v8::FunctionTemplate> fun2 =
226 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
227
228 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
229 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
230 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
231 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
232 v8::Handle<Value> value4 = CompileRun(
233 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
234 "'[object Cons1],[object Cons2],[object Cons3]'");
235 CHECK(value4->IsTrue());
236
237 v8::Handle<Value> value5 = CompileRun(
238 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
239 CHECK(value5->IsTrue());
240
241 v8::Handle<Value> value6 = CompileRun(
242 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
243 CHECK(value6->IsTrue());
244
245 v8::Handle<Value> value7 = CompileRun(
246 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
247 "'[object Cons1],[object Cons2],[object Cons3],d';");
248 CHECK(value7->IsTrue());
249
250 v8::Handle<Value> value8 = CompileRun(
251 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
252 CHECK(value8->IsTrue());
253}
254
255
256THREADED_TEST(HulIgennem) {
257 v8::HandleScope scope;
258 LocalContext env;
259 v8::Handle<v8::Primitive> undef = v8::Undefined();
260 Local<String> undef_str = undef->ToString();
261 char* value = i::NewArray<char>(undef_str->Length() + 1);
262 undef_str->WriteAscii(value);
263 CHECK_EQ(0, strcmp(value, "undefined"));
264 i::DeleteArray(value);
265}
266
267
268THREADED_TEST(Access) {
269 v8::HandleScope scope;
270 LocalContext env;
271 Local<v8::Object> obj = v8::Object::New();
272 Local<Value> foo_before = obj->Get(v8_str("foo"));
273 CHECK(foo_before->IsUndefined());
274 Local<String> bar_str = v8_str("bar");
275 obj->Set(v8_str("foo"), bar_str);
276 Local<Value> foo_after = obj->Get(v8_str("foo"));
277 CHECK(!foo_after->IsUndefined());
278 CHECK(foo_after->IsString());
279 CHECK_EQ(bar_str, foo_after);
280}
281
282
Steve Block6ded16b2010-05-10 14:33:55 +0100283THREADED_TEST(AccessElement) {
284 v8::HandleScope scope;
285 LocalContext env;
286 Local<v8::Object> obj = v8::Object::New();
287 Local<Value> before = obj->Get(1);
288 CHECK(before->IsUndefined());
289 Local<String> bar_str = v8_str("bar");
290 obj->Set(1, bar_str);
291 Local<Value> after = obj->Get(1);
292 CHECK(!after->IsUndefined());
293 CHECK(after->IsString());
294 CHECK_EQ(bar_str, after);
295
296 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
297 CHECK_EQ(v8_str("a"), value->Get(0));
298 CHECK_EQ(v8_str("b"), value->Get(1));
299}
300
301
Steve Blocka7e24c12009-10-30 11:49:00 +0000302THREADED_TEST(Script) {
303 v8::HandleScope scope;
304 LocalContext env;
305 const char* c_source = "1 + 2 + 3";
306 Local<String> source = String::New(c_source);
307 Local<Script> script = Script::Compile(source);
308 CHECK_EQ(6, script->Run()->Int32Value());
309}
310
311
312static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000313 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000314 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000315 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000316 return converted;
317}
318
319
320class TestResource: public String::ExternalStringResource {
321 public:
322 static int dispose_count;
323
324 explicit TestResource(uint16_t* data)
325 : data_(data), length_(0) {
326 while (data[length_]) ++length_;
327 }
328
329 ~TestResource() {
330 i::DeleteArray(data_);
331 ++dispose_count;
332 }
333
334 const uint16_t* data() const {
335 return data_;
336 }
337
338 size_t length() const {
339 return length_;
340 }
341 private:
342 uint16_t* data_;
343 size_t length_;
344};
345
346
347int TestResource::dispose_count = 0;
348
349
350class TestAsciiResource: public String::ExternalAsciiStringResource {
351 public:
352 static int dispose_count;
353
354 explicit TestAsciiResource(const char* data)
355 : data_(data),
356 length_(strlen(data)) { }
357
358 ~TestAsciiResource() {
359 i::DeleteArray(data_);
360 ++dispose_count;
361 }
362
363 const char* data() const {
364 return data_;
365 }
366
367 size_t length() const {
368 return length_;
369 }
370 private:
371 const char* data_;
372 size_t length_;
373};
374
375
376int TestAsciiResource::dispose_count = 0;
377
378
379THREADED_TEST(ScriptUsingStringResource) {
380 TestResource::dispose_count = 0;
381 const char* c_source = "1 + 2 * 3";
382 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
383 {
384 v8::HandleScope scope;
385 LocalContext env;
386 TestResource* resource = new TestResource(two_byte_source);
387 Local<String> source = String::NewExternal(resource);
388 Local<Script> script = Script::Compile(source);
389 Local<Value> value = script->Run();
390 CHECK(value->IsNumber());
391 CHECK_EQ(7, value->Int32Value());
392 CHECK(source->IsExternal());
393 CHECK_EQ(resource,
394 static_cast<TestResource*>(source->GetExternalStringResource()));
Steve Block8defd9f2010-07-08 12:39:36 +0100395 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000396 CHECK_EQ(0, TestResource::dispose_count);
397 }
Steve Block8defd9f2010-07-08 12:39:36 +0100398 i::CompilationCache::Clear();
399 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000400 CHECK_EQ(1, TestResource::dispose_count);
401}
402
403
404THREADED_TEST(ScriptUsingAsciiStringResource) {
405 TestAsciiResource::dispose_count = 0;
406 const char* c_source = "1 + 2 * 3";
407 {
408 v8::HandleScope scope;
409 LocalContext env;
410 Local<String> source =
411 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
412 Local<Script> script = Script::Compile(source);
413 Local<Value> value = script->Run();
414 CHECK(value->IsNumber());
415 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100416 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000417 CHECK_EQ(0, TestAsciiResource::dispose_count);
418 }
Steve Block8defd9f2010-07-08 12:39:36 +0100419 i::CompilationCache::Clear();
420 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000421 CHECK_EQ(1, TestAsciiResource::dispose_count);
422}
423
424
425THREADED_TEST(ScriptMakingExternalString) {
426 TestResource::dispose_count = 0;
427 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
428 {
429 v8::HandleScope scope;
430 LocalContext env;
431 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000432 // Trigger GCs so that the newly allocated string moves to old gen.
433 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
434 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000435 bool success = source->MakeExternal(new TestResource(two_byte_source));
436 CHECK(success);
437 Local<Script> script = Script::Compile(source);
438 Local<Value> value = script->Run();
439 CHECK(value->IsNumber());
440 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100441 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000442 CHECK_EQ(0, TestResource::dispose_count);
443 }
Steve Block8defd9f2010-07-08 12:39:36 +0100444 i::CompilationCache::Clear();
445 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000446 CHECK_EQ(1, TestResource::dispose_count);
447}
448
449
450THREADED_TEST(ScriptMakingExternalAsciiString) {
451 TestAsciiResource::dispose_count = 0;
452 const char* c_source = "1 + 2 * 3";
453 {
454 v8::HandleScope scope;
455 LocalContext env;
456 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000457 // Trigger GCs so that the newly allocated string moves to old gen.
458 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
459 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000460 bool success = source->MakeExternal(
461 new TestAsciiResource(i::StrDup(c_source)));
462 CHECK(success);
463 Local<Script> script = Script::Compile(source);
464 Local<Value> value = script->Run();
465 CHECK(value->IsNumber());
466 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100467 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000468 CHECK_EQ(0, TestAsciiResource::dispose_count);
469 }
Steve Block8defd9f2010-07-08 12:39:36 +0100470 i::CompilationCache::Clear();
471 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000472 CHECK_EQ(1, TestAsciiResource::dispose_count);
473}
474
475
Andrei Popescu402d9372010-02-26 13:31:12 +0000476TEST(MakingExternalStringConditions) {
477 v8::HandleScope scope;
478 LocalContext env;
479
480 // Free some space in the new space so that we can check freshness.
481 i::Heap::CollectGarbage(0, i::NEW_SPACE);
482 i::Heap::CollectGarbage(0, i::NEW_SPACE);
483
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100484 uint16_t* two_byte_string = AsciiToTwoByteString("small");
485 Local<String> small_string = String::New(two_byte_string);
486 i::DeleteArray(two_byte_string);
487
Andrei Popescu402d9372010-02-26 13:31:12 +0000488 // We should refuse to externalize newly created small string.
489 CHECK(!small_string->CanMakeExternal());
490 // Trigger GCs so that the newly allocated string moves to old gen.
491 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
492 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
493 // Old space strings should be accepted.
494 CHECK(small_string->CanMakeExternal());
495
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100496 two_byte_string = AsciiToTwoByteString("small 2");
497 small_string = String::New(two_byte_string);
498 i::DeleteArray(two_byte_string);
499
Andrei Popescu402d9372010-02-26 13:31:12 +0000500 // We should refuse externalizing newly created small string.
501 CHECK(!small_string->CanMakeExternal());
502 for (int i = 0; i < 100; i++) {
503 String::Value value(small_string);
504 }
505 // Frequently used strings should be accepted.
506 CHECK(small_string->CanMakeExternal());
507
508 const int buf_size = 10 * 1024;
509 char* buf = i::NewArray<char>(buf_size);
510 memset(buf, 'a', buf_size);
511 buf[buf_size - 1] = '\0';
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100512
513 two_byte_string = AsciiToTwoByteString(buf);
514 Local<String> large_string = String::New(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000515 i::DeleteArray(buf);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100516 i::DeleteArray(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000517 // Large strings should be immediately accepted.
518 CHECK(large_string->CanMakeExternal());
519}
520
521
522TEST(MakingExternalAsciiStringConditions) {
523 v8::HandleScope scope;
524 LocalContext env;
525
526 // Free some space in the new space so that we can check freshness.
527 i::Heap::CollectGarbage(0, i::NEW_SPACE);
528 i::Heap::CollectGarbage(0, i::NEW_SPACE);
529
530 Local<String> small_string = String::New("small");
531 // We should refuse to externalize newly created small string.
532 CHECK(!small_string->CanMakeExternal());
533 // Trigger GCs so that the newly allocated string moves to old gen.
534 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
535 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
536 // Old space strings should be accepted.
537 CHECK(small_string->CanMakeExternal());
538
539 small_string = String::New("small 2");
540 // We should refuse externalizing newly created small string.
541 CHECK(!small_string->CanMakeExternal());
542 for (int i = 0; i < 100; i++) {
543 String::Value value(small_string);
544 }
545 // Frequently used strings should be accepted.
546 CHECK(small_string->CanMakeExternal());
547
548 const int buf_size = 10 * 1024;
549 char* buf = i::NewArray<char>(buf_size);
550 memset(buf, 'a', buf_size);
551 buf[buf_size - 1] = '\0';
552 Local<String> large_string = String::New(buf);
553 i::DeleteArray(buf);
554 // Large strings should be immediately accepted.
555 CHECK(large_string->CanMakeExternal());
556}
557
558
Steve Blocka7e24c12009-10-30 11:49:00 +0000559THREADED_TEST(UsingExternalString) {
560 {
561 v8::HandleScope scope;
562 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
563 Local<String> string =
564 String::NewExternal(new TestResource(two_byte_string));
565 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
566 // Trigger GCs so that the newly allocated string moves to old gen.
567 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
568 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
569 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
570 CHECK(isymbol->IsSymbol());
571 }
572 i::Heap::CollectAllGarbage(false);
573 i::Heap::CollectAllGarbage(false);
574}
575
576
577THREADED_TEST(UsingExternalAsciiString) {
578 {
579 v8::HandleScope scope;
580 const char* one_byte_string = "test string";
581 Local<String> string = String::NewExternal(
582 new TestAsciiResource(i::StrDup(one_byte_string)));
583 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
584 // Trigger GCs so that the newly allocated string moves to old gen.
585 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
586 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
587 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
588 CHECK(isymbol->IsSymbol());
589 }
590 i::Heap::CollectAllGarbage(false);
591 i::Heap::CollectAllGarbage(false);
592}
593
594
Leon Clarkee46be812010-01-19 14:06:41 +0000595THREADED_TEST(ScavengeExternalString) {
596 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100597 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000598 {
599 v8::HandleScope scope;
600 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
601 Local<String> string =
602 String::NewExternal(new TestResource(two_byte_string));
603 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
604 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100605 in_new_space = i::Heap::InNewSpace(*istring);
606 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000607 CHECK_EQ(0, TestResource::dispose_count);
608 }
Steve Block6ded16b2010-05-10 14:33:55 +0100609 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000610 CHECK_EQ(1, TestResource::dispose_count);
611}
612
613
614THREADED_TEST(ScavengeExternalAsciiString) {
615 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100616 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000617 {
618 v8::HandleScope scope;
619 const char* one_byte_string = "test string";
620 Local<String> string = String::NewExternal(
621 new TestAsciiResource(i::StrDup(one_byte_string)));
622 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
623 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100624 in_new_space = i::Heap::InNewSpace(*istring);
625 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000626 CHECK_EQ(0, TestAsciiResource::dispose_count);
627 }
Steve Block6ded16b2010-05-10 14:33:55 +0100628 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000629 CHECK_EQ(1, TestAsciiResource::dispose_count);
630}
631
632
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100633class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
634 public:
635 static int dispose_calls;
636
637 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
638 : TestAsciiResource(data),
639 dispose_(dispose) { }
640
641 void Dispose() {
642 ++dispose_calls;
643 if (dispose_) delete this;
644 }
645 private:
646 bool dispose_;
647};
648
649
650int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
651
652
653TEST(ExternalStringWithDisposeHandling) {
654 const char* c_source = "1 + 2 * 3";
655
656 // Use a stack allocated external string resource allocated object.
657 TestAsciiResource::dispose_count = 0;
658 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
659 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
660 {
661 v8::HandleScope scope;
662 LocalContext env;
663 Local<String> source = String::NewExternal(&res_stack);
664 Local<Script> script = Script::Compile(source);
665 Local<Value> value = script->Run();
666 CHECK(value->IsNumber());
667 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100668 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100669 CHECK_EQ(0, TestAsciiResource::dispose_count);
670 }
Steve Block8defd9f2010-07-08 12:39:36 +0100671 i::CompilationCache::Clear();
672 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100673 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
674 CHECK_EQ(0, TestAsciiResource::dispose_count);
675
676 // Use a heap allocated external string resource allocated object.
677 TestAsciiResource::dispose_count = 0;
678 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
679 TestAsciiResource* res_heap =
680 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
681 {
682 v8::HandleScope scope;
683 LocalContext env;
684 Local<String> source = String::NewExternal(res_heap);
685 Local<Script> script = Script::Compile(source);
686 Local<Value> value = script->Run();
687 CHECK(value->IsNumber());
688 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100689 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100690 CHECK_EQ(0, TestAsciiResource::dispose_count);
691 }
Steve Block8defd9f2010-07-08 12:39:36 +0100692 i::CompilationCache::Clear();
693 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100694 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
695 CHECK_EQ(1, TestAsciiResource::dispose_count);
696}
697
698
Steve Block3ce2e202009-11-05 08:53:23 +0000699THREADED_TEST(StringConcat) {
700 {
701 v8::HandleScope scope;
702 LocalContext env;
703 const char* one_byte_string_1 = "function a_times_t";
704 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
705 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
706 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
707 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
708 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
709 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
710 Local<String> left = v8_str(one_byte_string_1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100711
712 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
713 Local<String> right = String::New(two_byte_source);
714 i::DeleteArray(two_byte_source);
715
Steve Block3ce2e202009-11-05 08:53:23 +0000716 Local<String> source = String::Concat(left, right);
717 right = String::NewExternal(
718 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
719 source = String::Concat(source, right);
720 right = String::NewExternal(
721 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
722 source = String::Concat(source, right);
723 right = v8_str(one_byte_string_2);
724 source = String::Concat(source, right);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100725
726 two_byte_source = AsciiToTwoByteString(two_byte_string_2);
727 right = String::New(two_byte_source);
728 i::DeleteArray(two_byte_source);
729
Steve Block3ce2e202009-11-05 08:53:23 +0000730 source = String::Concat(source, right);
731 right = String::NewExternal(
732 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
733 source = String::Concat(source, right);
734 Local<Script> script = Script::Compile(source);
735 Local<Value> value = script->Run();
736 CHECK(value->IsNumber());
737 CHECK_EQ(68, value->Int32Value());
738 }
Steve Block8defd9f2010-07-08 12:39:36 +0100739 i::CompilationCache::Clear();
Steve Block3ce2e202009-11-05 08:53:23 +0000740 i::Heap::CollectAllGarbage(false);
741 i::Heap::CollectAllGarbage(false);
742}
743
744
Steve Blocka7e24c12009-10-30 11:49:00 +0000745THREADED_TEST(GlobalProperties) {
746 v8::HandleScope scope;
747 LocalContext env;
748 v8::Handle<v8::Object> global = env->Global();
749 global->Set(v8_str("pi"), v8_num(3.1415926));
750 Local<Value> pi = global->Get(v8_str("pi"));
751 CHECK_EQ(3.1415926, pi->NumberValue());
752}
753
754
755static v8::Handle<Value> handle_call(const v8::Arguments& args) {
756 ApiTestFuzzer::Fuzz();
757 return v8_num(102);
758}
759
760
761static v8::Handle<Value> construct_call(const v8::Arguments& args) {
762 ApiTestFuzzer::Fuzz();
763 args.This()->Set(v8_str("x"), v8_num(1));
764 args.This()->Set(v8_str("y"), v8_num(2));
765 return args.This();
766}
767
768THREADED_TEST(FunctionTemplate) {
769 v8::HandleScope scope;
770 LocalContext env;
771 {
772 Local<v8::FunctionTemplate> fun_templ =
773 v8::FunctionTemplate::New(handle_call);
774 Local<Function> fun = fun_templ->GetFunction();
775 env->Global()->Set(v8_str("obj"), fun);
776 Local<Script> script = v8_compile("obj()");
777 CHECK_EQ(102, script->Run()->Int32Value());
778 }
779 // Use SetCallHandler to initialize a function template, should work like the
780 // previous one.
781 {
782 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
783 fun_templ->SetCallHandler(handle_call);
784 Local<Function> fun = fun_templ->GetFunction();
785 env->Global()->Set(v8_str("obj"), fun);
786 Local<Script> script = v8_compile("obj()");
787 CHECK_EQ(102, script->Run()->Int32Value());
788 }
789 // Test constructor calls.
790 {
791 Local<v8::FunctionTemplate> fun_templ =
792 v8::FunctionTemplate::New(construct_call);
793 fun_templ->SetClassName(v8_str("funky"));
794 Local<Function> fun = fun_templ->GetFunction();
795 env->Global()->Set(v8_str("obj"), fun);
796 Local<Script> script = v8_compile("var s = new obj(); s.x");
797 CHECK_EQ(1, script->Run()->Int32Value());
798
799 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
800 CHECK_EQ(v8_str("[object funky]"), result);
801 }
802}
803
804
805THREADED_TEST(FindInstanceInPrototypeChain) {
806 v8::HandleScope scope;
807 LocalContext env;
808
809 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
810 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
811 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
812 derived->Inherit(base);
813
814 Local<v8::Function> base_function = base->GetFunction();
815 Local<v8::Function> derived_function = derived->GetFunction();
816 Local<v8::Function> other_function = other->GetFunction();
817
818 Local<v8::Object> base_instance = base_function->NewInstance();
819 Local<v8::Object> derived_instance = derived_function->NewInstance();
820 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
821 Local<v8::Object> other_instance = other_function->NewInstance();
822 derived_instance2->Set(v8_str("__proto__"), derived_instance);
823 other_instance->Set(v8_str("__proto__"), derived_instance2);
824
825 // base_instance is only an instance of base.
826 CHECK_EQ(base_instance,
827 base_instance->FindInstanceInPrototypeChain(base));
828 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
829 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
830
831 // derived_instance is an instance of base and derived.
832 CHECK_EQ(derived_instance,
833 derived_instance->FindInstanceInPrototypeChain(base));
834 CHECK_EQ(derived_instance,
835 derived_instance->FindInstanceInPrototypeChain(derived));
836 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
837
838 // other_instance is an instance of other and its immediate
839 // prototype derived_instance2 is an instance of base and derived.
840 // Note, derived_instance is an instance of base and derived too,
841 // but it comes after derived_instance2 in the prototype chain of
842 // other_instance.
843 CHECK_EQ(derived_instance2,
844 other_instance->FindInstanceInPrototypeChain(base));
845 CHECK_EQ(derived_instance2,
846 other_instance->FindInstanceInPrototypeChain(derived));
847 CHECK_EQ(other_instance,
848 other_instance->FindInstanceInPrototypeChain(other));
849}
850
851
Steve Block3ce2e202009-11-05 08:53:23 +0000852THREADED_TEST(TinyInteger) {
853 v8::HandleScope scope;
854 LocalContext env;
855 int32_t value = 239;
856 Local<v8::Integer> value_obj = v8::Integer::New(value);
857 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
858}
859
860
861THREADED_TEST(BigSmiInteger) {
862 v8::HandleScope scope;
863 LocalContext env;
864 int32_t value = i::Smi::kMaxValue;
865 // We cannot add one to a Smi::kMaxValue without wrapping.
866 if (i::kSmiValueSize < 32) {
867 CHECK(i::Smi::IsValid(value));
868 CHECK(!i::Smi::IsValid(value + 1));
869 Local<v8::Integer> value_obj = v8::Integer::New(value);
870 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
871 }
872}
873
874
875THREADED_TEST(BigInteger) {
876 v8::HandleScope scope;
877 LocalContext env;
878 // We cannot add one to a Smi::kMaxValue without wrapping.
879 if (i::kSmiValueSize < 32) {
880 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
881 // The code will not be run in that case, due to the "if" guard.
882 int32_t value =
883 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
884 CHECK(value > i::Smi::kMaxValue);
885 CHECK(!i::Smi::IsValid(value));
886 Local<v8::Integer> value_obj = v8::Integer::New(value);
887 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
888 }
889}
890
891
892THREADED_TEST(TinyUnsignedInteger) {
893 v8::HandleScope scope;
894 LocalContext env;
895 uint32_t value = 239;
896 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
897 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
898}
899
900
901THREADED_TEST(BigUnsignedSmiInteger) {
902 v8::HandleScope scope;
903 LocalContext env;
904 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
905 CHECK(i::Smi::IsValid(value));
906 CHECK(!i::Smi::IsValid(value + 1));
907 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
908 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
909}
910
911
912THREADED_TEST(BigUnsignedInteger) {
913 v8::HandleScope scope;
914 LocalContext env;
915 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
916 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
917 CHECK(!i::Smi::IsValid(value));
918 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
919 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
920}
921
922
923THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
924 v8::HandleScope scope;
925 LocalContext env;
926 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
927 uint32_t value = INT32_MAX_AS_UINT + 1;
928 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
929 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
930 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
931}
932
933
Steve Blocka7e24c12009-10-30 11:49:00 +0000934THREADED_TEST(Number) {
935 v8::HandleScope scope;
936 LocalContext env;
937 double PI = 3.1415926;
938 Local<v8::Number> pi_obj = v8::Number::New(PI);
939 CHECK_EQ(PI, pi_obj->NumberValue());
940}
941
942
943THREADED_TEST(ToNumber) {
944 v8::HandleScope scope;
945 LocalContext env;
946 Local<String> str = v8_str("3.1415926");
947 CHECK_EQ(3.1415926, str->NumberValue());
948 v8::Handle<v8::Boolean> t = v8::True();
949 CHECK_EQ(1.0, t->NumberValue());
950 v8::Handle<v8::Boolean> f = v8::False();
951 CHECK_EQ(0.0, f->NumberValue());
952}
953
954
955THREADED_TEST(Date) {
956 v8::HandleScope scope;
957 LocalContext env;
958 double PI = 3.1415926;
959 Local<Value> date_obj = v8::Date::New(PI);
960 CHECK_EQ(3.0, date_obj->NumberValue());
961}
962
963
964THREADED_TEST(Boolean) {
965 v8::HandleScope scope;
966 LocalContext env;
967 v8::Handle<v8::Boolean> t = v8::True();
968 CHECK(t->Value());
969 v8::Handle<v8::Boolean> f = v8::False();
970 CHECK(!f->Value());
971 v8::Handle<v8::Primitive> u = v8::Undefined();
972 CHECK(!u->BooleanValue());
973 v8::Handle<v8::Primitive> n = v8::Null();
974 CHECK(!n->BooleanValue());
975 v8::Handle<String> str1 = v8_str("");
976 CHECK(!str1->BooleanValue());
977 v8::Handle<String> str2 = v8_str("x");
978 CHECK(str2->BooleanValue());
979 CHECK(!v8::Number::New(0)->BooleanValue());
980 CHECK(v8::Number::New(-1)->BooleanValue());
981 CHECK(v8::Number::New(1)->BooleanValue());
982 CHECK(v8::Number::New(42)->BooleanValue());
983 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
984}
985
986
987static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
988 ApiTestFuzzer::Fuzz();
989 return v8_num(13.4);
990}
991
992
993static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
994 ApiTestFuzzer::Fuzz();
995 return v8_num(876);
996}
997
998
999THREADED_TEST(GlobalPrototype) {
1000 v8::HandleScope scope;
1001 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
1002 func_templ->PrototypeTemplate()->Set(
1003 "dummy",
1004 v8::FunctionTemplate::New(DummyCallHandler));
1005 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
1006 templ->Set("x", v8_num(200));
1007 templ->SetAccessor(v8_str("m"), GetM);
1008 LocalContext env(0, templ);
1009 v8::Handle<v8::Object> obj = env->Global();
1010 v8::Handle<Script> script = v8_compile("dummy()");
1011 v8::Handle<Value> result = script->Run();
1012 CHECK_EQ(13.4, result->NumberValue());
1013 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1014 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1015}
1016
1017
Steve Blocka7e24c12009-10-30 11:49:00 +00001018THREADED_TEST(ObjectTemplate) {
1019 v8::HandleScope scope;
1020 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1021 templ1->Set("x", v8_num(10));
1022 templ1->Set("y", v8_num(13));
1023 LocalContext env;
1024 Local<v8::Object> instance1 = templ1->NewInstance();
1025 env->Global()->Set(v8_str("p"), instance1);
1026 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1027 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1028 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1029 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1030 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1031 templ2->Set("a", v8_num(12));
1032 templ2->Set("b", templ1);
1033 Local<v8::Object> instance2 = templ2->NewInstance();
1034 env->Global()->Set(v8_str("q"), instance2);
1035 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1036 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1037 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1038 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1039}
1040
1041
1042static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1043 ApiTestFuzzer::Fuzz();
1044 return v8_num(17.2);
1045}
1046
1047
1048static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1049 ApiTestFuzzer::Fuzz();
1050 return v8_num(15.2);
1051}
1052
1053
1054THREADED_TEST(DescriptorInheritance) {
1055 v8::HandleScope scope;
1056 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1057 super->PrototypeTemplate()->Set("flabby",
1058 v8::FunctionTemplate::New(GetFlabby));
1059 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1060
1061 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1062
1063 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1064 base1->Inherit(super);
1065 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1066
1067 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1068 base2->Inherit(super);
1069 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1070
1071 LocalContext env;
1072
1073 env->Global()->Set(v8_str("s"), super->GetFunction());
1074 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1075 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1076
1077 // Checks right __proto__ chain.
1078 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1079 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1080
1081 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1082
1083 // Instance accessor should not be visible on function object or its prototype
1084 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1085 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1086 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1087
1088 env->Global()->Set(v8_str("obj"),
1089 base1->GetFunction()->NewInstance());
1090 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1091 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1092 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1093 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1094 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1095
1096 env->Global()->Set(v8_str("obj2"),
1097 base2->GetFunction()->NewInstance());
1098 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1099 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1100 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1101 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1102 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1103
1104 // base1 and base2 cannot cross reference to each's prototype
1105 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1106 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1107}
1108
1109
1110int echo_named_call_count;
1111
1112
1113static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1114 const AccessorInfo& info) {
1115 ApiTestFuzzer::Fuzz();
1116 CHECK_EQ(v8_str("data"), info.Data());
1117 echo_named_call_count++;
1118 return name;
1119}
1120
1121
1122THREADED_TEST(NamedPropertyHandlerGetter) {
1123 echo_named_call_count = 0;
1124 v8::HandleScope scope;
1125 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1126 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1127 0, 0, 0, 0,
1128 v8_str("data"));
1129 LocalContext env;
1130 env->Global()->Set(v8_str("obj"),
1131 templ->GetFunction()->NewInstance());
1132 CHECK_EQ(echo_named_call_count, 0);
1133 v8_compile("obj.x")->Run();
1134 CHECK_EQ(echo_named_call_count, 1);
1135 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1136 v8::Handle<Value> str = CompileRun(code);
1137 String::AsciiValue value(str);
1138 CHECK_EQ(*value, "oddlepoddle");
1139 // Check default behavior
1140 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1141 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1142 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1143}
1144
1145
1146int echo_indexed_call_count = 0;
1147
1148
1149static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1150 const AccessorInfo& info) {
1151 ApiTestFuzzer::Fuzz();
1152 CHECK_EQ(v8_num(637), info.Data());
1153 echo_indexed_call_count++;
1154 return v8_num(index);
1155}
1156
1157
1158THREADED_TEST(IndexedPropertyHandlerGetter) {
1159 v8::HandleScope scope;
1160 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1161 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1162 0, 0, 0, 0,
1163 v8_num(637));
1164 LocalContext env;
1165 env->Global()->Set(v8_str("obj"),
1166 templ->GetFunction()->NewInstance());
1167 Local<Script> script = v8_compile("obj[900]");
1168 CHECK_EQ(script->Run()->Int32Value(), 900);
1169}
1170
1171
1172v8::Handle<v8::Object> bottom;
1173
1174static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1175 uint32_t index,
1176 const AccessorInfo& info) {
1177 ApiTestFuzzer::Fuzz();
1178 CHECK(info.This()->Equals(bottom));
1179 return v8::Handle<Value>();
1180}
1181
1182static v8::Handle<Value> CheckThisNamedPropertyHandler(
1183 Local<String> name,
1184 const AccessorInfo& info) {
1185 ApiTestFuzzer::Fuzz();
1186 CHECK(info.This()->Equals(bottom));
1187 return v8::Handle<Value>();
1188}
1189
1190
1191v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1192 Local<Value> value,
1193 const AccessorInfo& info) {
1194 ApiTestFuzzer::Fuzz();
1195 CHECK(info.This()->Equals(bottom));
1196 return v8::Handle<Value>();
1197}
1198
1199
1200v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1201 Local<Value> value,
1202 const AccessorInfo& info) {
1203 ApiTestFuzzer::Fuzz();
1204 CHECK(info.This()->Equals(bottom));
1205 return v8::Handle<Value>();
1206}
1207
Iain Merrick75681382010-08-19 15:07:18 +01001208v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery(
Steve Blocka7e24c12009-10-30 11:49:00 +00001209 uint32_t index,
1210 const AccessorInfo& info) {
1211 ApiTestFuzzer::Fuzz();
1212 CHECK(info.This()->Equals(bottom));
Iain Merrick75681382010-08-19 15:07:18 +01001213 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001214}
1215
1216
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001217v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001218 const AccessorInfo& info) {
1219 ApiTestFuzzer::Fuzz();
1220 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001221 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001222}
1223
1224
1225v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1226 uint32_t index,
1227 const AccessorInfo& info) {
1228 ApiTestFuzzer::Fuzz();
1229 CHECK(info.This()->Equals(bottom));
1230 return v8::Handle<v8::Boolean>();
1231}
1232
1233
1234v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1235 Local<String> property,
1236 const AccessorInfo& info) {
1237 ApiTestFuzzer::Fuzz();
1238 CHECK(info.This()->Equals(bottom));
1239 return v8::Handle<v8::Boolean>();
1240}
1241
1242
1243v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1244 const AccessorInfo& info) {
1245 ApiTestFuzzer::Fuzz();
1246 CHECK(info.This()->Equals(bottom));
1247 return v8::Handle<v8::Array>();
1248}
1249
1250
1251v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1252 const AccessorInfo& info) {
1253 ApiTestFuzzer::Fuzz();
1254 CHECK(info.This()->Equals(bottom));
1255 return v8::Handle<v8::Array>();
1256}
1257
1258
1259THREADED_TEST(PropertyHandlerInPrototype) {
1260 v8::HandleScope scope;
1261 LocalContext env;
1262
1263 // Set up a prototype chain with three interceptors.
1264 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1265 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1266 CheckThisIndexedPropertyHandler,
1267 CheckThisIndexedPropertySetter,
1268 CheckThisIndexedPropertyQuery,
1269 CheckThisIndexedPropertyDeleter,
1270 CheckThisIndexedPropertyEnumerator);
1271
1272 templ->InstanceTemplate()->SetNamedPropertyHandler(
1273 CheckThisNamedPropertyHandler,
1274 CheckThisNamedPropertySetter,
1275 CheckThisNamedPropertyQuery,
1276 CheckThisNamedPropertyDeleter,
1277 CheckThisNamedPropertyEnumerator);
1278
1279 bottom = templ->GetFunction()->NewInstance();
1280 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1281 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1282
1283 bottom->Set(v8_str("__proto__"), middle);
1284 middle->Set(v8_str("__proto__"), top);
1285 env->Global()->Set(v8_str("obj"), bottom);
1286
1287 // Indexed and named get.
1288 Script::Compile(v8_str("obj[0]"))->Run();
1289 Script::Compile(v8_str("obj.x"))->Run();
1290
1291 // Indexed and named set.
1292 Script::Compile(v8_str("obj[1] = 42"))->Run();
1293 Script::Compile(v8_str("obj.y = 42"))->Run();
1294
1295 // Indexed and named query.
1296 Script::Compile(v8_str("0 in obj"))->Run();
1297 Script::Compile(v8_str("'x' in obj"))->Run();
1298
1299 // Indexed and named deleter.
1300 Script::Compile(v8_str("delete obj[0]"))->Run();
1301 Script::Compile(v8_str("delete obj.x"))->Run();
1302
1303 // Enumerators.
1304 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1305}
1306
1307
1308static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1309 const AccessorInfo& info) {
1310 ApiTestFuzzer::Fuzz();
1311 if (v8_str("pre")->Equals(key)) {
1312 return v8_str("PrePropertyHandler: pre");
1313 }
1314 return v8::Handle<String>();
1315}
1316
1317
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001318static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1319 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001320 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001321 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001322 }
1323
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001324 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001325}
1326
1327
1328THREADED_TEST(PrePropertyHandler) {
1329 v8::HandleScope scope;
1330 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1331 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1332 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001333 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001334 LocalContext env(NULL, desc->InstanceTemplate());
1335 Script::Compile(v8_str(
1336 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1337 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1338 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1339 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1340 CHECK_EQ(v8_str("Object: on"), result_on);
1341 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1342 CHECK(result_post.IsEmpty());
1343}
1344
1345
1346THREADED_TEST(UndefinedIsNotEnumerable) {
1347 v8::HandleScope scope;
1348 LocalContext env;
1349 v8::Handle<Value> result = Script::Compile(v8_str(
1350 "this.propertyIsEnumerable(undefined)"))->Run();
1351 CHECK(result->IsFalse());
1352}
1353
1354
1355v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001356static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001357
1358
1359static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1360 ApiTestFuzzer::Fuzz();
1361 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1362 if (depth == kTargetRecursionDepth) return v8::Undefined();
1363 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1364 return call_recursively_script->Run();
1365}
1366
1367
1368static v8::Handle<Value> CallFunctionRecursivelyCall(
1369 const v8::Arguments& args) {
1370 ApiTestFuzzer::Fuzz();
1371 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1372 if (depth == kTargetRecursionDepth) {
1373 printf("[depth = %d]\n", depth);
1374 return v8::Undefined();
1375 }
1376 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1377 v8::Handle<Value> function =
1378 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001379 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001380}
1381
1382
1383THREADED_TEST(DeepCrossLanguageRecursion) {
1384 v8::HandleScope scope;
1385 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1386 global->Set(v8_str("callScriptRecursively"),
1387 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1388 global->Set(v8_str("callFunctionRecursively"),
1389 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1390 LocalContext env(NULL, global);
1391
1392 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1393 call_recursively_script = v8_compile("callScriptRecursively()");
1394 v8::Handle<Value> result = call_recursively_script->Run();
1395 call_recursively_script = v8::Handle<Script>();
1396
1397 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1398 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1399}
1400
1401
1402static v8::Handle<Value>
1403 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1404 ApiTestFuzzer::Fuzz();
1405 return v8::ThrowException(key);
1406}
1407
1408
1409static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1410 Local<Value>,
1411 const AccessorInfo&) {
1412 v8::ThrowException(key);
1413 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1414}
1415
1416
1417THREADED_TEST(CallbackExceptionRegression) {
1418 v8::HandleScope scope;
1419 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1420 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1421 ThrowingPropertyHandlerSet);
1422 LocalContext env;
1423 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1424 v8::Handle<Value> otto = Script::Compile(v8_str(
1425 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1426 CHECK_EQ(v8_str("otto"), otto);
1427 v8::Handle<Value> netto = Script::Compile(v8_str(
1428 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1429 CHECK_EQ(v8_str("netto"), netto);
1430}
1431
1432
Steve Blocka7e24c12009-10-30 11:49:00 +00001433THREADED_TEST(FunctionPrototype) {
1434 v8::HandleScope scope;
1435 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1436 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1437 LocalContext env;
1438 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1439 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1440 CHECK_EQ(script->Run()->Int32Value(), 321);
1441}
1442
1443
1444THREADED_TEST(InternalFields) {
1445 v8::HandleScope scope;
1446 LocalContext env;
1447
1448 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1449 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1450 instance_templ->SetInternalFieldCount(1);
1451 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1452 CHECK_EQ(1, obj->InternalFieldCount());
1453 CHECK(obj->GetInternalField(0)->IsUndefined());
1454 obj->SetInternalField(0, v8_num(17));
1455 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1456}
1457
1458
Steve Block6ded16b2010-05-10 14:33:55 +01001459THREADED_TEST(GlobalObjectInternalFields) {
1460 v8::HandleScope scope;
1461 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1462 global_template->SetInternalFieldCount(1);
1463 LocalContext env(NULL, global_template);
1464 v8::Handle<v8::Object> global_proxy = env->Global();
1465 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1466 CHECK_EQ(1, global->InternalFieldCount());
1467 CHECK(global->GetInternalField(0)->IsUndefined());
1468 global->SetInternalField(0, v8_num(17));
1469 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1470}
1471
1472
Steve Blocka7e24c12009-10-30 11:49:00 +00001473THREADED_TEST(InternalFieldsNativePointers) {
1474 v8::HandleScope scope;
1475 LocalContext env;
1476
1477 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1478 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1479 instance_templ->SetInternalFieldCount(1);
1480 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1481 CHECK_EQ(1, obj->InternalFieldCount());
1482 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1483
1484 char* data = new char[100];
1485
1486 void* aligned = data;
1487 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1488 void* unaligned = data + 1;
1489 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1490
1491 // Check reading and writing aligned pointers.
1492 obj->SetPointerInInternalField(0, aligned);
1493 i::Heap::CollectAllGarbage(false);
1494 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1495
1496 // Check reading and writing unaligned pointers.
1497 obj->SetPointerInInternalField(0, unaligned);
1498 i::Heap::CollectAllGarbage(false);
1499 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1500
1501 delete[] data;
1502}
1503
1504
Steve Block3ce2e202009-11-05 08:53:23 +00001505THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1506 v8::HandleScope scope;
1507 LocalContext env;
1508
1509 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1510 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1511 instance_templ->SetInternalFieldCount(1);
1512 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1513 CHECK_EQ(1, obj->InternalFieldCount());
1514 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1515
1516 char* data = new char[100];
1517
1518 void* aligned = data;
1519 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1520 void* unaligned = data + 1;
1521 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1522
1523 obj->SetPointerInInternalField(0, aligned);
1524 i::Heap::CollectAllGarbage(false);
1525 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1526
1527 obj->SetPointerInInternalField(0, unaligned);
1528 i::Heap::CollectAllGarbage(false);
1529 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1530
1531 obj->SetInternalField(0, v8::External::Wrap(aligned));
1532 i::Heap::CollectAllGarbage(false);
1533 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1534
1535 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1536 i::Heap::CollectAllGarbage(false);
1537 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1538
1539 delete[] data;
1540}
1541
1542
Steve Blocka7e24c12009-10-30 11:49:00 +00001543THREADED_TEST(IdentityHash) {
1544 v8::HandleScope scope;
1545 LocalContext env;
1546
1547 // Ensure that the test starts with an fresh heap to test whether the hash
1548 // code is based on the address.
1549 i::Heap::CollectAllGarbage(false);
1550 Local<v8::Object> obj = v8::Object::New();
1551 int hash = obj->GetIdentityHash();
1552 int hash1 = obj->GetIdentityHash();
1553 CHECK_EQ(hash, hash1);
1554 int hash2 = v8::Object::New()->GetIdentityHash();
1555 // Since the identity hash is essentially a random number two consecutive
1556 // objects should not be assigned the same hash code. If the test below fails
1557 // the random number generator should be evaluated.
1558 CHECK_NE(hash, hash2);
1559 i::Heap::CollectAllGarbage(false);
1560 int hash3 = v8::Object::New()->GetIdentityHash();
1561 // Make sure that the identity hash is not based on the initial address of
1562 // the object alone. If the test below fails the random number generator
1563 // should be evaluated.
1564 CHECK_NE(hash, hash3);
1565 int hash4 = obj->GetIdentityHash();
1566 CHECK_EQ(hash, hash4);
1567}
1568
1569
1570THREADED_TEST(HiddenProperties) {
1571 v8::HandleScope scope;
1572 LocalContext env;
1573
1574 v8::Local<v8::Object> obj = v8::Object::New();
1575 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1576 v8::Local<v8::String> empty = v8_str("");
1577 v8::Local<v8::String> prop_name = v8_str("prop_name");
1578
1579 i::Heap::CollectAllGarbage(false);
1580
1581 // Make sure delete of a non-existent hidden value works
1582 CHECK(obj->DeleteHiddenValue(key));
1583
1584 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1585 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1586 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1587 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1588
1589 i::Heap::CollectAllGarbage(false);
1590
1591 // Make sure we do not find the hidden property.
1592 CHECK(!obj->Has(empty));
1593 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1594 CHECK(obj->Get(empty)->IsUndefined());
1595 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1596 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1597 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1598 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1599
1600 i::Heap::CollectAllGarbage(false);
1601
1602 // Add another property and delete it afterwards to force the object in
1603 // slow case.
1604 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1605 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1606 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1607 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1608 CHECK(obj->Delete(prop_name));
1609 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1610
1611 i::Heap::CollectAllGarbage(false);
1612
1613 CHECK(obj->DeleteHiddenValue(key));
1614 CHECK(obj->GetHiddenValue(key).IsEmpty());
1615}
1616
1617
Steve Blockd0582a62009-12-15 09:54:21 +00001618static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001619static v8::Handle<Value> InterceptorForHiddenProperties(
1620 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001621 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001622 return v8::Handle<Value>();
1623}
1624
1625
1626THREADED_TEST(HiddenPropertiesWithInterceptors) {
1627 v8::HandleScope scope;
1628 LocalContext context;
1629
Steve Blockd0582a62009-12-15 09:54:21 +00001630 interceptor_for_hidden_properties_called = false;
1631
Steve Blocka7e24c12009-10-30 11:49:00 +00001632 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1633
1634 // Associate an interceptor with an object and start setting hidden values.
1635 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1636 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1637 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1638 Local<v8::Function> function = fun_templ->GetFunction();
1639 Local<v8::Object> obj = function->NewInstance();
1640 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1641 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001642 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001643}
1644
1645
1646THREADED_TEST(External) {
1647 v8::HandleScope scope;
1648 int x = 3;
1649 Local<v8::External> ext = v8::External::New(&x);
1650 LocalContext env;
1651 env->Global()->Set(v8_str("ext"), ext);
1652 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001653 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001654 int* ptr = static_cast<int*>(reext->Value());
1655 CHECK_EQ(x, 3);
1656 *ptr = 10;
1657 CHECK_EQ(x, 10);
1658
1659 // Make sure unaligned pointers are wrapped properly.
1660 char* data = i::StrDup("0123456789");
1661 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1662 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1663 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1664 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1665
1666 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1667 CHECK_EQ('0', *char_ptr);
1668 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1669 CHECK_EQ('1', *char_ptr);
1670 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1671 CHECK_EQ('2', *char_ptr);
1672 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1673 CHECK_EQ('3', *char_ptr);
1674 i::DeleteArray(data);
1675}
1676
1677
1678THREADED_TEST(GlobalHandle) {
1679 v8::Persistent<String> global;
1680 {
1681 v8::HandleScope scope;
1682 Local<String> str = v8_str("str");
1683 global = v8::Persistent<String>::New(str);
1684 }
1685 CHECK_EQ(global->Length(), 3);
1686 global.Dispose();
1687}
1688
1689
1690THREADED_TEST(ScriptException) {
1691 v8::HandleScope scope;
1692 LocalContext env;
1693 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1694 v8::TryCatch try_catch;
1695 Local<Value> result = script->Run();
1696 CHECK(result.IsEmpty());
1697 CHECK(try_catch.HasCaught());
1698 String::AsciiValue exception_value(try_catch.Exception());
1699 CHECK_EQ(*exception_value, "panama!");
1700}
1701
1702
1703bool message_received;
1704
1705
1706static void check_message(v8::Handle<v8::Message> message,
1707 v8::Handle<Value> data) {
1708 CHECK_EQ(5.76, data->NumberValue());
1709 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1710 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1711 message_received = true;
1712}
1713
1714
1715THREADED_TEST(MessageHandlerData) {
1716 message_received = false;
1717 v8::HandleScope scope;
1718 CHECK(!message_received);
1719 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1720 LocalContext context;
1721 v8::ScriptOrigin origin =
1722 v8::ScriptOrigin(v8_str("6.75"));
1723 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1724 &origin);
1725 script->SetData(v8_str("7.56"));
1726 script->Run();
1727 CHECK(message_received);
1728 // clear out the message listener
1729 v8::V8::RemoveMessageListeners(check_message);
1730}
1731
1732
1733THREADED_TEST(GetSetProperty) {
1734 v8::HandleScope scope;
1735 LocalContext context;
1736 context->Global()->Set(v8_str("foo"), v8_num(14));
1737 context->Global()->Set(v8_str("12"), v8_num(92));
1738 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1739 context->Global()->Set(v8_num(13), v8_num(56));
1740 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1741 CHECK_EQ(14, foo->Int32Value());
1742 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1743 CHECK_EQ(92, twelve->Int32Value());
1744 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1745 CHECK_EQ(32, sixteen->Int32Value());
1746 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1747 CHECK_EQ(56, thirteen->Int32Value());
1748 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1749 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1750 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1751 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1752 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1753 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1754 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1755 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1756 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1757}
1758
1759
1760THREADED_TEST(PropertyAttributes) {
1761 v8::HandleScope scope;
1762 LocalContext context;
1763 // read-only
1764 Local<String> prop = v8_str("read_only");
1765 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1766 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1767 Script::Compile(v8_str("read_only = 9"))->Run();
1768 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1769 context->Global()->Set(prop, v8_num(10));
1770 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1771 // dont-delete
1772 prop = v8_str("dont_delete");
1773 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1774 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1775 Script::Compile(v8_str("delete dont_delete"))->Run();
1776 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1777}
1778
1779
1780THREADED_TEST(Array) {
1781 v8::HandleScope scope;
1782 LocalContext context;
1783 Local<v8::Array> array = v8::Array::New();
1784 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001785 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001786 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001787 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001788 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001789 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001790 CHECK_EQ(3, array->Length());
1791 CHECK(!array->Has(0));
1792 CHECK(!array->Has(1));
1793 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001794 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001795 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001796 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001797 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001798 CHECK_EQ(1, arr->Get(0)->Int32Value());
1799 CHECK_EQ(2, arr->Get(1)->Int32Value());
1800 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001801}
1802
1803
1804v8::Handle<Value> HandleF(const v8::Arguments& args) {
1805 v8::HandleScope scope;
1806 ApiTestFuzzer::Fuzz();
1807 Local<v8::Array> result = v8::Array::New(args.Length());
1808 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001809 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001810 return scope.Close(result);
1811}
1812
1813
1814THREADED_TEST(Vector) {
1815 v8::HandleScope scope;
1816 Local<ObjectTemplate> global = ObjectTemplate::New();
1817 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1818 LocalContext context(0, global);
1819
1820 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001821 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001822 CHECK_EQ(0, a0->Length());
1823
1824 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001825 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001826 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001827 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001828
1829 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001830 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001831 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001832 CHECK_EQ(12, a2->Get(0)->Int32Value());
1833 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001834
1835 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001836 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001837 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001838 CHECK_EQ(14, a3->Get(0)->Int32Value());
1839 CHECK_EQ(15, a3->Get(1)->Int32Value());
1840 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001841
1842 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001843 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001844 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001845 CHECK_EQ(17, a4->Get(0)->Int32Value());
1846 CHECK_EQ(18, a4->Get(1)->Int32Value());
1847 CHECK_EQ(19, a4->Get(2)->Int32Value());
1848 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001849}
1850
1851
1852THREADED_TEST(FunctionCall) {
1853 v8::HandleScope scope;
1854 LocalContext context;
1855 CompileRun(
1856 "function Foo() {"
1857 " var result = [];"
1858 " for (var i = 0; i < arguments.length; i++) {"
1859 " result.push(arguments[i]);"
1860 " }"
1861 " return result;"
1862 "}");
1863 Local<Function> Foo =
1864 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1865
1866 v8::Handle<Value>* args0 = NULL;
1867 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1868 CHECK_EQ(0, a0->Length());
1869
1870 v8::Handle<Value> args1[] = { v8_num(1.1) };
1871 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1872 CHECK_EQ(1, a1->Length());
1873 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1874
1875 v8::Handle<Value> args2[] = { v8_num(2.2),
1876 v8_num(3.3) };
1877 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1878 CHECK_EQ(2, a2->Length());
1879 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1880 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1881
1882 v8::Handle<Value> args3[] = { v8_num(4.4),
1883 v8_num(5.5),
1884 v8_num(6.6) };
1885 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1886 CHECK_EQ(3, a3->Length());
1887 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1888 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1889 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1890
1891 v8::Handle<Value> args4[] = { v8_num(7.7),
1892 v8_num(8.8),
1893 v8_num(9.9),
1894 v8_num(10.11) };
1895 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1896 CHECK_EQ(4, a4->Length());
1897 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1898 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1899 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1900 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1901}
1902
1903
1904static const char* js_code_causing_out_of_memory =
1905 "var a = new Array(); while(true) a.push(a);";
1906
1907
1908// These tests run for a long time and prevent us from running tests
1909// that come after them so they cannot run in parallel.
1910TEST(OutOfMemory) {
1911 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001912 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001913 // Set heap limits.
1914 static const int K = 1024;
1915 v8::ResourceConstraints constraints;
1916 constraints.set_max_young_space_size(256 * K);
1917 constraints.set_max_old_space_size(4 * K * K);
1918 v8::SetResourceConstraints(&constraints);
1919
1920 // Execute a script that causes out of memory.
1921 v8::HandleScope scope;
1922 LocalContext context;
1923 v8::V8::IgnoreOutOfMemoryException();
1924 Local<Script> script =
1925 Script::Compile(String::New(js_code_causing_out_of_memory));
1926 Local<Value> result = script->Run();
1927
1928 // Check for out of memory state.
1929 CHECK(result.IsEmpty());
1930 CHECK(context->HasOutOfMemoryException());
1931}
1932
1933
1934v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1935 ApiTestFuzzer::Fuzz();
1936
1937 v8::HandleScope scope;
1938 LocalContext context;
1939 Local<Script> script =
1940 Script::Compile(String::New(js_code_causing_out_of_memory));
1941 Local<Value> result = script->Run();
1942
1943 // Check for out of memory state.
1944 CHECK(result.IsEmpty());
1945 CHECK(context->HasOutOfMemoryException());
1946
1947 return result;
1948}
1949
1950
1951TEST(OutOfMemoryNested) {
1952 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001953 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001954 // Set heap limits.
1955 static const int K = 1024;
1956 v8::ResourceConstraints constraints;
1957 constraints.set_max_young_space_size(256 * K);
1958 constraints.set_max_old_space_size(4 * K * K);
1959 v8::SetResourceConstraints(&constraints);
1960
1961 v8::HandleScope scope;
1962 Local<ObjectTemplate> templ = ObjectTemplate::New();
1963 templ->Set(v8_str("ProvokeOutOfMemory"),
1964 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1965 LocalContext context(0, templ);
1966 v8::V8::IgnoreOutOfMemoryException();
1967 Local<Value> result = CompileRun(
1968 "var thrown = false;"
1969 "try {"
1970 " ProvokeOutOfMemory();"
1971 "} catch (e) {"
1972 " thrown = true;"
1973 "}");
1974 // Check for out of memory state.
1975 CHECK(result.IsEmpty());
1976 CHECK(context->HasOutOfMemoryException());
1977}
1978
1979
1980TEST(HugeConsStringOutOfMemory) {
1981 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001982 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001983 v8::HandleScope scope;
1984 LocalContext context;
1985 // Set heap limits.
1986 static const int K = 1024;
1987 v8::ResourceConstraints constraints;
1988 constraints.set_max_young_space_size(256 * K);
1989 constraints.set_max_old_space_size(2 * K * K);
1990 v8::SetResourceConstraints(&constraints);
1991
1992 // Execute a script that causes out of memory.
1993 v8::V8::IgnoreOutOfMemoryException();
1994
1995 // Build huge string. This should fail with out of memory exception.
1996 Local<Value> result = CompileRun(
1997 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00001998 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00001999
2000 // Check for out of memory state.
2001 CHECK(result.IsEmpty());
2002 CHECK(context->HasOutOfMemoryException());
2003}
2004
2005
2006THREADED_TEST(ConstructCall) {
2007 v8::HandleScope scope;
2008 LocalContext context;
2009 CompileRun(
2010 "function Foo() {"
2011 " var result = [];"
2012 " for (var i = 0; i < arguments.length; i++) {"
2013 " result.push(arguments[i]);"
2014 " }"
2015 " return result;"
2016 "}");
2017 Local<Function> Foo =
2018 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2019
2020 v8::Handle<Value>* args0 = NULL;
2021 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2022 CHECK_EQ(0, a0->Length());
2023
2024 v8::Handle<Value> args1[] = { v8_num(1.1) };
2025 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2026 CHECK_EQ(1, a1->Length());
2027 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2028
2029 v8::Handle<Value> args2[] = { v8_num(2.2),
2030 v8_num(3.3) };
2031 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2032 CHECK_EQ(2, a2->Length());
2033 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2034 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2035
2036 v8::Handle<Value> args3[] = { v8_num(4.4),
2037 v8_num(5.5),
2038 v8_num(6.6) };
2039 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2040 CHECK_EQ(3, a3->Length());
2041 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2042 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2043 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2044
2045 v8::Handle<Value> args4[] = { v8_num(7.7),
2046 v8_num(8.8),
2047 v8_num(9.9),
2048 v8_num(10.11) };
2049 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2050 CHECK_EQ(4, a4->Length());
2051 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2052 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2053 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2054 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2055}
2056
2057
2058static void CheckUncle(v8::TryCatch* try_catch) {
2059 CHECK(try_catch->HasCaught());
2060 String::AsciiValue str_value(try_catch->Exception());
2061 CHECK_EQ(*str_value, "uncle?");
2062 try_catch->Reset();
2063}
2064
2065
Steve Block6ded16b2010-05-10 14:33:55 +01002066THREADED_TEST(ConversionNumber) {
2067 v8::HandleScope scope;
2068 LocalContext env;
2069 // Very large number.
2070 CompileRun("var obj = Math.pow(2,32) * 1237;");
2071 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2072 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2073 CHECK_EQ(0, obj->ToInt32()->Value());
2074 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2075 // Large number.
2076 CompileRun("var obj = -1234567890123;");
2077 obj = env->Global()->Get(v8_str("obj"));
2078 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2079 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2080 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2081 // Small positive integer.
2082 CompileRun("var obj = 42;");
2083 obj = env->Global()->Get(v8_str("obj"));
2084 CHECK_EQ(42.0, obj->ToNumber()->Value());
2085 CHECK_EQ(42, obj->ToInt32()->Value());
2086 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2087 // Negative integer.
2088 CompileRun("var obj = -37;");
2089 obj = env->Global()->Get(v8_str("obj"));
2090 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2091 CHECK_EQ(-37, obj->ToInt32()->Value());
2092 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2093 // Positive non-int32 integer.
2094 CompileRun("var obj = 0x81234567;");
2095 obj = env->Global()->Get(v8_str("obj"));
2096 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2097 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2098 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2099 // Fraction.
2100 CompileRun("var obj = 42.3;");
2101 obj = env->Global()->Get(v8_str("obj"));
2102 CHECK_EQ(42.3, obj->ToNumber()->Value());
2103 CHECK_EQ(42, obj->ToInt32()->Value());
2104 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2105 // Large negative fraction.
2106 CompileRun("var obj = -5726623061.75;");
2107 obj = env->Global()->Get(v8_str("obj"));
2108 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2109 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2110 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2111}
2112
2113
2114THREADED_TEST(isNumberType) {
2115 v8::HandleScope scope;
2116 LocalContext env;
2117 // Very large number.
2118 CompileRun("var obj = Math.pow(2,32) * 1237;");
2119 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2120 CHECK(!obj->IsInt32());
2121 CHECK(!obj->IsUint32());
2122 // Large negative number.
2123 CompileRun("var obj = -1234567890123;");
2124 obj = env->Global()->Get(v8_str("obj"));
2125 CHECK(!obj->IsInt32());
2126 CHECK(!obj->IsUint32());
2127 // Small positive integer.
2128 CompileRun("var obj = 42;");
2129 obj = env->Global()->Get(v8_str("obj"));
2130 CHECK(obj->IsInt32());
2131 CHECK(obj->IsUint32());
2132 // Negative integer.
2133 CompileRun("var obj = -37;");
2134 obj = env->Global()->Get(v8_str("obj"));
2135 CHECK(obj->IsInt32());
2136 CHECK(!obj->IsUint32());
2137 // Positive non-int32 integer.
2138 CompileRun("var obj = 0x81234567;");
2139 obj = env->Global()->Get(v8_str("obj"));
2140 CHECK(!obj->IsInt32());
2141 CHECK(obj->IsUint32());
2142 // Fraction.
2143 CompileRun("var obj = 42.3;");
2144 obj = env->Global()->Get(v8_str("obj"));
2145 CHECK(!obj->IsInt32());
2146 CHECK(!obj->IsUint32());
2147 // Large negative fraction.
2148 CompileRun("var obj = -5726623061.75;");
2149 obj = env->Global()->Get(v8_str("obj"));
2150 CHECK(!obj->IsInt32());
2151 CHECK(!obj->IsUint32());
2152}
2153
2154
Steve Blocka7e24c12009-10-30 11:49:00 +00002155THREADED_TEST(ConversionException) {
2156 v8::HandleScope scope;
2157 LocalContext env;
2158 CompileRun(
2159 "function TestClass() { };"
2160 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2161 "var obj = new TestClass();");
2162 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2163
2164 v8::TryCatch try_catch;
2165
2166 Local<Value> to_string_result = obj->ToString();
2167 CHECK(to_string_result.IsEmpty());
2168 CheckUncle(&try_catch);
2169
2170 Local<Value> to_number_result = obj->ToNumber();
2171 CHECK(to_number_result.IsEmpty());
2172 CheckUncle(&try_catch);
2173
2174 Local<Value> to_integer_result = obj->ToInteger();
2175 CHECK(to_integer_result.IsEmpty());
2176 CheckUncle(&try_catch);
2177
2178 Local<Value> to_uint32_result = obj->ToUint32();
2179 CHECK(to_uint32_result.IsEmpty());
2180 CheckUncle(&try_catch);
2181
2182 Local<Value> to_int32_result = obj->ToInt32();
2183 CHECK(to_int32_result.IsEmpty());
2184 CheckUncle(&try_catch);
2185
2186 Local<Value> to_object_result = v8::Undefined()->ToObject();
2187 CHECK(to_object_result.IsEmpty());
2188 CHECK(try_catch.HasCaught());
2189 try_catch.Reset();
2190
2191 int32_t int32_value = obj->Int32Value();
2192 CHECK_EQ(0, int32_value);
2193 CheckUncle(&try_catch);
2194
2195 uint32_t uint32_value = obj->Uint32Value();
2196 CHECK_EQ(0, uint32_value);
2197 CheckUncle(&try_catch);
2198
2199 double number_value = obj->NumberValue();
2200 CHECK_NE(0, IsNaN(number_value));
2201 CheckUncle(&try_catch);
2202
2203 int64_t integer_value = obj->IntegerValue();
2204 CHECK_EQ(0.0, static_cast<double>(integer_value));
2205 CheckUncle(&try_catch);
2206}
2207
2208
2209v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2210 ApiTestFuzzer::Fuzz();
2211 return v8::ThrowException(v8_str("konto"));
2212}
2213
2214
2215v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2216 if (args.Length() < 1) return v8::Boolean::New(false);
2217 v8::HandleScope scope;
2218 v8::TryCatch try_catch;
2219 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2220 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2221 return v8::Boolean::New(try_catch.HasCaught());
2222}
2223
2224
2225THREADED_TEST(APICatch) {
2226 v8::HandleScope scope;
2227 Local<ObjectTemplate> templ = ObjectTemplate::New();
2228 templ->Set(v8_str("ThrowFromC"),
2229 v8::FunctionTemplate::New(ThrowFromC));
2230 LocalContext context(0, templ);
2231 CompileRun(
2232 "var thrown = false;"
2233 "try {"
2234 " ThrowFromC();"
2235 "} catch (e) {"
2236 " thrown = true;"
2237 "}");
2238 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2239 CHECK(thrown->BooleanValue());
2240}
2241
2242
2243THREADED_TEST(APIThrowTryCatch) {
2244 v8::HandleScope scope;
2245 Local<ObjectTemplate> templ = ObjectTemplate::New();
2246 templ->Set(v8_str("ThrowFromC"),
2247 v8::FunctionTemplate::New(ThrowFromC));
2248 LocalContext context(0, templ);
2249 v8::TryCatch try_catch;
2250 CompileRun("ThrowFromC();");
2251 CHECK(try_catch.HasCaught());
2252}
2253
2254
2255// Test that a try-finally block doesn't shadow a try-catch block
2256// when setting up an external handler.
2257//
2258// BUG(271): Some of the exception propagation does not work on the
2259// ARM simulator because the simulator separates the C++ stack and the
2260// JS stack. This test therefore fails on the simulator. The test is
2261// not threaded to allow the threading tests to run on the simulator.
2262TEST(TryCatchInTryFinally) {
2263 v8::HandleScope scope;
2264 Local<ObjectTemplate> templ = ObjectTemplate::New();
2265 templ->Set(v8_str("CCatcher"),
2266 v8::FunctionTemplate::New(CCatcher));
2267 LocalContext context(0, templ);
2268 Local<Value> result = CompileRun("try {"
2269 " try {"
2270 " CCatcher('throw 7;');"
2271 " } finally {"
2272 " }"
2273 "} catch (e) {"
2274 "}");
2275 CHECK(result->IsTrue());
2276}
2277
2278
2279static void receive_message(v8::Handle<v8::Message> message,
2280 v8::Handle<v8::Value> data) {
2281 message->Get();
2282 message_received = true;
2283}
2284
2285
2286TEST(APIThrowMessage) {
2287 message_received = false;
2288 v8::HandleScope scope;
2289 v8::V8::AddMessageListener(receive_message);
2290 Local<ObjectTemplate> templ = ObjectTemplate::New();
2291 templ->Set(v8_str("ThrowFromC"),
2292 v8::FunctionTemplate::New(ThrowFromC));
2293 LocalContext context(0, templ);
2294 CompileRun("ThrowFromC();");
2295 CHECK(message_received);
2296 v8::V8::RemoveMessageListeners(check_message);
2297}
2298
2299
2300TEST(APIThrowMessageAndVerboseTryCatch) {
2301 message_received = false;
2302 v8::HandleScope scope;
2303 v8::V8::AddMessageListener(receive_message);
2304 Local<ObjectTemplate> templ = ObjectTemplate::New();
2305 templ->Set(v8_str("ThrowFromC"),
2306 v8::FunctionTemplate::New(ThrowFromC));
2307 LocalContext context(0, templ);
2308 v8::TryCatch try_catch;
2309 try_catch.SetVerbose(true);
2310 Local<Value> result = CompileRun("ThrowFromC();");
2311 CHECK(try_catch.HasCaught());
2312 CHECK(result.IsEmpty());
2313 CHECK(message_received);
2314 v8::V8::RemoveMessageListeners(check_message);
2315}
2316
2317
2318THREADED_TEST(ExternalScriptException) {
2319 v8::HandleScope scope;
2320 Local<ObjectTemplate> templ = ObjectTemplate::New();
2321 templ->Set(v8_str("ThrowFromC"),
2322 v8::FunctionTemplate::New(ThrowFromC));
2323 LocalContext context(0, templ);
2324
2325 v8::TryCatch try_catch;
2326 Local<Script> script
2327 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2328 Local<Value> result = script->Run();
2329 CHECK(result.IsEmpty());
2330 CHECK(try_catch.HasCaught());
2331 String::AsciiValue exception_value(try_catch.Exception());
2332 CHECK_EQ("konto", *exception_value);
2333}
2334
2335
2336
2337v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2338 ApiTestFuzzer::Fuzz();
2339 CHECK_EQ(4, args.Length());
2340 int count = args[0]->Int32Value();
2341 int cInterval = args[2]->Int32Value();
2342 if (count == 0) {
2343 return v8::ThrowException(v8_str("FromC"));
2344 } else {
2345 Local<v8::Object> global = Context::GetCurrent()->Global();
2346 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2347 v8::Handle<Value> argv[] = { v8_num(count - 1),
2348 args[1],
2349 args[2],
2350 args[3] };
2351 if (count % cInterval == 0) {
2352 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002353 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002354 int expected = args[3]->Int32Value();
2355 if (try_catch.HasCaught()) {
2356 CHECK_EQ(expected, count);
2357 CHECK(result.IsEmpty());
2358 CHECK(!i::Top::has_scheduled_exception());
2359 } else {
2360 CHECK_NE(expected, count);
2361 }
2362 return result;
2363 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002364 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002365 }
2366 }
2367}
2368
2369
2370v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2371 ApiTestFuzzer::Fuzz();
2372 CHECK_EQ(3, args.Length());
2373 bool equality = args[0]->BooleanValue();
2374 int count = args[1]->Int32Value();
2375 int expected = args[2]->Int32Value();
2376 if (equality) {
2377 CHECK_EQ(count, expected);
2378 } else {
2379 CHECK_NE(count, expected);
2380 }
2381 return v8::Undefined();
2382}
2383
2384
2385THREADED_TEST(EvalInTryFinally) {
2386 v8::HandleScope scope;
2387 LocalContext context;
2388 v8::TryCatch try_catch;
2389 CompileRun("(function() {"
2390 " try {"
2391 " eval('asldkf (*&^&*^');"
2392 " } finally {"
2393 " return;"
2394 " }"
2395 "})()");
2396 CHECK(!try_catch.HasCaught());
2397}
2398
2399
2400// This test works by making a stack of alternating JavaScript and C
2401// activations. These activations set up exception handlers with regular
2402// intervals, one interval for C activations and another for JavaScript
2403// activations. When enough activations have been created an exception is
2404// thrown and we check that the right activation catches the exception and that
2405// no other activations do. The right activation is always the topmost one with
2406// a handler, regardless of whether it is in JavaScript or C.
2407//
2408// The notation used to describe a test case looks like this:
2409//
2410// *JS[4] *C[3] @JS[2] C[1] JS[0]
2411//
2412// Each entry is an activation, either JS or C. The index is the count at that
2413// level. Stars identify activations with exception handlers, the @ identifies
2414// the exception handler that should catch the exception.
2415//
2416// BUG(271): Some of the exception propagation does not work on the
2417// ARM simulator because the simulator separates the C++ stack and the
2418// JS stack. This test therefore fails on the simulator. The test is
2419// not threaded to allow the threading tests to run on the simulator.
2420TEST(ExceptionOrder) {
2421 v8::HandleScope scope;
2422 Local<ObjectTemplate> templ = ObjectTemplate::New();
2423 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2424 templ->Set(v8_str("CThrowCountDown"),
2425 v8::FunctionTemplate::New(CThrowCountDown));
2426 LocalContext context(0, templ);
2427 CompileRun(
2428 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2429 " if (count == 0) throw 'FromJS';"
2430 " if (count % jsInterval == 0) {"
2431 " try {"
2432 " var value = CThrowCountDown(count - 1,"
2433 " jsInterval,"
2434 " cInterval,"
2435 " expected);"
2436 " check(false, count, expected);"
2437 " return value;"
2438 " } catch (e) {"
2439 " check(true, count, expected);"
2440 " }"
2441 " } else {"
2442 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2443 " }"
2444 "}");
2445 Local<Function> fun =
2446 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2447
2448 const int argc = 4;
2449 // count jsInterval cInterval expected
2450
2451 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2452 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2453 fun->Call(fun, argc, a0);
2454
2455 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2456 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2457 fun->Call(fun, argc, a1);
2458
2459 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2460 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2461 fun->Call(fun, argc, a2);
2462
2463 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2464 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2465 fun->Call(fun, argc, a3);
2466
2467 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2468 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2469 fun->Call(fun, argc, a4);
2470
2471 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2472 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2473 fun->Call(fun, argc, a5);
2474}
2475
2476
2477v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2478 ApiTestFuzzer::Fuzz();
2479 CHECK_EQ(1, args.Length());
2480 return v8::ThrowException(args[0]);
2481}
2482
2483
2484THREADED_TEST(ThrowValues) {
2485 v8::HandleScope scope;
2486 Local<ObjectTemplate> templ = ObjectTemplate::New();
2487 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2488 LocalContext context(0, templ);
2489 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2490 "function Run(obj) {"
2491 " try {"
2492 " Throw(obj);"
2493 " } catch (e) {"
2494 " return e;"
2495 " }"
2496 " return 'no exception';"
2497 "}"
2498 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2499 CHECK_EQ(5, result->Length());
2500 CHECK(result->Get(v8::Integer::New(0))->IsString());
2501 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2502 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2503 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2504 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2505 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2506 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2507}
2508
2509
2510THREADED_TEST(CatchZero) {
2511 v8::HandleScope scope;
2512 LocalContext context;
2513 v8::TryCatch try_catch;
2514 CHECK(!try_catch.HasCaught());
2515 Script::Compile(v8_str("throw 10"))->Run();
2516 CHECK(try_catch.HasCaught());
2517 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2518 try_catch.Reset();
2519 CHECK(!try_catch.HasCaught());
2520 Script::Compile(v8_str("throw 0"))->Run();
2521 CHECK(try_catch.HasCaught());
2522 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2523}
2524
2525
2526THREADED_TEST(CatchExceptionFromWith) {
2527 v8::HandleScope scope;
2528 LocalContext context;
2529 v8::TryCatch try_catch;
2530 CHECK(!try_catch.HasCaught());
2531 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2532 CHECK(try_catch.HasCaught());
2533}
2534
2535
2536THREADED_TEST(Equality) {
2537 v8::HandleScope scope;
2538 LocalContext context;
2539 // Check that equality works at all before relying on CHECK_EQ
2540 CHECK(v8_str("a")->Equals(v8_str("a")));
2541 CHECK(!v8_str("a")->Equals(v8_str("b")));
2542
2543 CHECK_EQ(v8_str("a"), v8_str("a"));
2544 CHECK_NE(v8_str("a"), v8_str("b"));
2545 CHECK_EQ(v8_num(1), v8_num(1));
2546 CHECK_EQ(v8_num(1.00), v8_num(1));
2547 CHECK_NE(v8_num(1), v8_num(2));
2548
2549 // Assume String is not symbol.
2550 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2551 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2552 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2553 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2554 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2555 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2556 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2557 CHECK(!not_a_number->StrictEquals(not_a_number));
2558 CHECK(v8::False()->StrictEquals(v8::False()));
2559 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2560
2561 v8::Handle<v8::Object> obj = v8::Object::New();
2562 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2563 CHECK(alias->StrictEquals(obj));
2564 alias.Dispose();
2565}
2566
2567
2568THREADED_TEST(MultiRun) {
2569 v8::HandleScope scope;
2570 LocalContext context;
2571 Local<Script> script = Script::Compile(v8_str("x"));
2572 for (int i = 0; i < 10; i++)
2573 script->Run();
2574}
2575
2576
2577static v8::Handle<Value> GetXValue(Local<String> name,
2578 const AccessorInfo& info) {
2579 ApiTestFuzzer::Fuzz();
2580 CHECK_EQ(info.Data(), v8_str("donut"));
2581 CHECK_EQ(name, v8_str("x"));
2582 return name;
2583}
2584
2585
2586THREADED_TEST(SimplePropertyRead) {
2587 v8::HandleScope scope;
2588 Local<ObjectTemplate> templ = ObjectTemplate::New();
2589 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2590 LocalContext context;
2591 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2592 Local<Script> script = Script::Compile(v8_str("obj.x"));
2593 for (int i = 0; i < 10; i++) {
2594 Local<Value> result = script->Run();
2595 CHECK_EQ(result, v8_str("x"));
2596 }
2597}
2598
Andrei Popescu31002712010-02-23 13:46:05 +00002599THREADED_TEST(DefinePropertyOnAPIAccessor) {
2600 v8::HandleScope scope;
2601 Local<ObjectTemplate> templ = ObjectTemplate::New();
2602 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2603 LocalContext context;
2604 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2605
2606 // Uses getOwnPropertyDescriptor to check the configurable status
2607 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002608 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002609 "obj, 'x');"
2610 "prop.configurable;"));
2611 Local<Value> result = script_desc->Run();
2612 CHECK_EQ(result->BooleanValue(), true);
2613
2614 // Redefine get - but still configurable
2615 Local<Script> script_define
2616 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2617 " configurable: true };"
2618 "Object.defineProperty(obj, 'x', desc);"
2619 "obj.x"));
2620 result = script_define->Run();
2621 CHECK_EQ(result, v8_num(42));
2622
2623 // Check that the accessor is still configurable
2624 result = script_desc->Run();
2625 CHECK_EQ(result->BooleanValue(), true);
2626
2627 // Redefine to a non-configurable
2628 script_define
2629 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2630 " configurable: false };"
2631 "Object.defineProperty(obj, 'x', desc);"
2632 "obj.x"));
2633 result = script_define->Run();
2634 CHECK_EQ(result, v8_num(43));
2635 result = script_desc->Run();
2636 CHECK_EQ(result->BooleanValue(), false);
2637
2638 // Make sure that it is not possible to redefine again
2639 v8::TryCatch try_catch;
2640 result = script_define->Run();
2641 CHECK(try_catch.HasCaught());
2642 String::AsciiValue exception_value(try_catch.Exception());
2643 CHECK_EQ(*exception_value,
2644 "TypeError: Cannot redefine property: defineProperty");
2645}
2646
2647THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2648 v8::HandleScope scope;
2649 Local<ObjectTemplate> templ = ObjectTemplate::New();
2650 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2651 LocalContext context;
2652 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2653
2654 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2655 "Object.getOwnPropertyDescriptor( "
2656 "obj, 'x');"
2657 "prop.configurable;"));
2658 Local<Value> result = script_desc->Run();
2659 CHECK_EQ(result->BooleanValue(), true);
2660
2661 Local<Script> script_define =
2662 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2663 " configurable: true };"
2664 "Object.defineProperty(obj, 'x', desc);"
2665 "obj.x"));
2666 result = script_define->Run();
2667 CHECK_EQ(result, v8_num(42));
2668
2669
2670 result = script_desc->Run();
2671 CHECK_EQ(result->BooleanValue(), true);
2672
2673
2674 script_define =
2675 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2676 " configurable: false };"
2677 "Object.defineProperty(obj, 'x', desc);"
2678 "obj.x"));
2679 result = script_define->Run();
2680 CHECK_EQ(result, v8_num(43));
2681 result = script_desc->Run();
2682
2683 CHECK_EQ(result->BooleanValue(), false);
2684
2685 v8::TryCatch try_catch;
2686 result = script_define->Run();
2687 CHECK(try_catch.HasCaught());
2688 String::AsciiValue exception_value(try_catch.Exception());
2689 CHECK_EQ(*exception_value,
2690 "TypeError: Cannot redefine property: defineProperty");
2691}
2692
2693
Leon Clarkef7060e22010-06-03 12:02:55 +01002694static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2695 char const* name) {
2696 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2697}
Andrei Popescu31002712010-02-23 13:46:05 +00002698
2699
Leon Clarkef7060e22010-06-03 12:02:55 +01002700THREADED_TEST(DefineAPIAccessorOnObject) {
2701 v8::HandleScope scope;
2702 Local<ObjectTemplate> templ = ObjectTemplate::New();
2703 LocalContext context;
2704
2705 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2706 CompileRun("var obj2 = {};");
2707
2708 CHECK(CompileRun("obj1.x")->IsUndefined());
2709 CHECK(CompileRun("obj2.x")->IsUndefined());
2710
2711 CHECK(GetGlobalProperty(&context, "obj1")->
2712 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2713
2714 ExpectString("obj1.x", "x");
2715 CHECK(CompileRun("obj2.x")->IsUndefined());
2716
2717 CHECK(GetGlobalProperty(&context, "obj2")->
2718 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2719
2720 ExpectString("obj1.x", "x");
2721 ExpectString("obj2.x", "x");
2722
2723 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2724 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2725
2726 CompileRun("Object.defineProperty(obj1, 'x',"
2727 "{ get: function() { return 'y'; }, configurable: true })");
2728
2729 ExpectString("obj1.x", "y");
2730 ExpectString("obj2.x", "x");
2731
2732 CompileRun("Object.defineProperty(obj2, 'x',"
2733 "{ get: function() { return 'y'; }, configurable: true })");
2734
2735 ExpectString("obj1.x", "y");
2736 ExpectString("obj2.x", "y");
2737
2738 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2739 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2740
2741 CHECK(GetGlobalProperty(&context, "obj1")->
2742 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2743 CHECK(GetGlobalProperty(&context, "obj2")->
2744 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2745
2746 ExpectString("obj1.x", "x");
2747 ExpectString("obj2.x", "x");
2748
2749 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2750 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2751
2752 // Define getters/setters, but now make them not configurable.
2753 CompileRun("Object.defineProperty(obj1, 'x',"
2754 "{ get: function() { return 'z'; }, configurable: false })");
2755 CompileRun("Object.defineProperty(obj2, 'x',"
2756 "{ get: function() { return 'z'; }, configurable: false })");
2757
2758 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2759 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2760
2761 ExpectString("obj1.x", "z");
2762 ExpectString("obj2.x", "z");
2763
2764 CHECK(!GetGlobalProperty(&context, "obj1")->
2765 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2766 CHECK(!GetGlobalProperty(&context, "obj2")->
2767 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2768
2769 ExpectString("obj1.x", "z");
2770 ExpectString("obj2.x", "z");
2771}
2772
2773
2774THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2775 v8::HandleScope scope;
2776 Local<ObjectTemplate> templ = ObjectTemplate::New();
2777 LocalContext context;
2778
2779 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2780 CompileRun("var obj2 = {};");
2781
2782 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2783 v8_str("x"),
2784 GetXValue, NULL,
2785 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2786 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2787 v8_str("x"),
2788 GetXValue, NULL,
2789 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2790
2791 ExpectString("obj1.x", "x");
2792 ExpectString("obj2.x", "x");
2793
2794 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2795 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2796
2797 CHECK(!GetGlobalProperty(&context, "obj1")->
2798 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2799 CHECK(!GetGlobalProperty(&context, "obj2")->
2800 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2801
2802 {
2803 v8::TryCatch try_catch;
2804 CompileRun("Object.defineProperty(obj1, 'x',"
2805 "{get: function() { return 'func'; }})");
2806 CHECK(try_catch.HasCaught());
2807 String::AsciiValue exception_value(try_catch.Exception());
2808 CHECK_EQ(*exception_value,
2809 "TypeError: Cannot redefine property: defineProperty");
2810 }
2811 {
2812 v8::TryCatch try_catch;
2813 CompileRun("Object.defineProperty(obj2, 'x',"
2814 "{get: function() { return 'func'; }})");
2815 CHECK(try_catch.HasCaught());
2816 String::AsciiValue exception_value(try_catch.Exception());
2817 CHECK_EQ(*exception_value,
2818 "TypeError: Cannot redefine property: defineProperty");
2819 }
2820}
2821
2822
2823static v8::Handle<Value> Get239Value(Local<String> name,
2824 const AccessorInfo& info) {
2825 ApiTestFuzzer::Fuzz();
2826 CHECK_EQ(info.Data(), v8_str("donut"));
2827 CHECK_EQ(name, v8_str("239"));
2828 return name;
2829}
2830
2831
2832THREADED_TEST(ElementAPIAccessor) {
2833 v8::HandleScope scope;
2834 Local<ObjectTemplate> templ = ObjectTemplate::New();
2835 LocalContext context;
2836
2837 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2838 CompileRun("var obj2 = {};");
2839
2840 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2841 v8_str("239"),
2842 Get239Value, NULL,
2843 v8_str("donut")));
2844 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2845 v8_str("239"),
2846 Get239Value, NULL,
2847 v8_str("donut")));
2848
2849 ExpectString("obj1[239]", "239");
2850 ExpectString("obj2[239]", "239");
2851 ExpectString("obj1['239']", "239");
2852 ExpectString("obj2['239']", "239");
2853}
2854
Steve Blocka7e24c12009-10-30 11:49:00 +00002855
2856v8::Persistent<Value> xValue;
2857
2858
2859static void SetXValue(Local<String> name,
2860 Local<Value> value,
2861 const AccessorInfo& info) {
2862 CHECK_EQ(value, v8_num(4));
2863 CHECK_EQ(info.Data(), v8_str("donut"));
2864 CHECK_EQ(name, v8_str("x"));
2865 CHECK(xValue.IsEmpty());
2866 xValue = v8::Persistent<Value>::New(value);
2867}
2868
2869
2870THREADED_TEST(SimplePropertyWrite) {
2871 v8::HandleScope scope;
2872 Local<ObjectTemplate> templ = ObjectTemplate::New();
2873 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2874 LocalContext context;
2875 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2876 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2877 for (int i = 0; i < 10; i++) {
2878 CHECK(xValue.IsEmpty());
2879 script->Run();
2880 CHECK_EQ(v8_num(4), xValue);
2881 xValue.Dispose();
2882 xValue = v8::Persistent<Value>();
2883 }
2884}
2885
2886
2887static v8::Handle<Value> XPropertyGetter(Local<String> property,
2888 const AccessorInfo& info) {
2889 ApiTestFuzzer::Fuzz();
2890 CHECK(info.Data()->IsUndefined());
2891 return property;
2892}
2893
2894
2895THREADED_TEST(NamedInterceptorPropertyRead) {
2896 v8::HandleScope scope;
2897 Local<ObjectTemplate> templ = ObjectTemplate::New();
2898 templ->SetNamedPropertyHandler(XPropertyGetter);
2899 LocalContext context;
2900 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2901 Local<Script> script = Script::Compile(v8_str("obj.x"));
2902 for (int i = 0; i < 10; i++) {
2903 Local<Value> result = script->Run();
2904 CHECK_EQ(result, v8_str("x"));
2905 }
2906}
2907
2908
Steve Block6ded16b2010-05-10 14:33:55 +01002909THREADED_TEST(NamedInterceptorDictionaryIC) {
2910 v8::HandleScope scope;
2911 Local<ObjectTemplate> templ = ObjectTemplate::New();
2912 templ->SetNamedPropertyHandler(XPropertyGetter);
2913 LocalContext context;
2914 // Create an object with a named interceptor.
2915 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2916 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2917 for (int i = 0; i < 10; i++) {
2918 Local<Value> result = script->Run();
2919 CHECK_EQ(result, v8_str("x"));
2920 }
2921 // Create a slow case object and a function accessing a property in
2922 // that slow case object (with dictionary probing in generated
2923 // code). Then force object with a named interceptor into slow-case,
2924 // pass it to the function, and check that the interceptor is called
2925 // instead of accessing the local property.
2926 Local<Value> result =
2927 CompileRun("function get_x(o) { return o.x; };"
2928 "var obj = { x : 42, y : 0 };"
2929 "delete obj.y;"
2930 "for (var i = 0; i < 10; i++) get_x(obj);"
2931 "interceptor_obj.x = 42;"
2932 "interceptor_obj.y = 10;"
2933 "delete interceptor_obj.y;"
2934 "get_x(interceptor_obj)");
2935 CHECK_EQ(result, v8_str("x"));
2936}
2937
2938
Andrei Popescu402d9372010-02-26 13:31:12 +00002939static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2940 const AccessorInfo& info) {
2941 // Set x on the prototype object and do not handle the get request.
2942 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002943 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002944 return v8::Handle<Value>();
2945}
2946
2947
2948// This is a regression test for http://crbug.com/20104. Map
2949// transitions should not interfere with post interceptor lookup.
2950THREADED_TEST(NamedInterceptorMapTransitionRead) {
2951 v8::HandleScope scope;
2952 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
2953 Local<v8::ObjectTemplate> instance_template
2954 = function_template->InstanceTemplate();
2955 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
2956 LocalContext context;
2957 context->Global()->Set(v8_str("F"), function_template->GetFunction());
2958 // Create an instance of F and introduce a map transition for x.
2959 CompileRun("var o = new F(); o.x = 23;");
2960 // Create an instance of F and invoke the getter. The result should be 23.
2961 Local<Value> result = CompileRun("o = new F(); o.x");
2962 CHECK_EQ(result->Int32Value(), 23);
2963}
2964
2965
Steve Blocka7e24c12009-10-30 11:49:00 +00002966static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2967 const AccessorInfo& info) {
2968 ApiTestFuzzer::Fuzz();
2969 if (index == 37) {
2970 return v8::Handle<Value>(v8_num(625));
2971 }
2972 return v8::Handle<Value>();
2973}
2974
2975
2976static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2977 Local<Value> value,
2978 const AccessorInfo& info) {
2979 ApiTestFuzzer::Fuzz();
2980 if (index == 39) {
2981 return value;
2982 }
2983 return v8::Handle<Value>();
2984}
2985
2986
2987THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2988 v8::HandleScope scope;
2989 Local<ObjectTemplate> templ = ObjectTemplate::New();
2990 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2991 IndexedPropertySetter);
2992 LocalContext context;
2993 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2994 Local<Script> getter_script = Script::Compile(v8_str(
2995 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2996 Local<Script> setter_script = Script::Compile(v8_str(
2997 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2998 "obj[17] = 23;"
2999 "obj.foo;"));
3000 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
3001 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
3002 "obj[39] = 47;"
3003 "obj.foo;")); // This setter should not run, due to the interceptor.
3004 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
3005 "obj[37];"));
3006 Local<Value> result = getter_script->Run();
3007 CHECK_EQ(v8_num(5), result);
3008 result = setter_script->Run();
3009 CHECK_EQ(v8_num(23), result);
3010 result = interceptor_setter_script->Run();
3011 CHECK_EQ(v8_num(23), result);
3012 result = interceptor_getter_script->Run();
3013 CHECK_EQ(v8_num(625), result);
3014}
3015
3016
Leon Clarked91b9f72010-01-27 17:25:45 +00003017static v8::Handle<Value> IdentityIndexedPropertyGetter(
3018 uint32_t index,
3019 const AccessorInfo& info) {
3020 return v8::Integer::New(index);
3021}
3022
3023
3024THREADED_TEST(IndexedInterceptorWithNoSetter) {
3025 v8::HandleScope scope;
3026 Local<ObjectTemplate> templ = ObjectTemplate::New();
3027 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3028
3029 LocalContext context;
3030 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3031
3032 const char* code =
3033 "try {"
3034 " obj[0] = 239;"
3035 " for (var i = 0; i < 100; i++) {"
3036 " var v = obj[0];"
3037 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3038 " }"
3039 " 'PASSED'"
3040 "} catch(e) {"
3041 " e"
3042 "}";
3043 ExpectString(code, "PASSED");
3044}
3045
3046
Andrei Popescu402d9372010-02-26 13:31:12 +00003047THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3048 v8::HandleScope scope;
3049 Local<ObjectTemplate> templ = ObjectTemplate::New();
3050 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3051
3052 LocalContext context;
3053 Local<v8::Object> obj = templ->NewInstance();
3054 obj->TurnOnAccessCheck();
3055 context->Global()->Set(v8_str("obj"), obj);
3056
3057 const char* code =
3058 "try {"
3059 " for (var i = 0; i < 100; i++) {"
3060 " var v = obj[0];"
3061 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3062 " }"
3063 " 'PASSED'"
3064 "} catch(e) {"
3065 " e"
3066 "}";
3067 ExpectString(code, "PASSED");
3068}
3069
3070
3071THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3072 i::FLAG_allow_natives_syntax = true;
3073 v8::HandleScope scope;
3074 Local<ObjectTemplate> templ = ObjectTemplate::New();
3075 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3076
3077 LocalContext context;
3078 Local<v8::Object> obj = templ->NewInstance();
3079 context->Global()->Set(v8_str("obj"), obj);
3080
3081 const char* code =
3082 "try {"
3083 " for (var i = 0; i < 100; i++) {"
3084 " var expected = i;"
3085 " if (i == 5) {"
3086 " %EnableAccessChecks(obj);"
3087 " expected = undefined;"
3088 " }"
3089 " var v = obj[i];"
3090 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3091 " if (i == 5) %DisableAccessChecks(obj);"
3092 " }"
3093 " 'PASSED'"
3094 "} catch(e) {"
3095 " e"
3096 "}";
3097 ExpectString(code, "PASSED");
3098}
3099
3100
3101THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3102 v8::HandleScope scope;
3103 Local<ObjectTemplate> templ = ObjectTemplate::New();
3104 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3105
3106 LocalContext context;
3107 Local<v8::Object> obj = templ->NewInstance();
3108 context->Global()->Set(v8_str("obj"), obj);
3109
3110 const char* code =
3111 "try {"
3112 " for (var i = 0; i < 100; i++) {"
3113 " var v = obj[i];"
3114 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3115 " }"
3116 " 'PASSED'"
3117 "} catch(e) {"
3118 " e"
3119 "}";
3120 ExpectString(code, "PASSED");
3121}
3122
3123
3124THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3125 v8::HandleScope scope;
3126 Local<ObjectTemplate> templ = ObjectTemplate::New();
3127 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3128
3129 LocalContext context;
3130 Local<v8::Object> obj = templ->NewInstance();
3131 context->Global()->Set(v8_str("obj"), obj);
3132
3133 const char* code =
3134 "try {"
3135 " for (var i = 0; i < 100; i++) {"
3136 " var expected = i;"
3137 " if (i == 50) {"
3138 " i = 'foobar';"
3139 " expected = undefined;"
3140 " }"
3141 " var v = obj[i];"
3142 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3143 " }"
3144 " 'PASSED'"
3145 "} catch(e) {"
3146 " e"
3147 "}";
3148 ExpectString(code, "PASSED");
3149}
3150
3151
3152THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3153 v8::HandleScope scope;
3154 Local<ObjectTemplate> templ = ObjectTemplate::New();
3155 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3156
3157 LocalContext context;
3158 Local<v8::Object> obj = templ->NewInstance();
3159 context->Global()->Set(v8_str("obj"), obj);
3160
3161 const char* code =
3162 "var original = obj;"
3163 "try {"
3164 " for (var i = 0; i < 100; i++) {"
3165 " var expected = i;"
3166 " if (i == 50) {"
3167 " obj = {50: 'foobar'};"
3168 " expected = 'foobar';"
3169 " }"
3170 " var v = obj[i];"
3171 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3172 " if (i == 50) obj = original;"
3173 " }"
3174 " 'PASSED'"
3175 "} catch(e) {"
3176 " e"
3177 "}";
3178 ExpectString(code, "PASSED");
3179}
3180
3181
3182THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3183 v8::HandleScope scope;
3184 Local<ObjectTemplate> templ = ObjectTemplate::New();
3185 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3186
3187 LocalContext context;
3188 Local<v8::Object> obj = templ->NewInstance();
3189 context->Global()->Set(v8_str("obj"), obj);
3190
3191 const char* code =
3192 "var original = obj;"
3193 "try {"
3194 " for (var i = 0; i < 100; i++) {"
3195 " var expected = i;"
3196 " if (i == 5) {"
3197 " obj = 239;"
3198 " expected = undefined;"
3199 " }"
3200 " var v = obj[i];"
3201 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3202 " if (i == 5) obj = original;"
3203 " }"
3204 " 'PASSED'"
3205 "} catch(e) {"
3206 " e"
3207 "}";
3208 ExpectString(code, "PASSED");
3209}
3210
3211
3212THREADED_TEST(IndexedInterceptorOnProto) {
3213 v8::HandleScope scope;
3214 Local<ObjectTemplate> templ = ObjectTemplate::New();
3215 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3216
3217 LocalContext context;
3218 Local<v8::Object> obj = templ->NewInstance();
3219 context->Global()->Set(v8_str("obj"), obj);
3220
3221 const char* code =
3222 "var o = {__proto__: obj};"
3223 "try {"
3224 " for (var i = 0; i < 100; i++) {"
3225 " var v = o[i];"
3226 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3227 " }"
3228 " 'PASSED'"
3229 "} catch(e) {"
3230 " e"
3231 "}";
3232 ExpectString(code, "PASSED");
3233}
3234
3235
Steve Blocka7e24c12009-10-30 11:49:00 +00003236THREADED_TEST(MultiContexts) {
3237 v8::HandleScope scope;
3238 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3239 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3240
3241 Local<String> password = v8_str("Password");
3242
3243 // Create an environment
3244 LocalContext context0(0, templ);
3245 context0->SetSecurityToken(password);
3246 v8::Handle<v8::Object> global0 = context0->Global();
3247 global0->Set(v8_str("custom"), v8_num(1234));
3248 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3249
3250 // Create an independent environment
3251 LocalContext context1(0, templ);
3252 context1->SetSecurityToken(password);
3253 v8::Handle<v8::Object> global1 = context1->Global();
3254 global1->Set(v8_str("custom"), v8_num(1234));
3255 CHECK_NE(global0, global1);
3256 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3257 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3258
3259 // Now create a new context with the old global
3260 LocalContext context2(0, templ, global1);
3261 context2->SetSecurityToken(password);
3262 v8::Handle<v8::Object> global2 = context2->Global();
3263 CHECK_EQ(global1, global2);
3264 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3265 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3266}
3267
3268
3269THREADED_TEST(FunctionPrototypeAcrossContexts) {
3270 // Make sure that functions created by cloning boilerplates cannot
3271 // communicate through their __proto__ field.
3272
3273 v8::HandleScope scope;
3274
3275 LocalContext env0;
3276 v8::Handle<v8::Object> global0 =
3277 env0->Global();
3278 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003279 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003280 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003281 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003282 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003283 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003284 proto0->Set(v8_str("custom"), v8_num(1234));
3285
3286 LocalContext env1;
3287 v8::Handle<v8::Object> global1 =
3288 env1->Global();
3289 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003290 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003291 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003292 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003293 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003294 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003295 CHECK(!proto1->Has(v8_str("custom")));
3296}
3297
3298
3299THREADED_TEST(Regress892105) {
3300 // Make sure that object and array literals created by cloning
3301 // boilerplates cannot communicate through their __proto__
3302 // field. This is rather difficult to check, but we try to add stuff
3303 // to Object.prototype and Array.prototype and create a new
3304 // environment. This should succeed.
3305
3306 v8::HandleScope scope;
3307
3308 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3309 "Array.prototype.arr = 4567;"
3310 "8901");
3311
3312 LocalContext env0;
3313 Local<Script> script0 = Script::Compile(source);
3314 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3315
3316 LocalContext env1;
3317 Local<Script> script1 = Script::Compile(source);
3318 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3319}
3320
3321
Steve Blocka7e24c12009-10-30 11:49:00 +00003322THREADED_TEST(UndetectableObject) {
3323 v8::HandleScope scope;
3324 LocalContext env;
3325
3326 Local<v8::FunctionTemplate> desc =
3327 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3328 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3329
3330 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3331 env->Global()->Set(v8_str("undetectable"), obj);
3332
3333 ExpectString("undetectable.toString()", "[object Object]");
3334 ExpectString("typeof undetectable", "undefined");
3335 ExpectString("typeof(undetectable)", "undefined");
3336 ExpectBoolean("typeof undetectable == 'undefined'", true);
3337 ExpectBoolean("typeof undetectable == 'object'", false);
3338 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3339 ExpectBoolean("!undetectable", true);
3340
3341 ExpectObject("true&&undetectable", obj);
3342 ExpectBoolean("false&&undetectable", false);
3343 ExpectBoolean("true||undetectable", true);
3344 ExpectObject("false||undetectable", obj);
3345
3346 ExpectObject("undetectable&&true", obj);
3347 ExpectObject("undetectable&&false", obj);
3348 ExpectBoolean("undetectable||true", true);
3349 ExpectBoolean("undetectable||false", false);
3350
3351 ExpectBoolean("undetectable==null", true);
3352 ExpectBoolean("null==undetectable", true);
3353 ExpectBoolean("undetectable==undefined", true);
3354 ExpectBoolean("undefined==undetectable", true);
3355 ExpectBoolean("undetectable==undetectable", true);
3356
3357
3358 ExpectBoolean("undetectable===null", false);
3359 ExpectBoolean("null===undetectable", false);
3360 ExpectBoolean("undetectable===undefined", false);
3361 ExpectBoolean("undefined===undetectable", false);
3362 ExpectBoolean("undetectable===undetectable", true);
3363}
3364
3365
Steve Block8defd9f2010-07-08 12:39:36 +01003366
3367THREADED_TEST(ExtensibleOnUndetectable) {
3368 v8::HandleScope scope;
3369 LocalContext env;
3370
3371 Local<v8::FunctionTemplate> desc =
3372 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3373 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3374
3375 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3376 env->Global()->Set(v8_str("undetectable"), obj);
3377
3378 Local<String> source = v8_str("undetectable.x = 42;"
3379 "undetectable.x");
3380
3381 Local<Script> script = Script::Compile(source);
3382
3383 CHECK_EQ(v8::Integer::New(42), script->Run());
3384
3385 ExpectBoolean("Object.isExtensible(undetectable)", true);
3386
3387 source = v8_str("Object.preventExtensions(undetectable);");
3388 script = Script::Compile(source);
3389 script->Run();
3390 ExpectBoolean("Object.isExtensible(undetectable)", false);
3391
3392 source = v8_str("undetectable.y = 2000;");
3393 script = Script::Compile(source);
3394 v8::TryCatch try_catch;
3395 Local<Value> result = script->Run();
3396 CHECK(result.IsEmpty());
3397 CHECK(try_catch.HasCaught());
3398}
3399
3400
3401
Steve Blocka7e24c12009-10-30 11:49:00 +00003402THREADED_TEST(UndetectableString) {
3403 v8::HandleScope scope;
3404 LocalContext env;
3405
3406 Local<String> obj = String::NewUndetectable("foo");
3407 env->Global()->Set(v8_str("undetectable"), obj);
3408
3409 ExpectString("undetectable", "foo");
3410 ExpectString("typeof undetectable", "undefined");
3411 ExpectString("typeof(undetectable)", "undefined");
3412 ExpectBoolean("typeof undetectable == 'undefined'", true);
3413 ExpectBoolean("typeof undetectable == 'string'", false);
3414 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3415 ExpectBoolean("!undetectable", true);
3416
3417 ExpectObject("true&&undetectable", obj);
3418 ExpectBoolean("false&&undetectable", false);
3419 ExpectBoolean("true||undetectable", true);
3420 ExpectObject("false||undetectable", obj);
3421
3422 ExpectObject("undetectable&&true", obj);
3423 ExpectObject("undetectable&&false", obj);
3424 ExpectBoolean("undetectable||true", true);
3425 ExpectBoolean("undetectable||false", false);
3426
3427 ExpectBoolean("undetectable==null", true);
3428 ExpectBoolean("null==undetectable", true);
3429 ExpectBoolean("undetectable==undefined", true);
3430 ExpectBoolean("undefined==undetectable", true);
3431 ExpectBoolean("undetectable==undetectable", true);
3432
3433
3434 ExpectBoolean("undetectable===null", false);
3435 ExpectBoolean("null===undetectable", false);
3436 ExpectBoolean("undetectable===undefined", false);
3437 ExpectBoolean("undefined===undetectable", false);
3438 ExpectBoolean("undetectable===undetectable", true);
3439}
3440
3441
3442template <typename T> static void USE(T) { }
3443
3444
3445// This test is not intended to be run, just type checked.
3446static void PersistentHandles() {
3447 USE(PersistentHandles);
3448 Local<String> str = v8_str("foo");
3449 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3450 USE(p_str);
3451 Local<Script> scr = Script::Compile(v8_str(""));
3452 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3453 USE(p_scr);
3454 Local<ObjectTemplate> templ = ObjectTemplate::New();
3455 v8::Persistent<ObjectTemplate> p_templ =
3456 v8::Persistent<ObjectTemplate>::New(templ);
3457 USE(p_templ);
3458}
3459
3460
3461static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3462 ApiTestFuzzer::Fuzz();
3463 return v8::Undefined();
3464}
3465
3466
3467THREADED_TEST(GlobalObjectTemplate) {
3468 v8::HandleScope handle_scope;
3469 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3470 global_template->Set(v8_str("JSNI_Log"),
3471 v8::FunctionTemplate::New(HandleLogDelegator));
3472 v8::Persistent<Context> context = Context::New(0, global_template);
3473 Context::Scope context_scope(context);
3474 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3475 context.Dispose();
3476}
3477
3478
3479static const char* kSimpleExtensionSource =
3480 "function Foo() {"
3481 " return 4;"
3482 "}";
3483
3484
3485THREADED_TEST(SimpleExtensions) {
3486 v8::HandleScope handle_scope;
3487 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3488 const char* extension_names[] = { "simpletest" };
3489 v8::ExtensionConfiguration extensions(1, extension_names);
3490 v8::Handle<Context> context = Context::New(&extensions);
3491 Context::Scope lock(context);
3492 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3493 CHECK_EQ(result, v8::Integer::New(4));
3494}
3495
3496
3497static const char* kEvalExtensionSource1 =
3498 "function UseEval1() {"
3499 " var x = 42;"
3500 " return eval('x');"
3501 "}";
3502
3503
3504static const char* kEvalExtensionSource2 =
3505 "(function() {"
3506 " var x = 42;"
3507 " function e() {"
3508 " return eval('x');"
3509 " }"
3510 " this.UseEval2 = e;"
3511 "})()";
3512
3513
3514THREADED_TEST(UseEvalFromExtension) {
3515 v8::HandleScope handle_scope;
3516 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3517 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3518 const char* extension_names[] = { "evaltest1", "evaltest2" };
3519 v8::ExtensionConfiguration extensions(2, extension_names);
3520 v8::Handle<Context> context = Context::New(&extensions);
3521 Context::Scope lock(context);
3522 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3523 CHECK_EQ(result, v8::Integer::New(42));
3524 result = Script::Compile(v8_str("UseEval2()"))->Run();
3525 CHECK_EQ(result, v8::Integer::New(42));
3526}
3527
3528
3529static const char* kWithExtensionSource1 =
3530 "function UseWith1() {"
3531 " var x = 42;"
3532 " with({x:87}) { return x; }"
3533 "}";
3534
3535
3536
3537static const char* kWithExtensionSource2 =
3538 "(function() {"
3539 " var x = 42;"
3540 " function e() {"
3541 " with ({x:87}) { return x; }"
3542 " }"
3543 " this.UseWith2 = e;"
3544 "})()";
3545
3546
3547THREADED_TEST(UseWithFromExtension) {
3548 v8::HandleScope handle_scope;
3549 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3550 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3551 const char* extension_names[] = { "withtest1", "withtest2" };
3552 v8::ExtensionConfiguration extensions(2, extension_names);
3553 v8::Handle<Context> context = Context::New(&extensions);
3554 Context::Scope lock(context);
3555 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3556 CHECK_EQ(result, v8::Integer::New(87));
3557 result = Script::Compile(v8_str("UseWith2()"))->Run();
3558 CHECK_EQ(result, v8::Integer::New(87));
3559}
3560
3561
3562THREADED_TEST(AutoExtensions) {
3563 v8::HandleScope handle_scope;
3564 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3565 extension->set_auto_enable(true);
3566 v8::RegisterExtension(extension);
3567 v8::Handle<Context> context = Context::New();
3568 Context::Scope lock(context);
3569 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3570 CHECK_EQ(result, v8::Integer::New(4));
3571}
3572
3573
Steve Blockd0582a62009-12-15 09:54:21 +00003574static const char* kSyntaxErrorInExtensionSource =
3575 "[";
3576
3577
3578// Test that a syntax error in an extension does not cause a fatal
3579// error but results in an empty context.
3580THREADED_TEST(SyntaxErrorExtensions) {
3581 v8::HandleScope handle_scope;
3582 v8::RegisterExtension(new Extension("syntaxerror",
3583 kSyntaxErrorInExtensionSource));
3584 const char* extension_names[] = { "syntaxerror" };
3585 v8::ExtensionConfiguration extensions(1, extension_names);
3586 v8::Handle<Context> context = Context::New(&extensions);
3587 CHECK(context.IsEmpty());
3588}
3589
3590
3591static const char* kExceptionInExtensionSource =
3592 "throw 42";
3593
3594
3595// Test that an exception when installing an extension does not cause
3596// a fatal error but results in an empty context.
3597THREADED_TEST(ExceptionExtensions) {
3598 v8::HandleScope handle_scope;
3599 v8::RegisterExtension(new Extension("exception",
3600 kExceptionInExtensionSource));
3601 const char* extension_names[] = { "exception" };
3602 v8::ExtensionConfiguration extensions(1, extension_names);
3603 v8::Handle<Context> context = Context::New(&extensions);
3604 CHECK(context.IsEmpty());
3605}
3606
3607
Steve Blocka7e24c12009-10-30 11:49:00 +00003608static void CheckDependencies(const char* name, const char* expected) {
3609 v8::HandleScope handle_scope;
3610 v8::ExtensionConfiguration config(1, &name);
3611 LocalContext context(&config);
3612 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3613}
3614
3615
3616/*
3617 * Configuration:
3618 *
3619 * /-- B <--\
3620 * A <- -- D <-- E
3621 * \-- C <--/
3622 */
3623THREADED_TEST(ExtensionDependency) {
3624 static const char* kEDeps[] = { "D" };
3625 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3626 static const char* kDDeps[] = { "B", "C" };
3627 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3628 static const char* kBCDeps[] = { "A" };
3629 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3630 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3631 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3632 CheckDependencies("A", "undefinedA");
3633 CheckDependencies("B", "undefinedAB");
3634 CheckDependencies("C", "undefinedAC");
3635 CheckDependencies("D", "undefinedABCD");
3636 CheckDependencies("E", "undefinedABCDE");
3637 v8::HandleScope handle_scope;
3638 static const char* exts[2] = { "C", "E" };
3639 v8::ExtensionConfiguration config(2, exts);
3640 LocalContext context(&config);
3641 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3642}
3643
3644
3645static const char* kExtensionTestScript =
3646 "native function A();"
3647 "native function B();"
3648 "native function C();"
3649 "function Foo(i) {"
3650 " if (i == 0) return A();"
3651 " if (i == 1) return B();"
3652 " if (i == 2) return C();"
3653 "}";
3654
3655
3656static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3657 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003658 if (args.IsConstructCall()) {
3659 args.This()->Set(v8_str("data"), args.Data());
3660 return v8::Null();
3661 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003662 return args.Data();
3663}
3664
3665
3666class FunctionExtension : public Extension {
3667 public:
3668 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3669 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3670 v8::Handle<String> name);
3671};
3672
3673
3674static int lookup_count = 0;
3675v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3676 v8::Handle<String> name) {
3677 lookup_count++;
3678 if (name->Equals(v8_str("A"))) {
3679 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3680 } else if (name->Equals(v8_str("B"))) {
3681 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3682 } else if (name->Equals(v8_str("C"))) {
3683 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3684 } else {
3685 return v8::Handle<v8::FunctionTemplate>();
3686 }
3687}
3688
3689
3690THREADED_TEST(FunctionLookup) {
3691 v8::RegisterExtension(new FunctionExtension());
3692 v8::HandleScope handle_scope;
3693 static const char* exts[1] = { "functiontest" };
3694 v8::ExtensionConfiguration config(1, exts);
3695 LocalContext context(&config);
3696 CHECK_EQ(3, lookup_count);
3697 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3698 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3699 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3700}
3701
3702
Leon Clarkee46be812010-01-19 14:06:41 +00003703THREADED_TEST(NativeFunctionConstructCall) {
3704 v8::RegisterExtension(new FunctionExtension());
3705 v8::HandleScope handle_scope;
3706 static const char* exts[1] = { "functiontest" };
3707 v8::ExtensionConfiguration config(1, exts);
3708 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003709 for (int i = 0; i < 10; i++) {
3710 // Run a few times to ensure that allocation of objects doesn't
3711 // change behavior of a constructor function.
3712 CHECK_EQ(v8::Integer::New(8),
3713 Script::Compile(v8_str("(new A()).data"))->Run());
3714 CHECK_EQ(v8::Integer::New(7),
3715 Script::Compile(v8_str("(new B()).data"))->Run());
3716 CHECK_EQ(v8::Integer::New(6),
3717 Script::Compile(v8_str("(new C()).data"))->Run());
3718 }
Leon Clarkee46be812010-01-19 14:06:41 +00003719}
3720
3721
Steve Blocka7e24c12009-10-30 11:49:00 +00003722static const char* last_location;
3723static const char* last_message;
3724void StoringErrorCallback(const char* location, const char* message) {
3725 if (last_location == NULL) {
3726 last_location = location;
3727 last_message = message;
3728 }
3729}
3730
3731
3732// ErrorReporting creates a circular extensions configuration and
3733// tests that the fatal error handler gets called. This renders V8
3734// unusable and therefore this test cannot be run in parallel.
3735TEST(ErrorReporting) {
3736 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3737 static const char* aDeps[] = { "B" };
3738 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3739 static const char* bDeps[] = { "A" };
3740 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3741 last_location = NULL;
3742 v8::ExtensionConfiguration config(1, bDeps);
3743 v8::Handle<Context> context = Context::New(&config);
3744 CHECK(context.IsEmpty());
3745 CHECK_NE(last_location, NULL);
3746}
3747
3748
3749static const char* js_code_causing_huge_string_flattening =
3750 "var str = 'X';"
3751 "for (var i = 0; i < 30; i++) {"
3752 " str = str + str;"
3753 "}"
3754 "str.match(/X/);";
3755
3756
3757void OOMCallback(const char* location, const char* message) {
3758 exit(0);
3759}
3760
3761
3762TEST(RegexpOutOfMemory) {
3763 // Execute a script that causes out of memory when flattening a string.
3764 v8::HandleScope scope;
3765 v8::V8::SetFatalErrorHandler(OOMCallback);
3766 LocalContext context;
3767 Local<Script> script =
3768 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3769 last_location = NULL;
3770 Local<Value> result = script->Run();
3771
3772 CHECK(false); // Should not return.
3773}
3774
3775
3776static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3777 v8::Handle<Value> data) {
3778 CHECK_EQ(v8::Undefined(), data);
3779 CHECK(message->GetScriptResourceName()->IsUndefined());
3780 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3781 message->GetLineNumber();
3782 message->GetSourceLine();
3783}
3784
3785
3786THREADED_TEST(ErrorWithMissingScriptInfo) {
3787 v8::HandleScope scope;
3788 LocalContext context;
3789 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3790 Script::Compile(v8_str("throw Error()"))->Run();
3791 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3792}
3793
3794
3795int global_index = 0;
3796
3797class Snorkel {
3798 public:
3799 Snorkel() { index_ = global_index++; }
3800 int index_;
3801};
3802
3803class Whammy {
3804 public:
3805 Whammy() {
3806 cursor_ = 0;
3807 }
3808 ~Whammy() {
3809 script_.Dispose();
3810 }
3811 v8::Handle<Script> getScript() {
3812 if (script_.IsEmpty())
3813 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3814 return Local<Script>(*script_);
3815 }
3816
3817 public:
3818 static const int kObjectCount = 256;
3819 int cursor_;
3820 v8::Persistent<v8::Object> objects_[kObjectCount];
3821 v8::Persistent<Script> script_;
3822};
3823
3824static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3825 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3826 delete snorkel;
3827 obj.ClearWeak();
3828}
3829
3830v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3831 const AccessorInfo& info) {
3832 Whammy* whammy =
3833 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3834
3835 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3836
3837 v8::Handle<v8::Object> obj = v8::Object::New();
3838 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3839 if (!prev.IsEmpty()) {
3840 prev->Set(v8_str("next"), obj);
3841 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3842 whammy->objects_[whammy->cursor_].Clear();
3843 }
3844 whammy->objects_[whammy->cursor_] = global;
3845 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3846 return whammy->getScript()->Run();
3847}
3848
3849THREADED_TEST(WeakReference) {
3850 v8::HandleScope handle_scope;
3851 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003852 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00003853 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3854 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003855 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00003856 const char* extension_list[] = { "v8/gc" };
3857 v8::ExtensionConfiguration extensions(1, extension_list);
3858 v8::Persistent<Context> context = Context::New(&extensions);
3859 Context::Scope context_scope(context);
3860
3861 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3862 context->Global()->Set(v8_str("whammy"), interceptor);
3863 const char* code =
3864 "var last;"
3865 "for (var i = 0; i < 10000; i++) {"
3866 " var obj = whammy.length;"
3867 " if (last) last.next = obj;"
3868 " last = obj;"
3869 "}"
3870 "gc();"
3871 "4";
3872 v8::Handle<Value> result = CompileRun(code);
3873 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003874 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00003875 context.Dispose();
3876}
3877
3878
Steve Blockd0582a62009-12-15 09:54:21 +00003879static bool in_scavenge = false;
3880static int last = -1;
3881
3882static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3883 CHECK_EQ(-1, last);
3884 last = 0;
3885 obj.Dispose();
3886 obj.Clear();
3887 in_scavenge = true;
3888 i::Heap::PerformScavenge();
3889 in_scavenge = false;
3890 *(reinterpret_cast<bool*>(data)) = true;
3891}
3892
3893static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3894 void* data) {
3895 CHECK_EQ(0, last);
3896 last = 1;
3897 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3898 obj.Dispose();
3899 obj.Clear();
3900}
3901
3902THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3903 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3904 // Calling callbacks from scavenges is unsafe as objects held by those
3905 // handlers might have become strongly reachable, but scavenge doesn't
3906 // check that.
3907 v8::Persistent<Context> context = Context::New();
3908 Context::Scope context_scope(context);
3909
3910 v8::Persistent<v8::Object> object_a;
3911 v8::Persistent<v8::Object> object_b;
3912
3913 {
3914 v8::HandleScope handle_scope;
3915 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
3916 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
3917 }
3918
3919 bool object_a_disposed = false;
3920 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
3921 bool released_in_scavenge = false;
3922 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
3923
3924 while (!object_a_disposed) {
3925 i::Heap::CollectAllGarbage(false);
3926 }
3927 CHECK(!released_in_scavenge);
3928}
3929
3930
Steve Blocka7e24c12009-10-30 11:49:00 +00003931v8::Handle<Function> args_fun;
3932
3933
3934static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3935 ApiTestFuzzer::Fuzz();
3936 CHECK_EQ(args_fun, args.Callee());
3937 CHECK_EQ(3, args.Length());
3938 CHECK_EQ(v8::Integer::New(1), args[0]);
3939 CHECK_EQ(v8::Integer::New(2), args[1]);
3940 CHECK_EQ(v8::Integer::New(3), args[2]);
3941 CHECK_EQ(v8::Undefined(), args[3]);
3942 v8::HandleScope scope;
3943 i::Heap::CollectAllGarbage(false);
3944 return v8::Undefined();
3945}
3946
3947
3948THREADED_TEST(Arguments) {
3949 v8::HandleScope scope;
3950 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3951 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3952 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01003953 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003954 v8_compile("f(1, 2, 3)")->Run();
3955}
3956
3957
Steve Blocka7e24c12009-10-30 11:49:00 +00003958static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3959 const AccessorInfo&) {
3960 return v8::Handle<Value>();
3961}
3962
3963
3964static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3965 const AccessorInfo&) {
3966 return v8::Handle<Value>();
3967}
3968
3969
3970static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3971 const AccessorInfo&) {
3972 if (!name->Equals(v8_str("foo"))) {
3973 return v8::Handle<v8::Boolean>(); // not intercepted
3974 }
3975
3976 return v8::False(); // intercepted, and don't delete the property
3977}
3978
3979
3980static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
3981 if (index != 2) {
3982 return v8::Handle<v8::Boolean>(); // not intercepted
3983 }
3984
3985 return v8::False(); // intercepted, and don't delete the property
3986}
3987
3988
3989THREADED_TEST(Deleter) {
3990 v8::HandleScope scope;
3991 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3992 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
3993 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
3994 LocalContext context;
3995 context->Global()->Set(v8_str("k"), obj->NewInstance());
3996 CompileRun(
3997 "k.foo = 'foo';"
3998 "k.bar = 'bar';"
3999 "k[2] = 2;"
4000 "k[4] = 4;");
4001 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
4002 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
4003
4004 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
4005 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
4006
4007 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
4008 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
4009
4010 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4011 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4012}
4013
4014
4015static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4016 ApiTestFuzzer::Fuzz();
4017 if (name->Equals(v8_str("foo")) ||
4018 name->Equals(v8_str("bar")) ||
4019 name->Equals(v8_str("baz"))) {
4020 return v8::Undefined();
4021 }
4022 return v8::Handle<Value>();
4023}
4024
4025
4026static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4027 ApiTestFuzzer::Fuzz();
4028 if (index == 0 || index == 1) return v8::Undefined();
4029 return v8::Handle<Value>();
4030}
4031
4032
4033static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4034 ApiTestFuzzer::Fuzz();
4035 v8::Handle<v8::Array> result = v8::Array::New(3);
4036 result->Set(v8::Integer::New(0), v8_str("foo"));
4037 result->Set(v8::Integer::New(1), v8_str("bar"));
4038 result->Set(v8::Integer::New(2), v8_str("baz"));
4039 return result;
4040}
4041
4042
4043static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4044 ApiTestFuzzer::Fuzz();
4045 v8::Handle<v8::Array> result = v8::Array::New(2);
4046 result->Set(v8::Integer::New(0), v8_str("0"));
4047 result->Set(v8::Integer::New(1), v8_str("1"));
4048 return result;
4049}
4050
4051
4052THREADED_TEST(Enumerators) {
4053 v8::HandleScope scope;
4054 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4055 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4056 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4057 LocalContext context;
4058 context->Global()->Set(v8_str("k"), obj->NewInstance());
4059 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4060 "k[10] = 0;"
4061 "k.a = 0;"
4062 "k[5] = 0;"
4063 "k.b = 0;"
4064 "k[4294967295] = 0;"
4065 "k.c = 0;"
4066 "k[4294967296] = 0;"
4067 "k.d = 0;"
4068 "k[140000] = 0;"
4069 "k.e = 0;"
4070 "k[30000000000] = 0;"
4071 "k.f = 0;"
4072 "var result = [];"
4073 "for (var prop in k) {"
4074 " result.push(prop);"
4075 "}"
4076 "result"));
4077 // Check that we get all the property names returned including the
4078 // ones from the enumerators in the right order: indexed properties
4079 // in numerical order, indexed interceptor properties, named
4080 // properties in insertion order, named interceptor properties.
4081 // This order is not mandated by the spec, so this test is just
4082 // documenting our behavior.
4083 CHECK_EQ(17, result->Length());
4084 // Indexed properties in numerical order.
4085 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4086 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4087 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4088 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4089 // Indexed interceptor properties in the order they are returned
4090 // from the enumerator interceptor.
4091 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4092 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4093 // Named properties in insertion order.
4094 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4095 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4096 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4097 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4098 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4099 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4100 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4101 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4102 // Named interceptor properties.
4103 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4104 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4105 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4106}
4107
4108
4109int p_getter_count;
4110int p_getter_count2;
4111
4112
4113static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4114 ApiTestFuzzer::Fuzz();
4115 p_getter_count++;
4116 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4117 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4118 if (name->Equals(v8_str("p1"))) {
4119 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4120 } else if (name->Equals(v8_str("p2"))) {
4121 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4122 } else if (name->Equals(v8_str("p3"))) {
4123 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4124 } else if (name->Equals(v8_str("p4"))) {
4125 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4126 }
4127 return v8::Undefined();
4128}
4129
4130
4131static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4132 ApiTestFuzzer::Fuzz();
4133 LocalContext context;
4134 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4135 CompileRun(
4136 "o1.__proto__ = { };"
4137 "var o2 = { __proto__: o1 };"
4138 "var o3 = { __proto__: o2 };"
4139 "var o4 = { __proto__: o3 };"
4140 "for (var i = 0; i < 10; i++) o4.p4;"
4141 "for (var i = 0; i < 10; i++) o3.p3;"
4142 "for (var i = 0; i < 10; i++) o2.p2;"
4143 "for (var i = 0; i < 10; i++) o1.p1;");
4144}
4145
4146
4147static v8::Handle<Value> PGetter2(Local<String> name,
4148 const AccessorInfo& info) {
4149 ApiTestFuzzer::Fuzz();
4150 p_getter_count2++;
4151 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4152 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4153 if (name->Equals(v8_str("p1"))) {
4154 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4155 } else if (name->Equals(v8_str("p2"))) {
4156 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4157 } else if (name->Equals(v8_str("p3"))) {
4158 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4159 } else if (name->Equals(v8_str("p4"))) {
4160 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4161 }
4162 return v8::Undefined();
4163}
4164
4165
4166THREADED_TEST(GetterHolders) {
4167 v8::HandleScope scope;
4168 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4169 obj->SetAccessor(v8_str("p1"), PGetter);
4170 obj->SetAccessor(v8_str("p2"), PGetter);
4171 obj->SetAccessor(v8_str("p3"), PGetter);
4172 obj->SetAccessor(v8_str("p4"), PGetter);
4173 p_getter_count = 0;
4174 RunHolderTest(obj);
4175 CHECK_EQ(40, p_getter_count);
4176}
4177
4178
4179THREADED_TEST(PreInterceptorHolders) {
4180 v8::HandleScope scope;
4181 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4182 obj->SetNamedPropertyHandler(PGetter2);
4183 p_getter_count2 = 0;
4184 RunHolderTest(obj);
4185 CHECK_EQ(40, p_getter_count2);
4186}
4187
4188
4189THREADED_TEST(ObjectInstantiation) {
4190 v8::HandleScope scope;
4191 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4192 templ->SetAccessor(v8_str("t"), PGetter2);
4193 LocalContext context;
4194 context->Global()->Set(v8_str("o"), templ->NewInstance());
4195 for (int i = 0; i < 100; i++) {
4196 v8::HandleScope inner_scope;
4197 v8::Handle<v8::Object> obj = templ->NewInstance();
4198 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4199 context->Global()->Set(v8_str("o2"), obj);
4200 v8::Handle<Value> value =
4201 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4202 CHECK_EQ(v8::True(), value);
4203 context->Global()->Set(v8_str("o"), obj);
4204 }
4205}
4206
4207
4208THREADED_TEST(StringWrite) {
4209 v8::HandleScope scope;
4210 v8::Handle<String> str = v8_str("abcde");
4211
4212 char buf[100];
4213 int len;
4214
4215 memset(buf, 0x1, sizeof(buf));
4216 len = str->WriteAscii(buf);
4217 CHECK_EQ(len, 5);
4218 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4219
4220 memset(buf, 0x1, sizeof(buf));
4221 len = str->WriteAscii(buf, 0, 4);
4222 CHECK_EQ(len, 4);
4223 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4224
4225 memset(buf, 0x1, sizeof(buf));
4226 len = str->WriteAscii(buf, 0, 5);
4227 CHECK_EQ(len, 5);
4228 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4229
4230 memset(buf, 0x1, sizeof(buf));
4231 len = str->WriteAscii(buf, 0, 6);
4232 CHECK_EQ(len, 5);
4233 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4234
4235 memset(buf, 0x1, sizeof(buf));
4236 len = str->WriteAscii(buf, 4, -1);
4237 CHECK_EQ(len, 1);
4238 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4239
4240 memset(buf, 0x1, sizeof(buf));
4241 len = str->WriteAscii(buf, 4, 6);
4242 CHECK_EQ(len, 1);
4243 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4244
4245 memset(buf, 0x1, sizeof(buf));
4246 len = str->WriteAscii(buf, 4, 1);
4247 CHECK_EQ(len, 1);
4248 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4249}
4250
4251
4252THREADED_TEST(ToArrayIndex) {
4253 v8::HandleScope scope;
4254 LocalContext context;
4255
4256 v8::Handle<String> str = v8_str("42");
4257 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4258 CHECK(!index.IsEmpty());
4259 CHECK_EQ(42.0, index->Uint32Value());
4260 str = v8_str("42asdf");
4261 index = str->ToArrayIndex();
4262 CHECK(index.IsEmpty());
4263 str = v8_str("-42");
4264 index = str->ToArrayIndex();
4265 CHECK(index.IsEmpty());
4266 str = v8_str("4294967295");
4267 index = str->ToArrayIndex();
4268 CHECK(!index.IsEmpty());
4269 CHECK_EQ(4294967295.0, index->Uint32Value());
4270 v8::Handle<v8::Number> num = v8::Number::New(1);
4271 index = num->ToArrayIndex();
4272 CHECK(!index.IsEmpty());
4273 CHECK_EQ(1.0, index->Uint32Value());
4274 num = v8::Number::New(-1);
4275 index = num->ToArrayIndex();
4276 CHECK(index.IsEmpty());
4277 v8::Handle<v8::Object> obj = v8::Object::New();
4278 index = obj->ToArrayIndex();
4279 CHECK(index.IsEmpty());
4280}
4281
4282
4283THREADED_TEST(ErrorConstruction) {
4284 v8::HandleScope scope;
4285 LocalContext context;
4286
4287 v8::Handle<String> foo = v8_str("foo");
4288 v8::Handle<String> message = v8_str("message");
4289 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4290 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004291 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4292 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004293 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4294 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004295 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004296 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4297 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004298 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004299 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4300 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004301 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004302 v8::Handle<Value> error = v8::Exception::Error(foo);
4303 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004304 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004305}
4306
4307
4308static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4309 ApiTestFuzzer::Fuzz();
4310 return v8_num(10);
4311}
4312
4313
4314static void YSetter(Local<String> name,
4315 Local<Value> value,
4316 const AccessorInfo& info) {
4317 if (info.This()->Has(name)) {
4318 info.This()->Delete(name);
4319 }
4320 info.This()->Set(name, value);
4321}
4322
4323
4324THREADED_TEST(DeleteAccessor) {
4325 v8::HandleScope scope;
4326 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4327 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4328 LocalContext context;
4329 v8::Handle<v8::Object> holder = obj->NewInstance();
4330 context->Global()->Set(v8_str("holder"), holder);
4331 v8::Handle<Value> result = CompileRun(
4332 "holder.y = 11; holder.y = 12; holder.y");
4333 CHECK_EQ(12, result->Uint32Value());
4334}
4335
4336
4337THREADED_TEST(TypeSwitch) {
4338 v8::HandleScope scope;
4339 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4340 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4341 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4342 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4343 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4344 LocalContext context;
4345 v8::Handle<v8::Object> obj0 = v8::Object::New();
4346 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4347 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4348 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4349 for (int i = 0; i < 10; i++) {
4350 CHECK_EQ(0, type_switch->match(obj0));
4351 CHECK_EQ(1, type_switch->match(obj1));
4352 CHECK_EQ(2, type_switch->match(obj2));
4353 CHECK_EQ(3, type_switch->match(obj3));
4354 CHECK_EQ(3, type_switch->match(obj3));
4355 CHECK_EQ(2, type_switch->match(obj2));
4356 CHECK_EQ(1, type_switch->match(obj1));
4357 CHECK_EQ(0, type_switch->match(obj0));
4358 }
4359}
4360
4361
4362// For use within the TestSecurityHandler() test.
4363static bool g_security_callback_result = false;
4364static bool NamedSecurityTestCallback(Local<v8::Object> global,
4365 Local<Value> name,
4366 v8::AccessType type,
4367 Local<Value> data) {
4368 // Always allow read access.
4369 if (type == v8::ACCESS_GET)
4370 return true;
4371
4372 // Sometimes allow other access.
4373 return g_security_callback_result;
4374}
4375
4376
4377static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4378 uint32_t key,
4379 v8::AccessType type,
4380 Local<Value> data) {
4381 // Always allow read access.
4382 if (type == v8::ACCESS_GET)
4383 return true;
4384
4385 // Sometimes allow other access.
4386 return g_security_callback_result;
4387}
4388
4389
4390static int trouble_nesting = 0;
4391static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4392 ApiTestFuzzer::Fuzz();
4393 trouble_nesting++;
4394
4395 // Call a JS function that throws an uncaught exception.
4396 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4397 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4398 arg_this->Get(v8_str("trouble_callee")) :
4399 arg_this->Get(v8_str("trouble_caller"));
4400 CHECK(trouble_callee->IsFunction());
4401 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4402}
4403
4404
4405static int report_count = 0;
4406static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4407 v8::Handle<Value>) {
4408 report_count++;
4409}
4410
4411
4412// Counts uncaught exceptions, but other tests running in parallel
4413// also have uncaught exceptions.
4414TEST(ApiUncaughtException) {
4415 report_count = 0;
4416 v8::HandleScope scope;
4417 LocalContext env;
4418 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4419
4420 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4421 v8::Local<v8::Object> global = env->Global();
4422 global->Set(v8_str("trouble"), fun->GetFunction());
4423
4424 Script::Compile(v8_str("function trouble_callee() {"
4425 " var x = null;"
4426 " return x.foo;"
4427 "};"
4428 "function trouble_caller() {"
4429 " trouble();"
4430 "};"))->Run();
4431 Local<Value> trouble = global->Get(v8_str("trouble"));
4432 CHECK(trouble->IsFunction());
4433 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4434 CHECK(trouble_callee->IsFunction());
4435 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4436 CHECK(trouble_caller->IsFunction());
4437 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4438 CHECK_EQ(1, report_count);
4439 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4440}
4441
Leon Clarke4515c472010-02-03 11:58:03 +00004442static const char* script_resource_name = "ExceptionInNativeScript.js";
4443static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4444 v8::Handle<Value>) {
4445 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4446 CHECK(!name_val.IsEmpty() && name_val->IsString());
4447 v8::String::AsciiValue name(message->GetScriptResourceName());
4448 CHECK_EQ(script_resource_name, *name);
4449 CHECK_EQ(3, message->GetLineNumber());
4450 v8::String::AsciiValue source_line(message->GetSourceLine());
4451 CHECK_EQ(" new o.foo();", *source_line);
4452}
4453
4454TEST(ExceptionInNativeScript) {
4455 v8::HandleScope scope;
4456 LocalContext env;
4457 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4458
4459 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4460 v8::Local<v8::Object> global = env->Global();
4461 global->Set(v8_str("trouble"), fun->GetFunction());
4462
4463 Script::Compile(v8_str("function trouble() {\n"
4464 " var o = {};\n"
4465 " new o.foo();\n"
4466 "};"), v8::String::New(script_resource_name))->Run();
4467 Local<Value> trouble = global->Get(v8_str("trouble"));
4468 CHECK(trouble->IsFunction());
4469 Function::Cast(*trouble)->Call(global, 0, NULL);
4470 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4471}
4472
Steve Blocka7e24c12009-10-30 11:49:00 +00004473
4474TEST(CompilationErrorUsingTryCatchHandler) {
4475 v8::HandleScope scope;
4476 LocalContext env;
4477 v8::TryCatch try_catch;
4478 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4479 CHECK_NE(NULL, *try_catch.Exception());
4480 CHECK(try_catch.HasCaught());
4481}
4482
4483
4484TEST(TryCatchFinallyUsingTryCatchHandler) {
4485 v8::HandleScope scope;
4486 LocalContext env;
4487 v8::TryCatch try_catch;
4488 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4489 CHECK(!try_catch.HasCaught());
4490 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4491 CHECK(try_catch.HasCaught());
4492 try_catch.Reset();
4493 Script::Compile(v8_str("(function() {"
4494 "try { throw ''; } finally { return; }"
4495 "})()"))->Run();
4496 CHECK(!try_catch.HasCaught());
4497 Script::Compile(v8_str("(function()"
4498 " { try { throw ''; } finally { throw 0; }"
4499 "})()"))->Run();
4500 CHECK(try_catch.HasCaught());
4501}
4502
4503
4504// SecurityHandler can't be run twice
4505TEST(SecurityHandler) {
4506 v8::HandleScope scope0;
4507 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4508 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4509 IndexedSecurityTestCallback);
4510 // Create an environment
4511 v8::Persistent<Context> context0 =
4512 Context::New(NULL, global_template);
4513 context0->Enter();
4514
4515 v8::Handle<v8::Object> global0 = context0->Global();
4516 v8::Handle<Script> script0 = v8_compile("foo = 111");
4517 script0->Run();
4518 global0->Set(v8_str("0"), v8_num(999));
4519 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4520 CHECK_EQ(111, foo0->Int32Value());
4521 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4522 CHECK_EQ(999, z0->Int32Value());
4523
4524 // Create another environment, should fail security checks.
4525 v8::HandleScope scope1;
4526
4527 v8::Persistent<Context> context1 =
4528 Context::New(NULL, global_template);
4529 context1->Enter();
4530
4531 v8::Handle<v8::Object> global1 = context1->Global();
4532 global1->Set(v8_str("othercontext"), global0);
4533 // This set will fail the security check.
4534 v8::Handle<Script> script1 =
4535 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4536 script1->Run();
4537 // This read will pass the security check.
4538 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4539 CHECK_EQ(111, foo1->Int32Value());
4540 // This read will pass the security check.
4541 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4542 CHECK_EQ(999, z1->Int32Value());
4543
4544 // Create another environment, should pass security checks.
4545 { g_security_callback_result = true; // allow security handler to pass.
4546 v8::HandleScope scope2;
4547 LocalContext context2;
4548 v8::Handle<v8::Object> global2 = context2->Global();
4549 global2->Set(v8_str("othercontext"), global0);
4550 v8::Handle<Script> script2 =
4551 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4552 script2->Run();
4553 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4554 CHECK_EQ(333, foo2->Int32Value());
4555 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4556 CHECK_EQ(888, z2->Int32Value());
4557 }
4558
4559 context1->Exit();
4560 context1.Dispose();
4561
4562 context0->Exit();
4563 context0.Dispose();
4564}
4565
4566
4567THREADED_TEST(SecurityChecks) {
4568 v8::HandleScope handle_scope;
4569 LocalContext env1;
4570 v8::Persistent<Context> env2 = Context::New();
4571
4572 Local<Value> foo = v8_str("foo");
4573 Local<Value> bar = v8_str("bar");
4574
4575 // Set to the same domain.
4576 env1->SetSecurityToken(foo);
4577
4578 // Create a function in env1.
4579 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4580 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4581 CHECK(spy->IsFunction());
4582
4583 // Create another function accessing global objects.
4584 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4585 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4586 CHECK(spy2->IsFunction());
4587
4588 // Switch to env2 in the same domain and invoke spy on env2.
4589 {
4590 env2->SetSecurityToken(foo);
4591 // Enter env2
4592 Context::Scope scope_env2(env2);
4593 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4594 CHECK(result->IsFunction());
4595 }
4596
4597 {
4598 env2->SetSecurityToken(bar);
4599 Context::Scope scope_env2(env2);
4600
4601 // Call cross_domain_call, it should throw an exception
4602 v8::TryCatch try_catch;
4603 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4604 CHECK(try_catch.HasCaught());
4605 }
4606
4607 env2.Dispose();
4608}
4609
4610
4611// Regression test case for issue 1183439.
4612THREADED_TEST(SecurityChecksForPrototypeChain) {
4613 v8::HandleScope scope;
4614 LocalContext current;
4615 v8::Persistent<Context> other = Context::New();
4616
4617 // Change context to be able to get to the Object function in the
4618 // other context without hitting the security checks.
4619 v8::Local<Value> other_object;
4620 { Context::Scope scope(other);
4621 other_object = other->Global()->Get(v8_str("Object"));
4622 other->Global()->Set(v8_num(42), v8_num(87));
4623 }
4624
4625 current->Global()->Set(v8_str("other"), other->Global());
4626 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4627
4628 // Make sure the security check fails here and we get an undefined
4629 // result instead of getting the Object function. Repeat in a loop
4630 // to make sure to exercise the IC code.
4631 v8::Local<Script> access_other0 = v8_compile("other.Object");
4632 v8::Local<Script> access_other1 = v8_compile("other[42]");
4633 for (int i = 0; i < 5; i++) {
4634 CHECK(!access_other0->Run()->Equals(other_object));
4635 CHECK(access_other0->Run()->IsUndefined());
4636 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4637 CHECK(access_other1->Run()->IsUndefined());
4638 }
4639
4640 // Create an object that has 'other' in its prototype chain and make
4641 // sure we cannot access the Object function indirectly through
4642 // that. Repeat in a loop to make sure to exercise the IC code.
4643 v8_compile("function F() { };"
4644 "F.prototype = other;"
4645 "var f = new F();")->Run();
4646 v8::Local<Script> access_f0 = v8_compile("f.Object");
4647 v8::Local<Script> access_f1 = v8_compile("f[42]");
4648 for (int j = 0; j < 5; j++) {
4649 CHECK(!access_f0->Run()->Equals(other_object));
4650 CHECK(access_f0->Run()->IsUndefined());
4651 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4652 CHECK(access_f1->Run()->IsUndefined());
4653 }
4654
4655 // Now it gets hairy: Set the prototype for the other global object
4656 // to be the current global object. The prototype chain for 'f' now
4657 // goes through 'other' but ends up in the current global object.
4658 { Context::Scope scope(other);
4659 other->Global()->Set(v8_str("__proto__"), current->Global());
4660 }
4661 // Set a named and an index property on the current global
4662 // object. To force the lookup to go through the other global object,
4663 // the properties must not exist in the other global object.
4664 current->Global()->Set(v8_str("foo"), v8_num(100));
4665 current->Global()->Set(v8_num(99), v8_num(101));
4666 // Try to read the properties from f and make sure that the access
4667 // gets stopped by the security checks on the other global object.
4668 Local<Script> access_f2 = v8_compile("f.foo");
4669 Local<Script> access_f3 = v8_compile("f[99]");
4670 for (int k = 0; k < 5; k++) {
4671 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4672 CHECK(access_f2->Run()->IsUndefined());
4673 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4674 CHECK(access_f3->Run()->IsUndefined());
4675 }
4676 other.Dispose();
4677}
4678
4679
4680THREADED_TEST(CrossDomainDelete) {
4681 v8::HandleScope handle_scope;
4682 LocalContext env1;
4683 v8::Persistent<Context> env2 = Context::New();
4684
4685 Local<Value> foo = v8_str("foo");
4686 Local<Value> bar = v8_str("bar");
4687
4688 // Set to the same domain.
4689 env1->SetSecurityToken(foo);
4690 env2->SetSecurityToken(foo);
4691
4692 env1->Global()->Set(v8_str("prop"), v8_num(3));
4693 env2->Global()->Set(v8_str("env1"), env1->Global());
4694
4695 // Change env2 to a different domain and delete env1.prop.
4696 env2->SetSecurityToken(bar);
4697 {
4698 Context::Scope scope_env2(env2);
4699 Local<Value> result =
4700 Script::Compile(v8_str("delete env1.prop"))->Run();
4701 CHECK(result->IsFalse());
4702 }
4703
4704 // Check that env1.prop still exists.
4705 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4706 CHECK(v->IsNumber());
4707 CHECK_EQ(3, v->Int32Value());
4708
4709 env2.Dispose();
4710}
4711
4712
4713THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4714 v8::HandleScope handle_scope;
4715 LocalContext env1;
4716 v8::Persistent<Context> env2 = Context::New();
4717
4718 Local<Value> foo = v8_str("foo");
4719 Local<Value> bar = v8_str("bar");
4720
4721 // Set to the same domain.
4722 env1->SetSecurityToken(foo);
4723 env2->SetSecurityToken(foo);
4724
4725 env1->Global()->Set(v8_str("prop"), v8_num(3));
4726 env2->Global()->Set(v8_str("env1"), env1->Global());
4727
4728 // env1.prop is enumerable in env2.
4729 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4730 {
4731 Context::Scope scope_env2(env2);
4732 Local<Value> result = Script::Compile(test)->Run();
4733 CHECK(result->IsTrue());
4734 }
4735
4736 // Change env2 to a different domain and test again.
4737 env2->SetSecurityToken(bar);
4738 {
4739 Context::Scope scope_env2(env2);
4740 Local<Value> result = Script::Compile(test)->Run();
4741 CHECK(result->IsFalse());
4742 }
4743
4744 env2.Dispose();
4745}
4746
4747
4748THREADED_TEST(CrossDomainForIn) {
4749 v8::HandleScope handle_scope;
4750 LocalContext env1;
4751 v8::Persistent<Context> env2 = Context::New();
4752
4753 Local<Value> foo = v8_str("foo");
4754 Local<Value> bar = v8_str("bar");
4755
4756 // Set to the same domain.
4757 env1->SetSecurityToken(foo);
4758 env2->SetSecurityToken(foo);
4759
4760 env1->Global()->Set(v8_str("prop"), v8_num(3));
4761 env2->Global()->Set(v8_str("env1"), env1->Global());
4762
4763 // Change env2 to a different domain and set env1's global object
4764 // as the __proto__ of an object in env2 and enumerate properties
4765 // in for-in. It shouldn't enumerate properties on env1's global
4766 // object.
4767 env2->SetSecurityToken(bar);
4768 {
4769 Context::Scope scope_env2(env2);
4770 Local<Value> result =
4771 CompileRun("(function(){var obj = {'__proto__':env1};"
4772 "for (var p in obj)"
4773 " if (p == 'prop') return false;"
4774 "return true;})()");
4775 CHECK(result->IsTrue());
4776 }
4777 env2.Dispose();
4778}
4779
4780
4781TEST(ContextDetachGlobal) {
4782 v8::HandleScope handle_scope;
4783 LocalContext env1;
4784 v8::Persistent<Context> env2 = Context::New();
4785
4786 Local<v8::Object> global1 = env1->Global();
4787
4788 Local<Value> foo = v8_str("foo");
4789
4790 // Set to the same domain.
4791 env1->SetSecurityToken(foo);
4792 env2->SetSecurityToken(foo);
4793
4794 // Enter env2
4795 env2->Enter();
4796
Andrei Popescu74b3c142010-03-29 12:03:09 +01004797 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004798 Local<v8::Object> global2 = env2->Global();
4799 global2->Set(v8_str("prop"), v8::Integer::New(1));
4800 CompileRun("function getProp() {return prop;}");
4801
4802 env1->Global()->Set(v8_str("getProp"),
4803 global2->Get(v8_str("getProp")));
4804
Andrei Popescu74b3c142010-03-29 12:03:09 +01004805 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004806 env2->Exit();
4807 env2->DetachGlobal();
4808 // env2 has a new global object.
4809 CHECK(!env2->Global()->Equals(global2));
4810
4811 v8::Persistent<Context> env3 =
4812 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4813 env3->SetSecurityToken(v8_str("bar"));
4814 env3->Enter();
4815
4816 Local<v8::Object> global3 = env3->Global();
4817 CHECK_EQ(global2, global3);
4818 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4819 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4820 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4821 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4822 env3->Exit();
4823
4824 // Call getProp in env1, and it should return the value 1
4825 {
4826 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4827 CHECK(get_prop->IsFunction());
4828 v8::TryCatch try_catch;
4829 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4830 CHECK(!try_catch.HasCaught());
4831 CHECK_EQ(1, r->Int32Value());
4832 }
4833
4834 // Check that env3 is not accessible from env1
4835 {
4836 Local<Value> r = global3->Get(v8_str("prop2"));
4837 CHECK(r->IsUndefined());
4838 }
4839
4840 env2.Dispose();
4841 env3.Dispose();
4842}
4843
4844
Andrei Popescu74b3c142010-03-29 12:03:09 +01004845TEST(DetachAndReattachGlobal) {
4846 v8::HandleScope scope;
4847 LocalContext env1;
4848
4849 // Create second environment.
4850 v8::Persistent<Context> env2 = Context::New();
4851
4852 Local<Value> foo = v8_str("foo");
4853
4854 // Set same security token for env1 and env2.
4855 env1->SetSecurityToken(foo);
4856 env2->SetSecurityToken(foo);
4857
4858 // Create a property on the global object in env2.
4859 {
4860 v8::Context::Scope scope(env2);
4861 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4862 }
4863
4864 // Create a reference to env2 global from env1 global.
4865 env1->Global()->Set(v8_str("other"), env2->Global());
4866
4867 // Check that we have access to other.p in env2 from env1.
4868 Local<Value> result = CompileRun("other.p");
4869 CHECK(result->IsInt32());
4870 CHECK_EQ(42, result->Int32Value());
4871
4872 // Hold on to global from env2 and detach global from env2.
4873 Local<v8::Object> global2 = env2->Global();
4874 env2->DetachGlobal();
4875
4876 // Check that the global has been detached. No other.p property can
4877 // be found.
4878 result = CompileRun("other.p");
4879 CHECK(result->IsUndefined());
4880
4881 // Reuse global2 for env3.
4882 v8::Persistent<Context> env3 =
4883 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4884 CHECK_EQ(global2, env3->Global());
4885
4886 // Start by using the same security token for env3 as for env1 and env2.
4887 env3->SetSecurityToken(foo);
4888
4889 // Create a property on the global object in env3.
4890 {
4891 v8::Context::Scope scope(env3);
4892 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4893 }
4894
4895 // Check that other.p is now the property in env3 and that we have access.
4896 result = CompileRun("other.p");
4897 CHECK(result->IsInt32());
4898 CHECK_EQ(24, result->Int32Value());
4899
4900 // Change security token for env3 to something different from env1 and env2.
4901 env3->SetSecurityToken(v8_str("bar"));
4902
4903 // Check that we do not have access to other.p in env1. |other| is now
4904 // the global object for env3 which has a different security token,
4905 // so access should be blocked.
4906 result = CompileRun("other.p");
4907 CHECK(result->IsUndefined());
4908
4909 // Detach the global for env3 and reattach it to env2.
4910 env3->DetachGlobal();
4911 env2->ReattachGlobal(global2);
4912
4913 // Check that we have access to other.p again in env1. |other| is now
4914 // the global object for env2 which has the same security token as env1.
4915 result = CompileRun("other.p");
4916 CHECK(result->IsInt32());
4917 CHECK_EQ(42, result->Int32Value());
4918
4919 env2.Dispose();
4920 env3.Dispose();
4921}
4922
4923
Steve Blocka7e24c12009-10-30 11:49:00 +00004924static bool NamedAccessBlocker(Local<v8::Object> global,
4925 Local<Value> name,
4926 v8::AccessType type,
4927 Local<Value> data) {
4928 return Context::GetCurrent()->Global()->Equals(global);
4929}
4930
4931
4932static bool IndexedAccessBlocker(Local<v8::Object> global,
4933 uint32_t key,
4934 v8::AccessType type,
4935 Local<Value> data) {
4936 return Context::GetCurrent()->Global()->Equals(global);
4937}
4938
4939
4940static int g_echo_value = -1;
4941static v8::Handle<Value> EchoGetter(Local<String> name,
4942 const AccessorInfo& info) {
4943 return v8_num(g_echo_value);
4944}
4945
4946
4947static void EchoSetter(Local<String> name,
4948 Local<Value> value,
4949 const AccessorInfo&) {
4950 if (value->IsNumber())
4951 g_echo_value = value->Int32Value();
4952}
4953
4954
4955static v8::Handle<Value> UnreachableGetter(Local<String> name,
4956 const AccessorInfo& info) {
4957 CHECK(false); // This function should not be called..
4958 return v8::Undefined();
4959}
4960
4961
4962static void UnreachableSetter(Local<String>, Local<Value>,
4963 const AccessorInfo&) {
4964 CHECK(false); // This function should nto be called.
4965}
4966
4967
4968THREADED_TEST(AccessControl) {
4969 v8::HandleScope handle_scope;
4970 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4971
4972 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4973 IndexedAccessBlocker);
4974
4975 // Add an accessor accessible by cross-domain JS code.
4976 global_template->SetAccessor(
4977 v8_str("accessible_prop"),
4978 EchoGetter, EchoSetter,
4979 v8::Handle<Value>(),
4980 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4981
4982 // Add an accessor that is not accessible by cross-domain JS code.
4983 global_template->SetAccessor(v8_str("blocked_prop"),
4984 UnreachableGetter, UnreachableSetter,
4985 v8::Handle<Value>(),
4986 v8::DEFAULT);
4987
4988 // Create an environment
4989 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4990 context0->Enter();
4991
4992 v8::Handle<v8::Object> global0 = context0->Global();
4993
4994 v8::HandleScope scope1;
4995
4996 v8::Persistent<Context> context1 = Context::New();
4997 context1->Enter();
4998
4999 v8::Handle<v8::Object> global1 = context1->Global();
5000 global1->Set(v8_str("other"), global0);
5001
5002 v8::Handle<Value> value;
5003
5004 // Access blocked property
5005 value = v8_compile("other.blocked_prop = 1")->Run();
5006 value = v8_compile("other.blocked_prop")->Run();
5007 CHECK(value->IsUndefined());
5008
5009 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
5010 CHECK(value->IsFalse());
5011
5012 // Access accessible property
5013 value = v8_compile("other.accessible_prop = 3")->Run();
5014 CHECK(value->IsNumber());
5015 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005016 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005017
5018 value = v8_compile("other.accessible_prop")->Run();
5019 CHECK(value->IsNumber());
5020 CHECK_EQ(3, value->Int32Value());
5021
5022 value =
5023 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
5024 CHECK(value->IsTrue());
5025
5026 // Enumeration doesn't enumerate accessors from inaccessible objects in
5027 // the prototype chain even if the accessors are in themselves accessible.
5028 Local<Value> result =
5029 CompileRun("(function(){var obj = {'__proto__':other};"
5030 "for (var p in obj)"
5031 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5032 " return false;"
5033 " }"
5034 "return true;})()");
5035 CHECK(result->IsTrue());
5036
5037 context1->Exit();
5038 context0->Exit();
5039 context1.Dispose();
5040 context0.Dispose();
5041}
5042
5043
Leon Clarke4515c472010-02-03 11:58:03 +00005044static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5045 Local<Value> name,
5046 v8::AccessType type,
5047 Local<Value> data) {
5048 return false;
5049}
5050
5051
5052static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5053 uint32_t key,
5054 v8::AccessType type,
5055 Local<Value> data) {
5056 return false;
5057}
5058
5059
5060THREADED_TEST(AccessControlGetOwnPropertyNames) {
5061 v8::HandleScope handle_scope;
5062 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5063
5064 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5065 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5066 GetOwnPropertyNamesIndexedBlocker);
5067
5068 // Create an environment
5069 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5070 context0->Enter();
5071
5072 v8::Handle<v8::Object> global0 = context0->Global();
5073
5074 v8::HandleScope scope1;
5075
5076 v8::Persistent<Context> context1 = Context::New();
5077 context1->Enter();
5078
5079 v8::Handle<v8::Object> global1 = context1->Global();
5080 global1->Set(v8_str("other"), global0);
5081 global1->Set(v8_str("object"), obj_template->NewInstance());
5082
5083 v8::Handle<Value> value;
5084
5085 // Attempt to get the property names of the other global object and
5086 // of an object that requires access checks. Accessing the other
5087 // global object should be blocked by access checks on the global
5088 // proxy object. Accessing the object that requires access checks
5089 // is blocked by the access checks on the object itself.
5090 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5091 CHECK(value->IsTrue());
5092
5093 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5094 CHECK(value->IsTrue());
5095
5096 context1->Exit();
5097 context0->Exit();
5098 context1.Dispose();
5099 context0.Dispose();
5100}
5101
5102
Steve Block8defd9f2010-07-08 12:39:36 +01005103static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5104 v8::Handle<v8::Array> result = v8::Array::New(1);
5105 result->Set(0, v8_str("x"));
5106 return result;
5107}
5108
5109
5110THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5111 v8::HandleScope handle_scope;
5112 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5113
5114 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5115 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5116 NamedPropertyEnumerator);
5117
5118 LocalContext context;
5119 v8::Handle<v8::Object> global = context->Global();
5120 global->Set(v8_str("object"), obj_template->NewInstance());
5121
5122 v8::Handle<Value> value =
5123 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5124 CHECK_EQ(v8_str("x"), value);
5125}
5126
5127
Steve Blocka7e24c12009-10-30 11:49:00 +00005128static v8::Handle<Value> ConstTenGetter(Local<String> name,
5129 const AccessorInfo& info) {
5130 return v8_num(10);
5131}
5132
5133
5134THREADED_TEST(CrossDomainAccessors) {
5135 v8::HandleScope handle_scope;
5136
5137 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5138
5139 v8::Handle<v8::ObjectTemplate> global_template =
5140 func_template->InstanceTemplate();
5141
5142 v8::Handle<v8::ObjectTemplate> proto_template =
5143 func_template->PrototypeTemplate();
5144
5145 // Add an accessor to proto that's accessible by cross-domain JS code.
5146 proto_template->SetAccessor(v8_str("accessible"),
5147 ConstTenGetter, 0,
5148 v8::Handle<Value>(),
5149 v8::ALL_CAN_READ);
5150
5151 // Add an accessor that is not accessible by cross-domain JS code.
5152 global_template->SetAccessor(v8_str("unreachable"),
5153 UnreachableGetter, 0,
5154 v8::Handle<Value>(),
5155 v8::DEFAULT);
5156
5157 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5158 context0->Enter();
5159
5160 Local<v8::Object> global = context0->Global();
5161 // Add a normal property that shadows 'accessible'
5162 global->Set(v8_str("accessible"), v8_num(11));
5163
5164 // Enter a new context.
5165 v8::HandleScope scope1;
5166 v8::Persistent<Context> context1 = Context::New();
5167 context1->Enter();
5168
5169 v8::Handle<v8::Object> global1 = context1->Global();
5170 global1->Set(v8_str("other"), global);
5171
5172 // Should return 10, instead of 11
5173 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5174 CHECK(value->IsNumber());
5175 CHECK_EQ(10, value->Int32Value());
5176
5177 value = v8_compile("other.unreachable")->Run();
5178 CHECK(value->IsUndefined());
5179
5180 context1->Exit();
5181 context0->Exit();
5182 context1.Dispose();
5183 context0.Dispose();
5184}
5185
5186
5187static int named_access_count = 0;
5188static int indexed_access_count = 0;
5189
5190static bool NamedAccessCounter(Local<v8::Object> global,
5191 Local<Value> name,
5192 v8::AccessType type,
5193 Local<Value> data) {
5194 named_access_count++;
5195 return true;
5196}
5197
5198
5199static bool IndexedAccessCounter(Local<v8::Object> global,
5200 uint32_t key,
5201 v8::AccessType type,
5202 Local<Value> data) {
5203 indexed_access_count++;
5204 return true;
5205}
5206
5207
5208// This one is too easily disturbed by other tests.
5209TEST(AccessControlIC) {
5210 named_access_count = 0;
5211 indexed_access_count = 0;
5212
5213 v8::HandleScope handle_scope;
5214
5215 // Create an environment.
5216 v8::Persistent<Context> context0 = Context::New();
5217 context0->Enter();
5218
5219 // Create an object that requires access-check functions to be
5220 // called for cross-domain access.
5221 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5222 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5223 IndexedAccessCounter);
5224 Local<v8::Object> object = object_template->NewInstance();
5225
5226 v8::HandleScope scope1;
5227
5228 // Create another environment.
5229 v8::Persistent<Context> context1 = Context::New();
5230 context1->Enter();
5231
5232 // Make easy access to the object from the other environment.
5233 v8::Handle<v8::Object> global1 = context1->Global();
5234 global1->Set(v8_str("obj"), object);
5235
5236 v8::Handle<Value> value;
5237
5238 // Check that the named access-control function is called every time.
5239 CompileRun("function testProp(obj) {"
5240 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5241 " for (var j = 0; j < 10; j++) obj.prop;"
5242 " return obj.prop"
5243 "}");
5244 value = CompileRun("testProp(obj)");
5245 CHECK(value->IsNumber());
5246 CHECK_EQ(1, value->Int32Value());
5247 CHECK_EQ(21, named_access_count);
5248
5249 // Check that the named access-control function is called every time.
5250 CompileRun("var p = 'prop';"
5251 "function testKeyed(obj) {"
5252 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5253 " for (var j = 0; j < 10; j++) obj[p];"
5254 " return obj[p];"
5255 "}");
5256 // Use obj which requires access checks. No inline caching is used
5257 // in that case.
5258 value = CompileRun("testKeyed(obj)");
5259 CHECK(value->IsNumber());
5260 CHECK_EQ(1, value->Int32Value());
5261 CHECK_EQ(42, named_access_count);
5262 // Force the inline caches into generic state and try again.
5263 CompileRun("testKeyed({ a: 0 })");
5264 CompileRun("testKeyed({ b: 0 })");
5265 value = CompileRun("testKeyed(obj)");
5266 CHECK(value->IsNumber());
5267 CHECK_EQ(1, value->Int32Value());
5268 CHECK_EQ(63, named_access_count);
5269
5270 // Check that the indexed access-control function is called every time.
5271 CompileRun("function testIndexed(obj) {"
5272 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5273 " for (var j = 0; j < 10; j++) obj[0];"
5274 " return obj[0]"
5275 "}");
5276 value = CompileRun("testIndexed(obj)");
5277 CHECK(value->IsNumber());
5278 CHECK_EQ(1, value->Int32Value());
5279 CHECK_EQ(21, indexed_access_count);
5280 // Force the inline caches into generic state.
5281 CompileRun("testIndexed(new Array(1))");
5282 // Test that the indexed access check is called.
5283 value = CompileRun("testIndexed(obj)");
5284 CHECK(value->IsNumber());
5285 CHECK_EQ(1, value->Int32Value());
5286 CHECK_EQ(42, indexed_access_count);
5287
5288 // Check that the named access check is called when invoking
5289 // functions on an object that requires access checks.
5290 CompileRun("obj.f = function() {}");
5291 CompileRun("function testCallNormal(obj) {"
5292 " for (var i = 0; i < 10; i++) obj.f();"
5293 "}");
5294 CompileRun("testCallNormal(obj)");
5295 CHECK_EQ(74, named_access_count);
5296
5297 // Force obj into slow case.
5298 value = CompileRun("delete obj.prop");
5299 CHECK(value->BooleanValue());
5300 // Force inline caches into dictionary probing mode.
5301 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5302 // Test that the named access check is called.
5303 value = CompileRun("testProp(obj);");
5304 CHECK(value->IsNumber());
5305 CHECK_EQ(1, value->Int32Value());
5306 CHECK_EQ(96, named_access_count);
5307
5308 // Force the call inline cache into dictionary probing mode.
5309 CompileRun("o.f = function() {}; testCallNormal(o)");
5310 // Test that the named access check is still called for each
5311 // invocation of the function.
5312 value = CompileRun("testCallNormal(obj)");
5313 CHECK_EQ(106, named_access_count);
5314
5315 context1->Exit();
5316 context0->Exit();
5317 context1.Dispose();
5318 context0.Dispose();
5319}
5320
5321
5322static bool NamedAccessFlatten(Local<v8::Object> global,
5323 Local<Value> name,
5324 v8::AccessType type,
5325 Local<Value> data) {
5326 char buf[100];
5327 int len;
5328
5329 CHECK(name->IsString());
5330
5331 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005332 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005333 CHECK_EQ(4, len);
5334
5335 uint16_t buf2[100];
5336
5337 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005338 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005339 CHECK_EQ(4, len);
5340
5341 return true;
5342}
5343
5344
5345static bool IndexedAccessFlatten(Local<v8::Object> global,
5346 uint32_t key,
5347 v8::AccessType type,
5348 Local<Value> data) {
5349 return true;
5350}
5351
5352
5353// Regression test. In access checks, operations that may cause
5354// garbage collection are not allowed. It used to be the case that
5355// using the Write operation on a string could cause a garbage
5356// collection due to flattening of the string. This is no longer the
5357// case.
5358THREADED_TEST(AccessControlFlatten) {
5359 named_access_count = 0;
5360 indexed_access_count = 0;
5361
5362 v8::HandleScope handle_scope;
5363
5364 // Create an environment.
5365 v8::Persistent<Context> context0 = Context::New();
5366 context0->Enter();
5367
5368 // Create an object that requires access-check functions to be
5369 // called for cross-domain access.
5370 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5371 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5372 IndexedAccessFlatten);
5373 Local<v8::Object> object = object_template->NewInstance();
5374
5375 v8::HandleScope scope1;
5376
5377 // Create another environment.
5378 v8::Persistent<Context> context1 = Context::New();
5379 context1->Enter();
5380
5381 // Make easy access to the object from the other environment.
5382 v8::Handle<v8::Object> global1 = context1->Global();
5383 global1->Set(v8_str("obj"), object);
5384
5385 v8::Handle<Value> value;
5386
5387 value = v8_compile("var p = 'as' + 'df';")->Run();
5388 value = v8_compile("obj[p];")->Run();
5389
5390 context1->Exit();
5391 context0->Exit();
5392 context1.Dispose();
5393 context0.Dispose();
5394}
5395
5396
5397static v8::Handle<Value> AccessControlNamedGetter(
5398 Local<String>, const AccessorInfo&) {
5399 return v8::Integer::New(42);
5400}
5401
5402
5403static v8::Handle<Value> AccessControlNamedSetter(
5404 Local<String>, Local<Value> value, const AccessorInfo&) {
5405 return value;
5406}
5407
5408
5409static v8::Handle<Value> AccessControlIndexedGetter(
5410 uint32_t index,
5411 const AccessorInfo& info) {
5412 return v8_num(42);
5413}
5414
5415
5416static v8::Handle<Value> AccessControlIndexedSetter(
5417 uint32_t, Local<Value> value, const AccessorInfo&) {
5418 return value;
5419}
5420
5421
5422THREADED_TEST(AccessControlInterceptorIC) {
5423 named_access_count = 0;
5424 indexed_access_count = 0;
5425
5426 v8::HandleScope handle_scope;
5427
5428 // Create an environment.
5429 v8::Persistent<Context> context0 = Context::New();
5430 context0->Enter();
5431
5432 // Create an object that requires access-check functions to be
5433 // called for cross-domain access. The object also has interceptors
5434 // interceptor.
5435 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5436 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5437 IndexedAccessCounter);
5438 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5439 AccessControlNamedSetter);
5440 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5441 AccessControlIndexedSetter);
5442 Local<v8::Object> object = object_template->NewInstance();
5443
5444 v8::HandleScope scope1;
5445
5446 // Create another environment.
5447 v8::Persistent<Context> context1 = Context::New();
5448 context1->Enter();
5449
5450 // Make easy access to the object from the other environment.
5451 v8::Handle<v8::Object> global1 = context1->Global();
5452 global1->Set(v8_str("obj"), object);
5453
5454 v8::Handle<Value> value;
5455
5456 // Check that the named access-control function is called every time
5457 // eventhough there is an interceptor on the object.
5458 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5459 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5460 "obj.x")->Run();
5461 CHECK(value->IsNumber());
5462 CHECK_EQ(42, value->Int32Value());
5463 CHECK_EQ(21, named_access_count);
5464
5465 value = v8_compile("var p = 'x';")->Run();
5466 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5467 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5468 "obj[p]")->Run();
5469 CHECK(value->IsNumber());
5470 CHECK_EQ(42, value->Int32Value());
5471 CHECK_EQ(42, named_access_count);
5472
5473 // Check that the indexed access-control function is called every
5474 // time eventhough there is an interceptor on the object.
5475 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5476 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5477 "obj[0]")->Run();
5478 CHECK(value->IsNumber());
5479 CHECK_EQ(42, value->Int32Value());
5480 CHECK_EQ(21, indexed_access_count);
5481
5482 context1->Exit();
5483 context0->Exit();
5484 context1.Dispose();
5485 context0.Dispose();
5486}
5487
5488
5489THREADED_TEST(Version) {
5490 v8::V8::GetVersion();
5491}
5492
5493
5494static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5495 ApiTestFuzzer::Fuzz();
5496 return v8_num(12);
5497}
5498
5499
5500THREADED_TEST(InstanceProperties) {
5501 v8::HandleScope handle_scope;
5502 LocalContext context;
5503
5504 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5505 Local<ObjectTemplate> instance = t->InstanceTemplate();
5506
5507 instance->Set(v8_str("x"), v8_num(42));
5508 instance->Set(v8_str("f"),
5509 v8::FunctionTemplate::New(InstanceFunctionCallback));
5510
5511 Local<Value> o = t->GetFunction()->NewInstance();
5512
5513 context->Global()->Set(v8_str("i"), o);
5514 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5515 CHECK_EQ(42, value->Int32Value());
5516
5517 value = Script::Compile(v8_str("i.f()"))->Run();
5518 CHECK_EQ(12, value->Int32Value());
5519}
5520
5521
5522static v8::Handle<Value>
5523GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5524 ApiTestFuzzer::Fuzz();
5525 return v8::Handle<Value>();
5526}
5527
5528
5529THREADED_TEST(GlobalObjectInstanceProperties) {
5530 v8::HandleScope handle_scope;
5531
5532 Local<Value> global_object;
5533
5534 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5535 t->InstanceTemplate()->SetNamedPropertyHandler(
5536 GlobalObjectInstancePropertiesGet);
5537 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5538 instance_template->Set(v8_str("x"), v8_num(42));
5539 instance_template->Set(v8_str("f"),
5540 v8::FunctionTemplate::New(InstanceFunctionCallback));
5541
5542 {
5543 LocalContext env(NULL, instance_template);
5544 // Hold on to the global object so it can be used again in another
5545 // environment initialization.
5546 global_object = env->Global();
5547
5548 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5549 CHECK_EQ(42, value->Int32Value());
5550 value = Script::Compile(v8_str("f()"))->Run();
5551 CHECK_EQ(12, value->Int32Value());
5552 }
5553
5554 {
5555 // Create new environment reusing the global object.
5556 LocalContext env(NULL, instance_template, global_object);
5557 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5558 CHECK_EQ(42, value->Int32Value());
5559 value = Script::Compile(v8_str("f()"))->Run();
5560 CHECK_EQ(12, value->Int32Value());
5561 }
5562}
5563
5564
5565static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5566 ApiTestFuzzer::Fuzz();
5567 return v8_num(42);
5568}
5569
5570
5571static int shadow_y;
5572static int shadow_y_setter_call_count;
5573static int shadow_y_getter_call_count;
5574
5575
5576static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5577 shadow_y_setter_call_count++;
5578 shadow_y = 42;
5579}
5580
5581
5582static v8::Handle<Value> ShadowYGetter(Local<String> name,
5583 const AccessorInfo& info) {
5584 ApiTestFuzzer::Fuzz();
5585 shadow_y_getter_call_count++;
5586 return v8_num(shadow_y);
5587}
5588
5589
5590static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5591 const AccessorInfo& info) {
5592 return v8::Handle<Value>();
5593}
5594
5595
5596static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5597 const AccessorInfo&) {
5598 return v8::Handle<Value>();
5599}
5600
5601
5602THREADED_TEST(ShadowObject) {
5603 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5604 v8::HandleScope handle_scope;
5605
5606 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5607 LocalContext context(NULL, global_template);
5608
5609 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5610 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5611 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5612 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5613 Local<ObjectTemplate> instance = t->InstanceTemplate();
5614
5615 // Only allow calls of f on instances of t.
5616 Local<v8::Signature> signature = v8::Signature::New(t);
5617 proto->Set(v8_str("f"),
5618 v8::FunctionTemplate::New(ShadowFunctionCallback,
5619 Local<Value>(),
5620 signature));
5621 proto->Set(v8_str("x"), v8_num(12));
5622
5623 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5624
5625 Local<Value> o = t->GetFunction()->NewInstance();
5626 context->Global()->Set(v8_str("__proto__"), o);
5627
5628 Local<Value> value =
5629 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5630 CHECK(value->IsBoolean());
5631 CHECK(!value->BooleanValue());
5632
5633 value = Script::Compile(v8_str("x"))->Run();
5634 CHECK_EQ(12, value->Int32Value());
5635
5636 value = Script::Compile(v8_str("f()"))->Run();
5637 CHECK_EQ(42, value->Int32Value());
5638
5639 Script::Compile(v8_str("y = 42"))->Run();
5640 CHECK_EQ(1, shadow_y_setter_call_count);
5641 value = Script::Compile(v8_str("y"))->Run();
5642 CHECK_EQ(1, shadow_y_getter_call_count);
5643 CHECK_EQ(42, value->Int32Value());
5644}
5645
5646
5647THREADED_TEST(HiddenPrototype) {
5648 v8::HandleScope handle_scope;
5649 LocalContext context;
5650
5651 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5652 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5653 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5654 t1->SetHiddenPrototype(true);
5655 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5656 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5657 t2->SetHiddenPrototype(true);
5658 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5659 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5660 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5661
5662 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5663 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5664 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5665 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5666
5667 // Setting the prototype on an object skips hidden prototypes.
5668 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5669 o0->Set(v8_str("__proto__"), o1);
5670 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5671 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5672 o0->Set(v8_str("__proto__"), o2);
5673 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5674 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5675 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5676 o0->Set(v8_str("__proto__"), o3);
5677 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5678 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5679 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5680 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5681
5682 // Getting the prototype of o0 should get the first visible one
5683 // which is o3. Therefore, z should not be defined on the prototype
5684 // object.
5685 Local<Value> proto = o0->Get(v8_str("__proto__"));
5686 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005687 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005688}
5689
5690
Andrei Popescu402d9372010-02-26 13:31:12 +00005691THREADED_TEST(SetPrototype) {
5692 v8::HandleScope handle_scope;
5693 LocalContext context;
5694
5695 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5696 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5697 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5698 t1->SetHiddenPrototype(true);
5699 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5700 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5701 t2->SetHiddenPrototype(true);
5702 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5703 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5704 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5705
5706 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5707 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5708 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5709 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5710
5711 // Setting the prototype on an object does not skip hidden prototypes.
5712 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5713 CHECK(o0->SetPrototype(o1));
5714 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5715 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5716 CHECK(o1->SetPrototype(o2));
5717 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5718 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5719 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5720 CHECK(o2->SetPrototype(o3));
5721 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5722 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5723 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5724 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5725
5726 // Getting the prototype of o0 should get the first visible one
5727 // which is o3. Therefore, z should not be defined on the prototype
5728 // object.
5729 Local<Value> proto = o0->Get(v8_str("__proto__"));
5730 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005731 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005732
5733 // However, Object::GetPrototype ignores hidden prototype.
5734 Local<Value> proto0 = o0->GetPrototype();
5735 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005736 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005737
5738 Local<Value> proto1 = o1->GetPrototype();
5739 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005740 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005741
5742 Local<Value> proto2 = o2->GetPrototype();
5743 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005744 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005745}
5746
5747
5748THREADED_TEST(SetPrototypeThrows) {
5749 v8::HandleScope handle_scope;
5750 LocalContext context;
5751
5752 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5753
5754 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5755 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5756
5757 CHECK(o0->SetPrototype(o1));
5758 // If setting the prototype leads to the cycle, SetPrototype should
5759 // return false and keep VM in sane state.
5760 v8::TryCatch try_catch;
5761 CHECK(!o1->SetPrototype(o0));
5762 CHECK(!try_catch.HasCaught());
5763 ASSERT(!i::Top::has_pending_exception());
5764
5765 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5766}
5767
5768
Steve Blocka7e24c12009-10-30 11:49:00 +00005769THREADED_TEST(GetterSetterExceptions) {
5770 v8::HandleScope handle_scope;
5771 LocalContext context;
5772 CompileRun(
5773 "function Foo() { };"
5774 "function Throw() { throw 5; };"
5775 "var x = { };"
5776 "x.__defineSetter__('set', Throw);"
5777 "x.__defineGetter__('get', Throw);");
5778 Local<v8::Object> x =
5779 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5780 v8::TryCatch try_catch;
5781 x->Set(v8_str("set"), v8::Integer::New(8));
5782 x->Get(v8_str("get"));
5783 x->Set(v8_str("set"), v8::Integer::New(8));
5784 x->Get(v8_str("get"));
5785 x->Set(v8_str("set"), v8::Integer::New(8));
5786 x->Get(v8_str("get"));
5787 x->Set(v8_str("set"), v8::Integer::New(8));
5788 x->Get(v8_str("get"));
5789}
5790
5791
5792THREADED_TEST(Constructor) {
5793 v8::HandleScope handle_scope;
5794 LocalContext context;
5795 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5796 templ->SetClassName(v8_str("Fun"));
5797 Local<Function> cons = templ->GetFunction();
5798 context->Global()->Set(v8_str("Fun"), cons);
5799 Local<v8::Object> inst = cons->NewInstance();
5800 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5801 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5802 CHECK(value->BooleanValue());
5803}
5804
5805THREADED_TEST(FunctionDescriptorException) {
5806 v8::HandleScope handle_scope;
5807 LocalContext context;
5808 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5809 templ->SetClassName(v8_str("Fun"));
5810 Local<Function> cons = templ->GetFunction();
5811 context->Global()->Set(v8_str("Fun"), cons);
5812 Local<Value> value = CompileRun(
5813 "function test() {"
5814 " try {"
5815 " (new Fun()).blah()"
5816 " } catch (e) {"
5817 " var str = String(e);"
5818 " if (str.indexOf('TypeError') == -1) return 1;"
5819 " if (str.indexOf('[object Fun]') != -1) return 2;"
5820 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5821 " return 0;"
5822 " }"
5823 " return 4;"
5824 "}"
5825 "test();");
5826 CHECK_EQ(0, value->Int32Value());
5827}
5828
5829
5830THREADED_TEST(EvalAliasedDynamic) {
5831 v8::HandleScope scope;
5832 LocalContext current;
5833
5834 // Tests where aliased eval can only be resolved dynamically.
5835 Local<Script> script =
5836 Script::Compile(v8_str("function f(x) { "
5837 " var foo = 2;"
5838 " with (x) { return eval('foo'); }"
5839 "}"
5840 "foo = 0;"
5841 "result1 = f(new Object());"
5842 "result2 = f(this);"
5843 "var x = new Object();"
5844 "x.eval = function(x) { return 1; };"
5845 "result3 = f(x);"));
5846 script->Run();
5847 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5848 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5849 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5850
5851 v8::TryCatch try_catch;
5852 script =
5853 Script::Compile(v8_str("function f(x) { "
5854 " var bar = 2;"
5855 " with (x) { return eval('bar'); }"
5856 "}"
5857 "f(this)"));
5858 script->Run();
5859 CHECK(try_catch.HasCaught());
5860 try_catch.Reset();
5861}
5862
5863
5864THREADED_TEST(CrossEval) {
5865 v8::HandleScope scope;
5866 LocalContext other;
5867 LocalContext current;
5868
5869 Local<String> token = v8_str("<security token>");
5870 other->SetSecurityToken(token);
5871 current->SetSecurityToken(token);
5872
5873 // Setup reference from current to other.
5874 current->Global()->Set(v8_str("other"), other->Global());
5875
5876 // Check that new variables are introduced in other context.
5877 Local<Script> script =
5878 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5879 script->Run();
5880 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5881 CHECK_EQ(1234, foo->Int32Value());
5882 CHECK(!current->Global()->Has(v8_str("foo")));
5883
5884 // Check that writing to non-existing properties introduces them in
5885 // the other context.
5886 script =
5887 Script::Compile(v8_str("other.eval('na = 1234')"));
5888 script->Run();
5889 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5890 CHECK(!current->Global()->Has(v8_str("na")));
5891
5892 // Check that global variables in current context are not visible in other
5893 // context.
5894 v8::TryCatch try_catch;
5895 script =
5896 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5897 Local<Value> result = script->Run();
5898 CHECK(try_catch.HasCaught());
5899 try_catch.Reset();
5900
5901 // Check that local variables in current context are not visible in other
5902 // context.
5903 script =
5904 Script::Compile(v8_str("(function() { "
5905 " var baz = 87;"
5906 " return other.eval('baz');"
5907 "})();"));
5908 result = script->Run();
5909 CHECK(try_catch.HasCaught());
5910 try_catch.Reset();
5911
5912 // Check that global variables in the other environment are visible
5913 // when evaluting code.
5914 other->Global()->Set(v8_str("bis"), v8_num(1234));
5915 script = Script::Compile(v8_str("other.eval('bis')"));
5916 CHECK_EQ(1234, script->Run()->Int32Value());
5917 CHECK(!try_catch.HasCaught());
5918
5919 // Check that the 'this' pointer points to the global object evaluating
5920 // code.
5921 other->Global()->Set(v8_str("t"), other->Global());
5922 script = Script::Compile(v8_str("other.eval('this == t')"));
5923 result = script->Run();
5924 CHECK(result->IsTrue());
5925 CHECK(!try_catch.HasCaught());
5926
5927 // Check that variables introduced in with-statement are not visible in
5928 // other context.
5929 script =
5930 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
5931 result = script->Run();
5932 CHECK(try_catch.HasCaught());
5933 try_catch.Reset();
5934
5935 // Check that you cannot use 'eval.call' with another object than the
5936 // current global object.
5937 script =
5938 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
5939 result = script->Run();
5940 CHECK(try_catch.HasCaught());
5941}
5942
5943
5944// Test that calling eval in a context which has been detached from
5945// its global throws an exception. This behavior is consistent with
5946// other JavaScript implementations.
5947THREADED_TEST(EvalInDetachedGlobal) {
5948 v8::HandleScope scope;
5949
5950 v8::Persistent<Context> context0 = Context::New();
5951 v8::Persistent<Context> context1 = Context::New();
5952
5953 // Setup function in context0 that uses eval from context0.
5954 context0->Enter();
5955 v8::Handle<v8::Value> fun =
5956 CompileRun("var x = 42;"
5957 "(function() {"
5958 " var e = eval;"
5959 " return function(s) { return e(s); }"
5960 "})()");
5961 context0->Exit();
5962
5963 // Put the function into context1 and call it before and after
5964 // detaching the global. Before detaching, the call succeeds and
5965 // after detaching and exception is thrown.
5966 context1->Enter();
5967 context1->Global()->Set(v8_str("fun"), fun);
5968 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
5969 CHECK_EQ(42, x_value->Int32Value());
5970 context0->DetachGlobal();
5971 v8::TryCatch catcher;
5972 x_value = CompileRun("fun('x')");
5973 CHECK(x_value.IsEmpty());
5974 CHECK(catcher.HasCaught());
5975 context1->Exit();
5976
5977 context1.Dispose();
5978 context0.Dispose();
5979}
5980
5981
5982THREADED_TEST(CrossLazyLoad) {
5983 v8::HandleScope scope;
5984 LocalContext other;
5985 LocalContext current;
5986
5987 Local<String> token = v8_str("<security token>");
5988 other->SetSecurityToken(token);
5989 current->SetSecurityToken(token);
5990
5991 // Setup reference from current to other.
5992 current->Global()->Set(v8_str("other"), other->Global());
5993
5994 // Trigger lazy loading in other context.
5995 Local<Script> script =
5996 Script::Compile(v8_str("other.eval('new Date(42)')"));
5997 Local<Value> value = script->Run();
5998 CHECK_EQ(42.0, value->NumberValue());
5999}
6000
6001
6002static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00006003 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00006004 if (args.IsConstructCall()) {
6005 if (args[0]->IsInt32()) {
6006 return v8_num(-args[0]->Int32Value());
6007 }
6008 }
6009
6010 return args[0];
6011}
6012
6013
6014// Test that a call handler can be set for objects which will allow
6015// non-function objects created through the API to be called as
6016// functions.
6017THREADED_TEST(CallAsFunction) {
6018 v8::HandleScope scope;
6019 LocalContext context;
6020
6021 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6022 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6023 instance_template->SetCallAsFunctionHandler(call_as_function);
6024 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6025 context->Global()->Set(v8_str("obj"), instance);
6026 v8::TryCatch try_catch;
6027 Local<Value> value;
6028 CHECK(!try_catch.HasCaught());
6029
6030 value = CompileRun("obj(42)");
6031 CHECK(!try_catch.HasCaught());
6032 CHECK_EQ(42, value->Int32Value());
6033
6034 value = CompileRun("(function(o){return o(49)})(obj)");
6035 CHECK(!try_catch.HasCaught());
6036 CHECK_EQ(49, value->Int32Value());
6037
6038 // test special case of call as function
6039 value = CompileRun("[obj]['0'](45)");
6040 CHECK(!try_catch.HasCaught());
6041 CHECK_EQ(45, value->Int32Value());
6042
6043 value = CompileRun("obj.call = Function.prototype.call;"
6044 "obj.call(null, 87)");
6045 CHECK(!try_catch.HasCaught());
6046 CHECK_EQ(87, value->Int32Value());
6047
6048 // Regression tests for bug #1116356: Calling call through call/apply
6049 // must work for non-function receivers.
6050 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6051 value = CompileRun(apply_99);
6052 CHECK(!try_catch.HasCaught());
6053 CHECK_EQ(99, value->Int32Value());
6054
6055 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6056 value = CompileRun(call_17);
6057 CHECK(!try_catch.HasCaught());
6058 CHECK_EQ(17, value->Int32Value());
6059
6060 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006061 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006062 value = CompileRun("new obj(43)");
6063 CHECK(!try_catch.HasCaught());
6064 CHECK_EQ(-43, value->Int32Value());
6065}
6066
6067
6068static int CountHandles() {
6069 return v8::HandleScope::NumberOfHandles();
6070}
6071
6072
6073static int Recurse(int depth, int iterations) {
6074 v8::HandleScope scope;
6075 if (depth == 0) return CountHandles();
6076 for (int i = 0; i < iterations; i++) {
6077 Local<v8::Number> n = v8::Integer::New(42);
6078 }
6079 return Recurse(depth - 1, iterations);
6080}
6081
6082
6083THREADED_TEST(HandleIteration) {
6084 static const int kIterations = 500;
6085 static const int kNesting = 200;
6086 CHECK_EQ(0, CountHandles());
6087 {
6088 v8::HandleScope scope1;
6089 CHECK_EQ(0, CountHandles());
6090 for (int i = 0; i < kIterations; i++) {
6091 Local<v8::Number> n = v8::Integer::New(42);
6092 CHECK_EQ(i + 1, CountHandles());
6093 }
6094
6095 CHECK_EQ(kIterations, CountHandles());
6096 {
6097 v8::HandleScope scope2;
6098 for (int j = 0; j < kIterations; j++) {
6099 Local<v8::Number> n = v8::Integer::New(42);
6100 CHECK_EQ(j + 1 + kIterations, CountHandles());
6101 }
6102 }
6103 CHECK_EQ(kIterations, CountHandles());
6104 }
6105 CHECK_EQ(0, CountHandles());
6106 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6107}
6108
6109
6110static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6111 Local<String> name,
6112 const AccessorInfo& info) {
6113 ApiTestFuzzer::Fuzz();
6114 return v8::Handle<Value>();
6115}
6116
6117
6118THREADED_TEST(InterceptorHasOwnProperty) {
6119 v8::HandleScope scope;
6120 LocalContext context;
6121 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6122 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6123 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6124 Local<Function> function = fun_templ->GetFunction();
6125 context->Global()->Set(v8_str("constructor"), function);
6126 v8::Handle<Value> value = CompileRun(
6127 "var o = new constructor();"
6128 "o.hasOwnProperty('ostehaps');");
6129 CHECK_EQ(false, value->BooleanValue());
6130 value = CompileRun(
6131 "o.ostehaps = 42;"
6132 "o.hasOwnProperty('ostehaps');");
6133 CHECK_EQ(true, value->BooleanValue());
6134 value = CompileRun(
6135 "var p = new constructor();"
6136 "p.hasOwnProperty('ostehaps');");
6137 CHECK_EQ(false, value->BooleanValue());
6138}
6139
6140
6141static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6142 Local<String> name,
6143 const AccessorInfo& info) {
6144 ApiTestFuzzer::Fuzz();
6145 i::Heap::CollectAllGarbage(false);
6146 return v8::Handle<Value>();
6147}
6148
6149
6150THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6151 v8::HandleScope scope;
6152 LocalContext context;
6153 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6154 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6155 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6156 Local<Function> function = fun_templ->GetFunction();
6157 context->Global()->Set(v8_str("constructor"), function);
6158 // Let's first make some stuff so we can be sure to get a good GC.
6159 CompileRun(
6160 "function makestr(size) {"
6161 " switch (size) {"
6162 " case 1: return 'f';"
6163 " case 2: return 'fo';"
6164 " case 3: return 'foo';"
6165 " }"
6166 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6167 "}"
6168 "var x = makestr(12345);"
6169 "x = makestr(31415);"
6170 "x = makestr(23456);");
6171 v8::Handle<Value> value = CompileRun(
6172 "var o = new constructor();"
6173 "o.__proto__ = new String(x);"
6174 "o.hasOwnProperty('ostehaps');");
6175 CHECK_EQ(false, value->BooleanValue());
6176}
6177
6178
6179typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6180 const AccessorInfo& info);
6181
6182
6183static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6184 const char* source,
6185 int expected) {
6186 v8::HandleScope scope;
6187 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6188 templ->SetNamedPropertyHandler(getter);
6189 LocalContext context;
6190 context->Global()->Set(v8_str("o"), templ->NewInstance());
6191 v8::Handle<Value> value = CompileRun(source);
6192 CHECK_EQ(expected, value->Int32Value());
6193}
6194
6195
6196static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6197 const AccessorInfo& info) {
6198 ApiTestFuzzer::Fuzz();
6199 CHECK(v8_str("x")->Equals(name));
6200 return v8::Integer::New(42);
6201}
6202
6203
6204// This test should hit the load IC for the interceptor case.
6205THREADED_TEST(InterceptorLoadIC) {
6206 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6207 "var result = 0;"
6208 "for (var i = 0; i < 1000; i++) {"
6209 " result = o.x;"
6210 "}",
6211 42);
6212}
6213
6214
6215// Below go several tests which verify that JITing for various
6216// configurations of interceptor and explicit fields works fine
6217// (those cases are special cased to get better performance).
6218
6219static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6220 const AccessorInfo& info) {
6221 ApiTestFuzzer::Fuzz();
6222 return v8_str("x")->Equals(name)
6223 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6224}
6225
6226
6227THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6228 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6229 "var result = 0;"
6230 "o.y = 239;"
6231 "for (var i = 0; i < 1000; i++) {"
6232 " result = o.y;"
6233 "}",
6234 239);
6235}
6236
6237
6238THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6239 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6240 "var result = 0;"
6241 "o.__proto__ = { 'y': 239 };"
6242 "for (var i = 0; i < 1000; i++) {"
6243 " result = o.y + o.x;"
6244 "}",
6245 239 + 42);
6246}
6247
6248
6249THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6250 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6251 "var result = 0;"
6252 "o.__proto__.y = 239;"
6253 "for (var i = 0; i < 1000; i++) {"
6254 " result = o.y + o.x;"
6255 "}",
6256 239 + 42);
6257}
6258
6259
6260THREADED_TEST(InterceptorLoadICUndefined) {
6261 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6262 "var result = 0;"
6263 "for (var i = 0; i < 1000; i++) {"
6264 " result = (o.y == undefined) ? 239 : 42;"
6265 "}",
6266 239);
6267}
6268
6269
6270THREADED_TEST(InterceptorLoadICWithOverride) {
6271 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6272 "fst = new Object(); fst.__proto__ = o;"
6273 "snd = new Object(); snd.__proto__ = fst;"
6274 "var result1 = 0;"
6275 "for (var i = 0; i < 1000; i++) {"
6276 " result1 = snd.x;"
6277 "}"
6278 "fst.x = 239;"
6279 "var result = 0;"
6280 "for (var i = 0; i < 1000; i++) {"
6281 " result = snd.x;"
6282 "}"
6283 "result + result1",
6284 239 + 42);
6285}
6286
6287
6288// Test the case when we stored field into
6289// a stub, but interceptor produced value on its own.
6290THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6291 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6292 "proto = new Object();"
6293 "o.__proto__ = proto;"
6294 "proto.x = 239;"
6295 "for (var i = 0; i < 1000; i++) {"
6296 " o.x;"
6297 // Now it should be ICed and keep a reference to x defined on proto
6298 "}"
6299 "var result = 0;"
6300 "for (var i = 0; i < 1000; i++) {"
6301 " result += o.x;"
6302 "}"
6303 "result;",
6304 42 * 1000);
6305}
6306
6307
6308// Test the case when we stored field into
6309// a stub, but it got invalidated later on.
6310THREADED_TEST(InterceptorLoadICInvalidatedField) {
6311 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6312 "proto1 = new Object();"
6313 "proto2 = new Object();"
6314 "o.__proto__ = proto1;"
6315 "proto1.__proto__ = proto2;"
6316 "proto2.y = 239;"
6317 "for (var i = 0; i < 1000; i++) {"
6318 " o.y;"
6319 // Now it should be ICed and keep a reference to y defined on proto2
6320 "}"
6321 "proto1.y = 42;"
6322 "var result = 0;"
6323 "for (var i = 0; i < 1000; i++) {"
6324 " result += o.y;"
6325 "}"
6326 "result;",
6327 42 * 1000);
6328}
6329
6330
Steve Block6ded16b2010-05-10 14:33:55 +01006331static int interceptor_load_not_handled_calls = 0;
6332static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6333 const AccessorInfo& info) {
6334 ++interceptor_load_not_handled_calls;
6335 return v8::Handle<v8::Value>();
6336}
6337
6338
6339// Test how post-interceptor lookups are done in the non-cacheable
6340// case: the interceptor should not be invoked during this lookup.
6341THREADED_TEST(InterceptorLoadICPostInterceptor) {
6342 interceptor_load_not_handled_calls = 0;
6343 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6344 "receiver = new Object();"
6345 "receiver.__proto__ = o;"
6346 "proto = new Object();"
6347 "/* Make proto a slow-case object. */"
6348 "for (var i = 0; i < 1000; i++) {"
6349 " proto[\"xxxxxxxx\" + i] = [];"
6350 "}"
6351 "proto.x = 17;"
6352 "o.__proto__ = proto;"
6353 "var result = 0;"
6354 "for (var i = 0; i < 1000; i++) {"
6355 " result += receiver.x;"
6356 "}"
6357 "result;",
6358 17 * 1000);
6359 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6360}
6361
6362
Steve Blocka7e24c12009-10-30 11:49:00 +00006363// Test the case when we stored field into
6364// a stub, but it got invalidated later on due to override on
6365// global object which is between interceptor and fields' holders.
6366THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6367 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6368 "o.__proto__ = this;" // set a global to be a proto of o.
6369 "this.__proto__.y = 239;"
6370 "for (var i = 0; i < 10; i++) {"
6371 " if (o.y != 239) throw 'oops: ' + o.y;"
6372 // Now it should be ICed and keep a reference to y defined on field_holder.
6373 "}"
6374 "this.y = 42;" // Assign on a global.
6375 "var result = 0;"
6376 "for (var i = 0; i < 10; i++) {"
6377 " result += o.y;"
6378 "}"
6379 "result;",
6380 42 * 10);
6381}
6382
6383
6384static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
6385 ApiTestFuzzer::Fuzz();
6386 return v8_num(239);
6387}
6388
6389
6390static void SetOnThis(Local<String> name,
6391 Local<Value> value,
6392 const AccessorInfo& info) {
6393 info.This()->ForceSet(name, value);
6394}
6395
6396
6397THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6398 v8::HandleScope scope;
6399 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6400 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6401 templ->SetAccessor(v8_str("y"), Return239);
6402 LocalContext context;
6403 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006404
6405 // Check the case when receiver and interceptor's holder
6406 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006407 v8::Handle<Value> value = CompileRun(
6408 "var result = 0;"
6409 "for (var i = 0; i < 7; i++) {"
6410 " result = o.y;"
6411 "}");
6412 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006413
6414 // Check the case when interceptor's holder is in proto chain
6415 // of receiver.
6416 value = CompileRun(
6417 "r = { __proto__: o };"
6418 "var result = 0;"
6419 "for (var i = 0; i < 7; i++) {"
6420 " result = r.y;"
6421 "}");
6422 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006423}
6424
6425
6426THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6427 v8::HandleScope scope;
6428 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6429 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6430 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6431 templ_p->SetAccessor(v8_str("y"), Return239);
6432
6433 LocalContext context;
6434 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6435 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6436
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006437 // Check the case when receiver and interceptor's holder
6438 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006439 v8::Handle<Value> value = CompileRun(
6440 "o.__proto__ = p;"
6441 "var result = 0;"
6442 "for (var i = 0; i < 7; i++) {"
6443 " result = o.x + o.y;"
6444 "}");
6445 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006446
6447 // Check the case when interceptor's holder is in proto chain
6448 // of receiver.
6449 value = CompileRun(
6450 "r = { __proto__: o };"
6451 "var result = 0;"
6452 "for (var i = 0; i < 7; i++) {"
6453 " result = r.x + r.y;"
6454 "}");
6455 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006456}
6457
6458
6459THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6460 v8::HandleScope scope;
6461 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6462 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6463 templ->SetAccessor(v8_str("y"), Return239);
6464
6465 LocalContext context;
6466 context->Global()->Set(v8_str("o"), templ->NewInstance());
6467
6468 v8::Handle<Value> value = CompileRun(
6469 "fst = new Object(); fst.__proto__ = o;"
6470 "snd = new Object(); snd.__proto__ = fst;"
6471 "var result1 = 0;"
6472 "for (var i = 0; i < 7; i++) {"
6473 " result1 = snd.x;"
6474 "}"
6475 "fst.x = 239;"
6476 "var result = 0;"
6477 "for (var i = 0; i < 7; i++) {"
6478 " result = snd.x;"
6479 "}"
6480 "result + result1");
6481 CHECK_EQ(239 + 42, value->Int32Value());
6482}
6483
6484
6485// Test the case when we stored callback into
6486// a stub, but interceptor produced value on its own.
6487THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6488 v8::HandleScope scope;
6489 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6490 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6491 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6492 templ_p->SetAccessor(v8_str("y"), Return239);
6493
6494 LocalContext context;
6495 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6496 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6497
6498 v8::Handle<Value> value = CompileRun(
6499 "o.__proto__ = p;"
6500 "for (var i = 0; i < 7; i++) {"
6501 " o.x;"
6502 // Now it should be ICed and keep a reference to x defined on p
6503 "}"
6504 "var result = 0;"
6505 "for (var i = 0; i < 7; i++) {"
6506 " result += o.x;"
6507 "}"
6508 "result");
6509 CHECK_EQ(42 * 7, value->Int32Value());
6510}
6511
6512
6513// Test the case when we stored callback into
6514// a stub, but it got invalidated later on.
6515THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6516 v8::HandleScope scope;
6517 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6518 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6519 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6520 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6521
6522 LocalContext context;
6523 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6524 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6525
6526 v8::Handle<Value> value = CompileRun(
6527 "inbetween = new Object();"
6528 "o.__proto__ = inbetween;"
6529 "inbetween.__proto__ = p;"
6530 "for (var i = 0; i < 10; i++) {"
6531 " o.y;"
6532 // Now it should be ICed and keep a reference to y defined on p
6533 "}"
6534 "inbetween.y = 42;"
6535 "var result = 0;"
6536 "for (var i = 0; i < 10; i++) {"
6537 " result += o.y;"
6538 "}"
6539 "result");
6540 CHECK_EQ(42 * 10, value->Int32Value());
6541}
6542
6543
6544// Test the case when we stored callback into
6545// a stub, but it got invalidated later on due to override on
6546// global object which is between interceptor and callbacks' holders.
6547THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6548 v8::HandleScope scope;
6549 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6550 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6551 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6552 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6553
6554 LocalContext context;
6555 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6556 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6557
6558 v8::Handle<Value> value = CompileRun(
6559 "o.__proto__ = this;"
6560 "this.__proto__ = p;"
6561 "for (var i = 0; i < 10; i++) {"
6562 " if (o.y != 239) throw 'oops: ' + o.y;"
6563 // Now it should be ICed and keep a reference to y defined on p
6564 "}"
6565 "this.y = 42;"
6566 "var result = 0;"
6567 "for (var i = 0; i < 10; i++) {"
6568 " result += o.y;"
6569 "}"
6570 "result");
6571 CHECK_EQ(42 * 10, value->Int32Value());
6572}
6573
6574
6575static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6576 const AccessorInfo& info) {
6577 ApiTestFuzzer::Fuzz();
6578 CHECK(v8_str("x")->Equals(name));
6579 return v8::Integer::New(0);
6580}
6581
6582
6583THREADED_TEST(InterceptorReturningZero) {
6584 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6585 "o.x == undefined ? 1 : 0",
6586 0);
6587}
6588
6589
6590static v8::Handle<Value> InterceptorStoreICSetter(
6591 Local<String> key, Local<Value> value, const AccessorInfo&) {
6592 CHECK(v8_str("x")->Equals(key));
6593 CHECK_EQ(42, value->Int32Value());
6594 return value;
6595}
6596
6597
6598// This test should hit the store IC for the interceptor case.
6599THREADED_TEST(InterceptorStoreIC) {
6600 v8::HandleScope scope;
6601 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6602 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6603 InterceptorStoreICSetter);
6604 LocalContext context;
6605 context->Global()->Set(v8_str("o"), templ->NewInstance());
6606 v8::Handle<Value> value = CompileRun(
6607 "for (var i = 0; i < 1000; i++) {"
6608 " o.x = 42;"
6609 "}");
6610}
6611
6612
6613THREADED_TEST(InterceptorStoreICWithNoSetter) {
6614 v8::HandleScope scope;
6615 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6616 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6617 LocalContext context;
6618 context->Global()->Set(v8_str("o"), templ->NewInstance());
6619 v8::Handle<Value> value = CompileRun(
6620 "for (var i = 0; i < 1000; i++) {"
6621 " o.y = 239;"
6622 "}"
6623 "42 + o.y");
6624 CHECK_EQ(239 + 42, value->Int32Value());
6625}
6626
6627
6628
6629
6630v8::Handle<Value> call_ic_function;
6631v8::Handle<Value> call_ic_function2;
6632v8::Handle<Value> call_ic_function3;
6633
6634static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6635 const AccessorInfo& info) {
6636 ApiTestFuzzer::Fuzz();
6637 CHECK(v8_str("x")->Equals(name));
6638 return call_ic_function;
6639}
6640
6641
6642// This test should hit the call IC for the interceptor case.
6643THREADED_TEST(InterceptorCallIC) {
6644 v8::HandleScope scope;
6645 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6646 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6647 LocalContext context;
6648 context->Global()->Set(v8_str("o"), templ->NewInstance());
6649 call_ic_function =
6650 v8_compile("function f(x) { return x + 1; }; f")->Run();
6651 v8::Handle<Value> value = CompileRun(
6652 "var result = 0;"
6653 "for (var i = 0; i < 1000; i++) {"
6654 " result = o.x(41);"
6655 "}");
6656 CHECK_EQ(42, value->Int32Value());
6657}
6658
6659
6660// This test checks that if interceptor doesn't provide
6661// a value, we can fetch regular value.
6662THREADED_TEST(InterceptorCallICSeesOthers) {
6663 v8::HandleScope scope;
6664 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6665 templ->SetNamedPropertyHandler(NoBlockGetterX);
6666 LocalContext context;
6667 context->Global()->Set(v8_str("o"), templ->NewInstance());
6668 v8::Handle<Value> value = CompileRun(
6669 "o.x = function f(x) { return x + 1; };"
6670 "var result = 0;"
6671 "for (var i = 0; i < 7; i++) {"
6672 " result = o.x(41);"
6673 "}");
6674 CHECK_EQ(42, value->Int32Value());
6675}
6676
6677
6678static v8::Handle<Value> call_ic_function4;
6679static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6680 const AccessorInfo& info) {
6681 ApiTestFuzzer::Fuzz();
6682 CHECK(v8_str("x")->Equals(name));
6683 return call_ic_function4;
6684}
6685
6686
6687// This test checks that if interceptor provides a function,
6688// even if we cached shadowed variant, interceptor's function
6689// is invoked
6690THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6691 v8::HandleScope scope;
6692 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6693 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6694 LocalContext context;
6695 context->Global()->Set(v8_str("o"), templ->NewInstance());
6696 call_ic_function4 =
6697 v8_compile("function f(x) { return x - 1; }; f")->Run();
6698 v8::Handle<Value> value = CompileRun(
6699 "o.__proto__.x = function(x) { return x + 1; };"
6700 "var result = 0;"
6701 "for (var i = 0; i < 1000; i++) {"
6702 " result = o.x(42);"
6703 "}");
6704 CHECK_EQ(41, value->Int32Value());
6705}
6706
6707
6708// Test the case when we stored cacheable lookup into
6709// a stub, but it got invalidated later on
6710THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6711 v8::HandleScope scope;
6712 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6713 templ->SetNamedPropertyHandler(NoBlockGetterX);
6714 LocalContext context;
6715 context->Global()->Set(v8_str("o"), templ->NewInstance());
6716 v8::Handle<Value> value = CompileRun(
6717 "proto1 = new Object();"
6718 "proto2 = new Object();"
6719 "o.__proto__ = proto1;"
6720 "proto1.__proto__ = proto2;"
6721 "proto2.y = function(x) { return x + 1; };"
6722 // Invoke it many times to compile a stub
6723 "for (var i = 0; i < 7; i++) {"
6724 " o.y(42);"
6725 "}"
6726 "proto1.y = function(x) { return x - 1; };"
6727 "var result = 0;"
6728 "for (var i = 0; i < 7; i++) {"
6729 " result += o.y(42);"
6730 "}");
6731 CHECK_EQ(41 * 7, value->Int32Value());
6732}
6733
6734
6735static v8::Handle<Value> call_ic_function5;
6736static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6737 const AccessorInfo& info) {
6738 ApiTestFuzzer::Fuzz();
6739 if (v8_str("x")->Equals(name))
6740 return call_ic_function5;
6741 else
6742 return Local<Value>();
6743}
6744
6745
6746// This test checks that if interceptor doesn't provide a function,
6747// cached constant function is used
6748THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6749 v8::HandleScope scope;
6750 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6751 templ->SetNamedPropertyHandler(NoBlockGetterX);
6752 LocalContext context;
6753 context->Global()->Set(v8_str("o"), templ->NewInstance());
6754 v8::Handle<Value> value = CompileRun(
6755 "function inc(x) { return x + 1; };"
6756 "inc(1);"
6757 "o.x = inc;"
6758 "var result = 0;"
6759 "for (var i = 0; i < 1000; i++) {"
6760 " result = o.x(42);"
6761 "}");
6762 CHECK_EQ(43, value->Int32Value());
6763}
6764
6765
6766// This test checks that if interceptor provides a function,
6767// even if we cached constant function, interceptor's function
6768// is invoked
6769THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6770 v8::HandleScope scope;
6771 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6772 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6773 LocalContext context;
6774 context->Global()->Set(v8_str("o"), templ->NewInstance());
6775 call_ic_function5 =
6776 v8_compile("function f(x) { return x - 1; }; f")->Run();
6777 v8::Handle<Value> value = CompileRun(
6778 "function inc(x) { return x + 1; };"
6779 "inc(1);"
6780 "o.x = inc;"
6781 "var result = 0;"
6782 "for (var i = 0; i < 1000; i++) {"
6783 " result = o.x(42);"
6784 "}");
6785 CHECK_EQ(41, value->Int32Value());
6786}
6787
6788
6789// Test the case when we stored constant function into
6790// a stub, but it got invalidated later on
6791THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6792 v8::HandleScope scope;
6793 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6794 templ->SetNamedPropertyHandler(NoBlockGetterX);
6795 LocalContext context;
6796 context->Global()->Set(v8_str("o"), templ->NewInstance());
6797 v8::Handle<Value> value = CompileRun(
6798 "function inc(x) { return x + 1; };"
6799 "inc(1);"
6800 "proto1 = new Object();"
6801 "proto2 = new Object();"
6802 "o.__proto__ = proto1;"
6803 "proto1.__proto__ = proto2;"
6804 "proto2.y = inc;"
6805 // Invoke it many times to compile a stub
6806 "for (var i = 0; i < 7; i++) {"
6807 " o.y(42);"
6808 "}"
6809 "proto1.y = function(x) { return x - 1; };"
6810 "var result = 0;"
6811 "for (var i = 0; i < 7; i++) {"
6812 " result += o.y(42);"
6813 "}");
6814 CHECK_EQ(41 * 7, value->Int32Value());
6815}
6816
6817
6818// Test the case when we stored constant function into
6819// a stub, but it got invalidated later on due to override on
6820// global object which is between interceptor and constant function' holders.
6821THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6822 v8::HandleScope scope;
6823 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6824 templ->SetNamedPropertyHandler(NoBlockGetterX);
6825 LocalContext context;
6826 context->Global()->Set(v8_str("o"), templ->NewInstance());
6827 v8::Handle<Value> value = CompileRun(
6828 "function inc(x) { return x + 1; };"
6829 "inc(1);"
6830 "o.__proto__ = this;"
6831 "this.__proto__.y = inc;"
6832 // Invoke it many times to compile a stub
6833 "for (var i = 0; i < 7; i++) {"
6834 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6835 "}"
6836 "this.y = function(x) { return x - 1; };"
6837 "var result = 0;"
6838 "for (var i = 0; i < 7; i++) {"
6839 " result += o.y(42);"
6840 "}");
6841 CHECK_EQ(41 * 7, value->Int32Value());
6842}
6843
6844
Leon Clarke4515c472010-02-03 11:58:03 +00006845// Test the case when actual function to call sits on global object.
6846THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6847 v8::HandleScope scope;
6848 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6849 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6850
6851 LocalContext context;
6852 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6853
6854 v8::Handle<Value> value = CompileRun(
6855 "try {"
6856 " o.__proto__ = this;"
6857 " for (var i = 0; i < 10; i++) {"
6858 " var v = o.parseFloat('239');"
6859 " if (v != 239) throw v;"
6860 // Now it should be ICed and keep a reference to parseFloat.
6861 " }"
6862 " var result = 0;"
6863 " for (var i = 0; i < 10; i++) {"
6864 " result += o.parseFloat('239');"
6865 " }"
6866 " result"
6867 "} catch(e) {"
6868 " e"
6869 "};");
6870 CHECK_EQ(239 * 10, value->Int32Value());
6871}
6872
Andrei Popescu402d9372010-02-26 13:31:12 +00006873static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6874 const AccessorInfo& info) {
6875 ApiTestFuzzer::Fuzz();
6876 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6877 ++(*call_count);
6878 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01006879 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00006880 }
6881 return v8::Handle<Value>();
6882}
6883
6884static v8::Handle<Value> FastApiCallback_TrivialSignature(
6885 const v8::Arguments& args) {
6886 ApiTestFuzzer::Fuzz();
6887 CHECK_EQ(args.This(), args.Holder());
6888 CHECK(args.Data()->Equals(v8_str("method_data")));
6889 return v8::Integer::New(args[0]->Int32Value() + 1);
6890}
6891
6892static v8::Handle<Value> FastApiCallback_SimpleSignature(
6893 const v8::Arguments& args) {
6894 ApiTestFuzzer::Fuzz();
6895 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6896 CHECK(args.Data()->Equals(v8_str("method_data")));
6897 // Note, we're using HasRealNamedProperty instead of Has to avoid
6898 // invoking the interceptor again.
6899 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6900 return v8::Integer::New(args[0]->Int32Value() + 1);
6901}
6902
6903// Helper to maximize the odds of object moving.
6904static void GenerateSomeGarbage() {
6905 CompileRun(
6906 "var garbage;"
6907 "for (var i = 0; i < 1000; i++) {"
6908 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6909 "}"
6910 "garbage = undefined;");
6911}
6912
6913THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
6914 int interceptor_call_count = 0;
6915 v8::HandleScope scope;
6916 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6917 v8::Handle<v8::FunctionTemplate> method_templ =
6918 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6919 v8_str("method_data"),
6920 v8::Handle<v8::Signature>());
6921 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6922 proto_templ->Set(v8_str("method"), method_templ);
6923 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6924 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6925 NULL, NULL, NULL, NULL,
6926 v8::External::Wrap(&interceptor_call_count));
6927 LocalContext context;
6928 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6929 GenerateSomeGarbage();
6930 context->Global()->Set(v8_str("o"), fun->NewInstance());
6931 v8::Handle<Value> value = CompileRun(
6932 "var result = 0;"
6933 "for (var i = 0; i < 100; i++) {"
6934 " result = o.method(41);"
6935 "}");
6936 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6937 CHECK_EQ(100, interceptor_call_count);
6938}
6939
6940THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
6941 int interceptor_call_count = 0;
6942 v8::HandleScope scope;
6943 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6944 v8::Handle<v8::FunctionTemplate> method_templ =
6945 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6946 v8_str("method_data"),
6947 v8::Signature::New(fun_templ));
6948 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6949 proto_templ->Set(v8_str("method"), method_templ);
6950 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6951 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6952 NULL, NULL, NULL, NULL,
6953 v8::External::Wrap(&interceptor_call_count));
6954 LocalContext context;
6955 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6956 GenerateSomeGarbage();
6957 context->Global()->Set(v8_str("o"), fun->NewInstance());
6958 v8::Handle<Value> value = CompileRun(
6959 "o.foo = 17;"
6960 "var receiver = {};"
6961 "receiver.__proto__ = o;"
6962 "var result = 0;"
6963 "for (var i = 0; i < 100; i++) {"
6964 " result = receiver.method(41);"
6965 "}");
6966 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6967 CHECK_EQ(100, interceptor_call_count);
6968}
6969
6970THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
6971 int interceptor_call_count = 0;
6972 v8::HandleScope scope;
6973 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6974 v8::Handle<v8::FunctionTemplate> method_templ =
6975 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6976 v8_str("method_data"),
6977 v8::Signature::New(fun_templ));
6978 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6979 proto_templ->Set(v8_str("method"), method_templ);
6980 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6981 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6982 NULL, NULL, NULL, NULL,
6983 v8::External::Wrap(&interceptor_call_count));
6984 LocalContext context;
6985 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6986 GenerateSomeGarbage();
6987 context->Global()->Set(v8_str("o"), fun->NewInstance());
6988 v8::Handle<Value> value = CompileRun(
6989 "o.foo = 17;"
6990 "var receiver = {};"
6991 "receiver.__proto__ = o;"
6992 "var result = 0;"
6993 "var saved_result = 0;"
6994 "for (var i = 0; i < 100; i++) {"
6995 " result = receiver.method(41);"
6996 " if (i == 50) {"
6997 " saved_result = result;"
6998 " receiver = {method: function(x) { return x - 1 }};"
6999 " }"
7000 "}");
7001 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7002 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7003 CHECK_GE(interceptor_call_count, 50);
7004}
7005
7006THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
7007 int interceptor_call_count = 0;
7008 v8::HandleScope scope;
7009 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7010 v8::Handle<v8::FunctionTemplate> method_templ =
7011 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7012 v8_str("method_data"),
7013 v8::Signature::New(fun_templ));
7014 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7015 proto_templ->Set(v8_str("method"), method_templ);
7016 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7017 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7018 NULL, NULL, NULL, NULL,
7019 v8::External::Wrap(&interceptor_call_count));
7020 LocalContext context;
7021 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7022 GenerateSomeGarbage();
7023 context->Global()->Set(v8_str("o"), fun->NewInstance());
7024 v8::Handle<Value> value = CompileRun(
7025 "o.foo = 17;"
7026 "var receiver = {};"
7027 "receiver.__proto__ = o;"
7028 "var result = 0;"
7029 "var saved_result = 0;"
7030 "for (var i = 0; i < 100; i++) {"
7031 " result = receiver.method(41);"
7032 " if (i == 50) {"
7033 " saved_result = result;"
7034 " o.method = function(x) { return x - 1 };"
7035 " }"
7036 "}");
7037 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7038 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7039 CHECK_GE(interceptor_call_count, 50);
7040}
7041
Steve Block6ded16b2010-05-10 14:33:55 +01007042THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7043 int interceptor_call_count = 0;
7044 v8::HandleScope scope;
7045 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7046 v8::Handle<v8::FunctionTemplate> method_templ =
7047 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7048 v8_str("method_data"),
7049 v8::Signature::New(fun_templ));
7050 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7051 proto_templ->Set(v8_str("method"), method_templ);
7052 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7053 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7054 NULL, NULL, NULL, NULL,
7055 v8::External::Wrap(&interceptor_call_count));
7056 LocalContext context;
7057 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7058 GenerateSomeGarbage();
7059 context->Global()->Set(v8_str("o"), fun->NewInstance());
7060 v8::TryCatch try_catch;
7061 v8::Handle<Value> value = CompileRun(
7062 "o.foo = 17;"
7063 "var receiver = {};"
7064 "receiver.__proto__ = o;"
7065 "var result = 0;"
7066 "var saved_result = 0;"
7067 "for (var i = 0; i < 100; i++) {"
7068 " result = receiver.method(41);"
7069 " if (i == 50) {"
7070 " saved_result = result;"
7071 " receiver = 333;"
7072 " }"
7073 "}");
7074 CHECK(try_catch.HasCaught());
7075 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7076 try_catch.Exception()->ToString());
7077 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7078 CHECK_GE(interceptor_call_count, 50);
7079}
7080
Andrei Popescu402d9372010-02-26 13:31:12 +00007081THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
7082 int interceptor_call_count = 0;
7083 v8::HandleScope scope;
7084 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7085 v8::Handle<v8::FunctionTemplate> method_templ =
7086 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7087 v8_str("method_data"),
7088 v8::Signature::New(fun_templ));
7089 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7090 proto_templ->Set(v8_str("method"), method_templ);
7091 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7092 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7093 NULL, NULL, NULL, NULL,
7094 v8::External::Wrap(&interceptor_call_count));
7095 LocalContext context;
7096 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7097 GenerateSomeGarbage();
7098 context->Global()->Set(v8_str("o"), fun->NewInstance());
7099 v8::TryCatch try_catch;
7100 v8::Handle<Value> value = CompileRun(
7101 "o.foo = 17;"
7102 "var receiver = {};"
7103 "receiver.__proto__ = o;"
7104 "var result = 0;"
7105 "var saved_result = 0;"
7106 "for (var i = 0; i < 100; i++) {"
7107 " result = receiver.method(41);"
7108 " if (i == 50) {"
7109 " saved_result = result;"
7110 " receiver = {method: receiver.method};"
7111 " }"
7112 "}");
7113 CHECK(try_catch.HasCaught());
7114 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7115 try_catch.Exception()->ToString());
7116 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7117 CHECK_GE(interceptor_call_count, 50);
7118}
7119
7120THREADED_TEST(CallICFastApi_TrivialSignature) {
7121 v8::HandleScope scope;
7122 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7123 v8::Handle<v8::FunctionTemplate> method_templ =
7124 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7125 v8_str("method_data"),
7126 v8::Handle<v8::Signature>());
7127 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7128 proto_templ->Set(v8_str("method"), method_templ);
7129 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7130 LocalContext context;
7131 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7132 GenerateSomeGarbage();
7133 context->Global()->Set(v8_str("o"), fun->NewInstance());
7134 v8::Handle<Value> value = CompileRun(
7135 "var result = 0;"
7136 "for (var i = 0; i < 100; i++) {"
7137 " result = o.method(41);"
7138 "}");
7139
7140 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7141}
7142
7143THREADED_TEST(CallICFastApi_SimpleSignature) {
7144 v8::HandleScope scope;
7145 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7146 v8::Handle<v8::FunctionTemplate> method_templ =
7147 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7148 v8_str("method_data"),
7149 v8::Signature::New(fun_templ));
7150 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7151 proto_templ->Set(v8_str("method"), method_templ);
7152 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7153 LocalContext context;
7154 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7155 GenerateSomeGarbage();
7156 context->Global()->Set(v8_str("o"), fun->NewInstance());
7157 v8::Handle<Value> value = CompileRun(
7158 "o.foo = 17;"
7159 "var receiver = {};"
7160 "receiver.__proto__ = o;"
7161 "var result = 0;"
7162 "for (var i = 0; i < 100; i++) {"
7163 " result = receiver.method(41);"
7164 "}");
7165
7166 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7167}
7168
Steve Block6ded16b2010-05-10 14:33:55 +01007169THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007170 v8::HandleScope scope;
7171 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7172 v8::Handle<v8::FunctionTemplate> method_templ =
7173 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7174 v8_str("method_data"),
7175 v8::Signature::New(fun_templ));
7176 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7177 proto_templ->Set(v8_str("method"), method_templ);
7178 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7179 LocalContext context;
7180 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7181 GenerateSomeGarbage();
7182 context->Global()->Set(v8_str("o"), fun->NewInstance());
7183 v8::Handle<Value> value = CompileRun(
7184 "o.foo = 17;"
7185 "var receiver = {};"
7186 "receiver.__proto__ = o;"
7187 "var result = 0;"
7188 "var saved_result = 0;"
7189 "for (var i = 0; i < 100; i++) {"
7190 " result = receiver.method(41);"
7191 " if (i == 50) {"
7192 " saved_result = result;"
7193 " receiver = {method: function(x) { return x - 1 }};"
7194 " }"
7195 "}");
7196 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7197 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7198}
7199
Steve Block6ded16b2010-05-10 14:33:55 +01007200THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7201 v8::HandleScope scope;
7202 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7203 v8::Handle<v8::FunctionTemplate> method_templ =
7204 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7205 v8_str("method_data"),
7206 v8::Signature::New(fun_templ));
7207 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7208 proto_templ->Set(v8_str("method"), method_templ);
7209 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7210 LocalContext context;
7211 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7212 GenerateSomeGarbage();
7213 context->Global()->Set(v8_str("o"), fun->NewInstance());
7214 v8::TryCatch try_catch;
7215 v8::Handle<Value> value = CompileRun(
7216 "o.foo = 17;"
7217 "var receiver = {};"
7218 "receiver.__proto__ = o;"
7219 "var result = 0;"
7220 "var saved_result = 0;"
7221 "for (var i = 0; i < 100; i++) {"
7222 " result = receiver.method(41);"
7223 " if (i == 50) {"
7224 " saved_result = result;"
7225 " receiver = 333;"
7226 " }"
7227 "}");
7228 CHECK(try_catch.HasCaught());
7229 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7230 try_catch.Exception()->ToString());
7231 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7232}
7233
Leon Clarke4515c472010-02-03 11:58:03 +00007234
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007235v8::Handle<Value> keyed_call_ic_function;
7236
7237static v8::Handle<Value> InterceptorKeyedCallICGetter(
7238 Local<String> name, const AccessorInfo& info) {
7239 ApiTestFuzzer::Fuzz();
7240 if (v8_str("x")->Equals(name)) {
7241 return keyed_call_ic_function;
7242 }
7243 return v8::Handle<Value>();
7244}
7245
7246
7247// Test the case when we stored cacheable lookup into
7248// a stub, but the function name changed (to another cacheable function).
7249THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7250 v8::HandleScope scope;
7251 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7252 templ->SetNamedPropertyHandler(NoBlockGetterX);
7253 LocalContext context;
7254 context->Global()->Set(v8_str("o"), templ->NewInstance());
7255 v8::Handle<Value> value = CompileRun(
7256 "proto = new Object();"
7257 "proto.y = function(x) { return x + 1; };"
7258 "proto.z = function(x) { return x - 1; };"
7259 "o.__proto__ = proto;"
7260 "var result = 0;"
7261 "var method = 'y';"
7262 "for (var i = 0; i < 10; i++) {"
7263 " if (i == 5) { method = 'z'; };"
7264 " result += o[method](41);"
7265 "}");
7266 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7267}
7268
7269
7270// Test the case when we stored cacheable lookup into
7271// a stub, but the function name changed (and the new function is present
7272// both before and after the interceptor in the prototype chain).
7273THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7274 v8::HandleScope scope;
7275 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7276 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7277 LocalContext context;
7278 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7279 keyed_call_ic_function =
7280 v8_compile("function f(x) { return x - 1; }; f")->Run();
7281 v8::Handle<Value> value = CompileRun(
7282 "o = new Object();"
7283 "proto2 = new Object();"
7284 "o.y = function(x) { return x + 1; };"
7285 "proto2.y = function(x) { return x + 2; };"
7286 "o.__proto__ = proto1;"
7287 "proto1.__proto__ = proto2;"
7288 "var result = 0;"
7289 "var method = 'x';"
7290 "for (var i = 0; i < 10; i++) {"
7291 " if (i == 5) { method = 'y'; };"
7292 " result += o[method](41);"
7293 "}");
7294 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7295}
7296
7297
7298// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7299// on the global object.
7300THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7301 v8::HandleScope scope;
7302 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7303 templ->SetNamedPropertyHandler(NoBlockGetterX);
7304 LocalContext context;
7305 context->Global()->Set(v8_str("o"), templ->NewInstance());
7306 v8::Handle<Value> value = CompileRun(
7307 "function inc(x) { return x + 1; };"
7308 "inc(1);"
7309 "function dec(x) { return x - 1; };"
7310 "dec(1);"
7311 "o.__proto__ = this;"
7312 "this.__proto__.x = inc;"
7313 "this.__proto__.y = dec;"
7314 "var result = 0;"
7315 "var method = 'x';"
7316 "for (var i = 0; i < 10; i++) {"
7317 " if (i == 5) { method = 'y'; };"
7318 " result += o[method](41);"
7319 "}");
7320 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7321}
7322
7323
7324// Test the case when actual function to call sits on global object.
7325THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7326 v8::HandleScope scope;
7327 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7328 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7329 LocalContext context;
7330 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7331
7332 v8::Handle<Value> value = CompileRun(
7333 "function len(x) { return x.length; };"
7334 "o.__proto__ = this;"
7335 "var m = 'parseFloat';"
7336 "var result = 0;"
7337 "for (var i = 0; i < 10; i++) {"
7338 " if (i == 5) {"
7339 " m = 'len';"
7340 " saved_result = result;"
7341 " };"
7342 " result = o[m]('239');"
7343 "}");
7344 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7345 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7346}
7347
7348// Test the map transition before the interceptor.
7349THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7350 v8::HandleScope scope;
7351 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7352 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7353 LocalContext context;
7354 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7355
7356 v8::Handle<Value> value = CompileRun(
7357 "var o = new Object();"
7358 "o.__proto__ = proto;"
7359 "o.method = function(x) { return x + 1; };"
7360 "var m = 'method';"
7361 "var result = 0;"
7362 "for (var i = 0; i < 10; i++) {"
7363 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7364 " result += o[m](41);"
7365 "}");
7366 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7367}
7368
7369
7370// Test the map transition after the interceptor.
7371THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7372 v8::HandleScope scope;
7373 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7374 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7375 LocalContext context;
7376 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7377
7378 v8::Handle<Value> value = CompileRun(
7379 "var proto = new Object();"
7380 "o.__proto__ = proto;"
7381 "proto.method = function(x) { return x + 1; };"
7382 "var m = 'method';"
7383 "var result = 0;"
7384 "for (var i = 0; i < 10; i++) {"
7385 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7386 " result += o[m](41);"
7387 "}");
7388 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7389}
7390
7391
Steve Blocka7e24c12009-10-30 11:49:00 +00007392static int interceptor_call_count = 0;
7393
7394static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7395 const AccessorInfo& info) {
7396 ApiTestFuzzer::Fuzz();
7397 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7398 return call_ic_function2;
7399 }
7400 return v8::Handle<Value>();
7401}
7402
7403
7404// This test should hit load and call ICs for the interceptor case.
7405// Once in a while, the interceptor will reply that a property was not
7406// found in which case we should get a reference error.
7407THREADED_TEST(InterceptorICReferenceErrors) {
7408 v8::HandleScope scope;
7409 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7410 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7411 LocalContext context(0, templ, v8::Handle<Value>());
7412 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7413 v8::Handle<Value> value = CompileRun(
7414 "function f() {"
7415 " for (var i = 0; i < 1000; i++) {"
7416 " try { x; } catch(e) { return true; }"
7417 " }"
7418 " return false;"
7419 "};"
7420 "f();");
7421 CHECK_EQ(true, value->BooleanValue());
7422 interceptor_call_count = 0;
7423 value = CompileRun(
7424 "function g() {"
7425 " for (var i = 0; i < 1000; i++) {"
7426 " try { x(42); } catch(e) { return true; }"
7427 " }"
7428 " return false;"
7429 "};"
7430 "g();");
7431 CHECK_EQ(true, value->BooleanValue());
7432}
7433
7434
7435static int interceptor_ic_exception_get_count = 0;
7436
7437static v8::Handle<Value> InterceptorICExceptionGetter(
7438 Local<String> name,
7439 const AccessorInfo& info) {
7440 ApiTestFuzzer::Fuzz();
7441 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7442 return call_ic_function3;
7443 }
7444 if (interceptor_ic_exception_get_count == 20) {
7445 return v8::ThrowException(v8_num(42));
7446 }
7447 // Do not handle get for properties other than x.
7448 return v8::Handle<Value>();
7449}
7450
7451// Test interceptor load/call IC where the interceptor throws an
7452// exception once in a while.
7453THREADED_TEST(InterceptorICGetterExceptions) {
7454 interceptor_ic_exception_get_count = 0;
7455 v8::HandleScope scope;
7456 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7457 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7458 LocalContext context(0, templ, v8::Handle<Value>());
7459 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7460 v8::Handle<Value> value = CompileRun(
7461 "function f() {"
7462 " for (var i = 0; i < 100; i++) {"
7463 " try { x; } catch(e) { return true; }"
7464 " }"
7465 " return false;"
7466 "};"
7467 "f();");
7468 CHECK_EQ(true, value->BooleanValue());
7469 interceptor_ic_exception_get_count = 0;
7470 value = CompileRun(
7471 "function f() {"
7472 " for (var i = 0; i < 100; i++) {"
7473 " try { x(42); } catch(e) { return true; }"
7474 " }"
7475 " return false;"
7476 "};"
7477 "f();");
7478 CHECK_EQ(true, value->BooleanValue());
7479}
7480
7481
7482static int interceptor_ic_exception_set_count = 0;
7483
7484static v8::Handle<Value> InterceptorICExceptionSetter(
7485 Local<String> key, Local<Value> value, const AccessorInfo&) {
7486 ApiTestFuzzer::Fuzz();
7487 if (++interceptor_ic_exception_set_count > 20) {
7488 return v8::ThrowException(v8_num(42));
7489 }
7490 // Do not actually handle setting.
7491 return v8::Handle<Value>();
7492}
7493
7494// Test interceptor store IC where the interceptor throws an exception
7495// once in a while.
7496THREADED_TEST(InterceptorICSetterExceptions) {
7497 interceptor_ic_exception_set_count = 0;
7498 v8::HandleScope scope;
7499 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7500 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7501 LocalContext context(0, templ, v8::Handle<Value>());
7502 v8::Handle<Value> value = CompileRun(
7503 "function f() {"
7504 " for (var i = 0; i < 100; i++) {"
7505 " try { x = 42; } catch(e) { return true; }"
7506 " }"
7507 " return false;"
7508 "};"
7509 "f();");
7510 CHECK_EQ(true, value->BooleanValue());
7511}
7512
7513
7514// Test that we ignore null interceptors.
7515THREADED_TEST(NullNamedInterceptor) {
7516 v8::HandleScope scope;
7517 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7518 templ->SetNamedPropertyHandler(0);
7519 LocalContext context;
7520 templ->Set("x", v8_num(42));
7521 v8::Handle<v8::Object> obj = templ->NewInstance();
7522 context->Global()->Set(v8_str("obj"), obj);
7523 v8::Handle<Value> value = CompileRun("obj.x");
7524 CHECK(value->IsInt32());
7525 CHECK_EQ(42, value->Int32Value());
7526}
7527
7528
7529// Test that we ignore null interceptors.
7530THREADED_TEST(NullIndexedInterceptor) {
7531 v8::HandleScope scope;
7532 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7533 templ->SetIndexedPropertyHandler(0);
7534 LocalContext context;
7535 templ->Set("42", v8_num(42));
7536 v8::Handle<v8::Object> obj = templ->NewInstance();
7537 context->Global()->Set(v8_str("obj"), obj);
7538 v8::Handle<Value> value = CompileRun("obj[42]");
7539 CHECK(value->IsInt32());
7540 CHECK_EQ(42, value->Int32Value());
7541}
7542
7543
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007544THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7545 v8::HandleScope scope;
7546 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7547 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7548 LocalContext env;
7549 env->Global()->Set(v8_str("obj"),
7550 templ->GetFunction()->NewInstance());
7551 ExpectTrue("obj.x === 42");
7552 ExpectTrue("!obj.propertyIsEnumerable('x')");
7553}
7554
7555
Steve Blocka7e24c12009-10-30 11:49:00 +00007556static v8::Handle<Value> ParentGetter(Local<String> name,
7557 const AccessorInfo& info) {
7558 ApiTestFuzzer::Fuzz();
7559 return v8_num(1);
7560}
7561
7562
7563static v8::Handle<Value> ChildGetter(Local<String> name,
7564 const AccessorInfo& info) {
7565 ApiTestFuzzer::Fuzz();
7566 return v8_num(42);
7567}
7568
7569
7570THREADED_TEST(Overriding) {
7571 v8::HandleScope scope;
7572 LocalContext context;
7573
7574 // Parent template.
7575 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7576 Local<ObjectTemplate> parent_instance_templ =
7577 parent_templ->InstanceTemplate();
7578 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7579
7580 // Template that inherits from the parent template.
7581 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7582 Local<ObjectTemplate> child_instance_templ =
7583 child_templ->InstanceTemplate();
7584 child_templ->Inherit(parent_templ);
7585 // Override 'f'. The child version of 'f' should get called for child
7586 // instances.
7587 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7588 // Add 'g' twice. The 'g' added last should get called for instances.
7589 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7590 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7591
7592 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7593 // so 'h' can be shadowed on the instance object.
7594 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7595 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7596 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7597
7598 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7599 // but the attribute does not have effect because it is duplicated with
7600 // NULL setter.
7601 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7602 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7603
7604
7605
7606 // Instantiate the child template.
7607 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7608
7609 // Check that the child function overrides the parent one.
7610 context->Global()->Set(v8_str("o"), instance);
7611 Local<Value> value = v8_compile("o.f")->Run();
7612 // Check that the 'g' that was added last is hit.
7613 CHECK_EQ(42, value->Int32Value());
7614 value = v8_compile("o.g")->Run();
7615 CHECK_EQ(42, value->Int32Value());
7616
7617 // Check 'h' can be shadowed.
7618 value = v8_compile("o.h = 3; o.h")->Run();
7619 CHECK_EQ(3, value->Int32Value());
7620
7621 // Check 'i' is cannot be shadowed or changed.
7622 value = v8_compile("o.i = 3; o.i")->Run();
7623 CHECK_EQ(42, value->Int32Value());
7624}
7625
7626
7627static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7628 ApiTestFuzzer::Fuzz();
7629 if (args.IsConstructCall()) {
7630 return v8::Boolean::New(true);
7631 }
7632 return v8::Boolean::New(false);
7633}
7634
7635
7636THREADED_TEST(IsConstructCall) {
7637 v8::HandleScope scope;
7638
7639 // Function template with call handler.
7640 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7641 templ->SetCallHandler(IsConstructHandler);
7642
7643 LocalContext context;
7644
7645 context->Global()->Set(v8_str("f"), templ->GetFunction());
7646 Local<Value> value = v8_compile("f()")->Run();
7647 CHECK(!value->BooleanValue());
7648 value = v8_compile("new f()")->Run();
7649 CHECK(value->BooleanValue());
7650}
7651
7652
7653THREADED_TEST(ObjectProtoToString) {
7654 v8::HandleScope scope;
7655 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7656 templ->SetClassName(v8_str("MyClass"));
7657
7658 LocalContext context;
7659
7660 Local<String> customized_tostring = v8_str("customized toString");
7661
7662 // Replace Object.prototype.toString
7663 v8_compile("Object.prototype.toString = function() {"
7664 " return 'customized toString';"
7665 "}")->Run();
7666
7667 // Normal ToString call should call replaced Object.prototype.toString
7668 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7669 Local<String> value = instance->ToString();
7670 CHECK(value->IsString() && value->Equals(customized_tostring));
7671
7672 // ObjectProtoToString should not call replace toString function.
7673 value = instance->ObjectProtoToString();
7674 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7675
7676 // Check global
7677 value = context->Global()->ObjectProtoToString();
7678 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7679
7680 // Check ordinary object
7681 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007682 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007683 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7684}
7685
7686
7687bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01007688i::Semaphore* ApiTestFuzzer::all_tests_done_=
7689 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007690int ApiTestFuzzer::active_tests_;
7691int ApiTestFuzzer::tests_being_run_;
7692int ApiTestFuzzer::current_;
7693
7694
7695// We are in a callback and want to switch to another thread (if we
7696// are currently running the thread fuzzing test).
7697void ApiTestFuzzer::Fuzz() {
7698 if (!fuzzing_) return;
7699 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7700 test->ContextSwitch();
7701}
7702
7703
7704// Let the next thread go. Since it is also waiting on the V8 lock it may
7705// not start immediately.
7706bool ApiTestFuzzer::NextThread() {
7707 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007708 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007709 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007710 if (kLogThreading)
7711 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007712 return false;
7713 }
Steve Blockd0582a62009-12-15 09:54:21 +00007714 if (kLogThreading) {
7715 printf("Switch from %s to %s\n",
7716 test_name,
7717 RegisterThreadedTest::nth(test_position)->name());
7718 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007719 current_ = test_position;
7720 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7721 return true;
7722}
7723
7724
7725void ApiTestFuzzer::Run() {
7726 // When it is our turn...
7727 gate_->Wait();
7728 {
7729 // ... get the V8 lock and start running the test.
7730 v8::Locker locker;
7731 CallTest();
7732 }
7733 // This test finished.
7734 active_ = false;
7735 active_tests_--;
7736 // If it was the last then signal that fact.
7737 if (active_tests_ == 0) {
7738 all_tests_done_->Signal();
7739 } else {
7740 // Otherwise select a new test and start that.
7741 NextThread();
7742 }
7743}
7744
7745
7746static unsigned linear_congruential_generator;
7747
7748
7749void ApiTestFuzzer::Setup(PartOfTest part) {
7750 linear_congruential_generator = i::FLAG_testing_prng_seed;
7751 fuzzing_ = true;
7752 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7753 int end = (part == FIRST_PART)
7754 ? (RegisterThreadedTest::count() >> 1)
7755 : RegisterThreadedTest::count();
7756 active_tests_ = tests_being_run_ = end - start;
7757 for (int i = 0; i < tests_being_run_; i++) {
7758 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7759 }
7760 for (int i = 0; i < active_tests_; i++) {
7761 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7762 }
7763}
7764
7765
7766static void CallTestNumber(int test_number) {
7767 (RegisterThreadedTest::nth(test_number)->callback())();
7768}
7769
7770
7771void ApiTestFuzzer::RunAllTests() {
7772 // Set off the first test.
7773 current_ = -1;
7774 NextThread();
7775 // Wait till they are all done.
7776 all_tests_done_->Wait();
7777}
7778
7779
7780int ApiTestFuzzer::GetNextTestNumber() {
7781 int next_test;
7782 do {
7783 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7784 linear_congruential_generator *= 1664525u;
7785 linear_congruential_generator += 1013904223u;
7786 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7787 return next_test;
7788}
7789
7790
7791void ApiTestFuzzer::ContextSwitch() {
7792 // If the new thread is the same as the current thread there is nothing to do.
7793 if (NextThread()) {
7794 // Now it can start.
7795 v8::Unlocker unlocker;
7796 // Wait till someone starts us again.
7797 gate_->Wait();
7798 // And we're off.
7799 }
7800}
7801
7802
7803void ApiTestFuzzer::TearDown() {
7804 fuzzing_ = false;
7805 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7806 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7807 if (fuzzer != NULL) fuzzer->Join();
7808 }
7809}
7810
7811
7812// Lets not be needlessly self-referential.
7813TEST(Threading) {
7814 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7815 ApiTestFuzzer::RunAllTests();
7816 ApiTestFuzzer::TearDown();
7817}
7818
7819TEST(Threading2) {
7820 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7821 ApiTestFuzzer::RunAllTests();
7822 ApiTestFuzzer::TearDown();
7823}
7824
7825
7826void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007827 if (kLogThreading)
7828 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007829 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007830 if (kLogThreading)
7831 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007832}
7833
7834
7835static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7836 CHECK(v8::Locker::IsLocked());
7837 ApiTestFuzzer::Fuzz();
7838 v8::Unlocker unlocker;
7839 const char* code = "throw 7;";
7840 {
7841 v8::Locker nested_locker;
7842 v8::HandleScope scope;
7843 v8::Handle<Value> exception;
7844 { v8::TryCatch try_catch;
7845 v8::Handle<Value> value = CompileRun(code);
7846 CHECK(value.IsEmpty());
7847 CHECK(try_catch.HasCaught());
7848 // Make sure to wrap the exception in a new handle because
7849 // the handle returned from the TryCatch is destroyed
7850 // when the TryCatch is destroyed.
7851 exception = Local<Value>::New(try_catch.Exception());
7852 }
7853 return v8::ThrowException(exception);
7854 }
7855}
7856
7857
7858static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7859 CHECK(v8::Locker::IsLocked());
7860 ApiTestFuzzer::Fuzz();
7861 v8::Unlocker unlocker;
7862 const char* code = "throw 7;";
7863 {
7864 v8::Locker nested_locker;
7865 v8::HandleScope scope;
7866 v8::Handle<Value> value = CompileRun(code);
7867 CHECK(value.IsEmpty());
7868 return v8_str("foo");
7869 }
7870}
7871
7872
7873// These are locking tests that don't need to be run again
7874// as part of the locking aggregation tests.
7875TEST(NestedLockers) {
7876 v8::Locker locker;
7877 CHECK(v8::Locker::IsLocked());
7878 v8::HandleScope scope;
7879 LocalContext env;
7880 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7881 Local<Function> fun = fun_templ->GetFunction();
7882 env->Global()->Set(v8_str("throw_in_js"), fun);
7883 Local<Script> script = v8_compile("(function () {"
7884 " try {"
7885 " throw_in_js();"
7886 " return 42;"
7887 " } catch (e) {"
7888 " return e * 13;"
7889 " }"
7890 "})();");
7891 CHECK_EQ(91, script->Run()->Int32Value());
7892}
7893
7894
7895// These are locking tests that don't need to be run again
7896// as part of the locking aggregation tests.
7897TEST(NestedLockersNoTryCatch) {
7898 v8::Locker locker;
7899 v8::HandleScope scope;
7900 LocalContext env;
7901 Local<v8::FunctionTemplate> fun_templ =
7902 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7903 Local<Function> fun = fun_templ->GetFunction();
7904 env->Global()->Set(v8_str("throw_in_js"), fun);
7905 Local<Script> script = v8_compile("(function () {"
7906 " try {"
7907 " throw_in_js();"
7908 " return 42;"
7909 " } catch (e) {"
7910 " return e * 13;"
7911 " }"
7912 "})();");
7913 CHECK_EQ(91, script->Run()->Int32Value());
7914}
7915
7916
7917THREADED_TEST(RecursiveLocking) {
7918 v8::Locker locker;
7919 {
7920 v8::Locker locker2;
7921 CHECK(v8::Locker::IsLocked());
7922 }
7923}
7924
7925
7926static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
7927 ApiTestFuzzer::Fuzz();
7928 v8::Unlocker unlocker;
7929 return v8::Undefined();
7930}
7931
7932
7933THREADED_TEST(LockUnlockLock) {
7934 {
7935 v8::Locker locker;
7936 v8::HandleScope scope;
7937 LocalContext env;
7938 Local<v8::FunctionTemplate> fun_templ =
7939 v8::FunctionTemplate::New(UnlockForAMoment);
7940 Local<Function> fun = fun_templ->GetFunction();
7941 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7942 Local<Script> script = v8_compile("(function () {"
7943 " unlock_for_a_moment();"
7944 " return 42;"
7945 "})();");
7946 CHECK_EQ(42, script->Run()->Int32Value());
7947 }
7948 {
7949 v8::Locker locker;
7950 v8::HandleScope scope;
7951 LocalContext env;
7952 Local<v8::FunctionTemplate> fun_templ =
7953 v8::FunctionTemplate::New(UnlockForAMoment);
7954 Local<Function> fun = fun_templ->GetFunction();
7955 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7956 Local<Script> script = v8_compile("(function () {"
7957 " unlock_for_a_moment();"
7958 " return 42;"
7959 "})();");
7960 CHECK_EQ(42, script->Run()->Int32Value());
7961 }
7962}
7963
7964
Leon Clarked91b9f72010-01-27 17:25:45 +00007965static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00007966 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01007967 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00007968 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
7969 if (object->IsJSGlobalObject()) count++;
7970 return count;
7971}
7972
7973
7974static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00007975 // We need to collect all garbage twice to be sure that everything
7976 // has been collected. This is because inline caches are cleared in
7977 // the first garbage collection but some of the maps have already
7978 // been marked at that point. Therefore some of the maps are not
7979 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01007980 i::Heap::CollectAllGarbage(false);
7981 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00007982 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00007983#ifdef DEBUG
Steve Block8defd9f2010-07-08 12:39:36 +01007984 if (count > 0) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00007985#endif
7986 return count;
7987}
7988
7989
7990TEST(DontLeakGlobalObjects) {
7991 // Regression test for issues 1139850 and 1174891.
7992
7993 v8::V8::Initialize();
7994
7995 int count = GetSurvivingGlobalObjectsCount();
7996
7997 for (int i = 0; i < 5; i++) {
7998 { v8::HandleScope scope;
7999 LocalContext context;
8000 }
8001 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8002
8003 { v8::HandleScope scope;
8004 LocalContext context;
8005 v8_compile("Date")->Run();
8006 }
8007 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8008
8009 { v8::HandleScope scope;
8010 LocalContext context;
8011 v8_compile("/aaa/")->Run();
8012 }
8013 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8014
8015 { v8::HandleScope scope;
8016 const char* extension_list[] = { "v8/gc" };
8017 v8::ExtensionConfiguration extensions(1, extension_list);
8018 LocalContext context(&extensions);
8019 v8_compile("gc();")->Run();
8020 }
8021 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8022 }
8023}
8024
8025
8026v8::Persistent<v8::Object> some_object;
8027v8::Persistent<v8::Object> bad_handle;
8028
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008029void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008030 v8::HandleScope scope;
8031 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008032 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008033}
8034
8035
8036THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8037 LocalContext context;
8038
8039 v8::Persistent<v8::Object> handle1, handle2;
8040 {
8041 v8::HandleScope scope;
8042 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8043 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8044 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8045 }
8046 // Note: order is implementation dependent alas: currently
8047 // global handle nodes are processed by PostGarbageCollectionProcessing
8048 // in reverse allocation order, so if second allocated handle is deleted,
8049 // weak callback of the first handle would be able to 'reallocate' it.
8050 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8051 handle2.Dispose();
8052 i::Heap::CollectAllGarbage(false);
8053}
8054
8055
8056v8::Persistent<v8::Object> to_be_disposed;
8057
8058void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8059 to_be_disposed.Dispose();
8060 i::Heap::CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008061 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008062}
8063
8064
8065THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8066 LocalContext context;
8067
8068 v8::Persistent<v8::Object> handle1, handle2;
8069 {
8070 v8::HandleScope scope;
8071 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8072 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8073 }
8074 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8075 to_be_disposed = handle2;
8076 i::Heap::CollectAllGarbage(false);
8077}
8078
Steve Blockd0582a62009-12-15 09:54:21 +00008079void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8080 handle.Dispose();
8081}
8082
8083void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8084 v8::HandleScope scope;
8085 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008086 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00008087}
8088
8089
8090THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8091 LocalContext context;
8092
8093 v8::Persistent<v8::Object> handle1, handle2, handle3;
8094 {
8095 v8::HandleScope scope;
8096 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8097 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8098 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8099 }
8100 handle2.MakeWeak(NULL, DisposingCallback);
8101 handle3.MakeWeak(NULL, HandleCreatingCallback);
8102 i::Heap::CollectAllGarbage(false);
8103}
8104
Steve Blocka7e24c12009-10-30 11:49:00 +00008105
8106THREADED_TEST(CheckForCrossContextObjectLiterals) {
8107 v8::V8::Initialize();
8108
8109 const int nof = 2;
8110 const char* sources[nof] = {
8111 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8112 "Object()"
8113 };
8114
8115 for (int i = 0; i < nof; i++) {
8116 const char* source = sources[i];
8117 { v8::HandleScope scope;
8118 LocalContext context;
8119 CompileRun(source);
8120 }
8121 { v8::HandleScope scope;
8122 LocalContext context;
8123 CompileRun(source);
8124 }
8125 }
8126}
8127
8128
8129static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8130 v8::HandleScope inner;
8131 env->Enter();
8132 v8::Handle<Value> three = v8_num(3);
8133 v8::Handle<Value> value = inner.Close(three);
8134 env->Exit();
8135 return value;
8136}
8137
8138
8139THREADED_TEST(NestedHandleScopeAndContexts) {
8140 v8::HandleScope outer;
8141 v8::Persistent<Context> env = Context::New();
8142 env->Enter();
8143 v8::Handle<Value> value = NestedScope(env);
8144 v8::Handle<String> str = value->ToString();
8145 env->Exit();
8146 env.Dispose();
8147}
8148
8149
8150THREADED_TEST(ExternalAllocatedMemory) {
8151 v8::HandleScope outer;
8152 v8::Persistent<Context> env = Context::New();
8153 const int kSize = 1024*1024;
8154 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8155 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8156}
8157
8158
8159THREADED_TEST(DisposeEnteredContext) {
8160 v8::HandleScope scope;
8161 LocalContext outer;
8162 { v8::Persistent<v8::Context> inner = v8::Context::New();
8163 inner->Enter();
8164 inner.Dispose();
8165 inner.Clear();
8166 inner->Exit();
8167 }
8168}
8169
8170
8171// Regression test for issue 54, object templates with internal fields
8172// but no accessors or interceptors did not get their internal field
8173// count set on instances.
8174THREADED_TEST(Regress54) {
8175 v8::HandleScope outer;
8176 LocalContext context;
8177 static v8::Persistent<v8::ObjectTemplate> templ;
8178 if (templ.IsEmpty()) {
8179 v8::HandleScope inner;
8180 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8181 local->SetInternalFieldCount(1);
8182 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8183 }
8184 v8::Handle<v8::Object> result = templ->NewInstance();
8185 CHECK_EQ(1, result->InternalFieldCount());
8186}
8187
8188
8189// If part of the threaded tests, this test makes ThreadingTest fail
8190// on mac.
8191TEST(CatchStackOverflow) {
8192 v8::HandleScope scope;
8193 LocalContext context;
8194 v8::TryCatch try_catch;
8195 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8196 "function f() {"
8197 " return f();"
8198 "}"
8199 ""
8200 "f();"));
8201 v8::Handle<v8::Value> result = script->Run();
8202 CHECK(result.IsEmpty());
8203}
8204
8205
8206static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8207 const char* resource_name,
8208 int line_offset) {
8209 v8::HandleScope scope;
8210 v8::TryCatch try_catch;
8211 v8::Handle<v8::Value> result = script->Run();
8212 CHECK(result.IsEmpty());
8213 CHECK(try_catch.HasCaught());
8214 v8::Handle<v8::Message> message = try_catch.Message();
8215 CHECK(!message.IsEmpty());
8216 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8217 CHECK_EQ(91, message->GetStartPosition());
8218 CHECK_EQ(92, message->GetEndPosition());
8219 CHECK_EQ(2, message->GetStartColumn());
8220 CHECK_EQ(3, message->GetEndColumn());
8221 v8::String::AsciiValue line(message->GetSourceLine());
8222 CHECK_EQ(" throw 'nirk';", *line);
8223 v8::String::AsciiValue name(message->GetScriptResourceName());
8224 CHECK_EQ(resource_name, *name);
8225}
8226
8227
8228THREADED_TEST(TryCatchSourceInfo) {
8229 v8::HandleScope scope;
8230 LocalContext context;
8231 v8::Handle<v8::String> source = v8::String::New(
8232 "function Foo() {\n"
8233 " return Bar();\n"
8234 "}\n"
8235 "\n"
8236 "function Bar() {\n"
8237 " return Baz();\n"
8238 "}\n"
8239 "\n"
8240 "function Baz() {\n"
8241 " throw 'nirk';\n"
8242 "}\n"
8243 "\n"
8244 "Foo();\n");
8245
8246 const char* resource_name;
8247 v8::Handle<v8::Script> script;
8248 resource_name = "test.js";
8249 script = v8::Script::Compile(source, v8::String::New(resource_name));
8250 CheckTryCatchSourceInfo(script, resource_name, 0);
8251
8252 resource_name = "test1.js";
8253 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8254 script = v8::Script::Compile(source, &origin1);
8255 CheckTryCatchSourceInfo(script, resource_name, 0);
8256
8257 resource_name = "test2.js";
8258 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8259 script = v8::Script::Compile(source, &origin2);
8260 CheckTryCatchSourceInfo(script, resource_name, 7);
8261}
8262
8263
8264THREADED_TEST(CompilationCache) {
8265 v8::HandleScope scope;
8266 LocalContext context;
8267 v8::Handle<v8::String> source0 = v8::String::New("1234");
8268 v8::Handle<v8::String> source1 = v8::String::New("1234");
8269 v8::Handle<v8::Script> script0 =
8270 v8::Script::Compile(source0, v8::String::New("test.js"));
8271 v8::Handle<v8::Script> script1 =
8272 v8::Script::Compile(source1, v8::String::New("test.js"));
8273 v8::Handle<v8::Script> script2 =
8274 v8::Script::Compile(source0); // different origin
8275 CHECK_EQ(1234, script0->Run()->Int32Value());
8276 CHECK_EQ(1234, script1->Run()->Int32Value());
8277 CHECK_EQ(1234, script2->Run()->Int32Value());
8278}
8279
8280
8281static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8282 ApiTestFuzzer::Fuzz();
8283 return v8_num(42);
8284}
8285
8286
8287THREADED_TEST(CallbackFunctionName) {
8288 v8::HandleScope scope;
8289 LocalContext context;
8290 Local<ObjectTemplate> t = ObjectTemplate::New();
8291 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8292 context->Global()->Set(v8_str("obj"), t->NewInstance());
8293 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8294 CHECK(value->IsString());
8295 v8::String::AsciiValue name(value);
8296 CHECK_EQ("asdf", *name);
8297}
8298
8299
8300THREADED_TEST(DateAccess) {
8301 v8::HandleScope scope;
8302 LocalContext context;
8303 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8304 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008305 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008306}
8307
8308
8309void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008310 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008311 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8312 CHECK_EQ(elmc, props->Length());
8313 for (int i = 0; i < elmc; i++) {
8314 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8315 CHECK_EQ(elmv[i], *elm);
8316 }
8317}
8318
8319
8320THREADED_TEST(PropertyEnumeration) {
8321 v8::HandleScope scope;
8322 LocalContext context;
8323 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8324 "var result = [];"
8325 "result[0] = {};"
8326 "result[1] = {a: 1, b: 2};"
8327 "result[2] = [1, 2, 3];"
8328 "var proto = {x: 1, y: 2, z: 3};"
8329 "var x = { __proto__: proto, w: 0, z: 1 };"
8330 "result[3] = x;"
8331 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008332 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008333 CHECK_EQ(4, elms->Length());
8334 int elmc0 = 0;
8335 const char** elmv0 = NULL;
8336 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8337 int elmc1 = 2;
8338 const char* elmv1[] = {"a", "b"};
8339 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8340 int elmc2 = 3;
8341 const char* elmv2[] = {"0", "1", "2"};
8342 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8343 int elmc3 = 4;
8344 const char* elmv3[] = {"w", "z", "x", "y"};
8345 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8346}
8347
8348
Steve Blocka7e24c12009-10-30 11:49:00 +00008349static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8350 Local<Value> name,
8351 v8::AccessType type,
8352 Local<Value> data) {
8353 return type != v8::ACCESS_SET;
8354}
8355
8356
8357static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8358 uint32_t key,
8359 v8::AccessType type,
8360 Local<Value> data) {
8361 return type != v8::ACCESS_SET;
8362}
8363
8364
8365THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8366 v8::HandleScope scope;
8367 LocalContext context;
8368 Local<ObjectTemplate> templ = ObjectTemplate::New();
8369 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8370 IndexedSetAccessBlocker);
8371 templ->Set(v8_str("x"), v8::True());
8372 Local<v8::Object> instance = templ->NewInstance();
8373 context->Global()->Set(v8_str("obj"), instance);
8374 Local<Value> value = CompileRun("obj.x");
8375 CHECK(value->BooleanValue());
8376}
8377
8378
8379static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8380 Local<Value> name,
8381 v8::AccessType type,
8382 Local<Value> data) {
8383 return false;
8384}
8385
8386
8387static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8388 uint32_t key,
8389 v8::AccessType type,
8390 Local<Value> data) {
8391 return false;
8392}
8393
8394
8395
8396THREADED_TEST(AccessChecksReenabledCorrectly) {
8397 v8::HandleScope scope;
8398 LocalContext context;
8399 Local<ObjectTemplate> templ = ObjectTemplate::New();
8400 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8401 IndexedGetAccessBlocker);
8402 templ->Set(v8_str("a"), v8_str("a"));
8403 // Add more than 8 (see kMaxFastProperties) properties
8404 // so that the constructor will force copying map.
8405 // Cannot sprintf, gcc complains unsafety.
8406 char buf[4];
8407 for (char i = '0'; i <= '9' ; i++) {
8408 buf[0] = i;
8409 for (char j = '0'; j <= '9'; j++) {
8410 buf[1] = j;
8411 for (char k = '0'; k <= '9'; k++) {
8412 buf[2] = k;
8413 buf[3] = 0;
8414 templ->Set(v8_str(buf), v8::Number::New(k));
8415 }
8416 }
8417 }
8418
8419 Local<v8::Object> instance_1 = templ->NewInstance();
8420 context->Global()->Set(v8_str("obj_1"), instance_1);
8421
8422 Local<Value> value_1 = CompileRun("obj_1.a");
8423 CHECK(value_1->IsUndefined());
8424
8425 Local<v8::Object> instance_2 = templ->NewInstance();
8426 context->Global()->Set(v8_str("obj_2"), instance_2);
8427
8428 Local<Value> value_2 = CompileRun("obj_2.a");
8429 CHECK(value_2->IsUndefined());
8430}
8431
8432
8433// This tests that access check information remains on the global
8434// object template when creating contexts.
8435THREADED_TEST(AccessControlRepeatedContextCreation) {
8436 v8::HandleScope handle_scope;
8437 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8438 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8439 IndexedSetAccessBlocker);
8440 i::Handle<i::ObjectTemplateInfo> internal_template =
8441 v8::Utils::OpenHandle(*global_template);
8442 CHECK(!internal_template->constructor()->IsUndefined());
8443 i::Handle<i::FunctionTemplateInfo> constructor(
8444 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8445 CHECK(!constructor->access_check_info()->IsUndefined());
8446 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8447 CHECK(!constructor->access_check_info()->IsUndefined());
8448}
8449
8450
8451THREADED_TEST(TurnOnAccessCheck) {
8452 v8::HandleScope handle_scope;
8453
8454 // Create an environment with access check to the global object disabled by
8455 // default.
8456 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8457 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8458 IndexedGetAccessBlocker,
8459 v8::Handle<v8::Value>(),
8460 false);
8461 v8::Persistent<Context> context = Context::New(NULL, global_template);
8462 Context::Scope context_scope(context);
8463
8464 // Set up a property and a number of functions.
8465 context->Global()->Set(v8_str("a"), v8_num(1));
8466 CompileRun("function f1() {return a;}"
8467 "function f2() {return a;}"
8468 "function g1() {return h();}"
8469 "function g2() {return h();}"
8470 "function h() {return 1;}");
8471 Local<Function> f1 =
8472 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8473 Local<Function> f2 =
8474 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8475 Local<Function> g1 =
8476 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8477 Local<Function> g2 =
8478 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8479 Local<Function> h =
8480 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8481
8482 // Get the global object.
8483 v8::Handle<v8::Object> global = context->Global();
8484
8485 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8486 // uses the runtime system to retreive property a whereas f2 uses global load
8487 // inline cache.
8488 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8489 for (int i = 0; i < 4; i++) {
8490 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8491 }
8492
8493 // Same for g1 and g2.
8494 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8495 for (int i = 0; i < 4; i++) {
8496 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8497 }
8498
8499 // Detach the global and turn on access check.
8500 context->DetachGlobal();
8501 context->Global()->TurnOnAccessCheck();
8502
8503 // Failing access check to property get results in undefined.
8504 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8505 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8506
8507 // Failing access check to function call results in exception.
8508 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8509 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8510
8511 // No failing access check when just returning a constant.
8512 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8513}
8514
8515
8516// This test verifies that pre-compilation (aka preparsing) can be called
8517// without initializing the whole VM. Thus we cannot run this test in a
8518// multi-threaded setup.
8519TEST(PreCompile) {
8520 // TODO(155): This test would break without the initialization of V8. This is
8521 // a workaround for now to make this test not fail.
8522 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008523 const char* script = "function foo(a) { return a+1; }";
8524 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008525 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008526 CHECK_NE(sd->Length(), 0);
8527 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008528 CHECK(!sd->HasError());
8529 delete sd;
8530}
8531
8532
8533TEST(PreCompileWithError) {
8534 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008535 const char* script = "function foo(a) { return 1 * * 2; }";
8536 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008537 v8::ScriptData::PreCompile(script, i::StrLength(script));
8538 CHECK(sd->HasError());
8539 delete sd;
8540}
8541
8542
8543TEST(Regress31661) {
8544 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008545 const char* script = " The Definintive Guide";
8546 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008547 v8::ScriptData::PreCompile(script, i::StrLength(script));
8548 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008549 delete sd;
8550}
8551
8552
Leon Clarkef7060e22010-06-03 12:02:55 +01008553// Tests that ScriptData can be serialized and deserialized.
8554TEST(PreCompileSerialization) {
8555 v8::V8::Initialize();
8556 const char* script = "function foo(a) { return a+1; }";
8557 v8::ScriptData* sd =
8558 v8::ScriptData::PreCompile(script, i::StrLength(script));
8559
8560 // Serialize.
8561 int serialized_data_length = sd->Length();
8562 char* serialized_data = i::NewArray<char>(serialized_data_length);
8563 memcpy(serialized_data, sd->Data(), serialized_data_length);
8564
8565 // Deserialize.
8566 v8::ScriptData* deserialized_sd =
8567 v8::ScriptData::New(serialized_data, serialized_data_length);
8568
8569 // Verify that the original is the same as the deserialized.
8570 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8571 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8572 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8573
8574 delete sd;
8575 delete deserialized_sd;
8576}
8577
8578
8579// Attempts to deserialize bad data.
8580TEST(PreCompileDeserializationError) {
8581 v8::V8::Initialize();
8582 const char* data = "DONT CARE";
8583 int invalid_size = 3;
8584 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8585
8586 CHECK_EQ(0, sd->Length());
8587
8588 delete sd;
8589}
8590
8591
Leon Clarkeac952652010-07-15 11:15:24 +01008592// Attempts to deserialize bad data.
8593TEST(PreCompileInvalidPreparseDataError) {
8594 v8::V8::Initialize();
8595 v8::HandleScope scope;
8596 LocalContext context;
8597
8598 const char* script = "function foo(){ return 5;}\n"
8599 "function bar(){ return 6 + 7;} foo();";
8600 v8::ScriptData* sd =
8601 v8::ScriptData::PreCompile(script, i::StrLength(script));
8602 CHECK(!sd->HasError());
8603 // ScriptDataImpl private implementation details
8604 const int kUnsignedSize = sizeof(unsigned);
8605 const int kHeaderSize = 4;
8606 const int kFunctionEntrySize = 4;
8607 const int kFunctionEntryStartOffset = 0;
8608 const int kFunctionEntryEndOffset = 1;
8609 unsigned* sd_data =
8610 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
8611 CHECK_EQ(sd->Length(),
8612 (kHeaderSize + 2 * kFunctionEntrySize) * kUnsignedSize);
8613
8614 // Overwrite function bar's end position with 0.
8615 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
8616 v8::TryCatch try_catch;
8617
8618 Local<String> source = String::New(script);
8619 Local<Script> compiled_script = Script::New(source, NULL, sd);
8620 CHECK(try_catch.HasCaught());
8621 String::AsciiValue exception_value(try_catch.Message()->Get());
8622 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8623 *exception_value);
8624
8625 try_catch.Reset();
8626 // Overwrite function bar's start position with 200. The function entry
8627 // will not be found when searching for it by position.
8628 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
8629 200;
8630 compiled_script = Script::New(source, NULL, sd);
8631 CHECK(try_catch.HasCaught());
8632 String::AsciiValue second_exception_value(try_catch.Message()->Get());
8633 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8634 *second_exception_value);
8635
8636 delete sd;
8637}
8638
8639
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008640// Verifies that the Handle<String> and const char* versions of the API produce
8641// the same results (at least for one trivial case).
8642TEST(PreCompileAPIVariationsAreSame) {
8643 v8::V8::Initialize();
8644 v8::HandleScope scope;
8645
8646 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008647
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008648 v8::ScriptData* sd_from_cstring =
8649 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8650
8651 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008652 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008653 v8::String::NewExternal(resource));
8654
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008655 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
8656 v8::String::New(cstring));
8657
8658 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008659 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008660 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008661 sd_from_cstring->Length()));
8662
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008663 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
8664 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8665 sd_from_string->Data(),
8666 sd_from_cstring->Length()));
8667
8668
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008669 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008670 delete sd_from_external_string;
8671 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008672}
8673
8674
Steve Blocka7e24c12009-10-30 11:49:00 +00008675// This tests that we do not allow dictionary load/call inline caches
8676// to use functions that have not yet been compiled. The potential
8677// problem of loading a function that has not yet been compiled can
8678// arise because we share code between contexts via the compilation
8679// cache.
8680THREADED_TEST(DictionaryICLoadedFunction) {
8681 v8::HandleScope scope;
8682 // Test LoadIC.
8683 for (int i = 0; i < 2; i++) {
8684 LocalContext context;
8685 context->Global()->Set(v8_str("tmp"), v8::True());
8686 context->Global()->Delete(v8_str("tmp"));
8687 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8688 }
8689 // Test CallIC.
8690 for (int i = 0; i < 2; i++) {
8691 LocalContext context;
8692 context->Global()->Set(v8_str("tmp"), v8::True());
8693 context->Global()->Delete(v8_str("tmp"));
8694 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8695 }
8696}
8697
8698
8699// Test that cross-context new calls use the context of the callee to
8700// create the new JavaScript object.
8701THREADED_TEST(CrossContextNew) {
8702 v8::HandleScope scope;
8703 v8::Persistent<Context> context0 = Context::New();
8704 v8::Persistent<Context> context1 = Context::New();
8705
8706 // Allow cross-domain access.
8707 Local<String> token = v8_str("<security token>");
8708 context0->SetSecurityToken(token);
8709 context1->SetSecurityToken(token);
8710
8711 // Set an 'x' property on the Object prototype and define a
8712 // constructor function in context0.
8713 context0->Enter();
8714 CompileRun("Object.prototype.x = 42; function C() {};");
8715 context0->Exit();
8716
8717 // Call the constructor function from context0 and check that the
8718 // result has the 'x' property.
8719 context1->Enter();
8720 context1->Global()->Set(v8_str("other"), context0->Global());
8721 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8722 CHECK(value->IsInt32());
8723 CHECK_EQ(42, value->Int32Value());
8724 context1->Exit();
8725
8726 // Dispose the contexts to allow them to be garbage collected.
8727 context0.Dispose();
8728 context1.Dispose();
8729}
8730
8731
8732class RegExpInterruptTest {
8733 public:
8734 RegExpInterruptTest() : block_(NULL) {}
8735 ~RegExpInterruptTest() { delete block_; }
8736 void RunTest() {
8737 block_ = i::OS::CreateSemaphore(0);
8738 gc_count_ = 0;
8739 gc_during_regexp_ = 0;
8740 regexp_success_ = false;
8741 gc_success_ = false;
8742 GCThread gc_thread(this);
8743 gc_thread.Start();
8744 v8::Locker::StartPreemption(1);
8745
8746 LongRunningRegExp();
8747 {
8748 v8::Unlocker unlock;
8749 gc_thread.Join();
8750 }
8751 v8::Locker::StopPreemption();
8752 CHECK(regexp_success_);
8753 CHECK(gc_success_);
8754 }
8755 private:
8756 // Number of garbage collections required.
8757 static const int kRequiredGCs = 5;
8758
8759 class GCThread : public i::Thread {
8760 public:
8761 explicit GCThread(RegExpInterruptTest* test)
8762 : test_(test) {}
8763 virtual void Run() {
8764 test_->CollectGarbage();
8765 }
8766 private:
8767 RegExpInterruptTest* test_;
8768 };
8769
8770 void CollectGarbage() {
8771 block_->Wait();
8772 while (gc_during_regexp_ < kRequiredGCs) {
8773 {
8774 v8::Locker lock;
8775 // TODO(lrn): Perhaps create some garbage before collecting.
8776 i::Heap::CollectAllGarbage(false);
8777 gc_count_++;
8778 }
8779 i::OS::Sleep(1);
8780 }
8781 gc_success_ = true;
8782 }
8783
8784 void LongRunningRegExp() {
8785 block_->Signal(); // Enable garbage collection thread on next preemption.
8786 int rounds = 0;
8787 while (gc_during_regexp_ < kRequiredGCs) {
8788 int gc_before = gc_count_;
8789 {
8790 // Match 15-30 "a"'s against 14 and a "b".
8791 const char* c_source =
8792 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8793 ".exec('aaaaaaaaaaaaaaab') === null";
8794 Local<String> source = String::New(c_source);
8795 Local<Script> script = Script::Compile(source);
8796 Local<Value> result = script->Run();
8797 if (!result->BooleanValue()) {
8798 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8799 return;
8800 }
8801 }
8802 {
8803 // Match 15-30 "a"'s against 15 and a "b".
8804 const char* c_source =
8805 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8806 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8807 Local<String> source = String::New(c_source);
8808 Local<Script> script = Script::Compile(source);
8809 Local<Value> result = script->Run();
8810 if (!result->BooleanValue()) {
8811 gc_during_regexp_ = kRequiredGCs;
8812 return;
8813 }
8814 }
8815 int gc_after = gc_count_;
8816 gc_during_regexp_ += gc_after - gc_before;
8817 rounds++;
8818 i::OS::Sleep(1);
8819 }
8820 regexp_success_ = true;
8821 }
8822
8823 i::Semaphore* block_;
8824 int gc_count_;
8825 int gc_during_regexp_;
8826 bool regexp_success_;
8827 bool gc_success_;
8828};
8829
8830
8831// Test that a regular expression execution can be interrupted and
8832// survive a garbage collection.
8833TEST(RegExpInterruption) {
8834 v8::Locker lock;
8835 v8::V8::Initialize();
8836 v8::HandleScope scope;
8837 Local<Context> local_env;
8838 {
8839 LocalContext env;
8840 local_env = env.local();
8841 }
8842
8843 // Local context should still be live.
8844 CHECK(!local_env.IsEmpty());
8845 local_env->Enter();
8846
8847 // Should complete without problems.
8848 RegExpInterruptTest().RunTest();
8849
8850 local_env->Exit();
8851}
8852
8853
8854class ApplyInterruptTest {
8855 public:
8856 ApplyInterruptTest() : block_(NULL) {}
8857 ~ApplyInterruptTest() { delete block_; }
8858 void RunTest() {
8859 block_ = i::OS::CreateSemaphore(0);
8860 gc_count_ = 0;
8861 gc_during_apply_ = 0;
8862 apply_success_ = false;
8863 gc_success_ = false;
8864 GCThread gc_thread(this);
8865 gc_thread.Start();
8866 v8::Locker::StartPreemption(1);
8867
8868 LongRunningApply();
8869 {
8870 v8::Unlocker unlock;
8871 gc_thread.Join();
8872 }
8873 v8::Locker::StopPreemption();
8874 CHECK(apply_success_);
8875 CHECK(gc_success_);
8876 }
8877 private:
8878 // Number of garbage collections required.
8879 static const int kRequiredGCs = 2;
8880
8881 class GCThread : public i::Thread {
8882 public:
8883 explicit GCThread(ApplyInterruptTest* test)
8884 : test_(test) {}
8885 virtual void Run() {
8886 test_->CollectGarbage();
8887 }
8888 private:
8889 ApplyInterruptTest* test_;
8890 };
8891
8892 void CollectGarbage() {
8893 block_->Wait();
8894 while (gc_during_apply_ < kRequiredGCs) {
8895 {
8896 v8::Locker lock;
8897 i::Heap::CollectAllGarbage(false);
8898 gc_count_++;
8899 }
8900 i::OS::Sleep(1);
8901 }
8902 gc_success_ = true;
8903 }
8904
8905 void LongRunningApply() {
8906 block_->Signal();
8907 int rounds = 0;
8908 while (gc_during_apply_ < kRequiredGCs) {
8909 int gc_before = gc_count_;
8910 {
8911 const char* c_source =
8912 "function do_very_little(bar) {"
8913 " this.foo = bar;"
8914 "}"
8915 "for (var i = 0; i < 100000; i++) {"
8916 " do_very_little.apply(this, ['bar']);"
8917 "}";
8918 Local<String> source = String::New(c_source);
8919 Local<Script> script = Script::Compile(source);
8920 Local<Value> result = script->Run();
8921 // Check that no exception was thrown.
8922 CHECK(!result.IsEmpty());
8923 }
8924 int gc_after = gc_count_;
8925 gc_during_apply_ += gc_after - gc_before;
8926 rounds++;
8927 }
8928 apply_success_ = true;
8929 }
8930
8931 i::Semaphore* block_;
8932 int gc_count_;
8933 int gc_during_apply_;
8934 bool apply_success_;
8935 bool gc_success_;
8936};
8937
8938
8939// Test that nothing bad happens if we get a preemption just when we were
8940// about to do an apply().
8941TEST(ApplyInterruption) {
8942 v8::Locker lock;
8943 v8::V8::Initialize();
8944 v8::HandleScope scope;
8945 Local<Context> local_env;
8946 {
8947 LocalContext env;
8948 local_env = env.local();
8949 }
8950
8951 // Local context should still be live.
8952 CHECK(!local_env.IsEmpty());
8953 local_env->Enter();
8954
8955 // Should complete without problems.
8956 ApplyInterruptTest().RunTest();
8957
8958 local_env->Exit();
8959}
8960
8961
8962// Verify that we can clone an object
8963TEST(ObjectClone) {
8964 v8::HandleScope scope;
8965 LocalContext env;
8966
8967 const char* sample =
8968 "var rv = {};" \
8969 "rv.alpha = 'hello';" \
8970 "rv.beta = 123;" \
8971 "rv;";
8972
8973 // Create an object, verify basics.
8974 Local<Value> val = CompileRun(sample);
8975 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01008976 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008977 obj->Set(v8_str("gamma"), v8_str("cloneme"));
8978
8979 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
8980 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8981 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
8982
8983 // Clone it.
8984 Local<v8::Object> clone = obj->Clone();
8985 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
8986 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
8987 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
8988
8989 // Set a property on the clone, verify each object.
8990 clone->Set(v8_str("beta"), v8::Integer::New(456));
8991 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8992 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
8993}
8994
8995
8996class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
8997 public:
8998 explicit AsciiVectorResource(i::Vector<const char> vector)
8999 : data_(vector) {}
9000 virtual ~AsciiVectorResource() {}
9001 virtual size_t length() const { return data_.length(); }
9002 virtual const char* data() const { return data_.start(); }
9003 private:
9004 i::Vector<const char> data_;
9005};
9006
9007
9008class UC16VectorResource : public v8::String::ExternalStringResource {
9009 public:
9010 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
9011 : data_(vector) {}
9012 virtual ~UC16VectorResource() {}
9013 virtual size_t length() const { return data_.length(); }
9014 virtual const i::uc16* data() const { return data_.start(); }
9015 private:
9016 i::Vector<const i::uc16> data_;
9017};
9018
9019
9020static void MorphAString(i::String* string,
9021 AsciiVectorResource* ascii_resource,
9022 UC16VectorResource* uc16_resource) {
9023 CHECK(i::StringShape(string).IsExternal());
9024 if (string->IsAsciiRepresentation()) {
9025 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009026 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009027 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009028 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009029 i::ExternalTwoByteString* morphed =
9030 i::ExternalTwoByteString::cast(string);
9031 morphed->set_resource(uc16_resource);
9032 } else {
9033 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009034 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009035 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009036 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009037 i::ExternalAsciiString* morphed =
9038 i::ExternalAsciiString::cast(string);
9039 morphed->set_resource(ascii_resource);
9040 }
9041}
9042
9043
9044// Test that we can still flatten a string if the components it is built up
9045// from have been turned into 16 bit strings in the mean time.
9046THREADED_TEST(MorphCompositeStringTest) {
9047 const char* c_string = "Now is the time for all good men"
9048 " to come to the aid of the party";
9049 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9050 {
9051 v8::HandleScope scope;
9052 LocalContext env;
9053 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009054 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009055 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009056 i::Vector<const uint16_t>(two_byte_string,
9057 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009058
9059 Local<String> lhs(v8::Utils::ToLocal(
9060 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9061 Local<String> rhs(v8::Utils::ToLocal(
9062 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9063
9064 env->Global()->Set(v8_str("lhs"), lhs);
9065 env->Global()->Set(v8_str("rhs"), rhs);
9066
9067 CompileRun(
9068 "var cons = lhs + rhs;"
9069 "var slice = lhs.substring(1, lhs.length - 1);"
9070 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
9071
9072 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
9073 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
9074
9075 // Now do some stuff to make sure the strings are flattened, etc.
9076 CompileRun(
9077 "/[^a-z]/.test(cons);"
9078 "/[^a-z]/.test(slice);"
9079 "/[^a-z]/.test(slice_on_cons);");
9080 const char* expected_cons =
9081 "Now is the time for all good men to come to the aid of the party"
9082 "Now is the time for all good men to come to the aid of the party";
9083 const char* expected_slice =
9084 "ow is the time for all good men to come to the aid of the part";
9085 const char* expected_slice_on_cons =
9086 "ow is the time for all good men to come to the aid of the party"
9087 "Now is the time for all good men to come to the aid of the part";
9088 CHECK_EQ(String::New(expected_cons),
9089 env->Global()->Get(v8_str("cons")));
9090 CHECK_EQ(String::New(expected_slice),
9091 env->Global()->Get(v8_str("slice")));
9092 CHECK_EQ(String::New(expected_slice_on_cons),
9093 env->Global()->Get(v8_str("slice_on_cons")));
9094 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009095 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009096}
9097
9098
9099TEST(CompileExternalTwoByteSource) {
9100 v8::HandleScope scope;
9101 LocalContext context;
9102
9103 // This is a very short list of sources, which currently is to check for a
9104 // regression caused by r2703.
9105 const char* ascii_sources[] = {
9106 "0.5",
9107 "-0.5", // This mainly testes PushBack in the Scanner.
9108 "--0.5", // This mainly testes PushBack in the Scanner.
9109 NULL
9110 };
9111
9112 // Compile the sources as external two byte strings.
9113 for (int i = 0; ascii_sources[i] != NULL; i++) {
9114 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9115 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009116 i::Vector<const uint16_t>(two_byte_string,
9117 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009118 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9119 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009120 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009121 }
9122}
9123
9124
9125class RegExpStringModificationTest {
9126 public:
9127 RegExpStringModificationTest()
9128 : block_(i::OS::CreateSemaphore(0)),
9129 morphs_(0),
9130 morphs_during_regexp_(0),
9131 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9132 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9133 ~RegExpStringModificationTest() { delete block_; }
9134 void RunTest() {
9135 regexp_success_ = false;
9136 morph_success_ = false;
9137
9138 // Initialize the contents of two_byte_content_ to be a uc16 representation
9139 // of "aaaaaaaaaaaaaab".
9140 for (int i = 0; i < 14; i++) {
9141 two_byte_content_[i] = 'a';
9142 }
9143 two_byte_content_[14] = 'b';
9144
9145 // Create the input string for the regexp - the one we are going to change
9146 // properties of.
9147 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9148
9149 // Inject the input as a global variable.
9150 i::Handle<i::String> input_name =
9151 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
9152 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
9153
9154
9155 MorphThread morph_thread(this);
9156 morph_thread.Start();
9157 v8::Locker::StartPreemption(1);
9158 LongRunningRegExp();
9159 {
9160 v8::Unlocker unlock;
9161 morph_thread.Join();
9162 }
9163 v8::Locker::StopPreemption();
9164 CHECK(regexp_success_);
9165 CHECK(morph_success_);
9166 }
9167 private:
9168
9169 // Number of string modifications required.
9170 static const int kRequiredModifications = 5;
9171 static const int kMaxModifications = 100;
9172
9173 class MorphThread : public i::Thread {
9174 public:
9175 explicit MorphThread(RegExpStringModificationTest* test)
9176 : test_(test) {}
9177 virtual void Run() {
9178 test_->MorphString();
9179 }
9180 private:
9181 RegExpStringModificationTest* test_;
9182 };
9183
9184 void MorphString() {
9185 block_->Wait();
9186 while (morphs_during_regexp_ < kRequiredModifications &&
9187 morphs_ < kMaxModifications) {
9188 {
9189 v8::Locker lock;
9190 // Swap string between ascii and two-byte representation.
9191 i::String* string = *input_;
9192 MorphAString(string, &ascii_resource_, &uc16_resource_);
9193 morphs_++;
9194 }
9195 i::OS::Sleep(1);
9196 }
9197 morph_success_ = true;
9198 }
9199
9200 void LongRunningRegExp() {
9201 block_->Signal(); // Enable morphing thread on next preemption.
9202 while (morphs_during_regexp_ < kRequiredModifications &&
9203 morphs_ < kMaxModifications) {
9204 int morphs_before = morphs_;
9205 {
9206 // Match 15-30 "a"'s against 14 and a "b".
9207 const char* c_source =
9208 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9209 ".exec(input) === null";
9210 Local<String> source = String::New(c_source);
9211 Local<Script> script = Script::Compile(source);
9212 Local<Value> result = script->Run();
9213 CHECK(result->IsTrue());
9214 }
9215 int morphs_after = morphs_;
9216 morphs_during_regexp_ += morphs_after - morphs_before;
9217 }
9218 regexp_success_ = true;
9219 }
9220
9221 i::uc16 two_byte_content_[15];
9222 i::Semaphore* block_;
9223 int morphs_;
9224 int morphs_during_regexp_;
9225 bool regexp_success_;
9226 bool morph_success_;
9227 i::Handle<i::String> input_;
9228 AsciiVectorResource ascii_resource_;
9229 UC16VectorResource uc16_resource_;
9230};
9231
9232
9233// Test that a regular expression execution can be interrupted and
9234// the string changed without failing.
9235TEST(RegExpStringModification) {
9236 v8::Locker lock;
9237 v8::V8::Initialize();
9238 v8::HandleScope scope;
9239 Local<Context> local_env;
9240 {
9241 LocalContext env;
9242 local_env = env.local();
9243 }
9244
9245 // Local context should still be live.
9246 CHECK(!local_env.IsEmpty());
9247 local_env->Enter();
9248
9249 // Should complete without problems.
9250 RegExpStringModificationTest().RunTest();
9251
9252 local_env->Exit();
9253}
9254
9255
9256// Test that we can set a property on the global object even if there
9257// is a read-only property in the prototype chain.
9258TEST(ReadOnlyPropertyInGlobalProto) {
9259 v8::HandleScope scope;
9260 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9261 LocalContext context(0, templ);
9262 v8::Handle<v8::Object> global = context->Global();
9263 v8::Handle<v8::Object> global_proto =
9264 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9265 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9266 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9267 // Check without 'eval' or 'with'.
9268 v8::Handle<v8::Value> res =
9269 CompileRun("function f() { x = 42; return x; }; f()");
9270 // Check with 'eval'.
9271 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9272 CHECK_EQ(v8::Integer::New(42), res);
9273 // Check with 'with'.
9274 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9275 CHECK_EQ(v8::Integer::New(42), res);
9276}
9277
9278static int force_set_set_count = 0;
9279static int force_set_get_count = 0;
9280bool pass_on_get = false;
9281
9282static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9283 const v8::AccessorInfo& info) {
9284 force_set_get_count++;
9285 if (pass_on_get) {
9286 return v8::Handle<v8::Value>();
9287 } else {
9288 return v8::Int32::New(3);
9289 }
9290}
9291
9292static void ForceSetSetter(v8::Local<v8::String> name,
9293 v8::Local<v8::Value> value,
9294 const v8::AccessorInfo& info) {
9295 force_set_set_count++;
9296}
9297
9298static v8::Handle<v8::Value> ForceSetInterceptSetter(
9299 v8::Local<v8::String> name,
9300 v8::Local<v8::Value> value,
9301 const v8::AccessorInfo& info) {
9302 force_set_set_count++;
9303 return v8::Undefined();
9304}
9305
9306TEST(ForceSet) {
9307 force_set_get_count = 0;
9308 force_set_set_count = 0;
9309 pass_on_get = false;
9310
9311 v8::HandleScope scope;
9312 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9313 v8::Handle<v8::String> access_property = v8::String::New("a");
9314 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9315 LocalContext context(NULL, templ);
9316 v8::Handle<v8::Object> global = context->Global();
9317
9318 // Ordinary properties
9319 v8::Handle<v8::String> simple_property = v8::String::New("p");
9320 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9321 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9322 // This should fail because the property is read-only
9323 global->Set(simple_property, v8::Int32::New(5));
9324 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9325 // This should succeed even though the property is read-only
9326 global->ForceSet(simple_property, v8::Int32::New(6));
9327 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9328
9329 // Accessors
9330 CHECK_EQ(0, force_set_set_count);
9331 CHECK_EQ(0, force_set_get_count);
9332 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9333 // CHECK_EQ the property shouldn't override it, just call the setter
9334 // which in this case does nothing.
9335 global->Set(access_property, v8::Int32::New(7));
9336 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9337 CHECK_EQ(1, force_set_set_count);
9338 CHECK_EQ(2, force_set_get_count);
9339 // Forcing the property to be set should override the accessor without
9340 // calling it
9341 global->ForceSet(access_property, v8::Int32::New(8));
9342 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9343 CHECK_EQ(1, force_set_set_count);
9344 CHECK_EQ(2, force_set_get_count);
9345}
9346
9347TEST(ForceSetWithInterceptor) {
9348 force_set_get_count = 0;
9349 force_set_set_count = 0;
9350 pass_on_get = false;
9351
9352 v8::HandleScope scope;
9353 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9354 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9355 LocalContext context(NULL, templ);
9356 v8::Handle<v8::Object> global = context->Global();
9357
9358 v8::Handle<v8::String> some_property = v8::String::New("a");
9359 CHECK_EQ(0, force_set_set_count);
9360 CHECK_EQ(0, force_set_get_count);
9361 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9362 // Setting the property shouldn't override it, just call the setter
9363 // which in this case does nothing.
9364 global->Set(some_property, v8::Int32::New(7));
9365 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9366 CHECK_EQ(1, force_set_set_count);
9367 CHECK_EQ(2, force_set_get_count);
9368 // Getting the property when the interceptor returns an empty handle
9369 // should yield undefined, since the property isn't present on the
9370 // object itself yet.
9371 pass_on_get = true;
9372 CHECK(global->Get(some_property)->IsUndefined());
9373 CHECK_EQ(1, force_set_set_count);
9374 CHECK_EQ(3, force_set_get_count);
9375 // Forcing the property to be set should cause the value to be
9376 // set locally without calling the interceptor.
9377 global->ForceSet(some_property, v8::Int32::New(8));
9378 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9379 CHECK_EQ(1, force_set_set_count);
9380 CHECK_EQ(4, force_set_get_count);
9381 // Reenabling the interceptor should cause it to take precedence over
9382 // the property
9383 pass_on_get = false;
9384 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9385 CHECK_EQ(1, force_set_set_count);
9386 CHECK_EQ(5, force_set_get_count);
9387 // The interceptor should also work for other properties
9388 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9389 CHECK_EQ(1, force_set_set_count);
9390 CHECK_EQ(6, force_set_get_count);
9391}
9392
9393
9394THREADED_TEST(ForceDelete) {
9395 v8::HandleScope scope;
9396 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9397 LocalContext context(NULL, templ);
9398 v8::Handle<v8::Object> global = context->Global();
9399
9400 // Ordinary properties
9401 v8::Handle<v8::String> simple_property = v8::String::New("p");
9402 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9403 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9404 // This should fail because the property is dont-delete.
9405 CHECK(!global->Delete(simple_property));
9406 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9407 // This should succeed even though the property is dont-delete.
9408 CHECK(global->ForceDelete(simple_property));
9409 CHECK(global->Get(simple_property)->IsUndefined());
9410}
9411
9412
9413static int force_delete_interceptor_count = 0;
9414static bool pass_on_delete = false;
9415
9416
9417static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9418 v8::Local<v8::String> name,
9419 const v8::AccessorInfo& info) {
9420 force_delete_interceptor_count++;
9421 if (pass_on_delete) {
9422 return v8::Handle<v8::Boolean>();
9423 } else {
9424 return v8::True();
9425 }
9426}
9427
9428
9429THREADED_TEST(ForceDeleteWithInterceptor) {
9430 force_delete_interceptor_count = 0;
9431 pass_on_delete = false;
9432
9433 v8::HandleScope scope;
9434 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9435 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9436 LocalContext context(NULL, templ);
9437 v8::Handle<v8::Object> global = context->Global();
9438
9439 v8::Handle<v8::String> some_property = v8::String::New("a");
9440 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9441
9442 // Deleting a property should get intercepted and nothing should
9443 // happen.
9444 CHECK_EQ(0, force_delete_interceptor_count);
9445 CHECK(global->Delete(some_property));
9446 CHECK_EQ(1, force_delete_interceptor_count);
9447 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9448 // Deleting the property when the interceptor returns an empty
9449 // handle should not delete the property since it is DontDelete.
9450 pass_on_delete = true;
9451 CHECK(!global->Delete(some_property));
9452 CHECK_EQ(2, force_delete_interceptor_count);
9453 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9454 // Forcing the property to be deleted should delete the value
9455 // without calling the interceptor.
9456 CHECK(global->ForceDelete(some_property));
9457 CHECK(global->Get(some_property)->IsUndefined());
9458 CHECK_EQ(2, force_delete_interceptor_count);
9459}
9460
9461
9462// Make sure that forcing a delete invalidates any IC stubs, so we
9463// don't read the hole value.
9464THREADED_TEST(ForceDeleteIC) {
9465 v8::HandleScope scope;
9466 LocalContext context;
9467 // Create a DontDelete variable on the global object.
9468 CompileRun("this.__proto__ = { foo: 'horse' };"
9469 "var foo = 'fish';"
9470 "function f() { return foo.length; }");
9471 // Initialize the IC for foo in f.
9472 CompileRun("for (var i = 0; i < 4; i++) f();");
9473 // Make sure the value of foo is correct before the deletion.
9474 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9475 // Force the deletion of foo.
9476 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9477 // Make sure the value for foo is read from the prototype, and that
9478 // we don't get in trouble with reading the deleted cell value
9479 // sentinel.
9480 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9481}
9482
9483
9484v8::Persistent<Context> calling_context0;
9485v8::Persistent<Context> calling_context1;
9486v8::Persistent<Context> calling_context2;
9487
9488
9489// Check that the call to the callback is initiated in
9490// calling_context2, the directly calling context is calling_context1
9491// and the callback itself is in calling_context0.
9492static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9493 ApiTestFuzzer::Fuzz();
9494 CHECK(Context::GetCurrent() == calling_context0);
9495 CHECK(Context::GetCalling() == calling_context1);
9496 CHECK(Context::GetEntered() == calling_context2);
9497 return v8::Integer::New(42);
9498}
9499
9500
9501THREADED_TEST(GetCallingContext) {
9502 v8::HandleScope scope;
9503
9504 calling_context0 = Context::New();
9505 calling_context1 = Context::New();
9506 calling_context2 = Context::New();
9507
9508 // Allow cross-domain access.
9509 Local<String> token = v8_str("<security token>");
9510 calling_context0->SetSecurityToken(token);
9511 calling_context1->SetSecurityToken(token);
9512 calling_context2->SetSecurityToken(token);
9513
9514 // Create an object with a C++ callback in context0.
9515 calling_context0->Enter();
9516 Local<v8::FunctionTemplate> callback_templ =
9517 v8::FunctionTemplate::New(GetCallingContextCallback);
9518 calling_context0->Global()->Set(v8_str("callback"),
9519 callback_templ->GetFunction());
9520 calling_context0->Exit();
9521
9522 // Expose context0 in context1 and setup a function that calls the
9523 // callback function.
9524 calling_context1->Enter();
9525 calling_context1->Global()->Set(v8_str("context0"),
9526 calling_context0->Global());
9527 CompileRun("function f() { context0.callback() }");
9528 calling_context1->Exit();
9529
9530 // Expose context1 in context2 and call the callback function in
9531 // context0 indirectly through f in context1.
9532 calling_context2->Enter();
9533 calling_context2->Global()->Set(v8_str("context1"),
9534 calling_context1->Global());
9535 CompileRun("context1.f()");
9536 calling_context2->Exit();
9537
9538 // Dispose the contexts to allow them to be garbage collected.
9539 calling_context0.Dispose();
9540 calling_context1.Dispose();
9541 calling_context2.Dispose();
9542 calling_context0.Clear();
9543 calling_context1.Clear();
9544 calling_context2.Clear();
9545}
9546
9547
9548// Check that a variable declaration with no explicit initialization
9549// value does not shadow an existing property in the prototype chain.
9550//
9551// This is consistent with Firefox and Safari.
9552//
9553// See http://crbug.com/12548.
9554THREADED_TEST(InitGlobalVarInProtoChain) {
9555 v8::HandleScope scope;
9556 LocalContext context;
9557 // Introduce a variable in the prototype chain.
9558 CompileRun("__proto__.x = 42");
9559 v8::Handle<v8::Value> result = CompileRun("var x; x");
9560 CHECK(!result->IsUndefined());
9561 CHECK_EQ(42, result->Int32Value());
9562}
9563
9564
9565// Regression test for issue 398.
9566// If a function is added to an object, creating a constant function
9567// field, and the result is cloned, replacing the constant function on the
9568// original should not affect the clone.
9569// See http://code.google.com/p/v8/issues/detail?id=398
9570THREADED_TEST(ReplaceConstantFunction) {
9571 v8::HandleScope scope;
9572 LocalContext context;
9573 v8::Handle<v8::Object> obj = v8::Object::New();
9574 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9575 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9576 obj->Set(foo_string, func_templ->GetFunction());
9577 v8::Handle<v8::Object> obj_clone = obj->Clone();
9578 obj_clone->Set(foo_string, v8::String::New("Hello"));
9579 CHECK(!obj->Get(foo_string)->IsUndefined());
9580}
9581
9582
9583// Regression test for http://crbug.com/16276.
9584THREADED_TEST(Regress16276) {
9585 v8::HandleScope scope;
9586 LocalContext context;
9587 // Force the IC in f to be a dictionary load IC.
9588 CompileRun("function f(obj) { return obj.x; }\n"
9589 "var obj = { x: { foo: 42 }, y: 87 };\n"
9590 "var x = obj.x;\n"
9591 "delete obj.y;\n"
9592 "for (var i = 0; i < 5; i++) f(obj);");
9593 // Detach the global object to make 'this' refer directly to the
9594 // global object (not the proxy), and make sure that the dictionary
9595 // load IC doesn't mess up loading directly from the global object.
9596 context->DetachGlobal();
9597 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9598}
9599
9600
9601THREADED_TEST(PixelArray) {
9602 v8::HandleScope scope;
9603 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009604 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009605 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9606 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9607 pixel_data);
9608 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9609 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009610 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009611 }
9612 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9613 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009614 CHECK_EQ(i % 256, pixels->get(i));
9615 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009616 }
9617
9618 v8::Handle<v8::Object> obj = v8::Object::New();
9619 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9620 // Set the elements to be the pixels.
9621 // jsobj->set_elements(*pixels);
9622 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9623 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9624 obj->Set(v8_str("field"), v8::Int32::New(1503));
9625 context->Global()->Set(v8_str("pixels"), obj);
9626 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9627 CHECK_EQ(1503, result->Int32Value());
9628 result = CompileRun("pixels[1]");
9629 CHECK_EQ(1, result->Int32Value());
9630
9631 result = CompileRun("var sum = 0;"
9632 "for (var i = 0; i < 8; i++) {"
9633 " sum += pixels[i] = pixels[i] = -i;"
9634 "}"
9635 "sum;");
9636 CHECK_EQ(-28, result->Int32Value());
9637
9638 result = CompileRun("var sum = 0;"
9639 "for (var i = 0; i < 8; i++) {"
9640 " sum += pixels[i] = pixels[i] = 0;"
9641 "}"
9642 "sum;");
9643 CHECK_EQ(0, result->Int32Value());
9644
9645 result = CompileRun("var sum = 0;"
9646 "for (var i = 0; i < 8; i++) {"
9647 " sum += pixels[i] = pixels[i] = 255;"
9648 "}"
9649 "sum;");
9650 CHECK_EQ(8 * 255, result->Int32Value());
9651
9652 result = CompileRun("var sum = 0;"
9653 "for (var i = 0; i < 8; i++) {"
9654 " sum += pixels[i] = pixels[i] = 256 + i;"
9655 "}"
9656 "sum;");
9657 CHECK_EQ(2076, result->Int32Value());
9658
9659 result = CompileRun("var sum = 0;"
9660 "for (var i = 0; i < 8; i++) {"
9661 " sum += pixels[i] = pixels[i] = i;"
9662 "}"
9663 "sum;");
9664 CHECK_EQ(28, result->Int32Value());
9665
9666 result = CompileRun("var sum = 0;"
9667 "for (var i = 0; i < 8; i++) {"
9668 " sum += pixels[i];"
9669 "}"
9670 "sum;");
9671 CHECK_EQ(28, result->Int32Value());
9672
9673 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9674 i::SetElement(jsobj, 1, value);
9675 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9676 *value.location() = i::Smi::FromInt(256);
9677 i::SetElement(jsobj, 1, value);
9678 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9679 *value.location() = i::Smi::FromInt(-1);
9680 i::SetElement(jsobj, 1, value);
9681 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9682
9683 result = CompileRun("for (var i = 0; i < 8; i++) {"
9684 " pixels[i] = (i * 65) - 109;"
9685 "}"
9686 "pixels[1] + pixels[6];");
9687 CHECK_EQ(255, result->Int32Value());
9688 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9689 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9690 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9691 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9692 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9693 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9694 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9695 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9696 result = CompileRun("var sum = 0;"
9697 "for (var i = 0; i < 8; i++) {"
9698 " sum += pixels[i];"
9699 "}"
9700 "sum;");
9701 CHECK_EQ(984, result->Int32Value());
9702
9703 result = CompileRun("for (var i = 0; i < 8; i++) {"
9704 " pixels[i] = (i * 1.1);"
9705 "}"
9706 "pixels[1] + pixels[6];");
9707 CHECK_EQ(8, result->Int32Value());
9708 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9709 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9710 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9711 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9712 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9713 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9714 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9715 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9716
9717 result = CompileRun("for (var i = 0; i < 8; i++) {"
9718 " pixels[7] = undefined;"
9719 "}"
9720 "pixels[7];");
9721 CHECK_EQ(0, result->Int32Value());
9722 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9723
9724 result = CompileRun("for (var i = 0; i < 8; i++) {"
9725 " pixels[6] = '2.3';"
9726 "}"
9727 "pixels[6];");
9728 CHECK_EQ(2, result->Int32Value());
9729 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9730
9731 result = CompileRun("for (var i = 0; i < 8; i++) {"
9732 " pixels[5] = NaN;"
9733 "}"
9734 "pixels[5];");
9735 CHECK_EQ(0, result->Int32Value());
9736 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9737
9738 result = CompileRun("for (var i = 0; i < 8; i++) {"
9739 " pixels[8] = Infinity;"
9740 "}"
9741 "pixels[8];");
9742 CHECK_EQ(255, result->Int32Value());
9743 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9744
9745 result = CompileRun("for (var i = 0; i < 8; i++) {"
9746 " pixels[9] = -Infinity;"
9747 "}"
9748 "pixels[9];");
9749 CHECK_EQ(0, result->Int32Value());
9750 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9751
9752 result = CompileRun("pixels[3] = 33;"
9753 "delete pixels[3];"
9754 "pixels[3];");
9755 CHECK_EQ(33, result->Int32Value());
9756
9757 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9758 "pixels[2] = 12; pixels[3] = 13;"
9759 "pixels.__defineGetter__('2',"
9760 "function() { return 120; });"
9761 "pixels[2];");
9762 CHECK_EQ(12, result->Int32Value());
9763
9764 result = CompileRun("var js_array = new Array(40);"
9765 "js_array[0] = 77;"
9766 "js_array;");
9767 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9768
9769 result = CompileRun("pixels[1] = 23;"
9770 "pixels.__proto__ = [];"
9771 "js_array.__proto__ = pixels;"
9772 "js_array.concat(pixels);");
9773 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9774 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9775
9776 result = CompileRun("pixels[1] = 23;");
9777 CHECK_EQ(23, result->Int32Value());
9778
Steve Blockd0582a62009-12-15 09:54:21 +00009779 // Test for index greater than 255. Regression test for:
9780 // http://code.google.com/p/chromium/issues/detail?id=26337.
9781 result = CompileRun("pixels[256] = 255;");
9782 CHECK_EQ(255, result->Int32Value());
9783 result = CompileRun("var i = 0;"
9784 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9785 "i");
9786 CHECK_EQ(255, result->Int32Value());
9787
Steve Blocka7e24c12009-10-30 11:49:00 +00009788 free(pixel_data);
9789}
9790
9791
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009792THREADED_TEST(PixelArrayInfo) {
9793 v8::HandleScope scope;
9794 LocalContext context;
9795 for (int size = 0; size < 100; size += 10) {
9796 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9797 v8::Handle<v8::Object> obj = v8::Object::New();
9798 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9799 CHECK(obj->HasIndexedPropertiesInPixelData());
9800 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9801 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9802 free(pixel_data);
9803 }
9804}
9805
9806
9807static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9808 switch (array_type) {
9809 case v8::kExternalByteArray:
9810 case v8::kExternalUnsignedByteArray:
9811 return 1;
9812 break;
9813 case v8::kExternalShortArray:
9814 case v8::kExternalUnsignedShortArray:
9815 return 2;
9816 break;
9817 case v8::kExternalIntArray:
9818 case v8::kExternalUnsignedIntArray:
9819 case v8::kExternalFloatArray:
9820 return 4;
9821 break;
9822 default:
9823 UNREACHABLE();
9824 return -1;
9825 }
9826 UNREACHABLE();
9827 return -1;
9828}
9829
9830
Steve Block3ce2e202009-11-05 08:53:23 +00009831template <class ExternalArrayClass, class ElementType>
9832static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9833 int64_t low,
9834 int64_t high) {
9835 v8::HandleScope scope;
9836 LocalContext context;
9837 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009838 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +00009839 ElementType* array_data =
9840 static_cast<ElementType*>(malloc(kElementCount * element_size));
9841 i::Handle<ExternalArrayClass> array =
9842 i::Handle<ExternalArrayClass>::cast(
9843 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9844 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9845 for (int i = 0; i < kElementCount; i++) {
9846 array->set(i, static_cast<ElementType>(i));
9847 }
9848 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9849 for (int i = 0; i < kElementCount; i++) {
9850 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9851 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9852 }
9853
9854 v8::Handle<v8::Object> obj = v8::Object::New();
9855 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9856 // Set the elements to be the external array.
9857 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9858 array_type,
9859 kElementCount);
9860 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9861 obj->Set(v8_str("field"), v8::Int32::New(1503));
9862 context->Global()->Set(v8_str("ext_array"), obj);
9863 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9864 CHECK_EQ(1503, result->Int32Value());
9865 result = CompileRun("ext_array[1]");
9866 CHECK_EQ(1, result->Int32Value());
9867
9868 // Check pass through of assigned smis
9869 result = CompileRun("var sum = 0;"
9870 "for (var i = 0; i < 8; i++) {"
9871 " sum += ext_array[i] = ext_array[i] = -i;"
9872 "}"
9873 "sum;");
9874 CHECK_EQ(-28, result->Int32Value());
9875
9876 // Check assigned smis
9877 result = CompileRun("for (var i = 0; i < 8; i++) {"
9878 " ext_array[i] = i;"
9879 "}"
9880 "var sum = 0;"
9881 "for (var i = 0; i < 8; i++) {"
9882 " sum += ext_array[i];"
9883 "}"
9884 "sum;");
9885 CHECK_EQ(28, result->Int32Value());
9886
9887 // Check assigned smis in reverse order
9888 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9889 " ext_array[i] = i;"
9890 "}"
9891 "var sum = 0;"
9892 "for (var i = 0; i < 8; i++) {"
9893 " sum += ext_array[i];"
9894 "}"
9895 "sum;");
9896 CHECK_EQ(28, result->Int32Value());
9897
9898 // Check pass through of assigned HeapNumbers
9899 result = CompileRun("var sum = 0;"
9900 "for (var i = 0; i < 16; i+=2) {"
9901 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9902 "}"
9903 "sum;");
9904 CHECK_EQ(-28, result->Int32Value());
9905
9906 // Check assigned HeapNumbers
9907 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9908 " ext_array[i] = (i * 0.5);"
9909 "}"
9910 "var sum = 0;"
9911 "for (var i = 0; i < 16; i+=2) {"
9912 " sum += ext_array[i];"
9913 "}"
9914 "sum;");
9915 CHECK_EQ(28, result->Int32Value());
9916
9917 // Check assigned HeapNumbers in reverse order
9918 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9919 " ext_array[i] = (i * 0.5);"
9920 "}"
9921 "var sum = 0;"
9922 "for (var i = 0; i < 16; i+=2) {"
9923 " sum += ext_array[i];"
9924 "}"
9925 "sum;");
9926 CHECK_EQ(28, result->Int32Value());
9927
9928 i::ScopedVector<char> test_buf(1024);
9929
9930 // Check legal boundary conditions.
9931 // The repeated loads and stores ensure the ICs are exercised.
9932 const char* boundary_program =
9933 "var res = 0;"
9934 "for (var i = 0; i < 16; i++) {"
9935 " ext_array[i] = %lld;"
9936 " if (i > 8) {"
9937 " res = ext_array[i];"
9938 " }"
9939 "}"
9940 "res;";
9941 i::OS::SNPrintF(test_buf,
9942 boundary_program,
9943 low);
9944 result = CompileRun(test_buf.start());
9945 CHECK_EQ(low, result->IntegerValue());
9946
9947 i::OS::SNPrintF(test_buf,
9948 boundary_program,
9949 high);
9950 result = CompileRun(test_buf.start());
9951 CHECK_EQ(high, result->IntegerValue());
9952
9953 // Check misprediction of type in IC.
9954 result = CompileRun("var tmp_array = ext_array;"
9955 "var sum = 0;"
9956 "for (var i = 0; i < 8; i++) {"
9957 " tmp_array[i] = i;"
9958 " sum += tmp_array[i];"
9959 " if (i == 4) {"
9960 " tmp_array = {};"
9961 " }"
9962 "}"
9963 "sum;");
9964 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9965 CHECK_EQ(28, result->Int32Value());
9966
9967 // Make sure out-of-range loads do not throw.
9968 i::OS::SNPrintF(test_buf,
9969 "var caught_exception = false;"
9970 "try {"
9971 " ext_array[%d];"
9972 "} catch (e) {"
9973 " caught_exception = true;"
9974 "}"
9975 "caught_exception;",
9976 kElementCount);
9977 result = CompileRun(test_buf.start());
9978 CHECK_EQ(false, result->BooleanValue());
9979
9980 // Make sure out-of-range stores do not throw.
9981 i::OS::SNPrintF(test_buf,
9982 "var caught_exception = false;"
9983 "try {"
9984 " ext_array[%d] = 1;"
9985 "} catch (e) {"
9986 " caught_exception = true;"
9987 "}"
9988 "caught_exception;",
9989 kElementCount);
9990 result = CompileRun(test_buf.start());
9991 CHECK_EQ(false, result->BooleanValue());
9992
9993 // Check other boundary conditions, values and operations.
9994 result = CompileRun("for (var i = 0; i < 8; i++) {"
9995 " ext_array[7] = undefined;"
9996 "}"
9997 "ext_array[7];");
9998 CHECK_EQ(0, result->Int32Value());
9999 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
10000
10001 result = CompileRun("for (var i = 0; i < 8; i++) {"
10002 " ext_array[6] = '2.3';"
10003 "}"
10004 "ext_array[6];");
10005 CHECK_EQ(2, result->Int32Value());
10006 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
10007
10008 if (array_type != v8::kExternalFloatArray) {
10009 // Though the specification doesn't state it, be explicit about
10010 // converting NaNs and +/-Infinity to zero.
10011 result = CompileRun("for (var i = 0; i < 8; i++) {"
10012 " ext_array[i] = 5;"
10013 "}"
10014 "for (var i = 0; i < 8; i++) {"
10015 " ext_array[i] = NaN;"
10016 "}"
10017 "ext_array[5];");
10018 CHECK_EQ(0, result->Int32Value());
10019 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10020
10021 result = CompileRun("for (var i = 0; i < 8; i++) {"
10022 " ext_array[i] = 5;"
10023 "}"
10024 "for (var i = 0; i < 8; i++) {"
10025 " ext_array[i] = Infinity;"
10026 "}"
10027 "ext_array[5];");
10028 CHECK_EQ(0, result->Int32Value());
10029 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10030
10031 result = CompileRun("for (var i = 0; i < 8; i++) {"
10032 " ext_array[i] = 5;"
10033 "}"
10034 "for (var i = 0; i < 8; i++) {"
10035 " ext_array[i] = -Infinity;"
10036 "}"
10037 "ext_array[5];");
10038 CHECK_EQ(0, result->Int32Value());
10039 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10040 }
10041
10042 result = CompileRun("ext_array[3] = 33;"
10043 "delete ext_array[3];"
10044 "ext_array[3];");
10045 CHECK_EQ(33, result->Int32Value());
10046
10047 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
10048 "ext_array[2] = 12; ext_array[3] = 13;"
10049 "ext_array.__defineGetter__('2',"
10050 "function() { return 120; });"
10051 "ext_array[2];");
10052 CHECK_EQ(12, result->Int32Value());
10053
10054 result = CompileRun("var js_array = new Array(40);"
10055 "js_array[0] = 77;"
10056 "js_array;");
10057 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10058
10059 result = CompileRun("ext_array[1] = 23;"
10060 "ext_array.__proto__ = [];"
10061 "js_array.__proto__ = ext_array;"
10062 "js_array.concat(ext_array);");
10063 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10064 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10065
10066 result = CompileRun("ext_array[1] = 23;");
10067 CHECK_EQ(23, result->Int32Value());
10068
Steve Blockd0582a62009-12-15 09:54:21 +000010069 // Test more complex manipulations which cause eax to contain values
10070 // that won't be completely overwritten by loads from the arrays.
10071 // This catches bugs in the instructions used for the KeyedLoadIC
10072 // for byte and word types.
10073 {
10074 const int kXSize = 300;
10075 const int kYSize = 300;
10076 const int kLargeElementCount = kXSize * kYSize * 4;
10077 ElementType* large_array_data =
10078 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
10079 i::Handle<ExternalArrayClass> large_array =
10080 i::Handle<ExternalArrayClass>::cast(
10081 i::Factory::NewExternalArray(kLargeElementCount,
10082 array_type,
10083 array_data));
10084 v8::Handle<v8::Object> large_obj = v8::Object::New();
10085 // Set the elements to be the external array.
10086 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
10087 array_type,
10088 kLargeElementCount);
10089 context->Global()->Set(v8_str("large_array"), large_obj);
10090 // Initialize contents of a few rows.
10091 for (int x = 0; x < 300; x++) {
10092 int row = 0;
10093 int offset = row * 300 * 4;
10094 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10095 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10096 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10097 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10098 row = 150;
10099 offset = row * 300 * 4;
10100 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10101 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10102 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10103 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10104 row = 298;
10105 offset = row * 300 * 4;
10106 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10107 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10108 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10109 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10110 }
10111 // The goal of the code below is to make "offset" large enough
10112 // that the computation of the index (which goes into eax) has
10113 // high bits set which will not be overwritten by a byte or short
10114 // load.
10115 result = CompileRun("var failed = false;"
10116 "var offset = 0;"
10117 "for (var i = 0; i < 300; i++) {"
10118 " if (large_array[4 * i] != 127 ||"
10119 " large_array[4 * i + 1] != 0 ||"
10120 " large_array[4 * i + 2] != 0 ||"
10121 " large_array[4 * i + 3] != 127) {"
10122 " failed = true;"
10123 " }"
10124 "}"
10125 "offset = 150 * 300 * 4;"
10126 "for (var i = 0; i < 300; i++) {"
10127 " if (large_array[offset + 4 * i] != 127 ||"
10128 " large_array[offset + 4 * i + 1] != 0 ||"
10129 " large_array[offset + 4 * i + 2] != 0 ||"
10130 " large_array[offset + 4 * i + 3] != 127) {"
10131 " failed = true;"
10132 " }"
10133 "}"
10134 "offset = 298 * 300 * 4;"
10135 "for (var i = 0; i < 300; i++) {"
10136 " if (large_array[offset + 4 * i] != 127 ||"
10137 " large_array[offset + 4 * i + 1] != 0 ||"
10138 " large_array[offset + 4 * i + 2] != 0 ||"
10139 " large_array[offset + 4 * i + 3] != 127) {"
10140 " failed = true;"
10141 " }"
10142 "}"
10143 "!failed;");
10144 CHECK_EQ(true, result->BooleanValue());
10145 free(large_array_data);
10146 }
10147
Steve Block3ce2e202009-11-05 08:53:23 +000010148 free(array_data);
10149}
10150
10151
10152THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010153 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010154 v8::kExternalByteArray,
10155 -128,
10156 127);
10157}
10158
10159
10160THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010161 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010162 v8::kExternalUnsignedByteArray,
10163 0,
10164 255);
10165}
10166
10167
10168THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010169 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010170 v8::kExternalShortArray,
10171 -32768,
10172 32767);
10173}
10174
10175
10176THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010177 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010178 v8::kExternalUnsignedShortArray,
10179 0,
10180 65535);
10181}
10182
10183
10184THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010185 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010186 v8::kExternalIntArray,
10187 INT_MIN, // -2147483648
10188 INT_MAX); // 2147483647
10189}
10190
10191
10192THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010193 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010194 v8::kExternalUnsignedIntArray,
10195 0,
10196 UINT_MAX); // 4294967295
10197}
10198
10199
10200THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010201 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000010202 v8::kExternalFloatArray,
10203 -500,
10204 500);
10205}
10206
10207
10208THREADED_TEST(ExternalArrays) {
10209 TestExternalByteArray();
10210 TestExternalUnsignedByteArray();
10211 TestExternalShortArray();
10212 TestExternalUnsignedShortArray();
10213 TestExternalIntArray();
10214 TestExternalUnsignedIntArray();
10215 TestExternalFloatArray();
10216}
10217
10218
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010219void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10220 v8::HandleScope scope;
10221 LocalContext context;
10222 for (int size = 0; size < 100; size += 10) {
10223 int element_size = ExternalArrayElementSize(array_type);
10224 void* external_data = malloc(size * element_size);
10225 v8::Handle<v8::Object> obj = v8::Object::New();
10226 obj->SetIndexedPropertiesToExternalArrayData(
10227 external_data, array_type, size);
10228 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10229 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10230 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10231 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10232 free(external_data);
10233 }
10234}
10235
10236
10237THREADED_TEST(ExternalArrayInfo) {
10238 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10239 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10240 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10241 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10242 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10243 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10244 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10245}
10246
10247
Steve Blocka7e24c12009-10-30 11:49:00 +000010248THREADED_TEST(ScriptContextDependence) {
10249 v8::HandleScope scope;
10250 LocalContext c1;
10251 const char *source = "foo";
10252 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10253 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10254 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10255 CHECK_EQ(dep->Run()->Int32Value(), 100);
10256 CHECK_EQ(indep->Run()->Int32Value(), 100);
10257 LocalContext c2;
10258 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10259 CHECK_EQ(dep->Run()->Int32Value(), 100);
10260 CHECK_EQ(indep->Run()->Int32Value(), 101);
10261}
10262
10263
10264THREADED_TEST(StackTrace) {
10265 v8::HandleScope scope;
10266 LocalContext context;
10267 v8::TryCatch try_catch;
10268 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10269 v8::Handle<v8::String> src = v8::String::New(source);
10270 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10271 v8::Script::New(src, origin)->Run();
10272 CHECK(try_catch.HasCaught());
10273 v8::String::Utf8Value stack(try_catch.StackTrace());
10274 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10275}
10276
10277
Kristian Monsen25f61362010-05-21 11:50:48 +010010278// Checks that a StackFrame has certain expected values.
10279void checkStackFrame(const char* expected_script_name,
10280 const char* expected_func_name, int expected_line_number,
10281 int expected_column, bool is_eval, bool is_constructor,
10282 v8::Handle<v8::StackFrame> frame) {
10283 v8::HandleScope scope;
10284 v8::String::Utf8Value func_name(frame->GetFunctionName());
10285 v8::String::Utf8Value script_name(frame->GetScriptName());
10286 if (*script_name == NULL) {
10287 // The situation where there is no associated script, like for evals.
10288 CHECK(expected_script_name == NULL);
10289 } else {
10290 CHECK(strstr(*script_name, expected_script_name) != NULL);
10291 }
10292 CHECK(strstr(*func_name, expected_func_name) != NULL);
10293 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10294 CHECK_EQ(expected_column, frame->GetColumn());
10295 CHECK_EQ(is_eval, frame->IsEval());
10296 CHECK_EQ(is_constructor, frame->IsConstructor());
10297}
10298
10299
10300v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10301 v8::HandleScope scope;
10302 const char* origin = "capture-stack-trace-test";
10303 const int kOverviewTest = 1;
10304 const int kDetailedTest = 2;
10305
10306 ASSERT(args.Length() == 1);
10307
10308 int testGroup = args[0]->Int32Value();
10309 if (testGroup == kOverviewTest) {
10310 v8::Handle<v8::StackTrace> stackTrace =
10311 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10312 CHECK_EQ(4, stackTrace->GetFrameCount());
10313 checkStackFrame(origin, "bar", 2, 10, false, false,
10314 stackTrace->GetFrame(0));
10315 checkStackFrame(origin, "foo", 6, 3, false, false,
10316 stackTrace->GetFrame(1));
10317 checkStackFrame(NULL, "", 1, 1, false, false,
10318 stackTrace->GetFrame(2));
10319 // The last frame is an anonymous function that has the initial call.
10320 checkStackFrame(origin, "", 8, 7, false, false,
10321 stackTrace->GetFrame(3));
10322
10323 CHECK(stackTrace->AsArray()->IsArray());
10324 } else if (testGroup == kDetailedTest) {
10325 v8::Handle<v8::StackTrace> stackTrace =
10326 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10327 CHECK_EQ(4, stackTrace->GetFrameCount());
10328 checkStackFrame(origin, "bat", 4, 22, false, false,
10329 stackTrace->GetFrame(0));
10330 checkStackFrame(origin, "baz", 8, 3, false, true,
10331 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010332#ifdef ENABLE_DEBUGGER_SUPPORT
10333 bool is_eval = true;
10334#else // ENABLE_DEBUGGER_SUPPORT
10335 bool is_eval = false;
10336#endif // ENABLE_DEBUGGER_SUPPORT
10337
10338 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010339 stackTrace->GetFrame(2));
10340 // The last frame is an anonymous function that has the initial call to foo.
10341 checkStackFrame(origin, "", 10, 1, false, false,
10342 stackTrace->GetFrame(3));
10343
10344 CHECK(stackTrace->AsArray()->IsArray());
10345 }
10346 return v8::Undefined();
10347}
10348
10349
10350// Tests the C++ StackTrace API.
10351THREADED_TEST(CaptureStackTrace) {
10352 v8::HandleScope scope;
10353 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10354 Local<ObjectTemplate> templ = ObjectTemplate::New();
10355 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10356 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10357 LocalContext context(0, templ);
10358
10359 // Test getting OVERVIEW information. Should ignore information that is not
10360 // script name, function name, line number, and column offset.
10361 const char *overview_source =
10362 "function bar() {\n"
10363 " var y; AnalyzeStackInNativeCode(1);\n"
10364 "}\n"
10365 "function foo() {\n"
10366 "\n"
10367 " bar();\n"
10368 "}\n"
10369 "var x;eval('new foo();');";
10370 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10371 v8::Handle<Value> overview_result =
10372 v8::Script::New(overview_src, origin)->Run();
10373 ASSERT(!overview_result.IsEmpty());
10374 ASSERT(overview_result->IsObject());
10375
10376 // Test getting DETAILED information.
10377 const char *detailed_source =
10378 "function bat() {AnalyzeStackInNativeCode(2);\n"
10379 "}\n"
10380 "\n"
10381 "function baz() {\n"
10382 " bat();\n"
10383 "}\n"
10384 "eval('new baz();');";
10385 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10386 // Make the script using a non-zero line and column offset.
10387 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10388 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10389 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10390 v8::Handle<v8::Script> detailed_script(
10391 v8::Script::New(detailed_src, &detailed_origin));
10392 v8::Handle<Value> detailed_result = detailed_script->Run();
10393 ASSERT(!detailed_result.IsEmpty());
10394 ASSERT(detailed_result->IsObject());
10395}
10396
10397
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010398static void StackTraceForUncaughtExceptionListener(
10399 v8::Handle<v8::Message> message,
10400 v8::Handle<Value>) {
10401 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
10402 CHECK_EQ(2, stack_trace->GetFrameCount());
10403 checkStackFrame("origin", "foo", 2, 3, false, false,
10404 stack_trace->GetFrame(0));
10405 checkStackFrame("origin", "bar", 5, 3, false, false,
10406 stack_trace->GetFrame(1));
10407}
10408
10409TEST(CaptureStackTraceForUncaughtException) {
10410 report_count = 0;
10411 v8::HandleScope scope;
10412 LocalContext env;
10413 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
10414 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
10415
10416 Script::Compile(v8_str("function foo() {\n"
10417 " throw 1;\n"
10418 "};\n"
10419 "function bar() {\n"
10420 " foo();\n"
10421 "};"),
10422 v8_str("origin"))->Run();
10423 v8::Local<v8::Object> global = env->Global();
10424 Local<Value> trouble = global->Get(v8_str("bar"));
10425 CHECK(trouble->IsFunction());
10426 Function::Cast(*trouble)->Call(global, 0, NULL);
10427 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
10428 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
10429}
10430
10431
Steve Block3ce2e202009-11-05 08:53:23 +000010432// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010433THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010434 bool rv = false;
10435 for (int i = 0; i < 100; i++) {
10436 rv = v8::V8::IdleNotification();
10437 if (rv)
10438 break;
10439 }
10440 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010441}
10442
10443
10444static uint32_t* stack_limit;
10445
10446static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10447 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10448 return v8::Undefined();
10449}
10450
10451
10452// Uses the address of a local variable to determine the stack top now.
10453// Given a size, returns an address that is that far from the current
10454// top of stack.
10455static uint32_t* ComputeStackLimit(uint32_t size) {
10456 uint32_t* answer = &size - (size / sizeof(size));
10457 // If the size is very large and the stack is very near the bottom of
10458 // memory then the calculation above may wrap around and give an address
10459 // that is above the (downwards-growing) stack. In that case we return
10460 // a very low address.
10461 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10462 return answer;
10463}
10464
10465
10466TEST(SetResourceConstraints) {
10467 static const int K = 1024;
10468 uint32_t* set_limit = ComputeStackLimit(128 * K);
10469
10470 // Set stack limit.
10471 v8::ResourceConstraints constraints;
10472 constraints.set_stack_limit(set_limit);
10473 CHECK(v8::SetResourceConstraints(&constraints));
10474
10475 // Execute a script.
10476 v8::HandleScope scope;
10477 LocalContext env;
10478 Local<v8::FunctionTemplate> fun_templ =
10479 v8::FunctionTemplate::New(GetStackLimitCallback);
10480 Local<Function> fun = fun_templ->GetFunction();
10481 env->Global()->Set(v8_str("get_stack_limit"), fun);
10482 CompileRun("get_stack_limit();");
10483
10484 CHECK(stack_limit == set_limit);
10485}
10486
10487
10488TEST(SetResourceConstraintsInThread) {
10489 uint32_t* set_limit;
10490 {
10491 v8::Locker locker;
10492 static const int K = 1024;
10493 set_limit = ComputeStackLimit(128 * K);
10494
10495 // Set stack limit.
10496 v8::ResourceConstraints constraints;
10497 constraints.set_stack_limit(set_limit);
10498 CHECK(v8::SetResourceConstraints(&constraints));
10499
10500 // Execute a script.
10501 v8::HandleScope scope;
10502 LocalContext env;
10503 Local<v8::FunctionTemplate> fun_templ =
10504 v8::FunctionTemplate::New(GetStackLimitCallback);
10505 Local<Function> fun = fun_templ->GetFunction();
10506 env->Global()->Set(v8_str("get_stack_limit"), fun);
10507 CompileRun("get_stack_limit();");
10508
10509 CHECK(stack_limit == set_limit);
10510 }
10511 {
10512 v8::Locker locker;
10513 CHECK(stack_limit == set_limit);
10514 }
10515}
Steve Block3ce2e202009-11-05 08:53:23 +000010516
10517
10518THREADED_TEST(GetHeapStatistics) {
10519 v8::HandleScope scope;
10520 LocalContext c1;
10521 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010522 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10523 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010524 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010525 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10526 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10527}
10528
10529
10530static double DoubleFromBits(uint64_t value) {
10531 double target;
10532#ifdef BIG_ENDIAN_FLOATING_POINT
10533 const int kIntSize = 4;
10534 // Somebody swapped the lower and higher half of doubles.
10535 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10536 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10537#else
10538 memcpy(&target, &value, sizeof(target));
10539#endif
10540 return target;
10541}
10542
10543
10544static uint64_t DoubleToBits(double value) {
10545 uint64_t target;
10546#ifdef BIG_ENDIAN_FLOATING_POINT
10547 const int kIntSize = 4;
10548 // Somebody swapped the lower and higher half of doubles.
10549 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10550 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10551#else
10552 memcpy(&target, &value, sizeof(target));
10553#endif
10554 return target;
10555}
10556
10557
10558static double DoubleToDateTime(double input) {
10559 double date_limit = 864e13;
10560 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10561 return i::OS::nan_value();
10562 }
10563 return (input < 0) ? -(floor(-input)) : floor(input);
10564}
10565
10566// We don't have a consistent way to write 64-bit constants syntactically, so we
10567// split them into two 32-bit constants and combine them programmatically.
10568static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10569 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10570}
10571
10572
10573THREADED_TEST(QuietSignalingNaNs) {
10574 v8::HandleScope scope;
10575 LocalContext context;
10576 v8::TryCatch try_catch;
10577
10578 // Special double values.
10579 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10580 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10581 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10582 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10583 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10584 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10585 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10586
10587 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10588 // on either side of the epoch.
10589 double date_limit = 864e13;
10590
10591 double test_values[] = {
10592 snan,
10593 qnan,
10594 infinity,
10595 max_normal,
10596 date_limit + 1,
10597 date_limit,
10598 min_normal,
10599 max_denormal,
10600 min_denormal,
10601 0,
10602 -0,
10603 -min_denormal,
10604 -max_denormal,
10605 -min_normal,
10606 -date_limit,
10607 -date_limit - 1,
10608 -max_normal,
10609 -infinity,
10610 -qnan,
10611 -snan
10612 };
10613 int num_test_values = 20;
10614
10615 for (int i = 0; i < num_test_values; i++) {
10616 double test_value = test_values[i];
10617
10618 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10619 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10620 double stored_number = number->NumberValue();
10621 if (!IsNaN(test_value)) {
10622 CHECK_EQ(test_value, stored_number);
10623 } else {
10624 uint64_t stored_bits = DoubleToBits(stored_number);
10625 // Check if quiet nan (bits 51..62 all set).
10626 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10627 }
10628
10629 // Check that Date::New preserves non-NaNs in the date range and
10630 // quiets SNaNs.
10631 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10632 double expected_stored_date = DoubleToDateTime(test_value);
10633 double stored_date = date->NumberValue();
10634 if (!IsNaN(expected_stored_date)) {
10635 CHECK_EQ(expected_stored_date, stored_date);
10636 } else {
10637 uint64_t stored_bits = DoubleToBits(stored_date);
10638 // Check if quiet nan (bits 51..62 all set).
10639 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10640 }
10641 }
10642}
10643
10644
10645static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10646 v8::HandleScope scope;
10647 v8::TryCatch tc;
10648 v8::Handle<v8::String> str = args[0]->ToString();
10649 if (tc.HasCaught())
10650 return tc.ReThrow();
10651 return v8::Undefined();
10652}
10653
10654
10655// Test that an exception can be propagated down through a spaghetti
10656// stack using ReThrow.
10657THREADED_TEST(SpaghettiStackReThrow) {
10658 v8::HandleScope scope;
10659 LocalContext context;
10660 context->Global()->Set(
10661 v8::String::New("s"),
10662 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10663 v8::TryCatch try_catch;
10664 CompileRun(
10665 "var i = 0;"
10666 "var o = {"
10667 " toString: function () {"
10668 " if (i == 10) {"
10669 " throw 'Hey!';"
10670 " } else {"
10671 " i++;"
10672 " return s(o);"
10673 " }"
10674 " }"
10675 "};"
10676 "s(o);");
10677 CHECK(try_catch.HasCaught());
10678 v8::String::Utf8Value value(try_catch.Exception());
10679 CHECK_EQ(0, strcmp(*value, "Hey!"));
10680}
10681
10682
Steve Blockd0582a62009-12-15 09:54:21 +000010683TEST(Regress528) {
10684 v8::V8::Initialize();
10685
10686 v8::HandleScope scope;
10687 v8::Persistent<Context> context;
10688 v8::Persistent<Context> other_context;
10689 int gc_count;
10690
10691 // Create a context used to keep the code from aging in the compilation
10692 // cache.
10693 other_context = Context::New();
10694
10695 // Context-dependent context data creates reference from the compilation
10696 // cache to the global object.
10697 const char* source_simple = "1";
10698 context = Context::New();
10699 {
10700 v8::HandleScope scope;
10701
10702 context->Enter();
10703 Local<v8::String> obj = v8::String::New("");
10704 context->SetData(obj);
10705 CompileRun(source_simple);
10706 context->Exit();
10707 }
10708 context.Dispose();
10709 for (gc_count = 1; gc_count < 10; gc_count++) {
10710 other_context->Enter();
10711 CompileRun(source_simple);
10712 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010713 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010714 if (GetGlobalObjectsCount() == 1) break;
10715 }
10716 CHECK_GE(2, gc_count);
10717 CHECK_EQ(1, GetGlobalObjectsCount());
10718
10719 // Eval in a function creates reference from the compilation cache to the
10720 // global object.
10721 const char* source_eval = "function f(){eval('1')}; f()";
10722 context = Context::New();
10723 {
10724 v8::HandleScope scope;
10725
10726 context->Enter();
10727 CompileRun(source_eval);
10728 context->Exit();
10729 }
10730 context.Dispose();
10731 for (gc_count = 1; gc_count < 10; gc_count++) {
10732 other_context->Enter();
10733 CompileRun(source_eval);
10734 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010735 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010736 if (GetGlobalObjectsCount() == 1) break;
10737 }
10738 CHECK_GE(2, gc_count);
10739 CHECK_EQ(1, GetGlobalObjectsCount());
10740
10741 // Looking up the line number for an exception creates reference from the
10742 // compilation cache to the global object.
10743 const char* source_exception = "function f(){throw 1;} f()";
10744 context = Context::New();
10745 {
10746 v8::HandleScope scope;
10747
10748 context->Enter();
10749 v8::TryCatch try_catch;
10750 CompileRun(source_exception);
10751 CHECK(try_catch.HasCaught());
10752 v8::Handle<v8::Message> message = try_catch.Message();
10753 CHECK(!message.IsEmpty());
10754 CHECK_EQ(1, message->GetLineNumber());
10755 context->Exit();
10756 }
10757 context.Dispose();
10758 for (gc_count = 1; gc_count < 10; gc_count++) {
10759 other_context->Enter();
10760 CompileRun(source_exception);
10761 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010762 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010763 if (GetGlobalObjectsCount() == 1) break;
10764 }
10765 CHECK_GE(2, gc_count);
10766 CHECK_EQ(1, GetGlobalObjectsCount());
10767
10768 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010769}
Andrei Popescu402d9372010-02-26 13:31:12 +000010770
10771
10772THREADED_TEST(ScriptOrigin) {
10773 v8::HandleScope scope;
10774 LocalContext env;
10775 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10776 v8::Handle<v8::String> script = v8::String::New(
10777 "function f() {}\n\nfunction g() {}");
10778 v8::Script::Compile(script, &origin)->Run();
10779 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10780 env->Global()->Get(v8::String::New("f")));
10781 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10782 env->Global()->Get(v8::String::New("g")));
10783
10784 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10785 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10786 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10787
10788 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10789 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10790 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10791}
10792
10793
10794THREADED_TEST(ScriptLineNumber) {
10795 v8::HandleScope scope;
10796 LocalContext env;
10797 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10798 v8::Handle<v8::String> script = v8::String::New(
10799 "function f() {}\n\nfunction g() {}");
10800 v8::Script::Compile(script, &origin)->Run();
10801 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10802 env->Global()->Get(v8::String::New("f")));
10803 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10804 env->Global()->Get(v8::String::New("g")));
10805 CHECK_EQ(0, f->GetScriptLineNumber());
10806 CHECK_EQ(2, g->GetScriptLineNumber());
10807}
10808
10809
10810static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10811 const AccessorInfo& info) {
10812 return v8_num(42);
10813}
10814
10815
10816static void SetterWhichSetsYOnThisTo23(Local<String> name,
10817 Local<Value> value,
10818 const AccessorInfo& info) {
10819 info.This()->Set(v8_str("y"), v8_num(23));
10820}
10821
10822
Steve Block6ded16b2010-05-10 14:33:55 +010010823TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010824 v8::HandleScope scope;
10825 Local<ObjectTemplate> templ = ObjectTemplate::New();
10826 templ->SetAccessor(v8_str("x"),
10827 GetterWhichReturns42,
10828 SetterWhichSetsYOnThisTo23);
10829 LocalContext context;
10830 context->Global()->Set(v8_str("P"), templ->NewInstance());
10831 CompileRun("function C1() {"
10832 " this.x = 23;"
10833 "};"
10834 "C1.prototype = P;"
10835 "function C2() {"
10836 " this.x = 23"
10837 "};"
10838 "C2.prototype = { };"
10839 "C2.prototype.__proto__ = P;");
10840
10841 v8::Local<v8::Script> script;
10842 script = v8::Script::Compile(v8_str("new C1();"));
10843 for (int i = 0; i < 10; i++) {
10844 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10845 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10846 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10847 }
10848
10849 script = v8::Script::Compile(v8_str("new C2();"));
10850 for (int i = 0; i < 10; i++) {
10851 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10852 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10853 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10854 }
10855}
10856
10857
10858static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10859 Local<String> name, const AccessorInfo& info) {
10860 return v8_num(42);
10861}
10862
10863
10864static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10865 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10866 if (name->Equals(v8_str("x"))) {
10867 info.This()->Set(v8_str("y"), v8_num(23));
10868 }
10869 return v8::Handle<Value>();
10870}
10871
10872
10873THREADED_TEST(InterceptorOnConstructorPrototype) {
10874 v8::HandleScope scope;
10875 Local<ObjectTemplate> templ = ObjectTemplate::New();
10876 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10877 NamedPropertySetterWhichSetsYOnThisTo23);
10878 LocalContext context;
10879 context->Global()->Set(v8_str("P"), templ->NewInstance());
10880 CompileRun("function C1() {"
10881 " this.x = 23;"
10882 "};"
10883 "C1.prototype = P;"
10884 "function C2() {"
10885 " this.x = 23"
10886 "};"
10887 "C2.prototype = { };"
10888 "C2.prototype.__proto__ = P;");
10889
10890 v8::Local<v8::Script> script;
10891 script = v8::Script::Compile(v8_str("new C1();"));
10892 for (int i = 0; i < 10; i++) {
10893 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10894 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10895 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10896 }
10897
10898 script = v8::Script::Compile(v8_str("new C2();"));
10899 for (int i = 0; i < 10; i++) {
10900 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10901 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10902 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10903 }
10904}
Steve Block6ded16b2010-05-10 14:33:55 +010010905
10906
10907TEST(Bug618) {
10908 const char* source = "function C1() {"
10909 " this.x = 23;"
10910 "};"
10911 "C1.prototype = P;";
10912
10913 v8::HandleScope scope;
10914 LocalContext context;
10915 v8::Local<v8::Script> script;
10916
10917 // Use a simple object as prototype.
10918 v8::Local<v8::Object> prototype = v8::Object::New();
10919 prototype->Set(v8_str("y"), v8_num(42));
10920 context->Global()->Set(v8_str("P"), prototype);
10921
10922 // This compile will add the code to the compilation cache.
10923 CompileRun(source);
10924
10925 script = v8::Script::Compile(v8_str("new C1();"));
10926 for (int i = 0; i < 10; i++) {
10927 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10928 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10929 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10930 }
10931
10932 // Use an API object with accessors as prototype.
10933 Local<ObjectTemplate> templ = ObjectTemplate::New();
10934 templ->SetAccessor(v8_str("x"),
10935 GetterWhichReturns42,
10936 SetterWhichSetsYOnThisTo23);
10937 context->Global()->Set(v8_str("P"), templ->NewInstance());
10938
10939 // This compile will get the code from the compilation cache.
10940 CompileRun(source);
10941
10942 script = v8::Script::Compile(v8_str("new C1();"));
10943 for (int i = 0; i < 10; i++) {
10944 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10945 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10946 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10947 }
10948}
10949
10950int prologue_call_count = 0;
10951int epilogue_call_count = 0;
10952int prologue_call_count_second = 0;
10953int epilogue_call_count_second = 0;
10954
10955void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
10956 ++prologue_call_count;
10957}
10958
10959void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
10960 ++epilogue_call_count;
10961}
10962
10963void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10964 ++prologue_call_count_second;
10965}
10966
10967void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10968 ++epilogue_call_count_second;
10969}
10970
10971TEST(GCCallbacks) {
10972 LocalContext context;
10973
10974 v8::V8::AddGCPrologueCallback(PrologueCallback);
10975 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
10976 CHECK_EQ(0, prologue_call_count);
10977 CHECK_EQ(0, epilogue_call_count);
10978 i::Heap::CollectAllGarbage(false);
10979 CHECK_EQ(1, prologue_call_count);
10980 CHECK_EQ(1, epilogue_call_count);
10981 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
10982 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
10983 i::Heap::CollectAllGarbage(false);
10984 CHECK_EQ(2, prologue_call_count);
10985 CHECK_EQ(2, epilogue_call_count);
10986 CHECK_EQ(1, prologue_call_count_second);
10987 CHECK_EQ(1, epilogue_call_count_second);
10988 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
10989 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
10990 i::Heap::CollectAllGarbage(false);
10991 CHECK_EQ(2, prologue_call_count);
10992 CHECK_EQ(2, epilogue_call_count);
10993 CHECK_EQ(2, prologue_call_count_second);
10994 CHECK_EQ(2, epilogue_call_count_second);
10995 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
10996 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
10997 i::Heap::CollectAllGarbage(false);
10998 CHECK_EQ(2, prologue_call_count);
10999 CHECK_EQ(2, epilogue_call_count);
11000 CHECK_EQ(2, prologue_call_count_second);
11001 CHECK_EQ(2, epilogue_call_count_second);
11002}
Kristian Monsen25f61362010-05-21 11:50:48 +010011003
11004
11005THREADED_TEST(AddToJSFunctionResultCache) {
11006 i::FLAG_allow_natives_syntax = true;
11007 v8::HandleScope scope;
11008
11009 LocalContext context;
11010
11011 const char* code =
11012 "(function() {"
11013 " var key0 = 'a';"
11014 " var key1 = 'b';"
11015 " var r0 = %_GetFromCache(0, key0);"
11016 " var r1 = %_GetFromCache(0, key1);"
11017 " var r0_ = %_GetFromCache(0, key0);"
11018 " if (r0 !== r0_)"
11019 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
11020 " var r1_ = %_GetFromCache(0, key1);"
11021 " if (r1 !== r1_)"
11022 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
11023 " return 'PASSED';"
11024 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011025 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011026 ExpectString(code, "PASSED");
11027}
11028
11029
11030static const int k0CacheSize = 16;
11031
11032THREADED_TEST(FillJSFunctionResultCache) {
11033 i::FLAG_allow_natives_syntax = true;
11034 v8::HandleScope scope;
11035
11036 LocalContext context;
11037
11038 const char* code =
11039 "(function() {"
11040 " var k = 'a';"
11041 " var r = %_GetFromCache(0, k);"
11042 " for (var i = 0; i < 16; i++) {"
11043 " %_GetFromCache(0, 'a' + i);"
11044 " };"
11045 " if (r === %_GetFromCache(0, k))"
11046 " return 'FAILED: k0CacheSize is too small';"
11047 " return 'PASSED';"
11048 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011049 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011050 ExpectString(code, "PASSED");
11051}
11052
11053
11054THREADED_TEST(RoundRobinGetFromCache) {
11055 i::FLAG_allow_natives_syntax = true;
11056 v8::HandleScope scope;
11057
11058 LocalContext context;
11059
11060 const char* code =
11061 "(function() {"
11062 " var keys = [];"
11063 " for (var i = 0; i < 16; i++) keys.push(i);"
11064 " var values = [];"
11065 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11066 " for (var i = 0; i < 16; i++) {"
11067 " var v = %_GetFromCache(0, keys[i]);"
11068 " if (v !== values[i])"
11069 " return 'Wrong value for ' + "
11070 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11071 " };"
11072 " return 'PASSED';"
11073 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011074 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011075 ExpectString(code, "PASSED");
11076}
11077
11078
11079THREADED_TEST(ReverseGetFromCache) {
11080 i::FLAG_allow_natives_syntax = true;
11081 v8::HandleScope scope;
11082
11083 LocalContext context;
11084
11085 const char* code =
11086 "(function() {"
11087 " var keys = [];"
11088 " for (var i = 0; i < 16; i++) keys.push(i);"
11089 " var values = [];"
11090 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11091 " for (var i = 15; i >= 16; i--) {"
11092 " var v = %_GetFromCache(0, keys[i]);"
11093 " if (v !== values[i])"
11094 " return 'Wrong value for ' + "
11095 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11096 " };"
11097 " return 'PASSED';"
11098 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011099 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011100 ExpectString(code, "PASSED");
11101}
11102
11103
11104THREADED_TEST(TestEviction) {
11105 i::FLAG_allow_natives_syntax = true;
11106 v8::HandleScope scope;
11107
11108 LocalContext context;
11109
11110 const char* code =
11111 "(function() {"
11112 " for (var i = 0; i < 2*16; i++) {"
11113 " %_GetFromCache(0, 'a' + i);"
11114 " };"
11115 " return 'PASSED';"
11116 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011117 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011118 ExpectString(code, "PASSED");
11119}
Steve Block8defd9f2010-07-08 12:39:36 +010011120
11121
11122THREADED_TEST(TwoByteStringInAsciiCons) {
11123 // See Chromium issue 47824.
11124 v8::HandleScope scope;
11125
11126 LocalContext context;
11127 const char* init_code =
11128 "var str1 = 'abelspendabel';"
11129 "var str2 = str1 + str1 + str1;"
11130 "str2;";
11131 Local<Value> result = CompileRun(init_code);
11132
11133 CHECK(result->IsString());
11134 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
11135 int length = string->length();
11136 CHECK(string->IsAsciiRepresentation());
11137
11138 FlattenString(string);
11139 i::Handle<i::String> flat_string = FlattenGetString(string);
11140
11141 CHECK(string->IsAsciiRepresentation());
11142 CHECK(flat_string->IsAsciiRepresentation());
11143
11144 // Create external resource.
11145 uint16_t* uc16_buffer = new uint16_t[length + 1];
11146
11147 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
11148 uc16_buffer[length] = 0;
11149
11150 TestResource resource(uc16_buffer);
11151
11152 flat_string->MakeExternal(&resource);
11153
11154 CHECK(flat_string->IsTwoByteRepresentation());
11155
11156 // At this point, we should have a Cons string which is flat and ASCII,
11157 // with a first half that is a two-byte string (although it only contains
11158 // ASCII characters). This is a valid sequence of steps, and it can happen
11159 // in real pages.
11160
11161 CHECK(string->IsAsciiRepresentation());
11162 i::ConsString* cons = i::ConsString::cast(*string);
11163 CHECK_EQ(0, cons->second()->length());
11164 CHECK(cons->first()->IsTwoByteRepresentation());
11165
11166 // Check that some string operations work.
11167
11168 // Atom RegExp.
11169 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
11170 CHECK_EQ(6, reresult->Int32Value());
11171
11172 // Nonatom RegExp.
11173 reresult = CompileRun("str2.match(/abe./g).length;");
11174 CHECK_EQ(6, reresult->Int32Value());
11175
11176 reresult = CompileRun("str2.search(/bel/g);");
11177 CHECK_EQ(1, reresult->Int32Value());
11178
11179 reresult = CompileRun("str2.search(/be./g);");
11180 CHECK_EQ(1, reresult->Int32Value());
11181
11182 ExpectTrue("/bel/g.test(str2);");
11183
11184 ExpectTrue("/be./g.test(str2);");
11185
11186 reresult = CompileRun("/bel/g.exec(str2);");
11187 CHECK(!reresult->IsNull());
11188
11189 reresult = CompileRun("/be./g.exec(str2);");
11190 CHECK(!reresult->IsNull());
11191
11192 ExpectString("str2.substring(2, 10);", "elspenda");
11193
11194 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
11195
11196 ExpectString("str2.charAt(2);", "e");
11197
11198 reresult = CompileRun("str2.charCodeAt(2);");
11199 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
11200}
Iain Merrick75681382010-08-19 15:07:18 +010011201
11202
11203// Failed access check callback that performs a GC on each invocation.
11204void FailedAccessCheckCallbackGC(Local<v8::Object> target,
11205 v8::AccessType type,
11206 Local<v8::Value> data) {
11207 i::Heap::CollectAllGarbage(true);
11208}
11209
11210
11211TEST(GCInFailedAccessCheckCallback) {
11212 // Install a failed access check callback that performs a GC on each
11213 // invocation. Then force the callback to be called from va
11214
11215 v8::V8::Initialize();
11216 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC);
11217
11218 v8::HandleScope scope;
11219
11220 // Create an ObjectTemplate for global objects and install access
11221 // check callbacks that will block access.
11222 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
11223 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
11224 IndexedGetAccessBlocker,
11225 v8::Handle<v8::Value>(),
11226 false);
11227
11228 // Create a context and set an x property on it's global object.
11229 LocalContext context0(NULL, global_template);
11230 context0->Global()->Set(v8_str("x"), v8_num(42));
11231 v8::Handle<v8::Object> global0 = context0->Global();
11232
11233 // Create a context with a different security token so that the
11234 // failed access check callback will be called on each access.
11235 LocalContext context1(NULL, global_template);
11236 context1->Global()->Set(v8_str("other"), global0);
11237
11238 // Get property with failed access check.
11239 ExpectUndefined("other.x");
11240
11241 // Get element with failed access check.
11242 ExpectUndefined("other[0]");
11243
11244 // Set property with failed access check.
11245 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()");
11246 CHECK(result->IsObject());
11247
11248 // Set element with failed access check.
11249 result = CompileRun("other[0] = new Object()");
11250 CHECK(result->IsObject());
11251
11252 // Get property attribute with failed access check.
11253 ExpectFalse("\'x\' in other");
11254
11255 // Get property attribute for element with failed access check.
11256 ExpectFalse("0 in other");
11257
11258 // Delete property.
11259 ExpectFalse("delete other.x");
11260
11261 // Delete element.
11262 CHECK_EQ(false, global0->Delete(0));
11263
11264 // DefineAccessor.
11265 CHECK_EQ(false,
11266 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x")));
11267
11268 // Define JavaScript accessor.
11269 ExpectUndefined("Object.prototype.__defineGetter__.call("
11270 " other, \'x\', function() { return 42; })");
11271
11272 // LookupAccessor.
11273 ExpectUndefined("Object.prototype.__lookupGetter__.call("
11274 " other, \'x\')");
11275
11276 // HasLocalElement.
11277 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')");
11278
11279 CHECK_EQ(false, global0->HasRealIndexedProperty(0));
11280 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x")));
11281 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x")));
11282
11283 // Reset the failed access check callback so it does not influence
11284 // the other tests.
11285 v8::V8::SetFailedAccessCheckCallbackFunction(NULL);
11286}