blob: bd6108c0a082821e8434269776bd27c32afa6bda [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
Leon Clarked91b9f72010-01-27 17:25:45 +000084static void ExpectObject(const char* code, Local<Value> expected) {
85 Local<Value> result = CompileRun(code);
86 CHECK(result->Equals(expected));
87}
88
89
Steve Blocka7e24c12009-10-30 11:49:00 +000090static int signature_callback_count;
91static v8::Handle<Value> IncrementingSignatureCallback(
92 const v8::Arguments& args) {
93 ApiTestFuzzer::Fuzz();
94 signature_callback_count++;
95 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
96 for (int i = 0; i < args.Length(); i++)
97 result->Set(v8::Integer::New(i), args[i]);
98 return result;
99}
100
101
102static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) {
103 ApiTestFuzzer::Fuzz();
104 v8::Handle<v8::Array> result = v8::Array::New(args.Length());
105 for (int i = 0; i < args.Length(); i++) {
106 result->Set(v8::Integer::New(i), args[i]);
107 }
108 return result;
109}
110
111
112THREADED_TEST(Handles) {
113 v8::HandleScope scope;
114 Local<Context> local_env;
115 {
116 LocalContext env;
117 local_env = env.local();
118 }
119
120 // Local context should still be live.
121 CHECK(!local_env.IsEmpty());
122 local_env->Enter();
123
124 v8::Handle<v8::Primitive> undef = v8::Undefined();
125 CHECK(!undef.IsEmpty());
126 CHECK(undef->IsUndefined());
127
128 const char* c_source = "1 + 2 + 3";
129 Local<String> source = String::New(c_source);
130 Local<Script> script = Script::Compile(source);
131 CHECK_EQ(6, script->Run()->Int32Value());
132
133 local_env->Exit();
134}
135
136
Steve Blocka7e24c12009-10-30 11:49:00 +0000137THREADED_TEST(ReceiverSignature) {
138 v8::HandleScope scope;
139 LocalContext env;
140 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
141 v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
142 fun->PrototypeTemplate()->Set(
143 v8_str("m"),
144 v8::FunctionTemplate::New(IncrementingSignatureCallback,
145 v8::Handle<Value>(),
146 sig));
147 env->Global()->Set(v8_str("Fun"), fun->GetFunction());
148 signature_callback_count = 0;
149 CompileRun(
150 "var o = new Fun();"
151 "o.m();");
152 CHECK_EQ(1, signature_callback_count);
153 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
154 sub_fun->Inherit(fun);
155 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction());
156 CompileRun(
157 "var o = new SubFun();"
158 "o.m();");
159 CHECK_EQ(2, signature_callback_count);
160
161 v8::TryCatch try_catch;
162 CompileRun(
163 "var o = { };"
164 "o.m = Fun.prototype.m;"
165 "o.m();");
166 CHECK_EQ(2, signature_callback_count);
167 CHECK(try_catch.HasCaught());
168 try_catch.Reset();
169 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
170 sub_fun->Inherit(fun);
171 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
172 CompileRun(
173 "var o = new UnrelFun();"
174 "o.m = Fun.prototype.m;"
175 "o.m();");
176 CHECK_EQ(2, signature_callback_count);
177 CHECK(try_catch.HasCaught());
178}
179
180
181
182
183THREADED_TEST(ArgumentSignature) {
184 v8::HandleScope scope;
185 LocalContext env;
186 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New();
187 cons->SetClassName(v8_str("Cons"));
188 v8::Handle<v8::Signature> sig =
189 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons);
190 v8::Handle<v8::FunctionTemplate> fun =
191 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig);
192 env->Global()->Set(v8_str("Cons"), cons->GetFunction());
193 env->Global()->Set(v8_str("Fun1"), fun->GetFunction());
194
195 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';");
196 CHECK(value1->IsTrue());
197
198 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';");
199 CHECK(value2->IsTrue());
200
201 v8::Handle<Value> value3 = CompileRun("Fun1() == '';");
202 CHECK(value3->IsTrue());
203
204 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New();
205 cons1->SetClassName(v8_str("Cons1"));
206 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New();
207 cons2->SetClassName(v8_str("Cons2"));
208 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New();
209 cons3->SetClassName(v8_str("Cons3"));
210
211 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 };
212 v8::Handle<v8::Signature> wsig =
213 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args);
214 v8::Handle<v8::FunctionTemplate> fun2 =
215 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig);
216
217 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction());
218 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction());
219 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction());
220 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction());
221 v8::Handle<Value> value4 = CompileRun(
222 "Fun2(new Cons1(), new Cons2(), new Cons3()) =="
223 "'[object Cons1],[object Cons2],[object Cons3]'");
224 CHECK(value4->IsTrue());
225
226 v8::Handle<Value> value5 = CompileRun(
227 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'");
228 CHECK(value5->IsTrue());
229
230 v8::Handle<Value> value6 = CompileRun(
231 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'");
232 CHECK(value6->IsTrue());
233
234 v8::Handle<Value> value7 = CompileRun(
235 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == "
236 "'[object Cons1],[object Cons2],[object Cons3],d';");
237 CHECK(value7->IsTrue());
238
239 v8::Handle<Value> value8 = CompileRun(
240 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'");
241 CHECK(value8->IsTrue());
242}
243
244
245THREADED_TEST(HulIgennem) {
246 v8::HandleScope scope;
247 LocalContext env;
248 v8::Handle<v8::Primitive> undef = v8::Undefined();
249 Local<String> undef_str = undef->ToString();
250 char* value = i::NewArray<char>(undef_str->Length() + 1);
251 undef_str->WriteAscii(value);
252 CHECK_EQ(0, strcmp(value, "undefined"));
253 i::DeleteArray(value);
254}
255
256
257THREADED_TEST(Access) {
258 v8::HandleScope scope;
259 LocalContext env;
260 Local<v8::Object> obj = v8::Object::New();
261 Local<Value> foo_before = obj->Get(v8_str("foo"));
262 CHECK(foo_before->IsUndefined());
263 Local<String> bar_str = v8_str("bar");
264 obj->Set(v8_str("foo"), bar_str);
265 Local<Value> foo_after = obj->Get(v8_str("foo"));
266 CHECK(!foo_after->IsUndefined());
267 CHECK(foo_after->IsString());
268 CHECK_EQ(bar_str, foo_after);
269}
270
271
Steve Block6ded16b2010-05-10 14:33:55 +0100272THREADED_TEST(AccessElement) {
273 v8::HandleScope scope;
274 LocalContext env;
275 Local<v8::Object> obj = v8::Object::New();
276 Local<Value> before = obj->Get(1);
277 CHECK(before->IsUndefined());
278 Local<String> bar_str = v8_str("bar");
279 obj->Set(1, bar_str);
280 Local<Value> after = obj->Get(1);
281 CHECK(!after->IsUndefined());
282 CHECK(after->IsString());
283 CHECK_EQ(bar_str, after);
284
285 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>();
286 CHECK_EQ(v8_str("a"), value->Get(0));
287 CHECK_EQ(v8_str("b"), value->Get(1));
288}
289
290
Steve Blocka7e24c12009-10-30 11:49:00 +0000291THREADED_TEST(Script) {
292 v8::HandleScope scope;
293 LocalContext env;
294 const char* c_source = "1 + 2 + 3";
295 Local<String> source = String::New(c_source);
296 Local<Script> script = Script::Compile(source);
297 CHECK_EQ(6, script->Run()->Int32Value());
298}
299
300
301static uint16_t* AsciiToTwoByteString(const char* source) {
Steve Blockd0582a62009-12-15 09:54:21 +0000302 int array_length = i::StrLength(source) + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000303 uint16_t* converted = i::NewArray<uint16_t>(array_length);
Steve Blockd0582a62009-12-15 09:54:21 +0000304 for (int i = 0; i < array_length; i++) converted[i] = source[i];
Steve Blocka7e24c12009-10-30 11:49:00 +0000305 return converted;
306}
307
308
309class TestResource: public String::ExternalStringResource {
310 public:
311 static int dispose_count;
312
313 explicit TestResource(uint16_t* data)
314 : data_(data), length_(0) {
315 while (data[length_]) ++length_;
316 }
317
318 ~TestResource() {
319 i::DeleteArray(data_);
320 ++dispose_count;
321 }
322
323 const uint16_t* data() const {
324 return data_;
325 }
326
327 size_t length() const {
328 return length_;
329 }
330 private:
331 uint16_t* data_;
332 size_t length_;
333};
334
335
336int TestResource::dispose_count = 0;
337
338
339class TestAsciiResource: public String::ExternalAsciiStringResource {
340 public:
341 static int dispose_count;
342
343 explicit TestAsciiResource(const char* data)
344 : data_(data),
345 length_(strlen(data)) { }
346
347 ~TestAsciiResource() {
348 i::DeleteArray(data_);
349 ++dispose_count;
350 }
351
352 const char* data() const {
353 return data_;
354 }
355
356 size_t length() const {
357 return length_;
358 }
359 private:
360 const char* data_;
361 size_t length_;
362};
363
364
365int TestAsciiResource::dispose_count = 0;
366
367
368THREADED_TEST(ScriptUsingStringResource) {
369 TestResource::dispose_count = 0;
370 const char* c_source = "1 + 2 * 3";
371 uint16_t* two_byte_source = AsciiToTwoByteString(c_source);
372 {
373 v8::HandleScope scope;
374 LocalContext env;
375 TestResource* resource = new TestResource(two_byte_source);
376 Local<String> source = String::NewExternal(resource);
377 Local<Script> script = Script::Compile(source);
378 Local<Value> value = script->Run();
379 CHECK(value->IsNumber());
380 CHECK_EQ(7, value->Int32Value());
381 CHECK(source->IsExternal());
382 CHECK_EQ(resource,
383 static_cast<TestResource*>(source->GetExternalStringResource()));
Steve Block8defd9f2010-07-08 12:39:36 +0100384 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000385 CHECK_EQ(0, TestResource::dispose_count);
386 }
Steve Block8defd9f2010-07-08 12:39:36 +0100387 i::CompilationCache::Clear();
388 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000389 CHECK_EQ(1, TestResource::dispose_count);
390}
391
392
393THREADED_TEST(ScriptUsingAsciiStringResource) {
394 TestAsciiResource::dispose_count = 0;
395 const char* c_source = "1 + 2 * 3";
396 {
397 v8::HandleScope scope;
398 LocalContext env;
399 Local<String> source =
400 String::NewExternal(new TestAsciiResource(i::StrDup(c_source)));
401 Local<Script> script = Script::Compile(source);
402 Local<Value> value = script->Run();
403 CHECK(value->IsNumber());
404 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100405 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000406 CHECK_EQ(0, TestAsciiResource::dispose_count);
407 }
Steve Block8defd9f2010-07-08 12:39:36 +0100408 i::CompilationCache::Clear();
409 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000410 CHECK_EQ(1, TestAsciiResource::dispose_count);
411}
412
413
414THREADED_TEST(ScriptMakingExternalString) {
415 TestResource::dispose_count = 0;
416 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
417 {
418 v8::HandleScope scope;
419 LocalContext env;
420 Local<String> source = String::New(two_byte_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000421 // Trigger GCs so that the newly allocated string moves to old gen.
422 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
423 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000424 bool success = source->MakeExternal(new TestResource(two_byte_source));
425 CHECK(success);
426 Local<Script> script = Script::Compile(source);
427 Local<Value> value = script->Run();
428 CHECK(value->IsNumber());
429 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100430 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000431 CHECK_EQ(0, TestResource::dispose_count);
432 }
Steve Block8defd9f2010-07-08 12:39:36 +0100433 i::CompilationCache::Clear();
434 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000435 CHECK_EQ(1, TestResource::dispose_count);
436}
437
438
439THREADED_TEST(ScriptMakingExternalAsciiString) {
440 TestAsciiResource::dispose_count = 0;
441 const char* c_source = "1 + 2 * 3";
442 {
443 v8::HandleScope scope;
444 LocalContext env;
445 Local<String> source = v8_str(c_source);
Andrei Popescu402d9372010-02-26 13:31:12 +0000446 // Trigger GCs so that the newly allocated string moves to old gen.
447 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
448 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
Steve Blocka7e24c12009-10-30 11:49:00 +0000449 bool success = source->MakeExternal(
450 new TestAsciiResource(i::StrDup(c_source)));
451 CHECK(success);
452 Local<Script> script = Script::Compile(source);
453 Local<Value> value = script->Run();
454 CHECK(value->IsNumber());
455 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100456 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000457 CHECK_EQ(0, TestAsciiResource::dispose_count);
458 }
Steve Block8defd9f2010-07-08 12:39:36 +0100459 i::CompilationCache::Clear();
460 i::Heap::CollectAllGarbage(false);
Steve Blocka7e24c12009-10-30 11:49:00 +0000461 CHECK_EQ(1, TestAsciiResource::dispose_count);
462}
463
464
Andrei Popescu402d9372010-02-26 13:31:12 +0000465TEST(MakingExternalStringConditions) {
466 v8::HandleScope scope;
467 LocalContext env;
468
469 // Free some space in the new space so that we can check freshness.
470 i::Heap::CollectGarbage(0, i::NEW_SPACE);
471 i::Heap::CollectGarbage(0, i::NEW_SPACE);
472
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100473 uint16_t* two_byte_string = AsciiToTwoByteString("small");
474 Local<String> small_string = String::New(two_byte_string);
475 i::DeleteArray(two_byte_string);
476
Andrei Popescu402d9372010-02-26 13:31:12 +0000477 // We should refuse to externalize newly created small string.
478 CHECK(!small_string->CanMakeExternal());
479 // Trigger GCs so that the newly allocated string moves to old gen.
480 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
481 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
482 // Old space strings should be accepted.
483 CHECK(small_string->CanMakeExternal());
484
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100485 two_byte_string = AsciiToTwoByteString("small 2");
486 small_string = String::New(two_byte_string);
487 i::DeleteArray(two_byte_string);
488
Andrei Popescu402d9372010-02-26 13:31:12 +0000489 // We should refuse externalizing newly created small string.
490 CHECK(!small_string->CanMakeExternal());
491 for (int i = 0; i < 100; i++) {
492 String::Value value(small_string);
493 }
494 // Frequently used strings should be accepted.
495 CHECK(small_string->CanMakeExternal());
496
497 const int buf_size = 10 * 1024;
498 char* buf = i::NewArray<char>(buf_size);
499 memset(buf, 'a', buf_size);
500 buf[buf_size - 1] = '\0';
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100501
502 two_byte_string = AsciiToTwoByteString(buf);
503 Local<String> large_string = String::New(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000504 i::DeleteArray(buf);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100505 i::DeleteArray(two_byte_string);
Andrei Popescu402d9372010-02-26 13:31:12 +0000506 // Large strings should be immediately accepted.
507 CHECK(large_string->CanMakeExternal());
508}
509
510
511TEST(MakingExternalAsciiStringConditions) {
512 v8::HandleScope scope;
513 LocalContext env;
514
515 // Free some space in the new space so that we can check freshness.
516 i::Heap::CollectGarbage(0, i::NEW_SPACE);
517 i::Heap::CollectGarbage(0, i::NEW_SPACE);
518
519 Local<String> small_string = String::New("small");
520 // We should refuse to externalize newly created small string.
521 CHECK(!small_string->CanMakeExternal());
522 // Trigger GCs so that the newly allocated string moves to old gen.
523 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
524 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
525 // Old space strings should be accepted.
526 CHECK(small_string->CanMakeExternal());
527
528 small_string = String::New("small 2");
529 // We should refuse externalizing newly created small string.
530 CHECK(!small_string->CanMakeExternal());
531 for (int i = 0; i < 100; i++) {
532 String::Value value(small_string);
533 }
534 // Frequently used strings should be accepted.
535 CHECK(small_string->CanMakeExternal());
536
537 const int buf_size = 10 * 1024;
538 char* buf = i::NewArray<char>(buf_size);
539 memset(buf, 'a', buf_size);
540 buf[buf_size - 1] = '\0';
541 Local<String> large_string = String::New(buf);
542 i::DeleteArray(buf);
543 // Large strings should be immediately accepted.
544 CHECK(large_string->CanMakeExternal());
545}
546
547
Steve Blocka7e24c12009-10-30 11:49:00 +0000548THREADED_TEST(UsingExternalString) {
549 {
550 v8::HandleScope scope;
551 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
552 Local<String> string =
553 String::NewExternal(new TestResource(two_byte_string));
554 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
555 // Trigger GCs so that the newly allocated string moves to old gen.
556 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
557 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
558 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
559 CHECK(isymbol->IsSymbol());
560 }
561 i::Heap::CollectAllGarbage(false);
562 i::Heap::CollectAllGarbage(false);
563}
564
565
566THREADED_TEST(UsingExternalAsciiString) {
567 {
568 v8::HandleScope scope;
569 const char* one_byte_string = "test string";
570 Local<String> string = String::NewExternal(
571 new TestAsciiResource(i::StrDup(one_byte_string)));
572 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
573 // Trigger GCs so that the newly allocated string moves to old gen.
574 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
575 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
576 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
577 CHECK(isymbol->IsSymbol());
578 }
579 i::Heap::CollectAllGarbage(false);
580 i::Heap::CollectAllGarbage(false);
581}
582
583
Leon Clarkee46be812010-01-19 14:06:41 +0000584THREADED_TEST(ScavengeExternalString) {
585 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100586 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000587 {
588 v8::HandleScope scope;
589 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
590 Local<String> string =
591 String::NewExternal(new TestResource(two_byte_string));
592 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
593 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100594 in_new_space = i::Heap::InNewSpace(*istring);
595 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000596 CHECK_EQ(0, TestResource::dispose_count);
597 }
Steve Block6ded16b2010-05-10 14:33:55 +0100598 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000599 CHECK_EQ(1, TestResource::dispose_count);
600}
601
602
603THREADED_TEST(ScavengeExternalAsciiString) {
604 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100605 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000606 {
607 v8::HandleScope scope;
608 const char* one_byte_string = "test string";
609 Local<String> string = String::NewExternal(
610 new TestAsciiResource(i::StrDup(one_byte_string)));
611 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
612 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100613 in_new_space = i::Heap::InNewSpace(*istring);
614 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000615 CHECK_EQ(0, TestAsciiResource::dispose_count);
616 }
Steve Block6ded16b2010-05-10 14:33:55 +0100617 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000618 CHECK_EQ(1, TestAsciiResource::dispose_count);
619}
620
621
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100622class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
623 public:
624 static int dispose_calls;
625
626 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
627 : TestAsciiResource(data),
628 dispose_(dispose) { }
629
630 void Dispose() {
631 ++dispose_calls;
632 if (dispose_) delete this;
633 }
634 private:
635 bool dispose_;
636};
637
638
639int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
640
641
642TEST(ExternalStringWithDisposeHandling) {
643 const char* c_source = "1 + 2 * 3";
644
645 // Use a stack allocated external string resource allocated object.
646 TestAsciiResource::dispose_count = 0;
647 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
648 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
649 {
650 v8::HandleScope scope;
651 LocalContext env;
652 Local<String> source = String::NewExternal(&res_stack);
653 Local<Script> script = Script::Compile(source);
654 Local<Value> value = script->Run();
655 CHECK(value->IsNumber());
656 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100657 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100658 CHECK_EQ(0, TestAsciiResource::dispose_count);
659 }
Steve Block8defd9f2010-07-08 12:39:36 +0100660 i::CompilationCache::Clear();
661 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100662 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
663 CHECK_EQ(0, TestAsciiResource::dispose_count);
664
665 // Use a heap allocated external string resource allocated object.
666 TestAsciiResource::dispose_count = 0;
667 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
668 TestAsciiResource* res_heap =
669 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
670 {
671 v8::HandleScope scope;
672 LocalContext env;
673 Local<String> source = String::NewExternal(res_heap);
674 Local<Script> script = Script::Compile(source);
675 Local<Value> value = script->Run();
676 CHECK(value->IsNumber());
677 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100678 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100679 CHECK_EQ(0, TestAsciiResource::dispose_count);
680 }
Steve Block8defd9f2010-07-08 12:39:36 +0100681 i::CompilationCache::Clear();
682 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100683 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
684 CHECK_EQ(1, TestAsciiResource::dispose_count);
685}
686
687
Steve Block3ce2e202009-11-05 08:53:23 +0000688THREADED_TEST(StringConcat) {
689 {
690 v8::HandleScope scope;
691 LocalContext env;
692 const char* one_byte_string_1 = "function a_times_t";
693 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
694 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
695 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
696 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
697 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
698 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
699 Local<String> left = v8_str(one_byte_string_1);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100700
701 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1);
702 Local<String> right = String::New(two_byte_source);
703 i::DeleteArray(two_byte_source);
704
Steve Block3ce2e202009-11-05 08:53:23 +0000705 Local<String> source = String::Concat(left, right);
706 right = String::NewExternal(
707 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
708 source = String::Concat(source, right);
709 right = String::NewExternal(
710 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
711 source = String::Concat(source, right);
712 right = v8_str(one_byte_string_2);
713 source = String::Concat(source, right);
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100714
715 two_byte_source = AsciiToTwoByteString(two_byte_string_2);
716 right = String::New(two_byte_source);
717 i::DeleteArray(two_byte_source);
718
Steve Block3ce2e202009-11-05 08:53:23 +0000719 source = String::Concat(source, right);
720 right = String::NewExternal(
721 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
722 source = String::Concat(source, right);
723 Local<Script> script = Script::Compile(source);
724 Local<Value> value = script->Run();
725 CHECK(value->IsNumber());
726 CHECK_EQ(68, value->Int32Value());
727 }
Steve Block8defd9f2010-07-08 12:39:36 +0100728 i::CompilationCache::Clear();
Steve Block3ce2e202009-11-05 08:53:23 +0000729 i::Heap::CollectAllGarbage(false);
730 i::Heap::CollectAllGarbage(false);
731}
732
733
Steve Blocka7e24c12009-10-30 11:49:00 +0000734THREADED_TEST(GlobalProperties) {
735 v8::HandleScope scope;
736 LocalContext env;
737 v8::Handle<v8::Object> global = env->Global();
738 global->Set(v8_str("pi"), v8_num(3.1415926));
739 Local<Value> pi = global->Get(v8_str("pi"));
740 CHECK_EQ(3.1415926, pi->NumberValue());
741}
742
743
744static v8::Handle<Value> handle_call(const v8::Arguments& args) {
745 ApiTestFuzzer::Fuzz();
746 return v8_num(102);
747}
748
749
750static v8::Handle<Value> construct_call(const v8::Arguments& args) {
751 ApiTestFuzzer::Fuzz();
752 args.This()->Set(v8_str("x"), v8_num(1));
753 args.This()->Set(v8_str("y"), v8_num(2));
754 return args.This();
755}
756
757THREADED_TEST(FunctionTemplate) {
758 v8::HandleScope scope;
759 LocalContext env;
760 {
761 Local<v8::FunctionTemplate> fun_templ =
762 v8::FunctionTemplate::New(handle_call);
763 Local<Function> fun = fun_templ->GetFunction();
764 env->Global()->Set(v8_str("obj"), fun);
765 Local<Script> script = v8_compile("obj()");
766 CHECK_EQ(102, script->Run()->Int32Value());
767 }
768 // Use SetCallHandler to initialize a function template, should work like the
769 // previous one.
770 {
771 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
772 fun_templ->SetCallHandler(handle_call);
773 Local<Function> fun = fun_templ->GetFunction();
774 env->Global()->Set(v8_str("obj"), fun);
775 Local<Script> script = v8_compile("obj()");
776 CHECK_EQ(102, script->Run()->Int32Value());
777 }
778 // Test constructor calls.
779 {
780 Local<v8::FunctionTemplate> fun_templ =
781 v8::FunctionTemplate::New(construct_call);
782 fun_templ->SetClassName(v8_str("funky"));
783 Local<Function> fun = fun_templ->GetFunction();
784 env->Global()->Set(v8_str("obj"), fun);
785 Local<Script> script = v8_compile("var s = new obj(); s.x");
786 CHECK_EQ(1, script->Run()->Int32Value());
787
788 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
789 CHECK_EQ(v8_str("[object funky]"), result);
790 }
791}
792
793
794THREADED_TEST(FindInstanceInPrototypeChain) {
795 v8::HandleScope scope;
796 LocalContext env;
797
798 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
799 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
800 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
801 derived->Inherit(base);
802
803 Local<v8::Function> base_function = base->GetFunction();
804 Local<v8::Function> derived_function = derived->GetFunction();
805 Local<v8::Function> other_function = other->GetFunction();
806
807 Local<v8::Object> base_instance = base_function->NewInstance();
808 Local<v8::Object> derived_instance = derived_function->NewInstance();
809 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
810 Local<v8::Object> other_instance = other_function->NewInstance();
811 derived_instance2->Set(v8_str("__proto__"), derived_instance);
812 other_instance->Set(v8_str("__proto__"), derived_instance2);
813
814 // base_instance is only an instance of base.
815 CHECK_EQ(base_instance,
816 base_instance->FindInstanceInPrototypeChain(base));
817 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
818 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
819
820 // derived_instance is an instance of base and derived.
821 CHECK_EQ(derived_instance,
822 derived_instance->FindInstanceInPrototypeChain(base));
823 CHECK_EQ(derived_instance,
824 derived_instance->FindInstanceInPrototypeChain(derived));
825 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
826
827 // other_instance is an instance of other and its immediate
828 // prototype derived_instance2 is an instance of base and derived.
829 // Note, derived_instance is an instance of base and derived too,
830 // but it comes after derived_instance2 in the prototype chain of
831 // other_instance.
832 CHECK_EQ(derived_instance2,
833 other_instance->FindInstanceInPrototypeChain(base));
834 CHECK_EQ(derived_instance2,
835 other_instance->FindInstanceInPrototypeChain(derived));
836 CHECK_EQ(other_instance,
837 other_instance->FindInstanceInPrototypeChain(other));
838}
839
840
Steve Block3ce2e202009-11-05 08:53:23 +0000841THREADED_TEST(TinyInteger) {
842 v8::HandleScope scope;
843 LocalContext env;
844 int32_t value = 239;
845 Local<v8::Integer> value_obj = v8::Integer::New(value);
846 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
847}
848
849
850THREADED_TEST(BigSmiInteger) {
851 v8::HandleScope scope;
852 LocalContext env;
853 int32_t value = i::Smi::kMaxValue;
854 // We cannot add one to a Smi::kMaxValue without wrapping.
855 if (i::kSmiValueSize < 32) {
856 CHECK(i::Smi::IsValid(value));
857 CHECK(!i::Smi::IsValid(value + 1));
858 Local<v8::Integer> value_obj = v8::Integer::New(value);
859 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
860 }
861}
862
863
864THREADED_TEST(BigInteger) {
865 v8::HandleScope scope;
866 LocalContext env;
867 // We cannot add one to a Smi::kMaxValue without wrapping.
868 if (i::kSmiValueSize < 32) {
869 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
870 // The code will not be run in that case, due to the "if" guard.
871 int32_t value =
872 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
873 CHECK(value > i::Smi::kMaxValue);
874 CHECK(!i::Smi::IsValid(value));
875 Local<v8::Integer> value_obj = v8::Integer::New(value);
876 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
877 }
878}
879
880
881THREADED_TEST(TinyUnsignedInteger) {
882 v8::HandleScope scope;
883 LocalContext env;
884 uint32_t value = 239;
885 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
886 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
887}
888
889
890THREADED_TEST(BigUnsignedSmiInteger) {
891 v8::HandleScope scope;
892 LocalContext env;
893 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
894 CHECK(i::Smi::IsValid(value));
895 CHECK(!i::Smi::IsValid(value + 1));
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(BigUnsignedInteger) {
902 v8::HandleScope scope;
903 LocalContext env;
904 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
905 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
906 CHECK(!i::Smi::IsValid(value));
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(OutOfSignedRangeUnsignedInteger) {
913 v8::HandleScope scope;
914 LocalContext env;
915 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
916 uint32_t value = INT32_MAX_AS_UINT + 1;
917 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
918 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
919 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
920}
921
922
Steve Blocka7e24c12009-10-30 11:49:00 +0000923THREADED_TEST(Number) {
924 v8::HandleScope scope;
925 LocalContext env;
926 double PI = 3.1415926;
927 Local<v8::Number> pi_obj = v8::Number::New(PI);
928 CHECK_EQ(PI, pi_obj->NumberValue());
929}
930
931
932THREADED_TEST(ToNumber) {
933 v8::HandleScope scope;
934 LocalContext env;
935 Local<String> str = v8_str("3.1415926");
936 CHECK_EQ(3.1415926, str->NumberValue());
937 v8::Handle<v8::Boolean> t = v8::True();
938 CHECK_EQ(1.0, t->NumberValue());
939 v8::Handle<v8::Boolean> f = v8::False();
940 CHECK_EQ(0.0, f->NumberValue());
941}
942
943
944THREADED_TEST(Date) {
945 v8::HandleScope scope;
946 LocalContext env;
947 double PI = 3.1415926;
948 Local<Value> date_obj = v8::Date::New(PI);
949 CHECK_EQ(3.0, date_obj->NumberValue());
950}
951
952
953THREADED_TEST(Boolean) {
954 v8::HandleScope scope;
955 LocalContext env;
956 v8::Handle<v8::Boolean> t = v8::True();
957 CHECK(t->Value());
958 v8::Handle<v8::Boolean> f = v8::False();
959 CHECK(!f->Value());
960 v8::Handle<v8::Primitive> u = v8::Undefined();
961 CHECK(!u->BooleanValue());
962 v8::Handle<v8::Primitive> n = v8::Null();
963 CHECK(!n->BooleanValue());
964 v8::Handle<String> str1 = v8_str("");
965 CHECK(!str1->BooleanValue());
966 v8::Handle<String> str2 = v8_str("x");
967 CHECK(str2->BooleanValue());
968 CHECK(!v8::Number::New(0)->BooleanValue());
969 CHECK(v8::Number::New(-1)->BooleanValue());
970 CHECK(v8::Number::New(1)->BooleanValue());
971 CHECK(v8::Number::New(42)->BooleanValue());
972 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
973}
974
975
976static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
977 ApiTestFuzzer::Fuzz();
978 return v8_num(13.4);
979}
980
981
982static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
983 ApiTestFuzzer::Fuzz();
984 return v8_num(876);
985}
986
987
988THREADED_TEST(GlobalPrototype) {
989 v8::HandleScope scope;
990 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
991 func_templ->PrototypeTemplate()->Set(
992 "dummy",
993 v8::FunctionTemplate::New(DummyCallHandler));
994 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
995 templ->Set("x", v8_num(200));
996 templ->SetAccessor(v8_str("m"), GetM);
997 LocalContext env(0, templ);
998 v8::Handle<v8::Object> obj = env->Global();
999 v8::Handle<Script> script = v8_compile("dummy()");
1000 v8::Handle<Value> result = script->Run();
1001 CHECK_EQ(13.4, result->NumberValue());
1002 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
1003 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
1004}
1005
1006
Steve Blocka7e24c12009-10-30 11:49:00 +00001007THREADED_TEST(ObjectTemplate) {
1008 v8::HandleScope scope;
1009 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
1010 templ1->Set("x", v8_num(10));
1011 templ1->Set("y", v8_num(13));
1012 LocalContext env;
1013 Local<v8::Object> instance1 = templ1->NewInstance();
1014 env->Global()->Set(v8_str("p"), instance1);
1015 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
1016 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1017 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1018 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1019 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1020 templ2->Set("a", v8_num(12));
1021 templ2->Set("b", templ1);
1022 Local<v8::Object> instance2 = templ2->NewInstance();
1023 env->Global()->Set(v8_str("q"), instance2);
1024 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1025 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1026 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1027 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1028}
1029
1030
1031static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1032 ApiTestFuzzer::Fuzz();
1033 return v8_num(17.2);
1034}
1035
1036
1037static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1038 ApiTestFuzzer::Fuzz();
1039 return v8_num(15.2);
1040}
1041
1042
1043THREADED_TEST(DescriptorInheritance) {
1044 v8::HandleScope scope;
1045 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1046 super->PrototypeTemplate()->Set("flabby",
1047 v8::FunctionTemplate::New(GetFlabby));
1048 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1049
1050 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1051
1052 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1053 base1->Inherit(super);
1054 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1055
1056 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1057 base2->Inherit(super);
1058 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1059
1060 LocalContext env;
1061
1062 env->Global()->Set(v8_str("s"), super->GetFunction());
1063 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1064 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1065
1066 // Checks right __proto__ chain.
1067 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1068 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1069
1070 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1071
1072 // Instance accessor should not be visible on function object or its prototype
1073 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1074 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1075 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1076
1077 env->Global()->Set(v8_str("obj"),
1078 base1->GetFunction()->NewInstance());
1079 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1080 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1081 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1082 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1083 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1084
1085 env->Global()->Set(v8_str("obj2"),
1086 base2->GetFunction()->NewInstance());
1087 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1088 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1089 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1090 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1091 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1092
1093 // base1 and base2 cannot cross reference to each's prototype
1094 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1095 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1096}
1097
1098
1099int echo_named_call_count;
1100
1101
1102static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1103 const AccessorInfo& info) {
1104 ApiTestFuzzer::Fuzz();
1105 CHECK_EQ(v8_str("data"), info.Data());
1106 echo_named_call_count++;
1107 return name;
1108}
1109
1110
1111THREADED_TEST(NamedPropertyHandlerGetter) {
1112 echo_named_call_count = 0;
1113 v8::HandleScope scope;
1114 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1115 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1116 0, 0, 0, 0,
1117 v8_str("data"));
1118 LocalContext env;
1119 env->Global()->Set(v8_str("obj"),
1120 templ->GetFunction()->NewInstance());
1121 CHECK_EQ(echo_named_call_count, 0);
1122 v8_compile("obj.x")->Run();
1123 CHECK_EQ(echo_named_call_count, 1);
1124 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1125 v8::Handle<Value> str = CompileRun(code);
1126 String::AsciiValue value(str);
1127 CHECK_EQ(*value, "oddlepoddle");
1128 // Check default behavior
1129 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1130 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1131 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1132}
1133
1134
1135int echo_indexed_call_count = 0;
1136
1137
1138static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1139 const AccessorInfo& info) {
1140 ApiTestFuzzer::Fuzz();
1141 CHECK_EQ(v8_num(637), info.Data());
1142 echo_indexed_call_count++;
1143 return v8_num(index);
1144}
1145
1146
1147THREADED_TEST(IndexedPropertyHandlerGetter) {
1148 v8::HandleScope scope;
1149 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1150 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1151 0, 0, 0, 0,
1152 v8_num(637));
1153 LocalContext env;
1154 env->Global()->Set(v8_str("obj"),
1155 templ->GetFunction()->NewInstance());
1156 Local<Script> script = v8_compile("obj[900]");
1157 CHECK_EQ(script->Run()->Int32Value(), 900);
1158}
1159
1160
1161v8::Handle<v8::Object> bottom;
1162
1163static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1164 uint32_t index,
1165 const AccessorInfo& info) {
1166 ApiTestFuzzer::Fuzz();
1167 CHECK(info.This()->Equals(bottom));
1168 return v8::Handle<Value>();
1169}
1170
1171static v8::Handle<Value> CheckThisNamedPropertyHandler(
1172 Local<String> name,
1173 const AccessorInfo& info) {
1174 ApiTestFuzzer::Fuzz();
1175 CHECK(info.This()->Equals(bottom));
1176 return v8::Handle<Value>();
1177}
1178
1179
1180v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1181 Local<Value> value,
1182 const AccessorInfo& info) {
1183 ApiTestFuzzer::Fuzz();
1184 CHECK(info.This()->Equals(bottom));
1185 return v8::Handle<Value>();
1186}
1187
1188
1189v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1190 Local<Value> value,
1191 const AccessorInfo& info) {
1192 ApiTestFuzzer::Fuzz();
1193 CHECK(info.This()->Equals(bottom));
1194 return v8::Handle<Value>();
1195}
1196
1197v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery(
1198 uint32_t index,
1199 const AccessorInfo& info) {
1200 ApiTestFuzzer::Fuzz();
1201 CHECK(info.This()->Equals(bottom));
1202 return v8::Handle<v8::Boolean>();
1203}
1204
1205
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001206v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001207 const AccessorInfo& info) {
1208 ApiTestFuzzer::Fuzz();
1209 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001210 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001211}
1212
1213
1214v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
1215 uint32_t index,
1216 const AccessorInfo& info) {
1217 ApiTestFuzzer::Fuzz();
1218 CHECK(info.This()->Equals(bottom));
1219 return v8::Handle<v8::Boolean>();
1220}
1221
1222
1223v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1224 Local<String> property,
1225 const AccessorInfo& info) {
1226 ApiTestFuzzer::Fuzz();
1227 CHECK(info.This()->Equals(bottom));
1228 return v8::Handle<v8::Boolean>();
1229}
1230
1231
1232v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1233 const AccessorInfo& info) {
1234 ApiTestFuzzer::Fuzz();
1235 CHECK(info.This()->Equals(bottom));
1236 return v8::Handle<v8::Array>();
1237}
1238
1239
1240v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1241 const AccessorInfo& info) {
1242 ApiTestFuzzer::Fuzz();
1243 CHECK(info.This()->Equals(bottom));
1244 return v8::Handle<v8::Array>();
1245}
1246
1247
1248THREADED_TEST(PropertyHandlerInPrototype) {
1249 v8::HandleScope scope;
1250 LocalContext env;
1251
1252 // Set up a prototype chain with three interceptors.
1253 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1254 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1255 CheckThisIndexedPropertyHandler,
1256 CheckThisIndexedPropertySetter,
1257 CheckThisIndexedPropertyQuery,
1258 CheckThisIndexedPropertyDeleter,
1259 CheckThisIndexedPropertyEnumerator);
1260
1261 templ->InstanceTemplate()->SetNamedPropertyHandler(
1262 CheckThisNamedPropertyHandler,
1263 CheckThisNamedPropertySetter,
1264 CheckThisNamedPropertyQuery,
1265 CheckThisNamedPropertyDeleter,
1266 CheckThisNamedPropertyEnumerator);
1267
1268 bottom = templ->GetFunction()->NewInstance();
1269 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1270 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1271
1272 bottom->Set(v8_str("__proto__"), middle);
1273 middle->Set(v8_str("__proto__"), top);
1274 env->Global()->Set(v8_str("obj"), bottom);
1275
1276 // Indexed and named get.
1277 Script::Compile(v8_str("obj[0]"))->Run();
1278 Script::Compile(v8_str("obj.x"))->Run();
1279
1280 // Indexed and named set.
1281 Script::Compile(v8_str("obj[1] = 42"))->Run();
1282 Script::Compile(v8_str("obj.y = 42"))->Run();
1283
1284 // Indexed and named query.
1285 Script::Compile(v8_str("0 in obj"))->Run();
1286 Script::Compile(v8_str("'x' in obj"))->Run();
1287
1288 // Indexed and named deleter.
1289 Script::Compile(v8_str("delete obj[0]"))->Run();
1290 Script::Compile(v8_str("delete obj.x"))->Run();
1291
1292 // Enumerators.
1293 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1294}
1295
1296
1297static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1298 const AccessorInfo& info) {
1299 ApiTestFuzzer::Fuzz();
1300 if (v8_str("pre")->Equals(key)) {
1301 return v8_str("PrePropertyHandler: pre");
1302 }
1303 return v8::Handle<String>();
1304}
1305
1306
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001307static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1308 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001309 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001310 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001311 }
1312
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001313 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001314}
1315
1316
1317THREADED_TEST(PrePropertyHandler) {
1318 v8::HandleScope scope;
1319 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1320 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1321 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001322 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001323 LocalContext env(NULL, desc->InstanceTemplate());
1324 Script::Compile(v8_str(
1325 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1326 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1327 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1328 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1329 CHECK_EQ(v8_str("Object: on"), result_on);
1330 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1331 CHECK(result_post.IsEmpty());
1332}
1333
1334
1335THREADED_TEST(UndefinedIsNotEnumerable) {
1336 v8::HandleScope scope;
1337 LocalContext env;
1338 v8::Handle<Value> result = Script::Compile(v8_str(
1339 "this.propertyIsEnumerable(undefined)"))->Run();
1340 CHECK(result->IsFalse());
1341}
1342
1343
1344v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001345static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001346
1347
1348static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1349 ApiTestFuzzer::Fuzz();
1350 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1351 if (depth == kTargetRecursionDepth) return v8::Undefined();
1352 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1353 return call_recursively_script->Run();
1354}
1355
1356
1357static v8::Handle<Value> CallFunctionRecursivelyCall(
1358 const v8::Arguments& args) {
1359 ApiTestFuzzer::Fuzz();
1360 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1361 if (depth == kTargetRecursionDepth) {
1362 printf("[depth = %d]\n", depth);
1363 return v8::Undefined();
1364 }
1365 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1366 v8::Handle<Value> function =
1367 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001368 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001369}
1370
1371
1372THREADED_TEST(DeepCrossLanguageRecursion) {
1373 v8::HandleScope scope;
1374 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1375 global->Set(v8_str("callScriptRecursively"),
1376 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1377 global->Set(v8_str("callFunctionRecursively"),
1378 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1379 LocalContext env(NULL, global);
1380
1381 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1382 call_recursively_script = v8_compile("callScriptRecursively()");
1383 v8::Handle<Value> result = call_recursively_script->Run();
1384 call_recursively_script = v8::Handle<Script>();
1385
1386 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1387 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1388}
1389
1390
1391static v8::Handle<Value>
1392 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1393 ApiTestFuzzer::Fuzz();
1394 return v8::ThrowException(key);
1395}
1396
1397
1398static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1399 Local<Value>,
1400 const AccessorInfo&) {
1401 v8::ThrowException(key);
1402 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1403}
1404
1405
1406THREADED_TEST(CallbackExceptionRegression) {
1407 v8::HandleScope scope;
1408 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1409 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1410 ThrowingPropertyHandlerSet);
1411 LocalContext env;
1412 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1413 v8::Handle<Value> otto = Script::Compile(v8_str(
1414 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1415 CHECK_EQ(v8_str("otto"), otto);
1416 v8::Handle<Value> netto = Script::Compile(v8_str(
1417 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1418 CHECK_EQ(v8_str("netto"), netto);
1419}
1420
1421
Steve Blocka7e24c12009-10-30 11:49:00 +00001422THREADED_TEST(FunctionPrototype) {
1423 v8::HandleScope scope;
1424 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1425 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1426 LocalContext env;
1427 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1428 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1429 CHECK_EQ(script->Run()->Int32Value(), 321);
1430}
1431
1432
1433THREADED_TEST(InternalFields) {
1434 v8::HandleScope scope;
1435 LocalContext env;
1436
1437 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1438 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1439 instance_templ->SetInternalFieldCount(1);
1440 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1441 CHECK_EQ(1, obj->InternalFieldCount());
1442 CHECK(obj->GetInternalField(0)->IsUndefined());
1443 obj->SetInternalField(0, v8_num(17));
1444 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1445}
1446
1447
Steve Block6ded16b2010-05-10 14:33:55 +01001448THREADED_TEST(GlobalObjectInternalFields) {
1449 v8::HandleScope scope;
1450 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1451 global_template->SetInternalFieldCount(1);
1452 LocalContext env(NULL, global_template);
1453 v8::Handle<v8::Object> global_proxy = env->Global();
1454 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1455 CHECK_EQ(1, global->InternalFieldCount());
1456 CHECK(global->GetInternalField(0)->IsUndefined());
1457 global->SetInternalField(0, v8_num(17));
1458 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1459}
1460
1461
Steve Blocka7e24c12009-10-30 11:49:00 +00001462THREADED_TEST(InternalFieldsNativePointers) {
1463 v8::HandleScope scope;
1464 LocalContext env;
1465
1466 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1467 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1468 instance_templ->SetInternalFieldCount(1);
1469 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1470 CHECK_EQ(1, obj->InternalFieldCount());
1471 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1472
1473 char* data = new char[100];
1474
1475 void* aligned = data;
1476 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1477 void* unaligned = data + 1;
1478 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1479
1480 // Check reading and writing aligned pointers.
1481 obj->SetPointerInInternalField(0, aligned);
1482 i::Heap::CollectAllGarbage(false);
1483 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1484
1485 // Check reading and writing unaligned pointers.
1486 obj->SetPointerInInternalField(0, unaligned);
1487 i::Heap::CollectAllGarbage(false);
1488 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1489
1490 delete[] data;
1491}
1492
1493
Steve Block3ce2e202009-11-05 08:53:23 +00001494THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1495 v8::HandleScope scope;
1496 LocalContext env;
1497
1498 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1499 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1500 instance_templ->SetInternalFieldCount(1);
1501 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1502 CHECK_EQ(1, obj->InternalFieldCount());
1503 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1504
1505 char* data = new char[100];
1506
1507 void* aligned = data;
1508 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1509 void* unaligned = data + 1;
1510 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1511
1512 obj->SetPointerInInternalField(0, aligned);
1513 i::Heap::CollectAllGarbage(false);
1514 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1515
1516 obj->SetPointerInInternalField(0, unaligned);
1517 i::Heap::CollectAllGarbage(false);
1518 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1519
1520 obj->SetInternalField(0, v8::External::Wrap(aligned));
1521 i::Heap::CollectAllGarbage(false);
1522 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1523
1524 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1525 i::Heap::CollectAllGarbage(false);
1526 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1527
1528 delete[] data;
1529}
1530
1531
Steve Blocka7e24c12009-10-30 11:49:00 +00001532THREADED_TEST(IdentityHash) {
1533 v8::HandleScope scope;
1534 LocalContext env;
1535
1536 // Ensure that the test starts with an fresh heap to test whether the hash
1537 // code is based on the address.
1538 i::Heap::CollectAllGarbage(false);
1539 Local<v8::Object> obj = v8::Object::New();
1540 int hash = obj->GetIdentityHash();
1541 int hash1 = obj->GetIdentityHash();
1542 CHECK_EQ(hash, hash1);
1543 int hash2 = v8::Object::New()->GetIdentityHash();
1544 // Since the identity hash is essentially a random number two consecutive
1545 // objects should not be assigned the same hash code. If the test below fails
1546 // the random number generator should be evaluated.
1547 CHECK_NE(hash, hash2);
1548 i::Heap::CollectAllGarbage(false);
1549 int hash3 = v8::Object::New()->GetIdentityHash();
1550 // Make sure that the identity hash is not based on the initial address of
1551 // the object alone. If the test below fails the random number generator
1552 // should be evaluated.
1553 CHECK_NE(hash, hash3);
1554 int hash4 = obj->GetIdentityHash();
1555 CHECK_EQ(hash, hash4);
1556}
1557
1558
1559THREADED_TEST(HiddenProperties) {
1560 v8::HandleScope scope;
1561 LocalContext env;
1562
1563 v8::Local<v8::Object> obj = v8::Object::New();
1564 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1565 v8::Local<v8::String> empty = v8_str("");
1566 v8::Local<v8::String> prop_name = v8_str("prop_name");
1567
1568 i::Heap::CollectAllGarbage(false);
1569
1570 // Make sure delete of a non-existent hidden value works
1571 CHECK(obj->DeleteHiddenValue(key));
1572
1573 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1574 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1575 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1576 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1577
1578 i::Heap::CollectAllGarbage(false);
1579
1580 // Make sure we do not find the hidden property.
1581 CHECK(!obj->Has(empty));
1582 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1583 CHECK(obj->Get(empty)->IsUndefined());
1584 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1585 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1586 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1587 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1588
1589 i::Heap::CollectAllGarbage(false);
1590
1591 // Add another property and delete it afterwards to force the object in
1592 // slow case.
1593 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1594 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1595 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1596 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1597 CHECK(obj->Delete(prop_name));
1598 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1599
1600 i::Heap::CollectAllGarbage(false);
1601
1602 CHECK(obj->DeleteHiddenValue(key));
1603 CHECK(obj->GetHiddenValue(key).IsEmpty());
1604}
1605
1606
Steve Blockd0582a62009-12-15 09:54:21 +00001607static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001608static v8::Handle<Value> InterceptorForHiddenProperties(
1609 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001610 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001611 return v8::Handle<Value>();
1612}
1613
1614
1615THREADED_TEST(HiddenPropertiesWithInterceptors) {
1616 v8::HandleScope scope;
1617 LocalContext context;
1618
Steve Blockd0582a62009-12-15 09:54:21 +00001619 interceptor_for_hidden_properties_called = false;
1620
Steve Blocka7e24c12009-10-30 11:49:00 +00001621 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1622
1623 // Associate an interceptor with an object and start setting hidden values.
1624 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1625 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1626 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1627 Local<v8::Function> function = fun_templ->GetFunction();
1628 Local<v8::Object> obj = function->NewInstance();
1629 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1630 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001631 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001632}
1633
1634
1635THREADED_TEST(External) {
1636 v8::HandleScope scope;
1637 int x = 3;
1638 Local<v8::External> ext = v8::External::New(&x);
1639 LocalContext env;
1640 env->Global()->Set(v8_str("ext"), ext);
1641 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001642 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001643 int* ptr = static_cast<int*>(reext->Value());
1644 CHECK_EQ(x, 3);
1645 *ptr = 10;
1646 CHECK_EQ(x, 10);
1647
1648 // Make sure unaligned pointers are wrapped properly.
1649 char* data = i::StrDup("0123456789");
1650 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1651 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1652 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1653 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1654
1655 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1656 CHECK_EQ('0', *char_ptr);
1657 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1658 CHECK_EQ('1', *char_ptr);
1659 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1660 CHECK_EQ('2', *char_ptr);
1661 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1662 CHECK_EQ('3', *char_ptr);
1663 i::DeleteArray(data);
1664}
1665
1666
1667THREADED_TEST(GlobalHandle) {
1668 v8::Persistent<String> global;
1669 {
1670 v8::HandleScope scope;
1671 Local<String> str = v8_str("str");
1672 global = v8::Persistent<String>::New(str);
1673 }
1674 CHECK_EQ(global->Length(), 3);
1675 global.Dispose();
1676}
1677
1678
1679THREADED_TEST(ScriptException) {
1680 v8::HandleScope scope;
1681 LocalContext env;
1682 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1683 v8::TryCatch try_catch;
1684 Local<Value> result = script->Run();
1685 CHECK(result.IsEmpty());
1686 CHECK(try_catch.HasCaught());
1687 String::AsciiValue exception_value(try_catch.Exception());
1688 CHECK_EQ(*exception_value, "panama!");
1689}
1690
1691
1692bool message_received;
1693
1694
1695static void check_message(v8::Handle<v8::Message> message,
1696 v8::Handle<Value> data) {
1697 CHECK_EQ(5.76, data->NumberValue());
1698 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1699 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1700 message_received = true;
1701}
1702
1703
1704THREADED_TEST(MessageHandlerData) {
1705 message_received = false;
1706 v8::HandleScope scope;
1707 CHECK(!message_received);
1708 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1709 LocalContext context;
1710 v8::ScriptOrigin origin =
1711 v8::ScriptOrigin(v8_str("6.75"));
1712 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1713 &origin);
1714 script->SetData(v8_str("7.56"));
1715 script->Run();
1716 CHECK(message_received);
1717 // clear out the message listener
1718 v8::V8::RemoveMessageListeners(check_message);
1719}
1720
1721
1722THREADED_TEST(GetSetProperty) {
1723 v8::HandleScope scope;
1724 LocalContext context;
1725 context->Global()->Set(v8_str("foo"), v8_num(14));
1726 context->Global()->Set(v8_str("12"), v8_num(92));
1727 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1728 context->Global()->Set(v8_num(13), v8_num(56));
1729 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1730 CHECK_EQ(14, foo->Int32Value());
1731 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1732 CHECK_EQ(92, twelve->Int32Value());
1733 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1734 CHECK_EQ(32, sixteen->Int32Value());
1735 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1736 CHECK_EQ(56, thirteen->Int32Value());
1737 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1738 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1739 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1740 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1741 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1742 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1743 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1744 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1745 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1746}
1747
1748
1749THREADED_TEST(PropertyAttributes) {
1750 v8::HandleScope scope;
1751 LocalContext context;
1752 // read-only
1753 Local<String> prop = v8_str("read_only");
1754 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1755 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1756 Script::Compile(v8_str("read_only = 9"))->Run();
1757 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1758 context->Global()->Set(prop, v8_num(10));
1759 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1760 // dont-delete
1761 prop = v8_str("dont_delete");
1762 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1763 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1764 Script::Compile(v8_str("delete dont_delete"))->Run();
1765 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1766}
1767
1768
1769THREADED_TEST(Array) {
1770 v8::HandleScope scope;
1771 LocalContext context;
1772 Local<v8::Array> array = v8::Array::New();
1773 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001774 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001775 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001776 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001777 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001778 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001779 CHECK_EQ(3, array->Length());
1780 CHECK(!array->Has(0));
1781 CHECK(!array->Has(1));
1782 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001783 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001784 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001785 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001786 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001787 CHECK_EQ(1, arr->Get(0)->Int32Value());
1788 CHECK_EQ(2, arr->Get(1)->Int32Value());
1789 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001790}
1791
1792
1793v8::Handle<Value> HandleF(const v8::Arguments& args) {
1794 v8::HandleScope scope;
1795 ApiTestFuzzer::Fuzz();
1796 Local<v8::Array> result = v8::Array::New(args.Length());
1797 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001798 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001799 return scope.Close(result);
1800}
1801
1802
1803THREADED_TEST(Vector) {
1804 v8::HandleScope scope;
1805 Local<ObjectTemplate> global = ObjectTemplate::New();
1806 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1807 LocalContext context(0, global);
1808
1809 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001810 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001811 CHECK_EQ(0, a0->Length());
1812
1813 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001814 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001815 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001816 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001817
1818 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001819 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001820 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001821 CHECK_EQ(12, a2->Get(0)->Int32Value());
1822 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001823
1824 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001825 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001826 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001827 CHECK_EQ(14, a3->Get(0)->Int32Value());
1828 CHECK_EQ(15, a3->Get(1)->Int32Value());
1829 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001830
1831 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001832 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001833 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001834 CHECK_EQ(17, a4->Get(0)->Int32Value());
1835 CHECK_EQ(18, a4->Get(1)->Int32Value());
1836 CHECK_EQ(19, a4->Get(2)->Int32Value());
1837 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001838}
1839
1840
1841THREADED_TEST(FunctionCall) {
1842 v8::HandleScope scope;
1843 LocalContext context;
1844 CompileRun(
1845 "function Foo() {"
1846 " var result = [];"
1847 " for (var i = 0; i < arguments.length; i++) {"
1848 " result.push(arguments[i]);"
1849 " }"
1850 " return result;"
1851 "}");
1852 Local<Function> Foo =
1853 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1854
1855 v8::Handle<Value>* args0 = NULL;
1856 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1857 CHECK_EQ(0, a0->Length());
1858
1859 v8::Handle<Value> args1[] = { v8_num(1.1) };
1860 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1861 CHECK_EQ(1, a1->Length());
1862 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1863
1864 v8::Handle<Value> args2[] = { v8_num(2.2),
1865 v8_num(3.3) };
1866 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1867 CHECK_EQ(2, a2->Length());
1868 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1869 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1870
1871 v8::Handle<Value> args3[] = { v8_num(4.4),
1872 v8_num(5.5),
1873 v8_num(6.6) };
1874 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1875 CHECK_EQ(3, a3->Length());
1876 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1877 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1878 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1879
1880 v8::Handle<Value> args4[] = { v8_num(7.7),
1881 v8_num(8.8),
1882 v8_num(9.9),
1883 v8_num(10.11) };
1884 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1885 CHECK_EQ(4, a4->Length());
1886 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1887 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1888 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1889 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1890}
1891
1892
1893static const char* js_code_causing_out_of_memory =
1894 "var a = new Array(); while(true) a.push(a);";
1895
1896
1897// These tests run for a long time and prevent us from running tests
1898// that come after them so they cannot run in parallel.
1899TEST(OutOfMemory) {
1900 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001901 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001902 // Set heap limits.
1903 static const int K = 1024;
1904 v8::ResourceConstraints constraints;
1905 constraints.set_max_young_space_size(256 * K);
1906 constraints.set_max_old_space_size(4 * K * K);
1907 v8::SetResourceConstraints(&constraints);
1908
1909 // Execute a script that causes out of memory.
1910 v8::HandleScope scope;
1911 LocalContext context;
1912 v8::V8::IgnoreOutOfMemoryException();
1913 Local<Script> script =
1914 Script::Compile(String::New(js_code_causing_out_of_memory));
1915 Local<Value> result = script->Run();
1916
1917 // Check for out of memory state.
1918 CHECK(result.IsEmpty());
1919 CHECK(context->HasOutOfMemoryException());
1920}
1921
1922
1923v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1924 ApiTestFuzzer::Fuzz();
1925
1926 v8::HandleScope scope;
1927 LocalContext context;
1928 Local<Script> script =
1929 Script::Compile(String::New(js_code_causing_out_of_memory));
1930 Local<Value> result = script->Run();
1931
1932 // Check for out of memory state.
1933 CHECK(result.IsEmpty());
1934 CHECK(context->HasOutOfMemoryException());
1935
1936 return result;
1937}
1938
1939
1940TEST(OutOfMemoryNested) {
1941 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001942 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001943 // Set heap limits.
1944 static const int K = 1024;
1945 v8::ResourceConstraints constraints;
1946 constraints.set_max_young_space_size(256 * K);
1947 constraints.set_max_old_space_size(4 * K * K);
1948 v8::SetResourceConstraints(&constraints);
1949
1950 v8::HandleScope scope;
1951 Local<ObjectTemplate> templ = ObjectTemplate::New();
1952 templ->Set(v8_str("ProvokeOutOfMemory"),
1953 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1954 LocalContext context(0, templ);
1955 v8::V8::IgnoreOutOfMemoryException();
1956 Local<Value> result = CompileRun(
1957 "var thrown = false;"
1958 "try {"
1959 " ProvokeOutOfMemory();"
1960 "} catch (e) {"
1961 " thrown = true;"
1962 "}");
1963 // Check for out of memory state.
1964 CHECK(result.IsEmpty());
1965 CHECK(context->HasOutOfMemoryException());
1966}
1967
1968
1969TEST(HugeConsStringOutOfMemory) {
1970 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001971 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001972 v8::HandleScope scope;
1973 LocalContext context;
1974 // Set heap limits.
1975 static const int K = 1024;
1976 v8::ResourceConstraints constraints;
1977 constraints.set_max_young_space_size(256 * K);
1978 constraints.set_max_old_space_size(2 * K * K);
1979 v8::SetResourceConstraints(&constraints);
1980
1981 // Execute a script that causes out of memory.
1982 v8::V8::IgnoreOutOfMemoryException();
1983
1984 // Build huge string. This should fail with out of memory exception.
1985 Local<Value> result = CompileRun(
1986 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00001987 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00001988
1989 // Check for out of memory state.
1990 CHECK(result.IsEmpty());
1991 CHECK(context->HasOutOfMemoryException());
1992}
1993
1994
1995THREADED_TEST(ConstructCall) {
1996 v8::HandleScope scope;
1997 LocalContext context;
1998 CompileRun(
1999 "function Foo() {"
2000 " var result = [];"
2001 " for (var i = 0; i < arguments.length; i++) {"
2002 " result.push(arguments[i]);"
2003 " }"
2004 " return result;"
2005 "}");
2006 Local<Function> Foo =
2007 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
2008
2009 v8::Handle<Value>* args0 = NULL;
2010 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
2011 CHECK_EQ(0, a0->Length());
2012
2013 v8::Handle<Value> args1[] = { v8_num(1.1) };
2014 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
2015 CHECK_EQ(1, a1->Length());
2016 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2017
2018 v8::Handle<Value> args2[] = { v8_num(2.2),
2019 v8_num(3.3) };
2020 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2021 CHECK_EQ(2, a2->Length());
2022 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2023 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2024
2025 v8::Handle<Value> args3[] = { v8_num(4.4),
2026 v8_num(5.5),
2027 v8_num(6.6) };
2028 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2029 CHECK_EQ(3, a3->Length());
2030 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2031 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2032 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2033
2034 v8::Handle<Value> args4[] = { v8_num(7.7),
2035 v8_num(8.8),
2036 v8_num(9.9),
2037 v8_num(10.11) };
2038 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2039 CHECK_EQ(4, a4->Length());
2040 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2041 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2042 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2043 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2044}
2045
2046
2047static void CheckUncle(v8::TryCatch* try_catch) {
2048 CHECK(try_catch->HasCaught());
2049 String::AsciiValue str_value(try_catch->Exception());
2050 CHECK_EQ(*str_value, "uncle?");
2051 try_catch->Reset();
2052}
2053
2054
Steve Block6ded16b2010-05-10 14:33:55 +01002055THREADED_TEST(ConversionNumber) {
2056 v8::HandleScope scope;
2057 LocalContext env;
2058 // Very large number.
2059 CompileRun("var obj = Math.pow(2,32) * 1237;");
2060 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2061 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2062 CHECK_EQ(0, obj->ToInt32()->Value());
2063 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2064 // Large number.
2065 CompileRun("var obj = -1234567890123;");
2066 obj = env->Global()->Get(v8_str("obj"));
2067 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2068 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2069 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2070 // Small positive integer.
2071 CompileRun("var obj = 42;");
2072 obj = env->Global()->Get(v8_str("obj"));
2073 CHECK_EQ(42.0, obj->ToNumber()->Value());
2074 CHECK_EQ(42, obj->ToInt32()->Value());
2075 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2076 // Negative integer.
2077 CompileRun("var obj = -37;");
2078 obj = env->Global()->Get(v8_str("obj"));
2079 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2080 CHECK_EQ(-37, obj->ToInt32()->Value());
2081 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2082 // Positive non-int32 integer.
2083 CompileRun("var obj = 0x81234567;");
2084 obj = env->Global()->Get(v8_str("obj"));
2085 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2086 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2087 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2088 // Fraction.
2089 CompileRun("var obj = 42.3;");
2090 obj = env->Global()->Get(v8_str("obj"));
2091 CHECK_EQ(42.3, obj->ToNumber()->Value());
2092 CHECK_EQ(42, obj->ToInt32()->Value());
2093 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2094 // Large negative fraction.
2095 CompileRun("var obj = -5726623061.75;");
2096 obj = env->Global()->Get(v8_str("obj"));
2097 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2098 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2099 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2100}
2101
2102
2103THREADED_TEST(isNumberType) {
2104 v8::HandleScope scope;
2105 LocalContext env;
2106 // Very large number.
2107 CompileRun("var obj = Math.pow(2,32) * 1237;");
2108 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2109 CHECK(!obj->IsInt32());
2110 CHECK(!obj->IsUint32());
2111 // Large negative number.
2112 CompileRun("var obj = -1234567890123;");
2113 obj = env->Global()->Get(v8_str("obj"));
2114 CHECK(!obj->IsInt32());
2115 CHECK(!obj->IsUint32());
2116 // Small positive integer.
2117 CompileRun("var obj = 42;");
2118 obj = env->Global()->Get(v8_str("obj"));
2119 CHECK(obj->IsInt32());
2120 CHECK(obj->IsUint32());
2121 // Negative integer.
2122 CompileRun("var obj = -37;");
2123 obj = env->Global()->Get(v8_str("obj"));
2124 CHECK(obj->IsInt32());
2125 CHECK(!obj->IsUint32());
2126 // Positive non-int32 integer.
2127 CompileRun("var obj = 0x81234567;");
2128 obj = env->Global()->Get(v8_str("obj"));
2129 CHECK(!obj->IsInt32());
2130 CHECK(obj->IsUint32());
2131 // Fraction.
2132 CompileRun("var obj = 42.3;");
2133 obj = env->Global()->Get(v8_str("obj"));
2134 CHECK(!obj->IsInt32());
2135 CHECK(!obj->IsUint32());
2136 // Large negative fraction.
2137 CompileRun("var obj = -5726623061.75;");
2138 obj = env->Global()->Get(v8_str("obj"));
2139 CHECK(!obj->IsInt32());
2140 CHECK(!obj->IsUint32());
2141}
2142
2143
Steve Blocka7e24c12009-10-30 11:49:00 +00002144THREADED_TEST(ConversionException) {
2145 v8::HandleScope scope;
2146 LocalContext env;
2147 CompileRun(
2148 "function TestClass() { };"
2149 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2150 "var obj = new TestClass();");
2151 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2152
2153 v8::TryCatch try_catch;
2154
2155 Local<Value> to_string_result = obj->ToString();
2156 CHECK(to_string_result.IsEmpty());
2157 CheckUncle(&try_catch);
2158
2159 Local<Value> to_number_result = obj->ToNumber();
2160 CHECK(to_number_result.IsEmpty());
2161 CheckUncle(&try_catch);
2162
2163 Local<Value> to_integer_result = obj->ToInteger();
2164 CHECK(to_integer_result.IsEmpty());
2165 CheckUncle(&try_catch);
2166
2167 Local<Value> to_uint32_result = obj->ToUint32();
2168 CHECK(to_uint32_result.IsEmpty());
2169 CheckUncle(&try_catch);
2170
2171 Local<Value> to_int32_result = obj->ToInt32();
2172 CHECK(to_int32_result.IsEmpty());
2173 CheckUncle(&try_catch);
2174
2175 Local<Value> to_object_result = v8::Undefined()->ToObject();
2176 CHECK(to_object_result.IsEmpty());
2177 CHECK(try_catch.HasCaught());
2178 try_catch.Reset();
2179
2180 int32_t int32_value = obj->Int32Value();
2181 CHECK_EQ(0, int32_value);
2182 CheckUncle(&try_catch);
2183
2184 uint32_t uint32_value = obj->Uint32Value();
2185 CHECK_EQ(0, uint32_value);
2186 CheckUncle(&try_catch);
2187
2188 double number_value = obj->NumberValue();
2189 CHECK_NE(0, IsNaN(number_value));
2190 CheckUncle(&try_catch);
2191
2192 int64_t integer_value = obj->IntegerValue();
2193 CHECK_EQ(0.0, static_cast<double>(integer_value));
2194 CheckUncle(&try_catch);
2195}
2196
2197
2198v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2199 ApiTestFuzzer::Fuzz();
2200 return v8::ThrowException(v8_str("konto"));
2201}
2202
2203
2204v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2205 if (args.Length() < 1) return v8::Boolean::New(false);
2206 v8::HandleScope scope;
2207 v8::TryCatch try_catch;
2208 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2209 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2210 return v8::Boolean::New(try_catch.HasCaught());
2211}
2212
2213
2214THREADED_TEST(APICatch) {
2215 v8::HandleScope scope;
2216 Local<ObjectTemplate> templ = ObjectTemplate::New();
2217 templ->Set(v8_str("ThrowFromC"),
2218 v8::FunctionTemplate::New(ThrowFromC));
2219 LocalContext context(0, templ);
2220 CompileRun(
2221 "var thrown = false;"
2222 "try {"
2223 " ThrowFromC();"
2224 "} catch (e) {"
2225 " thrown = true;"
2226 "}");
2227 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2228 CHECK(thrown->BooleanValue());
2229}
2230
2231
2232THREADED_TEST(APIThrowTryCatch) {
2233 v8::HandleScope scope;
2234 Local<ObjectTemplate> templ = ObjectTemplate::New();
2235 templ->Set(v8_str("ThrowFromC"),
2236 v8::FunctionTemplate::New(ThrowFromC));
2237 LocalContext context(0, templ);
2238 v8::TryCatch try_catch;
2239 CompileRun("ThrowFromC();");
2240 CHECK(try_catch.HasCaught());
2241}
2242
2243
2244// Test that a try-finally block doesn't shadow a try-catch block
2245// when setting up an external handler.
2246//
2247// BUG(271): Some of the exception propagation does not work on the
2248// ARM simulator because the simulator separates the C++ stack and the
2249// JS stack. This test therefore fails on the simulator. The test is
2250// not threaded to allow the threading tests to run on the simulator.
2251TEST(TryCatchInTryFinally) {
2252 v8::HandleScope scope;
2253 Local<ObjectTemplate> templ = ObjectTemplate::New();
2254 templ->Set(v8_str("CCatcher"),
2255 v8::FunctionTemplate::New(CCatcher));
2256 LocalContext context(0, templ);
2257 Local<Value> result = CompileRun("try {"
2258 " try {"
2259 " CCatcher('throw 7;');"
2260 " } finally {"
2261 " }"
2262 "} catch (e) {"
2263 "}");
2264 CHECK(result->IsTrue());
2265}
2266
2267
2268static void receive_message(v8::Handle<v8::Message> message,
2269 v8::Handle<v8::Value> data) {
2270 message->Get();
2271 message_received = true;
2272}
2273
2274
2275TEST(APIThrowMessage) {
2276 message_received = false;
2277 v8::HandleScope scope;
2278 v8::V8::AddMessageListener(receive_message);
2279 Local<ObjectTemplate> templ = ObjectTemplate::New();
2280 templ->Set(v8_str("ThrowFromC"),
2281 v8::FunctionTemplate::New(ThrowFromC));
2282 LocalContext context(0, templ);
2283 CompileRun("ThrowFromC();");
2284 CHECK(message_received);
2285 v8::V8::RemoveMessageListeners(check_message);
2286}
2287
2288
2289TEST(APIThrowMessageAndVerboseTryCatch) {
2290 message_received = false;
2291 v8::HandleScope scope;
2292 v8::V8::AddMessageListener(receive_message);
2293 Local<ObjectTemplate> templ = ObjectTemplate::New();
2294 templ->Set(v8_str("ThrowFromC"),
2295 v8::FunctionTemplate::New(ThrowFromC));
2296 LocalContext context(0, templ);
2297 v8::TryCatch try_catch;
2298 try_catch.SetVerbose(true);
2299 Local<Value> result = CompileRun("ThrowFromC();");
2300 CHECK(try_catch.HasCaught());
2301 CHECK(result.IsEmpty());
2302 CHECK(message_received);
2303 v8::V8::RemoveMessageListeners(check_message);
2304}
2305
2306
2307THREADED_TEST(ExternalScriptException) {
2308 v8::HandleScope scope;
2309 Local<ObjectTemplate> templ = ObjectTemplate::New();
2310 templ->Set(v8_str("ThrowFromC"),
2311 v8::FunctionTemplate::New(ThrowFromC));
2312 LocalContext context(0, templ);
2313
2314 v8::TryCatch try_catch;
2315 Local<Script> script
2316 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2317 Local<Value> result = script->Run();
2318 CHECK(result.IsEmpty());
2319 CHECK(try_catch.HasCaught());
2320 String::AsciiValue exception_value(try_catch.Exception());
2321 CHECK_EQ("konto", *exception_value);
2322}
2323
2324
2325
2326v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2327 ApiTestFuzzer::Fuzz();
2328 CHECK_EQ(4, args.Length());
2329 int count = args[0]->Int32Value();
2330 int cInterval = args[2]->Int32Value();
2331 if (count == 0) {
2332 return v8::ThrowException(v8_str("FromC"));
2333 } else {
2334 Local<v8::Object> global = Context::GetCurrent()->Global();
2335 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2336 v8::Handle<Value> argv[] = { v8_num(count - 1),
2337 args[1],
2338 args[2],
2339 args[3] };
2340 if (count % cInterval == 0) {
2341 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002342 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002343 int expected = args[3]->Int32Value();
2344 if (try_catch.HasCaught()) {
2345 CHECK_EQ(expected, count);
2346 CHECK(result.IsEmpty());
2347 CHECK(!i::Top::has_scheduled_exception());
2348 } else {
2349 CHECK_NE(expected, count);
2350 }
2351 return result;
2352 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002353 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002354 }
2355 }
2356}
2357
2358
2359v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2360 ApiTestFuzzer::Fuzz();
2361 CHECK_EQ(3, args.Length());
2362 bool equality = args[0]->BooleanValue();
2363 int count = args[1]->Int32Value();
2364 int expected = args[2]->Int32Value();
2365 if (equality) {
2366 CHECK_EQ(count, expected);
2367 } else {
2368 CHECK_NE(count, expected);
2369 }
2370 return v8::Undefined();
2371}
2372
2373
2374THREADED_TEST(EvalInTryFinally) {
2375 v8::HandleScope scope;
2376 LocalContext context;
2377 v8::TryCatch try_catch;
2378 CompileRun("(function() {"
2379 " try {"
2380 " eval('asldkf (*&^&*^');"
2381 " } finally {"
2382 " return;"
2383 " }"
2384 "})()");
2385 CHECK(!try_catch.HasCaught());
2386}
2387
2388
2389// This test works by making a stack of alternating JavaScript and C
2390// activations. These activations set up exception handlers with regular
2391// intervals, one interval for C activations and another for JavaScript
2392// activations. When enough activations have been created an exception is
2393// thrown and we check that the right activation catches the exception and that
2394// no other activations do. The right activation is always the topmost one with
2395// a handler, regardless of whether it is in JavaScript or C.
2396//
2397// The notation used to describe a test case looks like this:
2398//
2399// *JS[4] *C[3] @JS[2] C[1] JS[0]
2400//
2401// Each entry is an activation, either JS or C. The index is the count at that
2402// level. Stars identify activations with exception handlers, the @ identifies
2403// the exception handler that should catch the exception.
2404//
2405// BUG(271): Some of the exception propagation does not work on the
2406// ARM simulator because the simulator separates the C++ stack and the
2407// JS stack. This test therefore fails on the simulator. The test is
2408// not threaded to allow the threading tests to run on the simulator.
2409TEST(ExceptionOrder) {
2410 v8::HandleScope scope;
2411 Local<ObjectTemplate> templ = ObjectTemplate::New();
2412 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2413 templ->Set(v8_str("CThrowCountDown"),
2414 v8::FunctionTemplate::New(CThrowCountDown));
2415 LocalContext context(0, templ);
2416 CompileRun(
2417 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2418 " if (count == 0) throw 'FromJS';"
2419 " if (count % jsInterval == 0) {"
2420 " try {"
2421 " var value = CThrowCountDown(count - 1,"
2422 " jsInterval,"
2423 " cInterval,"
2424 " expected);"
2425 " check(false, count, expected);"
2426 " return value;"
2427 " } catch (e) {"
2428 " check(true, count, expected);"
2429 " }"
2430 " } else {"
2431 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2432 " }"
2433 "}");
2434 Local<Function> fun =
2435 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2436
2437 const int argc = 4;
2438 // count jsInterval cInterval expected
2439
2440 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2441 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2442 fun->Call(fun, argc, a0);
2443
2444 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2445 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2446 fun->Call(fun, argc, a1);
2447
2448 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2449 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2450 fun->Call(fun, argc, a2);
2451
2452 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2453 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2454 fun->Call(fun, argc, a3);
2455
2456 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2457 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2458 fun->Call(fun, argc, a4);
2459
2460 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2461 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2462 fun->Call(fun, argc, a5);
2463}
2464
2465
2466v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2467 ApiTestFuzzer::Fuzz();
2468 CHECK_EQ(1, args.Length());
2469 return v8::ThrowException(args[0]);
2470}
2471
2472
2473THREADED_TEST(ThrowValues) {
2474 v8::HandleScope scope;
2475 Local<ObjectTemplate> templ = ObjectTemplate::New();
2476 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2477 LocalContext context(0, templ);
2478 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2479 "function Run(obj) {"
2480 " try {"
2481 " Throw(obj);"
2482 " } catch (e) {"
2483 " return e;"
2484 " }"
2485 " return 'no exception';"
2486 "}"
2487 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2488 CHECK_EQ(5, result->Length());
2489 CHECK(result->Get(v8::Integer::New(0))->IsString());
2490 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2491 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2492 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2493 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2494 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2495 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2496}
2497
2498
2499THREADED_TEST(CatchZero) {
2500 v8::HandleScope scope;
2501 LocalContext context;
2502 v8::TryCatch try_catch;
2503 CHECK(!try_catch.HasCaught());
2504 Script::Compile(v8_str("throw 10"))->Run();
2505 CHECK(try_catch.HasCaught());
2506 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2507 try_catch.Reset();
2508 CHECK(!try_catch.HasCaught());
2509 Script::Compile(v8_str("throw 0"))->Run();
2510 CHECK(try_catch.HasCaught());
2511 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2512}
2513
2514
2515THREADED_TEST(CatchExceptionFromWith) {
2516 v8::HandleScope scope;
2517 LocalContext context;
2518 v8::TryCatch try_catch;
2519 CHECK(!try_catch.HasCaught());
2520 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2521 CHECK(try_catch.HasCaught());
2522}
2523
2524
2525THREADED_TEST(Equality) {
2526 v8::HandleScope scope;
2527 LocalContext context;
2528 // Check that equality works at all before relying on CHECK_EQ
2529 CHECK(v8_str("a")->Equals(v8_str("a")));
2530 CHECK(!v8_str("a")->Equals(v8_str("b")));
2531
2532 CHECK_EQ(v8_str("a"), v8_str("a"));
2533 CHECK_NE(v8_str("a"), v8_str("b"));
2534 CHECK_EQ(v8_num(1), v8_num(1));
2535 CHECK_EQ(v8_num(1.00), v8_num(1));
2536 CHECK_NE(v8_num(1), v8_num(2));
2537
2538 // Assume String is not symbol.
2539 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2540 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2541 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2542 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2543 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2544 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2545 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2546 CHECK(!not_a_number->StrictEquals(not_a_number));
2547 CHECK(v8::False()->StrictEquals(v8::False()));
2548 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2549
2550 v8::Handle<v8::Object> obj = v8::Object::New();
2551 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2552 CHECK(alias->StrictEquals(obj));
2553 alias.Dispose();
2554}
2555
2556
2557THREADED_TEST(MultiRun) {
2558 v8::HandleScope scope;
2559 LocalContext context;
2560 Local<Script> script = Script::Compile(v8_str("x"));
2561 for (int i = 0; i < 10; i++)
2562 script->Run();
2563}
2564
2565
2566static v8::Handle<Value> GetXValue(Local<String> name,
2567 const AccessorInfo& info) {
2568 ApiTestFuzzer::Fuzz();
2569 CHECK_EQ(info.Data(), v8_str("donut"));
2570 CHECK_EQ(name, v8_str("x"));
2571 return name;
2572}
2573
2574
2575THREADED_TEST(SimplePropertyRead) {
2576 v8::HandleScope scope;
2577 Local<ObjectTemplate> templ = ObjectTemplate::New();
2578 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2579 LocalContext context;
2580 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2581 Local<Script> script = Script::Compile(v8_str("obj.x"));
2582 for (int i = 0; i < 10; i++) {
2583 Local<Value> result = script->Run();
2584 CHECK_EQ(result, v8_str("x"));
2585 }
2586}
2587
Andrei Popescu31002712010-02-23 13:46:05 +00002588THREADED_TEST(DefinePropertyOnAPIAccessor) {
2589 v8::HandleScope scope;
2590 Local<ObjectTemplate> templ = ObjectTemplate::New();
2591 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2592 LocalContext context;
2593 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2594
2595 // Uses getOwnPropertyDescriptor to check the configurable status
2596 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002597 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002598 "obj, 'x');"
2599 "prop.configurable;"));
2600 Local<Value> result = script_desc->Run();
2601 CHECK_EQ(result->BooleanValue(), true);
2602
2603 // Redefine get - but still configurable
2604 Local<Script> script_define
2605 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2606 " configurable: true };"
2607 "Object.defineProperty(obj, 'x', desc);"
2608 "obj.x"));
2609 result = script_define->Run();
2610 CHECK_EQ(result, v8_num(42));
2611
2612 // Check that the accessor is still configurable
2613 result = script_desc->Run();
2614 CHECK_EQ(result->BooleanValue(), true);
2615
2616 // Redefine to a non-configurable
2617 script_define
2618 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2619 " configurable: false };"
2620 "Object.defineProperty(obj, 'x', desc);"
2621 "obj.x"));
2622 result = script_define->Run();
2623 CHECK_EQ(result, v8_num(43));
2624 result = script_desc->Run();
2625 CHECK_EQ(result->BooleanValue(), false);
2626
2627 // Make sure that it is not possible to redefine again
2628 v8::TryCatch try_catch;
2629 result = script_define->Run();
2630 CHECK(try_catch.HasCaught());
2631 String::AsciiValue exception_value(try_catch.Exception());
2632 CHECK_EQ(*exception_value,
2633 "TypeError: Cannot redefine property: defineProperty");
2634}
2635
2636THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2637 v8::HandleScope scope;
2638 Local<ObjectTemplate> templ = ObjectTemplate::New();
2639 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2640 LocalContext context;
2641 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2642
2643 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2644 "Object.getOwnPropertyDescriptor( "
2645 "obj, 'x');"
2646 "prop.configurable;"));
2647 Local<Value> result = script_desc->Run();
2648 CHECK_EQ(result->BooleanValue(), true);
2649
2650 Local<Script> script_define =
2651 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2652 " configurable: true };"
2653 "Object.defineProperty(obj, 'x', desc);"
2654 "obj.x"));
2655 result = script_define->Run();
2656 CHECK_EQ(result, v8_num(42));
2657
2658
2659 result = script_desc->Run();
2660 CHECK_EQ(result->BooleanValue(), true);
2661
2662
2663 script_define =
2664 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2665 " configurable: false };"
2666 "Object.defineProperty(obj, 'x', desc);"
2667 "obj.x"));
2668 result = script_define->Run();
2669 CHECK_EQ(result, v8_num(43));
2670 result = script_desc->Run();
2671
2672 CHECK_EQ(result->BooleanValue(), false);
2673
2674 v8::TryCatch try_catch;
2675 result = script_define->Run();
2676 CHECK(try_catch.HasCaught());
2677 String::AsciiValue exception_value(try_catch.Exception());
2678 CHECK_EQ(*exception_value,
2679 "TypeError: Cannot redefine property: defineProperty");
2680}
2681
2682
Leon Clarkef7060e22010-06-03 12:02:55 +01002683static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2684 char const* name) {
2685 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2686}
Andrei Popescu31002712010-02-23 13:46:05 +00002687
2688
Leon Clarkef7060e22010-06-03 12:02:55 +01002689THREADED_TEST(DefineAPIAccessorOnObject) {
2690 v8::HandleScope scope;
2691 Local<ObjectTemplate> templ = ObjectTemplate::New();
2692 LocalContext context;
2693
2694 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2695 CompileRun("var obj2 = {};");
2696
2697 CHECK(CompileRun("obj1.x")->IsUndefined());
2698 CHECK(CompileRun("obj2.x")->IsUndefined());
2699
2700 CHECK(GetGlobalProperty(&context, "obj1")->
2701 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2702
2703 ExpectString("obj1.x", "x");
2704 CHECK(CompileRun("obj2.x")->IsUndefined());
2705
2706 CHECK(GetGlobalProperty(&context, "obj2")->
2707 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2708
2709 ExpectString("obj1.x", "x");
2710 ExpectString("obj2.x", "x");
2711
2712 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2713 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2714
2715 CompileRun("Object.defineProperty(obj1, 'x',"
2716 "{ get: function() { return 'y'; }, configurable: true })");
2717
2718 ExpectString("obj1.x", "y");
2719 ExpectString("obj2.x", "x");
2720
2721 CompileRun("Object.defineProperty(obj2, 'x',"
2722 "{ get: function() { return 'y'; }, configurable: true })");
2723
2724 ExpectString("obj1.x", "y");
2725 ExpectString("obj2.x", "y");
2726
2727 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2728 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2729
2730 CHECK(GetGlobalProperty(&context, "obj1")->
2731 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2732 CHECK(GetGlobalProperty(&context, "obj2")->
2733 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2734
2735 ExpectString("obj1.x", "x");
2736 ExpectString("obj2.x", "x");
2737
2738 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2739 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2740
2741 // Define getters/setters, but now make them not configurable.
2742 CompileRun("Object.defineProperty(obj1, 'x',"
2743 "{ get: function() { return 'z'; }, configurable: false })");
2744 CompileRun("Object.defineProperty(obj2, 'x',"
2745 "{ get: function() { return 'z'; }, configurable: false })");
2746
2747 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2748 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2749
2750 ExpectString("obj1.x", "z");
2751 ExpectString("obj2.x", "z");
2752
2753 CHECK(!GetGlobalProperty(&context, "obj1")->
2754 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2755 CHECK(!GetGlobalProperty(&context, "obj2")->
2756 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2757
2758 ExpectString("obj1.x", "z");
2759 ExpectString("obj2.x", "z");
2760}
2761
2762
2763THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2764 v8::HandleScope scope;
2765 Local<ObjectTemplate> templ = ObjectTemplate::New();
2766 LocalContext context;
2767
2768 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2769 CompileRun("var obj2 = {};");
2770
2771 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2772 v8_str("x"),
2773 GetXValue, NULL,
2774 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2775 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2776 v8_str("x"),
2777 GetXValue, NULL,
2778 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2779
2780 ExpectString("obj1.x", "x");
2781 ExpectString("obj2.x", "x");
2782
2783 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2784 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2785
2786 CHECK(!GetGlobalProperty(&context, "obj1")->
2787 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2788 CHECK(!GetGlobalProperty(&context, "obj2")->
2789 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2790
2791 {
2792 v8::TryCatch try_catch;
2793 CompileRun("Object.defineProperty(obj1, 'x',"
2794 "{get: function() { return 'func'; }})");
2795 CHECK(try_catch.HasCaught());
2796 String::AsciiValue exception_value(try_catch.Exception());
2797 CHECK_EQ(*exception_value,
2798 "TypeError: Cannot redefine property: defineProperty");
2799 }
2800 {
2801 v8::TryCatch try_catch;
2802 CompileRun("Object.defineProperty(obj2, 'x',"
2803 "{get: function() { return 'func'; }})");
2804 CHECK(try_catch.HasCaught());
2805 String::AsciiValue exception_value(try_catch.Exception());
2806 CHECK_EQ(*exception_value,
2807 "TypeError: Cannot redefine property: defineProperty");
2808 }
2809}
2810
2811
2812static v8::Handle<Value> Get239Value(Local<String> name,
2813 const AccessorInfo& info) {
2814 ApiTestFuzzer::Fuzz();
2815 CHECK_EQ(info.Data(), v8_str("donut"));
2816 CHECK_EQ(name, v8_str("239"));
2817 return name;
2818}
2819
2820
2821THREADED_TEST(ElementAPIAccessor) {
2822 v8::HandleScope scope;
2823 Local<ObjectTemplate> templ = ObjectTemplate::New();
2824 LocalContext context;
2825
2826 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2827 CompileRun("var obj2 = {};");
2828
2829 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2830 v8_str("239"),
2831 Get239Value, NULL,
2832 v8_str("donut")));
2833 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2834 v8_str("239"),
2835 Get239Value, NULL,
2836 v8_str("donut")));
2837
2838 ExpectString("obj1[239]", "239");
2839 ExpectString("obj2[239]", "239");
2840 ExpectString("obj1['239']", "239");
2841 ExpectString("obj2['239']", "239");
2842}
2843
Steve Blocka7e24c12009-10-30 11:49:00 +00002844
2845v8::Persistent<Value> xValue;
2846
2847
2848static void SetXValue(Local<String> name,
2849 Local<Value> value,
2850 const AccessorInfo& info) {
2851 CHECK_EQ(value, v8_num(4));
2852 CHECK_EQ(info.Data(), v8_str("donut"));
2853 CHECK_EQ(name, v8_str("x"));
2854 CHECK(xValue.IsEmpty());
2855 xValue = v8::Persistent<Value>::New(value);
2856}
2857
2858
2859THREADED_TEST(SimplePropertyWrite) {
2860 v8::HandleScope scope;
2861 Local<ObjectTemplate> templ = ObjectTemplate::New();
2862 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2863 LocalContext context;
2864 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2865 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2866 for (int i = 0; i < 10; i++) {
2867 CHECK(xValue.IsEmpty());
2868 script->Run();
2869 CHECK_EQ(v8_num(4), xValue);
2870 xValue.Dispose();
2871 xValue = v8::Persistent<Value>();
2872 }
2873}
2874
2875
2876static v8::Handle<Value> XPropertyGetter(Local<String> property,
2877 const AccessorInfo& info) {
2878 ApiTestFuzzer::Fuzz();
2879 CHECK(info.Data()->IsUndefined());
2880 return property;
2881}
2882
2883
2884THREADED_TEST(NamedInterceptorPropertyRead) {
2885 v8::HandleScope scope;
2886 Local<ObjectTemplate> templ = ObjectTemplate::New();
2887 templ->SetNamedPropertyHandler(XPropertyGetter);
2888 LocalContext context;
2889 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2890 Local<Script> script = Script::Compile(v8_str("obj.x"));
2891 for (int i = 0; i < 10; i++) {
2892 Local<Value> result = script->Run();
2893 CHECK_EQ(result, v8_str("x"));
2894 }
2895}
2896
2897
Steve Block6ded16b2010-05-10 14:33:55 +01002898THREADED_TEST(NamedInterceptorDictionaryIC) {
2899 v8::HandleScope scope;
2900 Local<ObjectTemplate> templ = ObjectTemplate::New();
2901 templ->SetNamedPropertyHandler(XPropertyGetter);
2902 LocalContext context;
2903 // Create an object with a named interceptor.
2904 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2905 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2906 for (int i = 0; i < 10; i++) {
2907 Local<Value> result = script->Run();
2908 CHECK_EQ(result, v8_str("x"));
2909 }
2910 // Create a slow case object and a function accessing a property in
2911 // that slow case object (with dictionary probing in generated
2912 // code). Then force object with a named interceptor into slow-case,
2913 // pass it to the function, and check that the interceptor is called
2914 // instead of accessing the local property.
2915 Local<Value> result =
2916 CompileRun("function get_x(o) { return o.x; };"
2917 "var obj = { x : 42, y : 0 };"
2918 "delete obj.y;"
2919 "for (var i = 0; i < 10; i++) get_x(obj);"
2920 "interceptor_obj.x = 42;"
2921 "interceptor_obj.y = 10;"
2922 "delete interceptor_obj.y;"
2923 "get_x(interceptor_obj)");
2924 CHECK_EQ(result, v8_str("x"));
2925}
2926
2927
Andrei Popescu402d9372010-02-26 13:31:12 +00002928static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2929 const AccessorInfo& info) {
2930 // Set x on the prototype object and do not handle the get request.
2931 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002932 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002933 return v8::Handle<Value>();
2934}
2935
2936
2937// This is a regression test for http://crbug.com/20104. Map
2938// transitions should not interfere with post interceptor lookup.
2939THREADED_TEST(NamedInterceptorMapTransitionRead) {
2940 v8::HandleScope scope;
2941 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
2942 Local<v8::ObjectTemplate> instance_template
2943 = function_template->InstanceTemplate();
2944 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
2945 LocalContext context;
2946 context->Global()->Set(v8_str("F"), function_template->GetFunction());
2947 // Create an instance of F and introduce a map transition for x.
2948 CompileRun("var o = new F(); o.x = 23;");
2949 // Create an instance of F and invoke the getter. The result should be 23.
2950 Local<Value> result = CompileRun("o = new F(); o.x");
2951 CHECK_EQ(result->Int32Value(), 23);
2952}
2953
2954
Steve Blocka7e24c12009-10-30 11:49:00 +00002955static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2956 const AccessorInfo& info) {
2957 ApiTestFuzzer::Fuzz();
2958 if (index == 37) {
2959 return v8::Handle<Value>(v8_num(625));
2960 }
2961 return v8::Handle<Value>();
2962}
2963
2964
2965static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2966 Local<Value> value,
2967 const AccessorInfo& info) {
2968 ApiTestFuzzer::Fuzz();
2969 if (index == 39) {
2970 return value;
2971 }
2972 return v8::Handle<Value>();
2973}
2974
2975
2976THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2977 v8::HandleScope scope;
2978 Local<ObjectTemplate> templ = ObjectTemplate::New();
2979 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2980 IndexedPropertySetter);
2981 LocalContext context;
2982 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2983 Local<Script> getter_script = Script::Compile(v8_str(
2984 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2985 Local<Script> setter_script = Script::Compile(v8_str(
2986 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2987 "obj[17] = 23;"
2988 "obj.foo;"));
2989 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
2990 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
2991 "obj[39] = 47;"
2992 "obj.foo;")); // This setter should not run, due to the interceptor.
2993 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
2994 "obj[37];"));
2995 Local<Value> result = getter_script->Run();
2996 CHECK_EQ(v8_num(5), result);
2997 result = setter_script->Run();
2998 CHECK_EQ(v8_num(23), result);
2999 result = interceptor_setter_script->Run();
3000 CHECK_EQ(v8_num(23), result);
3001 result = interceptor_getter_script->Run();
3002 CHECK_EQ(v8_num(625), result);
3003}
3004
3005
Leon Clarked91b9f72010-01-27 17:25:45 +00003006static v8::Handle<Value> IdentityIndexedPropertyGetter(
3007 uint32_t index,
3008 const AccessorInfo& info) {
3009 return v8::Integer::New(index);
3010}
3011
3012
3013THREADED_TEST(IndexedInterceptorWithNoSetter) {
3014 v8::HandleScope scope;
3015 Local<ObjectTemplate> templ = ObjectTemplate::New();
3016 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3017
3018 LocalContext context;
3019 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3020
3021 const char* code =
3022 "try {"
3023 " obj[0] = 239;"
3024 " for (var i = 0; i < 100; i++) {"
3025 " var v = obj[0];"
3026 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3027 " }"
3028 " 'PASSED'"
3029 "} catch(e) {"
3030 " e"
3031 "}";
3032 ExpectString(code, "PASSED");
3033}
3034
3035
Andrei Popescu402d9372010-02-26 13:31:12 +00003036THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3037 v8::HandleScope scope;
3038 Local<ObjectTemplate> templ = ObjectTemplate::New();
3039 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3040
3041 LocalContext context;
3042 Local<v8::Object> obj = templ->NewInstance();
3043 obj->TurnOnAccessCheck();
3044 context->Global()->Set(v8_str("obj"), obj);
3045
3046 const char* code =
3047 "try {"
3048 " for (var i = 0; i < 100; i++) {"
3049 " var v = obj[0];"
3050 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3051 " }"
3052 " 'PASSED'"
3053 "} catch(e) {"
3054 " e"
3055 "}";
3056 ExpectString(code, "PASSED");
3057}
3058
3059
3060THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3061 i::FLAG_allow_natives_syntax = true;
3062 v8::HandleScope scope;
3063 Local<ObjectTemplate> templ = ObjectTemplate::New();
3064 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3065
3066 LocalContext context;
3067 Local<v8::Object> obj = templ->NewInstance();
3068 context->Global()->Set(v8_str("obj"), obj);
3069
3070 const char* code =
3071 "try {"
3072 " for (var i = 0; i < 100; i++) {"
3073 " var expected = i;"
3074 " if (i == 5) {"
3075 " %EnableAccessChecks(obj);"
3076 " expected = undefined;"
3077 " }"
3078 " var v = obj[i];"
3079 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3080 " if (i == 5) %DisableAccessChecks(obj);"
3081 " }"
3082 " 'PASSED'"
3083 "} catch(e) {"
3084 " e"
3085 "}";
3086 ExpectString(code, "PASSED");
3087}
3088
3089
3090THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3091 v8::HandleScope scope;
3092 Local<ObjectTemplate> templ = ObjectTemplate::New();
3093 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3094
3095 LocalContext context;
3096 Local<v8::Object> obj = templ->NewInstance();
3097 context->Global()->Set(v8_str("obj"), obj);
3098
3099 const char* code =
3100 "try {"
3101 " for (var i = 0; i < 100; i++) {"
3102 " var v = obj[i];"
3103 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3104 " }"
3105 " 'PASSED'"
3106 "} catch(e) {"
3107 " e"
3108 "}";
3109 ExpectString(code, "PASSED");
3110}
3111
3112
3113THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3114 v8::HandleScope scope;
3115 Local<ObjectTemplate> templ = ObjectTemplate::New();
3116 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3117
3118 LocalContext context;
3119 Local<v8::Object> obj = templ->NewInstance();
3120 context->Global()->Set(v8_str("obj"), obj);
3121
3122 const char* code =
3123 "try {"
3124 " for (var i = 0; i < 100; i++) {"
3125 " var expected = i;"
3126 " if (i == 50) {"
3127 " i = 'foobar';"
3128 " expected = undefined;"
3129 " }"
3130 " var v = obj[i];"
3131 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3132 " }"
3133 " 'PASSED'"
3134 "} catch(e) {"
3135 " e"
3136 "}";
3137 ExpectString(code, "PASSED");
3138}
3139
3140
3141THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3142 v8::HandleScope scope;
3143 Local<ObjectTemplate> templ = ObjectTemplate::New();
3144 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3145
3146 LocalContext context;
3147 Local<v8::Object> obj = templ->NewInstance();
3148 context->Global()->Set(v8_str("obj"), obj);
3149
3150 const char* code =
3151 "var original = obj;"
3152 "try {"
3153 " for (var i = 0; i < 100; i++) {"
3154 " var expected = i;"
3155 " if (i == 50) {"
3156 " obj = {50: 'foobar'};"
3157 " expected = 'foobar';"
3158 " }"
3159 " var v = obj[i];"
3160 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3161 " if (i == 50) obj = original;"
3162 " }"
3163 " 'PASSED'"
3164 "} catch(e) {"
3165 " e"
3166 "}";
3167 ExpectString(code, "PASSED");
3168}
3169
3170
3171THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3172 v8::HandleScope scope;
3173 Local<ObjectTemplate> templ = ObjectTemplate::New();
3174 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3175
3176 LocalContext context;
3177 Local<v8::Object> obj = templ->NewInstance();
3178 context->Global()->Set(v8_str("obj"), obj);
3179
3180 const char* code =
3181 "var original = obj;"
3182 "try {"
3183 " for (var i = 0; i < 100; i++) {"
3184 " var expected = i;"
3185 " if (i == 5) {"
3186 " obj = 239;"
3187 " expected = undefined;"
3188 " }"
3189 " var v = obj[i];"
3190 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3191 " if (i == 5) obj = original;"
3192 " }"
3193 " 'PASSED'"
3194 "} catch(e) {"
3195 " e"
3196 "}";
3197 ExpectString(code, "PASSED");
3198}
3199
3200
3201THREADED_TEST(IndexedInterceptorOnProto) {
3202 v8::HandleScope scope;
3203 Local<ObjectTemplate> templ = ObjectTemplate::New();
3204 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3205
3206 LocalContext context;
3207 Local<v8::Object> obj = templ->NewInstance();
3208 context->Global()->Set(v8_str("obj"), obj);
3209
3210 const char* code =
3211 "var o = {__proto__: obj};"
3212 "try {"
3213 " for (var i = 0; i < 100; i++) {"
3214 " var v = o[i];"
3215 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3216 " }"
3217 " 'PASSED'"
3218 "} catch(e) {"
3219 " e"
3220 "}";
3221 ExpectString(code, "PASSED");
3222}
3223
3224
Steve Blocka7e24c12009-10-30 11:49:00 +00003225THREADED_TEST(MultiContexts) {
3226 v8::HandleScope scope;
3227 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3228 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3229
3230 Local<String> password = v8_str("Password");
3231
3232 // Create an environment
3233 LocalContext context0(0, templ);
3234 context0->SetSecurityToken(password);
3235 v8::Handle<v8::Object> global0 = context0->Global();
3236 global0->Set(v8_str("custom"), v8_num(1234));
3237 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3238
3239 // Create an independent environment
3240 LocalContext context1(0, templ);
3241 context1->SetSecurityToken(password);
3242 v8::Handle<v8::Object> global1 = context1->Global();
3243 global1->Set(v8_str("custom"), v8_num(1234));
3244 CHECK_NE(global0, global1);
3245 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3246 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3247
3248 // Now create a new context with the old global
3249 LocalContext context2(0, templ, global1);
3250 context2->SetSecurityToken(password);
3251 v8::Handle<v8::Object> global2 = context2->Global();
3252 CHECK_EQ(global1, global2);
3253 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3254 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3255}
3256
3257
3258THREADED_TEST(FunctionPrototypeAcrossContexts) {
3259 // Make sure that functions created by cloning boilerplates cannot
3260 // communicate through their __proto__ field.
3261
3262 v8::HandleScope scope;
3263
3264 LocalContext env0;
3265 v8::Handle<v8::Object> global0 =
3266 env0->Global();
3267 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003268 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003269 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003270 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003271 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003272 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003273 proto0->Set(v8_str("custom"), v8_num(1234));
3274
3275 LocalContext env1;
3276 v8::Handle<v8::Object> global1 =
3277 env1->Global();
3278 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003279 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003280 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003281 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003282 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003283 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003284 CHECK(!proto1->Has(v8_str("custom")));
3285}
3286
3287
3288THREADED_TEST(Regress892105) {
3289 // Make sure that object and array literals created by cloning
3290 // boilerplates cannot communicate through their __proto__
3291 // field. This is rather difficult to check, but we try to add stuff
3292 // to Object.prototype and Array.prototype and create a new
3293 // environment. This should succeed.
3294
3295 v8::HandleScope scope;
3296
3297 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3298 "Array.prototype.arr = 4567;"
3299 "8901");
3300
3301 LocalContext env0;
3302 Local<Script> script0 = Script::Compile(source);
3303 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3304
3305 LocalContext env1;
3306 Local<Script> script1 = Script::Compile(source);
3307 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3308}
3309
3310
Steve Blocka7e24c12009-10-30 11:49:00 +00003311THREADED_TEST(UndetectableObject) {
3312 v8::HandleScope scope;
3313 LocalContext env;
3314
3315 Local<v8::FunctionTemplate> desc =
3316 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3317 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3318
3319 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3320 env->Global()->Set(v8_str("undetectable"), obj);
3321
3322 ExpectString("undetectable.toString()", "[object Object]");
3323 ExpectString("typeof undetectable", "undefined");
3324 ExpectString("typeof(undetectable)", "undefined");
3325 ExpectBoolean("typeof undetectable == 'undefined'", true);
3326 ExpectBoolean("typeof undetectable == 'object'", false);
3327 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3328 ExpectBoolean("!undetectable", true);
3329
3330 ExpectObject("true&&undetectable", obj);
3331 ExpectBoolean("false&&undetectable", false);
3332 ExpectBoolean("true||undetectable", true);
3333 ExpectObject("false||undetectable", obj);
3334
3335 ExpectObject("undetectable&&true", obj);
3336 ExpectObject("undetectable&&false", obj);
3337 ExpectBoolean("undetectable||true", true);
3338 ExpectBoolean("undetectable||false", false);
3339
3340 ExpectBoolean("undetectable==null", true);
3341 ExpectBoolean("null==undetectable", true);
3342 ExpectBoolean("undetectable==undefined", true);
3343 ExpectBoolean("undefined==undetectable", true);
3344 ExpectBoolean("undetectable==undetectable", true);
3345
3346
3347 ExpectBoolean("undetectable===null", false);
3348 ExpectBoolean("null===undetectable", false);
3349 ExpectBoolean("undetectable===undefined", false);
3350 ExpectBoolean("undefined===undetectable", false);
3351 ExpectBoolean("undetectable===undetectable", true);
3352}
3353
3354
Steve Block8defd9f2010-07-08 12:39:36 +01003355
3356THREADED_TEST(ExtensibleOnUndetectable) {
3357 v8::HandleScope scope;
3358 LocalContext env;
3359
3360 Local<v8::FunctionTemplate> desc =
3361 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3362 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3363
3364 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3365 env->Global()->Set(v8_str("undetectable"), obj);
3366
3367 Local<String> source = v8_str("undetectable.x = 42;"
3368 "undetectable.x");
3369
3370 Local<Script> script = Script::Compile(source);
3371
3372 CHECK_EQ(v8::Integer::New(42), script->Run());
3373
3374 ExpectBoolean("Object.isExtensible(undetectable)", true);
3375
3376 source = v8_str("Object.preventExtensions(undetectable);");
3377 script = Script::Compile(source);
3378 script->Run();
3379 ExpectBoolean("Object.isExtensible(undetectable)", false);
3380
3381 source = v8_str("undetectable.y = 2000;");
3382 script = Script::Compile(source);
3383 v8::TryCatch try_catch;
3384 Local<Value> result = script->Run();
3385 CHECK(result.IsEmpty());
3386 CHECK(try_catch.HasCaught());
3387}
3388
3389
3390
Steve Blocka7e24c12009-10-30 11:49:00 +00003391THREADED_TEST(UndetectableString) {
3392 v8::HandleScope scope;
3393 LocalContext env;
3394
3395 Local<String> obj = String::NewUndetectable("foo");
3396 env->Global()->Set(v8_str("undetectable"), obj);
3397
3398 ExpectString("undetectable", "foo");
3399 ExpectString("typeof undetectable", "undefined");
3400 ExpectString("typeof(undetectable)", "undefined");
3401 ExpectBoolean("typeof undetectable == 'undefined'", true);
3402 ExpectBoolean("typeof undetectable == 'string'", false);
3403 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3404 ExpectBoolean("!undetectable", true);
3405
3406 ExpectObject("true&&undetectable", obj);
3407 ExpectBoolean("false&&undetectable", false);
3408 ExpectBoolean("true||undetectable", true);
3409 ExpectObject("false||undetectable", obj);
3410
3411 ExpectObject("undetectable&&true", obj);
3412 ExpectObject("undetectable&&false", obj);
3413 ExpectBoolean("undetectable||true", true);
3414 ExpectBoolean("undetectable||false", false);
3415
3416 ExpectBoolean("undetectable==null", true);
3417 ExpectBoolean("null==undetectable", true);
3418 ExpectBoolean("undetectable==undefined", true);
3419 ExpectBoolean("undefined==undetectable", true);
3420 ExpectBoolean("undetectable==undetectable", true);
3421
3422
3423 ExpectBoolean("undetectable===null", false);
3424 ExpectBoolean("null===undetectable", false);
3425 ExpectBoolean("undetectable===undefined", false);
3426 ExpectBoolean("undefined===undetectable", false);
3427 ExpectBoolean("undetectable===undetectable", true);
3428}
3429
3430
3431template <typename T> static void USE(T) { }
3432
3433
3434// This test is not intended to be run, just type checked.
3435static void PersistentHandles() {
3436 USE(PersistentHandles);
3437 Local<String> str = v8_str("foo");
3438 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3439 USE(p_str);
3440 Local<Script> scr = Script::Compile(v8_str(""));
3441 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3442 USE(p_scr);
3443 Local<ObjectTemplate> templ = ObjectTemplate::New();
3444 v8::Persistent<ObjectTemplate> p_templ =
3445 v8::Persistent<ObjectTemplate>::New(templ);
3446 USE(p_templ);
3447}
3448
3449
3450static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3451 ApiTestFuzzer::Fuzz();
3452 return v8::Undefined();
3453}
3454
3455
3456THREADED_TEST(GlobalObjectTemplate) {
3457 v8::HandleScope handle_scope;
3458 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3459 global_template->Set(v8_str("JSNI_Log"),
3460 v8::FunctionTemplate::New(HandleLogDelegator));
3461 v8::Persistent<Context> context = Context::New(0, global_template);
3462 Context::Scope context_scope(context);
3463 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3464 context.Dispose();
3465}
3466
3467
3468static const char* kSimpleExtensionSource =
3469 "function Foo() {"
3470 " return 4;"
3471 "}";
3472
3473
3474THREADED_TEST(SimpleExtensions) {
3475 v8::HandleScope handle_scope;
3476 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3477 const char* extension_names[] = { "simpletest" };
3478 v8::ExtensionConfiguration extensions(1, extension_names);
3479 v8::Handle<Context> context = Context::New(&extensions);
3480 Context::Scope lock(context);
3481 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3482 CHECK_EQ(result, v8::Integer::New(4));
3483}
3484
3485
3486static const char* kEvalExtensionSource1 =
3487 "function UseEval1() {"
3488 " var x = 42;"
3489 " return eval('x');"
3490 "}";
3491
3492
3493static const char* kEvalExtensionSource2 =
3494 "(function() {"
3495 " var x = 42;"
3496 " function e() {"
3497 " return eval('x');"
3498 " }"
3499 " this.UseEval2 = e;"
3500 "})()";
3501
3502
3503THREADED_TEST(UseEvalFromExtension) {
3504 v8::HandleScope handle_scope;
3505 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3506 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3507 const char* extension_names[] = { "evaltest1", "evaltest2" };
3508 v8::ExtensionConfiguration extensions(2, extension_names);
3509 v8::Handle<Context> context = Context::New(&extensions);
3510 Context::Scope lock(context);
3511 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3512 CHECK_EQ(result, v8::Integer::New(42));
3513 result = Script::Compile(v8_str("UseEval2()"))->Run();
3514 CHECK_EQ(result, v8::Integer::New(42));
3515}
3516
3517
3518static const char* kWithExtensionSource1 =
3519 "function UseWith1() {"
3520 " var x = 42;"
3521 " with({x:87}) { return x; }"
3522 "}";
3523
3524
3525
3526static const char* kWithExtensionSource2 =
3527 "(function() {"
3528 " var x = 42;"
3529 " function e() {"
3530 " with ({x:87}) { return x; }"
3531 " }"
3532 " this.UseWith2 = e;"
3533 "})()";
3534
3535
3536THREADED_TEST(UseWithFromExtension) {
3537 v8::HandleScope handle_scope;
3538 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3539 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3540 const char* extension_names[] = { "withtest1", "withtest2" };
3541 v8::ExtensionConfiguration extensions(2, extension_names);
3542 v8::Handle<Context> context = Context::New(&extensions);
3543 Context::Scope lock(context);
3544 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3545 CHECK_EQ(result, v8::Integer::New(87));
3546 result = Script::Compile(v8_str("UseWith2()"))->Run();
3547 CHECK_EQ(result, v8::Integer::New(87));
3548}
3549
3550
3551THREADED_TEST(AutoExtensions) {
3552 v8::HandleScope handle_scope;
3553 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3554 extension->set_auto_enable(true);
3555 v8::RegisterExtension(extension);
3556 v8::Handle<Context> context = Context::New();
3557 Context::Scope lock(context);
3558 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3559 CHECK_EQ(result, v8::Integer::New(4));
3560}
3561
3562
Steve Blockd0582a62009-12-15 09:54:21 +00003563static const char* kSyntaxErrorInExtensionSource =
3564 "[";
3565
3566
3567// Test that a syntax error in an extension does not cause a fatal
3568// error but results in an empty context.
3569THREADED_TEST(SyntaxErrorExtensions) {
3570 v8::HandleScope handle_scope;
3571 v8::RegisterExtension(new Extension("syntaxerror",
3572 kSyntaxErrorInExtensionSource));
3573 const char* extension_names[] = { "syntaxerror" };
3574 v8::ExtensionConfiguration extensions(1, extension_names);
3575 v8::Handle<Context> context = Context::New(&extensions);
3576 CHECK(context.IsEmpty());
3577}
3578
3579
3580static const char* kExceptionInExtensionSource =
3581 "throw 42";
3582
3583
3584// Test that an exception when installing an extension does not cause
3585// a fatal error but results in an empty context.
3586THREADED_TEST(ExceptionExtensions) {
3587 v8::HandleScope handle_scope;
3588 v8::RegisterExtension(new Extension("exception",
3589 kExceptionInExtensionSource));
3590 const char* extension_names[] = { "exception" };
3591 v8::ExtensionConfiguration extensions(1, extension_names);
3592 v8::Handle<Context> context = Context::New(&extensions);
3593 CHECK(context.IsEmpty());
3594}
3595
3596
Steve Blocka7e24c12009-10-30 11:49:00 +00003597static void CheckDependencies(const char* name, const char* expected) {
3598 v8::HandleScope handle_scope;
3599 v8::ExtensionConfiguration config(1, &name);
3600 LocalContext context(&config);
3601 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3602}
3603
3604
3605/*
3606 * Configuration:
3607 *
3608 * /-- B <--\
3609 * A <- -- D <-- E
3610 * \-- C <--/
3611 */
3612THREADED_TEST(ExtensionDependency) {
3613 static const char* kEDeps[] = { "D" };
3614 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3615 static const char* kDDeps[] = { "B", "C" };
3616 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3617 static const char* kBCDeps[] = { "A" };
3618 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3619 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3620 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3621 CheckDependencies("A", "undefinedA");
3622 CheckDependencies("B", "undefinedAB");
3623 CheckDependencies("C", "undefinedAC");
3624 CheckDependencies("D", "undefinedABCD");
3625 CheckDependencies("E", "undefinedABCDE");
3626 v8::HandleScope handle_scope;
3627 static const char* exts[2] = { "C", "E" };
3628 v8::ExtensionConfiguration config(2, exts);
3629 LocalContext context(&config);
3630 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3631}
3632
3633
3634static const char* kExtensionTestScript =
3635 "native function A();"
3636 "native function B();"
3637 "native function C();"
3638 "function Foo(i) {"
3639 " if (i == 0) return A();"
3640 " if (i == 1) return B();"
3641 " if (i == 2) return C();"
3642 "}";
3643
3644
3645static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3646 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003647 if (args.IsConstructCall()) {
3648 args.This()->Set(v8_str("data"), args.Data());
3649 return v8::Null();
3650 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003651 return args.Data();
3652}
3653
3654
3655class FunctionExtension : public Extension {
3656 public:
3657 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3658 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3659 v8::Handle<String> name);
3660};
3661
3662
3663static int lookup_count = 0;
3664v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3665 v8::Handle<String> name) {
3666 lookup_count++;
3667 if (name->Equals(v8_str("A"))) {
3668 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3669 } else if (name->Equals(v8_str("B"))) {
3670 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3671 } else if (name->Equals(v8_str("C"))) {
3672 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3673 } else {
3674 return v8::Handle<v8::FunctionTemplate>();
3675 }
3676}
3677
3678
3679THREADED_TEST(FunctionLookup) {
3680 v8::RegisterExtension(new FunctionExtension());
3681 v8::HandleScope handle_scope;
3682 static const char* exts[1] = { "functiontest" };
3683 v8::ExtensionConfiguration config(1, exts);
3684 LocalContext context(&config);
3685 CHECK_EQ(3, lookup_count);
3686 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3687 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3688 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3689}
3690
3691
Leon Clarkee46be812010-01-19 14:06:41 +00003692THREADED_TEST(NativeFunctionConstructCall) {
3693 v8::RegisterExtension(new FunctionExtension());
3694 v8::HandleScope handle_scope;
3695 static const char* exts[1] = { "functiontest" };
3696 v8::ExtensionConfiguration config(1, exts);
3697 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003698 for (int i = 0; i < 10; i++) {
3699 // Run a few times to ensure that allocation of objects doesn't
3700 // change behavior of a constructor function.
3701 CHECK_EQ(v8::Integer::New(8),
3702 Script::Compile(v8_str("(new A()).data"))->Run());
3703 CHECK_EQ(v8::Integer::New(7),
3704 Script::Compile(v8_str("(new B()).data"))->Run());
3705 CHECK_EQ(v8::Integer::New(6),
3706 Script::Compile(v8_str("(new C()).data"))->Run());
3707 }
Leon Clarkee46be812010-01-19 14:06:41 +00003708}
3709
3710
Steve Blocka7e24c12009-10-30 11:49:00 +00003711static const char* last_location;
3712static const char* last_message;
3713void StoringErrorCallback(const char* location, const char* message) {
3714 if (last_location == NULL) {
3715 last_location = location;
3716 last_message = message;
3717 }
3718}
3719
3720
3721// ErrorReporting creates a circular extensions configuration and
3722// tests that the fatal error handler gets called. This renders V8
3723// unusable and therefore this test cannot be run in parallel.
3724TEST(ErrorReporting) {
3725 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3726 static const char* aDeps[] = { "B" };
3727 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3728 static const char* bDeps[] = { "A" };
3729 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3730 last_location = NULL;
3731 v8::ExtensionConfiguration config(1, bDeps);
3732 v8::Handle<Context> context = Context::New(&config);
3733 CHECK(context.IsEmpty());
3734 CHECK_NE(last_location, NULL);
3735}
3736
3737
3738static const char* js_code_causing_huge_string_flattening =
3739 "var str = 'X';"
3740 "for (var i = 0; i < 30; i++) {"
3741 " str = str + str;"
3742 "}"
3743 "str.match(/X/);";
3744
3745
3746void OOMCallback(const char* location, const char* message) {
3747 exit(0);
3748}
3749
3750
3751TEST(RegexpOutOfMemory) {
3752 // Execute a script that causes out of memory when flattening a string.
3753 v8::HandleScope scope;
3754 v8::V8::SetFatalErrorHandler(OOMCallback);
3755 LocalContext context;
3756 Local<Script> script =
3757 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3758 last_location = NULL;
3759 Local<Value> result = script->Run();
3760
3761 CHECK(false); // Should not return.
3762}
3763
3764
3765static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3766 v8::Handle<Value> data) {
3767 CHECK_EQ(v8::Undefined(), data);
3768 CHECK(message->GetScriptResourceName()->IsUndefined());
3769 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3770 message->GetLineNumber();
3771 message->GetSourceLine();
3772}
3773
3774
3775THREADED_TEST(ErrorWithMissingScriptInfo) {
3776 v8::HandleScope scope;
3777 LocalContext context;
3778 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3779 Script::Compile(v8_str("throw Error()"))->Run();
3780 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3781}
3782
3783
3784int global_index = 0;
3785
3786class Snorkel {
3787 public:
3788 Snorkel() { index_ = global_index++; }
3789 int index_;
3790};
3791
3792class Whammy {
3793 public:
3794 Whammy() {
3795 cursor_ = 0;
3796 }
3797 ~Whammy() {
3798 script_.Dispose();
3799 }
3800 v8::Handle<Script> getScript() {
3801 if (script_.IsEmpty())
3802 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3803 return Local<Script>(*script_);
3804 }
3805
3806 public:
3807 static const int kObjectCount = 256;
3808 int cursor_;
3809 v8::Persistent<v8::Object> objects_[kObjectCount];
3810 v8::Persistent<Script> script_;
3811};
3812
3813static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3814 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3815 delete snorkel;
3816 obj.ClearWeak();
3817}
3818
3819v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3820 const AccessorInfo& info) {
3821 Whammy* whammy =
3822 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3823
3824 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3825
3826 v8::Handle<v8::Object> obj = v8::Object::New();
3827 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3828 if (!prev.IsEmpty()) {
3829 prev->Set(v8_str("next"), obj);
3830 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3831 whammy->objects_[whammy->cursor_].Clear();
3832 }
3833 whammy->objects_[whammy->cursor_] = global;
3834 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3835 return whammy->getScript()->Run();
3836}
3837
3838THREADED_TEST(WeakReference) {
3839 v8::HandleScope handle_scope;
3840 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003841 Whammy* whammy = new Whammy();
Steve Blocka7e24c12009-10-30 11:49:00 +00003842 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3843 0, 0, 0, 0,
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003844 v8::External::New(whammy));
Steve Blocka7e24c12009-10-30 11:49:00 +00003845 const char* extension_list[] = { "v8/gc" };
3846 v8::ExtensionConfiguration extensions(1, extension_list);
3847 v8::Persistent<Context> context = Context::New(&extensions);
3848 Context::Scope context_scope(context);
3849
3850 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3851 context->Global()->Set(v8_str("whammy"), interceptor);
3852 const char* code =
3853 "var last;"
3854 "for (var i = 0; i < 10000; i++) {"
3855 " var obj = whammy.length;"
3856 " if (last) last.next = obj;"
3857 " last = obj;"
3858 "}"
3859 "gc();"
3860 "4";
3861 v8::Handle<Value> result = CompileRun(code);
3862 CHECK_EQ(4.0, result->NumberValue());
Ben Murdoch3bec4d22010-07-22 14:51:16 +01003863 delete whammy;
Steve Blocka7e24c12009-10-30 11:49:00 +00003864 context.Dispose();
3865}
3866
3867
Steve Blockd0582a62009-12-15 09:54:21 +00003868static bool in_scavenge = false;
3869static int last = -1;
3870
3871static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3872 CHECK_EQ(-1, last);
3873 last = 0;
3874 obj.Dispose();
3875 obj.Clear();
3876 in_scavenge = true;
3877 i::Heap::PerformScavenge();
3878 in_scavenge = false;
3879 *(reinterpret_cast<bool*>(data)) = true;
3880}
3881
3882static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3883 void* data) {
3884 CHECK_EQ(0, last);
3885 last = 1;
3886 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3887 obj.Dispose();
3888 obj.Clear();
3889}
3890
3891THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3892 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3893 // Calling callbacks from scavenges is unsafe as objects held by those
3894 // handlers might have become strongly reachable, but scavenge doesn't
3895 // check that.
3896 v8::Persistent<Context> context = Context::New();
3897 Context::Scope context_scope(context);
3898
3899 v8::Persistent<v8::Object> object_a;
3900 v8::Persistent<v8::Object> object_b;
3901
3902 {
3903 v8::HandleScope handle_scope;
3904 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
3905 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
3906 }
3907
3908 bool object_a_disposed = false;
3909 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
3910 bool released_in_scavenge = false;
3911 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
3912
3913 while (!object_a_disposed) {
3914 i::Heap::CollectAllGarbage(false);
3915 }
3916 CHECK(!released_in_scavenge);
3917}
3918
3919
Steve Blocka7e24c12009-10-30 11:49:00 +00003920v8::Handle<Function> args_fun;
3921
3922
3923static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3924 ApiTestFuzzer::Fuzz();
3925 CHECK_EQ(args_fun, args.Callee());
3926 CHECK_EQ(3, args.Length());
3927 CHECK_EQ(v8::Integer::New(1), args[0]);
3928 CHECK_EQ(v8::Integer::New(2), args[1]);
3929 CHECK_EQ(v8::Integer::New(3), args[2]);
3930 CHECK_EQ(v8::Undefined(), args[3]);
3931 v8::HandleScope scope;
3932 i::Heap::CollectAllGarbage(false);
3933 return v8::Undefined();
3934}
3935
3936
3937THREADED_TEST(Arguments) {
3938 v8::HandleScope scope;
3939 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3940 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3941 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01003942 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003943 v8_compile("f(1, 2, 3)")->Run();
3944}
3945
3946
Steve Blocka7e24c12009-10-30 11:49:00 +00003947static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3948 const AccessorInfo&) {
3949 return v8::Handle<Value>();
3950}
3951
3952
3953static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3954 const AccessorInfo&) {
3955 return v8::Handle<Value>();
3956}
3957
3958
3959static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3960 const AccessorInfo&) {
3961 if (!name->Equals(v8_str("foo"))) {
3962 return v8::Handle<v8::Boolean>(); // not intercepted
3963 }
3964
3965 return v8::False(); // intercepted, and don't delete the property
3966}
3967
3968
3969static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
3970 if (index != 2) {
3971 return v8::Handle<v8::Boolean>(); // not intercepted
3972 }
3973
3974 return v8::False(); // intercepted, and don't delete the property
3975}
3976
3977
3978THREADED_TEST(Deleter) {
3979 v8::HandleScope scope;
3980 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3981 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
3982 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
3983 LocalContext context;
3984 context->Global()->Set(v8_str("k"), obj->NewInstance());
3985 CompileRun(
3986 "k.foo = 'foo';"
3987 "k.bar = 'bar';"
3988 "k[2] = 2;"
3989 "k[4] = 4;");
3990 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
3991 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
3992
3993 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
3994 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
3995
3996 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
3997 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
3998
3999 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
4000 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
4001}
4002
4003
4004static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
4005 ApiTestFuzzer::Fuzz();
4006 if (name->Equals(v8_str("foo")) ||
4007 name->Equals(v8_str("bar")) ||
4008 name->Equals(v8_str("baz"))) {
4009 return v8::Undefined();
4010 }
4011 return v8::Handle<Value>();
4012}
4013
4014
4015static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
4016 ApiTestFuzzer::Fuzz();
4017 if (index == 0 || index == 1) return v8::Undefined();
4018 return v8::Handle<Value>();
4019}
4020
4021
4022static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4023 ApiTestFuzzer::Fuzz();
4024 v8::Handle<v8::Array> result = v8::Array::New(3);
4025 result->Set(v8::Integer::New(0), v8_str("foo"));
4026 result->Set(v8::Integer::New(1), v8_str("bar"));
4027 result->Set(v8::Integer::New(2), v8_str("baz"));
4028 return result;
4029}
4030
4031
4032static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4033 ApiTestFuzzer::Fuzz();
4034 v8::Handle<v8::Array> result = v8::Array::New(2);
4035 result->Set(v8::Integer::New(0), v8_str("0"));
4036 result->Set(v8::Integer::New(1), v8_str("1"));
4037 return result;
4038}
4039
4040
4041THREADED_TEST(Enumerators) {
4042 v8::HandleScope scope;
4043 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4044 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4045 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4046 LocalContext context;
4047 context->Global()->Set(v8_str("k"), obj->NewInstance());
4048 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4049 "k[10] = 0;"
4050 "k.a = 0;"
4051 "k[5] = 0;"
4052 "k.b = 0;"
4053 "k[4294967295] = 0;"
4054 "k.c = 0;"
4055 "k[4294967296] = 0;"
4056 "k.d = 0;"
4057 "k[140000] = 0;"
4058 "k.e = 0;"
4059 "k[30000000000] = 0;"
4060 "k.f = 0;"
4061 "var result = [];"
4062 "for (var prop in k) {"
4063 " result.push(prop);"
4064 "}"
4065 "result"));
4066 // Check that we get all the property names returned including the
4067 // ones from the enumerators in the right order: indexed properties
4068 // in numerical order, indexed interceptor properties, named
4069 // properties in insertion order, named interceptor properties.
4070 // This order is not mandated by the spec, so this test is just
4071 // documenting our behavior.
4072 CHECK_EQ(17, result->Length());
4073 // Indexed properties in numerical order.
4074 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4075 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4076 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4077 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4078 // Indexed interceptor properties in the order they are returned
4079 // from the enumerator interceptor.
4080 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4081 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4082 // Named properties in insertion order.
4083 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4084 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4085 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4086 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4087 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4088 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4089 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4090 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4091 // Named interceptor properties.
4092 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4093 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4094 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4095}
4096
4097
4098int p_getter_count;
4099int p_getter_count2;
4100
4101
4102static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4103 ApiTestFuzzer::Fuzz();
4104 p_getter_count++;
4105 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4106 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4107 if (name->Equals(v8_str("p1"))) {
4108 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4109 } else if (name->Equals(v8_str("p2"))) {
4110 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4111 } else if (name->Equals(v8_str("p3"))) {
4112 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4113 } else if (name->Equals(v8_str("p4"))) {
4114 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4115 }
4116 return v8::Undefined();
4117}
4118
4119
4120static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4121 ApiTestFuzzer::Fuzz();
4122 LocalContext context;
4123 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4124 CompileRun(
4125 "o1.__proto__ = { };"
4126 "var o2 = { __proto__: o1 };"
4127 "var o3 = { __proto__: o2 };"
4128 "var o4 = { __proto__: o3 };"
4129 "for (var i = 0; i < 10; i++) o4.p4;"
4130 "for (var i = 0; i < 10; i++) o3.p3;"
4131 "for (var i = 0; i < 10; i++) o2.p2;"
4132 "for (var i = 0; i < 10; i++) o1.p1;");
4133}
4134
4135
4136static v8::Handle<Value> PGetter2(Local<String> name,
4137 const AccessorInfo& info) {
4138 ApiTestFuzzer::Fuzz();
4139 p_getter_count2++;
4140 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4141 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4142 if (name->Equals(v8_str("p1"))) {
4143 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4144 } else if (name->Equals(v8_str("p2"))) {
4145 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4146 } else if (name->Equals(v8_str("p3"))) {
4147 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4148 } else if (name->Equals(v8_str("p4"))) {
4149 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4150 }
4151 return v8::Undefined();
4152}
4153
4154
4155THREADED_TEST(GetterHolders) {
4156 v8::HandleScope scope;
4157 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4158 obj->SetAccessor(v8_str("p1"), PGetter);
4159 obj->SetAccessor(v8_str("p2"), PGetter);
4160 obj->SetAccessor(v8_str("p3"), PGetter);
4161 obj->SetAccessor(v8_str("p4"), PGetter);
4162 p_getter_count = 0;
4163 RunHolderTest(obj);
4164 CHECK_EQ(40, p_getter_count);
4165}
4166
4167
4168THREADED_TEST(PreInterceptorHolders) {
4169 v8::HandleScope scope;
4170 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4171 obj->SetNamedPropertyHandler(PGetter2);
4172 p_getter_count2 = 0;
4173 RunHolderTest(obj);
4174 CHECK_EQ(40, p_getter_count2);
4175}
4176
4177
4178THREADED_TEST(ObjectInstantiation) {
4179 v8::HandleScope scope;
4180 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4181 templ->SetAccessor(v8_str("t"), PGetter2);
4182 LocalContext context;
4183 context->Global()->Set(v8_str("o"), templ->NewInstance());
4184 for (int i = 0; i < 100; i++) {
4185 v8::HandleScope inner_scope;
4186 v8::Handle<v8::Object> obj = templ->NewInstance();
4187 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4188 context->Global()->Set(v8_str("o2"), obj);
4189 v8::Handle<Value> value =
4190 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4191 CHECK_EQ(v8::True(), value);
4192 context->Global()->Set(v8_str("o"), obj);
4193 }
4194}
4195
4196
4197THREADED_TEST(StringWrite) {
4198 v8::HandleScope scope;
4199 v8::Handle<String> str = v8_str("abcde");
4200
4201 char buf[100];
4202 int len;
4203
4204 memset(buf, 0x1, sizeof(buf));
4205 len = str->WriteAscii(buf);
4206 CHECK_EQ(len, 5);
4207 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4208
4209 memset(buf, 0x1, sizeof(buf));
4210 len = str->WriteAscii(buf, 0, 4);
4211 CHECK_EQ(len, 4);
4212 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4213
4214 memset(buf, 0x1, sizeof(buf));
4215 len = str->WriteAscii(buf, 0, 5);
4216 CHECK_EQ(len, 5);
4217 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4218
4219 memset(buf, 0x1, sizeof(buf));
4220 len = str->WriteAscii(buf, 0, 6);
4221 CHECK_EQ(len, 5);
4222 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4223
4224 memset(buf, 0x1, sizeof(buf));
4225 len = str->WriteAscii(buf, 4, -1);
4226 CHECK_EQ(len, 1);
4227 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4228
4229 memset(buf, 0x1, sizeof(buf));
4230 len = str->WriteAscii(buf, 4, 6);
4231 CHECK_EQ(len, 1);
4232 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4233
4234 memset(buf, 0x1, sizeof(buf));
4235 len = str->WriteAscii(buf, 4, 1);
4236 CHECK_EQ(len, 1);
4237 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4238}
4239
4240
4241THREADED_TEST(ToArrayIndex) {
4242 v8::HandleScope scope;
4243 LocalContext context;
4244
4245 v8::Handle<String> str = v8_str("42");
4246 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4247 CHECK(!index.IsEmpty());
4248 CHECK_EQ(42.0, index->Uint32Value());
4249 str = v8_str("42asdf");
4250 index = str->ToArrayIndex();
4251 CHECK(index.IsEmpty());
4252 str = v8_str("-42");
4253 index = str->ToArrayIndex();
4254 CHECK(index.IsEmpty());
4255 str = v8_str("4294967295");
4256 index = str->ToArrayIndex();
4257 CHECK(!index.IsEmpty());
4258 CHECK_EQ(4294967295.0, index->Uint32Value());
4259 v8::Handle<v8::Number> num = v8::Number::New(1);
4260 index = num->ToArrayIndex();
4261 CHECK(!index.IsEmpty());
4262 CHECK_EQ(1.0, index->Uint32Value());
4263 num = v8::Number::New(-1);
4264 index = num->ToArrayIndex();
4265 CHECK(index.IsEmpty());
4266 v8::Handle<v8::Object> obj = v8::Object::New();
4267 index = obj->ToArrayIndex();
4268 CHECK(index.IsEmpty());
4269}
4270
4271
4272THREADED_TEST(ErrorConstruction) {
4273 v8::HandleScope scope;
4274 LocalContext context;
4275
4276 v8::Handle<String> foo = v8_str("foo");
4277 v8::Handle<String> message = v8_str("message");
4278 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4279 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004280 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4281 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004282 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4283 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004284 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004285 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4286 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004287 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004288 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4289 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004290 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004291 v8::Handle<Value> error = v8::Exception::Error(foo);
4292 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004293 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004294}
4295
4296
4297static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4298 ApiTestFuzzer::Fuzz();
4299 return v8_num(10);
4300}
4301
4302
4303static void YSetter(Local<String> name,
4304 Local<Value> value,
4305 const AccessorInfo& info) {
4306 if (info.This()->Has(name)) {
4307 info.This()->Delete(name);
4308 }
4309 info.This()->Set(name, value);
4310}
4311
4312
4313THREADED_TEST(DeleteAccessor) {
4314 v8::HandleScope scope;
4315 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4316 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4317 LocalContext context;
4318 v8::Handle<v8::Object> holder = obj->NewInstance();
4319 context->Global()->Set(v8_str("holder"), holder);
4320 v8::Handle<Value> result = CompileRun(
4321 "holder.y = 11; holder.y = 12; holder.y");
4322 CHECK_EQ(12, result->Uint32Value());
4323}
4324
4325
4326THREADED_TEST(TypeSwitch) {
4327 v8::HandleScope scope;
4328 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4329 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4330 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4331 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4332 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4333 LocalContext context;
4334 v8::Handle<v8::Object> obj0 = v8::Object::New();
4335 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4336 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4337 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4338 for (int i = 0; i < 10; i++) {
4339 CHECK_EQ(0, type_switch->match(obj0));
4340 CHECK_EQ(1, type_switch->match(obj1));
4341 CHECK_EQ(2, type_switch->match(obj2));
4342 CHECK_EQ(3, type_switch->match(obj3));
4343 CHECK_EQ(3, type_switch->match(obj3));
4344 CHECK_EQ(2, type_switch->match(obj2));
4345 CHECK_EQ(1, type_switch->match(obj1));
4346 CHECK_EQ(0, type_switch->match(obj0));
4347 }
4348}
4349
4350
4351// For use within the TestSecurityHandler() test.
4352static bool g_security_callback_result = false;
4353static bool NamedSecurityTestCallback(Local<v8::Object> global,
4354 Local<Value> name,
4355 v8::AccessType type,
4356 Local<Value> data) {
4357 // Always allow read access.
4358 if (type == v8::ACCESS_GET)
4359 return true;
4360
4361 // Sometimes allow other access.
4362 return g_security_callback_result;
4363}
4364
4365
4366static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4367 uint32_t key,
4368 v8::AccessType type,
4369 Local<Value> data) {
4370 // Always allow read access.
4371 if (type == v8::ACCESS_GET)
4372 return true;
4373
4374 // Sometimes allow other access.
4375 return g_security_callback_result;
4376}
4377
4378
4379static int trouble_nesting = 0;
4380static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4381 ApiTestFuzzer::Fuzz();
4382 trouble_nesting++;
4383
4384 // Call a JS function that throws an uncaught exception.
4385 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4386 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4387 arg_this->Get(v8_str("trouble_callee")) :
4388 arg_this->Get(v8_str("trouble_caller"));
4389 CHECK(trouble_callee->IsFunction());
4390 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4391}
4392
4393
4394static int report_count = 0;
4395static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4396 v8::Handle<Value>) {
4397 report_count++;
4398}
4399
4400
4401// Counts uncaught exceptions, but other tests running in parallel
4402// also have uncaught exceptions.
4403TEST(ApiUncaughtException) {
4404 report_count = 0;
4405 v8::HandleScope scope;
4406 LocalContext env;
4407 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4408
4409 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4410 v8::Local<v8::Object> global = env->Global();
4411 global->Set(v8_str("trouble"), fun->GetFunction());
4412
4413 Script::Compile(v8_str("function trouble_callee() {"
4414 " var x = null;"
4415 " return x.foo;"
4416 "};"
4417 "function trouble_caller() {"
4418 " trouble();"
4419 "};"))->Run();
4420 Local<Value> trouble = global->Get(v8_str("trouble"));
4421 CHECK(trouble->IsFunction());
4422 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4423 CHECK(trouble_callee->IsFunction());
4424 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4425 CHECK(trouble_caller->IsFunction());
4426 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4427 CHECK_EQ(1, report_count);
4428 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4429}
4430
Leon Clarke4515c472010-02-03 11:58:03 +00004431static const char* script_resource_name = "ExceptionInNativeScript.js";
4432static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4433 v8::Handle<Value>) {
4434 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4435 CHECK(!name_val.IsEmpty() && name_val->IsString());
4436 v8::String::AsciiValue name(message->GetScriptResourceName());
4437 CHECK_EQ(script_resource_name, *name);
4438 CHECK_EQ(3, message->GetLineNumber());
4439 v8::String::AsciiValue source_line(message->GetSourceLine());
4440 CHECK_EQ(" new o.foo();", *source_line);
4441}
4442
4443TEST(ExceptionInNativeScript) {
4444 v8::HandleScope scope;
4445 LocalContext env;
4446 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4447
4448 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4449 v8::Local<v8::Object> global = env->Global();
4450 global->Set(v8_str("trouble"), fun->GetFunction());
4451
4452 Script::Compile(v8_str("function trouble() {\n"
4453 " var o = {};\n"
4454 " new o.foo();\n"
4455 "};"), v8::String::New(script_resource_name))->Run();
4456 Local<Value> trouble = global->Get(v8_str("trouble"));
4457 CHECK(trouble->IsFunction());
4458 Function::Cast(*trouble)->Call(global, 0, NULL);
4459 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4460}
4461
Steve Blocka7e24c12009-10-30 11:49:00 +00004462
4463TEST(CompilationErrorUsingTryCatchHandler) {
4464 v8::HandleScope scope;
4465 LocalContext env;
4466 v8::TryCatch try_catch;
4467 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4468 CHECK_NE(NULL, *try_catch.Exception());
4469 CHECK(try_catch.HasCaught());
4470}
4471
4472
4473TEST(TryCatchFinallyUsingTryCatchHandler) {
4474 v8::HandleScope scope;
4475 LocalContext env;
4476 v8::TryCatch try_catch;
4477 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4478 CHECK(!try_catch.HasCaught());
4479 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4480 CHECK(try_catch.HasCaught());
4481 try_catch.Reset();
4482 Script::Compile(v8_str("(function() {"
4483 "try { throw ''; } finally { return; }"
4484 "})()"))->Run();
4485 CHECK(!try_catch.HasCaught());
4486 Script::Compile(v8_str("(function()"
4487 " { try { throw ''; } finally { throw 0; }"
4488 "})()"))->Run();
4489 CHECK(try_catch.HasCaught());
4490}
4491
4492
4493// SecurityHandler can't be run twice
4494TEST(SecurityHandler) {
4495 v8::HandleScope scope0;
4496 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4497 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4498 IndexedSecurityTestCallback);
4499 // Create an environment
4500 v8::Persistent<Context> context0 =
4501 Context::New(NULL, global_template);
4502 context0->Enter();
4503
4504 v8::Handle<v8::Object> global0 = context0->Global();
4505 v8::Handle<Script> script0 = v8_compile("foo = 111");
4506 script0->Run();
4507 global0->Set(v8_str("0"), v8_num(999));
4508 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4509 CHECK_EQ(111, foo0->Int32Value());
4510 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4511 CHECK_EQ(999, z0->Int32Value());
4512
4513 // Create another environment, should fail security checks.
4514 v8::HandleScope scope1;
4515
4516 v8::Persistent<Context> context1 =
4517 Context::New(NULL, global_template);
4518 context1->Enter();
4519
4520 v8::Handle<v8::Object> global1 = context1->Global();
4521 global1->Set(v8_str("othercontext"), global0);
4522 // This set will fail the security check.
4523 v8::Handle<Script> script1 =
4524 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4525 script1->Run();
4526 // This read will pass the security check.
4527 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4528 CHECK_EQ(111, foo1->Int32Value());
4529 // This read will pass the security check.
4530 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4531 CHECK_EQ(999, z1->Int32Value());
4532
4533 // Create another environment, should pass security checks.
4534 { g_security_callback_result = true; // allow security handler to pass.
4535 v8::HandleScope scope2;
4536 LocalContext context2;
4537 v8::Handle<v8::Object> global2 = context2->Global();
4538 global2->Set(v8_str("othercontext"), global0);
4539 v8::Handle<Script> script2 =
4540 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4541 script2->Run();
4542 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4543 CHECK_EQ(333, foo2->Int32Value());
4544 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4545 CHECK_EQ(888, z2->Int32Value());
4546 }
4547
4548 context1->Exit();
4549 context1.Dispose();
4550
4551 context0->Exit();
4552 context0.Dispose();
4553}
4554
4555
4556THREADED_TEST(SecurityChecks) {
4557 v8::HandleScope handle_scope;
4558 LocalContext env1;
4559 v8::Persistent<Context> env2 = Context::New();
4560
4561 Local<Value> foo = v8_str("foo");
4562 Local<Value> bar = v8_str("bar");
4563
4564 // Set to the same domain.
4565 env1->SetSecurityToken(foo);
4566
4567 // Create a function in env1.
4568 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4569 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4570 CHECK(spy->IsFunction());
4571
4572 // Create another function accessing global objects.
4573 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4574 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4575 CHECK(spy2->IsFunction());
4576
4577 // Switch to env2 in the same domain and invoke spy on env2.
4578 {
4579 env2->SetSecurityToken(foo);
4580 // Enter env2
4581 Context::Scope scope_env2(env2);
4582 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4583 CHECK(result->IsFunction());
4584 }
4585
4586 {
4587 env2->SetSecurityToken(bar);
4588 Context::Scope scope_env2(env2);
4589
4590 // Call cross_domain_call, it should throw an exception
4591 v8::TryCatch try_catch;
4592 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4593 CHECK(try_catch.HasCaught());
4594 }
4595
4596 env2.Dispose();
4597}
4598
4599
4600// Regression test case for issue 1183439.
4601THREADED_TEST(SecurityChecksForPrototypeChain) {
4602 v8::HandleScope scope;
4603 LocalContext current;
4604 v8::Persistent<Context> other = Context::New();
4605
4606 // Change context to be able to get to the Object function in the
4607 // other context without hitting the security checks.
4608 v8::Local<Value> other_object;
4609 { Context::Scope scope(other);
4610 other_object = other->Global()->Get(v8_str("Object"));
4611 other->Global()->Set(v8_num(42), v8_num(87));
4612 }
4613
4614 current->Global()->Set(v8_str("other"), other->Global());
4615 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4616
4617 // Make sure the security check fails here and we get an undefined
4618 // result instead of getting the Object function. Repeat in a loop
4619 // to make sure to exercise the IC code.
4620 v8::Local<Script> access_other0 = v8_compile("other.Object");
4621 v8::Local<Script> access_other1 = v8_compile("other[42]");
4622 for (int i = 0; i < 5; i++) {
4623 CHECK(!access_other0->Run()->Equals(other_object));
4624 CHECK(access_other0->Run()->IsUndefined());
4625 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4626 CHECK(access_other1->Run()->IsUndefined());
4627 }
4628
4629 // Create an object that has 'other' in its prototype chain and make
4630 // sure we cannot access the Object function indirectly through
4631 // that. Repeat in a loop to make sure to exercise the IC code.
4632 v8_compile("function F() { };"
4633 "F.prototype = other;"
4634 "var f = new F();")->Run();
4635 v8::Local<Script> access_f0 = v8_compile("f.Object");
4636 v8::Local<Script> access_f1 = v8_compile("f[42]");
4637 for (int j = 0; j < 5; j++) {
4638 CHECK(!access_f0->Run()->Equals(other_object));
4639 CHECK(access_f0->Run()->IsUndefined());
4640 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4641 CHECK(access_f1->Run()->IsUndefined());
4642 }
4643
4644 // Now it gets hairy: Set the prototype for the other global object
4645 // to be the current global object. The prototype chain for 'f' now
4646 // goes through 'other' but ends up in the current global object.
4647 { Context::Scope scope(other);
4648 other->Global()->Set(v8_str("__proto__"), current->Global());
4649 }
4650 // Set a named and an index property on the current global
4651 // object. To force the lookup to go through the other global object,
4652 // the properties must not exist in the other global object.
4653 current->Global()->Set(v8_str("foo"), v8_num(100));
4654 current->Global()->Set(v8_num(99), v8_num(101));
4655 // Try to read the properties from f and make sure that the access
4656 // gets stopped by the security checks on the other global object.
4657 Local<Script> access_f2 = v8_compile("f.foo");
4658 Local<Script> access_f3 = v8_compile("f[99]");
4659 for (int k = 0; k < 5; k++) {
4660 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4661 CHECK(access_f2->Run()->IsUndefined());
4662 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4663 CHECK(access_f3->Run()->IsUndefined());
4664 }
4665 other.Dispose();
4666}
4667
4668
4669THREADED_TEST(CrossDomainDelete) {
4670 v8::HandleScope handle_scope;
4671 LocalContext env1;
4672 v8::Persistent<Context> env2 = Context::New();
4673
4674 Local<Value> foo = v8_str("foo");
4675 Local<Value> bar = v8_str("bar");
4676
4677 // Set to the same domain.
4678 env1->SetSecurityToken(foo);
4679 env2->SetSecurityToken(foo);
4680
4681 env1->Global()->Set(v8_str("prop"), v8_num(3));
4682 env2->Global()->Set(v8_str("env1"), env1->Global());
4683
4684 // Change env2 to a different domain and delete env1.prop.
4685 env2->SetSecurityToken(bar);
4686 {
4687 Context::Scope scope_env2(env2);
4688 Local<Value> result =
4689 Script::Compile(v8_str("delete env1.prop"))->Run();
4690 CHECK(result->IsFalse());
4691 }
4692
4693 // Check that env1.prop still exists.
4694 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4695 CHECK(v->IsNumber());
4696 CHECK_EQ(3, v->Int32Value());
4697
4698 env2.Dispose();
4699}
4700
4701
4702THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4703 v8::HandleScope handle_scope;
4704 LocalContext env1;
4705 v8::Persistent<Context> env2 = Context::New();
4706
4707 Local<Value> foo = v8_str("foo");
4708 Local<Value> bar = v8_str("bar");
4709
4710 // Set to the same domain.
4711 env1->SetSecurityToken(foo);
4712 env2->SetSecurityToken(foo);
4713
4714 env1->Global()->Set(v8_str("prop"), v8_num(3));
4715 env2->Global()->Set(v8_str("env1"), env1->Global());
4716
4717 // env1.prop is enumerable in env2.
4718 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4719 {
4720 Context::Scope scope_env2(env2);
4721 Local<Value> result = Script::Compile(test)->Run();
4722 CHECK(result->IsTrue());
4723 }
4724
4725 // Change env2 to a different domain and test again.
4726 env2->SetSecurityToken(bar);
4727 {
4728 Context::Scope scope_env2(env2);
4729 Local<Value> result = Script::Compile(test)->Run();
4730 CHECK(result->IsFalse());
4731 }
4732
4733 env2.Dispose();
4734}
4735
4736
4737THREADED_TEST(CrossDomainForIn) {
4738 v8::HandleScope handle_scope;
4739 LocalContext env1;
4740 v8::Persistent<Context> env2 = Context::New();
4741
4742 Local<Value> foo = v8_str("foo");
4743 Local<Value> bar = v8_str("bar");
4744
4745 // Set to the same domain.
4746 env1->SetSecurityToken(foo);
4747 env2->SetSecurityToken(foo);
4748
4749 env1->Global()->Set(v8_str("prop"), v8_num(3));
4750 env2->Global()->Set(v8_str("env1"), env1->Global());
4751
4752 // Change env2 to a different domain and set env1's global object
4753 // as the __proto__ of an object in env2 and enumerate properties
4754 // in for-in. It shouldn't enumerate properties on env1's global
4755 // object.
4756 env2->SetSecurityToken(bar);
4757 {
4758 Context::Scope scope_env2(env2);
4759 Local<Value> result =
4760 CompileRun("(function(){var obj = {'__proto__':env1};"
4761 "for (var p in obj)"
4762 " if (p == 'prop') return false;"
4763 "return true;})()");
4764 CHECK(result->IsTrue());
4765 }
4766 env2.Dispose();
4767}
4768
4769
4770TEST(ContextDetachGlobal) {
4771 v8::HandleScope handle_scope;
4772 LocalContext env1;
4773 v8::Persistent<Context> env2 = Context::New();
4774
4775 Local<v8::Object> global1 = env1->Global();
4776
4777 Local<Value> foo = v8_str("foo");
4778
4779 // Set to the same domain.
4780 env1->SetSecurityToken(foo);
4781 env2->SetSecurityToken(foo);
4782
4783 // Enter env2
4784 env2->Enter();
4785
Andrei Popescu74b3c142010-03-29 12:03:09 +01004786 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004787 Local<v8::Object> global2 = env2->Global();
4788 global2->Set(v8_str("prop"), v8::Integer::New(1));
4789 CompileRun("function getProp() {return prop;}");
4790
4791 env1->Global()->Set(v8_str("getProp"),
4792 global2->Get(v8_str("getProp")));
4793
Andrei Popescu74b3c142010-03-29 12:03:09 +01004794 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004795 env2->Exit();
4796 env2->DetachGlobal();
4797 // env2 has a new global object.
4798 CHECK(!env2->Global()->Equals(global2));
4799
4800 v8::Persistent<Context> env3 =
4801 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4802 env3->SetSecurityToken(v8_str("bar"));
4803 env3->Enter();
4804
4805 Local<v8::Object> global3 = env3->Global();
4806 CHECK_EQ(global2, global3);
4807 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4808 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4809 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4810 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4811 env3->Exit();
4812
4813 // Call getProp in env1, and it should return the value 1
4814 {
4815 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4816 CHECK(get_prop->IsFunction());
4817 v8::TryCatch try_catch;
4818 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4819 CHECK(!try_catch.HasCaught());
4820 CHECK_EQ(1, r->Int32Value());
4821 }
4822
4823 // Check that env3 is not accessible from env1
4824 {
4825 Local<Value> r = global3->Get(v8_str("prop2"));
4826 CHECK(r->IsUndefined());
4827 }
4828
4829 env2.Dispose();
4830 env3.Dispose();
4831}
4832
4833
Andrei Popescu74b3c142010-03-29 12:03:09 +01004834TEST(DetachAndReattachGlobal) {
4835 v8::HandleScope scope;
4836 LocalContext env1;
4837
4838 // Create second environment.
4839 v8::Persistent<Context> env2 = Context::New();
4840
4841 Local<Value> foo = v8_str("foo");
4842
4843 // Set same security token for env1 and env2.
4844 env1->SetSecurityToken(foo);
4845 env2->SetSecurityToken(foo);
4846
4847 // Create a property on the global object in env2.
4848 {
4849 v8::Context::Scope scope(env2);
4850 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4851 }
4852
4853 // Create a reference to env2 global from env1 global.
4854 env1->Global()->Set(v8_str("other"), env2->Global());
4855
4856 // Check that we have access to other.p in env2 from env1.
4857 Local<Value> result = CompileRun("other.p");
4858 CHECK(result->IsInt32());
4859 CHECK_EQ(42, result->Int32Value());
4860
4861 // Hold on to global from env2 and detach global from env2.
4862 Local<v8::Object> global2 = env2->Global();
4863 env2->DetachGlobal();
4864
4865 // Check that the global has been detached. No other.p property can
4866 // be found.
4867 result = CompileRun("other.p");
4868 CHECK(result->IsUndefined());
4869
4870 // Reuse global2 for env3.
4871 v8::Persistent<Context> env3 =
4872 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4873 CHECK_EQ(global2, env3->Global());
4874
4875 // Start by using the same security token for env3 as for env1 and env2.
4876 env3->SetSecurityToken(foo);
4877
4878 // Create a property on the global object in env3.
4879 {
4880 v8::Context::Scope scope(env3);
4881 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4882 }
4883
4884 // Check that other.p is now the property in env3 and that we have access.
4885 result = CompileRun("other.p");
4886 CHECK(result->IsInt32());
4887 CHECK_EQ(24, result->Int32Value());
4888
4889 // Change security token for env3 to something different from env1 and env2.
4890 env3->SetSecurityToken(v8_str("bar"));
4891
4892 // Check that we do not have access to other.p in env1. |other| is now
4893 // the global object for env3 which has a different security token,
4894 // so access should be blocked.
4895 result = CompileRun("other.p");
4896 CHECK(result->IsUndefined());
4897
4898 // Detach the global for env3 and reattach it to env2.
4899 env3->DetachGlobal();
4900 env2->ReattachGlobal(global2);
4901
4902 // Check that we have access to other.p again in env1. |other| is now
4903 // the global object for env2 which has the same security token as env1.
4904 result = CompileRun("other.p");
4905 CHECK(result->IsInt32());
4906 CHECK_EQ(42, result->Int32Value());
4907
4908 env2.Dispose();
4909 env3.Dispose();
4910}
4911
4912
Steve Blocka7e24c12009-10-30 11:49:00 +00004913static bool NamedAccessBlocker(Local<v8::Object> global,
4914 Local<Value> name,
4915 v8::AccessType type,
4916 Local<Value> data) {
4917 return Context::GetCurrent()->Global()->Equals(global);
4918}
4919
4920
4921static bool IndexedAccessBlocker(Local<v8::Object> global,
4922 uint32_t key,
4923 v8::AccessType type,
4924 Local<Value> data) {
4925 return Context::GetCurrent()->Global()->Equals(global);
4926}
4927
4928
4929static int g_echo_value = -1;
4930static v8::Handle<Value> EchoGetter(Local<String> name,
4931 const AccessorInfo& info) {
4932 return v8_num(g_echo_value);
4933}
4934
4935
4936static void EchoSetter(Local<String> name,
4937 Local<Value> value,
4938 const AccessorInfo&) {
4939 if (value->IsNumber())
4940 g_echo_value = value->Int32Value();
4941}
4942
4943
4944static v8::Handle<Value> UnreachableGetter(Local<String> name,
4945 const AccessorInfo& info) {
4946 CHECK(false); // This function should not be called..
4947 return v8::Undefined();
4948}
4949
4950
4951static void UnreachableSetter(Local<String>, Local<Value>,
4952 const AccessorInfo&) {
4953 CHECK(false); // This function should nto be called.
4954}
4955
4956
4957THREADED_TEST(AccessControl) {
4958 v8::HandleScope handle_scope;
4959 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4960
4961 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4962 IndexedAccessBlocker);
4963
4964 // Add an accessor accessible by cross-domain JS code.
4965 global_template->SetAccessor(
4966 v8_str("accessible_prop"),
4967 EchoGetter, EchoSetter,
4968 v8::Handle<Value>(),
4969 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4970
4971 // Add an accessor that is not accessible by cross-domain JS code.
4972 global_template->SetAccessor(v8_str("blocked_prop"),
4973 UnreachableGetter, UnreachableSetter,
4974 v8::Handle<Value>(),
4975 v8::DEFAULT);
4976
4977 // Create an environment
4978 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4979 context0->Enter();
4980
4981 v8::Handle<v8::Object> global0 = context0->Global();
4982
4983 v8::HandleScope scope1;
4984
4985 v8::Persistent<Context> context1 = Context::New();
4986 context1->Enter();
4987
4988 v8::Handle<v8::Object> global1 = context1->Global();
4989 global1->Set(v8_str("other"), global0);
4990
4991 v8::Handle<Value> value;
4992
4993 // Access blocked property
4994 value = v8_compile("other.blocked_prop = 1")->Run();
4995 value = v8_compile("other.blocked_prop")->Run();
4996 CHECK(value->IsUndefined());
4997
4998 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4999 CHECK(value->IsFalse());
5000
5001 // Access accessible property
5002 value = v8_compile("other.accessible_prop = 3")->Run();
5003 CHECK(value->IsNumber());
5004 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00005005 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00005006
5007 value = v8_compile("other.accessible_prop")->Run();
5008 CHECK(value->IsNumber());
5009 CHECK_EQ(3, value->Int32Value());
5010
5011 value =
5012 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
5013 CHECK(value->IsTrue());
5014
5015 // Enumeration doesn't enumerate accessors from inaccessible objects in
5016 // the prototype chain even if the accessors are in themselves accessible.
5017 Local<Value> result =
5018 CompileRun("(function(){var obj = {'__proto__':other};"
5019 "for (var p in obj)"
5020 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5021 " return false;"
5022 " }"
5023 "return true;})()");
5024 CHECK(result->IsTrue());
5025
5026 context1->Exit();
5027 context0->Exit();
5028 context1.Dispose();
5029 context0.Dispose();
5030}
5031
5032
Leon Clarke4515c472010-02-03 11:58:03 +00005033static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5034 Local<Value> name,
5035 v8::AccessType type,
5036 Local<Value> data) {
5037 return false;
5038}
5039
5040
5041static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5042 uint32_t key,
5043 v8::AccessType type,
5044 Local<Value> data) {
5045 return false;
5046}
5047
5048
5049THREADED_TEST(AccessControlGetOwnPropertyNames) {
5050 v8::HandleScope handle_scope;
5051 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5052
5053 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5054 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5055 GetOwnPropertyNamesIndexedBlocker);
5056
5057 // Create an environment
5058 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5059 context0->Enter();
5060
5061 v8::Handle<v8::Object> global0 = context0->Global();
5062
5063 v8::HandleScope scope1;
5064
5065 v8::Persistent<Context> context1 = Context::New();
5066 context1->Enter();
5067
5068 v8::Handle<v8::Object> global1 = context1->Global();
5069 global1->Set(v8_str("other"), global0);
5070 global1->Set(v8_str("object"), obj_template->NewInstance());
5071
5072 v8::Handle<Value> value;
5073
5074 // Attempt to get the property names of the other global object and
5075 // of an object that requires access checks. Accessing the other
5076 // global object should be blocked by access checks on the global
5077 // proxy object. Accessing the object that requires access checks
5078 // is blocked by the access checks on the object itself.
5079 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5080 CHECK(value->IsTrue());
5081
5082 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5083 CHECK(value->IsTrue());
5084
5085 context1->Exit();
5086 context0->Exit();
5087 context1.Dispose();
5088 context0.Dispose();
5089}
5090
5091
Steve Block8defd9f2010-07-08 12:39:36 +01005092static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5093 v8::Handle<v8::Array> result = v8::Array::New(1);
5094 result->Set(0, v8_str("x"));
5095 return result;
5096}
5097
5098
5099THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5100 v8::HandleScope handle_scope;
5101 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5102
5103 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5104 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5105 NamedPropertyEnumerator);
5106
5107 LocalContext context;
5108 v8::Handle<v8::Object> global = context->Global();
5109 global->Set(v8_str("object"), obj_template->NewInstance());
5110
5111 v8::Handle<Value> value =
5112 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5113 CHECK_EQ(v8_str("x"), value);
5114}
5115
5116
Steve Blocka7e24c12009-10-30 11:49:00 +00005117static v8::Handle<Value> ConstTenGetter(Local<String> name,
5118 const AccessorInfo& info) {
5119 return v8_num(10);
5120}
5121
5122
5123THREADED_TEST(CrossDomainAccessors) {
5124 v8::HandleScope handle_scope;
5125
5126 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5127
5128 v8::Handle<v8::ObjectTemplate> global_template =
5129 func_template->InstanceTemplate();
5130
5131 v8::Handle<v8::ObjectTemplate> proto_template =
5132 func_template->PrototypeTemplate();
5133
5134 // Add an accessor to proto that's accessible by cross-domain JS code.
5135 proto_template->SetAccessor(v8_str("accessible"),
5136 ConstTenGetter, 0,
5137 v8::Handle<Value>(),
5138 v8::ALL_CAN_READ);
5139
5140 // Add an accessor that is not accessible by cross-domain JS code.
5141 global_template->SetAccessor(v8_str("unreachable"),
5142 UnreachableGetter, 0,
5143 v8::Handle<Value>(),
5144 v8::DEFAULT);
5145
5146 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5147 context0->Enter();
5148
5149 Local<v8::Object> global = context0->Global();
5150 // Add a normal property that shadows 'accessible'
5151 global->Set(v8_str("accessible"), v8_num(11));
5152
5153 // Enter a new context.
5154 v8::HandleScope scope1;
5155 v8::Persistent<Context> context1 = Context::New();
5156 context1->Enter();
5157
5158 v8::Handle<v8::Object> global1 = context1->Global();
5159 global1->Set(v8_str("other"), global);
5160
5161 // Should return 10, instead of 11
5162 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5163 CHECK(value->IsNumber());
5164 CHECK_EQ(10, value->Int32Value());
5165
5166 value = v8_compile("other.unreachable")->Run();
5167 CHECK(value->IsUndefined());
5168
5169 context1->Exit();
5170 context0->Exit();
5171 context1.Dispose();
5172 context0.Dispose();
5173}
5174
5175
5176static int named_access_count = 0;
5177static int indexed_access_count = 0;
5178
5179static bool NamedAccessCounter(Local<v8::Object> global,
5180 Local<Value> name,
5181 v8::AccessType type,
5182 Local<Value> data) {
5183 named_access_count++;
5184 return true;
5185}
5186
5187
5188static bool IndexedAccessCounter(Local<v8::Object> global,
5189 uint32_t key,
5190 v8::AccessType type,
5191 Local<Value> data) {
5192 indexed_access_count++;
5193 return true;
5194}
5195
5196
5197// This one is too easily disturbed by other tests.
5198TEST(AccessControlIC) {
5199 named_access_count = 0;
5200 indexed_access_count = 0;
5201
5202 v8::HandleScope handle_scope;
5203
5204 // Create an environment.
5205 v8::Persistent<Context> context0 = Context::New();
5206 context0->Enter();
5207
5208 // Create an object that requires access-check functions to be
5209 // called for cross-domain access.
5210 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5211 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5212 IndexedAccessCounter);
5213 Local<v8::Object> object = object_template->NewInstance();
5214
5215 v8::HandleScope scope1;
5216
5217 // Create another environment.
5218 v8::Persistent<Context> context1 = Context::New();
5219 context1->Enter();
5220
5221 // Make easy access to the object from the other environment.
5222 v8::Handle<v8::Object> global1 = context1->Global();
5223 global1->Set(v8_str("obj"), object);
5224
5225 v8::Handle<Value> value;
5226
5227 // Check that the named access-control function is called every time.
5228 CompileRun("function testProp(obj) {"
5229 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5230 " for (var j = 0; j < 10; j++) obj.prop;"
5231 " return obj.prop"
5232 "}");
5233 value = CompileRun("testProp(obj)");
5234 CHECK(value->IsNumber());
5235 CHECK_EQ(1, value->Int32Value());
5236 CHECK_EQ(21, named_access_count);
5237
5238 // Check that the named access-control function is called every time.
5239 CompileRun("var p = 'prop';"
5240 "function testKeyed(obj) {"
5241 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5242 " for (var j = 0; j < 10; j++) obj[p];"
5243 " return obj[p];"
5244 "}");
5245 // Use obj which requires access checks. No inline caching is used
5246 // in that case.
5247 value = CompileRun("testKeyed(obj)");
5248 CHECK(value->IsNumber());
5249 CHECK_EQ(1, value->Int32Value());
5250 CHECK_EQ(42, named_access_count);
5251 // Force the inline caches into generic state and try again.
5252 CompileRun("testKeyed({ a: 0 })");
5253 CompileRun("testKeyed({ b: 0 })");
5254 value = CompileRun("testKeyed(obj)");
5255 CHECK(value->IsNumber());
5256 CHECK_EQ(1, value->Int32Value());
5257 CHECK_EQ(63, named_access_count);
5258
5259 // Check that the indexed access-control function is called every time.
5260 CompileRun("function testIndexed(obj) {"
5261 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5262 " for (var j = 0; j < 10; j++) obj[0];"
5263 " return obj[0]"
5264 "}");
5265 value = CompileRun("testIndexed(obj)");
5266 CHECK(value->IsNumber());
5267 CHECK_EQ(1, value->Int32Value());
5268 CHECK_EQ(21, indexed_access_count);
5269 // Force the inline caches into generic state.
5270 CompileRun("testIndexed(new Array(1))");
5271 // Test that the indexed access check is called.
5272 value = CompileRun("testIndexed(obj)");
5273 CHECK(value->IsNumber());
5274 CHECK_EQ(1, value->Int32Value());
5275 CHECK_EQ(42, indexed_access_count);
5276
5277 // Check that the named access check is called when invoking
5278 // functions on an object that requires access checks.
5279 CompileRun("obj.f = function() {}");
5280 CompileRun("function testCallNormal(obj) {"
5281 " for (var i = 0; i < 10; i++) obj.f();"
5282 "}");
5283 CompileRun("testCallNormal(obj)");
5284 CHECK_EQ(74, named_access_count);
5285
5286 // Force obj into slow case.
5287 value = CompileRun("delete obj.prop");
5288 CHECK(value->BooleanValue());
5289 // Force inline caches into dictionary probing mode.
5290 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5291 // Test that the named access check is called.
5292 value = CompileRun("testProp(obj);");
5293 CHECK(value->IsNumber());
5294 CHECK_EQ(1, value->Int32Value());
5295 CHECK_EQ(96, named_access_count);
5296
5297 // Force the call inline cache into dictionary probing mode.
5298 CompileRun("o.f = function() {}; testCallNormal(o)");
5299 // Test that the named access check is still called for each
5300 // invocation of the function.
5301 value = CompileRun("testCallNormal(obj)");
5302 CHECK_EQ(106, named_access_count);
5303
5304 context1->Exit();
5305 context0->Exit();
5306 context1.Dispose();
5307 context0.Dispose();
5308}
5309
5310
5311static bool NamedAccessFlatten(Local<v8::Object> global,
5312 Local<Value> name,
5313 v8::AccessType type,
5314 Local<Value> data) {
5315 char buf[100];
5316 int len;
5317
5318 CHECK(name->IsString());
5319
5320 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005321 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005322 CHECK_EQ(4, len);
5323
5324 uint16_t buf2[100];
5325
5326 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005327 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005328 CHECK_EQ(4, len);
5329
5330 return true;
5331}
5332
5333
5334static bool IndexedAccessFlatten(Local<v8::Object> global,
5335 uint32_t key,
5336 v8::AccessType type,
5337 Local<Value> data) {
5338 return true;
5339}
5340
5341
5342// Regression test. In access checks, operations that may cause
5343// garbage collection are not allowed. It used to be the case that
5344// using the Write operation on a string could cause a garbage
5345// collection due to flattening of the string. This is no longer the
5346// case.
5347THREADED_TEST(AccessControlFlatten) {
5348 named_access_count = 0;
5349 indexed_access_count = 0;
5350
5351 v8::HandleScope handle_scope;
5352
5353 // Create an environment.
5354 v8::Persistent<Context> context0 = Context::New();
5355 context0->Enter();
5356
5357 // Create an object that requires access-check functions to be
5358 // called for cross-domain access.
5359 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5360 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5361 IndexedAccessFlatten);
5362 Local<v8::Object> object = object_template->NewInstance();
5363
5364 v8::HandleScope scope1;
5365
5366 // Create another environment.
5367 v8::Persistent<Context> context1 = Context::New();
5368 context1->Enter();
5369
5370 // Make easy access to the object from the other environment.
5371 v8::Handle<v8::Object> global1 = context1->Global();
5372 global1->Set(v8_str("obj"), object);
5373
5374 v8::Handle<Value> value;
5375
5376 value = v8_compile("var p = 'as' + 'df';")->Run();
5377 value = v8_compile("obj[p];")->Run();
5378
5379 context1->Exit();
5380 context0->Exit();
5381 context1.Dispose();
5382 context0.Dispose();
5383}
5384
5385
5386static v8::Handle<Value> AccessControlNamedGetter(
5387 Local<String>, const AccessorInfo&) {
5388 return v8::Integer::New(42);
5389}
5390
5391
5392static v8::Handle<Value> AccessControlNamedSetter(
5393 Local<String>, Local<Value> value, const AccessorInfo&) {
5394 return value;
5395}
5396
5397
5398static v8::Handle<Value> AccessControlIndexedGetter(
5399 uint32_t index,
5400 const AccessorInfo& info) {
5401 return v8_num(42);
5402}
5403
5404
5405static v8::Handle<Value> AccessControlIndexedSetter(
5406 uint32_t, Local<Value> value, const AccessorInfo&) {
5407 return value;
5408}
5409
5410
5411THREADED_TEST(AccessControlInterceptorIC) {
5412 named_access_count = 0;
5413 indexed_access_count = 0;
5414
5415 v8::HandleScope handle_scope;
5416
5417 // Create an environment.
5418 v8::Persistent<Context> context0 = Context::New();
5419 context0->Enter();
5420
5421 // Create an object that requires access-check functions to be
5422 // called for cross-domain access. The object also has interceptors
5423 // interceptor.
5424 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5425 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5426 IndexedAccessCounter);
5427 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5428 AccessControlNamedSetter);
5429 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5430 AccessControlIndexedSetter);
5431 Local<v8::Object> object = object_template->NewInstance();
5432
5433 v8::HandleScope scope1;
5434
5435 // Create another environment.
5436 v8::Persistent<Context> context1 = Context::New();
5437 context1->Enter();
5438
5439 // Make easy access to the object from the other environment.
5440 v8::Handle<v8::Object> global1 = context1->Global();
5441 global1->Set(v8_str("obj"), object);
5442
5443 v8::Handle<Value> value;
5444
5445 // Check that the named access-control function is called every time
5446 // eventhough there is an interceptor on the object.
5447 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5448 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5449 "obj.x")->Run();
5450 CHECK(value->IsNumber());
5451 CHECK_EQ(42, value->Int32Value());
5452 CHECK_EQ(21, named_access_count);
5453
5454 value = v8_compile("var p = 'x';")->Run();
5455 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5456 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5457 "obj[p]")->Run();
5458 CHECK(value->IsNumber());
5459 CHECK_EQ(42, value->Int32Value());
5460 CHECK_EQ(42, named_access_count);
5461
5462 // Check that the indexed access-control function is called every
5463 // time eventhough there is an interceptor on the object.
5464 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5465 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5466 "obj[0]")->Run();
5467 CHECK(value->IsNumber());
5468 CHECK_EQ(42, value->Int32Value());
5469 CHECK_EQ(21, indexed_access_count);
5470
5471 context1->Exit();
5472 context0->Exit();
5473 context1.Dispose();
5474 context0.Dispose();
5475}
5476
5477
5478THREADED_TEST(Version) {
5479 v8::V8::GetVersion();
5480}
5481
5482
5483static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5484 ApiTestFuzzer::Fuzz();
5485 return v8_num(12);
5486}
5487
5488
5489THREADED_TEST(InstanceProperties) {
5490 v8::HandleScope handle_scope;
5491 LocalContext context;
5492
5493 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5494 Local<ObjectTemplate> instance = t->InstanceTemplate();
5495
5496 instance->Set(v8_str("x"), v8_num(42));
5497 instance->Set(v8_str("f"),
5498 v8::FunctionTemplate::New(InstanceFunctionCallback));
5499
5500 Local<Value> o = t->GetFunction()->NewInstance();
5501
5502 context->Global()->Set(v8_str("i"), o);
5503 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5504 CHECK_EQ(42, value->Int32Value());
5505
5506 value = Script::Compile(v8_str("i.f()"))->Run();
5507 CHECK_EQ(12, value->Int32Value());
5508}
5509
5510
5511static v8::Handle<Value>
5512GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5513 ApiTestFuzzer::Fuzz();
5514 return v8::Handle<Value>();
5515}
5516
5517
5518THREADED_TEST(GlobalObjectInstanceProperties) {
5519 v8::HandleScope handle_scope;
5520
5521 Local<Value> global_object;
5522
5523 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5524 t->InstanceTemplate()->SetNamedPropertyHandler(
5525 GlobalObjectInstancePropertiesGet);
5526 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5527 instance_template->Set(v8_str("x"), v8_num(42));
5528 instance_template->Set(v8_str("f"),
5529 v8::FunctionTemplate::New(InstanceFunctionCallback));
5530
5531 {
5532 LocalContext env(NULL, instance_template);
5533 // Hold on to the global object so it can be used again in another
5534 // environment initialization.
5535 global_object = env->Global();
5536
5537 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5538 CHECK_EQ(42, value->Int32Value());
5539 value = Script::Compile(v8_str("f()"))->Run();
5540 CHECK_EQ(12, value->Int32Value());
5541 }
5542
5543 {
5544 // Create new environment reusing the global object.
5545 LocalContext env(NULL, instance_template, global_object);
5546 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5547 CHECK_EQ(42, value->Int32Value());
5548 value = Script::Compile(v8_str("f()"))->Run();
5549 CHECK_EQ(12, value->Int32Value());
5550 }
5551}
5552
5553
5554static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5555 ApiTestFuzzer::Fuzz();
5556 return v8_num(42);
5557}
5558
5559
5560static int shadow_y;
5561static int shadow_y_setter_call_count;
5562static int shadow_y_getter_call_count;
5563
5564
5565static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5566 shadow_y_setter_call_count++;
5567 shadow_y = 42;
5568}
5569
5570
5571static v8::Handle<Value> ShadowYGetter(Local<String> name,
5572 const AccessorInfo& info) {
5573 ApiTestFuzzer::Fuzz();
5574 shadow_y_getter_call_count++;
5575 return v8_num(shadow_y);
5576}
5577
5578
5579static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5580 const AccessorInfo& info) {
5581 return v8::Handle<Value>();
5582}
5583
5584
5585static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5586 const AccessorInfo&) {
5587 return v8::Handle<Value>();
5588}
5589
5590
5591THREADED_TEST(ShadowObject) {
5592 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5593 v8::HandleScope handle_scope;
5594
5595 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5596 LocalContext context(NULL, global_template);
5597
5598 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5599 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5600 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5601 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5602 Local<ObjectTemplate> instance = t->InstanceTemplate();
5603
5604 // Only allow calls of f on instances of t.
5605 Local<v8::Signature> signature = v8::Signature::New(t);
5606 proto->Set(v8_str("f"),
5607 v8::FunctionTemplate::New(ShadowFunctionCallback,
5608 Local<Value>(),
5609 signature));
5610 proto->Set(v8_str("x"), v8_num(12));
5611
5612 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5613
5614 Local<Value> o = t->GetFunction()->NewInstance();
5615 context->Global()->Set(v8_str("__proto__"), o);
5616
5617 Local<Value> value =
5618 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5619 CHECK(value->IsBoolean());
5620 CHECK(!value->BooleanValue());
5621
5622 value = Script::Compile(v8_str("x"))->Run();
5623 CHECK_EQ(12, value->Int32Value());
5624
5625 value = Script::Compile(v8_str("f()"))->Run();
5626 CHECK_EQ(42, value->Int32Value());
5627
5628 Script::Compile(v8_str("y = 42"))->Run();
5629 CHECK_EQ(1, shadow_y_setter_call_count);
5630 value = Script::Compile(v8_str("y"))->Run();
5631 CHECK_EQ(1, shadow_y_getter_call_count);
5632 CHECK_EQ(42, value->Int32Value());
5633}
5634
5635
5636THREADED_TEST(HiddenPrototype) {
5637 v8::HandleScope handle_scope;
5638 LocalContext context;
5639
5640 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5641 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5642 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5643 t1->SetHiddenPrototype(true);
5644 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5645 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5646 t2->SetHiddenPrototype(true);
5647 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5648 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5649 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5650
5651 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5652 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5653 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5654 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5655
5656 // Setting the prototype on an object skips hidden prototypes.
5657 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5658 o0->Set(v8_str("__proto__"), o1);
5659 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5660 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5661 o0->Set(v8_str("__proto__"), o2);
5662 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5663 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5664 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5665 o0->Set(v8_str("__proto__"), o3);
5666 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5667 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5668 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5669 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5670
5671 // Getting the prototype of o0 should get the first visible one
5672 // which is o3. Therefore, z should not be defined on the prototype
5673 // object.
5674 Local<Value> proto = o0->Get(v8_str("__proto__"));
5675 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005676 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005677}
5678
5679
Andrei Popescu402d9372010-02-26 13:31:12 +00005680THREADED_TEST(SetPrototype) {
5681 v8::HandleScope handle_scope;
5682 LocalContext context;
5683
5684 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5685 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5686 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5687 t1->SetHiddenPrototype(true);
5688 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5689 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5690 t2->SetHiddenPrototype(true);
5691 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5692 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5693 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5694
5695 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5696 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5697 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5698 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5699
5700 // Setting the prototype on an object does not skip hidden prototypes.
5701 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5702 CHECK(o0->SetPrototype(o1));
5703 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5704 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5705 CHECK(o1->SetPrototype(o2));
5706 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5707 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5708 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5709 CHECK(o2->SetPrototype(o3));
5710 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5711 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5712 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5713 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5714
5715 // Getting the prototype of o0 should get the first visible one
5716 // which is o3. Therefore, z should not be defined on the prototype
5717 // object.
5718 Local<Value> proto = o0->Get(v8_str("__proto__"));
5719 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005720 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005721
5722 // However, Object::GetPrototype ignores hidden prototype.
5723 Local<Value> proto0 = o0->GetPrototype();
5724 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005725 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005726
5727 Local<Value> proto1 = o1->GetPrototype();
5728 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005729 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005730
5731 Local<Value> proto2 = o2->GetPrototype();
5732 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005733 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005734}
5735
5736
5737THREADED_TEST(SetPrototypeThrows) {
5738 v8::HandleScope handle_scope;
5739 LocalContext context;
5740
5741 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5742
5743 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5744 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5745
5746 CHECK(o0->SetPrototype(o1));
5747 // If setting the prototype leads to the cycle, SetPrototype should
5748 // return false and keep VM in sane state.
5749 v8::TryCatch try_catch;
5750 CHECK(!o1->SetPrototype(o0));
5751 CHECK(!try_catch.HasCaught());
5752 ASSERT(!i::Top::has_pending_exception());
5753
5754 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5755}
5756
5757
Steve Blocka7e24c12009-10-30 11:49:00 +00005758THREADED_TEST(GetterSetterExceptions) {
5759 v8::HandleScope handle_scope;
5760 LocalContext context;
5761 CompileRun(
5762 "function Foo() { };"
5763 "function Throw() { throw 5; };"
5764 "var x = { };"
5765 "x.__defineSetter__('set', Throw);"
5766 "x.__defineGetter__('get', Throw);");
5767 Local<v8::Object> x =
5768 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5769 v8::TryCatch try_catch;
5770 x->Set(v8_str("set"), v8::Integer::New(8));
5771 x->Get(v8_str("get"));
5772 x->Set(v8_str("set"), v8::Integer::New(8));
5773 x->Get(v8_str("get"));
5774 x->Set(v8_str("set"), v8::Integer::New(8));
5775 x->Get(v8_str("get"));
5776 x->Set(v8_str("set"), v8::Integer::New(8));
5777 x->Get(v8_str("get"));
5778}
5779
5780
5781THREADED_TEST(Constructor) {
5782 v8::HandleScope handle_scope;
5783 LocalContext context;
5784 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5785 templ->SetClassName(v8_str("Fun"));
5786 Local<Function> cons = templ->GetFunction();
5787 context->Global()->Set(v8_str("Fun"), cons);
5788 Local<v8::Object> inst = cons->NewInstance();
5789 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5790 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5791 CHECK(value->BooleanValue());
5792}
5793
5794THREADED_TEST(FunctionDescriptorException) {
5795 v8::HandleScope handle_scope;
5796 LocalContext context;
5797 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5798 templ->SetClassName(v8_str("Fun"));
5799 Local<Function> cons = templ->GetFunction();
5800 context->Global()->Set(v8_str("Fun"), cons);
5801 Local<Value> value = CompileRun(
5802 "function test() {"
5803 " try {"
5804 " (new Fun()).blah()"
5805 " } catch (e) {"
5806 " var str = String(e);"
5807 " if (str.indexOf('TypeError') == -1) return 1;"
5808 " if (str.indexOf('[object Fun]') != -1) return 2;"
5809 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5810 " return 0;"
5811 " }"
5812 " return 4;"
5813 "}"
5814 "test();");
5815 CHECK_EQ(0, value->Int32Value());
5816}
5817
5818
5819THREADED_TEST(EvalAliasedDynamic) {
5820 v8::HandleScope scope;
5821 LocalContext current;
5822
5823 // Tests where aliased eval can only be resolved dynamically.
5824 Local<Script> script =
5825 Script::Compile(v8_str("function f(x) { "
5826 " var foo = 2;"
5827 " with (x) { return eval('foo'); }"
5828 "}"
5829 "foo = 0;"
5830 "result1 = f(new Object());"
5831 "result2 = f(this);"
5832 "var x = new Object();"
5833 "x.eval = function(x) { return 1; };"
5834 "result3 = f(x);"));
5835 script->Run();
5836 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5837 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5838 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5839
5840 v8::TryCatch try_catch;
5841 script =
5842 Script::Compile(v8_str("function f(x) { "
5843 " var bar = 2;"
5844 " with (x) { return eval('bar'); }"
5845 "}"
5846 "f(this)"));
5847 script->Run();
5848 CHECK(try_catch.HasCaught());
5849 try_catch.Reset();
5850}
5851
5852
5853THREADED_TEST(CrossEval) {
5854 v8::HandleScope scope;
5855 LocalContext other;
5856 LocalContext current;
5857
5858 Local<String> token = v8_str("<security token>");
5859 other->SetSecurityToken(token);
5860 current->SetSecurityToken(token);
5861
5862 // Setup reference from current to other.
5863 current->Global()->Set(v8_str("other"), other->Global());
5864
5865 // Check that new variables are introduced in other context.
5866 Local<Script> script =
5867 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5868 script->Run();
5869 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5870 CHECK_EQ(1234, foo->Int32Value());
5871 CHECK(!current->Global()->Has(v8_str("foo")));
5872
5873 // Check that writing to non-existing properties introduces them in
5874 // the other context.
5875 script =
5876 Script::Compile(v8_str("other.eval('na = 1234')"));
5877 script->Run();
5878 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5879 CHECK(!current->Global()->Has(v8_str("na")));
5880
5881 // Check that global variables in current context are not visible in other
5882 // context.
5883 v8::TryCatch try_catch;
5884 script =
5885 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5886 Local<Value> result = script->Run();
5887 CHECK(try_catch.HasCaught());
5888 try_catch.Reset();
5889
5890 // Check that local variables in current context are not visible in other
5891 // context.
5892 script =
5893 Script::Compile(v8_str("(function() { "
5894 " var baz = 87;"
5895 " return other.eval('baz');"
5896 "})();"));
5897 result = script->Run();
5898 CHECK(try_catch.HasCaught());
5899 try_catch.Reset();
5900
5901 // Check that global variables in the other environment are visible
5902 // when evaluting code.
5903 other->Global()->Set(v8_str("bis"), v8_num(1234));
5904 script = Script::Compile(v8_str("other.eval('bis')"));
5905 CHECK_EQ(1234, script->Run()->Int32Value());
5906 CHECK(!try_catch.HasCaught());
5907
5908 // Check that the 'this' pointer points to the global object evaluating
5909 // code.
5910 other->Global()->Set(v8_str("t"), other->Global());
5911 script = Script::Compile(v8_str("other.eval('this == t')"));
5912 result = script->Run();
5913 CHECK(result->IsTrue());
5914 CHECK(!try_catch.HasCaught());
5915
5916 // Check that variables introduced in with-statement are not visible in
5917 // other context.
5918 script =
5919 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
5920 result = script->Run();
5921 CHECK(try_catch.HasCaught());
5922 try_catch.Reset();
5923
5924 // Check that you cannot use 'eval.call' with another object than the
5925 // current global object.
5926 script =
5927 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
5928 result = script->Run();
5929 CHECK(try_catch.HasCaught());
5930}
5931
5932
5933// Test that calling eval in a context which has been detached from
5934// its global throws an exception. This behavior is consistent with
5935// other JavaScript implementations.
5936THREADED_TEST(EvalInDetachedGlobal) {
5937 v8::HandleScope scope;
5938
5939 v8::Persistent<Context> context0 = Context::New();
5940 v8::Persistent<Context> context1 = Context::New();
5941
5942 // Setup function in context0 that uses eval from context0.
5943 context0->Enter();
5944 v8::Handle<v8::Value> fun =
5945 CompileRun("var x = 42;"
5946 "(function() {"
5947 " var e = eval;"
5948 " return function(s) { return e(s); }"
5949 "})()");
5950 context0->Exit();
5951
5952 // Put the function into context1 and call it before and after
5953 // detaching the global. Before detaching, the call succeeds and
5954 // after detaching and exception is thrown.
5955 context1->Enter();
5956 context1->Global()->Set(v8_str("fun"), fun);
5957 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
5958 CHECK_EQ(42, x_value->Int32Value());
5959 context0->DetachGlobal();
5960 v8::TryCatch catcher;
5961 x_value = CompileRun("fun('x')");
5962 CHECK(x_value.IsEmpty());
5963 CHECK(catcher.HasCaught());
5964 context1->Exit();
5965
5966 context1.Dispose();
5967 context0.Dispose();
5968}
5969
5970
5971THREADED_TEST(CrossLazyLoad) {
5972 v8::HandleScope scope;
5973 LocalContext other;
5974 LocalContext current;
5975
5976 Local<String> token = v8_str("<security token>");
5977 other->SetSecurityToken(token);
5978 current->SetSecurityToken(token);
5979
5980 // Setup reference from current to other.
5981 current->Global()->Set(v8_str("other"), other->Global());
5982
5983 // Trigger lazy loading in other context.
5984 Local<Script> script =
5985 Script::Compile(v8_str("other.eval('new Date(42)')"));
5986 Local<Value> value = script->Run();
5987 CHECK_EQ(42.0, value->NumberValue());
5988}
5989
5990
5991static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00005992 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00005993 if (args.IsConstructCall()) {
5994 if (args[0]->IsInt32()) {
5995 return v8_num(-args[0]->Int32Value());
5996 }
5997 }
5998
5999 return args[0];
6000}
6001
6002
6003// Test that a call handler can be set for objects which will allow
6004// non-function objects created through the API to be called as
6005// functions.
6006THREADED_TEST(CallAsFunction) {
6007 v8::HandleScope scope;
6008 LocalContext context;
6009
6010 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
6011 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
6012 instance_template->SetCallAsFunctionHandler(call_as_function);
6013 Local<v8::Object> instance = t->GetFunction()->NewInstance();
6014 context->Global()->Set(v8_str("obj"), instance);
6015 v8::TryCatch try_catch;
6016 Local<Value> value;
6017 CHECK(!try_catch.HasCaught());
6018
6019 value = CompileRun("obj(42)");
6020 CHECK(!try_catch.HasCaught());
6021 CHECK_EQ(42, value->Int32Value());
6022
6023 value = CompileRun("(function(o){return o(49)})(obj)");
6024 CHECK(!try_catch.HasCaught());
6025 CHECK_EQ(49, value->Int32Value());
6026
6027 // test special case of call as function
6028 value = CompileRun("[obj]['0'](45)");
6029 CHECK(!try_catch.HasCaught());
6030 CHECK_EQ(45, value->Int32Value());
6031
6032 value = CompileRun("obj.call = Function.prototype.call;"
6033 "obj.call(null, 87)");
6034 CHECK(!try_catch.HasCaught());
6035 CHECK_EQ(87, value->Int32Value());
6036
6037 // Regression tests for bug #1116356: Calling call through call/apply
6038 // must work for non-function receivers.
6039 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6040 value = CompileRun(apply_99);
6041 CHECK(!try_catch.HasCaught());
6042 CHECK_EQ(99, value->Int32Value());
6043
6044 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6045 value = CompileRun(call_17);
6046 CHECK(!try_catch.HasCaught());
6047 CHECK_EQ(17, value->Int32Value());
6048
6049 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006050 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006051 value = CompileRun("new obj(43)");
6052 CHECK(!try_catch.HasCaught());
6053 CHECK_EQ(-43, value->Int32Value());
6054}
6055
6056
6057static int CountHandles() {
6058 return v8::HandleScope::NumberOfHandles();
6059}
6060
6061
6062static int Recurse(int depth, int iterations) {
6063 v8::HandleScope scope;
6064 if (depth == 0) return CountHandles();
6065 for (int i = 0; i < iterations; i++) {
6066 Local<v8::Number> n = v8::Integer::New(42);
6067 }
6068 return Recurse(depth - 1, iterations);
6069}
6070
6071
6072THREADED_TEST(HandleIteration) {
6073 static const int kIterations = 500;
6074 static const int kNesting = 200;
6075 CHECK_EQ(0, CountHandles());
6076 {
6077 v8::HandleScope scope1;
6078 CHECK_EQ(0, CountHandles());
6079 for (int i = 0; i < kIterations; i++) {
6080 Local<v8::Number> n = v8::Integer::New(42);
6081 CHECK_EQ(i + 1, CountHandles());
6082 }
6083
6084 CHECK_EQ(kIterations, CountHandles());
6085 {
6086 v8::HandleScope scope2;
6087 for (int j = 0; j < kIterations; j++) {
6088 Local<v8::Number> n = v8::Integer::New(42);
6089 CHECK_EQ(j + 1 + kIterations, CountHandles());
6090 }
6091 }
6092 CHECK_EQ(kIterations, CountHandles());
6093 }
6094 CHECK_EQ(0, CountHandles());
6095 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6096}
6097
6098
6099static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6100 Local<String> name,
6101 const AccessorInfo& info) {
6102 ApiTestFuzzer::Fuzz();
6103 return v8::Handle<Value>();
6104}
6105
6106
6107THREADED_TEST(InterceptorHasOwnProperty) {
6108 v8::HandleScope scope;
6109 LocalContext context;
6110 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6111 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6112 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6113 Local<Function> function = fun_templ->GetFunction();
6114 context->Global()->Set(v8_str("constructor"), function);
6115 v8::Handle<Value> value = CompileRun(
6116 "var o = new constructor();"
6117 "o.hasOwnProperty('ostehaps');");
6118 CHECK_EQ(false, value->BooleanValue());
6119 value = CompileRun(
6120 "o.ostehaps = 42;"
6121 "o.hasOwnProperty('ostehaps');");
6122 CHECK_EQ(true, value->BooleanValue());
6123 value = CompileRun(
6124 "var p = new constructor();"
6125 "p.hasOwnProperty('ostehaps');");
6126 CHECK_EQ(false, value->BooleanValue());
6127}
6128
6129
6130static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6131 Local<String> name,
6132 const AccessorInfo& info) {
6133 ApiTestFuzzer::Fuzz();
6134 i::Heap::CollectAllGarbage(false);
6135 return v8::Handle<Value>();
6136}
6137
6138
6139THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6140 v8::HandleScope scope;
6141 LocalContext context;
6142 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6143 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6144 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6145 Local<Function> function = fun_templ->GetFunction();
6146 context->Global()->Set(v8_str("constructor"), function);
6147 // Let's first make some stuff so we can be sure to get a good GC.
6148 CompileRun(
6149 "function makestr(size) {"
6150 " switch (size) {"
6151 " case 1: return 'f';"
6152 " case 2: return 'fo';"
6153 " case 3: return 'foo';"
6154 " }"
6155 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6156 "}"
6157 "var x = makestr(12345);"
6158 "x = makestr(31415);"
6159 "x = makestr(23456);");
6160 v8::Handle<Value> value = CompileRun(
6161 "var o = new constructor();"
6162 "o.__proto__ = new String(x);"
6163 "o.hasOwnProperty('ostehaps');");
6164 CHECK_EQ(false, value->BooleanValue());
6165}
6166
6167
6168typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6169 const AccessorInfo& info);
6170
6171
6172static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6173 const char* source,
6174 int expected) {
6175 v8::HandleScope scope;
6176 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6177 templ->SetNamedPropertyHandler(getter);
6178 LocalContext context;
6179 context->Global()->Set(v8_str("o"), templ->NewInstance());
6180 v8::Handle<Value> value = CompileRun(source);
6181 CHECK_EQ(expected, value->Int32Value());
6182}
6183
6184
6185static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6186 const AccessorInfo& info) {
6187 ApiTestFuzzer::Fuzz();
6188 CHECK(v8_str("x")->Equals(name));
6189 return v8::Integer::New(42);
6190}
6191
6192
6193// This test should hit the load IC for the interceptor case.
6194THREADED_TEST(InterceptorLoadIC) {
6195 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6196 "var result = 0;"
6197 "for (var i = 0; i < 1000; i++) {"
6198 " result = o.x;"
6199 "}",
6200 42);
6201}
6202
6203
6204// Below go several tests which verify that JITing for various
6205// configurations of interceptor and explicit fields works fine
6206// (those cases are special cased to get better performance).
6207
6208static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6209 const AccessorInfo& info) {
6210 ApiTestFuzzer::Fuzz();
6211 return v8_str("x")->Equals(name)
6212 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6213}
6214
6215
6216THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6217 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6218 "var result = 0;"
6219 "o.y = 239;"
6220 "for (var i = 0; i < 1000; i++) {"
6221 " result = o.y;"
6222 "}",
6223 239);
6224}
6225
6226
6227THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6228 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6229 "var result = 0;"
6230 "o.__proto__ = { 'y': 239 };"
6231 "for (var i = 0; i < 1000; i++) {"
6232 " result = o.y + o.x;"
6233 "}",
6234 239 + 42);
6235}
6236
6237
6238THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
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(InterceptorLoadICUndefined) {
6250 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6251 "var result = 0;"
6252 "for (var i = 0; i < 1000; i++) {"
6253 " result = (o.y == undefined) ? 239 : 42;"
6254 "}",
6255 239);
6256}
6257
6258
6259THREADED_TEST(InterceptorLoadICWithOverride) {
6260 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6261 "fst = new Object(); fst.__proto__ = o;"
6262 "snd = new Object(); snd.__proto__ = fst;"
6263 "var result1 = 0;"
6264 "for (var i = 0; i < 1000; i++) {"
6265 " result1 = snd.x;"
6266 "}"
6267 "fst.x = 239;"
6268 "var result = 0;"
6269 "for (var i = 0; i < 1000; i++) {"
6270 " result = snd.x;"
6271 "}"
6272 "result + result1",
6273 239 + 42);
6274}
6275
6276
6277// Test the case when we stored field into
6278// a stub, but interceptor produced value on its own.
6279THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6280 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6281 "proto = new Object();"
6282 "o.__proto__ = proto;"
6283 "proto.x = 239;"
6284 "for (var i = 0; i < 1000; i++) {"
6285 " o.x;"
6286 // Now it should be ICed and keep a reference to x defined on proto
6287 "}"
6288 "var result = 0;"
6289 "for (var i = 0; i < 1000; i++) {"
6290 " result += o.x;"
6291 "}"
6292 "result;",
6293 42 * 1000);
6294}
6295
6296
6297// Test the case when we stored field into
6298// a stub, but it got invalidated later on.
6299THREADED_TEST(InterceptorLoadICInvalidatedField) {
6300 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6301 "proto1 = new Object();"
6302 "proto2 = new Object();"
6303 "o.__proto__ = proto1;"
6304 "proto1.__proto__ = proto2;"
6305 "proto2.y = 239;"
6306 "for (var i = 0; i < 1000; i++) {"
6307 " o.y;"
6308 // Now it should be ICed and keep a reference to y defined on proto2
6309 "}"
6310 "proto1.y = 42;"
6311 "var result = 0;"
6312 "for (var i = 0; i < 1000; i++) {"
6313 " result += o.y;"
6314 "}"
6315 "result;",
6316 42 * 1000);
6317}
6318
6319
Steve Block6ded16b2010-05-10 14:33:55 +01006320static int interceptor_load_not_handled_calls = 0;
6321static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6322 const AccessorInfo& info) {
6323 ++interceptor_load_not_handled_calls;
6324 return v8::Handle<v8::Value>();
6325}
6326
6327
6328// Test how post-interceptor lookups are done in the non-cacheable
6329// case: the interceptor should not be invoked during this lookup.
6330THREADED_TEST(InterceptorLoadICPostInterceptor) {
6331 interceptor_load_not_handled_calls = 0;
6332 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6333 "receiver = new Object();"
6334 "receiver.__proto__ = o;"
6335 "proto = new Object();"
6336 "/* Make proto a slow-case object. */"
6337 "for (var i = 0; i < 1000; i++) {"
6338 " proto[\"xxxxxxxx\" + i] = [];"
6339 "}"
6340 "proto.x = 17;"
6341 "o.__proto__ = proto;"
6342 "var result = 0;"
6343 "for (var i = 0; i < 1000; i++) {"
6344 " result += receiver.x;"
6345 "}"
6346 "result;",
6347 17 * 1000);
6348 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6349}
6350
6351
Steve Blocka7e24c12009-10-30 11:49:00 +00006352// Test the case when we stored field into
6353// a stub, but it got invalidated later on due to override on
6354// global object which is between interceptor and fields' holders.
6355THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6356 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6357 "o.__proto__ = this;" // set a global to be a proto of o.
6358 "this.__proto__.y = 239;"
6359 "for (var i = 0; i < 10; i++) {"
6360 " if (o.y != 239) throw 'oops: ' + o.y;"
6361 // Now it should be ICed and keep a reference to y defined on field_holder.
6362 "}"
6363 "this.y = 42;" // Assign on a global.
6364 "var result = 0;"
6365 "for (var i = 0; i < 10; i++) {"
6366 " result += o.y;"
6367 "}"
6368 "result;",
6369 42 * 10);
6370}
6371
6372
6373static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
6374 ApiTestFuzzer::Fuzz();
6375 return v8_num(239);
6376}
6377
6378
6379static void SetOnThis(Local<String> name,
6380 Local<Value> value,
6381 const AccessorInfo& info) {
6382 info.This()->ForceSet(name, value);
6383}
6384
6385
6386THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6387 v8::HandleScope scope;
6388 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6389 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6390 templ->SetAccessor(v8_str("y"), Return239);
6391 LocalContext context;
6392 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006393
6394 // Check the case when receiver and interceptor's holder
6395 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006396 v8::Handle<Value> value = CompileRun(
6397 "var result = 0;"
6398 "for (var i = 0; i < 7; i++) {"
6399 " result = o.y;"
6400 "}");
6401 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006402
6403 // Check the case when interceptor's holder is in proto chain
6404 // of receiver.
6405 value = CompileRun(
6406 "r = { __proto__: o };"
6407 "var result = 0;"
6408 "for (var i = 0; i < 7; i++) {"
6409 " result = r.y;"
6410 "}");
6411 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006412}
6413
6414
6415THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6416 v8::HandleScope scope;
6417 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6418 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6419 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6420 templ_p->SetAccessor(v8_str("y"), Return239);
6421
6422 LocalContext context;
6423 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6424 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6425
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006426 // Check the case when receiver and interceptor's holder
6427 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006428 v8::Handle<Value> value = CompileRun(
6429 "o.__proto__ = p;"
6430 "var result = 0;"
6431 "for (var i = 0; i < 7; i++) {"
6432 " result = o.x + o.y;"
6433 "}");
6434 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006435
6436 // Check the case when interceptor's holder is in proto chain
6437 // of receiver.
6438 value = CompileRun(
6439 "r = { __proto__: o };"
6440 "var result = 0;"
6441 "for (var i = 0; i < 7; i++) {"
6442 " result = r.x + r.y;"
6443 "}");
6444 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006445}
6446
6447
6448THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6449 v8::HandleScope scope;
6450 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6451 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6452 templ->SetAccessor(v8_str("y"), Return239);
6453
6454 LocalContext context;
6455 context->Global()->Set(v8_str("o"), templ->NewInstance());
6456
6457 v8::Handle<Value> value = CompileRun(
6458 "fst = new Object(); fst.__proto__ = o;"
6459 "snd = new Object(); snd.__proto__ = fst;"
6460 "var result1 = 0;"
6461 "for (var i = 0; i < 7; i++) {"
6462 " result1 = snd.x;"
6463 "}"
6464 "fst.x = 239;"
6465 "var result = 0;"
6466 "for (var i = 0; i < 7; i++) {"
6467 " result = snd.x;"
6468 "}"
6469 "result + result1");
6470 CHECK_EQ(239 + 42, value->Int32Value());
6471}
6472
6473
6474// Test the case when we stored callback into
6475// a stub, but interceptor produced value on its own.
6476THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6477 v8::HandleScope scope;
6478 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6479 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6480 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6481 templ_p->SetAccessor(v8_str("y"), Return239);
6482
6483 LocalContext context;
6484 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6485 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6486
6487 v8::Handle<Value> value = CompileRun(
6488 "o.__proto__ = p;"
6489 "for (var i = 0; i < 7; i++) {"
6490 " o.x;"
6491 // Now it should be ICed and keep a reference to x defined on p
6492 "}"
6493 "var result = 0;"
6494 "for (var i = 0; i < 7; i++) {"
6495 " result += o.x;"
6496 "}"
6497 "result");
6498 CHECK_EQ(42 * 7, value->Int32Value());
6499}
6500
6501
6502// Test the case when we stored callback into
6503// a stub, but it got invalidated later on.
6504THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6505 v8::HandleScope scope;
6506 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6507 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6508 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6509 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6510
6511 LocalContext context;
6512 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6513 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6514
6515 v8::Handle<Value> value = CompileRun(
6516 "inbetween = new Object();"
6517 "o.__proto__ = inbetween;"
6518 "inbetween.__proto__ = p;"
6519 "for (var i = 0; i < 10; i++) {"
6520 " o.y;"
6521 // Now it should be ICed and keep a reference to y defined on p
6522 "}"
6523 "inbetween.y = 42;"
6524 "var result = 0;"
6525 "for (var i = 0; i < 10; i++) {"
6526 " result += o.y;"
6527 "}"
6528 "result");
6529 CHECK_EQ(42 * 10, value->Int32Value());
6530}
6531
6532
6533// Test the case when we stored callback into
6534// a stub, but it got invalidated later on due to override on
6535// global object which is between interceptor and callbacks' holders.
6536THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6537 v8::HandleScope scope;
6538 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6539 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6540 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6541 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6542
6543 LocalContext context;
6544 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6545 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6546
6547 v8::Handle<Value> value = CompileRun(
6548 "o.__proto__ = this;"
6549 "this.__proto__ = p;"
6550 "for (var i = 0; i < 10; i++) {"
6551 " if (o.y != 239) throw 'oops: ' + o.y;"
6552 // Now it should be ICed and keep a reference to y defined on p
6553 "}"
6554 "this.y = 42;"
6555 "var result = 0;"
6556 "for (var i = 0; i < 10; i++) {"
6557 " result += o.y;"
6558 "}"
6559 "result");
6560 CHECK_EQ(42 * 10, value->Int32Value());
6561}
6562
6563
6564static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6565 const AccessorInfo& info) {
6566 ApiTestFuzzer::Fuzz();
6567 CHECK(v8_str("x")->Equals(name));
6568 return v8::Integer::New(0);
6569}
6570
6571
6572THREADED_TEST(InterceptorReturningZero) {
6573 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6574 "o.x == undefined ? 1 : 0",
6575 0);
6576}
6577
6578
6579static v8::Handle<Value> InterceptorStoreICSetter(
6580 Local<String> key, Local<Value> value, const AccessorInfo&) {
6581 CHECK(v8_str("x")->Equals(key));
6582 CHECK_EQ(42, value->Int32Value());
6583 return value;
6584}
6585
6586
6587// This test should hit the store IC for the interceptor case.
6588THREADED_TEST(InterceptorStoreIC) {
6589 v8::HandleScope scope;
6590 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6591 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6592 InterceptorStoreICSetter);
6593 LocalContext context;
6594 context->Global()->Set(v8_str("o"), templ->NewInstance());
6595 v8::Handle<Value> value = CompileRun(
6596 "for (var i = 0; i < 1000; i++) {"
6597 " o.x = 42;"
6598 "}");
6599}
6600
6601
6602THREADED_TEST(InterceptorStoreICWithNoSetter) {
6603 v8::HandleScope scope;
6604 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6605 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6606 LocalContext context;
6607 context->Global()->Set(v8_str("o"), templ->NewInstance());
6608 v8::Handle<Value> value = CompileRun(
6609 "for (var i = 0; i < 1000; i++) {"
6610 " o.y = 239;"
6611 "}"
6612 "42 + o.y");
6613 CHECK_EQ(239 + 42, value->Int32Value());
6614}
6615
6616
6617
6618
6619v8::Handle<Value> call_ic_function;
6620v8::Handle<Value> call_ic_function2;
6621v8::Handle<Value> call_ic_function3;
6622
6623static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6624 const AccessorInfo& info) {
6625 ApiTestFuzzer::Fuzz();
6626 CHECK(v8_str("x")->Equals(name));
6627 return call_ic_function;
6628}
6629
6630
6631// This test should hit the call IC for the interceptor case.
6632THREADED_TEST(InterceptorCallIC) {
6633 v8::HandleScope scope;
6634 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6635 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6636 LocalContext context;
6637 context->Global()->Set(v8_str("o"), templ->NewInstance());
6638 call_ic_function =
6639 v8_compile("function f(x) { return x + 1; }; f")->Run();
6640 v8::Handle<Value> value = CompileRun(
6641 "var result = 0;"
6642 "for (var i = 0; i < 1000; i++) {"
6643 " result = o.x(41);"
6644 "}");
6645 CHECK_EQ(42, value->Int32Value());
6646}
6647
6648
6649// This test checks that if interceptor doesn't provide
6650// a value, we can fetch regular value.
6651THREADED_TEST(InterceptorCallICSeesOthers) {
6652 v8::HandleScope scope;
6653 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6654 templ->SetNamedPropertyHandler(NoBlockGetterX);
6655 LocalContext context;
6656 context->Global()->Set(v8_str("o"), templ->NewInstance());
6657 v8::Handle<Value> value = CompileRun(
6658 "o.x = function f(x) { return x + 1; };"
6659 "var result = 0;"
6660 "for (var i = 0; i < 7; i++) {"
6661 " result = o.x(41);"
6662 "}");
6663 CHECK_EQ(42, value->Int32Value());
6664}
6665
6666
6667static v8::Handle<Value> call_ic_function4;
6668static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6669 const AccessorInfo& info) {
6670 ApiTestFuzzer::Fuzz();
6671 CHECK(v8_str("x")->Equals(name));
6672 return call_ic_function4;
6673}
6674
6675
6676// This test checks that if interceptor provides a function,
6677// even if we cached shadowed variant, interceptor's function
6678// is invoked
6679THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6680 v8::HandleScope scope;
6681 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6682 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6683 LocalContext context;
6684 context->Global()->Set(v8_str("o"), templ->NewInstance());
6685 call_ic_function4 =
6686 v8_compile("function f(x) { return x - 1; }; f")->Run();
6687 v8::Handle<Value> value = CompileRun(
6688 "o.__proto__.x = function(x) { return x + 1; };"
6689 "var result = 0;"
6690 "for (var i = 0; i < 1000; i++) {"
6691 " result = o.x(42);"
6692 "}");
6693 CHECK_EQ(41, value->Int32Value());
6694}
6695
6696
6697// Test the case when we stored cacheable lookup into
6698// a stub, but it got invalidated later on
6699THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6700 v8::HandleScope scope;
6701 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6702 templ->SetNamedPropertyHandler(NoBlockGetterX);
6703 LocalContext context;
6704 context->Global()->Set(v8_str("o"), templ->NewInstance());
6705 v8::Handle<Value> value = CompileRun(
6706 "proto1 = new Object();"
6707 "proto2 = new Object();"
6708 "o.__proto__ = proto1;"
6709 "proto1.__proto__ = proto2;"
6710 "proto2.y = function(x) { return x + 1; };"
6711 // Invoke it many times to compile a stub
6712 "for (var i = 0; i < 7; i++) {"
6713 " o.y(42);"
6714 "}"
6715 "proto1.y = function(x) { return x - 1; };"
6716 "var result = 0;"
6717 "for (var i = 0; i < 7; i++) {"
6718 " result += o.y(42);"
6719 "}");
6720 CHECK_EQ(41 * 7, value->Int32Value());
6721}
6722
6723
6724static v8::Handle<Value> call_ic_function5;
6725static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6726 const AccessorInfo& info) {
6727 ApiTestFuzzer::Fuzz();
6728 if (v8_str("x")->Equals(name))
6729 return call_ic_function5;
6730 else
6731 return Local<Value>();
6732}
6733
6734
6735// This test checks that if interceptor doesn't provide a function,
6736// cached constant function is used
6737THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6738 v8::HandleScope scope;
6739 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6740 templ->SetNamedPropertyHandler(NoBlockGetterX);
6741 LocalContext context;
6742 context->Global()->Set(v8_str("o"), templ->NewInstance());
6743 v8::Handle<Value> value = CompileRun(
6744 "function inc(x) { return x + 1; };"
6745 "inc(1);"
6746 "o.x = inc;"
6747 "var result = 0;"
6748 "for (var i = 0; i < 1000; i++) {"
6749 " result = o.x(42);"
6750 "}");
6751 CHECK_EQ(43, value->Int32Value());
6752}
6753
6754
6755// This test checks that if interceptor provides a function,
6756// even if we cached constant function, interceptor's function
6757// is invoked
6758THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6759 v8::HandleScope scope;
6760 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6761 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6762 LocalContext context;
6763 context->Global()->Set(v8_str("o"), templ->NewInstance());
6764 call_ic_function5 =
6765 v8_compile("function f(x) { return x - 1; }; f")->Run();
6766 v8::Handle<Value> value = CompileRun(
6767 "function inc(x) { return x + 1; };"
6768 "inc(1);"
6769 "o.x = inc;"
6770 "var result = 0;"
6771 "for (var i = 0; i < 1000; i++) {"
6772 " result = o.x(42);"
6773 "}");
6774 CHECK_EQ(41, value->Int32Value());
6775}
6776
6777
6778// Test the case when we stored constant function into
6779// a stub, but it got invalidated later on
6780THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6781 v8::HandleScope scope;
6782 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6783 templ->SetNamedPropertyHandler(NoBlockGetterX);
6784 LocalContext context;
6785 context->Global()->Set(v8_str("o"), templ->NewInstance());
6786 v8::Handle<Value> value = CompileRun(
6787 "function inc(x) { return x + 1; };"
6788 "inc(1);"
6789 "proto1 = new Object();"
6790 "proto2 = new Object();"
6791 "o.__proto__ = proto1;"
6792 "proto1.__proto__ = proto2;"
6793 "proto2.y = inc;"
6794 // Invoke it many times to compile a stub
6795 "for (var i = 0; i < 7; i++) {"
6796 " o.y(42);"
6797 "}"
6798 "proto1.y = function(x) { return x - 1; };"
6799 "var result = 0;"
6800 "for (var i = 0; i < 7; i++) {"
6801 " result += o.y(42);"
6802 "}");
6803 CHECK_EQ(41 * 7, value->Int32Value());
6804}
6805
6806
6807// Test the case when we stored constant function into
6808// a stub, but it got invalidated later on due to override on
6809// global object which is between interceptor and constant function' holders.
6810THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6811 v8::HandleScope scope;
6812 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6813 templ->SetNamedPropertyHandler(NoBlockGetterX);
6814 LocalContext context;
6815 context->Global()->Set(v8_str("o"), templ->NewInstance());
6816 v8::Handle<Value> value = CompileRun(
6817 "function inc(x) { return x + 1; };"
6818 "inc(1);"
6819 "o.__proto__ = this;"
6820 "this.__proto__.y = inc;"
6821 // Invoke it many times to compile a stub
6822 "for (var i = 0; i < 7; i++) {"
6823 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6824 "}"
6825 "this.y = function(x) { return x - 1; };"
6826 "var result = 0;"
6827 "for (var i = 0; i < 7; i++) {"
6828 " result += o.y(42);"
6829 "}");
6830 CHECK_EQ(41 * 7, value->Int32Value());
6831}
6832
6833
Leon Clarke4515c472010-02-03 11:58:03 +00006834// Test the case when actual function to call sits on global object.
6835THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6836 v8::HandleScope scope;
6837 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6838 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6839
6840 LocalContext context;
6841 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6842
6843 v8::Handle<Value> value = CompileRun(
6844 "try {"
6845 " o.__proto__ = this;"
6846 " for (var i = 0; i < 10; i++) {"
6847 " var v = o.parseFloat('239');"
6848 " if (v != 239) throw v;"
6849 // Now it should be ICed and keep a reference to parseFloat.
6850 " }"
6851 " var result = 0;"
6852 " for (var i = 0; i < 10; i++) {"
6853 " result += o.parseFloat('239');"
6854 " }"
6855 " result"
6856 "} catch(e) {"
6857 " e"
6858 "};");
6859 CHECK_EQ(239 * 10, value->Int32Value());
6860}
6861
Andrei Popescu402d9372010-02-26 13:31:12 +00006862static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6863 const AccessorInfo& info) {
6864 ApiTestFuzzer::Fuzz();
6865 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6866 ++(*call_count);
6867 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01006868 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00006869 }
6870 return v8::Handle<Value>();
6871}
6872
6873static v8::Handle<Value> FastApiCallback_TrivialSignature(
6874 const v8::Arguments& args) {
6875 ApiTestFuzzer::Fuzz();
6876 CHECK_EQ(args.This(), args.Holder());
6877 CHECK(args.Data()->Equals(v8_str("method_data")));
6878 return v8::Integer::New(args[0]->Int32Value() + 1);
6879}
6880
6881static v8::Handle<Value> FastApiCallback_SimpleSignature(
6882 const v8::Arguments& args) {
6883 ApiTestFuzzer::Fuzz();
6884 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6885 CHECK(args.Data()->Equals(v8_str("method_data")));
6886 // Note, we're using HasRealNamedProperty instead of Has to avoid
6887 // invoking the interceptor again.
6888 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6889 return v8::Integer::New(args[0]->Int32Value() + 1);
6890}
6891
6892// Helper to maximize the odds of object moving.
6893static void GenerateSomeGarbage() {
6894 CompileRun(
6895 "var garbage;"
6896 "for (var i = 0; i < 1000; i++) {"
6897 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6898 "}"
6899 "garbage = undefined;");
6900}
6901
6902THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
6903 int interceptor_call_count = 0;
6904 v8::HandleScope scope;
6905 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6906 v8::Handle<v8::FunctionTemplate> method_templ =
6907 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6908 v8_str("method_data"),
6909 v8::Handle<v8::Signature>());
6910 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6911 proto_templ->Set(v8_str("method"), method_templ);
6912 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6913 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6914 NULL, NULL, NULL, NULL,
6915 v8::External::Wrap(&interceptor_call_count));
6916 LocalContext context;
6917 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6918 GenerateSomeGarbage();
6919 context->Global()->Set(v8_str("o"), fun->NewInstance());
6920 v8::Handle<Value> value = CompileRun(
6921 "var result = 0;"
6922 "for (var i = 0; i < 100; i++) {"
6923 " result = o.method(41);"
6924 "}");
6925 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6926 CHECK_EQ(100, interceptor_call_count);
6927}
6928
6929THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
6930 int interceptor_call_count = 0;
6931 v8::HandleScope scope;
6932 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6933 v8::Handle<v8::FunctionTemplate> method_templ =
6934 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6935 v8_str("method_data"),
6936 v8::Signature::New(fun_templ));
6937 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6938 proto_templ->Set(v8_str("method"), method_templ);
6939 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6940 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6941 NULL, NULL, NULL, NULL,
6942 v8::External::Wrap(&interceptor_call_count));
6943 LocalContext context;
6944 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6945 GenerateSomeGarbage();
6946 context->Global()->Set(v8_str("o"), fun->NewInstance());
6947 v8::Handle<Value> value = CompileRun(
6948 "o.foo = 17;"
6949 "var receiver = {};"
6950 "receiver.__proto__ = o;"
6951 "var result = 0;"
6952 "for (var i = 0; i < 100; i++) {"
6953 " result = receiver.method(41);"
6954 "}");
6955 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6956 CHECK_EQ(100, interceptor_call_count);
6957}
6958
6959THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
6960 int interceptor_call_count = 0;
6961 v8::HandleScope scope;
6962 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6963 v8::Handle<v8::FunctionTemplate> method_templ =
6964 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6965 v8_str("method_data"),
6966 v8::Signature::New(fun_templ));
6967 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6968 proto_templ->Set(v8_str("method"), method_templ);
6969 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6970 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6971 NULL, NULL, NULL, NULL,
6972 v8::External::Wrap(&interceptor_call_count));
6973 LocalContext context;
6974 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6975 GenerateSomeGarbage();
6976 context->Global()->Set(v8_str("o"), fun->NewInstance());
6977 v8::Handle<Value> value = CompileRun(
6978 "o.foo = 17;"
6979 "var receiver = {};"
6980 "receiver.__proto__ = o;"
6981 "var result = 0;"
6982 "var saved_result = 0;"
6983 "for (var i = 0; i < 100; i++) {"
6984 " result = receiver.method(41);"
6985 " if (i == 50) {"
6986 " saved_result = result;"
6987 " receiver = {method: function(x) { return x - 1 }};"
6988 " }"
6989 "}");
6990 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6991 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6992 CHECK_GE(interceptor_call_count, 50);
6993}
6994
6995THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
6996 int interceptor_call_count = 0;
6997 v8::HandleScope scope;
6998 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6999 v8::Handle<v8::FunctionTemplate> method_templ =
7000 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7001 v8_str("method_data"),
7002 v8::Signature::New(fun_templ));
7003 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7004 proto_templ->Set(v8_str("method"), method_templ);
7005 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7006 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7007 NULL, NULL, NULL, NULL,
7008 v8::External::Wrap(&interceptor_call_count));
7009 LocalContext context;
7010 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7011 GenerateSomeGarbage();
7012 context->Global()->Set(v8_str("o"), fun->NewInstance());
7013 v8::Handle<Value> value = CompileRun(
7014 "o.foo = 17;"
7015 "var receiver = {};"
7016 "receiver.__proto__ = o;"
7017 "var result = 0;"
7018 "var saved_result = 0;"
7019 "for (var i = 0; i < 100; i++) {"
7020 " result = receiver.method(41);"
7021 " if (i == 50) {"
7022 " saved_result = result;"
7023 " o.method = function(x) { return x - 1 };"
7024 " }"
7025 "}");
7026 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7027 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7028 CHECK_GE(interceptor_call_count, 50);
7029}
7030
Steve Block6ded16b2010-05-10 14:33:55 +01007031THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7032 int interceptor_call_count = 0;
7033 v8::HandleScope scope;
7034 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7035 v8::Handle<v8::FunctionTemplate> method_templ =
7036 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7037 v8_str("method_data"),
7038 v8::Signature::New(fun_templ));
7039 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7040 proto_templ->Set(v8_str("method"), method_templ);
7041 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7042 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7043 NULL, NULL, NULL, NULL,
7044 v8::External::Wrap(&interceptor_call_count));
7045 LocalContext context;
7046 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7047 GenerateSomeGarbage();
7048 context->Global()->Set(v8_str("o"), fun->NewInstance());
7049 v8::TryCatch try_catch;
7050 v8::Handle<Value> value = CompileRun(
7051 "o.foo = 17;"
7052 "var receiver = {};"
7053 "receiver.__proto__ = o;"
7054 "var result = 0;"
7055 "var saved_result = 0;"
7056 "for (var i = 0; i < 100; i++) {"
7057 " result = receiver.method(41);"
7058 " if (i == 50) {"
7059 " saved_result = result;"
7060 " receiver = 333;"
7061 " }"
7062 "}");
7063 CHECK(try_catch.HasCaught());
7064 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7065 try_catch.Exception()->ToString());
7066 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7067 CHECK_GE(interceptor_call_count, 50);
7068}
7069
Andrei Popescu402d9372010-02-26 13:31:12 +00007070THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
7071 int interceptor_call_count = 0;
7072 v8::HandleScope scope;
7073 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7074 v8::Handle<v8::FunctionTemplate> method_templ =
7075 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7076 v8_str("method_data"),
7077 v8::Signature::New(fun_templ));
7078 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7079 proto_templ->Set(v8_str("method"), method_templ);
7080 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7081 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7082 NULL, NULL, NULL, NULL,
7083 v8::External::Wrap(&interceptor_call_count));
7084 LocalContext context;
7085 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7086 GenerateSomeGarbage();
7087 context->Global()->Set(v8_str("o"), fun->NewInstance());
7088 v8::TryCatch try_catch;
7089 v8::Handle<Value> value = CompileRun(
7090 "o.foo = 17;"
7091 "var receiver = {};"
7092 "receiver.__proto__ = o;"
7093 "var result = 0;"
7094 "var saved_result = 0;"
7095 "for (var i = 0; i < 100; i++) {"
7096 " result = receiver.method(41);"
7097 " if (i == 50) {"
7098 " saved_result = result;"
7099 " receiver = {method: receiver.method};"
7100 " }"
7101 "}");
7102 CHECK(try_catch.HasCaught());
7103 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7104 try_catch.Exception()->ToString());
7105 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7106 CHECK_GE(interceptor_call_count, 50);
7107}
7108
7109THREADED_TEST(CallICFastApi_TrivialSignature) {
7110 v8::HandleScope scope;
7111 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7112 v8::Handle<v8::FunctionTemplate> method_templ =
7113 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7114 v8_str("method_data"),
7115 v8::Handle<v8::Signature>());
7116 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7117 proto_templ->Set(v8_str("method"), method_templ);
7118 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7119 LocalContext context;
7120 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7121 GenerateSomeGarbage();
7122 context->Global()->Set(v8_str("o"), fun->NewInstance());
7123 v8::Handle<Value> value = CompileRun(
7124 "var result = 0;"
7125 "for (var i = 0; i < 100; i++) {"
7126 " result = o.method(41);"
7127 "}");
7128
7129 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7130}
7131
7132THREADED_TEST(CallICFastApi_SimpleSignature) {
7133 v8::HandleScope scope;
7134 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7135 v8::Handle<v8::FunctionTemplate> method_templ =
7136 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7137 v8_str("method_data"),
7138 v8::Signature::New(fun_templ));
7139 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7140 proto_templ->Set(v8_str("method"), method_templ);
7141 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7142 LocalContext context;
7143 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7144 GenerateSomeGarbage();
7145 context->Global()->Set(v8_str("o"), fun->NewInstance());
7146 v8::Handle<Value> value = CompileRun(
7147 "o.foo = 17;"
7148 "var receiver = {};"
7149 "receiver.__proto__ = o;"
7150 "var result = 0;"
7151 "for (var i = 0; i < 100; i++) {"
7152 " result = receiver.method(41);"
7153 "}");
7154
7155 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7156}
7157
Steve Block6ded16b2010-05-10 14:33:55 +01007158THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007159 v8::HandleScope scope;
7160 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7161 v8::Handle<v8::FunctionTemplate> method_templ =
7162 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7163 v8_str("method_data"),
7164 v8::Signature::New(fun_templ));
7165 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7166 proto_templ->Set(v8_str("method"), method_templ);
7167 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7168 LocalContext context;
7169 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7170 GenerateSomeGarbage();
7171 context->Global()->Set(v8_str("o"), fun->NewInstance());
7172 v8::Handle<Value> value = CompileRun(
7173 "o.foo = 17;"
7174 "var receiver = {};"
7175 "receiver.__proto__ = o;"
7176 "var result = 0;"
7177 "var saved_result = 0;"
7178 "for (var i = 0; i < 100; i++) {"
7179 " result = receiver.method(41);"
7180 " if (i == 50) {"
7181 " saved_result = result;"
7182 " receiver = {method: function(x) { return x - 1 }};"
7183 " }"
7184 "}");
7185 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7186 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7187}
7188
Steve Block6ded16b2010-05-10 14:33:55 +01007189THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7190 v8::HandleScope scope;
7191 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7192 v8::Handle<v8::FunctionTemplate> method_templ =
7193 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7194 v8_str("method_data"),
7195 v8::Signature::New(fun_templ));
7196 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7197 proto_templ->Set(v8_str("method"), method_templ);
7198 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7199 LocalContext context;
7200 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7201 GenerateSomeGarbage();
7202 context->Global()->Set(v8_str("o"), fun->NewInstance());
7203 v8::TryCatch try_catch;
7204 v8::Handle<Value> value = CompileRun(
7205 "o.foo = 17;"
7206 "var receiver = {};"
7207 "receiver.__proto__ = o;"
7208 "var result = 0;"
7209 "var saved_result = 0;"
7210 "for (var i = 0; i < 100; i++) {"
7211 " result = receiver.method(41);"
7212 " if (i == 50) {"
7213 " saved_result = result;"
7214 " receiver = 333;"
7215 " }"
7216 "}");
7217 CHECK(try_catch.HasCaught());
7218 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7219 try_catch.Exception()->ToString());
7220 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7221}
7222
Leon Clarke4515c472010-02-03 11:58:03 +00007223
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007224v8::Handle<Value> keyed_call_ic_function;
7225
7226static v8::Handle<Value> InterceptorKeyedCallICGetter(
7227 Local<String> name, const AccessorInfo& info) {
7228 ApiTestFuzzer::Fuzz();
7229 if (v8_str("x")->Equals(name)) {
7230 return keyed_call_ic_function;
7231 }
7232 return v8::Handle<Value>();
7233}
7234
7235
7236// Test the case when we stored cacheable lookup into
7237// a stub, but the function name changed (to another cacheable function).
7238THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7239 v8::HandleScope scope;
7240 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7241 templ->SetNamedPropertyHandler(NoBlockGetterX);
7242 LocalContext context;
7243 context->Global()->Set(v8_str("o"), templ->NewInstance());
7244 v8::Handle<Value> value = CompileRun(
7245 "proto = new Object();"
7246 "proto.y = function(x) { return x + 1; };"
7247 "proto.z = function(x) { return x - 1; };"
7248 "o.__proto__ = proto;"
7249 "var result = 0;"
7250 "var method = 'y';"
7251 "for (var i = 0; i < 10; i++) {"
7252 " if (i == 5) { method = 'z'; };"
7253 " result += o[method](41);"
7254 "}");
7255 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7256}
7257
7258
7259// Test the case when we stored cacheable lookup into
7260// a stub, but the function name changed (and the new function is present
7261// both before and after the interceptor in the prototype chain).
7262THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7263 v8::HandleScope scope;
7264 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7265 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7266 LocalContext context;
7267 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7268 keyed_call_ic_function =
7269 v8_compile("function f(x) { return x - 1; }; f")->Run();
7270 v8::Handle<Value> value = CompileRun(
7271 "o = new Object();"
7272 "proto2 = new Object();"
7273 "o.y = function(x) { return x + 1; };"
7274 "proto2.y = function(x) { return x + 2; };"
7275 "o.__proto__ = proto1;"
7276 "proto1.__proto__ = proto2;"
7277 "var result = 0;"
7278 "var method = 'x';"
7279 "for (var i = 0; i < 10; i++) {"
7280 " if (i == 5) { method = 'y'; };"
7281 " result += o[method](41);"
7282 "}");
7283 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7284}
7285
7286
7287// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7288// on the global object.
7289THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7290 v8::HandleScope scope;
7291 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7292 templ->SetNamedPropertyHandler(NoBlockGetterX);
7293 LocalContext context;
7294 context->Global()->Set(v8_str("o"), templ->NewInstance());
7295 v8::Handle<Value> value = CompileRun(
7296 "function inc(x) { return x + 1; };"
7297 "inc(1);"
7298 "function dec(x) { return x - 1; };"
7299 "dec(1);"
7300 "o.__proto__ = this;"
7301 "this.__proto__.x = inc;"
7302 "this.__proto__.y = dec;"
7303 "var result = 0;"
7304 "var method = 'x';"
7305 "for (var i = 0; i < 10; i++) {"
7306 " if (i == 5) { method = 'y'; };"
7307 " result += o[method](41);"
7308 "}");
7309 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7310}
7311
7312
7313// Test the case when actual function to call sits on global object.
7314THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7315 v8::HandleScope scope;
7316 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7317 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7318 LocalContext context;
7319 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7320
7321 v8::Handle<Value> value = CompileRun(
7322 "function len(x) { return x.length; };"
7323 "o.__proto__ = this;"
7324 "var m = 'parseFloat';"
7325 "var result = 0;"
7326 "for (var i = 0; i < 10; i++) {"
7327 " if (i == 5) {"
7328 " m = 'len';"
7329 " saved_result = result;"
7330 " };"
7331 " result = o[m]('239');"
7332 "}");
7333 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7334 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7335}
7336
7337// Test the map transition before the interceptor.
7338THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7339 v8::HandleScope scope;
7340 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7341 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7342 LocalContext context;
7343 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7344
7345 v8::Handle<Value> value = CompileRun(
7346 "var o = new Object();"
7347 "o.__proto__ = proto;"
7348 "o.method = function(x) { return x + 1; };"
7349 "var m = 'method';"
7350 "var result = 0;"
7351 "for (var i = 0; i < 10; i++) {"
7352 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7353 " result += o[m](41);"
7354 "}");
7355 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7356}
7357
7358
7359// Test the map transition after the interceptor.
7360THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7361 v8::HandleScope scope;
7362 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7363 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7364 LocalContext context;
7365 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7366
7367 v8::Handle<Value> value = CompileRun(
7368 "var proto = new Object();"
7369 "o.__proto__ = proto;"
7370 "proto.method = function(x) { return x + 1; };"
7371 "var m = 'method';"
7372 "var result = 0;"
7373 "for (var i = 0; i < 10; i++) {"
7374 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7375 " result += o[m](41);"
7376 "}");
7377 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7378}
7379
7380
Steve Blocka7e24c12009-10-30 11:49:00 +00007381static int interceptor_call_count = 0;
7382
7383static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7384 const AccessorInfo& info) {
7385 ApiTestFuzzer::Fuzz();
7386 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7387 return call_ic_function2;
7388 }
7389 return v8::Handle<Value>();
7390}
7391
7392
7393// This test should hit load and call ICs for the interceptor case.
7394// Once in a while, the interceptor will reply that a property was not
7395// found in which case we should get a reference error.
7396THREADED_TEST(InterceptorICReferenceErrors) {
7397 v8::HandleScope scope;
7398 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7399 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7400 LocalContext context(0, templ, v8::Handle<Value>());
7401 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7402 v8::Handle<Value> value = CompileRun(
7403 "function f() {"
7404 " for (var i = 0; i < 1000; i++) {"
7405 " try { x; } catch(e) { return true; }"
7406 " }"
7407 " return false;"
7408 "};"
7409 "f();");
7410 CHECK_EQ(true, value->BooleanValue());
7411 interceptor_call_count = 0;
7412 value = CompileRun(
7413 "function g() {"
7414 " for (var i = 0; i < 1000; i++) {"
7415 " try { x(42); } catch(e) { return true; }"
7416 " }"
7417 " return false;"
7418 "};"
7419 "g();");
7420 CHECK_EQ(true, value->BooleanValue());
7421}
7422
7423
7424static int interceptor_ic_exception_get_count = 0;
7425
7426static v8::Handle<Value> InterceptorICExceptionGetter(
7427 Local<String> name,
7428 const AccessorInfo& info) {
7429 ApiTestFuzzer::Fuzz();
7430 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7431 return call_ic_function3;
7432 }
7433 if (interceptor_ic_exception_get_count == 20) {
7434 return v8::ThrowException(v8_num(42));
7435 }
7436 // Do not handle get for properties other than x.
7437 return v8::Handle<Value>();
7438}
7439
7440// Test interceptor load/call IC where the interceptor throws an
7441// exception once in a while.
7442THREADED_TEST(InterceptorICGetterExceptions) {
7443 interceptor_ic_exception_get_count = 0;
7444 v8::HandleScope scope;
7445 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7446 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7447 LocalContext context(0, templ, v8::Handle<Value>());
7448 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7449 v8::Handle<Value> value = CompileRun(
7450 "function f() {"
7451 " for (var i = 0; i < 100; i++) {"
7452 " try { x; } catch(e) { return true; }"
7453 " }"
7454 " return false;"
7455 "};"
7456 "f();");
7457 CHECK_EQ(true, value->BooleanValue());
7458 interceptor_ic_exception_get_count = 0;
7459 value = CompileRun(
7460 "function f() {"
7461 " for (var i = 0; i < 100; i++) {"
7462 " try { x(42); } catch(e) { return true; }"
7463 " }"
7464 " return false;"
7465 "};"
7466 "f();");
7467 CHECK_EQ(true, value->BooleanValue());
7468}
7469
7470
7471static int interceptor_ic_exception_set_count = 0;
7472
7473static v8::Handle<Value> InterceptorICExceptionSetter(
7474 Local<String> key, Local<Value> value, const AccessorInfo&) {
7475 ApiTestFuzzer::Fuzz();
7476 if (++interceptor_ic_exception_set_count > 20) {
7477 return v8::ThrowException(v8_num(42));
7478 }
7479 // Do not actually handle setting.
7480 return v8::Handle<Value>();
7481}
7482
7483// Test interceptor store IC where the interceptor throws an exception
7484// once in a while.
7485THREADED_TEST(InterceptorICSetterExceptions) {
7486 interceptor_ic_exception_set_count = 0;
7487 v8::HandleScope scope;
7488 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7489 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7490 LocalContext context(0, templ, v8::Handle<Value>());
7491 v8::Handle<Value> value = CompileRun(
7492 "function f() {"
7493 " for (var i = 0; i < 100; i++) {"
7494 " try { x = 42; } catch(e) { return true; }"
7495 " }"
7496 " return false;"
7497 "};"
7498 "f();");
7499 CHECK_EQ(true, value->BooleanValue());
7500}
7501
7502
7503// Test that we ignore null interceptors.
7504THREADED_TEST(NullNamedInterceptor) {
7505 v8::HandleScope scope;
7506 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7507 templ->SetNamedPropertyHandler(0);
7508 LocalContext context;
7509 templ->Set("x", v8_num(42));
7510 v8::Handle<v8::Object> obj = templ->NewInstance();
7511 context->Global()->Set(v8_str("obj"), obj);
7512 v8::Handle<Value> value = CompileRun("obj.x");
7513 CHECK(value->IsInt32());
7514 CHECK_EQ(42, value->Int32Value());
7515}
7516
7517
7518// Test that we ignore null interceptors.
7519THREADED_TEST(NullIndexedInterceptor) {
7520 v8::HandleScope scope;
7521 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7522 templ->SetIndexedPropertyHandler(0);
7523 LocalContext context;
7524 templ->Set("42", v8_num(42));
7525 v8::Handle<v8::Object> obj = templ->NewInstance();
7526 context->Global()->Set(v8_str("obj"), obj);
7527 v8::Handle<Value> value = CompileRun("obj[42]");
7528 CHECK(value->IsInt32());
7529 CHECK_EQ(42, value->Int32Value());
7530}
7531
7532
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007533THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7534 v8::HandleScope scope;
7535 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7536 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7537 LocalContext env;
7538 env->Global()->Set(v8_str("obj"),
7539 templ->GetFunction()->NewInstance());
7540 ExpectTrue("obj.x === 42");
7541 ExpectTrue("!obj.propertyIsEnumerable('x')");
7542}
7543
7544
Steve Blocka7e24c12009-10-30 11:49:00 +00007545static v8::Handle<Value> ParentGetter(Local<String> name,
7546 const AccessorInfo& info) {
7547 ApiTestFuzzer::Fuzz();
7548 return v8_num(1);
7549}
7550
7551
7552static v8::Handle<Value> ChildGetter(Local<String> name,
7553 const AccessorInfo& info) {
7554 ApiTestFuzzer::Fuzz();
7555 return v8_num(42);
7556}
7557
7558
7559THREADED_TEST(Overriding) {
7560 v8::HandleScope scope;
7561 LocalContext context;
7562
7563 // Parent template.
7564 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7565 Local<ObjectTemplate> parent_instance_templ =
7566 parent_templ->InstanceTemplate();
7567 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7568
7569 // Template that inherits from the parent template.
7570 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7571 Local<ObjectTemplate> child_instance_templ =
7572 child_templ->InstanceTemplate();
7573 child_templ->Inherit(parent_templ);
7574 // Override 'f'. The child version of 'f' should get called for child
7575 // instances.
7576 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7577 // Add 'g' twice. The 'g' added last should get called for instances.
7578 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7579 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7580
7581 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7582 // so 'h' can be shadowed on the instance object.
7583 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7584 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7585 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7586
7587 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7588 // but the attribute does not have effect because it is duplicated with
7589 // NULL setter.
7590 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7591 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7592
7593
7594
7595 // Instantiate the child template.
7596 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7597
7598 // Check that the child function overrides the parent one.
7599 context->Global()->Set(v8_str("o"), instance);
7600 Local<Value> value = v8_compile("o.f")->Run();
7601 // Check that the 'g' that was added last is hit.
7602 CHECK_EQ(42, value->Int32Value());
7603 value = v8_compile("o.g")->Run();
7604 CHECK_EQ(42, value->Int32Value());
7605
7606 // Check 'h' can be shadowed.
7607 value = v8_compile("o.h = 3; o.h")->Run();
7608 CHECK_EQ(3, value->Int32Value());
7609
7610 // Check 'i' is cannot be shadowed or changed.
7611 value = v8_compile("o.i = 3; o.i")->Run();
7612 CHECK_EQ(42, value->Int32Value());
7613}
7614
7615
7616static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7617 ApiTestFuzzer::Fuzz();
7618 if (args.IsConstructCall()) {
7619 return v8::Boolean::New(true);
7620 }
7621 return v8::Boolean::New(false);
7622}
7623
7624
7625THREADED_TEST(IsConstructCall) {
7626 v8::HandleScope scope;
7627
7628 // Function template with call handler.
7629 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7630 templ->SetCallHandler(IsConstructHandler);
7631
7632 LocalContext context;
7633
7634 context->Global()->Set(v8_str("f"), templ->GetFunction());
7635 Local<Value> value = v8_compile("f()")->Run();
7636 CHECK(!value->BooleanValue());
7637 value = v8_compile("new f()")->Run();
7638 CHECK(value->BooleanValue());
7639}
7640
7641
7642THREADED_TEST(ObjectProtoToString) {
7643 v8::HandleScope scope;
7644 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7645 templ->SetClassName(v8_str("MyClass"));
7646
7647 LocalContext context;
7648
7649 Local<String> customized_tostring = v8_str("customized toString");
7650
7651 // Replace Object.prototype.toString
7652 v8_compile("Object.prototype.toString = function() {"
7653 " return 'customized toString';"
7654 "}")->Run();
7655
7656 // Normal ToString call should call replaced Object.prototype.toString
7657 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7658 Local<String> value = instance->ToString();
7659 CHECK(value->IsString() && value->Equals(customized_tostring));
7660
7661 // ObjectProtoToString should not call replace toString function.
7662 value = instance->ObjectProtoToString();
7663 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7664
7665 // Check global
7666 value = context->Global()->ObjectProtoToString();
7667 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7668
7669 // Check ordinary object
7670 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007671 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007672 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7673}
7674
7675
7676bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01007677i::Semaphore* ApiTestFuzzer::all_tests_done_=
7678 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007679int ApiTestFuzzer::active_tests_;
7680int ApiTestFuzzer::tests_being_run_;
7681int ApiTestFuzzer::current_;
7682
7683
7684// We are in a callback and want to switch to another thread (if we
7685// are currently running the thread fuzzing test).
7686void ApiTestFuzzer::Fuzz() {
7687 if (!fuzzing_) return;
7688 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7689 test->ContextSwitch();
7690}
7691
7692
7693// Let the next thread go. Since it is also waiting on the V8 lock it may
7694// not start immediately.
7695bool ApiTestFuzzer::NextThread() {
7696 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007697 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007698 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007699 if (kLogThreading)
7700 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007701 return false;
7702 }
Steve Blockd0582a62009-12-15 09:54:21 +00007703 if (kLogThreading) {
7704 printf("Switch from %s to %s\n",
7705 test_name,
7706 RegisterThreadedTest::nth(test_position)->name());
7707 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007708 current_ = test_position;
7709 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7710 return true;
7711}
7712
7713
7714void ApiTestFuzzer::Run() {
7715 // When it is our turn...
7716 gate_->Wait();
7717 {
7718 // ... get the V8 lock and start running the test.
7719 v8::Locker locker;
7720 CallTest();
7721 }
7722 // This test finished.
7723 active_ = false;
7724 active_tests_--;
7725 // If it was the last then signal that fact.
7726 if (active_tests_ == 0) {
7727 all_tests_done_->Signal();
7728 } else {
7729 // Otherwise select a new test and start that.
7730 NextThread();
7731 }
7732}
7733
7734
7735static unsigned linear_congruential_generator;
7736
7737
7738void ApiTestFuzzer::Setup(PartOfTest part) {
7739 linear_congruential_generator = i::FLAG_testing_prng_seed;
7740 fuzzing_ = true;
7741 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7742 int end = (part == FIRST_PART)
7743 ? (RegisterThreadedTest::count() >> 1)
7744 : RegisterThreadedTest::count();
7745 active_tests_ = tests_being_run_ = end - start;
7746 for (int i = 0; i < tests_being_run_; i++) {
7747 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7748 }
7749 for (int i = 0; i < active_tests_; i++) {
7750 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7751 }
7752}
7753
7754
7755static void CallTestNumber(int test_number) {
7756 (RegisterThreadedTest::nth(test_number)->callback())();
7757}
7758
7759
7760void ApiTestFuzzer::RunAllTests() {
7761 // Set off the first test.
7762 current_ = -1;
7763 NextThread();
7764 // Wait till they are all done.
7765 all_tests_done_->Wait();
7766}
7767
7768
7769int ApiTestFuzzer::GetNextTestNumber() {
7770 int next_test;
7771 do {
7772 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7773 linear_congruential_generator *= 1664525u;
7774 linear_congruential_generator += 1013904223u;
7775 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7776 return next_test;
7777}
7778
7779
7780void ApiTestFuzzer::ContextSwitch() {
7781 // If the new thread is the same as the current thread there is nothing to do.
7782 if (NextThread()) {
7783 // Now it can start.
7784 v8::Unlocker unlocker;
7785 // Wait till someone starts us again.
7786 gate_->Wait();
7787 // And we're off.
7788 }
7789}
7790
7791
7792void ApiTestFuzzer::TearDown() {
7793 fuzzing_ = false;
7794 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7795 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7796 if (fuzzer != NULL) fuzzer->Join();
7797 }
7798}
7799
7800
7801// Lets not be needlessly self-referential.
7802TEST(Threading) {
7803 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7804 ApiTestFuzzer::RunAllTests();
7805 ApiTestFuzzer::TearDown();
7806}
7807
7808TEST(Threading2) {
7809 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7810 ApiTestFuzzer::RunAllTests();
7811 ApiTestFuzzer::TearDown();
7812}
7813
7814
7815void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007816 if (kLogThreading)
7817 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007818 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007819 if (kLogThreading)
7820 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007821}
7822
7823
7824static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7825 CHECK(v8::Locker::IsLocked());
7826 ApiTestFuzzer::Fuzz();
7827 v8::Unlocker unlocker;
7828 const char* code = "throw 7;";
7829 {
7830 v8::Locker nested_locker;
7831 v8::HandleScope scope;
7832 v8::Handle<Value> exception;
7833 { v8::TryCatch try_catch;
7834 v8::Handle<Value> value = CompileRun(code);
7835 CHECK(value.IsEmpty());
7836 CHECK(try_catch.HasCaught());
7837 // Make sure to wrap the exception in a new handle because
7838 // the handle returned from the TryCatch is destroyed
7839 // when the TryCatch is destroyed.
7840 exception = Local<Value>::New(try_catch.Exception());
7841 }
7842 return v8::ThrowException(exception);
7843 }
7844}
7845
7846
7847static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7848 CHECK(v8::Locker::IsLocked());
7849 ApiTestFuzzer::Fuzz();
7850 v8::Unlocker unlocker;
7851 const char* code = "throw 7;";
7852 {
7853 v8::Locker nested_locker;
7854 v8::HandleScope scope;
7855 v8::Handle<Value> value = CompileRun(code);
7856 CHECK(value.IsEmpty());
7857 return v8_str("foo");
7858 }
7859}
7860
7861
7862// These are locking tests that don't need to be run again
7863// as part of the locking aggregation tests.
7864TEST(NestedLockers) {
7865 v8::Locker locker;
7866 CHECK(v8::Locker::IsLocked());
7867 v8::HandleScope scope;
7868 LocalContext env;
7869 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7870 Local<Function> fun = fun_templ->GetFunction();
7871 env->Global()->Set(v8_str("throw_in_js"), fun);
7872 Local<Script> script = v8_compile("(function () {"
7873 " try {"
7874 " throw_in_js();"
7875 " return 42;"
7876 " } catch (e) {"
7877 " return e * 13;"
7878 " }"
7879 "})();");
7880 CHECK_EQ(91, script->Run()->Int32Value());
7881}
7882
7883
7884// These are locking tests that don't need to be run again
7885// as part of the locking aggregation tests.
7886TEST(NestedLockersNoTryCatch) {
7887 v8::Locker locker;
7888 v8::HandleScope scope;
7889 LocalContext env;
7890 Local<v8::FunctionTemplate> fun_templ =
7891 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7892 Local<Function> fun = fun_templ->GetFunction();
7893 env->Global()->Set(v8_str("throw_in_js"), fun);
7894 Local<Script> script = v8_compile("(function () {"
7895 " try {"
7896 " throw_in_js();"
7897 " return 42;"
7898 " } catch (e) {"
7899 " return e * 13;"
7900 " }"
7901 "})();");
7902 CHECK_EQ(91, script->Run()->Int32Value());
7903}
7904
7905
7906THREADED_TEST(RecursiveLocking) {
7907 v8::Locker locker;
7908 {
7909 v8::Locker locker2;
7910 CHECK(v8::Locker::IsLocked());
7911 }
7912}
7913
7914
7915static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
7916 ApiTestFuzzer::Fuzz();
7917 v8::Unlocker unlocker;
7918 return v8::Undefined();
7919}
7920
7921
7922THREADED_TEST(LockUnlockLock) {
7923 {
7924 v8::Locker locker;
7925 v8::HandleScope scope;
7926 LocalContext env;
7927 Local<v8::FunctionTemplate> fun_templ =
7928 v8::FunctionTemplate::New(UnlockForAMoment);
7929 Local<Function> fun = fun_templ->GetFunction();
7930 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7931 Local<Script> script = v8_compile("(function () {"
7932 " unlock_for_a_moment();"
7933 " return 42;"
7934 "})();");
7935 CHECK_EQ(42, script->Run()->Int32Value());
7936 }
7937 {
7938 v8::Locker locker;
7939 v8::HandleScope scope;
7940 LocalContext env;
7941 Local<v8::FunctionTemplate> fun_templ =
7942 v8::FunctionTemplate::New(UnlockForAMoment);
7943 Local<Function> fun = fun_templ->GetFunction();
7944 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7945 Local<Script> script = v8_compile("(function () {"
7946 " unlock_for_a_moment();"
7947 " return 42;"
7948 "})();");
7949 CHECK_EQ(42, script->Run()->Int32Value());
7950 }
7951}
7952
7953
Leon Clarked91b9f72010-01-27 17:25:45 +00007954static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00007955 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01007956 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00007957 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
7958 if (object->IsJSGlobalObject()) count++;
7959 return count;
7960}
7961
7962
7963static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00007964 // We need to collect all garbage twice to be sure that everything
7965 // has been collected. This is because inline caches are cleared in
7966 // the first garbage collection but some of the maps have already
7967 // been marked at that point. Therefore some of the maps are not
7968 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01007969 i::Heap::CollectAllGarbage(false);
7970 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00007971 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00007972#ifdef DEBUG
Steve Block8defd9f2010-07-08 12:39:36 +01007973 if (count > 0) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00007974#endif
7975 return count;
7976}
7977
7978
7979TEST(DontLeakGlobalObjects) {
7980 // Regression test for issues 1139850 and 1174891.
7981
7982 v8::V8::Initialize();
7983
7984 int count = GetSurvivingGlobalObjectsCount();
7985
7986 for (int i = 0; i < 5; i++) {
7987 { v8::HandleScope scope;
7988 LocalContext context;
7989 }
7990 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7991
7992 { v8::HandleScope scope;
7993 LocalContext context;
7994 v8_compile("Date")->Run();
7995 }
7996 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7997
7998 { v8::HandleScope scope;
7999 LocalContext context;
8000 v8_compile("/aaa/")->Run();
8001 }
8002 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8003
8004 { v8::HandleScope scope;
8005 const char* extension_list[] = { "v8/gc" };
8006 v8::ExtensionConfiguration extensions(1, extension_list);
8007 LocalContext context(&extensions);
8008 v8_compile("gc();")->Run();
8009 }
8010 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
8011 }
8012}
8013
8014
8015v8::Persistent<v8::Object> some_object;
8016v8::Persistent<v8::Object> bad_handle;
8017
8018void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
8019 v8::HandleScope scope;
8020 bad_handle = v8::Persistent<v8::Object>::New(some_object);
8021}
8022
8023
8024THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8025 LocalContext context;
8026
8027 v8::Persistent<v8::Object> handle1, handle2;
8028 {
8029 v8::HandleScope scope;
8030 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8031 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8032 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8033 }
8034 // Note: order is implementation dependent alas: currently
8035 // global handle nodes are processed by PostGarbageCollectionProcessing
8036 // in reverse allocation order, so if second allocated handle is deleted,
8037 // weak callback of the first handle would be able to 'reallocate' it.
8038 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8039 handle2.Dispose();
8040 i::Heap::CollectAllGarbage(false);
8041}
8042
8043
8044v8::Persistent<v8::Object> to_be_disposed;
8045
8046void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8047 to_be_disposed.Dispose();
8048 i::Heap::CollectAllGarbage(false);
8049}
8050
8051
8052THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8053 LocalContext context;
8054
8055 v8::Persistent<v8::Object> handle1, handle2;
8056 {
8057 v8::HandleScope scope;
8058 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8059 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8060 }
8061 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8062 to_be_disposed = handle2;
8063 i::Heap::CollectAllGarbage(false);
8064}
8065
Steve Blockd0582a62009-12-15 09:54:21 +00008066void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8067 handle.Dispose();
8068}
8069
8070void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8071 v8::HandleScope scope;
8072 v8::Persistent<v8::Object>::New(v8::Object::New());
8073}
8074
8075
8076THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8077 LocalContext context;
8078
8079 v8::Persistent<v8::Object> handle1, handle2, handle3;
8080 {
8081 v8::HandleScope scope;
8082 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8083 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8084 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8085 }
8086 handle2.MakeWeak(NULL, DisposingCallback);
8087 handle3.MakeWeak(NULL, HandleCreatingCallback);
8088 i::Heap::CollectAllGarbage(false);
8089}
8090
Steve Blocka7e24c12009-10-30 11:49:00 +00008091
8092THREADED_TEST(CheckForCrossContextObjectLiterals) {
8093 v8::V8::Initialize();
8094
8095 const int nof = 2;
8096 const char* sources[nof] = {
8097 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8098 "Object()"
8099 };
8100
8101 for (int i = 0; i < nof; i++) {
8102 const char* source = sources[i];
8103 { v8::HandleScope scope;
8104 LocalContext context;
8105 CompileRun(source);
8106 }
8107 { v8::HandleScope scope;
8108 LocalContext context;
8109 CompileRun(source);
8110 }
8111 }
8112}
8113
8114
8115static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8116 v8::HandleScope inner;
8117 env->Enter();
8118 v8::Handle<Value> three = v8_num(3);
8119 v8::Handle<Value> value = inner.Close(three);
8120 env->Exit();
8121 return value;
8122}
8123
8124
8125THREADED_TEST(NestedHandleScopeAndContexts) {
8126 v8::HandleScope outer;
8127 v8::Persistent<Context> env = Context::New();
8128 env->Enter();
8129 v8::Handle<Value> value = NestedScope(env);
8130 v8::Handle<String> str = value->ToString();
8131 env->Exit();
8132 env.Dispose();
8133}
8134
8135
8136THREADED_TEST(ExternalAllocatedMemory) {
8137 v8::HandleScope outer;
8138 v8::Persistent<Context> env = Context::New();
8139 const int kSize = 1024*1024;
8140 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8141 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8142}
8143
8144
8145THREADED_TEST(DisposeEnteredContext) {
8146 v8::HandleScope scope;
8147 LocalContext outer;
8148 { v8::Persistent<v8::Context> inner = v8::Context::New();
8149 inner->Enter();
8150 inner.Dispose();
8151 inner.Clear();
8152 inner->Exit();
8153 }
8154}
8155
8156
8157// Regression test for issue 54, object templates with internal fields
8158// but no accessors or interceptors did not get their internal field
8159// count set on instances.
8160THREADED_TEST(Regress54) {
8161 v8::HandleScope outer;
8162 LocalContext context;
8163 static v8::Persistent<v8::ObjectTemplate> templ;
8164 if (templ.IsEmpty()) {
8165 v8::HandleScope inner;
8166 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8167 local->SetInternalFieldCount(1);
8168 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8169 }
8170 v8::Handle<v8::Object> result = templ->NewInstance();
8171 CHECK_EQ(1, result->InternalFieldCount());
8172}
8173
8174
8175// If part of the threaded tests, this test makes ThreadingTest fail
8176// on mac.
8177TEST(CatchStackOverflow) {
8178 v8::HandleScope scope;
8179 LocalContext context;
8180 v8::TryCatch try_catch;
8181 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8182 "function f() {"
8183 " return f();"
8184 "}"
8185 ""
8186 "f();"));
8187 v8::Handle<v8::Value> result = script->Run();
8188 CHECK(result.IsEmpty());
8189}
8190
8191
8192static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8193 const char* resource_name,
8194 int line_offset) {
8195 v8::HandleScope scope;
8196 v8::TryCatch try_catch;
8197 v8::Handle<v8::Value> result = script->Run();
8198 CHECK(result.IsEmpty());
8199 CHECK(try_catch.HasCaught());
8200 v8::Handle<v8::Message> message = try_catch.Message();
8201 CHECK(!message.IsEmpty());
8202 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8203 CHECK_EQ(91, message->GetStartPosition());
8204 CHECK_EQ(92, message->GetEndPosition());
8205 CHECK_EQ(2, message->GetStartColumn());
8206 CHECK_EQ(3, message->GetEndColumn());
8207 v8::String::AsciiValue line(message->GetSourceLine());
8208 CHECK_EQ(" throw 'nirk';", *line);
8209 v8::String::AsciiValue name(message->GetScriptResourceName());
8210 CHECK_EQ(resource_name, *name);
8211}
8212
8213
8214THREADED_TEST(TryCatchSourceInfo) {
8215 v8::HandleScope scope;
8216 LocalContext context;
8217 v8::Handle<v8::String> source = v8::String::New(
8218 "function Foo() {\n"
8219 " return Bar();\n"
8220 "}\n"
8221 "\n"
8222 "function Bar() {\n"
8223 " return Baz();\n"
8224 "}\n"
8225 "\n"
8226 "function Baz() {\n"
8227 " throw 'nirk';\n"
8228 "}\n"
8229 "\n"
8230 "Foo();\n");
8231
8232 const char* resource_name;
8233 v8::Handle<v8::Script> script;
8234 resource_name = "test.js";
8235 script = v8::Script::Compile(source, v8::String::New(resource_name));
8236 CheckTryCatchSourceInfo(script, resource_name, 0);
8237
8238 resource_name = "test1.js";
8239 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8240 script = v8::Script::Compile(source, &origin1);
8241 CheckTryCatchSourceInfo(script, resource_name, 0);
8242
8243 resource_name = "test2.js";
8244 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8245 script = v8::Script::Compile(source, &origin2);
8246 CheckTryCatchSourceInfo(script, resource_name, 7);
8247}
8248
8249
8250THREADED_TEST(CompilationCache) {
8251 v8::HandleScope scope;
8252 LocalContext context;
8253 v8::Handle<v8::String> source0 = v8::String::New("1234");
8254 v8::Handle<v8::String> source1 = v8::String::New("1234");
8255 v8::Handle<v8::Script> script0 =
8256 v8::Script::Compile(source0, v8::String::New("test.js"));
8257 v8::Handle<v8::Script> script1 =
8258 v8::Script::Compile(source1, v8::String::New("test.js"));
8259 v8::Handle<v8::Script> script2 =
8260 v8::Script::Compile(source0); // different origin
8261 CHECK_EQ(1234, script0->Run()->Int32Value());
8262 CHECK_EQ(1234, script1->Run()->Int32Value());
8263 CHECK_EQ(1234, script2->Run()->Int32Value());
8264}
8265
8266
8267static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8268 ApiTestFuzzer::Fuzz();
8269 return v8_num(42);
8270}
8271
8272
8273THREADED_TEST(CallbackFunctionName) {
8274 v8::HandleScope scope;
8275 LocalContext context;
8276 Local<ObjectTemplate> t = ObjectTemplate::New();
8277 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8278 context->Global()->Set(v8_str("obj"), t->NewInstance());
8279 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8280 CHECK(value->IsString());
8281 v8::String::AsciiValue name(value);
8282 CHECK_EQ("asdf", *name);
8283}
8284
8285
8286THREADED_TEST(DateAccess) {
8287 v8::HandleScope scope;
8288 LocalContext context;
8289 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8290 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008291 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008292}
8293
8294
8295void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008296 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008297 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8298 CHECK_EQ(elmc, props->Length());
8299 for (int i = 0; i < elmc; i++) {
8300 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8301 CHECK_EQ(elmv[i], *elm);
8302 }
8303}
8304
8305
8306THREADED_TEST(PropertyEnumeration) {
8307 v8::HandleScope scope;
8308 LocalContext context;
8309 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8310 "var result = [];"
8311 "result[0] = {};"
8312 "result[1] = {a: 1, b: 2};"
8313 "result[2] = [1, 2, 3];"
8314 "var proto = {x: 1, y: 2, z: 3};"
8315 "var x = { __proto__: proto, w: 0, z: 1 };"
8316 "result[3] = x;"
8317 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008318 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008319 CHECK_EQ(4, elms->Length());
8320 int elmc0 = 0;
8321 const char** elmv0 = NULL;
8322 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8323 int elmc1 = 2;
8324 const char* elmv1[] = {"a", "b"};
8325 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8326 int elmc2 = 3;
8327 const char* elmv2[] = {"0", "1", "2"};
8328 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8329 int elmc3 = 4;
8330 const char* elmv3[] = {"w", "z", "x", "y"};
8331 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8332}
8333
8334
Steve Blocka7e24c12009-10-30 11:49:00 +00008335static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8336 Local<Value> name,
8337 v8::AccessType type,
8338 Local<Value> data) {
8339 return type != v8::ACCESS_SET;
8340}
8341
8342
8343static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8344 uint32_t key,
8345 v8::AccessType type,
8346 Local<Value> data) {
8347 return type != v8::ACCESS_SET;
8348}
8349
8350
8351THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8352 v8::HandleScope scope;
8353 LocalContext context;
8354 Local<ObjectTemplate> templ = ObjectTemplate::New();
8355 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8356 IndexedSetAccessBlocker);
8357 templ->Set(v8_str("x"), v8::True());
8358 Local<v8::Object> instance = templ->NewInstance();
8359 context->Global()->Set(v8_str("obj"), instance);
8360 Local<Value> value = CompileRun("obj.x");
8361 CHECK(value->BooleanValue());
8362}
8363
8364
8365static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8366 Local<Value> name,
8367 v8::AccessType type,
8368 Local<Value> data) {
8369 return false;
8370}
8371
8372
8373static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8374 uint32_t key,
8375 v8::AccessType type,
8376 Local<Value> data) {
8377 return false;
8378}
8379
8380
8381
8382THREADED_TEST(AccessChecksReenabledCorrectly) {
8383 v8::HandleScope scope;
8384 LocalContext context;
8385 Local<ObjectTemplate> templ = ObjectTemplate::New();
8386 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8387 IndexedGetAccessBlocker);
8388 templ->Set(v8_str("a"), v8_str("a"));
8389 // Add more than 8 (see kMaxFastProperties) properties
8390 // so that the constructor will force copying map.
8391 // Cannot sprintf, gcc complains unsafety.
8392 char buf[4];
8393 for (char i = '0'; i <= '9' ; i++) {
8394 buf[0] = i;
8395 for (char j = '0'; j <= '9'; j++) {
8396 buf[1] = j;
8397 for (char k = '0'; k <= '9'; k++) {
8398 buf[2] = k;
8399 buf[3] = 0;
8400 templ->Set(v8_str(buf), v8::Number::New(k));
8401 }
8402 }
8403 }
8404
8405 Local<v8::Object> instance_1 = templ->NewInstance();
8406 context->Global()->Set(v8_str("obj_1"), instance_1);
8407
8408 Local<Value> value_1 = CompileRun("obj_1.a");
8409 CHECK(value_1->IsUndefined());
8410
8411 Local<v8::Object> instance_2 = templ->NewInstance();
8412 context->Global()->Set(v8_str("obj_2"), instance_2);
8413
8414 Local<Value> value_2 = CompileRun("obj_2.a");
8415 CHECK(value_2->IsUndefined());
8416}
8417
8418
8419// This tests that access check information remains on the global
8420// object template when creating contexts.
8421THREADED_TEST(AccessControlRepeatedContextCreation) {
8422 v8::HandleScope handle_scope;
8423 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8424 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8425 IndexedSetAccessBlocker);
8426 i::Handle<i::ObjectTemplateInfo> internal_template =
8427 v8::Utils::OpenHandle(*global_template);
8428 CHECK(!internal_template->constructor()->IsUndefined());
8429 i::Handle<i::FunctionTemplateInfo> constructor(
8430 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8431 CHECK(!constructor->access_check_info()->IsUndefined());
8432 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8433 CHECK(!constructor->access_check_info()->IsUndefined());
8434}
8435
8436
8437THREADED_TEST(TurnOnAccessCheck) {
8438 v8::HandleScope handle_scope;
8439
8440 // Create an environment with access check to the global object disabled by
8441 // default.
8442 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8443 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8444 IndexedGetAccessBlocker,
8445 v8::Handle<v8::Value>(),
8446 false);
8447 v8::Persistent<Context> context = Context::New(NULL, global_template);
8448 Context::Scope context_scope(context);
8449
8450 // Set up a property and a number of functions.
8451 context->Global()->Set(v8_str("a"), v8_num(1));
8452 CompileRun("function f1() {return a;}"
8453 "function f2() {return a;}"
8454 "function g1() {return h();}"
8455 "function g2() {return h();}"
8456 "function h() {return 1;}");
8457 Local<Function> f1 =
8458 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8459 Local<Function> f2 =
8460 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8461 Local<Function> g1 =
8462 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8463 Local<Function> g2 =
8464 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8465 Local<Function> h =
8466 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8467
8468 // Get the global object.
8469 v8::Handle<v8::Object> global = context->Global();
8470
8471 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8472 // uses the runtime system to retreive property a whereas f2 uses global load
8473 // inline cache.
8474 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8475 for (int i = 0; i < 4; i++) {
8476 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8477 }
8478
8479 // Same for g1 and g2.
8480 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8481 for (int i = 0; i < 4; i++) {
8482 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8483 }
8484
8485 // Detach the global and turn on access check.
8486 context->DetachGlobal();
8487 context->Global()->TurnOnAccessCheck();
8488
8489 // Failing access check to property get results in undefined.
8490 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8491 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8492
8493 // Failing access check to function call results in exception.
8494 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8495 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8496
8497 // No failing access check when just returning a constant.
8498 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8499}
8500
8501
8502// This test verifies that pre-compilation (aka preparsing) can be called
8503// without initializing the whole VM. Thus we cannot run this test in a
8504// multi-threaded setup.
8505TEST(PreCompile) {
8506 // TODO(155): This test would break without the initialization of V8. This is
8507 // a workaround for now to make this test not fail.
8508 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008509 const char* script = "function foo(a) { return a+1; }";
8510 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008511 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008512 CHECK_NE(sd->Length(), 0);
8513 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008514 CHECK(!sd->HasError());
8515 delete sd;
8516}
8517
8518
8519TEST(PreCompileWithError) {
8520 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008521 const char* script = "function foo(a) { return 1 * * 2; }";
8522 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008523 v8::ScriptData::PreCompile(script, i::StrLength(script));
8524 CHECK(sd->HasError());
8525 delete sd;
8526}
8527
8528
8529TEST(Regress31661) {
8530 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008531 const char* script = " The Definintive Guide";
8532 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008533 v8::ScriptData::PreCompile(script, i::StrLength(script));
8534 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008535 delete sd;
8536}
8537
8538
Leon Clarkef7060e22010-06-03 12:02:55 +01008539// Tests that ScriptData can be serialized and deserialized.
8540TEST(PreCompileSerialization) {
8541 v8::V8::Initialize();
8542 const char* script = "function foo(a) { return a+1; }";
8543 v8::ScriptData* sd =
8544 v8::ScriptData::PreCompile(script, i::StrLength(script));
8545
8546 // Serialize.
8547 int serialized_data_length = sd->Length();
8548 char* serialized_data = i::NewArray<char>(serialized_data_length);
8549 memcpy(serialized_data, sd->Data(), serialized_data_length);
8550
8551 // Deserialize.
8552 v8::ScriptData* deserialized_sd =
8553 v8::ScriptData::New(serialized_data, serialized_data_length);
8554
8555 // Verify that the original is the same as the deserialized.
8556 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8557 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8558 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8559
8560 delete sd;
8561 delete deserialized_sd;
8562}
8563
8564
8565// Attempts to deserialize bad data.
8566TEST(PreCompileDeserializationError) {
8567 v8::V8::Initialize();
8568 const char* data = "DONT CARE";
8569 int invalid_size = 3;
8570 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8571
8572 CHECK_EQ(0, sd->Length());
8573
8574 delete sd;
8575}
8576
8577
Leon Clarkeac952652010-07-15 11:15:24 +01008578// Attempts to deserialize bad data.
8579TEST(PreCompileInvalidPreparseDataError) {
8580 v8::V8::Initialize();
8581 v8::HandleScope scope;
8582 LocalContext context;
8583
8584 const char* script = "function foo(){ return 5;}\n"
8585 "function bar(){ return 6 + 7;} foo();";
8586 v8::ScriptData* sd =
8587 v8::ScriptData::PreCompile(script, i::StrLength(script));
8588 CHECK(!sd->HasError());
8589 // ScriptDataImpl private implementation details
8590 const int kUnsignedSize = sizeof(unsigned);
8591 const int kHeaderSize = 4;
8592 const int kFunctionEntrySize = 4;
8593 const int kFunctionEntryStartOffset = 0;
8594 const int kFunctionEntryEndOffset = 1;
8595 unsigned* sd_data =
8596 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
8597 CHECK_EQ(sd->Length(),
8598 (kHeaderSize + 2 * kFunctionEntrySize) * kUnsignedSize);
8599
8600 // Overwrite function bar's end position with 0.
8601 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
8602 v8::TryCatch try_catch;
8603
8604 Local<String> source = String::New(script);
8605 Local<Script> compiled_script = Script::New(source, NULL, sd);
8606 CHECK(try_catch.HasCaught());
8607 String::AsciiValue exception_value(try_catch.Message()->Get());
8608 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8609 *exception_value);
8610
8611 try_catch.Reset();
8612 // Overwrite function bar's start position with 200. The function entry
8613 // will not be found when searching for it by position.
8614 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
8615 200;
8616 compiled_script = Script::New(source, NULL, sd);
8617 CHECK(try_catch.HasCaught());
8618 String::AsciiValue second_exception_value(try_catch.Message()->Get());
8619 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8620 *second_exception_value);
8621
8622 delete sd;
8623}
8624
8625
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008626// Verifies that the Handle<String> and const char* versions of the API produce
8627// the same results (at least for one trivial case).
8628TEST(PreCompileAPIVariationsAreSame) {
8629 v8::V8::Initialize();
8630 v8::HandleScope scope;
8631
8632 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008633
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008634 v8::ScriptData* sd_from_cstring =
8635 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8636
8637 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008638 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008639 v8::String::NewExternal(resource));
8640
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008641 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
8642 v8::String::New(cstring));
8643
8644 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008645 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008646 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008647 sd_from_cstring->Length()));
8648
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008649 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
8650 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8651 sd_from_string->Data(),
8652 sd_from_cstring->Length()));
8653
8654
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008655 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008656 delete sd_from_external_string;
8657 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008658}
8659
8660
Steve Blocka7e24c12009-10-30 11:49:00 +00008661// This tests that we do not allow dictionary load/call inline caches
8662// to use functions that have not yet been compiled. The potential
8663// problem of loading a function that has not yet been compiled can
8664// arise because we share code between contexts via the compilation
8665// cache.
8666THREADED_TEST(DictionaryICLoadedFunction) {
8667 v8::HandleScope scope;
8668 // Test LoadIC.
8669 for (int i = 0; i < 2; i++) {
8670 LocalContext context;
8671 context->Global()->Set(v8_str("tmp"), v8::True());
8672 context->Global()->Delete(v8_str("tmp"));
8673 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8674 }
8675 // Test CallIC.
8676 for (int i = 0; i < 2; i++) {
8677 LocalContext context;
8678 context->Global()->Set(v8_str("tmp"), v8::True());
8679 context->Global()->Delete(v8_str("tmp"));
8680 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8681 }
8682}
8683
8684
8685// Test that cross-context new calls use the context of the callee to
8686// create the new JavaScript object.
8687THREADED_TEST(CrossContextNew) {
8688 v8::HandleScope scope;
8689 v8::Persistent<Context> context0 = Context::New();
8690 v8::Persistent<Context> context1 = Context::New();
8691
8692 // Allow cross-domain access.
8693 Local<String> token = v8_str("<security token>");
8694 context0->SetSecurityToken(token);
8695 context1->SetSecurityToken(token);
8696
8697 // Set an 'x' property on the Object prototype and define a
8698 // constructor function in context0.
8699 context0->Enter();
8700 CompileRun("Object.prototype.x = 42; function C() {};");
8701 context0->Exit();
8702
8703 // Call the constructor function from context0 and check that the
8704 // result has the 'x' property.
8705 context1->Enter();
8706 context1->Global()->Set(v8_str("other"), context0->Global());
8707 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8708 CHECK(value->IsInt32());
8709 CHECK_EQ(42, value->Int32Value());
8710 context1->Exit();
8711
8712 // Dispose the contexts to allow them to be garbage collected.
8713 context0.Dispose();
8714 context1.Dispose();
8715}
8716
8717
8718class RegExpInterruptTest {
8719 public:
8720 RegExpInterruptTest() : block_(NULL) {}
8721 ~RegExpInterruptTest() { delete block_; }
8722 void RunTest() {
8723 block_ = i::OS::CreateSemaphore(0);
8724 gc_count_ = 0;
8725 gc_during_regexp_ = 0;
8726 regexp_success_ = false;
8727 gc_success_ = false;
8728 GCThread gc_thread(this);
8729 gc_thread.Start();
8730 v8::Locker::StartPreemption(1);
8731
8732 LongRunningRegExp();
8733 {
8734 v8::Unlocker unlock;
8735 gc_thread.Join();
8736 }
8737 v8::Locker::StopPreemption();
8738 CHECK(regexp_success_);
8739 CHECK(gc_success_);
8740 }
8741 private:
8742 // Number of garbage collections required.
8743 static const int kRequiredGCs = 5;
8744
8745 class GCThread : public i::Thread {
8746 public:
8747 explicit GCThread(RegExpInterruptTest* test)
8748 : test_(test) {}
8749 virtual void Run() {
8750 test_->CollectGarbage();
8751 }
8752 private:
8753 RegExpInterruptTest* test_;
8754 };
8755
8756 void CollectGarbage() {
8757 block_->Wait();
8758 while (gc_during_regexp_ < kRequiredGCs) {
8759 {
8760 v8::Locker lock;
8761 // TODO(lrn): Perhaps create some garbage before collecting.
8762 i::Heap::CollectAllGarbage(false);
8763 gc_count_++;
8764 }
8765 i::OS::Sleep(1);
8766 }
8767 gc_success_ = true;
8768 }
8769
8770 void LongRunningRegExp() {
8771 block_->Signal(); // Enable garbage collection thread on next preemption.
8772 int rounds = 0;
8773 while (gc_during_regexp_ < kRequiredGCs) {
8774 int gc_before = gc_count_;
8775 {
8776 // Match 15-30 "a"'s against 14 and a "b".
8777 const char* c_source =
8778 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8779 ".exec('aaaaaaaaaaaaaaab') === null";
8780 Local<String> source = String::New(c_source);
8781 Local<Script> script = Script::Compile(source);
8782 Local<Value> result = script->Run();
8783 if (!result->BooleanValue()) {
8784 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8785 return;
8786 }
8787 }
8788 {
8789 // Match 15-30 "a"'s against 15 and a "b".
8790 const char* c_source =
8791 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8792 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8793 Local<String> source = String::New(c_source);
8794 Local<Script> script = Script::Compile(source);
8795 Local<Value> result = script->Run();
8796 if (!result->BooleanValue()) {
8797 gc_during_regexp_ = kRequiredGCs;
8798 return;
8799 }
8800 }
8801 int gc_after = gc_count_;
8802 gc_during_regexp_ += gc_after - gc_before;
8803 rounds++;
8804 i::OS::Sleep(1);
8805 }
8806 regexp_success_ = true;
8807 }
8808
8809 i::Semaphore* block_;
8810 int gc_count_;
8811 int gc_during_regexp_;
8812 bool regexp_success_;
8813 bool gc_success_;
8814};
8815
8816
8817// Test that a regular expression execution can be interrupted and
8818// survive a garbage collection.
8819TEST(RegExpInterruption) {
8820 v8::Locker lock;
8821 v8::V8::Initialize();
8822 v8::HandleScope scope;
8823 Local<Context> local_env;
8824 {
8825 LocalContext env;
8826 local_env = env.local();
8827 }
8828
8829 // Local context should still be live.
8830 CHECK(!local_env.IsEmpty());
8831 local_env->Enter();
8832
8833 // Should complete without problems.
8834 RegExpInterruptTest().RunTest();
8835
8836 local_env->Exit();
8837}
8838
8839
8840class ApplyInterruptTest {
8841 public:
8842 ApplyInterruptTest() : block_(NULL) {}
8843 ~ApplyInterruptTest() { delete block_; }
8844 void RunTest() {
8845 block_ = i::OS::CreateSemaphore(0);
8846 gc_count_ = 0;
8847 gc_during_apply_ = 0;
8848 apply_success_ = false;
8849 gc_success_ = false;
8850 GCThread gc_thread(this);
8851 gc_thread.Start();
8852 v8::Locker::StartPreemption(1);
8853
8854 LongRunningApply();
8855 {
8856 v8::Unlocker unlock;
8857 gc_thread.Join();
8858 }
8859 v8::Locker::StopPreemption();
8860 CHECK(apply_success_);
8861 CHECK(gc_success_);
8862 }
8863 private:
8864 // Number of garbage collections required.
8865 static const int kRequiredGCs = 2;
8866
8867 class GCThread : public i::Thread {
8868 public:
8869 explicit GCThread(ApplyInterruptTest* test)
8870 : test_(test) {}
8871 virtual void Run() {
8872 test_->CollectGarbage();
8873 }
8874 private:
8875 ApplyInterruptTest* test_;
8876 };
8877
8878 void CollectGarbage() {
8879 block_->Wait();
8880 while (gc_during_apply_ < kRequiredGCs) {
8881 {
8882 v8::Locker lock;
8883 i::Heap::CollectAllGarbage(false);
8884 gc_count_++;
8885 }
8886 i::OS::Sleep(1);
8887 }
8888 gc_success_ = true;
8889 }
8890
8891 void LongRunningApply() {
8892 block_->Signal();
8893 int rounds = 0;
8894 while (gc_during_apply_ < kRequiredGCs) {
8895 int gc_before = gc_count_;
8896 {
8897 const char* c_source =
8898 "function do_very_little(bar) {"
8899 " this.foo = bar;"
8900 "}"
8901 "for (var i = 0; i < 100000; i++) {"
8902 " do_very_little.apply(this, ['bar']);"
8903 "}";
8904 Local<String> source = String::New(c_source);
8905 Local<Script> script = Script::Compile(source);
8906 Local<Value> result = script->Run();
8907 // Check that no exception was thrown.
8908 CHECK(!result.IsEmpty());
8909 }
8910 int gc_after = gc_count_;
8911 gc_during_apply_ += gc_after - gc_before;
8912 rounds++;
8913 }
8914 apply_success_ = true;
8915 }
8916
8917 i::Semaphore* block_;
8918 int gc_count_;
8919 int gc_during_apply_;
8920 bool apply_success_;
8921 bool gc_success_;
8922};
8923
8924
8925// Test that nothing bad happens if we get a preemption just when we were
8926// about to do an apply().
8927TEST(ApplyInterruption) {
8928 v8::Locker lock;
8929 v8::V8::Initialize();
8930 v8::HandleScope scope;
8931 Local<Context> local_env;
8932 {
8933 LocalContext env;
8934 local_env = env.local();
8935 }
8936
8937 // Local context should still be live.
8938 CHECK(!local_env.IsEmpty());
8939 local_env->Enter();
8940
8941 // Should complete without problems.
8942 ApplyInterruptTest().RunTest();
8943
8944 local_env->Exit();
8945}
8946
8947
8948// Verify that we can clone an object
8949TEST(ObjectClone) {
8950 v8::HandleScope scope;
8951 LocalContext env;
8952
8953 const char* sample =
8954 "var rv = {};" \
8955 "rv.alpha = 'hello';" \
8956 "rv.beta = 123;" \
8957 "rv;";
8958
8959 // Create an object, verify basics.
8960 Local<Value> val = CompileRun(sample);
8961 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01008962 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008963 obj->Set(v8_str("gamma"), v8_str("cloneme"));
8964
8965 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
8966 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8967 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
8968
8969 // Clone it.
8970 Local<v8::Object> clone = obj->Clone();
8971 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
8972 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
8973 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
8974
8975 // Set a property on the clone, verify each object.
8976 clone->Set(v8_str("beta"), v8::Integer::New(456));
8977 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8978 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
8979}
8980
8981
8982class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
8983 public:
8984 explicit AsciiVectorResource(i::Vector<const char> vector)
8985 : data_(vector) {}
8986 virtual ~AsciiVectorResource() {}
8987 virtual size_t length() const { return data_.length(); }
8988 virtual const char* data() const { return data_.start(); }
8989 private:
8990 i::Vector<const char> data_;
8991};
8992
8993
8994class UC16VectorResource : public v8::String::ExternalStringResource {
8995 public:
8996 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
8997 : data_(vector) {}
8998 virtual ~UC16VectorResource() {}
8999 virtual size_t length() const { return data_.length(); }
9000 virtual const i::uc16* data() const { return data_.start(); }
9001 private:
9002 i::Vector<const i::uc16> data_;
9003};
9004
9005
9006static void MorphAString(i::String* string,
9007 AsciiVectorResource* ascii_resource,
9008 UC16VectorResource* uc16_resource) {
9009 CHECK(i::StringShape(string).IsExternal());
9010 if (string->IsAsciiRepresentation()) {
9011 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009012 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009013 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009014 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009015 i::ExternalTwoByteString* morphed =
9016 i::ExternalTwoByteString::cast(string);
9017 morphed->set_resource(uc16_resource);
9018 } else {
9019 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009020 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009021 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009022 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009023 i::ExternalAsciiString* morphed =
9024 i::ExternalAsciiString::cast(string);
9025 morphed->set_resource(ascii_resource);
9026 }
9027}
9028
9029
9030// Test that we can still flatten a string if the components it is built up
9031// from have been turned into 16 bit strings in the mean time.
9032THREADED_TEST(MorphCompositeStringTest) {
9033 const char* c_string = "Now is the time for all good men"
9034 " to come to the aid of the party";
9035 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9036 {
9037 v8::HandleScope scope;
9038 LocalContext env;
9039 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009040 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009041 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009042 i::Vector<const uint16_t>(two_byte_string,
9043 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009044
9045 Local<String> lhs(v8::Utils::ToLocal(
9046 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9047 Local<String> rhs(v8::Utils::ToLocal(
9048 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9049
9050 env->Global()->Set(v8_str("lhs"), lhs);
9051 env->Global()->Set(v8_str("rhs"), rhs);
9052
9053 CompileRun(
9054 "var cons = lhs + rhs;"
9055 "var slice = lhs.substring(1, lhs.length - 1);"
9056 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
9057
9058 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
9059 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
9060
9061 // Now do some stuff to make sure the strings are flattened, etc.
9062 CompileRun(
9063 "/[^a-z]/.test(cons);"
9064 "/[^a-z]/.test(slice);"
9065 "/[^a-z]/.test(slice_on_cons);");
9066 const char* expected_cons =
9067 "Now is the time for all good men to come to the aid of the party"
9068 "Now is the time for all good men to come to the aid of the party";
9069 const char* expected_slice =
9070 "ow is the time for all good men to come to the aid of the part";
9071 const char* expected_slice_on_cons =
9072 "ow is the time for all good men to come to the aid of the party"
9073 "Now is the time for all good men to come to the aid of the part";
9074 CHECK_EQ(String::New(expected_cons),
9075 env->Global()->Get(v8_str("cons")));
9076 CHECK_EQ(String::New(expected_slice),
9077 env->Global()->Get(v8_str("slice")));
9078 CHECK_EQ(String::New(expected_slice_on_cons),
9079 env->Global()->Get(v8_str("slice_on_cons")));
9080 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009081 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009082}
9083
9084
9085TEST(CompileExternalTwoByteSource) {
9086 v8::HandleScope scope;
9087 LocalContext context;
9088
9089 // This is a very short list of sources, which currently is to check for a
9090 // regression caused by r2703.
9091 const char* ascii_sources[] = {
9092 "0.5",
9093 "-0.5", // This mainly testes PushBack in the Scanner.
9094 "--0.5", // This mainly testes PushBack in the Scanner.
9095 NULL
9096 };
9097
9098 // Compile the sources as external two byte strings.
9099 for (int i = 0; ascii_sources[i] != NULL; i++) {
9100 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9101 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009102 i::Vector<const uint16_t>(two_byte_string,
9103 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009104 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9105 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009106 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009107 }
9108}
9109
9110
9111class RegExpStringModificationTest {
9112 public:
9113 RegExpStringModificationTest()
9114 : block_(i::OS::CreateSemaphore(0)),
9115 morphs_(0),
9116 morphs_during_regexp_(0),
9117 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9118 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9119 ~RegExpStringModificationTest() { delete block_; }
9120 void RunTest() {
9121 regexp_success_ = false;
9122 morph_success_ = false;
9123
9124 // Initialize the contents of two_byte_content_ to be a uc16 representation
9125 // of "aaaaaaaaaaaaaab".
9126 for (int i = 0; i < 14; i++) {
9127 two_byte_content_[i] = 'a';
9128 }
9129 two_byte_content_[14] = 'b';
9130
9131 // Create the input string for the regexp - the one we are going to change
9132 // properties of.
9133 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9134
9135 // Inject the input as a global variable.
9136 i::Handle<i::String> input_name =
9137 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
9138 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
9139
9140
9141 MorphThread morph_thread(this);
9142 morph_thread.Start();
9143 v8::Locker::StartPreemption(1);
9144 LongRunningRegExp();
9145 {
9146 v8::Unlocker unlock;
9147 morph_thread.Join();
9148 }
9149 v8::Locker::StopPreemption();
9150 CHECK(regexp_success_);
9151 CHECK(morph_success_);
9152 }
9153 private:
9154
9155 // Number of string modifications required.
9156 static const int kRequiredModifications = 5;
9157 static const int kMaxModifications = 100;
9158
9159 class MorphThread : public i::Thread {
9160 public:
9161 explicit MorphThread(RegExpStringModificationTest* test)
9162 : test_(test) {}
9163 virtual void Run() {
9164 test_->MorphString();
9165 }
9166 private:
9167 RegExpStringModificationTest* test_;
9168 };
9169
9170 void MorphString() {
9171 block_->Wait();
9172 while (morphs_during_regexp_ < kRequiredModifications &&
9173 morphs_ < kMaxModifications) {
9174 {
9175 v8::Locker lock;
9176 // Swap string between ascii and two-byte representation.
9177 i::String* string = *input_;
9178 MorphAString(string, &ascii_resource_, &uc16_resource_);
9179 morphs_++;
9180 }
9181 i::OS::Sleep(1);
9182 }
9183 morph_success_ = true;
9184 }
9185
9186 void LongRunningRegExp() {
9187 block_->Signal(); // Enable morphing thread on next preemption.
9188 while (morphs_during_regexp_ < kRequiredModifications &&
9189 morphs_ < kMaxModifications) {
9190 int morphs_before = morphs_;
9191 {
9192 // Match 15-30 "a"'s against 14 and a "b".
9193 const char* c_source =
9194 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9195 ".exec(input) === null";
9196 Local<String> source = String::New(c_source);
9197 Local<Script> script = Script::Compile(source);
9198 Local<Value> result = script->Run();
9199 CHECK(result->IsTrue());
9200 }
9201 int morphs_after = morphs_;
9202 morphs_during_regexp_ += morphs_after - morphs_before;
9203 }
9204 regexp_success_ = true;
9205 }
9206
9207 i::uc16 two_byte_content_[15];
9208 i::Semaphore* block_;
9209 int morphs_;
9210 int morphs_during_regexp_;
9211 bool regexp_success_;
9212 bool morph_success_;
9213 i::Handle<i::String> input_;
9214 AsciiVectorResource ascii_resource_;
9215 UC16VectorResource uc16_resource_;
9216};
9217
9218
9219// Test that a regular expression execution can be interrupted and
9220// the string changed without failing.
9221TEST(RegExpStringModification) {
9222 v8::Locker lock;
9223 v8::V8::Initialize();
9224 v8::HandleScope scope;
9225 Local<Context> local_env;
9226 {
9227 LocalContext env;
9228 local_env = env.local();
9229 }
9230
9231 // Local context should still be live.
9232 CHECK(!local_env.IsEmpty());
9233 local_env->Enter();
9234
9235 // Should complete without problems.
9236 RegExpStringModificationTest().RunTest();
9237
9238 local_env->Exit();
9239}
9240
9241
9242// Test that we can set a property on the global object even if there
9243// is a read-only property in the prototype chain.
9244TEST(ReadOnlyPropertyInGlobalProto) {
9245 v8::HandleScope scope;
9246 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9247 LocalContext context(0, templ);
9248 v8::Handle<v8::Object> global = context->Global();
9249 v8::Handle<v8::Object> global_proto =
9250 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9251 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9252 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9253 // Check without 'eval' or 'with'.
9254 v8::Handle<v8::Value> res =
9255 CompileRun("function f() { x = 42; return x; }; f()");
9256 // Check with 'eval'.
9257 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9258 CHECK_EQ(v8::Integer::New(42), res);
9259 // Check with 'with'.
9260 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9261 CHECK_EQ(v8::Integer::New(42), res);
9262}
9263
9264static int force_set_set_count = 0;
9265static int force_set_get_count = 0;
9266bool pass_on_get = false;
9267
9268static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9269 const v8::AccessorInfo& info) {
9270 force_set_get_count++;
9271 if (pass_on_get) {
9272 return v8::Handle<v8::Value>();
9273 } else {
9274 return v8::Int32::New(3);
9275 }
9276}
9277
9278static void ForceSetSetter(v8::Local<v8::String> name,
9279 v8::Local<v8::Value> value,
9280 const v8::AccessorInfo& info) {
9281 force_set_set_count++;
9282}
9283
9284static v8::Handle<v8::Value> ForceSetInterceptSetter(
9285 v8::Local<v8::String> name,
9286 v8::Local<v8::Value> value,
9287 const v8::AccessorInfo& info) {
9288 force_set_set_count++;
9289 return v8::Undefined();
9290}
9291
9292TEST(ForceSet) {
9293 force_set_get_count = 0;
9294 force_set_set_count = 0;
9295 pass_on_get = false;
9296
9297 v8::HandleScope scope;
9298 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9299 v8::Handle<v8::String> access_property = v8::String::New("a");
9300 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9301 LocalContext context(NULL, templ);
9302 v8::Handle<v8::Object> global = context->Global();
9303
9304 // Ordinary properties
9305 v8::Handle<v8::String> simple_property = v8::String::New("p");
9306 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9307 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9308 // This should fail because the property is read-only
9309 global->Set(simple_property, v8::Int32::New(5));
9310 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9311 // This should succeed even though the property is read-only
9312 global->ForceSet(simple_property, v8::Int32::New(6));
9313 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9314
9315 // Accessors
9316 CHECK_EQ(0, force_set_set_count);
9317 CHECK_EQ(0, force_set_get_count);
9318 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9319 // CHECK_EQ the property shouldn't override it, just call the setter
9320 // which in this case does nothing.
9321 global->Set(access_property, v8::Int32::New(7));
9322 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9323 CHECK_EQ(1, force_set_set_count);
9324 CHECK_EQ(2, force_set_get_count);
9325 // Forcing the property to be set should override the accessor without
9326 // calling it
9327 global->ForceSet(access_property, v8::Int32::New(8));
9328 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9329 CHECK_EQ(1, force_set_set_count);
9330 CHECK_EQ(2, force_set_get_count);
9331}
9332
9333TEST(ForceSetWithInterceptor) {
9334 force_set_get_count = 0;
9335 force_set_set_count = 0;
9336 pass_on_get = false;
9337
9338 v8::HandleScope scope;
9339 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9340 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9341 LocalContext context(NULL, templ);
9342 v8::Handle<v8::Object> global = context->Global();
9343
9344 v8::Handle<v8::String> some_property = v8::String::New("a");
9345 CHECK_EQ(0, force_set_set_count);
9346 CHECK_EQ(0, force_set_get_count);
9347 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9348 // Setting the property shouldn't override it, just call the setter
9349 // which in this case does nothing.
9350 global->Set(some_property, v8::Int32::New(7));
9351 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9352 CHECK_EQ(1, force_set_set_count);
9353 CHECK_EQ(2, force_set_get_count);
9354 // Getting the property when the interceptor returns an empty handle
9355 // should yield undefined, since the property isn't present on the
9356 // object itself yet.
9357 pass_on_get = true;
9358 CHECK(global->Get(some_property)->IsUndefined());
9359 CHECK_EQ(1, force_set_set_count);
9360 CHECK_EQ(3, force_set_get_count);
9361 // Forcing the property to be set should cause the value to be
9362 // set locally without calling the interceptor.
9363 global->ForceSet(some_property, v8::Int32::New(8));
9364 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9365 CHECK_EQ(1, force_set_set_count);
9366 CHECK_EQ(4, force_set_get_count);
9367 // Reenabling the interceptor should cause it to take precedence over
9368 // the property
9369 pass_on_get = false;
9370 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9371 CHECK_EQ(1, force_set_set_count);
9372 CHECK_EQ(5, force_set_get_count);
9373 // The interceptor should also work for other properties
9374 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9375 CHECK_EQ(1, force_set_set_count);
9376 CHECK_EQ(6, force_set_get_count);
9377}
9378
9379
9380THREADED_TEST(ForceDelete) {
9381 v8::HandleScope scope;
9382 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9383 LocalContext context(NULL, templ);
9384 v8::Handle<v8::Object> global = context->Global();
9385
9386 // Ordinary properties
9387 v8::Handle<v8::String> simple_property = v8::String::New("p");
9388 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9389 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9390 // This should fail because the property is dont-delete.
9391 CHECK(!global->Delete(simple_property));
9392 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9393 // This should succeed even though the property is dont-delete.
9394 CHECK(global->ForceDelete(simple_property));
9395 CHECK(global->Get(simple_property)->IsUndefined());
9396}
9397
9398
9399static int force_delete_interceptor_count = 0;
9400static bool pass_on_delete = false;
9401
9402
9403static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9404 v8::Local<v8::String> name,
9405 const v8::AccessorInfo& info) {
9406 force_delete_interceptor_count++;
9407 if (pass_on_delete) {
9408 return v8::Handle<v8::Boolean>();
9409 } else {
9410 return v8::True();
9411 }
9412}
9413
9414
9415THREADED_TEST(ForceDeleteWithInterceptor) {
9416 force_delete_interceptor_count = 0;
9417 pass_on_delete = false;
9418
9419 v8::HandleScope scope;
9420 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9421 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9422 LocalContext context(NULL, templ);
9423 v8::Handle<v8::Object> global = context->Global();
9424
9425 v8::Handle<v8::String> some_property = v8::String::New("a");
9426 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9427
9428 // Deleting a property should get intercepted and nothing should
9429 // happen.
9430 CHECK_EQ(0, force_delete_interceptor_count);
9431 CHECK(global->Delete(some_property));
9432 CHECK_EQ(1, force_delete_interceptor_count);
9433 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9434 // Deleting the property when the interceptor returns an empty
9435 // handle should not delete the property since it is DontDelete.
9436 pass_on_delete = true;
9437 CHECK(!global->Delete(some_property));
9438 CHECK_EQ(2, force_delete_interceptor_count);
9439 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9440 // Forcing the property to be deleted should delete the value
9441 // without calling the interceptor.
9442 CHECK(global->ForceDelete(some_property));
9443 CHECK(global->Get(some_property)->IsUndefined());
9444 CHECK_EQ(2, force_delete_interceptor_count);
9445}
9446
9447
9448// Make sure that forcing a delete invalidates any IC stubs, so we
9449// don't read the hole value.
9450THREADED_TEST(ForceDeleteIC) {
9451 v8::HandleScope scope;
9452 LocalContext context;
9453 // Create a DontDelete variable on the global object.
9454 CompileRun("this.__proto__ = { foo: 'horse' };"
9455 "var foo = 'fish';"
9456 "function f() { return foo.length; }");
9457 // Initialize the IC for foo in f.
9458 CompileRun("for (var i = 0; i < 4; i++) f();");
9459 // Make sure the value of foo is correct before the deletion.
9460 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9461 // Force the deletion of foo.
9462 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9463 // Make sure the value for foo is read from the prototype, and that
9464 // we don't get in trouble with reading the deleted cell value
9465 // sentinel.
9466 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9467}
9468
9469
9470v8::Persistent<Context> calling_context0;
9471v8::Persistent<Context> calling_context1;
9472v8::Persistent<Context> calling_context2;
9473
9474
9475// Check that the call to the callback is initiated in
9476// calling_context2, the directly calling context is calling_context1
9477// and the callback itself is in calling_context0.
9478static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9479 ApiTestFuzzer::Fuzz();
9480 CHECK(Context::GetCurrent() == calling_context0);
9481 CHECK(Context::GetCalling() == calling_context1);
9482 CHECK(Context::GetEntered() == calling_context2);
9483 return v8::Integer::New(42);
9484}
9485
9486
9487THREADED_TEST(GetCallingContext) {
9488 v8::HandleScope scope;
9489
9490 calling_context0 = Context::New();
9491 calling_context1 = Context::New();
9492 calling_context2 = Context::New();
9493
9494 // Allow cross-domain access.
9495 Local<String> token = v8_str("<security token>");
9496 calling_context0->SetSecurityToken(token);
9497 calling_context1->SetSecurityToken(token);
9498 calling_context2->SetSecurityToken(token);
9499
9500 // Create an object with a C++ callback in context0.
9501 calling_context0->Enter();
9502 Local<v8::FunctionTemplate> callback_templ =
9503 v8::FunctionTemplate::New(GetCallingContextCallback);
9504 calling_context0->Global()->Set(v8_str("callback"),
9505 callback_templ->GetFunction());
9506 calling_context0->Exit();
9507
9508 // Expose context0 in context1 and setup a function that calls the
9509 // callback function.
9510 calling_context1->Enter();
9511 calling_context1->Global()->Set(v8_str("context0"),
9512 calling_context0->Global());
9513 CompileRun("function f() { context0.callback() }");
9514 calling_context1->Exit();
9515
9516 // Expose context1 in context2 and call the callback function in
9517 // context0 indirectly through f in context1.
9518 calling_context2->Enter();
9519 calling_context2->Global()->Set(v8_str("context1"),
9520 calling_context1->Global());
9521 CompileRun("context1.f()");
9522 calling_context2->Exit();
9523
9524 // Dispose the contexts to allow them to be garbage collected.
9525 calling_context0.Dispose();
9526 calling_context1.Dispose();
9527 calling_context2.Dispose();
9528 calling_context0.Clear();
9529 calling_context1.Clear();
9530 calling_context2.Clear();
9531}
9532
9533
9534// Check that a variable declaration with no explicit initialization
9535// value does not shadow an existing property in the prototype chain.
9536//
9537// This is consistent with Firefox and Safari.
9538//
9539// See http://crbug.com/12548.
9540THREADED_TEST(InitGlobalVarInProtoChain) {
9541 v8::HandleScope scope;
9542 LocalContext context;
9543 // Introduce a variable in the prototype chain.
9544 CompileRun("__proto__.x = 42");
9545 v8::Handle<v8::Value> result = CompileRun("var x; x");
9546 CHECK(!result->IsUndefined());
9547 CHECK_EQ(42, result->Int32Value());
9548}
9549
9550
9551// Regression test for issue 398.
9552// If a function is added to an object, creating a constant function
9553// field, and the result is cloned, replacing the constant function on the
9554// original should not affect the clone.
9555// See http://code.google.com/p/v8/issues/detail?id=398
9556THREADED_TEST(ReplaceConstantFunction) {
9557 v8::HandleScope scope;
9558 LocalContext context;
9559 v8::Handle<v8::Object> obj = v8::Object::New();
9560 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9561 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9562 obj->Set(foo_string, func_templ->GetFunction());
9563 v8::Handle<v8::Object> obj_clone = obj->Clone();
9564 obj_clone->Set(foo_string, v8::String::New("Hello"));
9565 CHECK(!obj->Get(foo_string)->IsUndefined());
9566}
9567
9568
9569// Regression test for http://crbug.com/16276.
9570THREADED_TEST(Regress16276) {
9571 v8::HandleScope scope;
9572 LocalContext context;
9573 // Force the IC in f to be a dictionary load IC.
9574 CompileRun("function f(obj) { return obj.x; }\n"
9575 "var obj = { x: { foo: 42 }, y: 87 };\n"
9576 "var x = obj.x;\n"
9577 "delete obj.y;\n"
9578 "for (var i = 0; i < 5; i++) f(obj);");
9579 // Detach the global object to make 'this' refer directly to the
9580 // global object (not the proxy), and make sure that the dictionary
9581 // load IC doesn't mess up loading directly from the global object.
9582 context->DetachGlobal();
9583 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9584}
9585
9586
9587THREADED_TEST(PixelArray) {
9588 v8::HandleScope scope;
9589 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009590 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009591 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9592 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9593 pixel_data);
9594 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9595 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009596 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009597 }
9598 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9599 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009600 CHECK_EQ(i % 256, pixels->get(i));
9601 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009602 }
9603
9604 v8::Handle<v8::Object> obj = v8::Object::New();
9605 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9606 // Set the elements to be the pixels.
9607 // jsobj->set_elements(*pixels);
9608 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9609 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9610 obj->Set(v8_str("field"), v8::Int32::New(1503));
9611 context->Global()->Set(v8_str("pixels"), obj);
9612 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9613 CHECK_EQ(1503, result->Int32Value());
9614 result = CompileRun("pixels[1]");
9615 CHECK_EQ(1, result->Int32Value());
9616
9617 result = CompileRun("var sum = 0;"
9618 "for (var i = 0; i < 8; i++) {"
9619 " sum += pixels[i] = pixels[i] = -i;"
9620 "}"
9621 "sum;");
9622 CHECK_EQ(-28, result->Int32Value());
9623
9624 result = CompileRun("var sum = 0;"
9625 "for (var i = 0; i < 8; i++) {"
9626 " sum += pixels[i] = pixels[i] = 0;"
9627 "}"
9628 "sum;");
9629 CHECK_EQ(0, result->Int32Value());
9630
9631 result = CompileRun("var sum = 0;"
9632 "for (var i = 0; i < 8; i++) {"
9633 " sum += pixels[i] = pixels[i] = 255;"
9634 "}"
9635 "sum;");
9636 CHECK_EQ(8 * 255, result->Int32Value());
9637
9638 result = CompileRun("var sum = 0;"
9639 "for (var i = 0; i < 8; i++) {"
9640 " sum += pixels[i] = pixels[i] = 256 + i;"
9641 "}"
9642 "sum;");
9643 CHECK_EQ(2076, result->Int32Value());
9644
9645 result = CompileRun("var sum = 0;"
9646 "for (var i = 0; i < 8; i++) {"
9647 " sum += pixels[i] = pixels[i] = i;"
9648 "}"
9649 "sum;");
9650 CHECK_EQ(28, result->Int32Value());
9651
9652 result = CompileRun("var sum = 0;"
9653 "for (var i = 0; i < 8; i++) {"
9654 " sum += pixels[i];"
9655 "}"
9656 "sum;");
9657 CHECK_EQ(28, result->Int32Value());
9658
9659 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9660 i::SetElement(jsobj, 1, value);
9661 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9662 *value.location() = i::Smi::FromInt(256);
9663 i::SetElement(jsobj, 1, value);
9664 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9665 *value.location() = i::Smi::FromInt(-1);
9666 i::SetElement(jsobj, 1, value);
9667 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9668
9669 result = CompileRun("for (var i = 0; i < 8; i++) {"
9670 " pixels[i] = (i * 65) - 109;"
9671 "}"
9672 "pixels[1] + pixels[6];");
9673 CHECK_EQ(255, result->Int32Value());
9674 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9675 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9676 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9677 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9678 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9679 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9680 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9681 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9682 result = CompileRun("var sum = 0;"
9683 "for (var i = 0; i < 8; i++) {"
9684 " sum += pixels[i];"
9685 "}"
9686 "sum;");
9687 CHECK_EQ(984, result->Int32Value());
9688
9689 result = CompileRun("for (var i = 0; i < 8; i++) {"
9690 " pixels[i] = (i * 1.1);"
9691 "}"
9692 "pixels[1] + pixels[6];");
9693 CHECK_EQ(8, result->Int32Value());
9694 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9695 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9696 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9697 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9698 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9699 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9700 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9701 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9702
9703 result = CompileRun("for (var i = 0; i < 8; i++) {"
9704 " pixels[7] = undefined;"
9705 "}"
9706 "pixels[7];");
9707 CHECK_EQ(0, result->Int32Value());
9708 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9709
9710 result = CompileRun("for (var i = 0; i < 8; i++) {"
9711 " pixels[6] = '2.3';"
9712 "}"
9713 "pixels[6];");
9714 CHECK_EQ(2, result->Int32Value());
9715 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9716
9717 result = CompileRun("for (var i = 0; i < 8; i++) {"
9718 " pixels[5] = NaN;"
9719 "}"
9720 "pixels[5];");
9721 CHECK_EQ(0, result->Int32Value());
9722 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9723
9724 result = CompileRun("for (var i = 0; i < 8; i++) {"
9725 " pixels[8] = Infinity;"
9726 "}"
9727 "pixels[8];");
9728 CHECK_EQ(255, result->Int32Value());
9729 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9730
9731 result = CompileRun("for (var i = 0; i < 8; i++) {"
9732 " pixels[9] = -Infinity;"
9733 "}"
9734 "pixels[9];");
9735 CHECK_EQ(0, result->Int32Value());
9736 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9737
9738 result = CompileRun("pixels[3] = 33;"
9739 "delete pixels[3];"
9740 "pixels[3];");
9741 CHECK_EQ(33, result->Int32Value());
9742
9743 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9744 "pixels[2] = 12; pixels[3] = 13;"
9745 "pixels.__defineGetter__('2',"
9746 "function() { return 120; });"
9747 "pixels[2];");
9748 CHECK_EQ(12, result->Int32Value());
9749
9750 result = CompileRun("var js_array = new Array(40);"
9751 "js_array[0] = 77;"
9752 "js_array;");
9753 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9754
9755 result = CompileRun("pixels[1] = 23;"
9756 "pixels.__proto__ = [];"
9757 "js_array.__proto__ = pixels;"
9758 "js_array.concat(pixels);");
9759 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9760 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9761
9762 result = CompileRun("pixels[1] = 23;");
9763 CHECK_EQ(23, result->Int32Value());
9764
Steve Blockd0582a62009-12-15 09:54:21 +00009765 // Test for index greater than 255. Regression test for:
9766 // http://code.google.com/p/chromium/issues/detail?id=26337.
9767 result = CompileRun("pixels[256] = 255;");
9768 CHECK_EQ(255, result->Int32Value());
9769 result = CompileRun("var i = 0;"
9770 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9771 "i");
9772 CHECK_EQ(255, result->Int32Value());
9773
Steve Blocka7e24c12009-10-30 11:49:00 +00009774 free(pixel_data);
9775}
9776
9777
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009778THREADED_TEST(PixelArrayInfo) {
9779 v8::HandleScope scope;
9780 LocalContext context;
9781 for (int size = 0; size < 100; size += 10) {
9782 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9783 v8::Handle<v8::Object> obj = v8::Object::New();
9784 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9785 CHECK(obj->HasIndexedPropertiesInPixelData());
9786 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9787 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9788 free(pixel_data);
9789 }
9790}
9791
9792
9793static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9794 switch (array_type) {
9795 case v8::kExternalByteArray:
9796 case v8::kExternalUnsignedByteArray:
9797 return 1;
9798 break;
9799 case v8::kExternalShortArray:
9800 case v8::kExternalUnsignedShortArray:
9801 return 2;
9802 break;
9803 case v8::kExternalIntArray:
9804 case v8::kExternalUnsignedIntArray:
9805 case v8::kExternalFloatArray:
9806 return 4;
9807 break;
9808 default:
9809 UNREACHABLE();
9810 return -1;
9811 }
9812 UNREACHABLE();
9813 return -1;
9814}
9815
9816
Steve Block3ce2e202009-11-05 08:53:23 +00009817template <class ExternalArrayClass, class ElementType>
9818static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9819 int64_t low,
9820 int64_t high) {
9821 v8::HandleScope scope;
9822 LocalContext context;
9823 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009824 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +00009825 ElementType* array_data =
9826 static_cast<ElementType*>(malloc(kElementCount * element_size));
9827 i::Handle<ExternalArrayClass> array =
9828 i::Handle<ExternalArrayClass>::cast(
9829 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9830 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9831 for (int i = 0; i < kElementCount; i++) {
9832 array->set(i, static_cast<ElementType>(i));
9833 }
9834 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9835 for (int i = 0; i < kElementCount; i++) {
9836 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9837 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9838 }
9839
9840 v8::Handle<v8::Object> obj = v8::Object::New();
9841 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9842 // Set the elements to be the external array.
9843 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9844 array_type,
9845 kElementCount);
9846 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9847 obj->Set(v8_str("field"), v8::Int32::New(1503));
9848 context->Global()->Set(v8_str("ext_array"), obj);
9849 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9850 CHECK_EQ(1503, result->Int32Value());
9851 result = CompileRun("ext_array[1]");
9852 CHECK_EQ(1, result->Int32Value());
9853
9854 // Check pass through of assigned smis
9855 result = CompileRun("var sum = 0;"
9856 "for (var i = 0; i < 8; i++) {"
9857 " sum += ext_array[i] = ext_array[i] = -i;"
9858 "}"
9859 "sum;");
9860 CHECK_EQ(-28, result->Int32Value());
9861
9862 // Check assigned smis
9863 result = CompileRun("for (var i = 0; i < 8; i++) {"
9864 " ext_array[i] = i;"
9865 "}"
9866 "var sum = 0;"
9867 "for (var i = 0; i < 8; i++) {"
9868 " sum += ext_array[i];"
9869 "}"
9870 "sum;");
9871 CHECK_EQ(28, result->Int32Value());
9872
9873 // Check assigned smis in reverse order
9874 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9875 " ext_array[i] = i;"
9876 "}"
9877 "var sum = 0;"
9878 "for (var i = 0; i < 8; i++) {"
9879 " sum += ext_array[i];"
9880 "}"
9881 "sum;");
9882 CHECK_EQ(28, result->Int32Value());
9883
9884 // Check pass through of assigned HeapNumbers
9885 result = CompileRun("var sum = 0;"
9886 "for (var i = 0; i < 16; i+=2) {"
9887 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9888 "}"
9889 "sum;");
9890 CHECK_EQ(-28, result->Int32Value());
9891
9892 // Check assigned HeapNumbers
9893 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9894 " ext_array[i] = (i * 0.5);"
9895 "}"
9896 "var sum = 0;"
9897 "for (var i = 0; i < 16; i+=2) {"
9898 " sum += ext_array[i];"
9899 "}"
9900 "sum;");
9901 CHECK_EQ(28, result->Int32Value());
9902
9903 // Check assigned HeapNumbers in reverse order
9904 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9905 " ext_array[i] = (i * 0.5);"
9906 "}"
9907 "var sum = 0;"
9908 "for (var i = 0; i < 16; i+=2) {"
9909 " sum += ext_array[i];"
9910 "}"
9911 "sum;");
9912 CHECK_EQ(28, result->Int32Value());
9913
9914 i::ScopedVector<char> test_buf(1024);
9915
9916 // Check legal boundary conditions.
9917 // The repeated loads and stores ensure the ICs are exercised.
9918 const char* boundary_program =
9919 "var res = 0;"
9920 "for (var i = 0; i < 16; i++) {"
9921 " ext_array[i] = %lld;"
9922 " if (i > 8) {"
9923 " res = ext_array[i];"
9924 " }"
9925 "}"
9926 "res;";
9927 i::OS::SNPrintF(test_buf,
9928 boundary_program,
9929 low);
9930 result = CompileRun(test_buf.start());
9931 CHECK_EQ(low, result->IntegerValue());
9932
9933 i::OS::SNPrintF(test_buf,
9934 boundary_program,
9935 high);
9936 result = CompileRun(test_buf.start());
9937 CHECK_EQ(high, result->IntegerValue());
9938
9939 // Check misprediction of type in IC.
9940 result = CompileRun("var tmp_array = ext_array;"
9941 "var sum = 0;"
9942 "for (var i = 0; i < 8; i++) {"
9943 " tmp_array[i] = i;"
9944 " sum += tmp_array[i];"
9945 " if (i == 4) {"
9946 " tmp_array = {};"
9947 " }"
9948 "}"
9949 "sum;");
9950 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9951 CHECK_EQ(28, result->Int32Value());
9952
9953 // Make sure out-of-range loads do not throw.
9954 i::OS::SNPrintF(test_buf,
9955 "var caught_exception = false;"
9956 "try {"
9957 " ext_array[%d];"
9958 "} catch (e) {"
9959 " caught_exception = true;"
9960 "}"
9961 "caught_exception;",
9962 kElementCount);
9963 result = CompileRun(test_buf.start());
9964 CHECK_EQ(false, result->BooleanValue());
9965
9966 // Make sure out-of-range stores do not throw.
9967 i::OS::SNPrintF(test_buf,
9968 "var caught_exception = false;"
9969 "try {"
9970 " ext_array[%d] = 1;"
9971 "} catch (e) {"
9972 " caught_exception = true;"
9973 "}"
9974 "caught_exception;",
9975 kElementCount);
9976 result = CompileRun(test_buf.start());
9977 CHECK_EQ(false, result->BooleanValue());
9978
9979 // Check other boundary conditions, values and operations.
9980 result = CompileRun("for (var i = 0; i < 8; i++) {"
9981 " ext_array[7] = undefined;"
9982 "}"
9983 "ext_array[7];");
9984 CHECK_EQ(0, result->Int32Value());
9985 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
9986
9987 result = CompileRun("for (var i = 0; i < 8; i++) {"
9988 " ext_array[6] = '2.3';"
9989 "}"
9990 "ext_array[6];");
9991 CHECK_EQ(2, result->Int32Value());
9992 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
9993
9994 if (array_type != v8::kExternalFloatArray) {
9995 // Though the specification doesn't state it, be explicit about
9996 // converting NaNs and +/-Infinity to zero.
9997 result = CompileRun("for (var i = 0; i < 8; i++) {"
9998 " ext_array[i] = 5;"
9999 "}"
10000 "for (var i = 0; i < 8; i++) {"
10001 " ext_array[i] = NaN;"
10002 "}"
10003 "ext_array[5];");
10004 CHECK_EQ(0, result->Int32Value());
10005 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10006
10007 result = CompileRun("for (var i = 0; i < 8; i++) {"
10008 " ext_array[i] = 5;"
10009 "}"
10010 "for (var i = 0; i < 8; i++) {"
10011 " ext_array[i] = Infinity;"
10012 "}"
10013 "ext_array[5];");
10014 CHECK_EQ(0, result->Int32Value());
10015 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10016
10017 result = CompileRun("for (var i = 0; i < 8; i++) {"
10018 " ext_array[i] = 5;"
10019 "}"
10020 "for (var i = 0; i < 8; i++) {"
10021 " ext_array[i] = -Infinity;"
10022 "}"
10023 "ext_array[5];");
10024 CHECK_EQ(0, result->Int32Value());
10025 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10026 }
10027
10028 result = CompileRun("ext_array[3] = 33;"
10029 "delete ext_array[3];"
10030 "ext_array[3];");
10031 CHECK_EQ(33, result->Int32Value());
10032
10033 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
10034 "ext_array[2] = 12; ext_array[3] = 13;"
10035 "ext_array.__defineGetter__('2',"
10036 "function() { return 120; });"
10037 "ext_array[2];");
10038 CHECK_EQ(12, result->Int32Value());
10039
10040 result = CompileRun("var js_array = new Array(40);"
10041 "js_array[0] = 77;"
10042 "js_array;");
10043 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10044
10045 result = CompileRun("ext_array[1] = 23;"
10046 "ext_array.__proto__ = [];"
10047 "js_array.__proto__ = ext_array;"
10048 "js_array.concat(ext_array);");
10049 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10050 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10051
10052 result = CompileRun("ext_array[1] = 23;");
10053 CHECK_EQ(23, result->Int32Value());
10054
Steve Blockd0582a62009-12-15 09:54:21 +000010055 // Test more complex manipulations which cause eax to contain values
10056 // that won't be completely overwritten by loads from the arrays.
10057 // This catches bugs in the instructions used for the KeyedLoadIC
10058 // for byte and word types.
10059 {
10060 const int kXSize = 300;
10061 const int kYSize = 300;
10062 const int kLargeElementCount = kXSize * kYSize * 4;
10063 ElementType* large_array_data =
10064 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
10065 i::Handle<ExternalArrayClass> large_array =
10066 i::Handle<ExternalArrayClass>::cast(
10067 i::Factory::NewExternalArray(kLargeElementCount,
10068 array_type,
10069 array_data));
10070 v8::Handle<v8::Object> large_obj = v8::Object::New();
10071 // Set the elements to be the external array.
10072 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
10073 array_type,
10074 kLargeElementCount);
10075 context->Global()->Set(v8_str("large_array"), large_obj);
10076 // Initialize contents of a few rows.
10077 for (int x = 0; x < 300; x++) {
10078 int row = 0;
10079 int offset = row * 300 * 4;
10080 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10081 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10082 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10083 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10084 row = 150;
10085 offset = row * 300 * 4;
10086 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10087 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10088 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10089 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10090 row = 298;
10091 offset = row * 300 * 4;
10092 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10093 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10094 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10095 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10096 }
10097 // The goal of the code below is to make "offset" large enough
10098 // that the computation of the index (which goes into eax) has
10099 // high bits set which will not be overwritten by a byte or short
10100 // load.
10101 result = CompileRun("var failed = false;"
10102 "var offset = 0;"
10103 "for (var i = 0; i < 300; i++) {"
10104 " if (large_array[4 * i] != 127 ||"
10105 " large_array[4 * i + 1] != 0 ||"
10106 " large_array[4 * i + 2] != 0 ||"
10107 " large_array[4 * i + 3] != 127) {"
10108 " failed = true;"
10109 " }"
10110 "}"
10111 "offset = 150 * 300 * 4;"
10112 "for (var i = 0; i < 300; i++) {"
10113 " if (large_array[offset + 4 * i] != 127 ||"
10114 " large_array[offset + 4 * i + 1] != 0 ||"
10115 " large_array[offset + 4 * i + 2] != 0 ||"
10116 " large_array[offset + 4 * i + 3] != 127) {"
10117 " failed = true;"
10118 " }"
10119 "}"
10120 "offset = 298 * 300 * 4;"
10121 "for (var i = 0; i < 300; i++) {"
10122 " if (large_array[offset + 4 * i] != 127 ||"
10123 " large_array[offset + 4 * i + 1] != 0 ||"
10124 " large_array[offset + 4 * i + 2] != 0 ||"
10125 " large_array[offset + 4 * i + 3] != 127) {"
10126 " failed = true;"
10127 " }"
10128 "}"
10129 "!failed;");
10130 CHECK_EQ(true, result->BooleanValue());
10131 free(large_array_data);
10132 }
10133
Steve Block3ce2e202009-11-05 08:53:23 +000010134 free(array_data);
10135}
10136
10137
10138THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010139 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010140 v8::kExternalByteArray,
10141 -128,
10142 127);
10143}
10144
10145
10146THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010147 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010148 v8::kExternalUnsignedByteArray,
10149 0,
10150 255);
10151}
10152
10153
10154THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010155 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010156 v8::kExternalShortArray,
10157 -32768,
10158 32767);
10159}
10160
10161
10162THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010163 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010164 v8::kExternalUnsignedShortArray,
10165 0,
10166 65535);
10167}
10168
10169
10170THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010171 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010172 v8::kExternalIntArray,
10173 INT_MIN, // -2147483648
10174 INT_MAX); // 2147483647
10175}
10176
10177
10178THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010179 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010180 v8::kExternalUnsignedIntArray,
10181 0,
10182 UINT_MAX); // 4294967295
10183}
10184
10185
10186THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010187 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000010188 v8::kExternalFloatArray,
10189 -500,
10190 500);
10191}
10192
10193
10194THREADED_TEST(ExternalArrays) {
10195 TestExternalByteArray();
10196 TestExternalUnsignedByteArray();
10197 TestExternalShortArray();
10198 TestExternalUnsignedShortArray();
10199 TestExternalIntArray();
10200 TestExternalUnsignedIntArray();
10201 TestExternalFloatArray();
10202}
10203
10204
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010205void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10206 v8::HandleScope scope;
10207 LocalContext context;
10208 for (int size = 0; size < 100; size += 10) {
10209 int element_size = ExternalArrayElementSize(array_type);
10210 void* external_data = malloc(size * element_size);
10211 v8::Handle<v8::Object> obj = v8::Object::New();
10212 obj->SetIndexedPropertiesToExternalArrayData(
10213 external_data, array_type, size);
10214 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10215 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10216 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10217 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10218 free(external_data);
10219 }
10220}
10221
10222
10223THREADED_TEST(ExternalArrayInfo) {
10224 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10225 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10226 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10227 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10228 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10229 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10230 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10231}
10232
10233
Steve Blocka7e24c12009-10-30 11:49:00 +000010234THREADED_TEST(ScriptContextDependence) {
10235 v8::HandleScope scope;
10236 LocalContext c1;
10237 const char *source = "foo";
10238 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10239 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10240 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10241 CHECK_EQ(dep->Run()->Int32Value(), 100);
10242 CHECK_EQ(indep->Run()->Int32Value(), 100);
10243 LocalContext c2;
10244 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10245 CHECK_EQ(dep->Run()->Int32Value(), 100);
10246 CHECK_EQ(indep->Run()->Int32Value(), 101);
10247}
10248
10249
10250THREADED_TEST(StackTrace) {
10251 v8::HandleScope scope;
10252 LocalContext context;
10253 v8::TryCatch try_catch;
10254 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10255 v8::Handle<v8::String> src = v8::String::New(source);
10256 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10257 v8::Script::New(src, origin)->Run();
10258 CHECK(try_catch.HasCaught());
10259 v8::String::Utf8Value stack(try_catch.StackTrace());
10260 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10261}
10262
10263
Kristian Monsen25f61362010-05-21 11:50:48 +010010264// Checks that a StackFrame has certain expected values.
10265void checkStackFrame(const char* expected_script_name,
10266 const char* expected_func_name, int expected_line_number,
10267 int expected_column, bool is_eval, bool is_constructor,
10268 v8::Handle<v8::StackFrame> frame) {
10269 v8::HandleScope scope;
10270 v8::String::Utf8Value func_name(frame->GetFunctionName());
10271 v8::String::Utf8Value script_name(frame->GetScriptName());
10272 if (*script_name == NULL) {
10273 // The situation where there is no associated script, like for evals.
10274 CHECK(expected_script_name == NULL);
10275 } else {
10276 CHECK(strstr(*script_name, expected_script_name) != NULL);
10277 }
10278 CHECK(strstr(*func_name, expected_func_name) != NULL);
10279 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10280 CHECK_EQ(expected_column, frame->GetColumn());
10281 CHECK_EQ(is_eval, frame->IsEval());
10282 CHECK_EQ(is_constructor, frame->IsConstructor());
10283}
10284
10285
10286v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10287 v8::HandleScope scope;
10288 const char* origin = "capture-stack-trace-test";
10289 const int kOverviewTest = 1;
10290 const int kDetailedTest = 2;
10291
10292 ASSERT(args.Length() == 1);
10293
10294 int testGroup = args[0]->Int32Value();
10295 if (testGroup == kOverviewTest) {
10296 v8::Handle<v8::StackTrace> stackTrace =
10297 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10298 CHECK_EQ(4, stackTrace->GetFrameCount());
10299 checkStackFrame(origin, "bar", 2, 10, false, false,
10300 stackTrace->GetFrame(0));
10301 checkStackFrame(origin, "foo", 6, 3, false, false,
10302 stackTrace->GetFrame(1));
10303 checkStackFrame(NULL, "", 1, 1, false, false,
10304 stackTrace->GetFrame(2));
10305 // The last frame is an anonymous function that has the initial call.
10306 checkStackFrame(origin, "", 8, 7, false, false,
10307 stackTrace->GetFrame(3));
10308
10309 CHECK(stackTrace->AsArray()->IsArray());
10310 } else if (testGroup == kDetailedTest) {
10311 v8::Handle<v8::StackTrace> stackTrace =
10312 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10313 CHECK_EQ(4, stackTrace->GetFrameCount());
10314 checkStackFrame(origin, "bat", 4, 22, false, false,
10315 stackTrace->GetFrame(0));
10316 checkStackFrame(origin, "baz", 8, 3, false, true,
10317 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010318#ifdef ENABLE_DEBUGGER_SUPPORT
10319 bool is_eval = true;
10320#else // ENABLE_DEBUGGER_SUPPORT
10321 bool is_eval = false;
10322#endif // ENABLE_DEBUGGER_SUPPORT
10323
10324 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010325 stackTrace->GetFrame(2));
10326 // The last frame is an anonymous function that has the initial call to foo.
10327 checkStackFrame(origin, "", 10, 1, false, false,
10328 stackTrace->GetFrame(3));
10329
10330 CHECK(stackTrace->AsArray()->IsArray());
10331 }
10332 return v8::Undefined();
10333}
10334
10335
10336// Tests the C++ StackTrace API.
10337THREADED_TEST(CaptureStackTrace) {
10338 v8::HandleScope scope;
10339 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10340 Local<ObjectTemplate> templ = ObjectTemplate::New();
10341 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10342 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10343 LocalContext context(0, templ);
10344
10345 // Test getting OVERVIEW information. Should ignore information that is not
10346 // script name, function name, line number, and column offset.
10347 const char *overview_source =
10348 "function bar() {\n"
10349 " var y; AnalyzeStackInNativeCode(1);\n"
10350 "}\n"
10351 "function foo() {\n"
10352 "\n"
10353 " bar();\n"
10354 "}\n"
10355 "var x;eval('new foo();');";
10356 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10357 v8::Handle<Value> overview_result =
10358 v8::Script::New(overview_src, origin)->Run();
10359 ASSERT(!overview_result.IsEmpty());
10360 ASSERT(overview_result->IsObject());
10361
10362 // Test getting DETAILED information.
10363 const char *detailed_source =
10364 "function bat() {AnalyzeStackInNativeCode(2);\n"
10365 "}\n"
10366 "\n"
10367 "function baz() {\n"
10368 " bat();\n"
10369 "}\n"
10370 "eval('new baz();');";
10371 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10372 // Make the script using a non-zero line and column offset.
10373 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10374 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10375 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10376 v8::Handle<v8::Script> detailed_script(
10377 v8::Script::New(detailed_src, &detailed_origin));
10378 v8::Handle<Value> detailed_result = detailed_script->Run();
10379 ASSERT(!detailed_result.IsEmpty());
10380 ASSERT(detailed_result->IsObject());
10381}
10382
10383
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010384static void StackTraceForUncaughtExceptionListener(
10385 v8::Handle<v8::Message> message,
10386 v8::Handle<Value>) {
10387 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
10388 CHECK_EQ(2, stack_trace->GetFrameCount());
10389 checkStackFrame("origin", "foo", 2, 3, false, false,
10390 stack_trace->GetFrame(0));
10391 checkStackFrame("origin", "bar", 5, 3, false, false,
10392 stack_trace->GetFrame(1));
10393}
10394
10395TEST(CaptureStackTraceForUncaughtException) {
10396 report_count = 0;
10397 v8::HandleScope scope;
10398 LocalContext env;
10399 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
10400 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
10401
10402 Script::Compile(v8_str("function foo() {\n"
10403 " throw 1;\n"
10404 "};\n"
10405 "function bar() {\n"
10406 " foo();\n"
10407 "};"),
10408 v8_str("origin"))->Run();
10409 v8::Local<v8::Object> global = env->Global();
10410 Local<Value> trouble = global->Get(v8_str("bar"));
10411 CHECK(trouble->IsFunction());
10412 Function::Cast(*trouble)->Call(global, 0, NULL);
10413 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
10414 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
10415}
10416
10417
Steve Block3ce2e202009-11-05 08:53:23 +000010418// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010419THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010420 bool rv = false;
10421 for (int i = 0; i < 100; i++) {
10422 rv = v8::V8::IdleNotification();
10423 if (rv)
10424 break;
10425 }
10426 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010427}
10428
10429
10430static uint32_t* stack_limit;
10431
10432static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10433 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10434 return v8::Undefined();
10435}
10436
10437
10438// Uses the address of a local variable to determine the stack top now.
10439// Given a size, returns an address that is that far from the current
10440// top of stack.
10441static uint32_t* ComputeStackLimit(uint32_t size) {
10442 uint32_t* answer = &size - (size / sizeof(size));
10443 // If the size is very large and the stack is very near the bottom of
10444 // memory then the calculation above may wrap around and give an address
10445 // that is above the (downwards-growing) stack. In that case we return
10446 // a very low address.
10447 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10448 return answer;
10449}
10450
10451
10452TEST(SetResourceConstraints) {
10453 static const int K = 1024;
10454 uint32_t* set_limit = ComputeStackLimit(128 * K);
10455
10456 // Set stack limit.
10457 v8::ResourceConstraints constraints;
10458 constraints.set_stack_limit(set_limit);
10459 CHECK(v8::SetResourceConstraints(&constraints));
10460
10461 // Execute a script.
10462 v8::HandleScope scope;
10463 LocalContext env;
10464 Local<v8::FunctionTemplate> fun_templ =
10465 v8::FunctionTemplate::New(GetStackLimitCallback);
10466 Local<Function> fun = fun_templ->GetFunction();
10467 env->Global()->Set(v8_str("get_stack_limit"), fun);
10468 CompileRun("get_stack_limit();");
10469
10470 CHECK(stack_limit == set_limit);
10471}
10472
10473
10474TEST(SetResourceConstraintsInThread) {
10475 uint32_t* set_limit;
10476 {
10477 v8::Locker locker;
10478 static const int K = 1024;
10479 set_limit = ComputeStackLimit(128 * K);
10480
10481 // Set stack limit.
10482 v8::ResourceConstraints constraints;
10483 constraints.set_stack_limit(set_limit);
10484 CHECK(v8::SetResourceConstraints(&constraints));
10485
10486 // Execute a script.
10487 v8::HandleScope scope;
10488 LocalContext env;
10489 Local<v8::FunctionTemplate> fun_templ =
10490 v8::FunctionTemplate::New(GetStackLimitCallback);
10491 Local<Function> fun = fun_templ->GetFunction();
10492 env->Global()->Set(v8_str("get_stack_limit"), fun);
10493 CompileRun("get_stack_limit();");
10494
10495 CHECK(stack_limit == set_limit);
10496 }
10497 {
10498 v8::Locker locker;
10499 CHECK(stack_limit == set_limit);
10500 }
10501}
Steve Block3ce2e202009-11-05 08:53:23 +000010502
10503
10504THREADED_TEST(GetHeapStatistics) {
10505 v8::HandleScope scope;
10506 LocalContext c1;
10507 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010508 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10509 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010510 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010511 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10512 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10513}
10514
10515
10516static double DoubleFromBits(uint64_t value) {
10517 double target;
10518#ifdef BIG_ENDIAN_FLOATING_POINT
10519 const int kIntSize = 4;
10520 // Somebody swapped the lower and higher half of doubles.
10521 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10522 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10523#else
10524 memcpy(&target, &value, sizeof(target));
10525#endif
10526 return target;
10527}
10528
10529
10530static uint64_t DoubleToBits(double value) {
10531 uint64_t 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 double DoubleToDateTime(double input) {
10545 double date_limit = 864e13;
10546 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10547 return i::OS::nan_value();
10548 }
10549 return (input < 0) ? -(floor(-input)) : floor(input);
10550}
10551
10552// We don't have a consistent way to write 64-bit constants syntactically, so we
10553// split them into two 32-bit constants and combine them programmatically.
10554static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10555 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10556}
10557
10558
10559THREADED_TEST(QuietSignalingNaNs) {
10560 v8::HandleScope scope;
10561 LocalContext context;
10562 v8::TryCatch try_catch;
10563
10564 // Special double values.
10565 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10566 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10567 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10568 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10569 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10570 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10571 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10572
10573 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10574 // on either side of the epoch.
10575 double date_limit = 864e13;
10576
10577 double test_values[] = {
10578 snan,
10579 qnan,
10580 infinity,
10581 max_normal,
10582 date_limit + 1,
10583 date_limit,
10584 min_normal,
10585 max_denormal,
10586 min_denormal,
10587 0,
10588 -0,
10589 -min_denormal,
10590 -max_denormal,
10591 -min_normal,
10592 -date_limit,
10593 -date_limit - 1,
10594 -max_normal,
10595 -infinity,
10596 -qnan,
10597 -snan
10598 };
10599 int num_test_values = 20;
10600
10601 for (int i = 0; i < num_test_values; i++) {
10602 double test_value = test_values[i];
10603
10604 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10605 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10606 double stored_number = number->NumberValue();
10607 if (!IsNaN(test_value)) {
10608 CHECK_EQ(test_value, stored_number);
10609 } else {
10610 uint64_t stored_bits = DoubleToBits(stored_number);
10611 // Check if quiet nan (bits 51..62 all set).
10612 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10613 }
10614
10615 // Check that Date::New preserves non-NaNs in the date range and
10616 // quiets SNaNs.
10617 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10618 double expected_stored_date = DoubleToDateTime(test_value);
10619 double stored_date = date->NumberValue();
10620 if (!IsNaN(expected_stored_date)) {
10621 CHECK_EQ(expected_stored_date, stored_date);
10622 } else {
10623 uint64_t stored_bits = DoubleToBits(stored_date);
10624 // Check if quiet nan (bits 51..62 all set).
10625 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10626 }
10627 }
10628}
10629
10630
10631static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10632 v8::HandleScope scope;
10633 v8::TryCatch tc;
10634 v8::Handle<v8::String> str = args[0]->ToString();
10635 if (tc.HasCaught())
10636 return tc.ReThrow();
10637 return v8::Undefined();
10638}
10639
10640
10641// Test that an exception can be propagated down through a spaghetti
10642// stack using ReThrow.
10643THREADED_TEST(SpaghettiStackReThrow) {
10644 v8::HandleScope scope;
10645 LocalContext context;
10646 context->Global()->Set(
10647 v8::String::New("s"),
10648 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10649 v8::TryCatch try_catch;
10650 CompileRun(
10651 "var i = 0;"
10652 "var o = {"
10653 " toString: function () {"
10654 " if (i == 10) {"
10655 " throw 'Hey!';"
10656 " } else {"
10657 " i++;"
10658 " return s(o);"
10659 " }"
10660 " }"
10661 "};"
10662 "s(o);");
10663 CHECK(try_catch.HasCaught());
10664 v8::String::Utf8Value value(try_catch.Exception());
10665 CHECK_EQ(0, strcmp(*value, "Hey!"));
10666}
10667
10668
Steve Blockd0582a62009-12-15 09:54:21 +000010669TEST(Regress528) {
10670 v8::V8::Initialize();
10671
10672 v8::HandleScope scope;
10673 v8::Persistent<Context> context;
10674 v8::Persistent<Context> other_context;
10675 int gc_count;
10676
10677 // Create a context used to keep the code from aging in the compilation
10678 // cache.
10679 other_context = Context::New();
10680
10681 // Context-dependent context data creates reference from the compilation
10682 // cache to the global object.
10683 const char* source_simple = "1";
10684 context = Context::New();
10685 {
10686 v8::HandleScope scope;
10687
10688 context->Enter();
10689 Local<v8::String> obj = v8::String::New("");
10690 context->SetData(obj);
10691 CompileRun(source_simple);
10692 context->Exit();
10693 }
10694 context.Dispose();
10695 for (gc_count = 1; gc_count < 10; gc_count++) {
10696 other_context->Enter();
10697 CompileRun(source_simple);
10698 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010699 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010700 if (GetGlobalObjectsCount() == 1) break;
10701 }
10702 CHECK_GE(2, gc_count);
10703 CHECK_EQ(1, GetGlobalObjectsCount());
10704
10705 // Eval in a function creates reference from the compilation cache to the
10706 // global object.
10707 const char* source_eval = "function f(){eval('1')}; f()";
10708 context = Context::New();
10709 {
10710 v8::HandleScope scope;
10711
10712 context->Enter();
10713 CompileRun(source_eval);
10714 context->Exit();
10715 }
10716 context.Dispose();
10717 for (gc_count = 1; gc_count < 10; gc_count++) {
10718 other_context->Enter();
10719 CompileRun(source_eval);
10720 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010721 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010722 if (GetGlobalObjectsCount() == 1) break;
10723 }
10724 CHECK_GE(2, gc_count);
10725 CHECK_EQ(1, GetGlobalObjectsCount());
10726
10727 // Looking up the line number for an exception creates reference from the
10728 // compilation cache to the global object.
10729 const char* source_exception = "function f(){throw 1;} f()";
10730 context = Context::New();
10731 {
10732 v8::HandleScope scope;
10733
10734 context->Enter();
10735 v8::TryCatch try_catch;
10736 CompileRun(source_exception);
10737 CHECK(try_catch.HasCaught());
10738 v8::Handle<v8::Message> message = try_catch.Message();
10739 CHECK(!message.IsEmpty());
10740 CHECK_EQ(1, message->GetLineNumber());
10741 context->Exit();
10742 }
10743 context.Dispose();
10744 for (gc_count = 1; gc_count < 10; gc_count++) {
10745 other_context->Enter();
10746 CompileRun(source_exception);
10747 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010748 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010749 if (GetGlobalObjectsCount() == 1) break;
10750 }
10751 CHECK_GE(2, gc_count);
10752 CHECK_EQ(1, GetGlobalObjectsCount());
10753
10754 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010755}
Andrei Popescu402d9372010-02-26 13:31:12 +000010756
10757
10758THREADED_TEST(ScriptOrigin) {
10759 v8::HandleScope scope;
10760 LocalContext env;
10761 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10762 v8::Handle<v8::String> script = v8::String::New(
10763 "function f() {}\n\nfunction g() {}");
10764 v8::Script::Compile(script, &origin)->Run();
10765 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10766 env->Global()->Get(v8::String::New("f")));
10767 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10768 env->Global()->Get(v8::String::New("g")));
10769
10770 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10771 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10772 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10773
10774 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10775 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10776 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10777}
10778
10779
10780THREADED_TEST(ScriptLineNumber) {
10781 v8::HandleScope scope;
10782 LocalContext env;
10783 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10784 v8::Handle<v8::String> script = v8::String::New(
10785 "function f() {}\n\nfunction g() {}");
10786 v8::Script::Compile(script, &origin)->Run();
10787 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10788 env->Global()->Get(v8::String::New("f")));
10789 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10790 env->Global()->Get(v8::String::New("g")));
10791 CHECK_EQ(0, f->GetScriptLineNumber());
10792 CHECK_EQ(2, g->GetScriptLineNumber());
10793}
10794
10795
10796static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10797 const AccessorInfo& info) {
10798 return v8_num(42);
10799}
10800
10801
10802static void SetterWhichSetsYOnThisTo23(Local<String> name,
10803 Local<Value> value,
10804 const AccessorInfo& info) {
10805 info.This()->Set(v8_str("y"), v8_num(23));
10806}
10807
10808
Steve Block6ded16b2010-05-10 14:33:55 +010010809TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010810 v8::HandleScope scope;
10811 Local<ObjectTemplate> templ = ObjectTemplate::New();
10812 templ->SetAccessor(v8_str("x"),
10813 GetterWhichReturns42,
10814 SetterWhichSetsYOnThisTo23);
10815 LocalContext context;
10816 context->Global()->Set(v8_str("P"), templ->NewInstance());
10817 CompileRun("function C1() {"
10818 " this.x = 23;"
10819 "};"
10820 "C1.prototype = P;"
10821 "function C2() {"
10822 " this.x = 23"
10823 "};"
10824 "C2.prototype = { };"
10825 "C2.prototype.__proto__ = P;");
10826
10827 v8::Local<v8::Script> script;
10828 script = v8::Script::Compile(v8_str("new C1();"));
10829 for (int i = 0; i < 10; i++) {
10830 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10831 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10832 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10833 }
10834
10835 script = v8::Script::Compile(v8_str("new C2();"));
10836 for (int i = 0; i < 10; i++) {
10837 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10838 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10839 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10840 }
10841}
10842
10843
10844static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10845 Local<String> name, const AccessorInfo& info) {
10846 return v8_num(42);
10847}
10848
10849
10850static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10851 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10852 if (name->Equals(v8_str("x"))) {
10853 info.This()->Set(v8_str("y"), v8_num(23));
10854 }
10855 return v8::Handle<Value>();
10856}
10857
10858
10859THREADED_TEST(InterceptorOnConstructorPrototype) {
10860 v8::HandleScope scope;
10861 Local<ObjectTemplate> templ = ObjectTemplate::New();
10862 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10863 NamedPropertySetterWhichSetsYOnThisTo23);
10864 LocalContext context;
10865 context->Global()->Set(v8_str("P"), templ->NewInstance());
10866 CompileRun("function C1() {"
10867 " this.x = 23;"
10868 "};"
10869 "C1.prototype = P;"
10870 "function C2() {"
10871 " this.x = 23"
10872 "};"
10873 "C2.prototype = { };"
10874 "C2.prototype.__proto__ = P;");
10875
10876 v8::Local<v8::Script> script;
10877 script = v8::Script::Compile(v8_str("new C1();"));
10878 for (int i = 0; i < 10; i++) {
10879 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10880 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10881 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10882 }
10883
10884 script = v8::Script::Compile(v8_str("new C2();"));
10885 for (int i = 0; i < 10; i++) {
10886 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10887 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10888 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10889 }
10890}
Steve Block6ded16b2010-05-10 14:33:55 +010010891
10892
10893TEST(Bug618) {
10894 const char* source = "function C1() {"
10895 " this.x = 23;"
10896 "};"
10897 "C1.prototype = P;";
10898
10899 v8::HandleScope scope;
10900 LocalContext context;
10901 v8::Local<v8::Script> script;
10902
10903 // Use a simple object as prototype.
10904 v8::Local<v8::Object> prototype = v8::Object::New();
10905 prototype->Set(v8_str("y"), v8_num(42));
10906 context->Global()->Set(v8_str("P"), prototype);
10907
10908 // This compile will add the code to the compilation cache.
10909 CompileRun(source);
10910
10911 script = v8::Script::Compile(v8_str("new C1();"));
10912 for (int i = 0; i < 10; i++) {
10913 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10914 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10915 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10916 }
10917
10918 // Use an API object with accessors as prototype.
10919 Local<ObjectTemplate> templ = ObjectTemplate::New();
10920 templ->SetAccessor(v8_str("x"),
10921 GetterWhichReturns42,
10922 SetterWhichSetsYOnThisTo23);
10923 context->Global()->Set(v8_str("P"), templ->NewInstance());
10924
10925 // This compile will get the code from the compilation cache.
10926 CompileRun(source);
10927
10928 script = v8::Script::Compile(v8_str("new C1();"));
10929 for (int i = 0; i < 10; i++) {
10930 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10931 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10932 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10933 }
10934}
10935
10936int prologue_call_count = 0;
10937int epilogue_call_count = 0;
10938int prologue_call_count_second = 0;
10939int epilogue_call_count_second = 0;
10940
10941void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
10942 ++prologue_call_count;
10943}
10944
10945void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
10946 ++epilogue_call_count;
10947}
10948
10949void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10950 ++prologue_call_count_second;
10951}
10952
10953void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10954 ++epilogue_call_count_second;
10955}
10956
10957TEST(GCCallbacks) {
10958 LocalContext context;
10959
10960 v8::V8::AddGCPrologueCallback(PrologueCallback);
10961 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
10962 CHECK_EQ(0, prologue_call_count);
10963 CHECK_EQ(0, epilogue_call_count);
10964 i::Heap::CollectAllGarbage(false);
10965 CHECK_EQ(1, prologue_call_count);
10966 CHECK_EQ(1, epilogue_call_count);
10967 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
10968 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
10969 i::Heap::CollectAllGarbage(false);
10970 CHECK_EQ(2, prologue_call_count);
10971 CHECK_EQ(2, epilogue_call_count);
10972 CHECK_EQ(1, prologue_call_count_second);
10973 CHECK_EQ(1, epilogue_call_count_second);
10974 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
10975 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
10976 i::Heap::CollectAllGarbage(false);
10977 CHECK_EQ(2, prologue_call_count);
10978 CHECK_EQ(2, epilogue_call_count);
10979 CHECK_EQ(2, prologue_call_count_second);
10980 CHECK_EQ(2, epilogue_call_count_second);
10981 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
10982 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
10983 i::Heap::CollectAllGarbage(false);
10984 CHECK_EQ(2, prologue_call_count);
10985 CHECK_EQ(2, epilogue_call_count);
10986 CHECK_EQ(2, prologue_call_count_second);
10987 CHECK_EQ(2, epilogue_call_count_second);
10988}
Kristian Monsen25f61362010-05-21 11:50:48 +010010989
10990
10991THREADED_TEST(AddToJSFunctionResultCache) {
10992 i::FLAG_allow_natives_syntax = true;
10993 v8::HandleScope scope;
10994
10995 LocalContext context;
10996
10997 const char* code =
10998 "(function() {"
10999 " var key0 = 'a';"
11000 " var key1 = 'b';"
11001 " var r0 = %_GetFromCache(0, key0);"
11002 " var r1 = %_GetFromCache(0, key1);"
11003 " var r0_ = %_GetFromCache(0, key0);"
11004 " if (r0 !== r0_)"
11005 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
11006 " var r1_ = %_GetFromCache(0, key1);"
11007 " if (r1 !== r1_)"
11008 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
11009 " return 'PASSED';"
11010 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011011 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011012 ExpectString(code, "PASSED");
11013}
11014
11015
11016static const int k0CacheSize = 16;
11017
11018THREADED_TEST(FillJSFunctionResultCache) {
11019 i::FLAG_allow_natives_syntax = true;
11020 v8::HandleScope scope;
11021
11022 LocalContext context;
11023
11024 const char* code =
11025 "(function() {"
11026 " var k = 'a';"
11027 " var r = %_GetFromCache(0, k);"
11028 " for (var i = 0; i < 16; i++) {"
11029 " %_GetFromCache(0, 'a' + i);"
11030 " };"
11031 " if (r === %_GetFromCache(0, k))"
11032 " return 'FAILED: k0CacheSize is too small';"
11033 " return 'PASSED';"
11034 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011035 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011036 ExpectString(code, "PASSED");
11037}
11038
11039
11040THREADED_TEST(RoundRobinGetFromCache) {
11041 i::FLAG_allow_natives_syntax = true;
11042 v8::HandleScope scope;
11043
11044 LocalContext context;
11045
11046 const char* code =
11047 "(function() {"
11048 " var keys = [];"
11049 " for (var i = 0; i < 16; i++) keys.push(i);"
11050 " var values = [];"
11051 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11052 " for (var i = 0; i < 16; i++) {"
11053 " var v = %_GetFromCache(0, keys[i]);"
11054 " if (v !== values[i])"
11055 " return 'Wrong value for ' + "
11056 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11057 " };"
11058 " return 'PASSED';"
11059 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011060 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011061 ExpectString(code, "PASSED");
11062}
11063
11064
11065THREADED_TEST(ReverseGetFromCache) {
11066 i::FLAG_allow_natives_syntax = true;
11067 v8::HandleScope scope;
11068
11069 LocalContext context;
11070
11071 const char* code =
11072 "(function() {"
11073 " var keys = [];"
11074 " for (var i = 0; i < 16; i++) keys.push(i);"
11075 " var values = [];"
11076 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11077 " for (var i = 15; i >= 16; i--) {"
11078 " var v = %_GetFromCache(0, keys[i]);"
11079 " if (v !== values[i])"
11080 " return 'Wrong value for ' + "
11081 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11082 " };"
11083 " return 'PASSED';"
11084 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011085 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011086 ExpectString(code, "PASSED");
11087}
11088
11089
11090THREADED_TEST(TestEviction) {
11091 i::FLAG_allow_natives_syntax = true;
11092 v8::HandleScope scope;
11093
11094 LocalContext context;
11095
11096 const char* code =
11097 "(function() {"
11098 " for (var i = 0; i < 2*16; i++) {"
11099 " %_GetFromCache(0, 'a' + i);"
11100 " };"
11101 " return 'PASSED';"
11102 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011103 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011104 ExpectString(code, "PASSED");
11105}
Steve Block8defd9f2010-07-08 12:39:36 +010011106
11107
11108THREADED_TEST(TwoByteStringInAsciiCons) {
11109 // See Chromium issue 47824.
11110 v8::HandleScope scope;
11111
11112 LocalContext context;
11113 const char* init_code =
11114 "var str1 = 'abelspendabel';"
11115 "var str2 = str1 + str1 + str1;"
11116 "str2;";
11117 Local<Value> result = CompileRun(init_code);
11118
11119 CHECK(result->IsString());
11120 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
11121 int length = string->length();
11122 CHECK(string->IsAsciiRepresentation());
11123
11124 FlattenString(string);
11125 i::Handle<i::String> flat_string = FlattenGetString(string);
11126
11127 CHECK(string->IsAsciiRepresentation());
11128 CHECK(flat_string->IsAsciiRepresentation());
11129
11130 // Create external resource.
11131 uint16_t* uc16_buffer = new uint16_t[length + 1];
11132
11133 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
11134 uc16_buffer[length] = 0;
11135
11136 TestResource resource(uc16_buffer);
11137
11138 flat_string->MakeExternal(&resource);
11139
11140 CHECK(flat_string->IsTwoByteRepresentation());
11141
11142 // At this point, we should have a Cons string which is flat and ASCII,
11143 // with a first half that is a two-byte string (although it only contains
11144 // ASCII characters). This is a valid sequence of steps, and it can happen
11145 // in real pages.
11146
11147 CHECK(string->IsAsciiRepresentation());
11148 i::ConsString* cons = i::ConsString::cast(*string);
11149 CHECK_EQ(0, cons->second()->length());
11150 CHECK(cons->first()->IsTwoByteRepresentation());
11151
11152 // Check that some string operations work.
11153
11154 // Atom RegExp.
11155 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
11156 CHECK_EQ(6, reresult->Int32Value());
11157
11158 // Nonatom RegExp.
11159 reresult = CompileRun("str2.match(/abe./g).length;");
11160 CHECK_EQ(6, reresult->Int32Value());
11161
11162 reresult = CompileRun("str2.search(/bel/g);");
11163 CHECK_EQ(1, reresult->Int32Value());
11164
11165 reresult = CompileRun("str2.search(/be./g);");
11166 CHECK_EQ(1, reresult->Int32Value());
11167
11168 ExpectTrue("/bel/g.test(str2);");
11169
11170 ExpectTrue("/be./g.test(str2);");
11171
11172 reresult = CompileRun("/bel/g.exec(str2);");
11173 CHECK(!reresult->IsNull());
11174
11175 reresult = CompileRun("/be./g.exec(str2);");
11176 CHECK(!reresult->IsNull());
11177
11178 ExpectString("str2.substring(2, 10);", "elspenda");
11179
11180 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
11181
11182 ExpectString("str2.charAt(2);", "e");
11183
11184 reresult = CompileRun("str2.charCodeAt(2);");
11185 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
11186}