blob: 82b93c95cd0feb7f529ca2a18b295b3e4ae77e31 [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
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008018void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
Steve Blocka7e24c12009-10-30 11:49:00 +00008019 v8::HandleScope scope;
8020 bad_handle = v8::Persistent<v8::Object>::New(some_object);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008021 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008022}
8023
8024
8025THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8026 LocalContext context;
8027
8028 v8::Persistent<v8::Object> handle1, handle2;
8029 {
8030 v8::HandleScope scope;
8031 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8032 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8033 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8034 }
8035 // Note: order is implementation dependent alas: currently
8036 // global handle nodes are processed by PostGarbageCollectionProcessing
8037 // in reverse allocation order, so if second allocated handle is deleted,
8038 // weak callback of the first handle would be able to 'reallocate' it.
8039 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8040 handle2.Dispose();
8041 i::Heap::CollectAllGarbage(false);
8042}
8043
8044
8045v8::Persistent<v8::Object> to_be_disposed;
8046
8047void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8048 to_be_disposed.Dispose();
8049 i::Heap::CollectAllGarbage(false);
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008050 handle.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +00008051}
8052
8053
8054THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8055 LocalContext context;
8056
8057 v8::Persistent<v8::Object> handle1, handle2;
8058 {
8059 v8::HandleScope scope;
8060 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8061 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8062 }
8063 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8064 to_be_disposed = handle2;
8065 i::Heap::CollectAllGarbage(false);
8066}
8067
Steve Blockd0582a62009-12-15 09:54:21 +00008068void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8069 handle.Dispose();
8070}
8071
8072void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8073 v8::HandleScope scope;
8074 v8::Persistent<v8::Object>::New(v8::Object::New());
Kristian Monsen50ef84f2010-07-29 15:18:00 +01008075 handle.Dispose();
Steve Blockd0582a62009-12-15 09:54:21 +00008076}
8077
8078
8079THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8080 LocalContext context;
8081
8082 v8::Persistent<v8::Object> handle1, handle2, handle3;
8083 {
8084 v8::HandleScope scope;
8085 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8086 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8087 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8088 }
8089 handle2.MakeWeak(NULL, DisposingCallback);
8090 handle3.MakeWeak(NULL, HandleCreatingCallback);
8091 i::Heap::CollectAllGarbage(false);
8092}
8093
Steve Blocka7e24c12009-10-30 11:49:00 +00008094
8095THREADED_TEST(CheckForCrossContextObjectLiterals) {
8096 v8::V8::Initialize();
8097
8098 const int nof = 2;
8099 const char* sources[nof] = {
8100 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8101 "Object()"
8102 };
8103
8104 for (int i = 0; i < nof; i++) {
8105 const char* source = sources[i];
8106 { v8::HandleScope scope;
8107 LocalContext context;
8108 CompileRun(source);
8109 }
8110 { v8::HandleScope scope;
8111 LocalContext context;
8112 CompileRun(source);
8113 }
8114 }
8115}
8116
8117
8118static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8119 v8::HandleScope inner;
8120 env->Enter();
8121 v8::Handle<Value> three = v8_num(3);
8122 v8::Handle<Value> value = inner.Close(three);
8123 env->Exit();
8124 return value;
8125}
8126
8127
8128THREADED_TEST(NestedHandleScopeAndContexts) {
8129 v8::HandleScope outer;
8130 v8::Persistent<Context> env = Context::New();
8131 env->Enter();
8132 v8::Handle<Value> value = NestedScope(env);
8133 v8::Handle<String> str = value->ToString();
8134 env->Exit();
8135 env.Dispose();
8136}
8137
8138
8139THREADED_TEST(ExternalAllocatedMemory) {
8140 v8::HandleScope outer;
8141 v8::Persistent<Context> env = Context::New();
8142 const int kSize = 1024*1024;
8143 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8144 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8145}
8146
8147
8148THREADED_TEST(DisposeEnteredContext) {
8149 v8::HandleScope scope;
8150 LocalContext outer;
8151 { v8::Persistent<v8::Context> inner = v8::Context::New();
8152 inner->Enter();
8153 inner.Dispose();
8154 inner.Clear();
8155 inner->Exit();
8156 }
8157}
8158
8159
8160// Regression test for issue 54, object templates with internal fields
8161// but no accessors or interceptors did not get their internal field
8162// count set on instances.
8163THREADED_TEST(Regress54) {
8164 v8::HandleScope outer;
8165 LocalContext context;
8166 static v8::Persistent<v8::ObjectTemplate> templ;
8167 if (templ.IsEmpty()) {
8168 v8::HandleScope inner;
8169 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8170 local->SetInternalFieldCount(1);
8171 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8172 }
8173 v8::Handle<v8::Object> result = templ->NewInstance();
8174 CHECK_EQ(1, result->InternalFieldCount());
8175}
8176
8177
8178// If part of the threaded tests, this test makes ThreadingTest fail
8179// on mac.
8180TEST(CatchStackOverflow) {
8181 v8::HandleScope scope;
8182 LocalContext context;
8183 v8::TryCatch try_catch;
8184 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8185 "function f() {"
8186 " return f();"
8187 "}"
8188 ""
8189 "f();"));
8190 v8::Handle<v8::Value> result = script->Run();
8191 CHECK(result.IsEmpty());
8192}
8193
8194
8195static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8196 const char* resource_name,
8197 int line_offset) {
8198 v8::HandleScope scope;
8199 v8::TryCatch try_catch;
8200 v8::Handle<v8::Value> result = script->Run();
8201 CHECK(result.IsEmpty());
8202 CHECK(try_catch.HasCaught());
8203 v8::Handle<v8::Message> message = try_catch.Message();
8204 CHECK(!message.IsEmpty());
8205 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8206 CHECK_EQ(91, message->GetStartPosition());
8207 CHECK_EQ(92, message->GetEndPosition());
8208 CHECK_EQ(2, message->GetStartColumn());
8209 CHECK_EQ(3, message->GetEndColumn());
8210 v8::String::AsciiValue line(message->GetSourceLine());
8211 CHECK_EQ(" throw 'nirk';", *line);
8212 v8::String::AsciiValue name(message->GetScriptResourceName());
8213 CHECK_EQ(resource_name, *name);
8214}
8215
8216
8217THREADED_TEST(TryCatchSourceInfo) {
8218 v8::HandleScope scope;
8219 LocalContext context;
8220 v8::Handle<v8::String> source = v8::String::New(
8221 "function Foo() {\n"
8222 " return Bar();\n"
8223 "}\n"
8224 "\n"
8225 "function Bar() {\n"
8226 " return Baz();\n"
8227 "}\n"
8228 "\n"
8229 "function Baz() {\n"
8230 " throw 'nirk';\n"
8231 "}\n"
8232 "\n"
8233 "Foo();\n");
8234
8235 const char* resource_name;
8236 v8::Handle<v8::Script> script;
8237 resource_name = "test.js";
8238 script = v8::Script::Compile(source, v8::String::New(resource_name));
8239 CheckTryCatchSourceInfo(script, resource_name, 0);
8240
8241 resource_name = "test1.js";
8242 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8243 script = v8::Script::Compile(source, &origin1);
8244 CheckTryCatchSourceInfo(script, resource_name, 0);
8245
8246 resource_name = "test2.js";
8247 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8248 script = v8::Script::Compile(source, &origin2);
8249 CheckTryCatchSourceInfo(script, resource_name, 7);
8250}
8251
8252
8253THREADED_TEST(CompilationCache) {
8254 v8::HandleScope scope;
8255 LocalContext context;
8256 v8::Handle<v8::String> source0 = v8::String::New("1234");
8257 v8::Handle<v8::String> source1 = v8::String::New("1234");
8258 v8::Handle<v8::Script> script0 =
8259 v8::Script::Compile(source0, v8::String::New("test.js"));
8260 v8::Handle<v8::Script> script1 =
8261 v8::Script::Compile(source1, v8::String::New("test.js"));
8262 v8::Handle<v8::Script> script2 =
8263 v8::Script::Compile(source0); // different origin
8264 CHECK_EQ(1234, script0->Run()->Int32Value());
8265 CHECK_EQ(1234, script1->Run()->Int32Value());
8266 CHECK_EQ(1234, script2->Run()->Int32Value());
8267}
8268
8269
8270static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8271 ApiTestFuzzer::Fuzz();
8272 return v8_num(42);
8273}
8274
8275
8276THREADED_TEST(CallbackFunctionName) {
8277 v8::HandleScope scope;
8278 LocalContext context;
8279 Local<ObjectTemplate> t = ObjectTemplate::New();
8280 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8281 context->Global()->Set(v8_str("obj"), t->NewInstance());
8282 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8283 CHECK(value->IsString());
8284 v8::String::AsciiValue name(value);
8285 CHECK_EQ("asdf", *name);
8286}
8287
8288
8289THREADED_TEST(DateAccess) {
8290 v8::HandleScope scope;
8291 LocalContext context;
8292 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8293 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008294 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008295}
8296
8297
8298void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008299 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008300 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8301 CHECK_EQ(elmc, props->Length());
8302 for (int i = 0; i < elmc; i++) {
8303 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8304 CHECK_EQ(elmv[i], *elm);
8305 }
8306}
8307
8308
8309THREADED_TEST(PropertyEnumeration) {
8310 v8::HandleScope scope;
8311 LocalContext context;
8312 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8313 "var result = [];"
8314 "result[0] = {};"
8315 "result[1] = {a: 1, b: 2};"
8316 "result[2] = [1, 2, 3];"
8317 "var proto = {x: 1, y: 2, z: 3};"
8318 "var x = { __proto__: proto, w: 0, z: 1 };"
8319 "result[3] = x;"
8320 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008321 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008322 CHECK_EQ(4, elms->Length());
8323 int elmc0 = 0;
8324 const char** elmv0 = NULL;
8325 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8326 int elmc1 = 2;
8327 const char* elmv1[] = {"a", "b"};
8328 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8329 int elmc2 = 3;
8330 const char* elmv2[] = {"0", "1", "2"};
8331 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8332 int elmc3 = 4;
8333 const char* elmv3[] = {"w", "z", "x", "y"};
8334 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8335}
8336
8337
Steve Blocka7e24c12009-10-30 11:49:00 +00008338static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8339 Local<Value> name,
8340 v8::AccessType type,
8341 Local<Value> data) {
8342 return type != v8::ACCESS_SET;
8343}
8344
8345
8346static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8347 uint32_t key,
8348 v8::AccessType type,
8349 Local<Value> data) {
8350 return type != v8::ACCESS_SET;
8351}
8352
8353
8354THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8355 v8::HandleScope scope;
8356 LocalContext context;
8357 Local<ObjectTemplate> templ = ObjectTemplate::New();
8358 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8359 IndexedSetAccessBlocker);
8360 templ->Set(v8_str("x"), v8::True());
8361 Local<v8::Object> instance = templ->NewInstance();
8362 context->Global()->Set(v8_str("obj"), instance);
8363 Local<Value> value = CompileRun("obj.x");
8364 CHECK(value->BooleanValue());
8365}
8366
8367
8368static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8369 Local<Value> name,
8370 v8::AccessType type,
8371 Local<Value> data) {
8372 return false;
8373}
8374
8375
8376static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8377 uint32_t key,
8378 v8::AccessType type,
8379 Local<Value> data) {
8380 return false;
8381}
8382
8383
8384
8385THREADED_TEST(AccessChecksReenabledCorrectly) {
8386 v8::HandleScope scope;
8387 LocalContext context;
8388 Local<ObjectTemplate> templ = ObjectTemplate::New();
8389 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8390 IndexedGetAccessBlocker);
8391 templ->Set(v8_str("a"), v8_str("a"));
8392 // Add more than 8 (see kMaxFastProperties) properties
8393 // so that the constructor will force copying map.
8394 // Cannot sprintf, gcc complains unsafety.
8395 char buf[4];
8396 for (char i = '0'; i <= '9' ; i++) {
8397 buf[0] = i;
8398 for (char j = '0'; j <= '9'; j++) {
8399 buf[1] = j;
8400 for (char k = '0'; k <= '9'; k++) {
8401 buf[2] = k;
8402 buf[3] = 0;
8403 templ->Set(v8_str(buf), v8::Number::New(k));
8404 }
8405 }
8406 }
8407
8408 Local<v8::Object> instance_1 = templ->NewInstance();
8409 context->Global()->Set(v8_str("obj_1"), instance_1);
8410
8411 Local<Value> value_1 = CompileRun("obj_1.a");
8412 CHECK(value_1->IsUndefined());
8413
8414 Local<v8::Object> instance_2 = templ->NewInstance();
8415 context->Global()->Set(v8_str("obj_2"), instance_2);
8416
8417 Local<Value> value_2 = CompileRun("obj_2.a");
8418 CHECK(value_2->IsUndefined());
8419}
8420
8421
8422// This tests that access check information remains on the global
8423// object template when creating contexts.
8424THREADED_TEST(AccessControlRepeatedContextCreation) {
8425 v8::HandleScope handle_scope;
8426 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8427 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8428 IndexedSetAccessBlocker);
8429 i::Handle<i::ObjectTemplateInfo> internal_template =
8430 v8::Utils::OpenHandle(*global_template);
8431 CHECK(!internal_template->constructor()->IsUndefined());
8432 i::Handle<i::FunctionTemplateInfo> constructor(
8433 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8434 CHECK(!constructor->access_check_info()->IsUndefined());
8435 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8436 CHECK(!constructor->access_check_info()->IsUndefined());
8437}
8438
8439
8440THREADED_TEST(TurnOnAccessCheck) {
8441 v8::HandleScope handle_scope;
8442
8443 // Create an environment with access check to the global object disabled by
8444 // default.
8445 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8446 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8447 IndexedGetAccessBlocker,
8448 v8::Handle<v8::Value>(),
8449 false);
8450 v8::Persistent<Context> context = Context::New(NULL, global_template);
8451 Context::Scope context_scope(context);
8452
8453 // Set up a property and a number of functions.
8454 context->Global()->Set(v8_str("a"), v8_num(1));
8455 CompileRun("function f1() {return a;}"
8456 "function f2() {return a;}"
8457 "function g1() {return h();}"
8458 "function g2() {return h();}"
8459 "function h() {return 1;}");
8460 Local<Function> f1 =
8461 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8462 Local<Function> f2 =
8463 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8464 Local<Function> g1 =
8465 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8466 Local<Function> g2 =
8467 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8468 Local<Function> h =
8469 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8470
8471 // Get the global object.
8472 v8::Handle<v8::Object> global = context->Global();
8473
8474 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8475 // uses the runtime system to retreive property a whereas f2 uses global load
8476 // inline cache.
8477 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8478 for (int i = 0; i < 4; i++) {
8479 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8480 }
8481
8482 // Same for g1 and g2.
8483 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8484 for (int i = 0; i < 4; i++) {
8485 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8486 }
8487
8488 // Detach the global and turn on access check.
8489 context->DetachGlobal();
8490 context->Global()->TurnOnAccessCheck();
8491
8492 // Failing access check to property get results in undefined.
8493 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8494 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8495
8496 // Failing access check to function call results in exception.
8497 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8498 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8499
8500 // No failing access check when just returning a constant.
8501 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8502}
8503
8504
8505// This test verifies that pre-compilation (aka preparsing) can be called
8506// without initializing the whole VM. Thus we cannot run this test in a
8507// multi-threaded setup.
8508TEST(PreCompile) {
8509 // TODO(155): This test would break without the initialization of V8. This is
8510 // a workaround for now to make this test not fail.
8511 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008512 const char* script = "function foo(a) { return a+1; }";
8513 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008514 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008515 CHECK_NE(sd->Length(), 0);
8516 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008517 CHECK(!sd->HasError());
8518 delete sd;
8519}
8520
8521
8522TEST(PreCompileWithError) {
8523 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008524 const char* script = "function foo(a) { return 1 * * 2; }";
8525 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008526 v8::ScriptData::PreCompile(script, i::StrLength(script));
8527 CHECK(sd->HasError());
8528 delete sd;
8529}
8530
8531
8532TEST(Regress31661) {
8533 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008534 const char* script = " The Definintive Guide";
8535 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008536 v8::ScriptData::PreCompile(script, i::StrLength(script));
8537 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008538 delete sd;
8539}
8540
8541
Leon Clarkef7060e22010-06-03 12:02:55 +01008542// Tests that ScriptData can be serialized and deserialized.
8543TEST(PreCompileSerialization) {
8544 v8::V8::Initialize();
8545 const char* script = "function foo(a) { return a+1; }";
8546 v8::ScriptData* sd =
8547 v8::ScriptData::PreCompile(script, i::StrLength(script));
8548
8549 // Serialize.
8550 int serialized_data_length = sd->Length();
8551 char* serialized_data = i::NewArray<char>(serialized_data_length);
8552 memcpy(serialized_data, sd->Data(), serialized_data_length);
8553
8554 // Deserialize.
8555 v8::ScriptData* deserialized_sd =
8556 v8::ScriptData::New(serialized_data, serialized_data_length);
8557
8558 // Verify that the original is the same as the deserialized.
8559 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8560 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8561 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8562
8563 delete sd;
8564 delete deserialized_sd;
8565}
8566
8567
8568// Attempts to deserialize bad data.
8569TEST(PreCompileDeserializationError) {
8570 v8::V8::Initialize();
8571 const char* data = "DONT CARE";
8572 int invalid_size = 3;
8573 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8574
8575 CHECK_EQ(0, sd->Length());
8576
8577 delete sd;
8578}
8579
8580
Leon Clarkeac952652010-07-15 11:15:24 +01008581// Attempts to deserialize bad data.
8582TEST(PreCompileInvalidPreparseDataError) {
8583 v8::V8::Initialize();
8584 v8::HandleScope scope;
8585 LocalContext context;
8586
8587 const char* script = "function foo(){ return 5;}\n"
8588 "function bar(){ return 6 + 7;} foo();";
8589 v8::ScriptData* sd =
8590 v8::ScriptData::PreCompile(script, i::StrLength(script));
8591 CHECK(!sd->HasError());
8592 // ScriptDataImpl private implementation details
8593 const int kUnsignedSize = sizeof(unsigned);
8594 const int kHeaderSize = 4;
8595 const int kFunctionEntrySize = 4;
8596 const int kFunctionEntryStartOffset = 0;
8597 const int kFunctionEntryEndOffset = 1;
8598 unsigned* sd_data =
8599 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
8600 CHECK_EQ(sd->Length(),
8601 (kHeaderSize + 2 * kFunctionEntrySize) * kUnsignedSize);
8602
8603 // Overwrite function bar's end position with 0.
8604 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
8605 v8::TryCatch try_catch;
8606
8607 Local<String> source = String::New(script);
8608 Local<Script> compiled_script = Script::New(source, NULL, sd);
8609 CHECK(try_catch.HasCaught());
8610 String::AsciiValue exception_value(try_catch.Message()->Get());
8611 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8612 *exception_value);
8613
8614 try_catch.Reset();
8615 // Overwrite function bar's start position with 200. The function entry
8616 // will not be found when searching for it by position.
8617 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
8618 200;
8619 compiled_script = Script::New(source, NULL, sd);
8620 CHECK(try_catch.HasCaught());
8621 String::AsciiValue second_exception_value(try_catch.Message()->Get());
8622 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
8623 *second_exception_value);
8624
8625 delete sd;
8626}
8627
8628
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008629// Verifies that the Handle<String> and const char* versions of the API produce
8630// the same results (at least for one trivial case).
8631TEST(PreCompileAPIVariationsAreSame) {
8632 v8::V8::Initialize();
8633 v8::HandleScope scope;
8634
8635 const char* cstring = "function foo(a) { return a+1; }";
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008636
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008637 v8::ScriptData* sd_from_cstring =
8638 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8639
8640 TestAsciiResource* resource = new TestAsciiResource(cstring);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008641 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile(
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008642 v8::String::NewExternal(resource));
8643
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008644 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile(
8645 v8::String::New(cstring));
8646
8647 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008648 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008649 sd_from_external_string->Data(),
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008650 sd_from_cstring->Length()));
8651
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008652 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length());
8653 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8654 sd_from_string->Data(),
8655 sd_from_cstring->Length()));
8656
8657
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008658 delete sd_from_cstring;
Ben Murdoch3bec4d22010-07-22 14:51:16 +01008659 delete sd_from_external_string;
8660 delete sd_from_string;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008661}
8662
8663
Steve Blocka7e24c12009-10-30 11:49:00 +00008664// This tests that we do not allow dictionary load/call inline caches
8665// to use functions that have not yet been compiled. The potential
8666// problem of loading a function that has not yet been compiled can
8667// arise because we share code between contexts via the compilation
8668// cache.
8669THREADED_TEST(DictionaryICLoadedFunction) {
8670 v8::HandleScope scope;
8671 // Test LoadIC.
8672 for (int i = 0; i < 2; i++) {
8673 LocalContext context;
8674 context->Global()->Set(v8_str("tmp"), v8::True());
8675 context->Global()->Delete(v8_str("tmp"));
8676 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8677 }
8678 // Test CallIC.
8679 for (int i = 0; i < 2; i++) {
8680 LocalContext context;
8681 context->Global()->Set(v8_str("tmp"), v8::True());
8682 context->Global()->Delete(v8_str("tmp"));
8683 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8684 }
8685}
8686
8687
8688// Test that cross-context new calls use the context of the callee to
8689// create the new JavaScript object.
8690THREADED_TEST(CrossContextNew) {
8691 v8::HandleScope scope;
8692 v8::Persistent<Context> context0 = Context::New();
8693 v8::Persistent<Context> context1 = Context::New();
8694
8695 // Allow cross-domain access.
8696 Local<String> token = v8_str("<security token>");
8697 context0->SetSecurityToken(token);
8698 context1->SetSecurityToken(token);
8699
8700 // Set an 'x' property on the Object prototype and define a
8701 // constructor function in context0.
8702 context0->Enter();
8703 CompileRun("Object.prototype.x = 42; function C() {};");
8704 context0->Exit();
8705
8706 // Call the constructor function from context0 and check that the
8707 // result has the 'x' property.
8708 context1->Enter();
8709 context1->Global()->Set(v8_str("other"), context0->Global());
8710 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8711 CHECK(value->IsInt32());
8712 CHECK_EQ(42, value->Int32Value());
8713 context1->Exit();
8714
8715 // Dispose the contexts to allow them to be garbage collected.
8716 context0.Dispose();
8717 context1.Dispose();
8718}
8719
8720
8721class RegExpInterruptTest {
8722 public:
8723 RegExpInterruptTest() : block_(NULL) {}
8724 ~RegExpInterruptTest() { delete block_; }
8725 void RunTest() {
8726 block_ = i::OS::CreateSemaphore(0);
8727 gc_count_ = 0;
8728 gc_during_regexp_ = 0;
8729 regexp_success_ = false;
8730 gc_success_ = false;
8731 GCThread gc_thread(this);
8732 gc_thread.Start();
8733 v8::Locker::StartPreemption(1);
8734
8735 LongRunningRegExp();
8736 {
8737 v8::Unlocker unlock;
8738 gc_thread.Join();
8739 }
8740 v8::Locker::StopPreemption();
8741 CHECK(regexp_success_);
8742 CHECK(gc_success_);
8743 }
8744 private:
8745 // Number of garbage collections required.
8746 static const int kRequiredGCs = 5;
8747
8748 class GCThread : public i::Thread {
8749 public:
8750 explicit GCThread(RegExpInterruptTest* test)
8751 : test_(test) {}
8752 virtual void Run() {
8753 test_->CollectGarbage();
8754 }
8755 private:
8756 RegExpInterruptTest* test_;
8757 };
8758
8759 void CollectGarbage() {
8760 block_->Wait();
8761 while (gc_during_regexp_ < kRequiredGCs) {
8762 {
8763 v8::Locker lock;
8764 // TODO(lrn): Perhaps create some garbage before collecting.
8765 i::Heap::CollectAllGarbage(false);
8766 gc_count_++;
8767 }
8768 i::OS::Sleep(1);
8769 }
8770 gc_success_ = true;
8771 }
8772
8773 void LongRunningRegExp() {
8774 block_->Signal(); // Enable garbage collection thread on next preemption.
8775 int rounds = 0;
8776 while (gc_during_regexp_ < kRequiredGCs) {
8777 int gc_before = gc_count_;
8778 {
8779 // Match 15-30 "a"'s against 14 and a "b".
8780 const char* c_source =
8781 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8782 ".exec('aaaaaaaaaaaaaaab') === null";
8783 Local<String> source = String::New(c_source);
8784 Local<Script> script = Script::Compile(source);
8785 Local<Value> result = script->Run();
8786 if (!result->BooleanValue()) {
8787 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8788 return;
8789 }
8790 }
8791 {
8792 // Match 15-30 "a"'s against 15 and a "b".
8793 const char* c_source =
8794 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8795 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8796 Local<String> source = String::New(c_source);
8797 Local<Script> script = Script::Compile(source);
8798 Local<Value> result = script->Run();
8799 if (!result->BooleanValue()) {
8800 gc_during_regexp_ = kRequiredGCs;
8801 return;
8802 }
8803 }
8804 int gc_after = gc_count_;
8805 gc_during_regexp_ += gc_after - gc_before;
8806 rounds++;
8807 i::OS::Sleep(1);
8808 }
8809 regexp_success_ = true;
8810 }
8811
8812 i::Semaphore* block_;
8813 int gc_count_;
8814 int gc_during_regexp_;
8815 bool regexp_success_;
8816 bool gc_success_;
8817};
8818
8819
8820// Test that a regular expression execution can be interrupted and
8821// survive a garbage collection.
8822TEST(RegExpInterruption) {
8823 v8::Locker lock;
8824 v8::V8::Initialize();
8825 v8::HandleScope scope;
8826 Local<Context> local_env;
8827 {
8828 LocalContext env;
8829 local_env = env.local();
8830 }
8831
8832 // Local context should still be live.
8833 CHECK(!local_env.IsEmpty());
8834 local_env->Enter();
8835
8836 // Should complete without problems.
8837 RegExpInterruptTest().RunTest();
8838
8839 local_env->Exit();
8840}
8841
8842
8843class ApplyInterruptTest {
8844 public:
8845 ApplyInterruptTest() : block_(NULL) {}
8846 ~ApplyInterruptTest() { delete block_; }
8847 void RunTest() {
8848 block_ = i::OS::CreateSemaphore(0);
8849 gc_count_ = 0;
8850 gc_during_apply_ = 0;
8851 apply_success_ = false;
8852 gc_success_ = false;
8853 GCThread gc_thread(this);
8854 gc_thread.Start();
8855 v8::Locker::StartPreemption(1);
8856
8857 LongRunningApply();
8858 {
8859 v8::Unlocker unlock;
8860 gc_thread.Join();
8861 }
8862 v8::Locker::StopPreemption();
8863 CHECK(apply_success_);
8864 CHECK(gc_success_);
8865 }
8866 private:
8867 // Number of garbage collections required.
8868 static const int kRequiredGCs = 2;
8869
8870 class GCThread : public i::Thread {
8871 public:
8872 explicit GCThread(ApplyInterruptTest* test)
8873 : test_(test) {}
8874 virtual void Run() {
8875 test_->CollectGarbage();
8876 }
8877 private:
8878 ApplyInterruptTest* test_;
8879 };
8880
8881 void CollectGarbage() {
8882 block_->Wait();
8883 while (gc_during_apply_ < kRequiredGCs) {
8884 {
8885 v8::Locker lock;
8886 i::Heap::CollectAllGarbage(false);
8887 gc_count_++;
8888 }
8889 i::OS::Sleep(1);
8890 }
8891 gc_success_ = true;
8892 }
8893
8894 void LongRunningApply() {
8895 block_->Signal();
8896 int rounds = 0;
8897 while (gc_during_apply_ < kRequiredGCs) {
8898 int gc_before = gc_count_;
8899 {
8900 const char* c_source =
8901 "function do_very_little(bar) {"
8902 " this.foo = bar;"
8903 "}"
8904 "for (var i = 0; i < 100000; i++) {"
8905 " do_very_little.apply(this, ['bar']);"
8906 "}";
8907 Local<String> source = String::New(c_source);
8908 Local<Script> script = Script::Compile(source);
8909 Local<Value> result = script->Run();
8910 // Check that no exception was thrown.
8911 CHECK(!result.IsEmpty());
8912 }
8913 int gc_after = gc_count_;
8914 gc_during_apply_ += gc_after - gc_before;
8915 rounds++;
8916 }
8917 apply_success_ = true;
8918 }
8919
8920 i::Semaphore* block_;
8921 int gc_count_;
8922 int gc_during_apply_;
8923 bool apply_success_;
8924 bool gc_success_;
8925};
8926
8927
8928// Test that nothing bad happens if we get a preemption just when we were
8929// about to do an apply().
8930TEST(ApplyInterruption) {
8931 v8::Locker lock;
8932 v8::V8::Initialize();
8933 v8::HandleScope scope;
8934 Local<Context> local_env;
8935 {
8936 LocalContext env;
8937 local_env = env.local();
8938 }
8939
8940 // Local context should still be live.
8941 CHECK(!local_env.IsEmpty());
8942 local_env->Enter();
8943
8944 // Should complete without problems.
8945 ApplyInterruptTest().RunTest();
8946
8947 local_env->Exit();
8948}
8949
8950
8951// Verify that we can clone an object
8952TEST(ObjectClone) {
8953 v8::HandleScope scope;
8954 LocalContext env;
8955
8956 const char* sample =
8957 "var rv = {};" \
8958 "rv.alpha = 'hello';" \
8959 "rv.beta = 123;" \
8960 "rv;";
8961
8962 // Create an object, verify basics.
8963 Local<Value> val = CompileRun(sample);
8964 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01008965 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008966 obj->Set(v8_str("gamma"), v8_str("cloneme"));
8967
8968 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
8969 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8970 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
8971
8972 // Clone it.
8973 Local<v8::Object> clone = obj->Clone();
8974 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
8975 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
8976 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
8977
8978 // Set a property on the clone, verify each object.
8979 clone->Set(v8_str("beta"), v8::Integer::New(456));
8980 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8981 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
8982}
8983
8984
8985class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
8986 public:
8987 explicit AsciiVectorResource(i::Vector<const char> vector)
8988 : data_(vector) {}
8989 virtual ~AsciiVectorResource() {}
8990 virtual size_t length() const { return data_.length(); }
8991 virtual const char* data() const { return data_.start(); }
8992 private:
8993 i::Vector<const char> data_;
8994};
8995
8996
8997class UC16VectorResource : public v8::String::ExternalStringResource {
8998 public:
8999 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
9000 : data_(vector) {}
9001 virtual ~UC16VectorResource() {}
9002 virtual size_t length() const { return data_.length(); }
9003 virtual const i::uc16* data() const { return data_.start(); }
9004 private:
9005 i::Vector<const i::uc16> data_;
9006};
9007
9008
9009static void MorphAString(i::String* string,
9010 AsciiVectorResource* ascii_resource,
9011 UC16VectorResource* uc16_resource) {
9012 CHECK(i::StringShape(string).IsExternal());
9013 if (string->IsAsciiRepresentation()) {
9014 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009015 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009016 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00009017 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009018 i::ExternalTwoByteString* morphed =
9019 i::ExternalTwoByteString::cast(string);
9020 morphed->set_resource(uc16_resource);
9021 } else {
9022 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00009023 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009024 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00009025 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00009026 i::ExternalAsciiString* morphed =
9027 i::ExternalAsciiString::cast(string);
9028 morphed->set_resource(ascii_resource);
9029 }
9030}
9031
9032
9033// Test that we can still flatten a string if the components it is built up
9034// from have been turned into 16 bit strings in the mean time.
9035THREADED_TEST(MorphCompositeStringTest) {
9036 const char* c_string = "Now is the time for all good men"
9037 " to come to the aid of the party";
9038 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
9039 {
9040 v8::HandleScope scope;
9041 LocalContext env;
9042 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009043 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009044 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009045 i::Vector<const uint16_t>(two_byte_string,
9046 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00009047
9048 Local<String> lhs(v8::Utils::ToLocal(
9049 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9050 Local<String> rhs(v8::Utils::ToLocal(
9051 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
9052
9053 env->Global()->Set(v8_str("lhs"), lhs);
9054 env->Global()->Set(v8_str("rhs"), rhs);
9055
9056 CompileRun(
9057 "var cons = lhs + rhs;"
9058 "var slice = lhs.substring(1, lhs.length - 1);"
9059 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
9060
9061 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
9062 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
9063
9064 // Now do some stuff to make sure the strings are flattened, etc.
9065 CompileRun(
9066 "/[^a-z]/.test(cons);"
9067 "/[^a-z]/.test(slice);"
9068 "/[^a-z]/.test(slice_on_cons);");
9069 const char* expected_cons =
9070 "Now is the time for all good men to come to the aid of the party"
9071 "Now is the time for all good men to come to the aid of the party";
9072 const char* expected_slice =
9073 "ow is the time for all good men to come to the aid of the part";
9074 const char* expected_slice_on_cons =
9075 "ow is the time for all good men to come to the aid of the party"
9076 "Now is the time for all good men to come to the aid of the part";
9077 CHECK_EQ(String::New(expected_cons),
9078 env->Global()->Get(v8_str("cons")));
9079 CHECK_EQ(String::New(expected_slice),
9080 env->Global()->Get(v8_str("slice")));
9081 CHECK_EQ(String::New(expected_slice_on_cons),
9082 env->Global()->Get(v8_str("slice_on_cons")));
9083 }
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009084 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009085}
9086
9087
9088TEST(CompileExternalTwoByteSource) {
9089 v8::HandleScope scope;
9090 LocalContext context;
9091
9092 // This is a very short list of sources, which currently is to check for a
9093 // regression caused by r2703.
9094 const char* ascii_sources[] = {
9095 "0.5",
9096 "-0.5", // This mainly testes PushBack in the Scanner.
9097 "--0.5", // This mainly testes PushBack in the Scanner.
9098 NULL
9099 };
9100
9101 // Compile the sources as external two byte strings.
9102 for (int i = 0; ascii_sources[i] != NULL; i++) {
9103 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9104 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009105 i::Vector<const uint16_t>(two_byte_string,
9106 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009107 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9108 v8::Script::Compile(source);
Ben Murdoch3bec4d22010-07-22 14:51:16 +01009109 i::DeleteArray(two_byte_string);
Steve Blocka7e24c12009-10-30 11:49:00 +00009110 }
9111}
9112
9113
9114class RegExpStringModificationTest {
9115 public:
9116 RegExpStringModificationTest()
9117 : block_(i::OS::CreateSemaphore(0)),
9118 morphs_(0),
9119 morphs_during_regexp_(0),
9120 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9121 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9122 ~RegExpStringModificationTest() { delete block_; }
9123 void RunTest() {
9124 regexp_success_ = false;
9125 morph_success_ = false;
9126
9127 // Initialize the contents of two_byte_content_ to be a uc16 representation
9128 // of "aaaaaaaaaaaaaab".
9129 for (int i = 0; i < 14; i++) {
9130 two_byte_content_[i] = 'a';
9131 }
9132 two_byte_content_[14] = 'b';
9133
9134 // Create the input string for the regexp - the one we are going to change
9135 // properties of.
9136 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9137
9138 // Inject the input as a global variable.
9139 i::Handle<i::String> input_name =
9140 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
9141 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
9142
9143
9144 MorphThread morph_thread(this);
9145 morph_thread.Start();
9146 v8::Locker::StartPreemption(1);
9147 LongRunningRegExp();
9148 {
9149 v8::Unlocker unlock;
9150 morph_thread.Join();
9151 }
9152 v8::Locker::StopPreemption();
9153 CHECK(regexp_success_);
9154 CHECK(morph_success_);
9155 }
9156 private:
9157
9158 // Number of string modifications required.
9159 static const int kRequiredModifications = 5;
9160 static const int kMaxModifications = 100;
9161
9162 class MorphThread : public i::Thread {
9163 public:
9164 explicit MorphThread(RegExpStringModificationTest* test)
9165 : test_(test) {}
9166 virtual void Run() {
9167 test_->MorphString();
9168 }
9169 private:
9170 RegExpStringModificationTest* test_;
9171 };
9172
9173 void MorphString() {
9174 block_->Wait();
9175 while (morphs_during_regexp_ < kRequiredModifications &&
9176 morphs_ < kMaxModifications) {
9177 {
9178 v8::Locker lock;
9179 // Swap string between ascii and two-byte representation.
9180 i::String* string = *input_;
9181 MorphAString(string, &ascii_resource_, &uc16_resource_);
9182 morphs_++;
9183 }
9184 i::OS::Sleep(1);
9185 }
9186 morph_success_ = true;
9187 }
9188
9189 void LongRunningRegExp() {
9190 block_->Signal(); // Enable morphing thread on next preemption.
9191 while (morphs_during_regexp_ < kRequiredModifications &&
9192 morphs_ < kMaxModifications) {
9193 int morphs_before = morphs_;
9194 {
9195 // Match 15-30 "a"'s against 14 and a "b".
9196 const char* c_source =
9197 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9198 ".exec(input) === null";
9199 Local<String> source = String::New(c_source);
9200 Local<Script> script = Script::Compile(source);
9201 Local<Value> result = script->Run();
9202 CHECK(result->IsTrue());
9203 }
9204 int morphs_after = morphs_;
9205 morphs_during_regexp_ += morphs_after - morphs_before;
9206 }
9207 regexp_success_ = true;
9208 }
9209
9210 i::uc16 two_byte_content_[15];
9211 i::Semaphore* block_;
9212 int morphs_;
9213 int morphs_during_regexp_;
9214 bool regexp_success_;
9215 bool morph_success_;
9216 i::Handle<i::String> input_;
9217 AsciiVectorResource ascii_resource_;
9218 UC16VectorResource uc16_resource_;
9219};
9220
9221
9222// Test that a regular expression execution can be interrupted and
9223// the string changed without failing.
9224TEST(RegExpStringModification) {
9225 v8::Locker lock;
9226 v8::V8::Initialize();
9227 v8::HandleScope scope;
9228 Local<Context> local_env;
9229 {
9230 LocalContext env;
9231 local_env = env.local();
9232 }
9233
9234 // Local context should still be live.
9235 CHECK(!local_env.IsEmpty());
9236 local_env->Enter();
9237
9238 // Should complete without problems.
9239 RegExpStringModificationTest().RunTest();
9240
9241 local_env->Exit();
9242}
9243
9244
9245// Test that we can set a property on the global object even if there
9246// is a read-only property in the prototype chain.
9247TEST(ReadOnlyPropertyInGlobalProto) {
9248 v8::HandleScope scope;
9249 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9250 LocalContext context(0, templ);
9251 v8::Handle<v8::Object> global = context->Global();
9252 v8::Handle<v8::Object> global_proto =
9253 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9254 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9255 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9256 // Check without 'eval' or 'with'.
9257 v8::Handle<v8::Value> res =
9258 CompileRun("function f() { x = 42; return x; }; f()");
9259 // Check with 'eval'.
9260 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9261 CHECK_EQ(v8::Integer::New(42), res);
9262 // Check with 'with'.
9263 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9264 CHECK_EQ(v8::Integer::New(42), res);
9265}
9266
9267static int force_set_set_count = 0;
9268static int force_set_get_count = 0;
9269bool pass_on_get = false;
9270
9271static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9272 const v8::AccessorInfo& info) {
9273 force_set_get_count++;
9274 if (pass_on_get) {
9275 return v8::Handle<v8::Value>();
9276 } else {
9277 return v8::Int32::New(3);
9278 }
9279}
9280
9281static void ForceSetSetter(v8::Local<v8::String> name,
9282 v8::Local<v8::Value> value,
9283 const v8::AccessorInfo& info) {
9284 force_set_set_count++;
9285}
9286
9287static v8::Handle<v8::Value> ForceSetInterceptSetter(
9288 v8::Local<v8::String> name,
9289 v8::Local<v8::Value> value,
9290 const v8::AccessorInfo& info) {
9291 force_set_set_count++;
9292 return v8::Undefined();
9293}
9294
9295TEST(ForceSet) {
9296 force_set_get_count = 0;
9297 force_set_set_count = 0;
9298 pass_on_get = false;
9299
9300 v8::HandleScope scope;
9301 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9302 v8::Handle<v8::String> access_property = v8::String::New("a");
9303 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9304 LocalContext context(NULL, templ);
9305 v8::Handle<v8::Object> global = context->Global();
9306
9307 // Ordinary properties
9308 v8::Handle<v8::String> simple_property = v8::String::New("p");
9309 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9310 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9311 // This should fail because the property is read-only
9312 global->Set(simple_property, v8::Int32::New(5));
9313 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9314 // This should succeed even though the property is read-only
9315 global->ForceSet(simple_property, v8::Int32::New(6));
9316 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9317
9318 // Accessors
9319 CHECK_EQ(0, force_set_set_count);
9320 CHECK_EQ(0, force_set_get_count);
9321 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9322 // CHECK_EQ the property shouldn't override it, just call the setter
9323 // which in this case does nothing.
9324 global->Set(access_property, v8::Int32::New(7));
9325 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9326 CHECK_EQ(1, force_set_set_count);
9327 CHECK_EQ(2, force_set_get_count);
9328 // Forcing the property to be set should override the accessor without
9329 // calling it
9330 global->ForceSet(access_property, v8::Int32::New(8));
9331 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9332 CHECK_EQ(1, force_set_set_count);
9333 CHECK_EQ(2, force_set_get_count);
9334}
9335
9336TEST(ForceSetWithInterceptor) {
9337 force_set_get_count = 0;
9338 force_set_set_count = 0;
9339 pass_on_get = false;
9340
9341 v8::HandleScope scope;
9342 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9343 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9344 LocalContext context(NULL, templ);
9345 v8::Handle<v8::Object> global = context->Global();
9346
9347 v8::Handle<v8::String> some_property = v8::String::New("a");
9348 CHECK_EQ(0, force_set_set_count);
9349 CHECK_EQ(0, force_set_get_count);
9350 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9351 // Setting the property shouldn't override it, just call the setter
9352 // which in this case does nothing.
9353 global->Set(some_property, v8::Int32::New(7));
9354 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9355 CHECK_EQ(1, force_set_set_count);
9356 CHECK_EQ(2, force_set_get_count);
9357 // Getting the property when the interceptor returns an empty handle
9358 // should yield undefined, since the property isn't present on the
9359 // object itself yet.
9360 pass_on_get = true;
9361 CHECK(global->Get(some_property)->IsUndefined());
9362 CHECK_EQ(1, force_set_set_count);
9363 CHECK_EQ(3, force_set_get_count);
9364 // Forcing the property to be set should cause the value to be
9365 // set locally without calling the interceptor.
9366 global->ForceSet(some_property, v8::Int32::New(8));
9367 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9368 CHECK_EQ(1, force_set_set_count);
9369 CHECK_EQ(4, force_set_get_count);
9370 // Reenabling the interceptor should cause it to take precedence over
9371 // the property
9372 pass_on_get = false;
9373 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9374 CHECK_EQ(1, force_set_set_count);
9375 CHECK_EQ(5, force_set_get_count);
9376 // The interceptor should also work for other properties
9377 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9378 CHECK_EQ(1, force_set_set_count);
9379 CHECK_EQ(6, force_set_get_count);
9380}
9381
9382
9383THREADED_TEST(ForceDelete) {
9384 v8::HandleScope scope;
9385 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9386 LocalContext context(NULL, templ);
9387 v8::Handle<v8::Object> global = context->Global();
9388
9389 // Ordinary properties
9390 v8::Handle<v8::String> simple_property = v8::String::New("p");
9391 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete);
9392 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9393 // This should fail because the property is dont-delete.
9394 CHECK(!global->Delete(simple_property));
9395 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9396 // This should succeed even though the property is dont-delete.
9397 CHECK(global->ForceDelete(simple_property));
9398 CHECK(global->Get(simple_property)->IsUndefined());
9399}
9400
9401
9402static int force_delete_interceptor_count = 0;
9403static bool pass_on_delete = false;
9404
9405
9406static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9407 v8::Local<v8::String> name,
9408 const v8::AccessorInfo& info) {
9409 force_delete_interceptor_count++;
9410 if (pass_on_delete) {
9411 return v8::Handle<v8::Boolean>();
9412 } else {
9413 return v8::True();
9414 }
9415}
9416
9417
9418THREADED_TEST(ForceDeleteWithInterceptor) {
9419 force_delete_interceptor_count = 0;
9420 pass_on_delete = false;
9421
9422 v8::HandleScope scope;
9423 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9424 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9425 LocalContext context(NULL, templ);
9426 v8::Handle<v8::Object> global = context->Global();
9427
9428 v8::Handle<v8::String> some_property = v8::String::New("a");
9429 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9430
9431 // Deleting a property should get intercepted and nothing should
9432 // happen.
9433 CHECK_EQ(0, force_delete_interceptor_count);
9434 CHECK(global->Delete(some_property));
9435 CHECK_EQ(1, force_delete_interceptor_count);
9436 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9437 // Deleting the property when the interceptor returns an empty
9438 // handle should not delete the property since it is DontDelete.
9439 pass_on_delete = true;
9440 CHECK(!global->Delete(some_property));
9441 CHECK_EQ(2, force_delete_interceptor_count);
9442 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9443 // Forcing the property to be deleted should delete the value
9444 // without calling the interceptor.
9445 CHECK(global->ForceDelete(some_property));
9446 CHECK(global->Get(some_property)->IsUndefined());
9447 CHECK_EQ(2, force_delete_interceptor_count);
9448}
9449
9450
9451// Make sure that forcing a delete invalidates any IC stubs, so we
9452// don't read the hole value.
9453THREADED_TEST(ForceDeleteIC) {
9454 v8::HandleScope scope;
9455 LocalContext context;
9456 // Create a DontDelete variable on the global object.
9457 CompileRun("this.__proto__ = { foo: 'horse' };"
9458 "var foo = 'fish';"
9459 "function f() { return foo.length; }");
9460 // Initialize the IC for foo in f.
9461 CompileRun("for (var i = 0; i < 4; i++) f();");
9462 // Make sure the value of foo is correct before the deletion.
9463 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9464 // Force the deletion of foo.
9465 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9466 // Make sure the value for foo is read from the prototype, and that
9467 // we don't get in trouble with reading the deleted cell value
9468 // sentinel.
9469 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9470}
9471
9472
9473v8::Persistent<Context> calling_context0;
9474v8::Persistent<Context> calling_context1;
9475v8::Persistent<Context> calling_context2;
9476
9477
9478// Check that the call to the callback is initiated in
9479// calling_context2, the directly calling context is calling_context1
9480// and the callback itself is in calling_context0.
9481static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9482 ApiTestFuzzer::Fuzz();
9483 CHECK(Context::GetCurrent() == calling_context0);
9484 CHECK(Context::GetCalling() == calling_context1);
9485 CHECK(Context::GetEntered() == calling_context2);
9486 return v8::Integer::New(42);
9487}
9488
9489
9490THREADED_TEST(GetCallingContext) {
9491 v8::HandleScope scope;
9492
9493 calling_context0 = Context::New();
9494 calling_context1 = Context::New();
9495 calling_context2 = Context::New();
9496
9497 // Allow cross-domain access.
9498 Local<String> token = v8_str("<security token>");
9499 calling_context0->SetSecurityToken(token);
9500 calling_context1->SetSecurityToken(token);
9501 calling_context2->SetSecurityToken(token);
9502
9503 // Create an object with a C++ callback in context0.
9504 calling_context0->Enter();
9505 Local<v8::FunctionTemplate> callback_templ =
9506 v8::FunctionTemplate::New(GetCallingContextCallback);
9507 calling_context0->Global()->Set(v8_str("callback"),
9508 callback_templ->GetFunction());
9509 calling_context0->Exit();
9510
9511 // Expose context0 in context1 and setup a function that calls the
9512 // callback function.
9513 calling_context1->Enter();
9514 calling_context1->Global()->Set(v8_str("context0"),
9515 calling_context0->Global());
9516 CompileRun("function f() { context0.callback() }");
9517 calling_context1->Exit();
9518
9519 // Expose context1 in context2 and call the callback function in
9520 // context0 indirectly through f in context1.
9521 calling_context2->Enter();
9522 calling_context2->Global()->Set(v8_str("context1"),
9523 calling_context1->Global());
9524 CompileRun("context1.f()");
9525 calling_context2->Exit();
9526
9527 // Dispose the contexts to allow them to be garbage collected.
9528 calling_context0.Dispose();
9529 calling_context1.Dispose();
9530 calling_context2.Dispose();
9531 calling_context0.Clear();
9532 calling_context1.Clear();
9533 calling_context2.Clear();
9534}
9535
9536
9537// Check that a variable declaration with no explicit initialization
9538// value does not shadow an existing property in the prototype chain.
9539//
9540// This is consistent with Firefox and Safari.
9541//
9542// See http://crbug.com/12548.
9543THREADED_TEST(InitGlobalVarInProtoChain) {
9544 v8::HandleScope scope;
9545 LocalContext context;
9546 // Introduce a variable in the prototype chain.
9547 CompileRun("__proto__.x = 42");
9548 v8::Handle<v8::Value> result = CompileRun("var x; x");
9549 CHECK(!result->IsUndefined());
9550 CHECK_EQ(42, result->Int32Value());
9551}
9552
9553
9554// Regression test for issue 398.
9555// If a function is added to an object, creating a constant function
9556// field, and the result is cloned, replacing the constant function on the
9557// original should not affect the clone.
9558// See http://code.google.com/p/v8/issues/detail?id=398
9559THREADED_TEST(ReplaceConstantFunction) {
9560 v8::HandleScope scope;
9561 LocalContext context;
9562 v8::Handle<v8::Object> obj = v8::Object::New();
9563 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9564 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9565 obj->Set(foo_string, func_templ->GetFunction());
9566 v8::Handle<v8::Object> obj_clone = obj->Clone();
9567 obj_clone->Set(foo_string, v8::String::New("Hello"));
9568 CHECK(!obj->Get(foo_string)->IsUndefined());
9569}
9570
9571
9572// Regression test for http://crbug.com/16276.
9573THREADED_TEST(Regress16276) {
9574 v8::HandleScope scope;
9575 LocalContext context;
9576 // Force the IC in f to be a dictionary load IC.
9577 CompileRun("function f(obj) { return obj.x; }\n"
9578 "var obj = { x: { foo: 42 }, y: 87 };\n"
9579 "var x = obj.x;\n"
9580 "delete obj.y;\n"
9581 "for (var i = 0; i < 5; i++) f(obj);");
9582 // Detach the global object to make 'this' refer directly to the
9583 // global object (not the proxy), and make sure that the dictionary
9584 // load IC doesn't mess up loading directly from the global object.
9585 context->DetachGlobal();
9586 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9587}
9588
9589
9590THREADED_TEST(PixelArray) {
9591 v8::HandleScope scope;
9592 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009593 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009594 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9595 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9596 pixel_data);
9597 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9598 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009599 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009600 }
9601 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9602 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009603 CHECK_EQ(i % 256, pixels->get(i));
9604 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009605 }
9606
9607 v8::Handle<v8::Object> obj = v8::Object::New();
9608 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9609 // Set the elements to be the pixels.
9610 // jsobj->set_elements(*pixels);
9611 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9612 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9613 obj->Set(v8_str("field"), v8::Int32::New(1503));
9614 context->Global()->Set(v8_str("pixels"), obj);
9615 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9616 CHECK_EQ(1503, result->Int32Value());
9617 result = CompileRun("pixels[1]");
9618 CHECK_EQ(1, result->Int32Value());
9619
9620 result = CompileRun("var sum = 0;"
9621 "for (var i = 0; i < 8; i++) {"
9622 " sum += pixels[i] = pixels[i] = -i;"
9623 "}"
9624 "sum;");
9625 CHECK_EQ(-28, result->Int32Value());
9626
9627 result = CompileRun("var sum = 0;"
9628 "for (var i = 0; i < 8; i++) {"
9629 " sum += pixels[i] = pixels[i] = 0;"
9630 "}"
9631 "sum;");
9632 CHECK_EQ(0, result->Int32Value());
9633
9634 result = CompileRun("var sum = 0;"
9635 "for (var i = 0; i < 8; i++) {"
9636 " sum += pixels[i] = pixels[i] = 255;"
9637 "}"
9638 "sum;");
9639 CHECK_EQ(8 * 255, result->Int32Value());
9640
9641 result = CompileRun("var sum = 0;"
9642 "for (var i = 0; i < 8; i++) {"
9643 " sum += pixels[i] = pixels[i] = 256 + i;"
9644 "}"
9645 "sum;");
9646 CHECK_EQ(2076, result->Int32Value());
9647
9648 result = CompileRun("var sum = 0;"
9649 "for (var i = 0; i < 8; i++) {"
9650 " sum += pixels[i] = pixels[i] = i;"
9651 "}"
9652 "sum;");
9653 CHECK_EQ(28, result->Int32Value());
9654
9655 result = CompileRun("var sum = 0;"
9656 "for (var i = 0; i < 8; i++) {"
9657 " sum += pixels[i];"
9658 "}"
9659 "sum;");
9660 CHECK_EQ(28, result->Int32Value());
9661
9662 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9663 i::SetElement(jsobj, 1, value);
9664 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9665 *value.location() = i::Smi::FromInt(256);
9666 i::SetElement(jsobj, 1, value);
9667 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9668 *value.location() = i::Smi::FromInt(-1);
9669 i::SetElement(jsobj, 1, value);
9670 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9671
9672 result = CompileRun("for (var i = 0; i < 8; i++) {"
9673 " pixels[i] = (i * 65) - 109;"
9674 "}"
9675 "pixels[1] + pixels[6];");
9676 CHECK_EQ(255, result->Int32Value());
9677 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9678 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9679 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9680 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9681 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9682 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9683 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9684 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9685 result = CompileRun("var sum = 0;"
9686 "for (var i = 0; i < 8; i++) {"
9687 " sum += pixels[i];"
9688 "}"
9689 "sum;");
9690 CHECK_EQ(984, result->Int32Value());
9691
9692 result = CompileRun("for (var i = 0; i < 8; i++) {"
9693 " pixels[i] = (i * 1.1);"
9694 "}"
9695 "pixels[1] + pixels[6];");
9696 CHECK_EQ(8, result->Int32Value());
9697 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9698 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9699 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9700 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9701 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9702 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9703 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9704 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9705
9706 result = CompileRun("for (var i = 0; i < 8; i++) {"
9707 " pixels[7] = undefined;"
9708 "}"
9709 "pixels[7];");
9710 CHECK_EQ(0, result->Int32Value());
9711 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9712
9713 result = CompileRun("for (var i = 0; i < 8; i++) {"
9714 " pixels[6] = '2.3';"
9715 "}"
9716 "pixels[6];");
9717 CHECK_EQ(2, result->Int32Value());
9718 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9719
9720 result = CompileRun("for (var i = 0; i < 8; i++) {"
9721 " pixels[5] = NaN;"
9722 "}"
9723 "pixels[5];");
9724 CHECK_EQ(0, result->Int32Value());
9725 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9726
9727 result = CompileRun("for (var i = 0; i < 8; i++) {"
9728 " pixels[8] = Infinity;"
9729 "}"
9730 "pixels[8];");
9731 CHECK_EQ(255, result->Int32Value());
9732 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9733
9734 result = CompileRun("for (var i = 0; i < 8; i++) {"
9735 " pixels[9] = -Infinity;"
9736 "}"
9737 "pixels[9];");
9738 CHECK_EQ(0, result->Int32Value());
9739 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9740
9741 result = CompileRun("pixels[3] = 33;"
9742 "delete pixels[3];"
9743 "pixels[3];");
9744 CHECK_EQ(33, result->Int32Value());
9745
9746 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9747 "pixels[2] = 12; pixels[3] = 13;"
9748 "pixels.__defineGetter__('2',"
9749 "function() { return 120; });"
9750 "pixels[2];");
9751 CHECK_EQ(12, result->Int32Value());
9752
9753 result = CompileRun("var js_array = new Array(40);"
9754 "js_array[0] = 77;"
9755 "js_array;");
9756 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9757
9758 result = CompileRun("pixels[1] = 23;"
9759 "pixels.__proto__ = [];"
9760 "js_array.__proto__ = pixels;"
9761 "js_array.concat(pixels);");
9762 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9763 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9764
9765 result = CompileRun("pixels[1] = 23;");
9766 CHECK_EQ(23, result->Int32Value());
9767
Steve Blockd0582a62009-12-15 09:54:21 +00009768 // Test for index greater than 255. Regression test for:
9769 // http://code.google.com/p/chromium/issues/detail?id=26337.
9770 result = CompileRun("pixels[256] = 255;");
9771 CHECK_EQ(255, result->Int32Value());
9772 result = CompileRun("var i = 0;"
9773 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9774 "i");
9775 CHECK_EQ(255, result->Int32Value());
9776
Steve Blocka7e24c12009-10-30 11:49:00 +00009777 free(pixel_data);
9778}
9779
9780
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009781THREADED_TEST(PixelArrayInfo) {
9782 v8::HandleScope scope;
9783 LocalContext context;
9784 for (int size = 0; size < 100; size += 10) {
9785 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9786 v8::Handle<v8::Object> obj = v8::Object::New();
9787 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9788 CHECK(obj->HasIndexedPropertiesInPixelData());
9789 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9790 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9791 free(pixel_data);
9792 }
9793}
9794
9795
9796static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9797 switch (array_type) {
9798 case v8::kExternalByteArray:
9799 case v8::kExternalUnsignedByteArray:
9800 return 1;
9801 break;
9802 case v8::kExternalShortArray:
9803 case v8::kExternalUnsignedShortArray:
9804 return 2;
9805 break;
9806 case v8::kExternalIntArray:
9807 case v8::kExternalUnsignedIntArray:
9808 case v8::kExternalFloatArray:
9809 return 4;
9810 break;
9811 default:
9812 UNREACHABLE();
9813 return -1;
9814 }
9815 UNREACHABLE();
9816 return -1;
9817}
9818
9819
Steve Block3ce2e202009-11-05 08:53:23 +00009820template <class ExternalArrayClass, class ElementType>
9821static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9822 int64_t low,
9823 int64_t high) {
9824 v8::HandleScope scope;
9825 LocalContext context;
9826 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009827 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +00009828 ElementType* array_data =
9829 static_cast<ElementType*>(malloc(kElementCount * element_size));
9830 i::Handle<ExternalArrayClass> array =
9831 i::Handle<ExternalArrayClass>::cast(
9832 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9833 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9834 for (int i = 0; i < kElementCount; i++) {
9835 array->set(i, static_cast<ElementType>(i));
9836 }
9837 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9838 for (int i = 0; i < kElementCount; i++) {
9839 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9840 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9841 }
9842
9843 v8::Handle<v8::Object> obj = v8::Object::New();
9844 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9845 // Set the elements to be the external array.
9846 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9847 array_type,
9848 kElementCount);
9849 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9850 obj->Set(v8_str("field"), v8::Int32::New(1503));
9851 context->Global()->Set(v8_str("ext_array"), obj);
9852 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9853 CHECK_EQ(1503, result->Int32Value());
9854 result = CompileRun("ext_array[1]");
9855 CHECK_EQ(1, result->Int32Value());
9856
9857 // Check pass through of assigned smis
9858 result = CompileRun("var sum = 0;"
9859 "for (var i = 0; i < 8; i++) {"
9860 " sum += ext_array[i] = ext_array[i] = -i;"
9861 "}"
9862 "sum;");
9863 CHECK_EQ(-28, result->Int32Value());
9864
9865 // Check assigned smis
9866 result = CompileRun("for (var i = 0; i < 8; i++) {"
9867 " ext_array[i] = i;"
9868 "}"
9869 "var sum = 0;"
9870 "for (var i = 0; i < 8; i++) {"
9871 " sum += ext_array[i];"
9872 "}"
9873 "sum;");
9874 CHECK_EQ(28, result->Int32Value());
9875
9876 // Check assigned smis in reverse order
9877 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9878 " ext_array[i] = i;"
9879 "}"
9880 "var sum = 0;"
9881 "for (var i = 0; i < 8; i++) {"
9882 " sum += ext_array[i];"
9883 "}"
9884 "sum;");
9885 CHECK_EQ(28, result->Int32Value());
9886
9887 // Check pass through of assigned HeapNumbers
9888 result = CompileRun("var sum = 0;"
9889 "for (var i = 0; i < 16; i+=2) {"
9890 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9891 "}"
9892 "sum;");
9893 CHECK_EQ(-28, result->Int32Value());
9894
9895 // Check assigned HeapNumbers
9896 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9897 " ext_array[i] = (i * 0.5);"
9898 "}"
9899 "var sum = 0;"
9900 "for (var i = 0; i < 16; i+=2) {"
9901 " sum += ext_array[i];"
9902 "}"
9903 "sum;");
9904 CHECK_EQ(28, result->Int32Value());
9905
9906 // Check assigned HeapNumbers in reverse order
9907 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9908 " ext_array[i] = (i * 0.5);"
9909 "}"
9910 "var sum = 0;"
9911 "for (var i = 0; i < 16; i+=2) {"
9912 " sum += ext_array[i];"
9913 "}"
9914 "sum;");
9915 CHECK_EQ(28, result->Int32Value());
9916
9917 i::ScopedVector<char> test_buf(1024);
9918
9919 // Check legal boundary conditions.
9920 // The repeated loads and stores ensure the ICs are exercised.
9921 const char* boundary_program =
9922 "var res = 0;"
9923 "for (var i = 0; i < 16; i++) {"
9924 " ext_array[i] = %lld;"
9925 " if (i > 8) {"
9926 " res = ext_array[i];"
9927 " }"
9928 "}"
9929 "res;";
9930 i::OS::SNPrintF(test_buf,
9931 boundary_program,
9932 low);
9933 result = CompileRun(test_buf.start());
9934 CHECK_EQ(low, result->IntegerValue());
9935
9936 i::OS::SNPrintF(test_buf,
9937 boundary_program,
9938 high);
9939 result = CompileRun(test_buf.start());
9940 CHECK_EQ(high, result->IntegerValue());
9941
9942 // Check misprediction of type in IC.
9943 result = CompileRun("var tmp_array = ext_array;"
9944 "var sum = 0;"
9945 "for (var i = 0; i < 8; i++) {"
9946 " tmp_array[i] = i;"
9947 " sum += tmp_array[i];"
9948 " if (i == 4) {"
9949 " tmp_array = {};"
9950 " }"
9951 "}"
9952 "sum;");
9953 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9954 CHECK_EQ(28, result->Int32Value());
9955
9956 // Make sure out-of-range loads do not throw.
9957 i::OS::SNPrintF(test_buf,
9958 "var caught_exception = false;"
9959 "try {"
9960 " ext_array[%d];"
9961 "} catch (e) {"
9962 " caught_exception = true;"
9963 "}"
9964 "caught_exception;",
9965 kElementCount);
9966 result = CompileRun(test_buf.start());
9967 CHECK_EQ(false, result->BooleanValue());
9968
9969 // Make sure out-of-range stores do not throw.
9970 i::OS::SNPrintF(test_buf,
9971 "var caught_exception = false;"
9972 "try {"
9973 " ext_array[%d] = 1;"
9974 "} catch (e) {"
9975 " caught_exception = true;"
9976 "}"
9977 "caught_exception;",
9978 kElementCount);
9979 result = CompileRun(test_buf.start());
9980 CHECK_EQ(false, result->BooleanValue());
9981
9982 // Check other boundary conditions, values and operations.
9983 result = CompileRun("for (var i = 0; i < 8; i++) {"
9984 " ext_array[7] = undefined;"
9985 "}"
9986 "ext_array[7];");
9987 CHECK_EQ(0, result->Int32Value());
9988 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
9989
9990 result = CompileRun("for (var i = 0; i < 8; i++) {"
9991 " ext_array[6] = '2.3';"
9992 "}"
9993 "ext_array[6];");
9994 CHECK_EQ(2, result->Int32Value());
9995 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
9996
9997 if (array_type != v8::kExternalFloatArray) {
9998 // Though the specification doesn't state it, be explicit about
9999 // converting NaNs and +/-Infinity to zero.
10000 result = CompileRun("for (var i = 0; i < 8; i++) {"
10001 " ext_array[i] = 5;"
10002 "}"
10003 "for (var i = 0; i < 8; i++) {"
10004 " ext_array[i] = NaN;"
10005 "}"
10006 "ext_array[5];");
10007 CHECK_EQ(0, result->Int32Value());
10008 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10009
10010 result = CompileRun("for (var i = 0; i < 8; i++) {"
10011 " ext_array[i] = 5;"
10012 "}"
10013 "for (var i = 0; i < 8; i++) {"
10014 " ext_array[i] = Infinity;"
10015 "}"
10016 "ext_array[5];");
10017 CHECK_EQ(0, result->Int32Value());
10018 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10019
10020 result = CompileRun("for (var i = 0; i < 8; i++) {"
10021 " ext_array[i] = 5;"
10022 "}"
10023 "for (var i = 0; i < 8; i++) {"
10024 " ext_array[i] = -Infinity;"
10025 "}"
10026 "ext_array[5];");
10027 CHECK_EQ(0, result->Int32Value());
10028 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
10029 }
10030
10031 result = CompileRun("ext_array[3] = 33;"
10032 "delete ext_array[3];"
10033 "ext_array[3];");
10034 CHECK_EQ(33, result->Int32Value());
10035
10036 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
10037 "ext_array[2] = 12; ext_array[3] = 13;"
10038 "ext_array.__defineGetter__('2',"
10039 "function() { return 120; });"
10040 "ext_array[2];");
10041 CHECK_EQ(12, result->Int32Value());
10042
10043 result = CompileRun("var js_array = new Array(40);"
10044 "js_array[0] = 77;"
10045 "js_array;");
10046 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10047
10048 result = CompileRun("ext_array[1] = 23;"
10049 "ext_array.__proto__ = [];"
10050 "js_array.__proto__ = ext_array;"
10051 "js_array.concat(ext_array);");
10052 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
10053 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
10054
10055 result = CompileRun("ext_array[1] = 23;");
10056 CHECK_EQ(23, result->Int32Value());
10057
Steve Blockd0582a62009-12-15 09:54:21 +000010058 // Test more complex manipulations which cause eax to contain values
10059 // that won't be completely overwritten by loads from the arrays.
10060 // This catches bugs in the instructions used for the KeyedLoadIC
10061 // for byte and word types.
10062 {
10063 const int kXSize = 300;
10064 const int kYSize = 300;
10065 const int kLargeElementCount = kXSize * kYSize * 4;
10066 ElementType* large_array_data =
10067 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
10068 i::Handle<ExternalArrayClass> large_array =
10069 i::Handle<ExternalArrayClass>::cast(
10070 i::Factory::NewExternalArray(kLargeElementCount,
10071 array_type,
10072 array_data));
10073 v8::Handle<v8::Object> large_obj = v8::Object::New();
10074 // Set the elements to be the external array.
10075 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
10076 array_type,
10077 kLargeElementCount);
10078 context->Global()->Set(v8_str("large_array"), large_obj);
10079 // Initialize contents of a few rows.
10080 for (int x = 0; x < 300; x++) {
10081 int row = 0;
10082 int offset = row * 300 * 4;
10083 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10084 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10085 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10086 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10087 row = 150;
10088 offset = row * 300 * 4;
10089 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10090 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10091 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10092 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10093 row = 298;
10094 offset = row * 300 * 4;
10095 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10096 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10097 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10098 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10099 }
10100 // The goal of the code below is to make "offset" large enough
10101 // that the computation of the index (which goes into eax) has
10102 // high bits set which will not be overwritten by a byte or short
10103 // load.
10104 result = CompileRun("var failed = false;"
10105 "var offset = 0;"
10106 "for (var i = 0; i < 300; i++) {"
10107 " if (large_array[4 * i] != 127 ||"
10108 " large_array[4 * i + 1] != 0 ||"
10109 " large_array[4 * i + 2] != 0 ||"
10110 " large_array[4 * i + 3] != 127) {"
10111 " failed = true;"
10112 " }"
10113 "}"
10114 "offset = 150 * 300 * 4;"
10115 "for (var i = 0; i < 300; i++) {"
10116 " if (large_array[offset + 4 * i] != 127 ||"
10117 " large_array[offset + 4 * i + 1] != 0 ||"
10118 " large_array[offset + 4 * i + 2] != 0 ||"
10119 " large_array[offset + 4 * i + 3] != 127) {"
10120 " failed = true;"
10121 " }"
10122 "}"
10123 "offset = 298 * 300 * 4;"
10124 "for (var i = 0; i < 300; i++) {"
10125 " if (large_array[offset + 4 * i] != 127 ||"
10126 " large_array[offset + 4 * i + 1] != 0 ||"
10127 " large_array[offset + 4 * i + 2] != 0 ||"
10128 " large_array[offset + 4 * i + 3] != 127) {"
10129 " failed = true;"
10130 " }"
10131 "}"
10132 "!failed;");
10133 CHECK_EQ(true, result->BooleanValue());
10134 free(large_array_data);
10135 }
10136
Steve Block3ce2e202009-11-05 08:53:23 +000010137 free(array_data);
10138}
10139
10140
10141THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010142 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010143 v8::kExternalByteArray,
10144 -128,
10145 127);
10146}
10147
10148
10149THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010150 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010151 v8::kExternalUnsignedByteArray,
10152 0,
10153 255);
10154}
10155
10156
10157THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010158 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010159 v8::kExternalShortArray,
10160 -32768,
10161 32767);
10162}
10163
10164
10165THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010166 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010167 v8::kExternalUnsignedShortArray,
10168 0,
10169 65535);
10170}
10171
10172
10173THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010174 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010175 v8::kExternalIntArray,
10176 INT_MIN, // -2147483648
10177 INT_MAX); // 2147483647
10178}
10179
10180
10181THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010182 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010183 v8::kExternalUnsignedIntArray,
10184 0,
10185 UINT_MAX); // 4294967295
10186}
10187
10188
10189THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010190 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000010191 v8::kExternalFloatArray,
10192 -500,
10193 500);
10194}
10195
10196
10197THREADED_TEST(ExternalArrays) {
10198 TestExternalByteArray();
10199 TestExternalUnsignedByteArray();
10200 TestExternalShortArray();
10201 TestExternalUnsignedShortArray();
10202 TestExternalIntArray();
10203 TestExternalUnsignedIntArray();
10204 TestExternalFloatArray();
10205}
10206
10207
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010208void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10209 v8::HandleScope scope;
10210 LocalContext context;
10211 for (int size = 0; size < 100; size += 10) {
10212 int element_size = ExternalArrayElementSize(array_type);
10213 void* external_data = malloc(size * element_size);
10214 v8::Handle<v8::Object> obj = v8::Object::New();
10215 obj->SetIndexedPropertiesToExternalArrayData(
10216 external_data, array_type, size);
10217 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10218 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10219 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10220 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10221 free(external_data);
10222 }
10223}
10224
10225
10226THREADED_TEST(ExternalArrayInfo) {
10227 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10228 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10229 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10230 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10231 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10232 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10233 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10234}
10235
10236
Steve Blocka7e24c12009-10-30 11:49:00 +000010237THREADED_TEST(ScriptContextDependence) {
10238 v8::HandleScope scope;
10239 LocalContext c1;
10240 const char *source = "foo";
10241 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10242 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10243 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10244 CHECK_EQ(dep->Run()->Int32Value(), 100);
10245 CHECK_EQ(indep->Run()->Int32Value(), 100);
10246 LocalContext c2;
10247 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10248 CHECK_EQ(dep->Run()->Int32Value(), 100);
10249 CHECK_EQ(indep->Run()->Int32Value(), 101);
10250}
10251
10252
10253THREADED_TEST(StackTrace) {
10254 v8::HandleScope scope;
10255 LocalContext context;
10256 v8::TryCatch try_catch;
10257 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10258 v8::Handle<v8::String> src = v8::String::New(source);
10259 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10260 v8::Script::New(src, origin)->Run();
10261 CHECK(try_catch.HasCaught());
10262 v8::String::Utf8Value stack(try_catch.StackTrace());
10263 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10264}
10265
10266
Kristian Monsen25f61362010-05-21 11:50:48 +010010267// Checks that a StackFrame has certain expected values.
10268void checkStackFrame(const char* expected_script_name,
10269 const char* expected_func_name, int expected_line_number,
10270 int expected_column, bool is_eval, bool is_constructor,
10271 v8::Handle<v8::StackFrame> frame) {
10272 v8::HandleScope scope;
10273 v8::String::Utf8Value func_name(frame->GetFunctionName());
10274 v8::String::Utf8Value script_name(frame->GetScriptName());
10275 if (*script_name == NULL) {
10276 // The situation where there is no associated script, like for evals.
10277 CHECK(expected_script_name == NULL);
10278 } else {
10279 CHECK(strstr(*script_name, expected_script_name) != NULL);
10280 }
10281 CHECK(strstr(*func_name, expected_func_name) != NULL);
10282 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10283 CHECK_EQ(expected_column, frame->GetColumn());
10284 CHECK_EQ(is_eval, frame->IsEval());
10285 CHECK_EQ(is_constructor, frame->IsConstructor());
10286}
10287
10288
10289v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10290 v8::HandleScope scope;
10291 const char* origin = "capture-stack-trace-test";
10292 const int kOverviewTest = 1;
10293 const int kDetailedTest = 2;
10294
10295 ASSERT(args.Length() == 1);
10296
10297 int testGroup = args[0]->Int32Value();
10298 if (testGroup == kOverviewTest) {
10299 v8::Handle<v8::StackTrace> stackTrace =
10300 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10301 CHECK_EQ(4, stackTrace->GetFrameCount());
10302 checkStackFrame(origin, "bar", 2, 10, false, false,
10303 stackTrace->GetFrame(0));
10304 checkStackFrame(origin, "foo", 6, 3, false, false,
10305 stackTrace->GetFrame(1));
10306 checkStackFrame(NULL, "", 1, 1, false, false,
10307 stackTrace->GetFrame(2));
10308 // The last frame is an anonymous function that has the initial call.
10309 checkStackFrame(origin, "", 8, 7, false, false,
10310 stackTrace->GetFrame(3));
10311
10312 CHECK(stackTrace->AsArray()->IsArray());
10313 } else if (testGroup == kDetailedTest) {
10314 v8::Handle<v8::StackTrace> stackTrace =
10315 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10316 CHECK_EQ(4, stackTrace->GetFrameCount());
10317 checkStackFrame(origin, "bat", 4, 22, false, false,
10318 stackTrace->GetFrame(0));
10319 checkStackFrame(origin, "baz", 8, 3, false, true,
10320 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010321#ifdef ENABLE_DEBUGGER_SUPPORT
10322 bool is_eval = true;
10323#else // ENABLE_DEBUGGER_SUPPORT
10324 bool is_eval = false;
10325#endif // ENABLE_DEBUGGER_SUPPORT
10326
10327 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010328 stackTrace->GetFrame(2));
10329 // The last frame is an anonymous function that has the initial call to foo.
10330 checkStackFrame(origin, "", 10, 1, false, false,
10331 stackTrace->GetFrame(3));
10332
10333 CHECK(stackTrace->AsArray()->IsArray());
10334 }
10335 return v8::Undefined();
10336}
10337
10338
10339// Tests the C++ StackTrace API.
10340THREADED_TEST(CaptureStackTrace) {
10341 v8::HandleScope scope;
10342 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10343 Local<ObjectTemplate> templ = ObjectTemplate::New();
10344 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10345 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10346 LocalContext context(0, templ);
10347
10348 // Test getting OVERVIEW information. Should ignore information that is not
10349 // script name, function name, line number, and column offset.
10350 const char *overview_source =
10351 "function bar() {\n"
10352 " var y; AnalyzeStackInNativeCode(1);\n"
10353 "}\n"
10354 "function foo() {\n"
10355 "\n"
10356 " bar();\n"
10357 "}\n"
10358 "var x;eval('new foo();');";
10359 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10360 v8::Handle<Value> overview_result =
10361 v8::Script::New(overview_src, origin)->Run();
10362 ASSERT(!overview_result.IsEmpty());
10363 ASSERT(overview_result->IsObject());
10364
10365 // Test getting DETAILED information.
10366 const char *detailed_source =
10367 "function bat() {AnalyzeStackInNativeCode(2);\n"
10368 "}\n"
10369 "\n"
10370 "function baz() {\n"
10371 " bat();\n"
10372 "}\n"
10373 "eval('new baz();');";
10374 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10375 // Make the script using a non-zero line and column offset.
10376 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10377 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10378 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10379 v8::Handle<v8::Script> detailed_script(
10380 v8::Script::New(detailed_src, &detailed_origin));
10381 v8::Handle<Value> detailed_result = detailed_script->Run();
10382 ASSERT(!detailed_result.IsEmpty());
10383 ASSERT(detailed_result->IsObject());
10384}
10385
10386
Ben Murdoch3bec4d22010-07-22 14:51:16 +010010387static void StackTraceForUncaughtExceptionListener(
10388 v8::Handle<v8::Message> message,
10389 v8::Handle<Value>) {
10390 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
10391 CHECK_EQ(2, stack_trace->GetFrameCount());
10392 checkStackFrame("origin", "foo", 2, 3, false, false,
10393 stack_trace->GetFrame(0));
10394 checkStackFrame("origin", "bar", 5, 3, false, false,
10395 stack_trace->GetFrame(1));
10396}
10397
10398TEST(CaptureStackTraceForUncaughtException) {
10399 report_count = 0;
10400 v8::HandleScope scope;
10401 LocalContext env;
10402 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
10403 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
10404
10405 Script::Compile(v8_str("function foo() {\n"
10406 " throw 1;\n"
10407 "};\n"
10408 "function bar() {\n"
10409 " foo();\n"
10410 "};"),
10411 v8_str("origin"))->Run();
10412 v8::Local<v8::Object> global = env->Global();
10413 Local<Value> trouble = global->Get(v8_str("bar"));
10414 CHECK(trouble->IsFunction());
10415 Function::Cast(*trouble)->Call(global, 0, NULL);
10416 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
10417 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
10418}
10419
10420
Steve Block3ce2e202009-11-05 08:53:23 +000010421// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010422THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010423 bool rv = false;
10424 for (int i = 0; i < 100; i++) {
10425 rv = v8::V8::IdleNotification();
10426 if (rv)
10427 break;
10428 }
10429 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010430}
10431
10432
10433static uint32_t* stack_limit;
10434
10435static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10436 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10437 return v8::Undefined();
10438}
10439
10440
10441// Uses the address of a local variable to determine the stack top now.
10442// Given a size, returns an address that is that far from the current
10443// top of stack.
10444static uint32_t* ComputeStackLimit(uint32_t size) {
10445 uint32_t* answer = &size - (size / sizeof(size));
10446 // If the size is very large and the stack is very near the bottom of
10447 // memory then the calculation above may wrap around and give an address
10448 // that is above the (downwards-growing) stack. In that case we return
10449 // a very low address.
10450 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10451 return answer;
10452}
10453
10454
10455TEST(SetResourceConstraints) {
10456 static const int K = 1024;
10457 uint32_t* set_limit = ComputeStackLimit(128 * K);
10458
10459 // Set stack limit.
10460 v8::ResourceConstraints constraints;
10461 constraints.set_stack_limit(set_limit);
10462 CHECK(v8::SetResourceConstraints(&constraints));
10463
10464 // Execute a script.
10465 v8::HandleScope scope;
10466 LocalContext env;
10467 Local<v8::FunctionTemplate> fun_templ =
10468 v8::FunctionTemplate::New(GetStackLimitCallback);
10469 Local<Function> fun = fun_templ->GetFunction();
10470 env->Global()->Set(v8_str("get_stack_limit"), fun);
10471 CompileRun("get_stack_limit();");
10472
10473 CHECK(stack_limit == set_limit);
10474}
10475
10476
10477TEST(SetResourceConstraintsInThread) {
10478 uint32_t* set_limit;
10479 {
10480 v8::Locker locker;
10481 static const int K = 1024;
10482 set_limit = ComputeStackLimit(128 * K);
10483
10484 // Set stack limit.
10485 v8::ResourceConstraints constraints;
10486 constraints.set_stack_limit(set_limit);
10487 CHECK(v8::SetResourceConstraints(&constraints));
10488
10489 // Execute a script.
10490 v8::HandleScope scope;
10491 LocalContext env;
10492 Local<v8::FunctionTemplate> fun_templ =
10493 v8::FunctionTemplate::New(GetStackLimitCallback);
10494 Local<Function> fun = fun_templ->GetFunction();
10495 env->Global()->Set(v8_str("get_stack_limit"), fun);
10496 CompileRun("get_stack_limit();");
10497
10498 CHECK(stack_limit == set_limit);
10499 }
10500 {
10501 v8::Locker locker;
10502 CHECK(stack_limit == set_limit);
10503 }
10504}
Steve Block3ce2e202009-11-05 08:53:23 +000010505
10506
10507THREADED_TEST(GetHeapStatistics) {
10508 v8::HandleScope scope;
10509 LocalContext c1;
10510 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010511 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10512 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010513 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010514 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10515 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10516}
10517
10518
10519static double DoubleFromBits(uint64_t value) {
10520 double target;
10521#ifdef BIG_ENDIAN_FLOATING_POINT
10522 const int kIntSize = 4;
10523 // Somebody swapped the lower and higher half of doubles.
10524 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10525 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10526#else
10527 memcpy(&target, &value, sizeof(target));
10528#endif
10529 return target;
10530}
10531
10532
10533static uint64_t DoubleToBits(double value) {
10534 uint64_t target;
10535#ifdef BIG_ENDIAN_FLOATING_POINT
10536 const int kIntSize = 4;
10537 // Somebody swapped the lower and higher half of doubles.
10538 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10539 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10540#else
10541 memcpy(&target, &value, sizeof(target));
10542#endif
10543 return target;
10544}
10545
10546
10547static double DoubleToDateTime(double input) {
10548 double date_limit = 864e13;
10549 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10550 return i::OS::nan_value();
10551 }
10552 return (input < 0) ? -(floor(-input)) : floor(input);
10553}
10554
10555// We don't have a consistent way to write 64-bit constants syntactically, so we
10556// split them into two 32-bit constants and combine them programmatically.
10557static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10558 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10559}
10560
10561
10562THREADED_TEST(QuietSignalingNaNs) {
10563 v8::HandleScope scope;
10564 LocalContext context;
10565 v8::TryCatch try_catch;
10566
10567 // Special double values.
10568 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10569 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10570 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10571 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10572 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10573 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10574 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10575
10576 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10577 // on either side of the epoch.
10578 double date_limit = 864e13;
10579
10580 double test_values[] = {
10581 snan,
10582 qnan,
10583 infinity,
10584 max_normal,
10585 date_limit + 1,
10586 date_limit,
10587 min_normal,
10588 max_denormal,
10589 min_denormal,
10590 0,
10591 -0,
10592 -min_denormal,
10593 -max_denormal,
10594 -min_normal,
10595 -date_limit,
10596 -date_limit - 1,
10597 -max_normal,
10598 -infinity,
10599 -qnan,
10600 -snan
10601 };
10602 int num_test_values = 20;
10603
10604 for (int i = 0; i < num_test_values; i++) {
10605 double test_value = test_values[i];
10606
10607 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10608 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10609 double stored_number = number->NumberValue();
10610 if (!IsNaN(test_value)) {
10611 CHECK_EQ(test_value, stored_number);
10612 } else {
10613 uint64_t stored_bits = DoubleToBits(stored_number);
10614 // Check if quiet nan (bits 51..62 all set).
10615 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10616 }
10617
10618 // Check that Date::New preserves non-NaNs in the date range and
10619 // quiets SNaNs.
10620 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10621 double expected_stored_date = DoubleToDateTime(test_value);
10622 double stored_date = date->NumberValue();
10623 if (!IsNaN(expected_stored_date)) {
10624 CHECK_EQ(expected_stored_date, stored_date);
10625 } else {
10626 uint64_t stored_bits = DoubleToBits(stored_date);
10627 // Check if quiet nan (bits 51..62 all set).
10628 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10629 }
10630 }
10631}
10632
10633
10634static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10635 v8::HandleScope scope;
10636 v8::TryCatch tc;
10637 v8::Handle<v8::String> str = args[0]->ToString();
10638 if (tc.HasCaught())
10639 return tc.ReThrow();
10640 return v8::Undefined();
10641}
10642
10643
10644// Test that an exception can be propagated down through a spaghetti
10645// stack using ReThrow.
10646THREADED_TEST(SpaghettiStackReThrow) {
10647 v8::HandleScope scope;
10648 LocalContext context;
10649 context->Global()->Set(
10650 v8::String::New("s"),
10651 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10652 v8::TryCatch try_catch;
10653 CompileRun(
10654 "var i = 0;"
10655 "var o = {"
10656 " toString: function () {"
10657 " if (i == 10) {"
10658 " throw 'Hey!';"
10659 " } else {"
10660 " i++;"
10661 " return s(o);"
10662 " }"
10663 " }"
10664 "};"
10665 "s(o);");
10666 CHECK(try_catch.HasCaught());
10667 v8::String::Utf8Value value(try_catch.Exception());
10668 CHECK_EQ(0, strcmp(*value, "Hey!"));
10669}
10670
10671
Steve Blockd0582a62009-12-15 09:54:21 +000010672TEST(Regress528) {
10673 v8::V8::Initialize();
10674
10675 v8::HandleScope scope;
10676 v8::Persistent<Context> context;
10677 v8::Persistent<Context> other_context;
10678 int gc_count;
10679
10680 // Create a context used to keep the code from aging in the compilation
10681 // cache.
10682 other_context = Context::New();
10683
10684 // Context-dependent context data creates reference from the compilation
10685 // cache to the global object.
10686 const char* source_simple = "1";
10687 context = Context::New();
10688 {
10689 v8::HandleScope scope;
10690
10691 context->Enter();
10692 Local<v8::String> obj = v8::String::New("");
10693 context->SetData(obj);
10694 CompileRun(source_simple);
10695 context->Exit();
10696 }
10697 context.Dispose();
10698 for (gc_count = 1; gc_count < 10; gc_count++) {
10699 other_context->Enter();
10700 CompileRun(source_simple);
10701 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010702 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010703 if (GetGlobalObjectsCount() == 1) break;
10704 }
10705 CHECK_GE(2, gc_count);
10706 CHECK_EQ(1, GetGlobalObjectsCount());
10707
10708 // Eval in a function creates reference from the compilation cache to the
10709 // global object.
10710 const char* source_eval = "function f(){eval('1')}; f()";
10711 context = Context::New();
10712 {
10713 v8::HandleScope scope;
10714
10715 context->Enter();
10716 CompileRun(source_eval);
10717 context->Exit();
10718 }
10719 context.Dispose();
10720 for (gc_count = 1; gc_count < 10; gc_count++) {
10721 other_context->Enter();
10722 CompileRun(source_eval);
10723 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010724 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010725 if (GetGlobalObjectsCount() == 1) break;
10726 }
10727 CHECK_GE(2, gc_count);
10728 CHECK_EQ(1, GetGlobalObjectsCount());
10729
10730 // Looking up the line number for an exception creates reference from the
10731 // compilation cache to the global object.
10732 const char* source_exception = "function f(){throw 1;} f()";
10733 context = Context::New();
10734 {
10735 v8::HandleScope scope;
10736
10737 context->Enter();
10738 v8::TryCatch try_catch;
10739 CompileRun(source_exception);
10740 CHECK(try_catch.HasCaught());
10741 v8::Handle<v8::Message> message = try_catch.Message();
10742 CHECK(!message.IsEmpty());
10743 CHECK_EQ(1, message->GetLineNumber());
10744 context->Exit();
10745 }
10746 context.Dispose();
10747 for (gc_count = 1; gc_count < 10; gc_count++) {
10748 other_context->Enter();
10749 CompileRun(source_exception);
10750 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010751 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010752 if (GetGlobalObjectsCount() == 1) break;
10753 }
10754 CHECK_GE(2, gc_count);
10755 CHECK_EQ(1, GetGlobalObjectsCount());
10756
10757 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010758}
Andrei Popescu402d9372010-02-26 13:31:12 +000010759
10760
10761THREADED_TEST(ScriptOrigin) {
10762 v8::HandleScope scope;
10763 LocalContext env;
10764 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10765 v8::Handle<v8::String> script = v8::String::New(
10766 "function f() {}\n\nfunction g() {}");
10767 v8::Script::Compile(script, &origin)->Run();
10768 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10769 env->Global()->Get(v8::String::New("f")));
10770 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10771 env->Global()->Get(v8::String::New("g")));
10772
10773 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10774 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10775 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10776
10777 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10778 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10779 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10780}
10781
10782
10783THREADED_TEST(ScriptLineNumber) {
10784 v8::HandleScope scope;
10785 LocalContext env;
10786 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10787 v8::Handle<v8::String> script = v8::String::New(
10788 "function f() {}\n\nfunction g() {}");
10789 v8::Script::Compile(script, &origin)->Run();
10790 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10791 env->Global()->Get(v8::String::New("f")));
10792 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10793 env->Global()->Get(v8::String::New("g")));
10794 CHECK_EQ(0, f->GetScriptLineNumber());
10795 CHECK_EQ(2, g->GetScriptLineNumber());
10796}
10797
10798
10799static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10800 const AccessorInfo& info) {
10801 return v8_num(42);
10802}
10803
10804
10805static void SetterWhichSetsYOnThisTo23(Local<String> name,
10806 Local<Value> value,
10807 const AccessorInfo& info) {
10808 info.This()->Set(v8_str("y"), v8_num(23));
10809}
10810
10811
Steve Block6ded16b2010-05-10 14:33:55 +010010812TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010813 v8::HandleScope scope;
10814 Local<ObjectTemplate> templ = ObjectTemplate::New();
10815 templ->SetAccessor(v8_str("x"),
10816 GetterWhichReturns42,
10817 SetterWhichSetsYOnThisTo23);
10818 LocalContext context;
10819 context->Global()->Set(v8_str("P"), templ->NewInstance());
10820 CompileRun("function C1() {"
10821 " this.x = 23;"
10822 "};"
10823 "C1.prototype = P;"
10824 "function C2() {"
10825 " this.x = 23"
10826 "};"
10827 "C2.prototype = { };"
10828 "C2.prototype.__proto__ = P;");
10829
10830 v8::Local<v8::Script> script;
10831 script = v8::Script::Compile(v8_str("new C1();"));
10832 for (int i = 0; i < 10; i++) {
10833 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10834 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10835 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10836 }
10837
10838 script = v8::Script::Compile(v8_str("new C2();"));
10839 for (int i = 0; i < 10; i++) {
10840 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10841 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10842 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10843 }
10844}
10845
10846
10847static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10848 Local<String> name, const AccessorInfo& info) {
10849 return v8_num(42);
10850}
10851
10852
10853static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10854 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10855 if (name->Equals(v8_str("x"))) {
10856 info.This()->Set(v8_str("y"), v8_num(23));
10857 }
10858 return v8::Handle<Value>();
10859}
10860
10861
10862THREADED_TEST(InterceptorOnConstructorPrototype) {
10863 v8::HandleScope scope;
10864 Local<ObjectTemplate> templ = ObjectTemplate::New();
10865 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10866 NamedPropertySetterWhichSetsYOnThisTo23);
10867 LocalContext context;
10868 context->Global()->Set(v8_str("P"), templ->NewInstance());
10869 CompileRun("function C1() {"
10870 " this.x = 23;"
10871 "};"
10872 "C1.prototype = P;"
10873 "function C2() {"
10874 " this.x = 23"
10875 "};"
10876 "C2.prototype = { };"
10877 "C2.prototype.__proto__ = P;");
10878
10879 v8::Local<v8::Script> script;
10880 script = v8::Script::Compile(v8_str("new C1();"));
10881 for (int i = 0; i < 10; i++) {
10882 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10883 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10884 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10885 }
10886
10887 script = v8::Script::Compile(v8_str("new C2();"));
10888 for (int i = 0; i < 10; i++) {
10889 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10890 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10891 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10892 }
10893}
Steve Block6ded16b2010-05-10 14:33:55 +010010894
10895
10896TEST(Bug618) {
10897 const char* source = "function C1() {"
10898 " this.x = 23;"
10899 "};"
10900 "C1.prototype = P;";
10901
10902 v8::HandleScope scope;
10903 LocalContext context;
10904 v8::Local<v8::Script> script;
10905
10906 // Use a simple object as prototype.
10907 v8::Local<v8::Object> prototype = v8::Object::New();
10908 prototype->Set(v8_str("y"), v8_num(42));
10909 context->Global()->Set(v8_str("P"), prototype);
10910
10911 // This compile will add the code to the compilation cache.
10912 CompileRun(source);
10913
10914 script = v8::Script::Compile(v8_str("new C1();"));
10915 for (int i = 0; i < 10; i++) {
10916 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10917 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10918 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10919 }
10920
10921 // Use an API object with accessors as prototype.
10922 Local<ObjectTemplate> templ = ObjectTemplate::New();
10923 templ->SetAccessor(v8_str("x"),
10924 GetterWhichReturns42,
10925 SetterWhichSetsYOnThisTo23);
10926 context->Global()->Set(v8_str("P"), templ->NewInstance());
10927
10928 // This compile will get the code from the compilation cache.
10929 CompileRun(source);
10930
10931 script = v8::Script::Compile(v8_str("new C1();"));
10932 for (int i = 0; i < 10; i++) {
10933 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10934 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10935 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10936 }
10937}
10938
10939int prologue_call_count = 0;
10940int epilogue_call_count = 0;
10941int prologue_call_count_second = 0;
10942int epilogue_call_count_second = 0;
10943
10944void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
10945 ++prologue_call_count;
10946}
10947
10948void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
10949 ++epilogue_call_count;
10950}
10951
10952void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10953 ++prologue_call_count_second;
10954}
10955
10956void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10957 ++epilogue_call_count_second;
10958}
10959
10960TEST(GCCallbacks) {
10961 LocalContext context;
10962
10963 v8::V8::AddGCPrologueCallback(PrologueCallback);
10964 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
10965 CHECK_EQ(0, prologue_call_count);
10966 CHECK_EQ(0, epilogue_call_count);
10967 i::Heap::CollectAllGarbage(false);
10968 CHECK_EQ(1, prologue_call_count);
10969 CHECK_EQ(1, epilogue_call_count);
10970 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
10971 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
10972 i::Heap::CollectAllGarbage(false);
10973 CHECK_EQ(2, prologue_call_count);
10974 CHECK_EQ(2, epilogue_call_count);
10975 CHECK_EQ(1, prologue_call_count_second);
10976 CHECK_EQ(1, epilogue_call_count_second);
10977 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
10978 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
10979 i::Heap::CollectAllGarbage(false);
10980 CHECK_EQ(2, prologue_call_count);
10981 CHECK_EQ(2, epilogue_call_count);
10982 CHECK_EQ(2, prologue_call_count_second);
10983 CHECK_EQ(2, epilogue_call_count_second);
10984 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
10985 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
10986 i::Heap::CollectAllGarbage(false);
10987 CHECK_EQ(2, prologue_call_count);
10988 CHECK_EQ(2, epilogue_call_count);
10989 CHECK_EQ(2, prologue_call_count_second);
10990 CHECK_EQ(2, epilogue_call_count_second);
10991}
Kristian Monsen25f61362010-05-21 11:50:48 +010010992
10993
10994THREADED_TEST(AddToJSFunctionResultCache) {
10995 i::FLAG_allow_natives_syntax = true;
10996 v8::HandleScope scope;
10997
10998 LocalContext context;
10999
11000 const char* code =
11001 "(function() {"
11002 " var key0 = 'a';"
11003 " var key1 = 'b';"
11004 " var r0 = %_GetFromCache(0, key0);"
11005 " var r1 = %_GetFromCache(0, key1);"
11006 " var r0_ = %_GetFromCache(0, key0);"
11007 " if (r0 !== r0_)"
11008 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
11009 " var r1_ = %_GetFromCache(0, key1);"
11010 " if (r1 !== r1_)"
11011 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
11012 " return 'PASSED';"
11013 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011014 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011015 ExpectString(code, "PASSED");
11016}
11017
11018
11019static const int k0CacheSize = 16;
11020
11021THREADED_TEST(FillJSFunctionResultCache) {
11022 i::FLAG_allow_natives_syntax = true;
11023 v8::HandleScope scope;
11024
11025 LocalContext context;
11026
11027 const char* code =
11028 "(function() {"
11029 " var k = 'a';"
11030 " var r = %_GetFromCache(0, k);"
11031 " for (var i = 0; i < 16; i++) {"
11032 " %_GetFromCache(0, 'a' + i);"
11033 " };"
11034 " if (r === %_GetFromCache(0, k))"
11035 " return 'FAILED: k0CacheSize is too small';"
11036 " return 'PASSED';"
11037 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011038 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011039 ExpectString(code, "PASSED");
11040}
11041
11042
11043THREADED_TEST(RoundRobinGetFromCache) {
11044 i::FLAG_allow_natives_syntax = true;
11045 v8::HandleScope scope;
11046
11047 LocalContext context;
11048
11049 const char* code =
11050 "(function() {"
11051 " var keys = [];"
11052 " for (var i = 0; i < 16; i++) keys.push(i);"
11053 " var values = [];"
11054 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11055 " for (var i = 0; i < 16; i++) {"
11056 " var v = %_GetFromCache(0, keys[i]);"
11057 " if (v !== values[i])"
11058 " return 'Wrong value for ' + "
11059 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11060 " };"
11061 " return 'PASSED';"
11062 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011063 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011064 ExpectString(code, "PASSED");
11065}
11066
11067
11068THREADED_TEST(ReverseGetFromCache) {
11069 i::FLAG_allow_natives_syntax = true;
11070 v8::HandleScope scope;
11071
11072 LocalContext context;
11073
11074 const char* code =
11075 "(function() {"
11076 " var keys = [];"
11077 " for (var i = 0; i < 16; i++) keys.push(i);"
11078 " var values = [];"
11079 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
11080 " for (var i = 15; i >= 16; i--) {"
11081 " var v = %_GetFromCache(0, keys[i]);"
11082 " if (v !== values[i])"
11083 " return 'Wrong value for ' + "
11084 " keys[i] + ': ' + v + ' vs. ' + values[i];"
11085 " };"
11086 " return 'PASSED';"
11087 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011088 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011089 ExpectString(code, "PASSED");
11090}
11091
11092
11093THREADED_TEST(TestEviction) {
11094 i::FLAG_allow_natives_syntax = true;
11095 v8::HandleScope scope;
11096
11097 LocalContext context;
11098
11099 const char* code =
11100 "(function() {"
11101 " for (var i = 0; i < 2*16; i++) {"
11102 " %_GetFromCache(0, 'a' + i);"
11103 " };"
11104 " return 'PASSED';"
11105 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010011106 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010011107 ExpectString(code, "PASSED");
11108}
Steve Block8defd9f2010-07-08 12:39:36 +010011109
11110
11111THREADED_TEST(TwoByteStringInAsciiCons) {
11112 // See Chromium issue 47824.
11113 v8::HandleScope scope;
11114
11115 LocalContext context;
11116 const char* init_code =
11117 "var str1 = 'abelspendabel';"
11118 "var str2 = str1 + str1 + str1;"
11119 "str2;";
11120 Local<Value> result = CompileRun(init_code);
11121
11122 CHECK(result->IsString());
11123 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
11124 int length = string->length();
11125 CHECK(string->IsAsciiRepresentation());
11126
11127 FlattenString(string);
11128 i::Handle<i::String> flat_string = FlattenGetString(string);
11129
11130 CHECK(string->IsAsciiRepresentation());
11131 CHECK(flat_string->IsAsciiRepresentation());
11132
11133 // Create external resource.
11134 uint16_t* uc16_buffer = new uint16_t[length + 1];
11135
11136 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
11137 uc16_buffer[length] = 0;
11138
11139 TestResource resource(uc16_buffer);
11140
11141 flat_string->MakeExternal(&resource);
11142
11143 CHECK(flat_string->IsTwoByteRepresentation());
11144
11145 // At this point, we should have a Cons string which is flat and ASCII,
11146 // with a first half that is a two-byte string (although it only contains
11147 // ASCII characters). This is a valid sequence of steps, and it can happen
11148 // in real pages.
11149
11150 CHECK(string->IsAsciiRepresentation());
11151 i::ConsString* cons = i::ConsString::cast(*string);
11152 CHECK_EQ(0, cons->second()->length());
11153 CHECK(cons->first()->IsTwoByteRepresentation());
11154
11155 // Check that some string operations work.
11156
11157 // Atom RegExp.
11158 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
11159 CHECK_EQ(6, reresult->Int32Value());
11160
11161 // Nonatom RegExp.
11162 reresult = CompileRun("str2.match(/abe./g).length;");
11163 CHECK_EQ(6, reresult->Int32Value());
11164
11165 reresult = CompileRun("str2.search(/bel/g);");
11166 CHECK_EQ(1, reresult->Int32Value());
11167
11168 reresult = CompileRun("str2.search(/be./g);");
11169 CHECK_EQ(1, reresult->Int32Value());
11170
11171 ExpectTrue("/bel/g.test(str2);");
11172
11173 ExpectTrue("/be./g.test(str2);");
11174
11175 reresult = CompileRun("/bel/g.exec(str2);");
11176 CHECK(!reresult->IsNull());
11177
11178 reresult = CompileRun("/be./g.exec(str2);");
11179 CHECK(!reresult->IsNull());
11180
11181 ExpectString("str2.substring(2, 10);", "elspenda");
11182
11183 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
11184
11185 ExpectString("str2.charAt(2);", "e");
11186
11187 reresult = CompileRun("str2.charCodeAt(2);");
11188 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
11189}