blob: 745babcc214e2a4c1a3bd05bf0cc334749e8b900 [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
473 Local<String> small_string = String::New(AsciiToTwoByteString("small"));
474 // We should refuse to externalize newly created small string.
475 CHECK(!small_string->CanMakeExternal());
476 // Trigger GCs so that the newly allocated string moves to old gen.
477 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
478 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
479 // Old space strings should be accepted.
480 CHECK(small_string->CanMakeExternal());
481
482 small_string = String::New(AsciiToTwoByteString("small 2"));
483 // We should refuse externalizing newly created small string.
484 CHECK(!small_string->CanMakeExternal());
485 for (int i = 0; i < 100; i++) {
486 String::Value value(small_string);
487 }
488 // Frequently used strings should be accepted.
489 CHECK(small_string->CanMakeExternal());
490
491 const int buf_size = 10 * 1024;
492 char* buf = i::NewArray<char>(buf_size);
493 memset(buf, 'a', buf_size);
494 buf[buf_size - 1] = '\0';
495 Local<String> large_string = String::New(AsciiToTwoByteString(buf));
496 i::DeleteArray(buf);
497 // Large strings should be immediately accepted.
498 CHECK(large_string->CanMakeExternal());
499}
500
501
502TEST(MakingExternalAsciiStringConditions) {
503 v8::HandleScope scope;
504 LocalContext env;
505
506 // Free some space in the new space so that we can check freshness.
507 i::Heap::CollectGarbage(0, i::NEW_SPACE);
508 i::Heap::CollectGarbage(0, i::NEW_SPACE);
509
510 Local<String> small_string = String::New("small");
511 // We should refuse to externalize newly created small string.
512 CHECK(!small_string->CanMakeExternal());
513 // Trigger GCs so that the newly allocated string moves to old gen.
514 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
515 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
516 // Old space strings should be accepted.
517 CHECK(small_string->CanMakeExternal());
518
519 small_string = String::New("small 2");
520 // We should refuse externalizing newly created small string.
521 CHECK(!small_string->CanMakeExternal());
522 for (int i = 0; i < 100; i++) {
523 String::Value value(small_string);
524 }
525 // Frequently used strings should be accepted.
526 CHECK(small_string->CanMakeExternal());
527
528 const int buf_size = 10 * 1024;
529 char* buf = i::NewArray<char>(buf_size);
530 memset(buf, 'a', buf_size);
531 buf[buf_size - 1] = '\0';
532 Local<String> large_string = String::New(buf);
533 i::DeleteArray(buf);
534 // Large strings should be immediately accepted.
535 CHECK(large_string->CanMakeExternal());
536}
537
538
Steve Blocka7e24c12009-10-30 11:49:00 +0000539THREADED_TEST(UsingExternalString) {
540 {
541 v8::HandleScope scope;
542 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
543 Local<String> string =
544 String::NewExternal(new TestResource(two_byte_string));
545 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
546 // Trigger GCs so that the newly allocated string moves to old gen.
547 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
548 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
549 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
550 CHECK(isymbol->IsSymbol());
551 }
552 i::Heap::CollectAllGarbage(false);
553 i::Heap::CollectAllGarbage(false);
554}
555
556
557THREADED_TEST(UsingExternalAsciiString) {
558 {
559 v8::HandleScope scope;
560 const char* one_byte_string = "test string";
561 Local<String> string = String::NewExternal(
562 new TestAsciiResource(i::StrDup(one_byte_string)));
563 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
564 // Trigger GCs so that the newly allocated string moves to old gen.
565 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
566 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
567 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
568 CHECK(isymbol->IsSymbol());
569 }
570 i::Heap::CollectAllGarbage(false);
571 i::Heap::CollectAllGarbage(false);
572}
573
574
Leon Clarkee46be812010-01-19 14:06:41 +0000575THREADED_TEST(ScavengeExternalString) {
576 TestResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100577 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000578 {
579 v8::HandleScope scope;
580 uint16_t* two_byte_string = AsciiToTwoByteString("test string");
581 Local<String> string =
582 String::NewExternal(new TestResource(two_byte_string));
583 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
584 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100585 in_new_space = i::Heap::InNewSpace(*istring);
586 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000587 CHECK_EQ(0, TestResource::dispose_count);
588 }
Steve Block6ded16b2010-05-10 14:33:55 +0100589 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000590 CHECK_EQ(1, TestResource::dispose_count);
591}
592
593
594THREADED_TEST(ScavengeExternalAsciiString) {
595 TestAsciiResource::dispose_count = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100596 bool in_new_space = false;
Leon Clarkee46be812010-01-19 14:06:41 +0000597 {
598 v8::HandleScope scope;
599 const char* one_byte_string = "test string";
600 Local<String> string = String::NewExternal(
601 new TestAsciiResource(i::StrDup(one_byte_string)));
602 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
603 i::Heap::CollectGarbage(0, i::NEW_SPACE);
Steve Block6ded16b2010-05-10 14:33:55 +0100604 in_new_space = i::Heap::InNewSpace(*istring);
605 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring));
Leon Clarkee46be812010-01-19 14:06:41 +0000606 CHECK_EQ(0, TestAsciiResource::dispose_count);
607 }
Steve Block6ded16b2010-05-10 14:33:55 +0100608 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE);
Leon Clarkee46be812010-01-19 14:06:41 +0000609 CHECK_EQ(1, TestAsciiResource::dispose_count);
610}
611
612
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100613class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
614 public:
615 static int dispose_calls;
616
617 TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
618 : TestAsciiResource(data),
619 dispose_(dispose) { }
620
621 void Dispose() {
622 ++dispose_calls;
623 if (dispose_) delete this;
624 }
625 private:
626 bool dispose_;
627};
628
629
630int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
631
632
633TEST(ExternalStringWithDisposeHandling) {
634 const char* c_source = "1 + 2 * 3";
635
636 // Use a stack allocated external string resource allocated object.
637 TestAsciiResource::dispose_count = 0;
638 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
639 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
640 {
641 v8::HandleScope scope;
642 LocalContext env;
643 Local<String> source = String::NewExternal(&res_stack);
644 Local<Script> script = Script::Compile(source);
645 Local<Value> value = script->Run();
646 CHECK(value->IsNumber());
647 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100648 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100649 CHECK_EQ(0, TestAsciiResource::dispose_count);
650 }
Steve Block8defd9f2010-07-08 12:39:36 +0100651 i::CompilationCache::Clear();
652 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100653 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
654 CHECK_EQ(0, TestAsciiResource::dispose_count);
655
656 // Use a heap allocated external string resource allocated object.
657 TestAsciiResource::dispose_count = 0;
658 TestAsciiResourceWithDisposeControl::dispose_calls = 0;
659 TestAsciiResource* res_heap =
660 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
661 {
662 v8::HandleScope scope;
663 LocalContext env;
664 Local<String> source = String::NewExternal(res_heap);
665 Local<Script> script = Script::Compile(source);
666 Local<Value> value = script->Run();
667 CHECK(value->IsNumber());
668 CHECK_EQ(7, value->Int32Value());
Steve Block8defd9f2010-07-08 12:39:36 +0100669 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100670 CHECK_EQ(0, TestAsciiResource::dispose_count);
671 }
Steve Block8defd9f2010-07-08 12:39:36 +0100672 i::CompilationCache::Clear();
673 i::Heap::CollectAllGarbage(false);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100674 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
675 CHECK_EQ(1, TestAsciiResource::dispose_count);
676}
677
678
Steve Block3ce2e202009-11-05 08:53:23 +0000679THREADED_TEST(StringConcat) {
680 {
681 v8::HandleScope scope;
682 LocalContext env;
683 const char* one_byte_string_1 = "function a_times_t";
684 const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
685 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
686 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
687 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
688 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
689 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
690 Local<String> left = v8_str(one_byte_string_1);
691 Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1));
692 Local<String> source = String::Concat(left, right);
693 right = String::NewExternal(
694 new TestAsciiResource(i::StrDup(one_byte_extern_1)));
695 source = String::Concat(source, right);
696 right = String::NewExternal(
697 new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
698 source = String::Concat(source, right);
699 right = v8_str(one_byte_string_2);
700 source = String::Concat(source, right);
701 right = String::New(AsciiToTwoByteString(two_byte_string_2));
702 source = String::Concat(source, right);
703 right = String::NewExternal(
704 new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
705 source = String::Concat(source, right);
706 Local<Script> script = Script::Compile(source);
707 Local<Value> value = script->Run();
708 CHECK(value->IsNumber());
709 CHECK_EQ(68, value->Int32Value());
710 }
Steve Block8defd9f2010-07-08 12:39:36 +0100711 i::CompilationCache::Clear();
Steve Block3ce2e202009-11-05 08:53:23 +0000712 i::Heap::CollectAllGarbage(false);
713 i::Heap::CollectAllGarbage(false);
714}
715
716
Steve Blocka7e24c12009-10-30 11:49:00 +0000717THREADED_TEST(GlobalProperties) {
718 v8::HandleScope scope;
719 LocalContext env;
720 v8::Handle<v8::Object> global = env->Global();
721 global->Set(v8_str("pi"), v8_num(3.1415926));
722 Local<Value> pi = global->Get(v8_str("pi"));
723 CHECK_EQ(3.1415926, pi->NumberValue());
724}
725
726
727static v8::Handle<Value> handle_call(const v8::Arguments& args) {
728 ApiTestFuzzer::Fuzz();
729 return v8_num(102);
730}
731
732
733static v8::Handle<Value> construct_call(const v8::Arguments& args) {
734 ApiTestFuzzer::Fuzz();
735 args.This()->Set(v8_str("x"), v8_num(1));
736 args.This()->Set(v8_str("y"), v8_num(2));
737 return args.This();
738}
739
740THREADED_TEST(FunctionTemplate) {
741 v8::HandleScope scope;
742 LocalContext env;
743 {
744 Local<v8::FunctionTemplate> fun_templ =
745 v8::FunctionTemplate::New(handle_call);
746 Local<Function> fun = fun_templ->GetFunction();
747 env->Global()->Set(v8_str("obj"), fun);
748 Local<Script> script = v8_compile("obj()");
749 CHECK_EQ(102, script->Run()->Int32Value());
750 }
751 // Use SetCallHandler to initialize a function template, should work like the
752 // previous one.
753 {
754 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
755 fun_templ->SetCallHandler(handle_call);
756 Local<Function> fun = fun_templ->GetFunction();
757 env->Global()->Set(v8_str("obj"), fun);
758 Local<Script> script = v8_compile("obj()");
759 CHECK_EQ(102, script->Run()->Int32Value());
760 }
761 // Test constructor calls.
762 {
763 Local<v8::FunctionTemplate> fun_templ =
764 v8::FunctionTemplate::New(construct_call);
765 fun_templ->SetClassName(v8_str("funky"));
766 Local<Function> fun = fun_templ->GetFunction();
767 env->Global()->Set(v8_str("obj"), fun);
768 Local<Script> script = v8_compile("var s = new obj(); s.x");
769 CHECK_EQ(1, script->Run()->Int32Value());
770
771 Local<Value> result = v8_compile("(new obj()).toString()")->Run();
772 CHECK_EQ(v8_str("[object funky]"), result);
773 }
774}
775
776
777THREADED_TEST(FindInstanceInPrototypeChain) {
778 v8::HandleScope scope;
779 LocalContext env;
780
781 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New();
782 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New();
783 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New();
784 derived->Inherit(base);
785
786 Local<v8::Function> base_function = base->GetFunction();
787 Local<v8::Function> derived_function = derived->GetFunction();
788 Local<v8::Function> other_function = other->GetFunction();
789
790 Local<v8::Object> base_instance = base_function->NewInstance();
791 Local<v8::Object> derived_instance = derived_function->NewInstance();
792 Local<v8::Object> derived_instance2 = derived_function->NewInstance();
793 Local<v8::Object> other_instance = other_function->NewInstance();
794 derived_instance2->Set(v8_str("__proto__"), derived_instance);
795 other_instance->Set(v8_str("__proto__"), derived_instance2);
796
797 // base_instance is only an instance of base.
798 CHECK_EQ(base_instance,
799 base_instance->FindInstanceInPrototypeChain(base));
800 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty());
801 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty());
802
803 // derived_instance is an instance of base and derived.
804 CHECK_EQ(derived_instance,
805 derived_instance->FindInstanceInPrototypeChain(base));
806 CHECK_EQ(derived_instance,
807 derived_instance->FindInstanceInPrototypeChain(derived));
808 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty());
809
810 // other_instance is an instance of other and its immediate
811 // prototype derived_instance2 is an instance of base and derived.
812 // Note, derived_instance is an instance of base and derived too,
813 // but it comes after derived_instance2 in the prototype chain of
814 // other_instance.
815 CHECK_EQ(derived_instance2,
816 other_instance->FindInstanceInPrototypeChain(base));
817 CHECK_EQ(derived_instance2,
818 other_instance->FindInstanceInPrototypeChain(derived));
819 CHECK_EQ(other_instance,
820 other_instance->FindInstanceInPrototypeChain(other));
821}
822
823
Steve Block3ce2e202009-11-05 08:53:23 +0000824THREADED_TEST(TinyInteger) {
825 v8::HandleScope scope;
826 LocalContext env;
827 int32_t value = 239;
828 Local<v8::Integer> value_obj = v8::Integer::New(value);
829 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
830}
831
832
833THREADED_TEST(BigSmiInteger) {
834 v8::HandleScope scope;
835 LocalContext env;
836 int32_t value = i::Smi::kMaxValue;
837 // We cannot add one to a Smi::kMaxValue without wrapping.
838 if (i::kSmiValueSize < 32) {
839 CHECK(i::Smi::IsValid(value));
840 CHECK(!i::Smi::IsValid(value + 1));
841 Local<v8::Integer> value_obj = v8::Integer::New(value);
842 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
843 }
844}
845
846
847THREADED_TEST(BigInteger) {
848 v8::HandleScope scope;
849 LocalContext env;
850 // We cannot add one to a Smi::kMaxValue without wrapping.
851 if (i::kSmiValueSize < 32) {
852 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
853 // The code will not be run in that case, due to the "if" guard.
854 int32_t value =
855 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
856 CHECK(value > i::Smi::kMaxValue);
857 CHECK(!i::Smi::IsValid(value));
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(TinyUnsignedInteger) {
865 v8::HandleScope scope;
866 LocalContext env;
867 uint32_t value = 239;
868 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
869 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
870}
871
872
873THREADED_TEST(BigUnsignedSmiInteger) {
874 v8::HandleScope scope;
875 LocalContext env;
876 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
877 CHECK(i::Smi::IsValid(value));
878 CHECK(!i::Smi::IsValid(value + 1));
879 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
880 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
881}
882
883
884THREADED_TEST(BigUnsignedInteger) {
885 v8::HandleScope scope;
886 LocalContext env;
887 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
888 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
889 CHECK(!i::Smi::IsValid(value));
890 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
891 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
892}
893
894
895THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
896 v8::HandleScope scope;
897 LocalContext env;
898 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
899 uint32_t value = INT32_MAX_AS_UINT + 1;
900 CHECK(value > INT32_MAX_AS_UINT); // No overflow.
901 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
902 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
903}
904
905
Steve Blocka7e24c12009-10-30 11:49:00 +0000906THREADED_TEST(Number) {
907 v8::HandleScope scope;
908 LocalContext env;
909 double PI = 3.1415926;
910 Local<v8::Number> pi_obj = v8::Number::New(PI);
911 CHECK_EQ(PI, pi_obj->NumberValue());
912}
913
914
915THREADED_TEST(ToNumber) {
916 v8::HandleScope scope;
917 LocalContext env;
918 Local<String> str = v8_str("3.1415926");
919 CHECK_EQ(3.1415926, str->NumberValue());
920 v8::Handle<v8::Boolean> t = v8::True();
921 CHECK_EQ(1.0, t->NumberValue());
922 v8::Handle<v8::Boolean> f = v8::False();
923 CHECK_EQ(0.0, f->NumberValue());
924}
925
926
927THREADED_TEST(Date) {
928 v8::HandleScope scope;
929 LocalContext env;
930 double PI = 3.1415926;
931 Local<Value> date_obj = v8::Date::New(PI);
932 CHECK_EQ(3.0, date_obj->NumberValue());
933}
934
935
936THREADED_TEST(Boolean) {
937 v8::HandleScope scope;
938 LocalContext env;
939 v8::Handle<v8::Boolean> t = v8::True();
940 CHECK(t->Value());
941 v8::Handle<v8::Boolean> f = v8::False();
942 CHECK(!f->Value());
943 v8::Handle<v8::Primitive> u = v8::Undefined();
944 CHECK(!u->BooleanValue());
945 v8::Handle<v8::Primitive> n = v8::Null();
946 CHECK(!n->BooleanValue());
947 v8::Handle<String> str1 = v8_str("");
948 CHECK(!str1->BooleanValue());
949 v8::Handle<String> str2 = v8_str("x");
950 CHECK(str2->BooleanValue());
951 CHECK(!v8::Number::New(0)->BooleanValue());
952 CHECK(v8::Number::New(-1)->BooleanValue());
953 CHECK(v8::Number::New(1)->BooleanValue());
954 CHECK(v8::Number::New(42)->BooleanValue());
955 CHECK(!v8_compile("NaN")->Run()->BooleanValue());
956}
957
958
959static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) {
960 ApiTestFuzzer::Fuzz();
961 return v8_num(13.4);
962}
963
964
965static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) {
966 ApiTestFuzzer::Fuzz();
967 return v8_num(876);
968}
969
970
971THREADED_TEST(GlobalPrototype) {
972 v8::HandleScope scope;
973 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
974 func_templ->PrototypeTemplate()->Set(
975 "dummy",
976 v8::FunctionTemplate::New(DummyCallHandler));
977 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate();
978 templ->Set("x", v8_num(200));
979 templ->SetAccessor(v8_str("m"), GetM);
980 LocalContext env(0, templ);
981 v8::Handle<v8::Object> obj = env->Global();
982 v8::Handle<Script> script = v8_compile("dummy()");
983 v8::Handle<Value> result = script->Run();
984 CHECK_EQ(13.4, result->NumberValue());
985 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value());
986 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value());
987}
988
989
Steve Blocka7e24c12009-10-30 11:49:00 +0000990THREADED_TEST(ObjectTemplate) {
991 v8::HandleScope scope;
992 Local<ObjectTemplate> templ1 = ObjectTemplate::New();
993 templ1->Set("x", v8_num(10));
994 templ1->Set("y", v8_num(13));
995 LocalContext env;
996 Local<v8::Object> instance1 = templ1->NewInstance();
997 env->Global()->Set(v8_str("p"), instance1);
998 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue());
999 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue());
1000 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
1001 fun->PrototypeTemplate()->Set("nirk", v8_num(123));
1002 Local<ObjectTemplate> templ2 = fun->InstanceTemplate();
1003 templ2->Set("a", v8_num(12));
1004 templ2->Set("b", templ1);
1005 Local<v8::Object> instance2 = templ2->NewInstance();
1006 env->Global()->Set(v8_str("q"), instance2);
1007 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue());
1008 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue());
1009 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue());
1010 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue());
1011}
1012
1013
1014static v8::Handle<Value> GetFlabby(const v8::Arguments& args) {
1015 ApiTestFuzzer::Fuzz();
1016 return v8_num(17.2);
1017}
1018
1019
1020static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) {
1021 ApiTestFuzzer::Fuzz();
1022 return v8_num(15.2);
1023}
1024
1025
1026THREADED_TEST(DescriptorInheritance) {
1027 v8::HandleScope scope;
1028 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New();
1029 super->PrototypeTemplate()->Set("flabby",
1030 v8::FunctionTemplate::New(GetFlabby));
1031 super->PrototypeTemplate()->Set("PI", v8_num(3.14));
1032
1033 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd);
1034
1035 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New();
1036 base1->Inherit(super);
1037 base1->PrototypeTemplate()->Set("v1", v8_num(20.1));
1038
1039 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New();
1040 base2->Inherit(super);
1041 base2->PrototypeTemplate()->Set("v2", v8_num(10.1));
1042
1043 LocalContext env;
1044
1045 env->Global()->Set(v8_str("s"), super->GetFunction());
1046 env->Global()->Set(v8_str("base1"), base1->GetFunction());
1047 env->Global()->Set(v8_str("base2"), base2->GetFunction());
1048
1049 // Checks right __proto__ chain.
1050 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue());
1051 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue());
1052
1053 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue());
1054
1055 // Instance accessor should not be visible on function object or its prototype
1056 CHECK(CompileRun("s.knurd == undefined")->BooleanValue());
1057 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue());
1058 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue());
1059
1060 env->Global()->Set(v8_str("obj"),
1061 base1->GetFunction()->NewInstance());
1062 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue());
1063 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue());
1064 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue());
1065 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue());
1066 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue());
1067
1068 env->Global()->Set(v8_str("obj2"),
1069 base2->GetFunction()->NewInstance());
1070 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue());
1071 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue());
1072 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue());
1073 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue());
1074 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue());
1075
1076 // base1 and base2 cannot cross reference to each's prototype
1077 CHECK(v8_compile("obj.v2")->Run()->IsUndefined());
1078 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined());
1079}
1080
1081
1082int echo_named_call_count;
1083
1084
1085static v8::Handle<Value> EchoNamedProperty(Local<String> name,
1086 const AccessorInfo& info) {
1087 ApiTestFuzzer::Fuzz();
1088 CHECK_EQ(v8_str("data"), info.Data());
1089 echo_named_call_count++;
1090 return name;
1091}
1092
1093
1094THREADED_TEST(NamedPropertyHandlerGetter) {
1095 echo_named_call_count = 0;
1096 v8::HandleScope scope;
1097 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1098 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
1099 0, 0, 0, 0,
1100 v8_str("data"));
1101 LocalContext env;
1102 env->Global()->Set(v8_str("obj"),
1103 templ->GetFunction()->NewInstance());
1104 CHECK_EQ(echo_named_call_count, 0);
1105 v8_compile("obj.x")->Run();
1106 CHECK_EQ(echo_named_call_count, 1);
1107 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;";
1108 v8::Handle<Value> str = CompileRun(code);
1109 String::AsciiValue value(str);
1110 CHECK_EQ(*value, "oddlepoddle");
1111 // Check default behavior
1112 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10);
1113 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue());
1114 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue());
1115}
1116
1117
1118int echo_indexed_call_count = 0;
1119
1120
1121static v8::Handle<Value> EchoIndexedProperty(uint32_t index,
1122 const AccessorInfo& info) {
1123 ApiTestFuzzer::Fuzz();
1124 CHECK_EQ(v8_num(637), info.Data());
1125 echo_indexed_call_count++;
1126 return v8_num(index);
1127}
1128
1129
1130THREADED_TEST(IndexedPropertyHandlerGetter) {
1131 v8::HandleScope scope;
1132 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1133 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
1134 0, 0, 0, 0,
1135 v8_num(637));
1136 LocalContext env;
1137 env->Global()->Set(v8_str("obj"),
1138 templ->GetFunction()->NewInstance());
1139 Local<Script> script = v8_compile("obj[900]");
1140 CHECK_EQ(script->Run()->Int32Value(), 900);
1141}
1142
1143
1144v8::Handle<v8::Object> bottom;
1145
1146static v8::Handle<Value> CheckThisIndexedPropertyHandler(
1147 uint32_t index,
1148 const AccessorInfo& info) {
1149 ApiTestFuzzer::Fuzz();
1150 CHECK(info.This()->Equals(bottom));
1151 return v8::Handle<Value>();
1152}
1153
1154static v8::Handle<Value> CheckThisNamedPropertyHandler(
1155 Local<String> name,
1156 const AccessorInfo& info) {
1157 ApiTestFuzzer::Fuzz();
1158 CHECK(info.This()->Equals(bottom));
1159 return v8::Handle<Value>();
1160}
1161
1162
1163v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index,
1164 Local<Value> value,
1165 const AccessorInfo& info) {
1166 ApiTestFuzzer::Fuzz();
1167 CHECK(info.This()->Equals(bottom));
1168 return v8::Handle<Value>();
1169}
1170
1171
1172v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property,
1173 Local<Value> value,
1174 const AccessorInfo& info) {
1175 ApiTestFuzzer::Fuzz();
1176 CHECK(info.This()->Equals(bottom));
1177 return v8::Handle<Value>();
1178}
1179
1180v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery(
1181 uint32_t index,
1182 const AccessorInfo& info) {
1183 ApiTestFuzzer::Fuzz();
1184 CHECK(info.This()->Equals(bottom));
1185 return v8::Handle<v8::Boolean>();
1186}
1187
1188
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001189v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property,
Steve Blocka7e24c12009-10-30 11:49:00 +00001190 const AccessorInfo& info) {
1191 ApiTestFuzzer::Fuzz();
1192 CHECK(info.This()->Equals(bottom));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001193 return v8::Handle<v8::Integer>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001194}
1195
1196
1197v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter(
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
1206v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter(
1207 Local<String> property,
1208 const AccessorInfo& info) {
1209 ApiTestFuzzer::Fuzz();
1210 CHECK(info.This()->Equals(bottom));
1211 return v8::Handle<v8::Boolean>();
1212}
1213
1214
1215v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator(
1216 const AccessorInfo& info) {
1217 ApiTestFuzzer::Fuzz();
1218 CHECK(info.This()->Equals(bottom));
1219 return v8::Handle<v8::Array>();
1220}
1221
1222
1223v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator(
1224 const AccessorInfo& info) {
1225 ApiTestFuzzer::Fuzz();
1226 CHECK(info.This()->Equals(bottom));
1227 return v8::Handle<v8::Array>();
1228}
1229
1230
1231THREADED_TEST(PropertyHandlerInPrototype) {
1232 v8::HandleScope scope;
1233 LocalContext env;
1234
1235 // Set up a prototype chain with three interceptors.
1236 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1237 templ->InstanceTemplate()->SetIndexedPropertyHandler(
1238 CheckThisIndexedPropertyHandler,
1239 CheckThisIndexedPropertySetter,
1240 CheckThisIndexedPropertyQuery,
1241 CheckThisIndexedPropertyDeleter,
1242 CheckThisIndexedPropertyEnumerator);
1243
1244 templ->InstanceTemplate()->SetNamedPropertyHandler(
1245 CheckThisNamedPropertyHandler,
1246 CheckThisNamedPropertySetter,
1247 CheckThisNamedPropertyQuery,
1248 CheckThisNamedPropertyDeleter,
1249 CheckThisNamedPropertyEnumerator);
1250
1251 bottom = templ->GetFunction()->NewInstance();
1252 Local<v8::Object> top = templ->GetFunction()->NewInstance();
1253 Local<v8::Object> middle = templ->GetFunction()->NewInstance();
1254
1255 bottom->Set(v8_str("__proto__"), middle);
1256 middle->Set(v8_str("__proto__"), top);
1257 env->Global()->Set(v8_str("obj"), bottom);
1258
1259 // Indexed and named get.
1260 Script::Compile(v8_str("obj[0]"))->Run();
1261 Script::Compile(v8_str("obj.x"))->Run();
1262
1263 // Indexed and named set.
1264 Script::Compile(v8_str("obj[1] = 42"))->Run();
1265 Script::Compile(v8_str("obj.y = 42"))->Run();
1266
1267 // Indexed and named query.
1268 Script::Compile(v8_str("0 in obj"))->Run();
1269 Script::Compile(v8_str("'x' in obj"))->Run();
1270
1271 // Indexed and named deleter.
1272 Script::Compile(v8_str("delete obj[0]"))->Run();
1273 Script::Compile(v8_str("delete obj.x"))->Run();
1274
1275 // Enumerators.
1276 Script::Compile(v8_str("for (var p in obj) ;"))->Run();
1277}
1278
1279
1280static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key,
1281 const AccessorInfo& info) {
1282 ApiTestFuzzer::Fuzz();
1283 if (v8_str("pre")->Equals(key)) {
1284 return v8_str("PrePropertyHandler: pre");
1285 }
1286 return v8::Handle<String>();
1287}
1288
1289
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001290static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key,
1291 const AccessorInfo&) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001292 if (v8_str("pre")->Equals(key)) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001293 return v8::Integer::New(v8::None);
Steve Blocka7e24c12009-10-30 11:49:00 +00001294 }
1295
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001296 return v8::Handle<v8::Integer>(); // do not intercept the call
Steve Blocka7e24c12009-10-30 11:49:00 +00001297}
1298
1299
1300THREADED_TEST(PrePropertyHandler) {
1301 v8::HandleScope scope;
1302 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New();
1303 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
1304 0,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001305 PrePropertyHandlerQuery);
Steve Blocka7e24c12009-10-30 11:49:00 +00001306 LocalContext env(NULL, desc->InstanceTemplate());
1307 Script::Compile(v8_str(
1308 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run();
1309 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run();
1310 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre);
1311 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run();
1312 CHECK_EQ(v8_str("Object: on"), result_on);
1313 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run();
1314 CHECK(result_post.IsEmpty());
1315}
1316
1317
1318THREADED_TEST(UndefinedIsNotEnumerable) {
1319 v8::HandleScope scope;
1320 LocalContext env;
1321 v8::Handle<Value> result = Script::Compile(v8_str(
1322 "this.propertyIsEnumerable(undefined)"))->Run();
1323 CHECK(result->IsFalse());
1324}
1325
1326
1327v8::Handle<Script> call_recursively_script;
Leon Clarke4515c472010-02-03 11:58:03 +00001328static const int kTargetRecursionDepth = 200; // near maximum
Steve Blocka7e24c12009-10-30 11:49:00 +00001329
1330
1331static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) {
1332 ApiTestFuzzer::Fuzz();
1333 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1334 if (depth == kTargetRecursionDepth) return v8::Undefined();
1335 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1336 return call_recursively_script->Run();
1337}
1338
1339
1340static v8::Handle<Value> CallFunctionRecursivelyCall(
1341 const v8::Arguments& args) {
1342 ApiTestFuzzer::Fuzz();
1343 int depth = args.This()->Get(v8_str("depth"))->Int32Value();
1344 if (depth == kTargetRecursionDepth) {
1345 printf("[depth = %d]\n", depth);
1346 return v8::Undefined();
1347 }
1348 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1));
1349 v8::Handle<Value> function =
1350 args.This()->Get(v8_str("callFunctionRecursively"));
Steve Block6ded16b2010-05-10 14:33:55 +01001351 return function.As<Function>()->Call(args.This(), 0, NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +00001352}
1353
1354
1355THREADED_TEST(DeepCrossLanguageRecursion) {
1356 v8::HandleScope scope;
1357 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
1358 global->Set(v8_str("callScriptRecursively"),
1359 v8::FunctionTemplate::New(CallScriptRecursivelyCall));
1360 global->Set(v8_str("callFunctionRecursively"),
1361 v8::FunctionTemplate::New(CallFunctionRecursivelyCall));
1362 LocalContext env(NULL, global);
1363
1364 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1365 call_recursively_script = v8_compile("callScriptRecursively()");
1366 v8::Handle<Value> result = call_recursively_script->Run();
1367 call_recursively_script = v8::Handle<Script>();
1368
1369 env->Global()->Set(v8_str("depth"), v8::Integer::New(0));
1370 Script::Compile(v8_str("callFunctionRecursively()"))->Run();
1371}
1372
1373
1374static v8::Handle<Value>
1375 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) {
1376 ApiTestFuzzer::Fuzz();
1377 return v8::ThrowException(key);
1378}
1379
1380
1381static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key,
1382 Local<Value>,
1383 const AccessorInfo&) {
1384 v8::ThrowException(key);
1385 return v8::Undefined(); // not the same as v8::Handle<v8::Value>()
1386}
1387
1388
1389THREADED_TEST(CallbackExceptionRegression) {
1390 v8::HandleScope scope;
1391 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
1392 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
1393 ThrowingPropertyHandlerSet);
1394 LocalContext env;
1395 env->Global()->Set(v8_str("obj"), obj->NewInstance());
1396 v8::Handle<Value> otto = Script::Compile(v8_str(
1397 "try { with (obj) { otto; } } catch (e) { e; }"))->Run();
1398 CHECK_EQ(v8_str("otto"), otto);
1399 v8::Handle<Value> netto = Script::Compile(v8_str(
1400 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run();
1401 CHECK_EQ(v8_str("netto"), netto);
1402}
1403
1404
Steve Blocka7e24c12009-10-30 11:49:00 +00001405THREADED_TEST(FunctionPrototype) {
1406 v8::HandleScope scope;
1407 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New();
1408 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321));
1409 LocalContext env;
1410 env->Global()->Set(v8_str("Foo"), Foo->GetFunction());
1411 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak"));
1412 CHECK_EQ(script->Run()->Int32Value(), 321);
1413}
1414
1415
1416THREADED_TEST(InternalFields) {
1417 v8::HandleScope scope;
1418 LocalContext env;
1419
1420 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1421 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1422 instance_templ->SetInternalFieldCount(1);
1423 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1424 CHECK_EQ(1, obj->InternalFieldCount());
1425 CHECK(obj->GetInternalField(0)->IsUndefined());
1426 obj->SetInternalField(0, v8_num(17));
1427 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value());
1428}
1429
1430
Steve Block6ded16b2010-05-10 14:33:55 +01001431THREADED_TEST(GlobalObjectInternalFields) {
1432 v8::HandleScope scope;
1433 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
1434 global_template->SetInternalFieldCount(1);
1435 LocalContext env(NULL, global_template);
1436 v8::Handle<v8::Object> global_proxy = env->Global();
1437 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
1438 CHECK_EQ(1, global->InternalFieldCount());
1439 CHECK(global->GetInternalField(0)->IsUndefined());
1440 global->SetInternalField(0, v8_num(17));
1441 CHECK_EQ(17, global->GetInternalField(0)->Int32Value());
1442}
1443
1444
Steve Blocka7e24c12009-10-30 11:49:00 +00001445THREADED_TEST(InternalFieldsNativePointers) {
1446 v8::HandleScope scope;
1447 LocalContext env;
1448
1449 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1450 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1451 instance_templ->SetInternalFieldCount(1);
1452 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1453 CHECK_EQ(1, obj->InternalFieldCount());
1454 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1455
1456 char* data = new char[100];
1457
1458 void* aligned = data;
1459 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1460 void* unaligned = data + 1;
1461 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1462
1463 // Check reading and writing aligned pointers.
1464 obj->SetPointerInInternalField(0, aligned);
1465 i::Heap::CollectAllGarbage(false);
1466 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1467
1468 // Check reading and writing unaligned pointers.
1469 obj->SetPointerInInternalField(0, unaligned);
1470 i::Heap::CollectAllGarbage(false);
1471 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1472
1473 delete[] data;
1474}
1475
1476
Steve Block3ce2e202009-11-05 08:53:23 +00001477THREADED_TEST(InternalFieldsNativePointersAndExternal) {
1478 v8::HandleScope scope;
1479 LocalContext env;
1480
1481 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
1482 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
1483 instance_templ->SetInternalFieldCount(1);
1484 Local<v8::Object> obj = templ->GetFunction()->NewInstance();
1485 CHECK_EQ(1, obj->InternalFieldCount());
1486 CHECK(obj->GetPointerFromInternalField(0) == NULL);
1487
1488 char* data = new char[100];
1489
1490 void* aligned = data;
1491 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
1492 void* unaligned = data + 1;
1493 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
1494
1495 obj->SetPointerInInternalField(0, aligned);
1496 i::Heap::CollectAllGarbage(false);
1497 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
1498
1499 obj->SetPointerInInternalField(0, unaligned);
1500 i::Heap::CollectAllGarbage(false);
1501 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
1502
1503 obj->SetInternalField(0, v8::External::Wrap(aligned));
1504 i::Heap::CollectAllGarbage(false);
1505 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
1506
1507 obj->SetInternalField(0, v8::External::Wrap(unaligned));
1508 i::Heap::CollectAllGarbage(false);
1509 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
1510
1511 delete[] data;
1512}
1513
1514
Steve Blocka7e24c12009-10-30 11:49:00 +00001515THREADED_TEST(IdentityHash) {
1516 v8::HandleScope scope;
1517 LocalContext env;
1518
1519 // Ensure that the test starts with an fresh heap to test whether the hash
1520 // code is based on the address.
1521 i::Heap::CollectAllGarbage(false);
1522 Local<v8::Object> obj = v8::Object::New();
1523 int hash = obj->GetIdentityHash();
1524 int hash1 = obj->GetIdentityHash();
1525 CHECK_EQ(hash, hash1);
1526 int hash2 = v8::Object::New()->GetIdentityHash();
1527 // Since the identity hash is essentially a random number two consecutive
1528 // objects should not be assigned the same hash code. If the test below fails
1529 // the random number generator should be evaluated.
1530 CHECK_NE(hash, hash2);
1531 i::Heap::CollectAllGarbage(false);
1532 int hash3 = v8::Object::New()->GetIdentityHash();
1533 // Make sure that the identity hash is not based on the initial address of
1534 // the object alone. If the test below fails the random number generator
1535 // should be evaluated.
1536 CHECK_NE(hash, hash3);
1537 int hash4 = obj->GetIdentityHash();
1538 CHECK_EQ(hash, hash4);
1539}
1540
1541
1542THREADED_TEST(HiddenProperties) {
1543 v8::HandleScope scope;
1544 LocalContext env;
1545
1546 v8::Local<v8::Object> obj = v8::Object::New();
1547 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1548 v8::Local<v8::String> empty = v8_str("");
1549 v8::Local<v8::String> prop_name = v8_str("prop_name");
1550
1551 i::Heap::CollectAllGarbage(false);
1552
1553 // Make sure delete of a non-existent hidden value works
1554 CHECK(obj->DeleteHiddenValue(key));
1555
1556 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503)));
1557 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value());
1558 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002)));
1559 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1560
1561 i::Heap::CollectAllGarbage(false);
1562
1563 // Make sure we do not find the hidden property.
1564 CHECK(!obj->Has(empty));
1565 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1566 CHECK(obj->Get(empty)->IsUndefined());
1567 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1568 CHECK(obj->Set(empty, v8::Integer::New(2003)));
1569 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1570 CHECK_EQ(2003, obj->Get(empty)->Int32Value());
1571
1572 i::Heap::CollectAllGarbage(false);
1573
1574 // Add another property and delete it afterwards to force the object in
1575 // slow case.
1576 CHECK(obj->Set(prop_name, v8::Integer::New(2008)));
1577 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1578 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value());
1579 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1580 CHECK(obj->Delete(prop_name));
1581 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value());
1582
1583 i::Heap::CollectAllGarbage(false);
1584
1585 CHECK(obj->DeleteHiddenValue(key));
1586 CHECK(obj->GetHiddenValue(key).IsEmpty());
1587}
1588
1589
Steve Blockd0582a62009-12-15 09:54:21 +00001590static bool interceptor_for_hidden_properties_called;
Steve Blocka7e24c12009-10-30 11:49:00 +00001591static v8::Handle<Value> InterceptorForHiddenProperties(
1592 Local<String> name, const AccessorInfo& info) {
Steve Blockd0582a62009-12-15 09:54:21 +00001593 interceptor_for_hidden_properties_called = true;
Steve Blocka7e24c12009-10-30 11:49:00 +00001594 return v8::Handle<Value>();
1595}
1596
1597
1598THREADED_TEST(HiddenPropertiesWithInterceptors) {
1599 v8::HandleScope scope;
1600 LocalContext context;
1601
Steve Blockd0582a62009-12-15 09:54:21 +00001602 interceptor_for_hidden_properties_called = false;
1603
Steve Blocka7e24c12009-10-30 11:49:00 +00001604 v8::Local<v8::String> key = v8_str("api-test::hidden-key");
1605
1606 // Associate an interceptor with an object and start setting hidden values.
1607 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
1608 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
1609 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
1610 Local<v8::Function> function = fun_templ->GetFunction();
1611 Local<v8::Object> obj = function->NewInstance();
1612 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302)));
1613 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value());
Steve Blockd0582a62009-12-15 09:54:21 +00001614 CHECK(!interceptor_for_hidden_properties_called);
Steve Blocka7e24c12009-10-30 11:49:00 +00001615}
1616
1617
1618THREADED_TEST(External) {
1619 v8::HandleScope scope;
1620 int x = 3;
1621 Local<v8::External> ext = v8::External::New(&x);
1622 LocalContext env;
1623 env->Global()->Set(v8_str("ext"), ext);
1624 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001625 v8::Handle<v8::External> reext = reext_obj.As<v8::External>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001626 int* ptr = static_cast<int*>(reext->Value());
1627 CHECK_EQ(x, 3);
1628 *ptr = 10;
1629 CHECK_EQ(x, 10);
1630
1631 // Make sure unaligned pointers are wrapped properly.
1632 char* data = i::StrDup("0123456789");
1633 Local<v8::Value> zero = v8::External::Wrap(&data[0]);
1634 Local<v8::Value> one = v8::External::Wrap(&data[1]);
1635 Local<v8::Value> two = v8::External::Wrap(&data[2]);
1636 Local<v8::Value> three = v8::External::Wrap(&data[3]);
1637
1638 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero));
1639 CHECK_EQ('0', *char_ptr);
1640 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one));
1641 CHECK_EQ('1', *char_ptr);
1642 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two));
1643 CHECK_EQ('2', *char_ptr);
1644 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three));
1645 CHECK_EQ('3', *char_ptr);
1646 i::DeleteArray(data);
1647}
1648
1649
1650THREADED_TEST(GlobalHandle) {
1651 v8::Persistent<String> global;
1652 {
1653 v8::HandleScope scope;
1654 Local<String> str = v8_str("str");
1655 global = v8::Persistent<String>::New(str);
1656 }
1657 CHECK_EQ(global->Length(), 3);
1658 global.Dispose();
1659}
1660
1661
1662THREADED_TEST(ScriptException) {
1663 v8::HandleScope scope;
1664 LocalContext env;
1665 Local<Script> script = Script::Compile(v8_str("throw 'panama!';"));
1666 v8::TryCatch try_catch;
1667 Local<Value> result = script->Run();
1668 CHECK(result.IsEmpty());
1669 CHECK(try_catch.HasCaught());
1670 String::AsciiValue exception_value(try_catch.Exception());
1671 CHECK_EQ(*exception_value, "panama!");
1672}
1673
1674
1675bool message_received;
1676
1677
1678static void check_message(v8::Handle<v8::Message> message,
1679 v8::Handle<Value> data) {
1680 CHECK_EQ(5.76, data->NumberValue());
1681 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
1682 CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
1683 message_received = true;
1684}
1685
1686
1687THREADED_TEST(MessageHandlerData) {
1688 message_received = false;
1689 v8::HandleScope scope;
1690 CHECK(!message_received);
1691 v8::V8::AddMessageListener(check_message, v8_num(5.76));
1692 LocalContext context;
1693 v8::ScriptOrigin origin =
1694 v8::ScriptOrigin(v8_str("6.75"));
1695 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
1696 &origin);
1697 script->SetData(v8_str("7.56"));
1698 script->Run();
1699 CHECK(message_received);
1700 // clear out the message listener
1701 v8::V8::RemoveMessageListeners(check_message);
1702}
1703
1704
1705THREADED_TEST(GetSetProperty) {
1706 v8::HandleScope scope;
1707 LocalContext context;
1708 context->Global()->Set(v8_str("foo"), v8_num(14));
1709 context->Global()->Set(v8_str("12"), v8_num(92));
1710 context->Global()->Set(v8::Integer::New(16), v8_num(32));
1711 context->Global()->Set(v8_num(13), v8_num(56));
1712 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run();
1713 CHECK_EQ(14, foo->Int32Value());
1714 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run();
1715 CHECK_EQ(92, twelve->Int32Value());
1716 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run();
1717 CHECK_EQ(32, sixteen->Int32Value());
1718 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run();
1719 CHECK_EQ(56, thirteen->Int32Value());
1720 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value());
1721 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value());
1722 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value());
1723 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value());
1724 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value());
1725 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value());
1726 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value());
1727 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value());
1728 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value());
1729}
1730
1731
1732THREADED_TEST(PropertyAttributes) {
1733 v8::HandleScope scope;
1734 LocalContext context;
1735 // read-only
1736 Local<String> prop = v8_str("read_only");
1737 context->Global()->Set(prop, v8_num(7), v8::ReadOnly);
1738 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1739 Script::Compile(v8_str("read_only = 9"))->Run();
1740 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1741 context->Global()->Set(prop, v8_num(10));
1742 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value());
1743 // dont-delete
1744 prop = v8_str("dont_delete");
1745 context->Global()->Set(prop, v8_num(13), v8::DontDelete);
1746 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1747 Script::Compile(v8_str("delete dont_delete"))->Run();
1748 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value());
1749}
1750
1751
1752THREADED_TEST(Array) {
1753 v8::HandleScope scope;
1754 LocalContext context;
1755 Local<v8::Array> array = v8::Array::New();
1756 CHECK_EQ(0, array->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001757 CHECK(array->Get(0)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001758 CHECK(!array->Has(0));
Steve Block6ded16b2010-05-10 14:33:55 +01001759 CHECK(array->Get(100)->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00001760 CHECK(!array->Has(100));
Steve Block6ded16b2010-05-10 14:33:55 +01001761 array->Set(2, v8_num(7));
Steve Blocka7e24c12009-10-30 11:49:00 +00001762 CHECK_EQ(3, array->Length());
1763 CHECK(!array->Has(0));
1764 CHECK(!array->Has(1));
1765 CHECK(array->Has(2));
Steve Block6ded16b2010-05-10 14:33:55 +01001766 CHECK_EQ(7, array->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001767 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01001768 Local<v8::Array> arr = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001769 CHECK_EQ(3, arr->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001770 CHECK_EQ(1, arr->Get(0)->Int32Value());
1771 CHECK_EQ(2, arr->Get(1)->Int32Value());
1772 CHECK_EQ(3, arr->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001773}
1774
1775
1776v8::Handle<Value> HandleF(const v8::Arguments& args) {
1777 v8::HandleScope scope;
1778 ApiTestFuzzer::Fuzz();
1779 Local<v8::Array> result = v8::Array::New(args.Length());
1780 for (int i = 0; i < args.Length(); i++)
Steve Block6ded16b2010-05-10 14:33:55 +01001781 result->Set(i, args[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001782 return scope.Close(result);
1783}
1784
1785
1786THREADED_TEST(Vector) {
1787 v8::HandleScope scope;
1788 Local<ObjectTemplate> global = ObjectTemplate::New();
1789 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF));
1790 LocalContext context(0, global);
1791
1792 const char* fun = "f()";
Steve Block6ded16b2010-05-10 14:33:55 +01001793 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001794 CHECK_EQ(0, a0->Length());
1795
1796 const char* fun2 = "f(11)";
Steve Block6ded16b2010-05-10 14:33:55 +01001797 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001798 CHECK_EQ(1, a1->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001799 CHECK_EQ(11, a1->Get(0)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001800
1801 const char* fun3 = "f(12, 13)";
Steve Block6ded16b2010-05-10 14:33:55 +01001802 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001803 CHECK_EQ(2, a2->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001804 CHECK_EQ(12, a2->Get(0)->Int32Value());
1805 CHECK_EQ(13, a2->Get(1)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001806
1807 const char* fun4 = "f(14, 15, 16)";
Steve Block6ded16b2010-05-10 14:33:55 +01001808 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001809 CHECK_EQ(3, a3->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001810 CHECK_EQ(14, a3->Get(0)->Int32Value());
1811 CHECK_EQ(15, a3->Get(1)->Int32Value());
1812 CHECK_EQ(16, a3->Get(2)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001813
1814 const char* fun5 = "f(17, 18, 19, 20)";
Steve Block6ded16b2010-05-10 14:33:55 +01001815 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00001816 CHECK_EQ(4, a4->Length());
Steve Block6ded16b2010-05-10 14:33:55 +01001817 CHECK_EQ(17, a4->Get(0)->Int32Value());
1818 CHECK_EQ(18, a4->Get(1)->Int32Value());
1819 CHECK_EQ(19, a4->Get(2)->Int32Value());
1820 CHECK_EQ(20, a4->Get(3)->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00001821}
1822
1823
1824THREADED_TEST(FunctionCall) {
1825 v8::HandleScope scope;
1826 LocalContext context;
1827 CompileRun(
1828 "function Foo() {"
1829 " var result = [];"
1830 " for (var i = 0; i < arguments.length; i++) {"
1831 " result.push(arguments[i]);"
1832 " }"
1833 " return result;"
1834 "}");
1835 Local<Function> Foo =
1836 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1837
1838 v8::Handle<Value>* args0 = NULL;
1839 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0));
1840 CHECK_EQ(0, a0->Length());
1841
1842 v8::Handle<Value> args1[] = { v8_num(1.1) };
1843 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1));
1844 CHECK_EQ(1, a1->Length());
1845 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
1846
1847 v8::Handle<Value> args2[] = { v8_num(2.2),
1848 v8_num(3.3) };
1849 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2));
1850 CHECK_EQ(2, a2->Length());
1851 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
1852 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
1853
1854 v8::Handle<Value> args3[] = { v8_num(4.4),
1855 v8_num(5.5),
1856 v8_num(6.6) };
1857 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3));
1858 CHECK_EQ(3, a3->Length());
1859 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
1860 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
1861 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
1862
1863 v8::Handle<Value> args4[] = { v8_num(7.7),
1864 v8_num(8.8),
1865 v8_num(9.9),
1866 v8_num(10.11) };
1867 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4));
1868 CHECK_EQ(4, a4->Length());
1869 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
1870 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
1871 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
1872 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
1873}
1874
1875
1876static const char* js_code_causing_out_of_memory =
1877 "var a = new Array(); while(true) a.push(a);";
1878
1879
1880// These tests run for a long time and prevent us from running tests
1881// that come after them so they cannot run in parallel.
1882TEST(OutOfMemory) {
1883 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001884 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001885 // Set heap limits.
1886 static const int K = 1024;
1887 v8::ResourceConstraints constraints;
1888 constraints.set_max_young_space_size(256 * K);
1889 constraints.set_max_old_space_size(4 * K * K);
1890 v8::SetResourceConstraints(&constraints);
1891
1892 // Execute a script that causes out of memory.
1893 v8::HandleScope scope;
1894 LocalContext context;
1895 v8::V8::IgnoreOutOfMemoryException();
1896 Local<Script> script =
1897 Script::Compile(String::New(js_code_causing_out_of_memory));
1898 Local<Value> result = script->Run();
1899
1900 // Check for out of memory state.
1901 CHECK(result.IsEmpty());
1902 CHECK(context->HasOutOfMemoryException());
1903}
1904
1905
1906v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) {
1907 ApiTestFuzzer::Fuzz();
1908
1909 v8::HandleScope scope;
1910 LocalContext context;
1911 Local<Script> script =
1912 Script::Compile(String::New(js_code_causing_out_of_memory));
1913 Local<Value> result = script->Run();
1914
1915 // Check for out of memory state.
1916 CHECK(result.IsEmpty());
1917 CHECK(context->HasOutOfMemoryException());
1918
1919 return result;
1920}
1921
1922
1923TEST(OutOfMemoryNested) {
1924 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001925 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001926 // Set heap limits.
1927 static const int K = 1024;
1928 v8::ResourceConstraints constraints;
1929 constraints.set_max_young_space_size(256 * K);
1930 constraints.set_max_old_space_size(4 * K * K);
1931 v8::SetResourceConstraints(&constraints);
1932
1933 v8::HandleScope scope;
1934 Local<ObjectTemplate> templ = ObjectTemplate::New();
1935 templ->Set(v8_str("ProvokeOutOfMemory"),
1936 v8::FunctionTemplate::New(ProvokeOutOfMemory));
1937 LocalContext context(0, templ);
1938 v8::V8::IgnoreOutOfMemoryException();
1939 Local<Value> result = CompileRun(
1940 "var thrown = false;"
1941 "try {"
1942 " ProvokeOutOfMemory();"
1943 "} catch (e) {"
1944 " thrown = true;"
1945 "}");
1946 // Check for out of memory state.
1947 CHECK(result.IsEmpty());
1948 CHECK(context->HasOutOfMemoryException());
1949}
1950
1951
1952TEST(HugeConsStringOutOfMemory) {
1953 // It's not possible to read a snapshot into a heap with different dimensions.
Steve Block8defd9f2010-07-08 12:39:36 +01001954 if (i::Snapshot::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001955 v8::HandleScope scope;
1956 LocalContext context;
1957 // Set heap limits.
1958 static const int K = 1024;
1959 v8::ResourceConstraints constraints;
1960 constraints.set_max_young_space_size(256 * K);
1961 constraints.set_max_old_space_size(2 * K * K);
1962 v8::SetResourceConstraints(&constraints);
1963
1964 // Execute a script that causes out of memory.
1965 v8::V8::IgnoreOutOfMemoryException();
1966
1967 // Build huge string. This should fail with out of memory exception.
1968 Local<Value> result = CompileRun(
1969 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
Steve Block3ce2e202009-11-05 08:53:23 +00001970 "for (var i = 0; i < 22; i++) { str = str + str; }");
Steve Blocka7e24c12009-10-30 11:49:00 +00001971
1972 // Check for out of memory state.
1973 CHECK(result.IsEmpty());
1974 CHECK(context->HasOutOfMemoryException());
1975}
1976
1977
1978THREADED_TEST(ConstructCall) {
1979 v8::HandleScope scope;
1980 LocalContext context;
1981 CompileRun(
1982 "function Foo() {"
1983 " var result = [];"
1984 " for (var i = 0; i < arguments.length; i++) {"
1985 " result.push(arguments[i]);"
1986 " }"
1987 " return result;"
1988 "}");
1989 Local<Function> Foo =
1990 Local<Function>::Cast(context->Global()->Get(v8_str("Foo")));
1991
1992 v8::Handle<Value>* args0 = NULL;
1993 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0));
1994 CHECK_EQ(0, a0->Length());
1995
1996 v8::Handle<Value> args1[] = { v8_num(1.1) };
1997 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1));
1998 CHECK_EQ(1, a1->Length());
1999 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue());
2000
2001 v8::Handle<Value> args2[] = { v8_num(2.2),
2002 v8_num(3.3) };
2003 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2));
2004 CHECK_EQ(2, a2->Length());
2005 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue());
2006 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue());
2007
2008 v8::Handle<Value> args3[] = { v8_num(4.4),
2009 v8_num(5.5),
2010 v8_num(6.6) };
2011 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3));
2012 CHECK_EQ(3, a3->Length());
2013 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue());
2014 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue());
2015 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue());
2016
2017 v8::Handle<Value> args4[] = { v8_num(7.7),
2018 v8_num(8.8),
2019 v8_num(9.9),
2020 v8_num(10.11) };
2021 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4));
2022 CHECK_EQ(4, a4->Length());
2023 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue());
2024 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue());
2025 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue());
2026 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue());
2027}
2028
2029
2030static void CheckUncle(v8::TryCatch* try_catch) {
2031 CHECK(try_catch->HasCaught());
2032 String::AsciiValue str_value(try_catch->Exception());
2033 CHECK_EQ(*str_value, "uncle?");
2034 try_catch->Reset();
2035}
2036
2037
Steve Block6ded16b2010-05-10 14:33:55 +01002038THREADED_TEST(ConversionNumber) {
2039 v8::HandleScope scope;
2040 LocalContext env;
2041 // Very large number.
2042 CompileRun("var obj = Math.pow(2,32) * 1237;");
2043 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2044 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
2045 CHECK_EQ(0, obj->ToInt32()->Value());
2046 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
2047 // Large number.
2048 CompileRun("var obj = -1234567890123;");
2049 obj = env->Global()->Get(v8_str("obj"));
2050 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
2051 CHECK_EQ(-1912276171, obj->ToInt32()->Value());
2052 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
2053 // Small positive integer.
2054 CompileRun("var obj = 42;");
2055 obj = env->Global()->Get(v8_str("obj"));
2056 CHECK_EQ(42.0, obj->ToNumber()->Value());
2057 CHECK_EQ(42, obj->ToInt32()->Value());
2058 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2059 // Negative integer.
2060 CompileRun("var obj = -37;");
2061 obj = env->Global()->Get(v8_str("obj"));
2062 CHECK_EQ(-37.0, obj->ToNumber()->Value());
2063 CHECK_EQ(-37, obj->ToInt32()->Value());
2064 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
2065 // Positive non-int32 integer.
2066 CompileRun("var obj = 0x81234567;");
2067 obj = env->Global()->Get(v8_str("obj"));
2068 CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
2069 CHECK_EQ(-2128394905, obj->ToInt32()->Value());
2070 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
2071 // Fraction.
2072 CompileRun("var obj = 42.3;");
2073 obj = env->Global()->Get(v8_str("obj"));
2074 CHECK_EQ(42.3, obj->ToNumber()->Value());
2075 CHECK_EQ(42, obj->ToInt32()->Value());
2076 CHECK(42u == obj->ToUint32()->Value()); // NOLINT
2077 // Large negative fraction.
2078 CompileRun("var obj = -5726623061.75;");
2079 obj = env->Global()->Get(v8_str("obj"));
2080 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
2081 CHECK_EQ(-1431655765, obj->ToInt32()->Value());
2082 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
2083}
2084
2085
2086THREADED_TEST(isNumberType) {
2087 v8::HandleScope scope;
2088 LocalContext env;
2089 // Very large number.
2090 CompileRun("var obj = Math.pow(2,32) * 1237;");
2091 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2092 CHECK(!obj->IsInt32());
2093 CHECK(!obj->IsUint32());
2094 // Large negative number.
2095 CompileRun("var obj = -1234567890123;");
2096 obj = env->Global()->Get(v8_str("obj"));
2097 CHECK(!obj->IsInt32());
2098 CHECK(!obj->IsUint32());
2099 // Small positive integer.
2100 CompileRun("var obj = 42;");
2101 obj = env->Global()->Get(v8_str("obj"));
2102 CHECK(obj->IsInt32());
2103 CHECK(obj->IsUint32());
2104 // Negative integer.
2105 CompileRun("var obj = -37;");
2106 obj = env->Global()->Get(v8_str("obj"));
2107 CHECK(obj->IsInt32());
2108 CHECK(!obj->IsUint32());
2109 // Positive non-int32 integer.
2110 CompileRun("var obj = 0x81234567;");
2111 obj = env->Global()->Get(v8_str("obj"));
2112 CHECK(!obj->IsInt32());
2113 CHECK(obj->IsUint32());
2114 // Fraction.
2115 CompileRun("var obj = 42.3;");
2116 obj = env->Global()->Get(v8_str("obj"));
2117 CHECK(!obj->IsInt32());
2118 CHECK(!obj->IsUint32());
2119 // Large negative fraction.
2120 CompileRun("var obj = -5726623061.75;");
2121 obj = env->Global()->Get(v8_str("obj"));
2122 CHECK(!obj->IsInt32());
2123 CHECK(!obj->IsUint32());
2124}
2125
2126
Steve Blocka7e24c12009-10-30 11:49:00 +00002127THREADED_TEST(ConversionException) {
2128 v8::HandleScope scope;
2129 LocalContext env;
2130 CompileRun(
2131 "function TestClass() { };"
2132 "TestClass.prototype.toString = function () { throw 'uncle?'; };"
2133 "var obj = new TestClass();");
2134 Local<Value> obj = env->Global()->Get(v8_str("obj"));
2135
2136 v8::TryCatch try_catch;
2137
2138 Local<Value> to_string_result = obj->ToString();
2139 CHECK(to_string_result.IsEmpty());
2140 CheckUncle(&try_catch);
2141
2142 Local<Value> to_number_result = obj->ToNumber();
2143 CHECK(to_number_result.IsEmpty());
2144 CheckUncle(&try_catch);
2145
2146 Local<Value> to_integer_result = obj->ToInteger();
2147 CHECK(to_integer_result.IsEmpty());
2148 CheckUncle(&try_catch);
2149
2150 Local<Value> to_uint32_result = obj->ToUint32();
2151 CHECK(to_uint32_result.IsEmpty());
2152 CheckUncle(&try_catch);
2153
2154 Local<Value> to_int32_result = obj->ToInt32();
2155 CHECK(to_int32_result.IsEmpty());
2156 CheckUncle(&try_catch);
2157
2158 Local<Value> to_object_result = v8::Undefined()->ToObject();
2159 CHECK(to_object_result.IsEmpty());
2160 CHECK(try_catch.HasCaught());
2161 try_catch.Reset();
2162
2163 int32_t int32_value = obj->Int32Value();
2164 CHECK_EQ(0, int32_value);
2165 CheckUncle(&try_catch);
2166
2167 uint32_t uint32_value = obj->Uint32Value();
2168 CHECK_EQ(0, uint32_value);
2169 CheckUncle(&try_catch);
2170
2171 double number_value = obj->NumberValue();
2172 CHECK_NE(0, IsNaN(number_value));
2173 CheckUncle(&try_catch);
2174
2175 int64_t integer_value = obj->IntegerValue();
2176 CHECK_EQ(0.0, static_cast<double>(integer_value));
2177 CheckUncle(&try_catch);
2178}
2179
2180
2181v8::Handle<Value> ThrowFromC(const v8::Arguments& args) {
2182 ApiTestFuzzer::Fuzz();
2183 return v8::ThrowException(v8_str("konto"));
2184}
2185
2186
2187v8::Handle<Value> CCatcher(const v8::Arguments& args) {
2188 if (args.Length() < 1) return v8::Boolean::New(false);
2189 v8::HandleScope scope;
2190 v8::TryCatch try_catch;
2191 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run();
2192 CHECK(!try_catch.HasCaught() || result.IsEmpty());
2193 return v8::Boolean::New(try_catch.HasCaught());
2194}
2195
2196
2197THREADED_TEST(APICatch) {
2198 v8::HandleScope scope;
2199 Local<ObjectTemplate> templ = ObjectTemplate::New();
2200 templ->Set(v8_str("ThrowFromC"),
2201 v8::FunctionTemplate::New(ThrowFromC));
2202 LocalContext context(0, templ);
2203 CompileRun(
2204 "var thrown = false;"
2205 "try {"
2206 " ThrowFromC();"
2207 "} catch (e) {"
2208 " thrown = true;"
2209 "}");
2210 Local<Value> thrown = context->Global()->Get(v8_str("thrown"));
2211 CHECK(thrown->BooleanValue());
2212}
2213
2214
2215THREADED_TEST(APIThrowTryCatch) {
2216 v8::HandleScope scope;
2217 Local<ObjectTemplate> templ = ObjectTemplate::New();
2218 templ->Set(v8_str("ThrowFromC"),
2219 v8::FunctionTemplate::New(ThrowFromC));
2220 LocalContext context(0, templ);
2221 v8::TryCatch try_catch;
2222 CompileRun("ThrowFromC();");
2223 CHECK(try_catch.HasCaught());
2224}
2225
2226
2227// Test that a try-finally block doesn't shadow a try-catch block
2228// when setting up an external handler.
2229//
2230// BUG(271): Some of the exception propagation does not work on the
2231// ARM simulator because the simulator separates the C++ stack and the
2232// JS stack. This test therefore fails on the simulator. The test is
2233// not threaded to allow the threading tests to run on the simulator.
2234TEST(TryCatchInTryFinally) {
2235 v8::HandleScope scope;
2236 Local<ObjectTemplate> templ = ObjectTemplate::New();
2237 templ->Set(v8_str("CCatcher"),
2238 v8::FunctionTemplate::New(CCatcher));
2239 LocalContext context(0, templ);
2240 Local<Value> result = CompileRun("try {"
2241 " try {"
2242 " CCatcher('throw 7;');"
2243 " } finally {"
2244 " }"
2245 "} catch (e) {"
2246 "}");
2247 CHECK(result->IsTrue());
2248}
2249
2250
2251static void receive_message(v8::Handle<v8::Message> message,
2252 v8::Handle<v8::Value> data) {
2253 message->Get();
2254 message_received = true;
2255}
2256
2257
2258TEST(APIThrowMessage) {
2259 message_received = false;
2260 v8::HandleScope scope;
2261 v8::V8::AddMessageListener(receive_message);
2262 Local<ObjectTemplate> templ = ObjectTemplate::New();
2263 templ->Set(v8_str("ThrowFromC"),
2264 v8::FunctionTemplate::New(ThrowFromC));
2265 LocalContext context(0, templ);
2266 CompileRun("ThrowFromC();");
2267 CHECK(message_received);
2268 v8::V8::RemoveMessageListeners(check_message);
2269}
2270
2271
2272TEST(APIThrowMessageAndVerboseTryCatch) {
2273 message_received = false;
2274 v8::HandleScope scope;
2275 v8::V8::AddMessageListener(receive_message);
2276 Local<ObjectTemplate> templ = ObjectTemplate::New();
2277 templ->Set(v8_str("ThrowFromC"),
2278 v8::FunctionTemplate::New(ThrowFromC));
2279 LocalContext context(0, templ);
2280 v8::TryCatch try_catch;
2281 try_catch.SetVerbose(true);
2282 Local<Value> result = CompileRun("ThrowFromC();");
2283 CHECK(try_catch.HasCaught());
2284 CHECK(result.IsEmpty());
2285 CHECK(message_received);
2286 v8::V8::RemoveMessageListeners(check_message);
2287}
2288
2289
2290THREADED_TEST(ExternalScriptException) {
2291 v8::HandleScope scope;
2292 Local<ObjectTemplate> templ = ObjectTemplate::New();
2293 templ->Set(v8_str("ThrowFromC"),
2294 v8::FunctionTemplate::New(ThrowFromC));
2295 LocalContext context(0, templ);
2296
2297 v8::TryCatch try_catch;
2298 Local<Script> script
2299 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';"));
2300 Local<Value> result = script->Run();
2301 CHECK(result.IsEmpty());
2302 CHECK(try_catch.HasCaught());
2303 String::AsciiValue exception_value(try_catch.Exception());
2304 CHECK_EQ("konto", *exception_value);
2305}
2306
2307
2308
2309v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) {
2310 ApiTestFuzzer::Fuzz();
2311 CHECK_EQ(4, args.Length());
2312 int count = args[0]->Int32Value();
2313 int cInterval = args[2]->Int32Value();
2314 if (count == 0) {
2315 return v8::ThrowException(v8_str("FromC"));
2316 } else {
2317 Local<v8::Object> global = Context::GetCurrent()->Global();
2318 Local<Value> fun = global->Get(v8_str("JSThrowCountDown"));
2319 v8::Handle<Value> argv[] = { v8_num(count - 1),
2320 args[1],
2321 args[2],
2322 args[3] };
2323 if (count % cInterval == 0) {
2324 v8::TryCatch try_catch;
Steve Block6ded16b2010-05-10 14:33:55 +01002325 Local<Value> result = fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002326 int expected = args[3]->Int32Value();
2327 if (try_catch.HasCaught()) {
2328 CHECK_EQ(expected, count);
2329 CHECK(result.IsEmpty());
2330 CHECK(!i::Top::has_scheduled_exception());
2331 } else {
2332 CHECK_NE(expected, count);
2333 }
2334 return result;
2335 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01002336 return fun.As<Function>()->Call(global, 4, argv);
Steve Blocka7e24c12009-10-30 11:49:00 +00002337 }
2338 }
2339}
2340
2341
2342v8::Handle<Value> JSCheck(const v8::Arguments& args) {
2343 ApiTestFuzzer::Fuzz();
2344 CHECK_EQ(3, args.Length());
2345 bool equality = args[0]->BooleanValue();
2346 int count = args[1]->Int32Value();
2347 int expected = args[2]->Int32Value();
2348 if (equality) {
2349 CHECK_EQ(count, expected);
2350 } else {
2351 CHECK_NE(count, expected);
2352 }
2353 return v8::Undefined();
2354}
2355
2356
2357THREADED_TEST(EvalInTryFinally) {
2358 v8::HandleScope scope;
2359 LocalContext context;
2360 v8::TryCatch try_catch;
2361 CompileRun("(function() {"
2362 " try {"
2363 " eval('asldkf (*&^&*^');"
2364 " } finally {"
2365 " return;"
2366 " }"
2367 "})()");
2368 CHECK(!try_catch.HasCaught());
2369}
2370
2371
2372// This test works by making a stack of alternating JavaScript and C
2373// activations. These activations set up exception handlers with regular
2374// intervals, one interval for C activations and another for JavaScript
2375// activations. When enough activations have been created an exception is
2376// thrown and we check that the right activation catches the exception and that
2377// no other activations do. The right activation is always the topmost one with
2378// a handler, regardless of whether it is in JavaScript or C.
2379//
2380// The notation used to describe a test case looks like this:
2381//
2382// *JS[4] *C[3] @JS[2] C[1] JS[0]
2383//
2384// Each entry is an activation, either JS or C. The index is the count at that
2385// level. Stars identify activations with exception handlers, the @ identifies
2386// the exception handler that should catch the exception.
2387//
2388// BUG(271): Some of the exception propagation does not work on the
2389// ARM simulator because the simulator separates the C++ stack and the
2390// JS stack. This test therefore fails on the simulator. The test is
2391// not threaded to allow the threading tests to run on the simulator.
2392TEST(ExceptionOrder) {
2393 v8::HandleScope scope;
2394 Local<ObjectTemplate> templ = ObjectTemplate::New();
2395 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck));
2396 templ->Set(v8_str("CThrowCountDown"),
2397 v8::FunctionTemplate::New(CThrowCountDown));
2398 LocalContext context(0, templ);
2399 CompileRun(
2400 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {"
2401 " if (count == 0) throw 'FromJS';"
2402 " if (count % jsInterval == 0) {"
2403 " try {"
2404 " var value = CThrowCountDown(count - 1,"
2405 " jsInterval,"
2406 " cInterval,"
2407 " expected);"
2408 " check(false, count, expected);"
2409 " return value;"
2410 " } catch (e) {"
2411 " check(true, count, expected);"
2412 " }"
2413 " } else {"
2414 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);"
2415 " }"
2416 "}");
2417 Local<Function> fun =
2418 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown")));
2419
2420 const int argc = 4;
2421 // count jsInterval cInterval expected
2422
2423 // *JS[4] *C[3] @JS[2] C[1] JS[0]
2424 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) };
2425 fun->Call(fun, argc, a0);
2426
2427 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0]
2428 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) };
2429 fun->Call(fun, argc, a1);
2430
2431 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0]
2432 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) };
2433 fun->Call(fun, argc, a2);
2434
2435 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0]
2436 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) };
2437 fun->Call(fun, argc, a3);
2438
2439 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0]
2440 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) };
2441 fun->Call(fun, argc, a4);
2442
2443 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0]
2444 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) };
2445 fun->Call(fun, argc, a5);
2446}
2447
2448
2449v8::Handle<Value> ThrowValue(const v8::Arguments& args) {
2450 ApiTestFuzzer::Fuzz();
2451 CHECK_EQ(1, args.Length());
2452 return v8::ThrowException(args[0]);
2453}
2454
2455
2456THREADED_TEST(ThrowValues) {
2457 v8::HandleScope scope;
2458 Local<ObjectTemplate> templ = ObjectTemplate::New();
2459 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue));
2460 LocalContext context(0, templ);
2461 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
2462 "function Run(obj) {"
2463 " try {"
2464 " Throw(obj);"
2465 " } catch (e) {"
2466 " return e;"
2467 " }"
2468 " return 'no exception';"
2469 "}"
2470 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];"));
2471 CHECK_EQ(5, result->Length());
2472 CHECK(result->Get(v8::Integer::New(0))->IsString());
2473 CHECK(result->Get(v8::Integer::New(1))->IsNumber());
2474 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value());
2475 CHECK(result->Get(v8::Integer::New(2))->IsNumber());
2476 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value());
2477 CHECK(result->Get(v8::Integer::New(3))->IsNull());
2478 CHECK(result->Get(v8::Integer::New(4))->IsUndefined());
2479}
2480
2481
2482THREADED_TEST(CatchZero) {
2483 v8::HandleScope scope;
2484 LocalContext context;
2485 v8::TryCatch try_catch;
2486 CHECK(!try_catch.HasCaught());
2487 Script::Compile(v8_str("throw 10"))->Run();
2488 CHECK(try_catch.HasCaught());
2489 CHECK_EQ(10, try_catch.Exception()->Int32Value());
2490 try_catch.Reset();
2491 CHECK(!try_catch.HasCaught());
2492 Script::Compile(v8_str("throw 0"))->Run();
2493 CHECK(try_catch.HasCaught());
2494 CHECK_EQ(0, try_catch.Exception()->Int32Value());
2495}
2496
2497
2498THREADED_TEST(CatchExceptionFromWith) {
2499 v8::HandleScope scope;
2500 LocalContext context;
2501 v8::TryCatch try_catch;
2502 CHECK(!try_catch.HasCaught());
2503 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run();
2504 CHECK(try_catch.HasCaught());
2505}
2506
2507
2508THREADED_TEST(Equality) {
2509 v8::HandleScope scope;
2510 LocalContext context;
2511 // Check that equality works at all before relying on CHECK_EQ
2512 CHECK(v8_str("a")->Equals(v8_str("a")));
2513 CHECK(!v8_str("a")->Equals(v8_str("b")));
2514
2515 CHECK_EQ(v8_str("a"), v8_str("a"));
2516 CHECK_NE(v8_str("a"), v8_str("b"));
2517 CHECK_EQ(v8_num(1), v8_num(1));
2518 CHECK_EQ(v8_num(1.00), v8_num(1));
2519 CHECK_NE(v8_num(1), v8_num(2));
2520
2521 // Assume String is not symbol.
2522 CHECK(v8_str("a")->StrictEquals(v8_str("a")));
2523 CHECK(!v8_str("a")->StrictEquals(v8_str("b")));
2524 CHECK(!v8_str("5")->StrictEquals(v8_num(5)));
2525 CHECK(v8_num(1)->StrictEquals(v8_num(1)));
2526 CHECK(!v8_num(1)->StrictEquals(v8_num(2)));
2527 CHECK(v8_num(0)->StrictEquals(v8_num(-0)));
2528 Local<Value> not_a_number = v8_num(i::OS::nan_value());
2529 CHECK(!not_a_number->StrictEquals(not_a_number));
2530 CHECK(v8::False()->StrictEquals(v8::False()));
2531 CHECK(!v8::False()->StrictEquals(v8::Undefined()));
2532
2533 v8::Handle<v8::Object> obj = v8::Object::New();
2534 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj);
2535 CHECK(alias->StrictEquals(obj));
2536 alias.Dispose();
2537}
2538
2539
2540THREADED_TEST(MultiRun) {
2541 v8::HandleScope scope;
2542 LocalContext context;
2543 Local<Script> script = Script::Compile(v8_str("x"));
2544 for (int i = 0; i < 10; i++)
2545 script->Run();
2546}
2547
2548
2549static v8::Handle<Value> GetXValue(Local<String> name,
2550 const AccessorInfo& info) {
2551 ApiTestFuzzer::Fuzz();
2552 CHECK_EQ(info.Data(), v8_str("donut"));
2553 CHECK_EQ(name, v8_str("x"));
2554 return name;
2555}
2556
2557
2558THREADED_TEST(SimplePropertyRead) {
2559 v8::HandleScope scope;
2560 Local<ObjectTemplate> templ = ObjectTemplate::New();
2561 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2562 LocalContext context;
2563 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2564 Local<Script> script = Script::Compile(v8_str("obj.x"));
2565 for (int i = 0; i < 10; i++) {
2566 Local<Value> result = script->Run();
2567 CHECK_EQ(result, v8_str("x"));
2568 }
2569}
2570
Andrei Popescu31002712010-02-23 13:46:05 +00002571THREADED_TEST(DefinePropertyOnAPIAccessor) {
2572 v8::HandleScope scope;
2573 Local<ObjectTemplate> templ = ObjectTemplate::New();
2574 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2575 LocalContext context;
2576 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2577
2578 // Uses getOwnPropertyDescriptor to check the configurable status
2579 Local<Script> script_desc
Leon Clarkef7060e22010-06-03 12:02:55 +01002580 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( "
Andrei Popescu31002712010-02-23 13:46:05 +00002581 "obj, 'x');"
2582 "prop.configurable;"));
2583 Local<Value> result = script_desc->Run();
2584 CHECK_EQ(result->BooleanValue(), true);
2585
2586 // Redefine get - but still configurable
2587 Local<Script> script_define
2588 = Script::Compile(v8_str("var desc = { get: function(){return 42; },"
2589 " configurable: true };"
2590 "Object.defineProperty(obj, 'x', desc);"
2591 "obj.x"));
2592 result = script_define->Run();
2593 CHECK_EQ(result, v8_num(42));
2594
2595 // Check that the accessor is still configurable
2596 result = script_desc->Run();
2597 CHECK_EQ(result->BooleanValue(), true);
2598
2599 // Redefine to a non-configurable
2600 script_define
2601 = Script::Compile(v8_str("var desc = { get: function(){return 43; },"
2602 " configurable: false };"
2603 "Object.defineProperty(obj, 'x', desc);"
2604 "obj.x"));
2605 result = script_define->Run();
2606 CHECK_EQ(result, v8_num(43));
2607 result = script_desc->Run();
2608 CHECK_EQ(result->BooleanValue(), false);
2609
2610 // Make sure that it is not possible to redefine again
2611 v8::TryCatch try_catch;
2612 result = script_define->Run();
2613 CHECK(try_catch.HasCaught());
2614 String::AsciiValue exception_value(try_catch.Exception());
2615 CHECK_EQ(*exception_value,
2616 "TypeError: Cannot redefine property: defineProperty");
2617}
2618
2619THREADED_TEST(DefinePropertyOnDefineGetterSetter) {
2620 v8::HandleScope scope;
2621 Local<ObjectTemplate> templ = ObjectTemplate::New();
2622 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"));
2623 LocalContext context;
2624 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2625
2626 Local<Script> script_desc = Script::Compile(v8_str("var prop ="
2627 "Object.getOwnPropertyDescriptor( "
2628 "obj, 'x');"
2629 "prop.configurable;"));
2630 Local<Value> result = script_desc->Run();
2631 CHECK_EQ(result->BooleanValue(), true);
2632
2633 Local<Script> script_define =
2634 Script::Compile(v8_str("var desc = {get: function(){return 42; },"
2635 " configurable: true };"
2636 "Object.defineProperty(obj, 'x', desc);"
2637 "obj.x"));
2638 result = script_define->Run();
2639 CHECK_EQ(result, v8_num(42));
2640
2641
2642 result = script_desc->Run();
2643 CHECK_EQ(result->BooleanValue(), true);
2644
2645
2646 script_define =
2647 Script::Compile(v8_str("var desc = {get: function(){return 43; },"
2648 " configurable: false };"
2649 "Object.defineProperty(obj, 'x', desc);"
2650 "obj.x"));
2651 result = script_define->Run();
2652 CHECK_EQ(result, v8_num(43));
2653 result = script_desc->Run();
2654
2655 CHECK_EQ(result->BooleanValue(), false);
2656
2657 v8::TryCatch try_catch;
2658 result = script_define->Run();
2659 CHECK(try_catch.HasCaught());
2660 String::AsciiValue exception_value(try_catch.Exception());
2661 CHECK_EQ(*exception_value,
2662 "TypeError: Cannot redefine property: defineProperty");
2663}
2664
2665
Leon Clarkef7060e22010-06-03 12:02:55 +01002666static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context,
2667 char const* name) {
2668 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name)));
2669}
Andrei Popescu31002712010-02-23 13:46:05 +00002670
2671
Leon Clarkef7060e22010-06-03 12:02:55 +01002672THREADED_TEST(DefineAPIAccessorOnObject) {
2673 v8::HandleScope scope;
2674 Local<ObjectTemplate> templ = ObjectTemplate::New();
2675 LocalContext context;
2676
2677 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2678 CompileRun("var obj2 = {};");
2679
2680 CHECK(CompileRun("obj1.x")->IsUndefined());
2681 CHECK(CompileRun("obj2.x")->IsUndefined());
2682
2683 CHECK(GetGlobalProperty(&context, "obj1")->
2684 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2685
2686 ExpectString("obj1.x", "x");
2687 CHECK(CompileRun("obj2.x")->IsUndefined());
2688
2689 CHECK(GetGlobalProperty(&context, "obj2")->
2690 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2691
2692 ExpectString("obj1.x", "x");
2693 ExpectString("obj2.x", "x");
2694
2695 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2696 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2697
2698 CompileRun("Object.defineProperty(obj1, 'x',"
2699 "{ get: function() { return 'y'; }, configurable: true })");
2700
2701 ExpectString("obj1.x", "y");
2702 ExpectString("obj2.x", "x");
2703
2704 CompileRun("Object.defineProperty(obj2, 'x',"
2705 "{ get: function() { return 'y'; }, configurable: true })");
2706
2707 ExpectString("obj1.x", "y");
2708 ExpectString("obj2.x", "y");
2709
2710 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2711 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2712
2713 CHECK(GetGlobalProperty(&context, "obj1")->
2714 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2715 CHECK(GetGlobalProperty(&context, "obj2")->
2716 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2717
2718 ExpectString("obj1.x", "x");
2719 ExpectString("obj2.x", "x");
2720
2721 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2722 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2723
2724 // Define getters/setters, but now make them not configurable.
2725 CompileRun("Object.defineProperty(obj1, 'x',"
2726 "{ get: function() { return 'z'; }, configurable: false })");
2727 CompileRun("Object.defineProperty(obj2, 'x',"
2728 "{ get: function() { return 'z'; }, configurable: false })");
2729
2730 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2731 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2732
2733 ExpectString("obj1.x", "z");
2734 ExpectString("obj2.x", "z");
2735
2736 CHECK(!GetGlobalProperty(&context, "obj1")->
2737 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2738 CHECK(!GetGlobalProperty(&context, "obj2")->
2739 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2740
2741 ExpectString("obj1.x", "z");
2742 ExpectString("obj2.x", "z");
2743}
2744
2745
2746THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) {
2747 v8::HandleScope scope;
2748 Local<ObjectTemplate> templ = ObjectTemplate::New();
2749 LocalContext context;
2750
2751 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2752 CompileRun("var obj2 = {};");
2753
2754 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2755 v8_str("x"),
2756 GetXValue, NULL,
2757 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2758 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2759 v8_str("x"),
2760 GetXValue, NULL,
2761 v8_str("donut"), v8::DEFAULT, v8::DontDelete));
2762
2763 ExpectString("obj1.x", "x");
2764 ExpectString("obj2.x", "x");
2765
2766 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable");
2767 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable");
2768
2769 CHECK(!GetGlobalProperty(&context, "obj1")->
2770 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2771 CHECK(!GetGlobalProperty(&context, "obj2")->
2772 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")));
2773
2774 {
2775 v8::TryCatch try_catch;
2776 CompileRun("Object.defineProperty(obj1, 'x',"
2777 "{get: function() { return 'func'; }})");
2778 CHECK(try_catch.HasCaught());
2779 String::AsciiValue exception_value(try_catch.Exception());
2780 CHECK_EQ(*exception_value,
2781 "TypeError: Cannot redefine property: defineProperty");
2782 }
2783 {
2784 v8::TryCatch try_catch;
2785 CompileRun("Object.defineProperty(obj2, 'x',"
2786 "{get: function() { return 'func'; }})");
2787 CHECK(try_catch.HasCaught());
2788 String::AsciiValue exception_value(try_catch.Exception());
2789 CHECK_EQ(*exception_value,
2790 "TypeError: Cannot redefine property: defineProperty");
2791 }
2792}
2793
2794
2795static v8::Handle<Value> Get239Value(Local<String> name,
2796 const AccessorInfo& info) {
2797 ApiTestFuzzer::Fuzz();
2798 CHECK_EQ(info.Data(), v8_str("donut"));
2799 CHECK_EQ(name, v8_str("239"));
2800 return name;
2801}
2802
2803
2804THREADED_TEST(ElementAPIAccessor) {
2805 v8::HandleScope scope;
2806 Local<ObjectTemplate> templ = ObjectTemplate::New();
2807 LocalContext context;
2808
2809 context->Global()->Set(v8_str("obj1"), templ->NewInstance());
2810 CompileRun("var obj2 = {};");
2811
2812 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor(
2813 v8_str("239"),
2814 Get239Value, NULL,
2815 v8_str("donut")));
2816 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor(
2817 v8_str("239"),
2818 Get239Value, NULL,
2819 v8_str("donut")));
2820
2821 ExpectString("obj1[239]", "239");
2822 ExpectString("obj2[239]", "239");
2823 ExpectString("obj1['239']", "239");
2824 ExpectString("obj2['239']", "239");
2825}
2826
Steve Blocka7e24c12009-10-30 11:49:00 +00002827
2828v8::Persistent<Value> xValue;
2829
2830
2831static void SetXValue(Local<String> name,
2832 Local<Value> value,
2833 const AccessorInfo& info) {
2834 CHECK_EQ(value, v8_num(4));
2835 CHECK_EQ(info.Data(), v8_str("donut"));
2836 CHECK_EQ(name, v8_str("x"));
2837 CHECK(xValue.IsEmpty());
2838 xValue = v8::Persistent<Value>::New(value);
2839}
2840
2841
2842THREADED_TEST(SimplePropertyWrite) {
2843 v8::HandleScope scope;
2844 Local<ObjectTemplate> templ = ObjectTemplate::New();
2845 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut"));
2846 LocalContext context;
2847 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2848 Local<Script> script = Script::Compile(v8_str("obj.x = 4"));
2849 for (int i = 0; i < 10; i++) {
2850 CHECK(xValue.IsEmpty());
2851 script->Run();
2852 CHECK_EQ(v8_num(4), xValue);
2853 xValue.Dispose();
2854 xValue = v8::Persistent<Value>();
2855 }
2856}
2857
2858
2859static v8::Handle<Value> XPropertyGetter(Local<String> property,
2860 const AccessorInfo& info) {
2861 ApiTestFuzzer::Fuzz();
2862 CHECK(info.Data()->IsUndefined());
2863 return property;
2864}
2865
2866
2867THREADED_TEST(NamedInterceptorPropertyRead) {
2868 v8::HandleScope scope;
2869 Local<ObjectTemplate> templ = ObjectTemplate::New();
2870 templ->SetNamedPropertyHandler(XPropertyGetter);
2871 LocalContext context;
2872 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2873 Local<Script> script = Script::Compile(v8_str("obj.x"));
2874 for (int i = 0; i < 10; i++) {
2875 Local<Value> result = script->Run();
2876 CHECK_EQ(result, v8_str("x"));
2877 }
2878}
2879
2880
Steve Block6ded16b2010-05-10 14:33:55 +01002881THREADED_TEST(NamedInterceptorDictionaryIC) {
2882 v8::HandleScope scope;
2883 Local<ObjectTemplate> templ = ObjectTemplate::New();
2884 templ->SetNamedPropertyHandler(XPropertyGetter);
2885 LocalContext context;
2886 // Create an object with a named interceptor.
2887 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
2888 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
2889 for (int i = 0; i < 10; i++) {
2890 Local<Value> result = script->Run();
2891 CHECK_EQ(result, v8_str("x"));
2892 }
2893 // Create a slow case object and a function accessing a property in
2894 // that slow case object (with dictionary probing in generated
2895 // code). Then force object with a named interceptor into slow-case,
2896 // pass it to the function, and check that the interceptor is called
2897 // instead of accessing the local property.
2898 Local<Value> result =
2899 CompileRun("function get_x(o) { return o.x; };"
2900 "var obj = { x : 42, y : 0 };"
2901 "delete obj.y;"
2902 "for (var i = 0; i < 10; i++) get_x(obj);"
2903 "interceptor_obj.x = 42;"
2904 "interceptor_obj.y = 10;"
2905 "delete interceptor_obj.y;"
2906 "get_x(interceptor_obj)");
2907 CHECK_EQ(result, v8_str("x"));
2908}
2909
2910
Andrei Popescu402d9372010-02-26 13:31:12 +00002911static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
2912 const AccessorInfo& info) {
2913 // Set x on the prototype object and do not handle the get request.
2914 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
Steve Block6ded16b2010-05-10 14:33:55 +01002915 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23));
Andrei Popescu402d9372010-02-26 13:31:12 +00002916 return v8::Handle<Value>();
2917}
2918
2919
2920// This is a regression test for http://crbug.com/20104. Map
2921// transitions should not interfere with post interceptor lookup.
2922THREADED_TEST(NamedInterceptorMapTransitionRead) {
2923 v8::HandleScope scope;
2924 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
2925 Local<v8::ObjectTemplate> instance_template
2926 = function_template->InstanceTemplate();
2927 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
2928 LocalContext context;
2929 context->Global()->Set(v8_str("F"), function_template->GetFunction());
2930 // Create an instance of F and introduce a map transition for x.
2931 CompileRun("var o = new F(); o.x = 23;");
2932 // Create an instance of F and invoke the getter. The result should be 23.
2933 Local<Value> result = CompileRun("o = new F(); o.x");
2934 CHECK_EQ(result->Int32Value(), 23);
2935}
2936
2937
Steve Blocka7e24c12009-10-30 11:49:00 +00002938static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
2939 const AccessorInfo& info) {
2940 ApiTestFuzzer::Fuzz();
2941 if (index == 37) {
2942 return v8::Handle<Value>(v8_num(625));
2943 }
2944 return v8::Handle<Value>();
2945}
2946
2947
2948static v8::Handle<Value> IndexedPropertySetter(uint32_t index,
2949 Local<Value> value,
2950 const AccessorInfo& info) {
2951 ApiTestFuzzer::Fuzz();
2952 if (index == 39) {
2953 return value;
2954 }
2955 return v8::Handle<Value>();
2956}
2957
2958
2959THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
2960 v8::HandleScope scope;
2961 Local<ObjectTemplate> templ = ObjectTemplate::New();
2962 templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
2963 IndexedPropertySetter);
2964 LocalContext context;
2965 context->Global()->Set(v8_str("obj"), templ->NewInstance());
2966 Local<Script> getter_script = Script::Compile(v8_str(
2967 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"));
2968 Local<Script> setter_script = Script::Compile(v8_str(
2969 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});"
2970 "obj[17] = 23;"
2971 "obj.foo;"));
2972 Local<Script> interceptor_setter_script = Script::Compile(v8_str(
2973 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});"
2974 "obj[39] = 47;"
2975 "obj.foo;")); // This setter should not run, due to the interceptor.
2976 Local<Script> interceptor_getter_script = Script::Compile(v8_str(
2977 "obj[37];"));
2978 Local<Value> result = getter_script->Run();
2979 CHECK_EQ(v8_num(5), result);
2980 result = setter_script->Run();
2981 CHECK_EQ(v8_num(23), result);
2982 result = interceptor_setter_script->Run();
2983 CHECK_EQ(v8_num(23), result);
2984 result = interceptor_getter_script->Run();
2985 CHECK_EQ(v8_num(625), result);
2986}
2987
2988
Leon Clarked91b9f72010-01-27 17:25:45 +00002989static v8::Handle<Value> IdentityIndexedPropertyGetter(
2990 uint32_t index,
2991 const AccessorInfo& info) {
2992 return v8::Integer::New(index);
2993}
2994
2995
2996THREADED_TEST(IndexedInterceptorWithNoSetter) {
2997 v8::HandleScope scope;
2998 Local<ObjectTemplate> templ = ObjectTemplate::New();
2999 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3000
3001 LocalContext context;
3002 context->Global()->Set(v8_str("obj"), templ->NewInstance());
3003
3004 const char* code =
3005 "try {"
3006 " obj[0] = 239;"
3007 " for (var i = 0; i < 100; i++) {"
3008 " var v = obj[0];"
3009 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;"
3010 " }"
3011 " 'PASSED'"
3012 "} catch(e) {"
3013 " e"
3014 "}";
3015 ExpectString(code, "PASSED");
3016}
3017
3018
Andrei Popescu402d9372010-02-26 13:31:12 +00003019THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
3020 v8::HandleScope scope;
3021 Local<ObjectTemplate> templ = ObjectTemplate::New();
3022 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3023
3024 LocalContext context;
3025 Local<v8::Object> obj = templ->NewInstance();
3026 obj->TurnOnAccessCheck();
3027 context->Global()->Set(v8_str("obj"), obj);
3028
3029 const char* code =
3030 "try {"
3031 " for (var i = 0; i < 100; i++) {"
3032 " var v = obj[0];"
3033 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
3034 " }"
3035 " 'PASSED'"
3036 "} catch(e) {"
3037 " e"
3038 "}";
3039 ExpectString(code, "PASSED");
3040}
3041
3042
3043THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
3044 i::FLAG_allow_natives_syntax = true;
3045 v8::HandleScope scope;
3046 Local<ObjectTemplate> templ = ObjectTemplate::New();
3047 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3048
3049 LocalContext context;
3050 Local<v8::Object> obj = templ->NewInstance();
3051 context->Global()->Set(v8_str("obj"), obj);
3052
3053 const char* code =
3054 "try {"
3055 " for (var i = 0; i < 100; i++) {"
3056 " var expected = i;"
3057 " if (i == 5) {"
3058 " %EnableAccessChecks(obj);"
3059 " expected = undefined;"
3060 " }"
3061 " var v = obj[i];"
3062 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3063 " if (i == 5) %DisableAccessChecks(obj);"
3064 " }"
3065 " 'PASSED'"
3066 "} catch(e) {"
3067 " e"
3068 "}";
3069 ExpectString(code, "PASSED");
3070}
3071
3072
3073THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
3074 v8::HandleScope scope;
3075 Local<ObjectTemplate> templ = ObjectTemplate::New();
3076 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3077
3078 LocalContext context;
3079 Local<v8::Object> obj = templ->NewInstance();
3080 context->Global()->Set(v8_str("obj"), obj);
3081
3082 const char* code =
3083 "try {"
3084 " for (var i = 0; i < 100; i++) {"
3085 " var v = obj[i];"
3086 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3087 " }"
3088 " 'PASSED'"
3089 "} catch(e) {"
3090 " e"
3091 "}";
3092 ExpectString(code, "PASSED");
3093}
3094
3095
3096THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
3097 v8::HandleScope scope;
3098 Local<ObjectTemplate> templ = ObjectTemplate::New();
3099 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3100
3101 LocalContext context;
3102 Local<v8::Object> obj = templ->NewInstance();
3103 context->Global()->Set(v8_str("obj"), obj);
3104
3105 const char* code =
3106 "try {"
3107 " for (var i = 0; i < 100; i++) {"
3108 " var expected = i;"
3109 " if (i == 50) {"
3110 " i = 'foobar';"
3111 " expected = undefined;"
3112 " }"
3113 " var v = obj[i];"
3114 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3115 " }"
3116 " 'PASSED'"
3117 "} catch(e) {"
3118 " e"
3119 "}";
3120 ExpectString(code, "PASSED");
3121}
3122
3123
3124THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
3125 v8::HandleScope scope;
3126 Local<ObjectTemplate> templ = ObjectTemplate::New();
3127 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3128
3129 LocalContext context;
3130 Local<v8::Object> obj = templ->NewInstance();
3131 context->Global()->Set(v8_str("obj"), obj);
3132
3133 const char* code =
3134 "var original = obj;"
3135 "try {"
3136 " for (var i = 0; i < 100; i++) {"
3137 " var expected = i;"
3138 " if (i == 50) {"
3139 " obj = {50: 'foobar'};"
3140 " expected = 'foobar';"
3141 " }"
3142 " var v = obj[i];"
3143 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3144 " if (i == 50) obj = original;"
3145 " }"
3146 " 'PASSED'"
3147 "} catch(e) {"
3148 " e"
3149 "}";
3150 ExpectString(code, "PASSED");
3151}
3152
3153
3154THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
3155 v8::HandleScope scope;
3156 Local<ObjectTemplate> templ = ObjectTemplate::New();
3157 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3158
3159 LocalContext context;
3160 Local<v8::Object> obj = templ->NewInstance();
3161 context->Global()->Set(v8_str("obj"), obj);
3162
3163 const char* code =
3164 "var original = obj;"
3165 "try {"
3166 " for (var i = 0; i < 100; i++) {"
3167 " var expected = i;"
3168 " if (i == 5) {"
3169 " obj = 239;"
3170 " expected = undefined;"
3171 " }"
3172 " var v = obj[i];"
3173 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
3174 " if (i == 5) obj = original;"
3175 " }"
3176 " 'PASSED'"
3177 "} catch(e) {"
3178 " e"
3179 "}";
3180 ExpectString(code, "PASSED");
3181}
3182
3183
3184THREADED_TEST(IndexedInterceptorOnProto) {
3185 v8::HandleScope scope;
3186 Local<ObjectTemplate> templ = ObjectTemplate::New();
3187 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
3188
3189 LocalContext context;
3190 Local<v8::Object> obj = templ->NewInstance();
3191 context->Global()->Set(v8_str("obj"), obj);
3192
3193 const char* code =
3194 "var o = {__proto__: obj};"
3195 "try {"
3196 " for (var i = 0; i < 100; i++) {"
3197 " var v = o[i];"
3198 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
3199 " }"
3200 " 'PASSED'"
3201 "} catch(e) {"
3202 " e"
3203 "}";
3204 ExpectString(code, "PASSED");
3205}
3206
3207
Steve Blocka7e24c12009-10-30 11:49:00 +00003208THREADED_TEST(MultiContexts) {
3209 v8::HandleScope scope;
3210 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
3211 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler));
3212
3213 Local<String> password = v8_str("Password");
3214
3215 // Create an environment
3216 LocalContext context0(0, templ);
3217 context0->SetSecurityToken(password);
3218 v8::Handle<v8::Object> global0 = context0->Global();
3219 global0->Set(v8_str("custom"), v8_num(1234));
3220 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3221
3222 // Create an independent environment
3223 LocalContext context1(0, templ);
3224 context1->SetSecurityToken(password);
3225 v8::Handle<v8::Object> global1 = context1->Global();
3226 global1->Set(v8_str("custom"), v8_num(1234));
3227 CHECK_NE(global0, global1);
3228 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value());
3229 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value());
3230
3231 // Now create a new context with the old global
3232 LocalContext context2(0, templ, global1);
3233 context2->SetSecurityToken(password);
3234 v8::Handle<v8::Object> global2 = context2->Global();
3235 CHECK_EQ(global1, global2);
3236 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value());
3237 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value());
3238}
3239
3240
3241THREADED_TEST(FunctionPrototypeAcrossContexts) {
3242 // Make sure that functions created by cloning boilerplates cannot
3243 // communicate through their __proto__ field.
3244
3245 v8::HandleScope scope;
3246
3247 LocalContext env0;
3248 v8::Handle<v8::Object> global0 =
3249 env0->Global();
3250 v8::Handle<v8::Object> object0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003251 global0->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003252 v8::Handle<v8::Object> tostring0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003253 object0->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003254 v8::Handle<v8::Object> proto0 =
Steve Block6ded16b2010-05-10 14:33:55 +01003255 tostring0->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003256 proto0->Set(v8_str("custom"), v8_num(1234));
3257
3258 LocalContext env1;
3259 v8::Handle<v8::Object> global1 =
3260 env1->Global();
3261 v8::Handle<v8::Object> object1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003262 global1->Get(v8_str("Object")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003263 v8::Handle<v8::Object> tostring1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003264 object1->Get(v8_str("toString")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003265 v8::Handle<v8::Object> proto1 =
Steve Block6ded16b2010-05-10 14:33:55 +01003266 tostring1->Get(v8_str("__proto__")).As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003267 CHECK(!proto1->Has(v8_str("custom")));
3268}
3269
3270
3271THREADED_TEST(Regress892105) {
3272 // Make sure that object and array literals created by cloning
3273 // boilerplates cannot communicate through their __proto__
3274 // field. This is rather difficult to check, but we try to add stuff
3275 // to Object.prototype and Array.prototype and create a new
3276 // environment. This should succeed.
3277
3278 v8::HandleScope scope;
3279
3280 Local<String> source = v8_str("Object.prototype.obj = 1234;"
3281 "Array.prototype.arr = 4567;"
3282 "8901");
3283
3284 LocalContext env0;
3285 Local<Script> script0 = Script::Compile(source);
3286 CHECK_EQ(8901.0, script0->Run()->NumberValue());
3287
3288 LocalContext env1;
3289 Local<Script> script1 = Script::Compile(source);
3290 CHECK_EQ(8901.0, script1->Run()->NumberValue());
3291}
3292
3293
Steve Blocka7e24c12009-10-30 11:49:00 +00003294THREADED_TEST(UndetectableObject) {
3295 v8::HandleScope scope;
3296 LocalContext env;
3297
3298 Local<v8::FunctionTemplate> desc =
3299 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3300 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3301
3302 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3303 env->Global()->Set(v8_str("undetectable"), obj);
3304
3305 ExpectString("undetectable.toString()", "[object Object]");
3306 ExpectString("typeof undetectable", "undefined");
3307 ExpectString("typeof(undetectable)", "undefined");
3308 ExpectBoolean("typeof undetectable == 'undefined'", true);
3309 ExpectBoolean("typeof undetectable == 'object'", false);
3310 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3311 ExpectBoolean("!undetectable", true);
3312
3313 ExpectObject("true&&undetectable", obj);
3314 ExpectBoolean("false&&undetectable", false);
3315 ExpectBoolean("true||undetectable", true);
3316 ExpectObject("false||undetectable", obj);
3317
3318 ExpectObject("undetectable&&true", obj);
3319 ExpectObject("undetectable&&false", obj);
3320 ExpectBoolean("undetectable||true", true);
3321 ExpectBoolean("undetectable||false", false);
3322
3323 ExpectBoolean("undetectable==null", true);
3324 ExpectBoolean("null==undetectable", true);
3325 ExpectBoolean("undetectable==undefined", true);
3326 ExpectBoolean("undefined==undetectable", true);
3327 ExpectBoolean("undetectable==undetectable", true);
3328
3329
3330 ExpectBoolean("undetectable===null", false);
3331 ExpectBoolean("null===undetectable", false);
3332 ExpectBoolean("undetectable===undefined", false);
3333 ExpectBoolean("undefined===undetectable", false);
3334 ExpectBoolean("undetectable===undetectable", true);
3335}
3336
3337
Steve Block8defd9f2010-07-08 12:39:36 +01003338
3339THREADED_TEST(ExtensibleOnUndetectable) {
3340 v8::HandleScope scope;
3341 LocalContext env;
3342
3343 Local<v8::FunctionTemplate> desc =
3344 v8::FunctionTemplate::New(0, v8::Handle<Value>());
3345 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
3346
3347 Local<v8::Object> obj = desc->GetFunction()->NewInstance();
3348 env->Global()->Set(v8_str("undetectable"), obj);
3349
3350 Local<String> source = v8_str("undetectable.x = 42;"
3351 "undetectable.x");
3352
3353 Local<Script> script = Script::Compile(source);
3354
3355 CHECK_EQ(v8::Integer::New(42), script->Run());
3356
3357 ExpectBoolean("Object.isExtensible(undetectable)", true);
3358
3359 source = v8_str("Object.preventExtensions(undetectable);");
3360 script = Script::Compile(source);
3361 script->Run();
3362 ExpectBoolean("Object.isExtensible(undetectable)", false);
3363
3364 source = v8_str("undetectable.y = 2000;");
3365 script = Script::Compile(source);
3366 v8::TryCatch try_catch;
3367 Local<Value> result = script->Run();
3368 CHECK(result.IsEmpty());
3369 CHECK(try_catch.HasCaught());
3370}
3371
3372
3373
Steve Blocka7e24c12009-10-30 11:49:00 +00003374THREADED_TEST(UndetectableString) {
3375 v8::HandleScope scope;
3376 LocalContext env;
3377
3378 Local<String> obj = String::NewUndetectable("foo");
3379 env->Global()->Set(v8_str("undetectable"), obj);
3380
3381 ExpectString("undetectable", "foo");
3382 ExpectString("typeof undetectable", "undefined");
3383 ExpectString("typeof(undetectable)", "undefined");
3384 ExpectBoolean("typeof undetectable == 'undefined'", true);
3385 ExpectBoolean("typeof undetectable == 'string'", false);
3386 ExpectBoolean("if (undetectable) { true; } else { false; }", false);
3387 ExpectBoolean("!undetectable", true);
3388
3389 ExpectObject("true&&undetectable", obj);
3390 ExpectBoolean("false&&undetectable", false);
3391 ExpectBoolean("true||undetectable", true);
3392 ExpectObject("false||undetectable", obj);
3393
3394 ExpectObject("undetectable&&true", obj);
3395 ExpectObject("undetectable&&false", obj);
3396 ExpectBoolean("undetectable||true", true);
3397 ExpectBoolean("undetectable||false", false);
3398
3399 ExpectBoolean("undetectable==null", true);
3400 ExpectBoolean("null==undetectable", true);
3401 ExpectBoolean("undetectable==undefined", true);
3402 ExpectBoolean("undefined==undetectable", true);
3403 ExpectBoolean("undetectable==undetectable", true);
3404
3405
3406 ExpectBoolean("undetectable===null", false);
3407 ExpectBoolean("null===undetectable", false);
3408 ExpectBoolean("undetectable===undefined", false);
3409 ExpectBoolean("undefined===undetectable", false);
3410 ExpectBoolean("undetectable===undetectable", true);
3411}
3412
3413
3414template <typename T> static void USE(T) { }
3415
3416
3417// This test is not intended to be run, just type checked.
3418static void PersistentHandles() {
3419 USE(PersistentHandles);
3420 Local<String> str = v8_str("foo");
3421 v8::Persistent<String> p_str = v8::Persistent<String>::New(str);
3422 USE(p_str);
3423 Local<Script> scr = Script::Compile(v8_str(""));
3424 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr);
3425 USE(p_scr);
3426 Local<ObjectTemplate> templ = ObjectTemplate::New();
3427 v8::Persistent<ObjectTemplate> p_templ =
3428 v8::Persistent<ObjectTemplate>::New(templ);
3429 USE(p_templ);
3430}
3431
3432
3433static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) {
3434 ApiTestFuzzer::Fuzz();
3435 return v8::Undefined();
3436}
3437
3438
3439THREADED_TEST(GlobalObjectTemplate) {
3440 v8::HandleScope handle_scope;
3441 Local<ObjectTemplate> global_template = ObjectTemplate::New();
3442 global_template->Set(v8_str("JSNI_Log"),
3443 v8::FunctionTemplate::New(HandleLogDelegator));
3444 v8::Persistent<Context> context = Context::New(0, global_template);
3445 Context::Scope context_scope(context);
3446 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run();
3447 context.Dispose();
3448}
3449
3450
3451static const char* kSimpleExtensionSource =
3452 "function Foo() {"
3453 " return 4;"
3454 "}";
3455
3456
3457THREADED_TEST(SimpleExtensions) {
3458 v8::HandleScope handle_scope;
3459 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource));
3460 const char* extension_names[] = { "simpletest" };
3461 v8::ExtensionConfiguration extensions(1, extension_names);
3462 v8::Handle<Context> context = Context::New(&extensions);
3463 Context::Scope lock(context);
3464 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3465 CHECK_EQ(result, v8::Integer::New(4));
3466}
3467
3468
3469static const char* kEvalExtensionSource1 =
3470 "function UseEval1() {"
3471 " var x = 42;"
3472 " return eval('x');"
3473 "}";
3474
3475
3476static const char* kEvalExtensionSource2 =
3477 "(function() {"
3478 " var x = 42;"
3479 " function e() {"
3480 " return eval('x');"
3481 " }"
3482 " this.UseEval2 = e;"
3483 "})()";
3484
3485
3486THREADED_TEST(UseEvalFromExtension) {
3487 v8::HandleScope handle_scope;
3488 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1));
3489 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2));
3490 const char* extension_names[] = { "evaltest1", "evaltest2" };
3491 v8::ExtensionConfiguration extensions(2, extension_names);
3492 v8::Handle<Context> context = Context::New(&extensions);
3493 Context::Scope lock(context);
3494 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run();
3495 CHECK_EQ(result, v8::Integer::New(42));
3496 result = Script::Compile(v8_str("UseEval2()"))->Run();
3497 CHECK_EQ(result, v8::Integer::New(42));
3498}
3499
3500
3501static const char* kWithExtensionSource1 =
3502 "function UseWith1() {"
3503 " var x = 42;"
3504 " with({x:87}) { return x; }"
3505 "}";
3506
3507
3508
3509static const char* kWithExtensionSource2 =
3510 "(function() {"
3511 " var x = 42;"
3512 " function e() {"
3513 " with ({x:87}) { return x; }"
3514 " }"
3515 " this.UseWith2 = e;"
3516 "})()";
3517
3518
3519THREADED_TEST(UseWithFromExtension) {
3520 v8::HandleScope handle_scope;
3521 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1));
3522 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2));
3523 const char* extension_names[] = { "withtest1", "withtest2" };
3524 v8::ExtensionConfiguration extensions(2, extension_names);
3525 v8::Handle<Context> context = Context::New(&extensions);
3526 Context::Scope lock(context);
3527 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run();
3528 CHECK_EQ(result, v8::Integer::New(87));
3529 result = Script::Compile(v8_str("UseWith2()"))->Run();
3530 CHECK_EQ(result, v8::Integer::New(87));
3531}
3532
3533
3534THREADED_TEST(AutoExtensions) {
3535 v8::HandleScope handle_scope;
3536 Extension* extension = new Extension("autotest", kSimpleExtensionSource);
3537 extension->set_auto_enable(true);
3538 v8::RegisterExtension(extension);
3539 v8::Handle<Context> context = Context::New();
3540 Context::Scope lock(context);
3541 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run();
3542 CHECK_EQ(result, v8::Integer::New(4));
3543}
3544
3545
Steve Blockd0582a62009-12-15 09:54:21 +00003546static const char* kSyntaxErrorInExtensionSource =
3547 "[";
3548
3549
3550// Test that a syntax error in an extension does not cause a fatal
3551// error but results in an empty context.
3552THREADED_TEST(SyntaxErrorExtensions) {
3553 v8::HandleScope handle_scope;
3554 v8::RegisterExtension(new Extension("syntaxerror",
3555 kSyntaxErrorInExtensionSource));
3556 const char* extension_names[] = { "syntaxerror" };
3557 v8::ExtensionConfiguration extensions(1, extension_names);
3558 v8::Handle<Context> context = Context::New(&extensions);
3559 CHECK(context.IsEmpty());
3560}
3561
3562
3563static const char* kExceptionInExtensionSource =
3564 "throw 42";
3565
3566
3567// Test that an exception when installing an extension does not cause
3568// a fatal error but results in an empty context.
3569THREADED_TEST(ExceptionExtensions) {
3570 v8::HandleScope handle_scope;
3571 v8::RegisterExtension(new Extension("exception",
3572 kExceptionInExtensionSource));
3573 const char* extension_names[] = { "exception" };
3574 v8::ExtensionConfiguration extensions(1, extension_names);
3575 v8::Handle<Context> context = Context::New(&extensions);
3576 CHECK(context.IsEmpty());
3577}
3578
3579
Steve Blocka7e24c12009-10-30 11:49:00 +00003580static void CheckDependencies(const char* name, const char* expected) {
3581 v8::HandleScope handle_scope;
3582 v8::ExtensionConfiguration config(1, &name);
3583 LocalContext context(&config);
3584 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded")));
3585}
3586
3587
3588/*
3589 * Configuration:
3590 *
3591 * /-- B <--\
3592 * A <- -- D <-- E
3593 * \-- C <--/
3594 */
3595THREADED_TEST(ExtensionDependency) {
3596 static const char* kEDeps[] = { "D" };
3597 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps));
3598 static const char* kDDeps[] = { "B", "C" };
3599 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps));
3600 static const char* kBCDeps[] = { "A" };
3601 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps));
3602 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps));
3603 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';"));
3604 CheckDependencies("A", "undefinedA");
3605 CheckDependencies("B", "undefinedAB");
3606 CheckDependencies("C", "undefinedAC");
3607 CheckDependencies("D", "undefinedABCD");
3608 CheckDependencies("E", "undefinedABCDE");
3609 v8::HandleScope handle_scope;
3610 static const char* exts[2] = { "C", "E" };
3611 v8::ExtensionConfiguration config(2, exts);
3612 LocalContext context(&config);
3613 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded")));
3614}
3615
3616
3617static const char* kExtensionTestScript =
3618 "native function A();"
3619 "native function B();"
3620 "native function C();"
3621 "function Foo(i) {"
3622 " if (i == 0) return A();"
3623 " if (i == 1) return B();"
3624 " if (i == 2) return C();"
3625 "}";
3626
3627
3628static v8::Handle<Value> CallFun(const v8::Arguments& args) {
3629 ApiTestFuzzer::Fuzz();
Leon Clarkee46be812010-01-19 14:06:41 +00003630 if (args.IsConstructCall()) {
3631 args.This()->Set(v8_str("data"), args.Data());
3632 return v8::Null();
3633 }
Steve Blocka7e24c12009-10-30 11:49:00 +00003634 return args.Data();
3635}
3636
3637
3638class FunctionExtension : public Extension {
3639 public:
3640 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { }
3641 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
3642 v8::Handle<String> name);
3643};
3644
3645
3646static int lookup_count = 0;
3647v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction(
3648 v8::Handle<String> name) {
3649 lookup_count++;
3650 if (name->Equals(v8_str("A"))) {
3651 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8));
3652 } else if (name->Equals(v8_str("B"))) {
3653 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7));
3654 } else if (name->Equals(v8_str("C"))) {
3655 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6));
3656 } else {
3657 return v8::Handle<v8::FunctionTemplate>();
3658 }
3659}
3660
3661
3662THREADED_TEST(FunctionLookup) {
3663 v8::RegisterExtension(new FunctionExtension());
3664 v8::HandleScope handle_scope;
3665 static const char* exts[1] = { "functiontest" };
3666 v8::ExtensionConfiguration config(1, exts);
3667 LocalContext context(&config);
3668 CHECK_EQ(3, lookup_count);
3669 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run());
3670 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run());
3671 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run());
3672}
3673
3674
Leon Clarkee46be812010-01-19 14:06:41 +00003675THREADED_TEST(NativeFunctionConstructCall) {
3676 v8::RegisterExtension(new FunctionExtension());
3677 v8::HandleScope handle_scope;
3678 static const char* exts[1] = { "functiontest" };
3679 v8::ExtensionConfiguration config(1, exts);
3680 LocalContext context(&config);
Leon Clarked91b9f72010-01-27 17:25:45 +00003681 for (int i = 0; i < 10; i++) {
3682 // Run a few times to ensure that allocation of objects doesn't
3683 // change behavior of a constructor function.
3684 CHECK_EQ(v8::Integer::New(8),
3685 Script::Compile(v8_str("(new A()).data"))->Run());
3686 CHECK_EQ(v8::Integer::New(7),
3687 Script::Compile(v8_str("(new B()).data"))->Run());
3688 CHECK_EQ(v8::Integer::New(6),
3689 Script::Compile(v8_str("(new C()).data"))->Run());
3690 }
Leon Clarkee46be812010-01-19 14:06:41 +00003691}
3692
3693
Steve Blocka7e24c12009-10-30 11:49:00 +00003694static const char* last_location;
3695static const char* last_message;
3696void StoringErrorCallback(const char* location, const char* message) {
3697 if (last_location == NULL) {
3698 last_location = location;
3699 last_message = message;
3700 }
3701}
3702
3703
3704// ErrorReporting creates a circular extensions configuration and
3705// tests that the fatal error handler gets called. This renders V8
3706// unusable and therefore this test cannot be run in parallel.
3707TEST(ErrorReporting) {
3708 v8::V8::SetFatalErrorHandler(StoringErrorCallback);
3709 static const char* aDeps[] = { "B" };
3710 v8::RegisterExtension(new Extension("A", "", 1, aDeps));
3711 static const char* bDeps[] = { "A" };
3712 v8::RegisterExtension(new Extension("B", "", 1, bDeps));
3713 last_location = NULL;
3714 v8::ExtensionConfiguration config(1, bDeps);
3715 v8::Handle<Context> context = Context::New(&config);
3716 CHECK(context.IsEmpty());
3717 CHECK_NE(last_location, NULL);
3718}
3719
3720
3721static const char* js_code_causing_huge_string_flattening =
3722 "var str = 'X';"
3723 "for (var i = 0; i < 30; i++) {"
3724 " str = str + str;"
3725 "}"
3726 "str.match(/X/);";
3727
3728
3729void OOMCallback(const char* location, const char* message) {
3730 exit(0);
3731}
3732
3733
3734TEST(RegexpOutOfMemory) {
3735 // Execute a script that causes out of memory when flattening a string.
3736 v8::HandleScope scope;
3737 v8::V8::SetFatalErrorHandler(OOMCallback);
3738 LocalContext context;
3739 Local<Script> script =
3740 Script::Compile(String::New(js_code_causing_huge_string_flattening));
3741 last_location = NULL;
3742 Local<Value> result = script->Run();
3743
3744 CHECK(false); // Should not return.
3745}
3746
3747
3748static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message,
3749 v8::Handle<Value> data) {
3750 CHECK_EQ(v8::Undefined(), data);
3751 CHECK(message->GetScriptResourceName()->IsUndefined());
3752 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName());
3753 message->GetLineNumber();
3754 message->GetSourceLine();
3755}
3756
3757
3758THREADED_TEST(ErrorWithMissingScriptInfo) {
3759 v8::HandleScope scope;
3760 LocalContext context;
3761 v8::V8::AddMessageListener(MissingScriptInfoMessageListener);
3762 Script::Compile(v8_str("throw Error()"))->Run();
3763 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener);
3764}
3765
3766
3767int global_index = 0;
3768
3769class Snorkel {
3770 public:
3771 Snorkel() { index_ = global_index++; }
3772 int index_;
3773};
3774
3775class Whammy {
3776 public:
3777 Whammy() {
3778 cursor_ = 0;
3779 }
3780 ~Whammy() {
3781 script_.Dispose();
3782 }
3783 v8::Handle<Script> getScript() {
3784 if (script_.IsEmpty())
3785 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo"));
3786 return Local<Script>(*script_);
3787 }
3788
3789 public:
3790 static const int kObjectCount = 256;
3791 int cursor_;
3792 v8::Persistent<v8::Object> objects_[kObjectCount];
3793 v8::Persistent<Script> script_;
3794};
3795
3796static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) {
3797 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data);
3798 delete snorkel;
3799 obj.ClearWeak();
3800}
3801
3802v8::Handle<Value> WhammyPropertyGetter(Local<String> name,
3803 const AccessorInfo& info) {
3804 Whammy* whammy =
3805 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
3806
3807 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_];
3808
3809 v8::Handle<v8::Object> obj = v8::Object::New();
3810 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj);
3811 if (!prev.IsEmpty()) {
3812 prev->Set(v8_str("next"), obj);
3813 prev.MakeWeak(new Snorkel(), &HandleWeakReference);
3814 whammy->objects_[whammy->cursor_].Clear();
3815 }
3816 whammy->objects_[whammy->cursor_] = global;
3817 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount;
3818 return whammy->getScript()->Run();
3819}
3820
3821THREADED_TEST(WeakReference) {
3822 v8::HandleScope handle_scope;
3823 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New();
3824 templ->SetNamedPropertyHandler(WhammyPropertyGetter,
3825 0, 0, 0, 0,
3826 v8::External::New(new Whammy()));
3827 const char* extension_list[] = { "v8/gc" };
3828 v8::ExtensionConfiguration extensions(1, extension_list);
3829 v8::Persistent<Context> context = Context::New(&extensions);
3830 Context::Scope context_scope(context);
3831
3832 v8::Handle<v8::Object> interceptor = templ->NewInstance();
3833 context->Global()->Set(v8_str("whammy"), interceptor);
3834 const char* code =
3835 "var last;"
3836 "for (var i = 0; i < 10000; i++) {"
3837 " var obj = whammy.length;"
3838 " if (last) last.next = obj;"
3839 " last = obj;"
3840 "}"
3841 "gc();"
3842 "4";
3843 v8::Handle<Value> result = CompileRun(code);
3844 CHECK_EQ(4.0, result->NumberValue());
3845
3846 context.Dispose();
3847}
3848
3849
Steve Blockd0582a62009-12-15 09:54:21 +00003850static bool in_scavenge = false;
3851static int last = -1;
3852
3853static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) {
3854 CHECK_EQ(-1, last);
3855 last = 0;
3856 obj.Dispose();
3857 obj.Clear();
3858 in_scavenge = true;
3859 i::Heap::PerformScavenge();
3860 in_scavenge = false;
3861 *(reinterpret_cast<bool*>(data)) = true;
3862}
3863
3864static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj,
3865 void* data) {
3866 CHECK_EQ(0, last);
3867 last = 1;
3868 *(reinterpret_cast<bool*>(data)) = in_scavenge;
3869 obj.Dispose();
3870 obj.Clear();
3871}
3872
3873THREADED_TEST(NoWeakRefCallbacksInScavenge) {
3874 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks.
3875 // Calling callbacks from scavenges is unsafe as objects held by those
3876 // handlers might have become strongly reachable, but scavenge doesn't
3877 // check that.
3878 v8::Persistent<Context> context = Context::New();
3879 Context::Scope context_scope(context);
3880
3881 v8::Persistent<v8::Object> object_a;
3882 v8::Persistent<v8::Object> object_b;
3883
3884 {
3885 v8::HandleScope handle_scope;
3886 object_b = v8::Persistent<v8::Object>::New(v8::Object::New());
3887 object_a = v8::Persistent<v8::Object>::New(v8::Object::New());
3888 }
3889
3890 bool object_a_disposed = false;
3891 object_a.MakeWeak(&object_a_disposed, &ForceScavenge);
3892 bool released_in_scavenge = false;
3893 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge);
3894
3895 while (!object_a_disposed) {
3896 i::Heap::CollectAllGarbage(false);
3897 }
3898 CHECK(!released_in_scavenge);
3899}
3900
3901
Steve Blocka7e24c12009-10-30 11:49:00 +00003902v8::Handle<Function> args_fun;
3903
3904
3905static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) {
3906 ApiTestFuzzer::Fuzz();
3907 CHECK_EQ(args_fun, args.Callee());
3908 CHECK_EQ(3, args.Length());
3909 CHECK_EQ(v8::Integer::New(1), args[0]);
3910 CHECK_EQ(v8::Integer::New(2), args[1]);
3911 CHECK_EQ(v8::Integer::New(3), args[2]);
3912 CHECK_EQ(v8::Undefined(), args[3]);
3913 v8::HandleScope scope;
3914 i::Heap::CollectAllGarbage(false);
3915 return v8::Undefined();
3916}
3917
3918
3919THREADED_TEST(Arguments) {
3920 v8::HandleScope scope;
3921 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New();
3922 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback));
3923 LocalContext context(NULL, global);
Steve Block6ded16b2010-05-10 14:33:55 +01003924 args_fun = context->Global()->Get(v8_str("f")).As<Function>();
Steve Blocka7e24c12009-10-30 11:49:00 +00003925 v8_compile("f(1, 2, 3)")->Run();
3926}
3927
3928
Steve Blocka7e24c12009-10-30 11:49:00 +00003929static v8::Handle<Value> NoBlockGetterX(Local<String> name,
3930 const AccessorInfo&) {
3931 return v8::Handle<Value>();
3932}
3933
3934
3935static v8::Handle<Value> NoBlockGetterI(uint32_t index,
3936 const AccessorInfo&) {
3937 return v8::Handle<Value>();
3938}
3939
3940
3941static v8::Handle<v8::Boolean> PDeleter(Local<String> name,
3942 const AccessorInfo&) {
3943 if (!name->Equals(v8_str("foo"))) {
3944 return v8::Handle<v8::Boolean>(); // not intercepted
3945 }
3946
3947 return v8::False(); // intercepted, and don't delete the property
3948}
3949
3950
3951static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) {
3952 if (index != 2) {
3953 return v8::Handle<v8::Boolean>(); // not intercepted
3954 }
3955
3956 return v8::False(); // intercepted, and don't delete the property
3957}
3958
3959
3960THREADED_TEST(Deleter) {
3961 v8::HandleScope scope;
3962 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
3963 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
3964 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
3965 LocalContext context;
3966 context->Global()->Set(v8_str("k"), obj->NewInstance());
3967 CompileRun(
3968 "k.foo = 'foo';"
3969 "k.bar = 'bar';"
3970 "k[2] = 2;"
3971 "k[4] = 4;");
3972 CHECK(v8_compile("delete k.foo")->Run()->IsFalse());
3973 CHECK(v8_compile("delete k.bar")->Run()->IsTrue());
3974
3975 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo"));
3976 CHECK(v8_compile("k.bar")->Run()->IsUndefined());
3977
3978 CHECK(v8_compile("delete k[2]")->Run()->IsFalse());
3979 CHECK(v8_compile("delete k[4]")->Run()->IsTrue());
3980
3981 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2));
3982 CHECK(v8_compile("k[4]")->Run()->IsUndefined());
3983}
3984
3985
3986static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
3987 ApiTestFuzzer::Fuzz();
3988 if (name->Equals(v8_str("foo")) ||
3989 name->Equals(v8_str("bar")) ||
3990 name->Equals(v8_str("baz"))) {
3991 return v8::Undefined();
3992 }
3993 return v8::Handle<Value>();
3994}
3995
3996
3997static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
3998 ApiTestFuzzer::Fuzz();
3999 if (index == 0 || index == 1) return v8::Undefined();
4000 return v8::Handle<Value>();
4001}
4002
4003
4004static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) {
4005 ApiTestFuzzer::Fuzz();
4006 v8::Handle<v8::Array> result = v8::Array::New(3);
4007 result->Set(v8::Integer::New(0), v8_str("foo"));
4008 result->Set(v8::Integer::New(1), v8_str("bar"));
4009 result->Set(v8::Integer::New(2), v8_str("baz"));
4010 return result;
4011}
4012
4013
4014static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
4015 ApiTestFuzzer::Fuzz();
4016 v8::Handle<v8::Array> result = v8::Array::New(2);
4017 result->Set(v8::Integer::New(0), v8_str("0"));
4018 result->Set(v8::Integer::New(1), v8_str("1"));
4019 return result;
4020}
4021
4022
4023THREADED_TEST(Enumerators) {
4024 v8::HandleScope scope;
4025 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4026 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
4027 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
4028 LocalContext context;
4029 context->Global()->Set(v8_str("k"), obj->NewInstance());
4030 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
4031 "k[10] = 0;"
4032 "k.a = 0;"
4033 "k[5] = 0;"
4034 "k.b = 0;"
4035 "k[4294967295] = 0;"
4036 "k.c = 0;"
4037 "k[4294967296] = 0;"
4038 "k.d = 0;"
4039 "k[140000] = 0;"
4040 "k.e = 0;"
4041 "k[30000000000] = 0;"
4042 "k.f = 0;"
4043 "var result = [];"
4044 "for (var prop in k) {"
4045 " result.push(prop);"
4046 "}"
4047 "result"));
4048 // Check that we get all the property names returned including the
4049 // ones from the enumerators in the right order: indexed properties
4050 // in numerical order, indexed interceptor properties, named
4051 // properties in insertion order, named interceptor properties.
4052 // This order is not mandated by the spec, so this test is just
4053 // documenting our behavior.
4054 CHECK_EQ(17, result->Length());
4055 // Indexed properties in numerical order.
4056 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
4057 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
4058 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
4059 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
4060 // Indexed interceptor properties in the order they are returned
4061 // from the enumerator interceptor.
4062 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
4063 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
4064 // Named properties in insertion order.
4065 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
4066 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
4067 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
4068 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
4069 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
4070 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
4071 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
4072 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
4073 // Named interceptor properties.
4074 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
4075 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
4076 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
4077}
4078
4079
4080int p_getter_count;
4081int p_getter_count2;
4082
4083
4084static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) {
4085 ApiTestFuzzer::Fuzz();
4086 p_getter_count++;
4087 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4088 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4089 if (name->Equals(v8_str("p1"))) {
4090 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4091 } else if (name->Equals(v8_str("p2"))) {
4092 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4093 } else if (name->Equals(v8_str("p3"))) {
4094 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4095 } else if (name->Equals(v8_str("p4"))) {
4096 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4097 }
4098 return v8::Undefined();
4099}
4100
4101
4102static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
4103 ApiTestFuzzer::Fuzz();
4104 LocalContext context;
4105 context->Global()->Set(v8_str("o1"), obj->NewInstance());
4106 CompileRun(
4107 "o1.__proto__ = { };"
4108 "var o2 = { __proto__: o1 };"
4109 "var o3 = { __proto__: o2 };"
4110 "var o4 = { __proto__: o3 };"
4111 "for (var i = 0; i < 10; i++) o4.p4;"
4112 "for (var i = 0; i < 10; i++) o3.p3;"
4113 "for (var i = 0; i < 10; i++) o2.p2;"
4114 "for (var i = 0; i < 10; i++) o1.p1;");
4115}
4116
4117
4118static v8::Handle<Value> PGetter2(Local<String> name,
4119 const AccessorInfo& info) {
4120 ApiTestFuzzer::Fuzz();
4121 p_getter_count2++;
4122 v8::Handle<v8::Object> global = Context::GetCurrent()->Global();
4123 CHECK_EQ(info.Holder(), global->Get(v8_str("o1")));
4124 if (name->Equals(v8_str("p1"))) {
4125 CHECK_EQ(info.This(), global->Get(v8_str("o1")));
4126 } else if (name->Equals(v8_str("p2"))) {
4127 CHECK_EQ(info.This(), global->Get(v8_str("o2")));
4128 } else if (name->Equals(v8_str("p3"))) {
4129 CHECK_EQ(info.This(), global->Get(v8_str("o3")));
4130 } else if (name->Equals(v8_str("p4"))) {
4131 CHECK_EQ(info.This(), global->Get(v8_str("o4")));
4132 }
4133 return v8::Undefined();
4134}
4135
4136
4137THREADED_TEST(GetterHolders) {
4138 v8::HandleScope scope;
4139 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4140 obj->SetAccessor(v8_str("p1"), PGetter);
4141 obj->SetAccessor(v8_str("p2"), PGetter);
4142 obj->SetAccessor(v8_str("p3"), PGetter);
4143 obj->SetAccessor(v8_str("p4"), PGetter);
4144 p_getter_count = 0;
4145 RunHolderTest(obj);
4146 CHECK_EQ(40, p_getter_count);
4147}
4148
4149
4150THREADED_TEST(PreInterceptorHolders) {
4151 v8::HandleScope scope;
4152 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4153 obj->SetNamedPropertyHandler(PGetter2);
4154 p_getter_count2 = 0;
4155 RunHolderTest(obj);
4156 CHECK_EQ(40, p_getter_count2);
4157}
4158
4159
4160THREADED_TEST(ObjectInstantiation) {
4161 v8::HandleScope scope;
4162 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
4163 templ->SetAccessor(v8_str("t"), PGetter2);
4164 LocalContext context;
4165 context->Global()->Set(v8_str("o"), templ->NewInstance());
4166 for (int i = 0; i < 100; i++) {
4167 v8::HandleScope inner_scope;
4168 v8::Handle<v8::Object> obj = templ->NewInstance();
4169 CHECK_NE(obj, context->Global()->Get(v8_str("o")));
4170 context->Global()->Set(v8_str("o2"), obj);
4171 v8::Handle<Value> value =
4172 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run();
4173 CHECK_EQ(v8::True(), value);
4174 context->Global()->Set(v8_str("o"), obj);
4175 }
4176}
4177
4178
4179THREADED_TEST(StringWrite) {
4180 v8::HandleScope scope;
4181 v8::Handle<String> str = v8_str("abcde");
4182
4183 char buf[100];
4184 int len;
4185
4186 memset(buf, 0x1, sizeof(buf));
4187 len = str->WriteAscii(buf);
4188 CHECK_EQ(len, 5);
4189 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4190
4191 memset(buf, 0x1, sizeof(buf));
4192 len = str->WriteAscii(buf, 0, 4);
4193 CHECK_EQ(len, 4);
4194 CHECK_EQ(strncmp("abcd\1", buf, 5), 0);
4195
4196 memset(buf, 0x1, sizeof(buf));
4197 len = str->WriteAscii(buf, 0, 5);
4198 CHECK_EQ(len, 5);
4199 CHECK_EQ(strncmp("abcde\1", buf, 6), 0);
4200
4201 memset(buf, 0x1, sizeof(buf));
4202 len = str->WriteAscii(buf, 0, 6);
4203 CHECK_EQ(len, 5);
4204 CHECK_EQ(strncmp("abcde\0", buf, 6), 0);
4205
4206 memset(buf, 0x1, sizeof(buf));
4207 len = str->WriteAscii(buf, 4, -1);
4208 CHECK_EQ(len, 1);
4209 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4210
4211 memset(buf, 0x1, sizeof(buf));
4212 len = str->WriteAscii(buf, 4, 6);
4213 CHECK_EQ(len, 1);
4214 CHECK_EQ(strncmp("e\0", buf, 2), 0);
4215
4216 memset(buf, 0x1, sizeof(buf));
4217 len = str->WriteAscii(buf, 4, 1);
4218 CHECK_EQ(len, 1);
4219 CHECK_EQ(strncmp("e\1", buf, 2), 0);
4220}
4221
4222
4223THREADED_TEST(ToArrayIndex) {
4224 v8::HandleScope scope;
4225 LocalContext context;
4226
4227 v8::Handle<String> str = v8_str("42");
4228 v8::Handle<v8::Uint32> index = str->ToArrayIndex();
4229 CHECK(!index.IsEmpty());
4230 CHECK_EQ(42.0, index->Uint32Value());
4231 str = v8_str("42asdf");
4232 index = str->ToArrayIndex();
4233 CHECK(index.IsEmpty());
4234 str = v8_str("-42");
4235 index = str->ToArrayIndex();
4236 CHECK(index.IsEmpty());
4237 str = v8_str("4294967295");
4238 index = str->ToArrayIndex();
4239 CHECK(!index.IsEmpty());
4240 CHECK_EQ(4294967295.0, index->Uint32Value());
4241 v8::Handle<v8::Number> num = v8::Number::New(1);
4242 index = num->ToArrayIndex();
4243 CHECK(!index.IsEmpty());
4244 CHECK_EQ(1.0, index->Uint32Value());
4245 num = v8::Number::New(-1);
4246 index = num->ToArrayIndex();
4247 CHECK(index.IsEmpty());
4248 v8::Handle<v8::Object> obj = v8::Object::New();
4249 index = obj->ToArrayIndex();
4250 CHECK(index.IsEmpty());
4251}
4252
4253
4254THREADED_TEST(ErrorConstruction) {
4255 v8::HandleScope scope;
4256 LocalContext context;
4257
4258 v8::Handle<String> foo = v8_str("foo");
4259 v8::Handle<String> message = v8_str("message");
4260 v8::Handle<Value> range_error = v8::Exception::RangeError(foo);
4261 CHECK(range_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004262 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>();
4263 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004264 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo);
4265 CHECK(reference_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004266 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004267 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo);
4268 CHECK(syntax_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004269 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004270 v8::Handle<Value> type_error = v8::Exception::TypeError(foo);
4271 CHECK(type_error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004272 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004273 v8::Handle<Value> error = v8::Exception::Error(foo);
4274 CHECK(error->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01004275 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
Steve Blocka7e24c12009-10-30 11:49:00 +00004276}
4277
4278
4279static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) {
4280 ApiTestFuzzer::Fuzz();
4281 return v8_num(10);
4282}
4283
4284
4285static void YSetter(Local<String> name,
4286 Local<Value> value,
4287 const AccessorInfo& info) {
4288 if (info.This()->Has(name)) {
4289 info.This()->Delete(name);
4290 }
4291 info.This()->Set(name, value);
4292}
4293
4294
4295THREADED_TEST(DeleteAccessor) {
4296 v8::HandleScope scope;
4297 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
4298 obj->SetAccessor(v8_str("y"), YGetter, YSetter);
4299 LocalContext context;
4300 v8::Handle<v8::Object> holder = obj->NewInstance();
4301 context->Global()->Set(v8_str("holder"), holder);
4302 v8::Handle<Value> result = CompileRun(
4303 "holder.y = 11; holder.y = 12; holder.y");
4304 CHECK_EQ(12, result->Uint32Value());
4305}
4306
4307
4308THREADED_TEST(TypeSwitch) {
4309 v8::HandleScope scope;
4310 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New();
4311 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New();
4312 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New();
4313 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 };
4314 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs);
4315 LocalContext context;
4316 v8::Handle<v8::Object> obj0 = v8::Object::New();
4317 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance();
4318 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance();
4319 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance();
4320 for (int i = 0; i < 10; i++) {
4321 CHECK_EQ(0, type_switch->match(obj0));
4322 CHECK_EQ(1, type_switch->match(obj1));
4323 CHECK_EQ(2, type_switch->match(obj2));
4324 CHECK_EQ(3, type_switch->match(obj3));
4325 CHECK_EQ(3, type_switch->match(obj3));
4326 CHECK_EQ(2, type_switch->match(obj2));
4327 CHECK_EQ(1, type_switch->match(obj1));
4328 CHECK_EQ(0, type_switch->match(obj0));
4329 }
4330}
4331
4332
4333// For use within the TestSecurityHandler() test.
4334static bool g_security_callback_result = false;
4335static bool NamedSecurityTestCallback(Local<v8::Object> global,
4336 Local<Value> name,
4337 v8::AccessType type,
4338 Local<Value> data) {
4339 // Always allow read access.
4340 if (type == v8::ACCESS_GET)
4341 return true;
4342
4343 // Sometimes allow other access.
4344 return g_security_callback_result;
4345}
4346
4347
4348static bool IndexedSecurityTestCallback(Local<v8::Object> global,
4349 uint32_t key,
4350 v8::AccessType type,
4351 Local<Value> data) {
4352 // Always allow read access.
4353 if (type == v8::ACCESS_GET)
4354 return true;
4355
4356 // Sometimes allow other access.
4357 return g_security_callback_result;
4358}
4359
4360
4361static int trouble_nesting = 0;
4362static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) {
4363 ApiTestFuzzer::Fuzz();
4364 trouble_nesting++;
4365
4366 // Call a JS function that throws an uncaught exception.
4367 Local<v8::Object> arg_this = Context::GetCurrent()->Global();
4368 Local<Value> trouble_callee = (trouble_nesting == 3) ?
4369 arg_this->Get(v8_str("trouble_callee")) :
4370 arg_this->Get(v8_str("trouble_caller"));
4371 CHECK(trouble_callee->IsFunction());
4372 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL);
4373}
4374
4375
4376static int report_count = 0;
4377static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>,
4378 v8::Handle<Value>) {
4379 report_count++;
4380}
4381
4382
4383// Counts uncaught exceptions, but other tests running in parallel
4384// also have uncaught exceptions.
4385TEST(ApiUncaughtException) {
4386 report_count = 0;
4387 v8::HandleScope scope;
4388 LocalContext env;
4389 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
4390
4391 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4392 v8::Local<v8::Object> global = env->Global();
4393 global->Set(v8_str("trouble"), fun->GetFunction());
4394
4395 Script::Compile(v8_str("function trouble_callee() {"
4396 " var x = null;"
4397 " return x.foo;"
4398 "};"
4399 "function trouble_caller() {"
4400 " trouble();"
4401 "};"))->Run();
4402 Local<Value> trouble = global->Get(v8_str("trouble"));
4403 CHECK(trouble->IsFunction());
4404 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee"));
4405 CHECK(trouble_callee->IsFunction());
4406 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller"));
4407 CHECK(trouble_caller->IsFunction());
4408 Function::Cast(*trouble_caller)->Call(global, 0, NULL);
4409 CHECK_EQ(1, report_count);
4410 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
4411}
4412
Leon Clarke4515c472010-02-03 11:58:03 +00004413static const char* script_resource_name = "ExceptionInNativeScript.js";
4414static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
4415 v8::Handle<Value>) {
4416 v8::Handle<v8::Value> name_val = message->GetScriptResourceName();
4417 CHECK(!name_val.IsEmpty() && name_val->IsString());
4418 v8::String::AsciiValue name(message->GetScriptResourceName());
4419 CHECK_EQ(script_resource_name, *name);
4420 CHECK_EQ(3, message->GetLineNumber());
4421 v8::String::AsciiValue source_line(message->GetSourceLine());
4422 CHECK_EQ(" new o.foo();", *source_line);
4423}
4424
4425TEST(ExceptionInNativeScript) {
4426 v8::HandleScope scope;
4427 LocalContext env;
4428 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener);
4429
4430 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback);
4431 v8::Local<v8::Object> global = env->Global();
4432 global->Set(v8_str("trouble"), fun->GetFunction());
4433
4434 Script::Compile(v8_str("function trouble() {\n"
4435 " var o = {};\n"
4436 " new o.foo();\n"
4437 "};"), v8::String::New(script_resource_name))->Run();
4438 Local<Value> trouble = global->Get(v8_str("trouble"));
4439 CHECK(trouble->IsFunction());
4440 Function::Cast(*trouble)->Call(global, 0, NULL);
4441 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener);
4442}
4443
Steve Blocka7e24c12009-10-30 11:49:00 +00004444
4445TEST(CompilationErrorUsingTryCatchHandler) {
4446 v8::HandleScope scope;
4447 LocalContext env;
4448 v8::TryCatch try_catch;
4449 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile."));
4450 CHECK_NE(NULL, *try_catch.Exception());
4451 CHECK(try_catch.HasCaught());
4452}
4453
4454
4455TEST(TryCatchFinallyUsingTryCatchHandler) {
4456 v8::HandleScope scope;
4457 LocalContext env;
4458 v8::TryCatch try_catch;
4459 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run();
4460 CHECK(!try_catch.HasCaught());
4461 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run();
4462 CHECK(try_catch.HasCaught());
4463 try_catch.Reset();
4464 Script::Compile(v8_str("(function() {"
4465 "try { throw ''; } finally { return; }"
4466 "})()"))->Run();
4467 CHECK(!try_catch.HasCaught());
4468 Script::Compile(v8_str("(function()"
4469 " { try { throw ''; } finally { throw 0; }"
4470 "})()"))->Run();
4471 CHECK(try_catch.HasCaught());
4472}
4473
4474
4475// SecurityHandler can't be run twice
4476TEST(SecurityHandler) {
4477 v8::HandleScope scope0;
4478 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4479 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback,
4480 IndexedSecurityTestCallback);
4481 // Create an environment
4482 v8::Persistent<Context> context0 =
4483 Context::New(NULL, global_template);
4484 context0->Enter();
4485
4486 v8::Handle<v8::Object> global0 = context0->Global();
4487 v8::Handle<Script> script0 = v8_compile("foo = 111");
4488 script0->Run();
4489 global0->Set(v8_str("0"), v8_num(999));
4490 v8::Handle<Value> foo0 = global0->Get(v8_str("foo"));
4491 CHECK_EQ(111, foo0->Int32Value());
4492 v8::Handle<Value> z0 = global0->Get(v8_str("0"));
4493 CHECK_EQ(999, z0->Int32Value());
4494
4495 // Create another environment, should fail security checks.
4496 v8::HandleScope scope1;
4497
4498 v8::Persistent<Context> context1 =
4499 Context::New(NULL, global_template);
4500 context1->Enter();
4501
4502 v8::Handle<v8::Object> global1 = context1->Global();
4503 global1->Set(v8_str("othercontext"), global0);
4504 // This set will fail the security check.
4505 v8::Handle<Script> script1 =
4506 v8_compile("othercontext.foo = 222; othercontext[0] = 888;");
4507 script1->Run();
4508 // This read will pass the security check.
4509 v8::Handle<Value> foo1 = global0->Get(v8_str("foo"));
4510 CHECK_EQ(111, foo1->Int32Value());
4511 // This read will pass the security check.
4512 v8::Handle<Value> z1 = global0->Get(v8_str("0"));
4513 CHECK_EQ(999, z1->Int32Value());
4514
4515 // Create another environment, should pass security checks.
4516 { g_security_callback_result = true; // allow security handler to pass.
4517 v8::HandleScope scope2;
4518 LocalContext context2;
4519 v8::Handle<v8::Object> global2 = context2->Global();
4520 global2->Set(v8_str("othercontext"), global0);
4521 v8::Handle<Script> script2 =
4522 v8_compile("othercontext.foo = 333; othercontext[0] = 888;");
4523 script2->Run();
4524 v8::Handle<Value> foo2 = global0->Get(v8_str("foo"));
4525 CHECK_EQ(333, foo2->Int32Value());
4526 v8::Handle<Value> z2 = global0->Get(v8_str("0"));
4527 CHECK_EQ(888, z2->Int32Value());
4528 }
4529
4530 context1->Exit();
4531 context1.Dispose();
4532
4533 context0->Exit();
4534 context0.Dispose();
4535}
4536
4537
4538THREADED_TEST(SecurityChecks) {
4539 v8::HandleScope handle_scope;
4540 LocalContext env1;
4541 v8::Persistent<Context> env2 = Context::New();
4542
4543 Local<Value> foo = v8_str("foo");
4544 Local<Value> bar = v8_str("bar");
4545
4546 // Set to the same domain.
4547 env1->SetSecurityToken(foo);
4548
4549 // Create a function in env1.
4550 Script::Compile(v8_str("spy=function(){return spy;}"))->Run();
4551 Local<Value> spy = env1->Global()->Get(v8_str("spy"));
4552 CHECK(spy->IsFunction());
4553
4554 // Create another function accessing global objects.
4555 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run();
4556 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2"));
4557 CHECK(spy2->IsFunction());
4558
4559 // Switch to env2 in the same domain and invoke spy on env2.
4560 {
4561 env2->SetSecurityToken(foo);
4562 // Enter env2
4563 Context::Scope scope_env2(env2);
4564 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL);
4565 CHECK(result->IsFunction());
4566 }
4567
4568 {
4569 env2->SetSecurityToken(bar);
4570 Context::Scope scope_env2(env2);
4571
4572 // Call cross_domain_call, it should throw an exception
4573 v8::TryCatch try_catch;
4574 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL);
4575 CHECK(try_catch.HasCaught());
4576 }
4577
4578 env2.Dispose();
4579}
4580
4581
4582// Regression test case for issue 1183439.
4583THREADED_TEST(SecurityChecksForPrototypeChain) {
4584 v8::HandleScope scope;
4585 LocalContext current;
4586 v8::Persistent<Context> other = Context::New();
4587
4588 // Change context to be able to get to the Object function in the
4589 // other context without hitting the security checks.
4590 v8::Local<Value> other_object;
4591 { Context::Scope scope(other);
4592 other_object = other->Global()->Get(v8_str("Object"));
4593 other->Global()->Set(v8_num(42), v8_num(87));
4594 }
4595
4596 current->Global()->Set(v8_str("other"), other->Global());
4597 CHECK(v8_compile("other")->Run()->Equals(other->Global()));
4598
4599 // Make sure the security check fails here and we get an undefined
4600 // result instead of getting the Object function. Repeat in a loop
4601 // to make sure to exercise the IC code.
4602 v8::Local<Script> access_other0 = v8_compile("other.Object");
4603 v8::Local<Script> access_other1 = v8_compile("other[42]");
4604 for (int i = 0; i < 5; i++) {
4605 CHECK(!access_other0->Run()->Equals(other_object));
4606 CHECK(access_other0->Run()->IsUndefined());
4607 CHECK(!access_other1->Run()->Equals(v8_num(87)));
4608 CHECK(access_other1->Run()->IsUndefined());
4609 }
4610
4611 // Create an object that has 'other' in its prototype chain and make
4612 // sure we cannot access the Object function indirectly through
4613 // that. Repeat in a loop to make sure to exercise the IC code.
4614 v8_compile("function F() { };"
4615 "F.prototype = other;"
4616 "var f = new F();")->Run();
4617 v8::Local<Script> access_f0 = v8_compile("f.Object");
4618 v8::Local<Script> access_f1 = v8_compile("f[42]");
4619 for (int j = 0; j < 5; j++) {
4620 CHECK(!access_f0->Run()->Equals(other_object));
4621 CHECK(access_f0->Run()->IsUndefined());
4622 CHECK(!access_f1->Run()->Equals(v8_num(87)));
4623 CHECK(access_f1->Run()->IsUndefined());
4624 }
4625
4626 // Now it gets hairy: Set the prototype for the other global object
4627 // to be the current global object. The prototype chain for 'f' now
4628 // goes through 'other' but ends up in the current global object.
4629 { Context::Scope scope(other);
4630 other->Global()->Set(v8_str("__proto__"), current->Global());
4631 }
4632 // Set a named and an index property on the current global
4633 // object. To force the lookup to go through the other global object,
4634 // the properties must not exist in the other global object.
4635 current->Global()->Set(v8_str("foo"), v8_num(100));
4636 current->Global()->Set(v8_num(99), v8_num(101));
4637 // Try to read the properties from f and make sure that the access
4638 // gets stopped by the security checks on the other global object.
4639 Local<Script> access_f2 = v8_compile("f.foo");
4640 Local<Script> access_f3 = v8_compile("f[99]");
4641 for (int k = 0; k < 5; k++) {
4642 CHECK(!access_f2->Run()->Equals(v8_num(100)));
4643 CHECK(access_f2->Run()->IsUndefined());
4644 CHECK(!access_f3->Run()->Equals(v8_num(101)));
4645 CHECK(access_f3->Run()->IsUndefined());
4646 }
4647 other.Dispose();
4648}
4649
4650
4651THREADED_TEST(CrossDomainDelete) {
4652 v8::HandleScope handle_scope;
4653 LocalContext env1;
4654 v8::Persistent<Context> env2 = Context::New();
4655
4656 Local<Value> foo = v8_str("foo");
4657 Local<Value> bar = v8_str("bar");
4658
4659 // Set to the same domain.
4660 env1->SetSecurityToken(foo);
4661 env2->SetSecurityToken(foo);
4662
4663 env1->Global()->Set(v8_str("prop"), v8_num(3));
4664 env2->Global()->Set(v8_str("env1"), env1->Global());
4665
4666 // Change env2 to a different domain and delete env1.prop.
4667 env2->SetSecurityToken(bar);
4668 {
4669 Context::Scope scope_env2(env2);
4670 Local<Value> result =
4671 Script::Compile(v8_str("delete env1.prop"))->Run();
4672 CHECK(result->IsFalse());
4673 }
4674
4675 // Check that env1.prop still exists.
4676 Local<Value> v = env1->Global()->Get(v8_str("prop"));
4677 CHECK(v->IsNumber());
4678 CHECK_EQ(3, v->Int32Value());
4679
4680 env2.Dispose();
4681}
4682
4683
4684THREADED_TEST(CrossDomainIsPropertyEnumerable) {
4685 v8::HandleScope handle_scope;
4686 LocalContext env1;
4687 v8::Persistent<Context> env2 = Context::New();
4688
4689 Local<Value> foo = v8_str("foo");
4690 Local<Value> bar = v8_str("bar");
4691
4692 // Set to the same domain.
4693 env1->SetSecurityToken(foo);
4694 env2->SetSecurityToken(foo);
4695
4696 env1->Global()->Set(v8_str("prop"), v8_num(3));
4697 env2->Global()->Set(v8_str("env1"), env1->Global());
4698
4699 // env1.prop is enumerable in env2.
4700 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')");
4701 {
4702 Context::Scope scope_env2(env2);
4703 Local<Value> result = Script::Compile(test)->Run();
4704 CHECK(result->IsTrue());
4705 }
4706
4707 // Change env2 to a different domain and test again.
4708 env2->SetSecurityToken(bar);
4709 {
4710 Context::Scope scope_env2(env2);
4711 Local<Value> result = Script::Compile(test)->Run();
4712 CHECK(result->IsFalse());
4713 }
4714
4715 env2.Dispose();
4716}
4717
4718
4719THREADED_TEST(CrossDomainForIn) {
4720 v8::HandleScope handle_scope;
4721 LocalContext env1;
4722 v8::Persistent<Context> env2 = Context::New();
4723
4724 Local<Value> foo = v8_str("foo");
4725 Local<Value> bar = v8_str("bar");
4726
4727 // Set to the same domain.
4728 env1->SetSecurityToken(foo);
4729 env2->SetSecurityToken(foo);
4730
4731 env1->Global()->Set(v8_str("prop"), v8_num(3));
4732 env2->Global()->Set(v8_str("env1"), env1->Global());
4733
4734 // Change env2 to a different domain and set env1's global object
4735 // as the __proto__ of an object in env2 and enumerate properties
4736 // in for-in. It shouldn't enumerate properties on env1's global
4737 // object.
4738 env2->SetSecurityToken(bar);
4739 {
4740 Context::Scope scope_env2(env2);
4741 Local<Value> result =
4742 CompileRun("(function(){var obj = {'__proto__':env1};"
4743 "for (var p in obj)"
4744 " if (p == 'prop') return false;"
4745 "return true;})()");
4746 CHECK(result->IsTrue());
4747 }
4748 env2.Dispose();
4749}
4750
4751
4752TEST(ContextDetachGlobal) {
4753 v8::HandleScope handle_scope;
4754 LocalContext env1;
4755 v8::Persistent<Context> env2 = Context::New();
4756
4757 Local<v8::Object> global1 = env1->Global();
4758
4759 Local<Value> foo = v8_str("foo");
4760
4761 // Set to the same domain.
4762 env1->SetSecurityToken(foo);
4763 env2->SetSecurityToken(foo);
4764
4765 // Enter env2
4766 env2->Enter();
4767
Andrei Popescu74b3c142010-03-29 12:03:09 +01004768 // Create a function in env2 and add a reference to it in env1.
Steve Blocka7e24c12009-10-30 11:49:00 +00004769 Local<v8::Object> global2 = env2->Global();
4770 global2->Set(v8_str("prop"), v8::Integer::New(1));
4771 CompileRun("function getProp() {return prop;}");
4772
4773 env1->Global()->Set(v8_str("getProp"),
4774 global2->Get(v8_str("getProp")));
4775
Andrei Popescu74b3c142010-03-29 12:03:09 +01004776 // Detach env2's global, and reuse the global object of env2
Steve Blocka7e24c12009-10-30 11:49:00 +00004777 env2->Exit();
4778 env2->DetachGlobal();
4779 // env2 has a new global object.
4780 CHECK(!env2->Global()->Equals(global2));
4781
4782 v8::Persistent<Context> env3 =
4783 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4784 env3->SetSecurityToken(v8_str("bar"));
4785 env3->Enter();
4786
4787 Local<v8::Object> global3 = env3->Global();
4788 CHECK_EQ(global2, global3);
4789 CHECK(global3->Get(v8_str("prop"))->IsUndefined());
4790 CHECK(global3->Get(v8_str("getProp"))->IsUndefined());
4791 global3->Set(v8_str("prop"), v8::Integer::New(-1));
4792 global3->Set(v8_str("prop2"), v8::Integer::New(2));
4793 env3->Exit();
4794
4795 // Call getProp in env1, and it should return the value 1
4796 {
4797 Local<Value> get_prop = global1->Get(v8_str("getProp"));
4798 CHECK(get_prop->IsFunction());
4799 v8::TryCatch try_catch;
4800 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL);
4801 CHECK(!try_catch.HasCaught());
4802 CHECK_EQ(1, r->Int32Value());
4803 }
4804
4805 // Check that env3 is not accessible from env1
4806 {
4807 Local<Value> r = global3->Get(v8_str("prop2"));
4808 CHECK(r->IsUndefined());
4809 }
4810
4811 env2.Dispose();
4812 env3.Dispose();
4813}
4814
4815
Andrei Popescu74b3c142010-03-29 12:03:09 +01004816TEST(DetachAndReattachGlobal) {
4817 v8::HandleScope scope;
4818 LocalContext env1;
4819
4820 // Create second environment.
4821 v8::Persistent<Context> env2 = Context::New();
4822
4823 Local<Value> foo = v8_str("foo");
4824
4825 // Set same security token for env1 and env2.
4826 env1->SetSecurityToken(foo);
4827 env2->SetSecurityToken(foo);
4828
4829 // Create a property on the global object in env2.
4830 {
4831 v8::Context::Scope scope(env2);
4832 env2->Global()->Set(v8_str("p"), v8::Integer::New(42));
4833 }
4834
4835 // Create a reference to env2 global from env1 global.
4836 env1->Global()->Set(v8_str("other"), env2->Global());
4837
4838 // Check that we have access to other.p in env2 from env1.
4839 Local<Value> result = CompileRun("other.p");
4840 CHECK(result->IsInt32());
4841 CHECK_EQ(42, result->Int32Value());
4842
4843 // Hold on to global from env2 and detach global from env2.
4844 Local<v8::Object> global2 = env2->Global();
4845 env2->DetachGlobal();
4846
4847 // Check that the global has been detached. No other.p property can
4848 // be found.
4849 result = CompileRun("other.p");
4850 CHECK(result->IsUndefined());
4851
4852 // Reuse global2 for env3.
4853 v8::Persistent<Context> env3 =
4854 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2);
4855 CHECK_EQ(global2, env3->Global());
4856
4857 // Start by using the same security token for env3 as for env1 and env2.
4858 env3->SetSecurityToken(foo);
4859
4860 // Create a property on the global object in env3.
4861 {
4862 v8::Context::Scope scope(env3);
4863 env3->Global()->Set(v8_str("p"), v8::Integer::New(24));
4864 }
4865
4866 // Check that other.p is now the property in env3 and that we have access.
4867 result = CompileRun("other.p");
4868 CHECK(result->IsInt32());
4869 CHECK_EQ(24, result->Int32Value());
4870
4871 // Change security token for env3 to something different from env1 and env2.
4872 env3->SetSecurityToken(v8_str("bar"));
4873
4874 // Check that we do not have access to other.p in env1. |other| is now
4875 // the global object for env3 which has a different security token,
4876 // so access should be blocked.
4877 result = CompileRun("other.p");
4878 CHECK(result->IsUndefined());
4879
4880 // Detach the global for env3 and reattach it to env2.
4881 env3->DetachGlobal();
4882 env2->ReattachGlobal(global2);
4883
4884 // Check that we have access to other.p again in env1. |other| is now
4885 // the global object for env2 which has the same security token as env1.
4886 result = CompileRun("other.p");
4887 CHECK(result->IsInt32());
4888 CHECK_EQ(42, result->Int32Value());
4889
4890 env2.Dispose();
4891 env3.Dispose();
4892}
4893
4894
Steve Blocka7e24c12009-10-30 11:49:00 +00004895static bool NamedAccessBlocker(Local<v8::Object> global,
4896 Local<Value> name,
4897 v8::AccessType type,
4898 Local<Value> data) {
4899 return Context::GetCurrent()->Global()->Equals(global);
4900}
4901
4902
4903static bool IndexedAccessBlocker(Local<v8::Object> global,
4904 uint32_t key,
4905 v8::AccessType type,
4906 Local<Value> data) {
4907 return Context::GetCurrent()->Global()->Equals(global);
4908}
4909
4910
4911static int g_echo_value = -1;
4912static v8::Handle<Value> EchoGetter(Local<String> name,
4913 const AccessorInfo& info) {
4914 return v8_num(g_echo_value);
4915}
4916
4917
4918static void EchoSetter(Local<String> name,
4919 Local<Value> value,
4920 const AccessorInfo&) {
4921 if (value->IsNumber())
4922 g_echo_value = value->Int32Value();
4923}
4924
4925
4926static v8::Handle<Value> UnreachableGetter(Local<String> name,
4927 const AccessorInfo& info) {
4928 CHECK(false); // This function should not be called..
4929 return v8::Undefined();
4930}
4931
4932
4933static void UnreachableSetter(Local<String>, Local<Value>,
4934 const AccessorInfo&) {
4935 CHECK(false); // This function should nto be called.
4936}
4937
4938
4939THREADED_TEST(AccessControl) {
4940 v8::HandleScope handle_scope;
4941 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
4942
4943 global_template->SetAccessCheckCallbacks(NamedAccessBlocker,
4944 IndexedAccessBlocker);
4945
4946 // Add an accessor accessible by cross-domain JS code.
4947 global_template->SetAccessor(
4948 v8_str("accessible_prop"),
4949 EchoGetter, EchoSetter,
4950 v8::Handle<Value>(),
4951 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE));
4952
4953 // Add an accessor that is not accessible by cross-domain JS code.
4954 global_template->SetAccessor(v8_str("blocked_prop"),
4955 UnreachableGetter, UnreachableSetter,
4956 v8::Handle<Value>(),
4957 v8::DEFAULT);
4958
4959 // Create an environment
4960 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
4961 context0->Enter();
4962
4963 v8::Handle<v8::Object> global0 = context0->Global();
4964
4965 v8::HandleScope scope1;
4966
4967 v8::Persistent<Context> context1 = Context::New();
4968 context1->Enter();
4969
4970 v8::Handle<v8::Object> global1 = context1->Global();
4971 global1->Set(v8_str("other"), global0);
4972
4973 v8::Handle<Value> value;
4974
4975 // Access blocked property
4976 value = v8_compile("other.blocked_prop = 1")->Run();
4977 value = v8_compile("other.blocked_prop")->Run();
4978 CHECK(value->IsUndefined());
4979
4980 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run();
4981 CHECK(value->IsFalse());
4982
4983 // Access accessible property
4984 value = v8_compile("other.accessible_prop = 3")->Run();
4985 CHECK(value->IsNumber());
4986 CHECK_EQ(3, value->Int32Value());
Andrei Popescu31002712010-02-23 13:46:05 +00004987 CHECK_EQ(3, g_echo_value);
Steve Blocka7e24c12009-10-30 11:49:00 +00004988
4989 value = v8_compile("other.accessible_prop")->Run();
4990 CHECK(value->IsNumber());
4991 CHECK_EQ(3, value->Int32Value());
4992
4993 value =
4994 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run();
4995 CHECK(value->IsTrue());
4996
4997 // Enumeration doesn't enumerate accessors from inaccessible objects in
4998 // the prototype chain even if the accessors are in themselves accessible.
4999 Local<Value> result =
5000 CompileRun("(function(){var obj = {'__proto__':other};"
5001 "for (var p in obj)"
5002 " if (p == 'accessible_prop' || p == 'blocked_prop') {"
5003 " return false;"
5004 " }"
5005 "return true;})()");
5006 CHECK(result->IsTrue());
5007
5008 context1->Exit();
5009 context0->Exit();
5010 context1.Dispose();
5011 context0.Dispose();
5012}
5013
5014
Leon Clarke4515c472010-02-03 11:58:03 +00005015static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global,
5016 Local<Value> name,
5017 v8::AccessType type,
5018 Local<Value> data) {
5019 return false;
5020}
5021
5022
5023static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global,
5024 uint32_t key,
5025 v8::AccessType type,
5026 Local<Value> data) {
5027 return false;
5028}
5029
5030
5031THREADED_TEST(AccessControlGetOwnPropertyNames) {
5032 v8::HandleScope handle_scope;
5033 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5034
5035 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5036 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker,
5037 GetOwnPropertyNamesIndexedBlocker);
5038
5039 // Create an environment
5040 v8::Persistent<Context> context0 = Context::New(NULL, obj_template);
5041 context0->Enter();
5042
5043 v8::Handle<v8::Object> global0 = context0->Global();
5044
5045 v8::HandleScope scope1;
5046
5047 v8::Persistent<Context> context1 = Context::New();
5048 context1->Enter();
5049
5050 v8::Handle<v8::Object> global1 = context1->Global();
5051 global1->Set(v8_str("other"), global0);
5052 global1->Set(v8_str("object"), obj_template->NewInstance());
5053
5054 v8::Handle<Value> value;
5055
5056 // Attempt to get the property names of the other global object and
5057 // of an object that requires access checks. Accessing the other
5058 // global object should be blocked by access checks on the global
5059 // proxy object. Accessing the object that requires access checks
5060 // is blocked by the access checks on the object itself.
5061 value = CompileRun("Object.getOwnPropertyNames(other).length == 0");
5062 CHECK(value->IsTrue());
5063
5064 value = CompileRun("Object.getOwnPropertyNames(object).length == 0");
5065 CHECK(value->IsTrue());
5066
5067 context1->Exit();
5068 context0->Exit();
5069 context1.Dispose();
5070 context0.Dispose();
5071}
5072
5073
Steve Block8defd9f2010-07-08 12:39:36 +01005074static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) {
5075 v8::Handle<v8::Array> result = v8::Array::New(1);
5076 result->Set(0, v8_str("x"));
5077 return result;
5078}
5079
5080
5081THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
5082 v8::HandleScope handle_scope;
5083 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New();
5084
5085 obj_template->Set(v8_str("x"), v8::Integer::New(42));
5086 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
5087 NamedPropertyEnumerator);
5088
5089 LocalContext context;
5090 v8::Handle<v8::Object> global = context->Global();
5091 global->Set(v8_str("object"), obj_template->NewInstance());
5092
5093 v8::Handle<Value> value =
5094 CompileRun("Object.getOwnPropertyNames(object).join(',')");
5095 CHECK_EQ(v8_str("x"), value);
5096}
5097
5098
Steve Blocka7e24c12009-10-30 11:49:00 +00005099static v8::Handle<Value> ConstTenGetter(Local<String> name,
5100 const AccessorInfo& info) {
5101 return v8_num(10);
5102}
5103
5104
5105THREADED_TEST(CrossDomainAccessors) {
5106 v8::HandleScope handle_scope;
5107
5108 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New();
5109
5110 v8::Handle<v8::ObjectTemplate> global_template =
5111 func_template->InstanceTemplate();
5112
5113 v8::Handle<v8::ObjectTemplate> proto_template =
5114 func_template->PrototypeTemplate();
5115
5116 // Add an accessor to proto that's accessible by cross-domain JS code.
5117 proto_template->SetAccessor(v8_str("accessible"),
5118 ConstTenGetter, 0,
5119 v8::Handle<Value>(),
5120 v8::ALL_CAN_READ);
5121
5122 // Add an accessor that is not accessible by cross-domain JS code.
5123 global_template->SetAccessor(v8_str("unreachable"),
5124 UnreachableGetter, 0,
5125 v8::Handle<Value>(),
5126 v8::DEFAULT);
5127
5128 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
5129 context0->Enter();
5130
5131 Local<v8::Object> global = context0->Global();
5132 // Add a normal property that shadows 'accessible'
5133 global->Set(v8_str("accessible"), v8_num(11));
5134
5135 // Enter a new context.
5136 v8::HandleScope scope1;
5137 v8::Persistent<Context> context1 = Context::New();
5138 context1->Enter();
5139
5140 v8::Handle<v8::Object> global1 = context1->Global();
5141 global1->Set(v8_str("other"), global);
5142
5143 // Should return 10, instead of 11
5144 v8::Handle<Value> value = v8_compile("other.accessible")->Run();
5145 CHECK(value->IsNumber());
5146 CHECK_EQ(10, value->Int32Value());
5147
5148 value = v8_compile("other.unreachable")->Run();
5149 CHECK(value->IsUndefined());
5150
5151 context1->Exit();
5152 context0->Exit();
5153 context1.Dispose();
5154 context0.Dispose();
5155}
5156
5157
5158static int named_access_count = 0;
5159static int indexed_access_count = 0;
5160
5161static bool NamedAccessCounter(Local<v8::Object> global,
5162 Local<Value> name,
5163 v8::AccessType type,
5164 Local<Value> data) {
5165 named_access_count++;
5166 return true;
5167}
5168
5169
5170static bool IndexedAccessCounter(Local<v8::Object> global,
5171 uint32_t key,
5172 v8::AccessType type,
5173 Local<Value> data) {
5174 indexed_access_count++;
5175 return true;
5176}
5177
5178
5179// This one is too easily disturbed by other tests.
5180TEST(AccessControlIC) {
5181 named_access_count = 0;
5182 indexed_access_count = 0;
5183
5184 v8::HandleScope handle_scope;
5185
5186 // Create an environment.
5187 v8::Persistent<Context> context0 = Context::New();
5188 context0->Enter();
5189
5190 // Create an object that requires access-check functions to be
5191 // called for cross-domain access.
5192 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5193 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5194 IndexedAccessCounter);
5195 Local<v8::Object> object = object_template->NewInstance();
5196
5197 v8::HandleScope scope1;
5198
5199 // Create another environment.
5200 v8::Persistent<Context> context1 = Context::New();
5201 context1->Enter();
5202
5203 // Make easy access to the object from the other environment.
5204 v8::Handle<v8::Object> global1 = context1->Global();
5205 global1->Set(v8_str("obj"), object);
5206
5207 v8::Handle<Value> value;
5208
5209 // Check that the named access-control function is called every time.
5210 CompileRun("function testProp(obj) {"
5211 " for (var i = 0; i < 10; i++) obj.prop = 1;"
5212 " for (var j = 0; j < 10; j++) obj.prop;"
5213 " return obj.prop"
5214 "}");
5215 value = CompileRun("testProp(obj)");
5216 CHECK(value->IsNumber());
5217 CHECK_EQ(1, value->Int32Value());
5218 CHECK_EQ(21, named_access_count);
5219
5220 // Check that the named access-control function is called every time.
5221 CompileRun("var p = 'prop';"
5222 "function testKeyed(obj) {"
5223 " for (var i = 0; i < 10; i++) obj[p] = 1;"
5224 " for (var j = 0; j < 10; j++) obj[p];"
5225 " return obj[p];"
5226 "}");
5227 // Use obj which requires access checks. No inline caching is used
5228 // in that case.
5229 value = CompileRun("testKeyed(obj)");
5230 CHECK(value->IsNumber());
5231 CHECK_EQ(1, value->Int32Value());
5232 CHECK_EQ(42, named_access_count);
5233 // Force the inline caches into generic state and try again.
5234 CompileRun("testKeyed({ a: 0 })");
5235 CompileRun("testKeyed({ b: 0 })");
5236 value = CompileRun("testKeyed(obj)");
5237 CHECK(value->IsNumber());
5238 CHECK_EQ(1, value->Int32Value());
5239 CHECK_EQ(63, named_access_count);
5240
5241 // Check that the indexed access-control function is called every time.
5242 CompileRun("function testIndexed(obj) {"
5243 " for (var i = 0; i < 10; i++) obj[0] = 1;"
5244 " for (var j = 0; j < 10; j++) obj[0];"
5245 " return obj[0]"
5246 "}");
5247 value = CompileRun("testIndexed(obj)");
5248 CHECK(value->IsNumber());
5249 CHECK_EQ(1, value->Int32Value());
5250 CHECK_EQ(21, indexed_access_count);
5251 // Force the inline caches into generic state.
5252 CompileRun("testIndexed(new Array(1))");
5253 // Test that the indexed access check is called.
5254 value = CompileRun("testIndexed(obj)");
5255 CHECK(value->IsNumber());
5256 CHECK_EQ(1, value->Int32Value());
5257 CHECK_EQ(42, indexed_access_count);
5258
5259 // Check that the named access check is called when invoking
5260 // functions on an object that requires access checks.
5261 CompileRun("obj.f = function() {}");
5262 CompileRun("function testCallNormal(obj) {"
5263 " for (var i = 0; i < 10; i++) obj.f();"
5264 "}");
5265 CompileRun("testCallNormal(obj)");
5266 CHECK_EQ(74, named_access_count);
5267
5268 // Force obj into slow case.
5269 value = CompileRun("delete obj.prop");
5270 CHECK(value->BooleanValue());
5271 // Force inline caches into dictionary probing mode.
5272 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
5273 // Test that the named access check is called.
5274 value = CompileRun("testProp(obj);");
5275 CHECK(value->IsNumber());
5276 CHECK_EQ(1, value->Int32Value());
5277 CHECK_EQ(96, named_access_count);
5278
5279 // Force the call inline cache into dictionary probing mode.
5280 CompileRun("o.f = function() {}; testCallNormal(o)");
5281 // Test that the named access check is still called for each
5282 // invocation of the function.
5283 value = CompileRun("testCallNormal(obj)");
5284 CHECK_EQ(106, named_access_count);
5285
5286 context1->Exit();
5287 context0->Exit();
5288 context1.Dispose();
5289 context0.Dispose();
5290}
5291
5292
5293static bool NamedAccessFlatten(Local<v8::Object> global,
5294 Local<Value> name,
5295 v8::AccessType type,
5296 Local<Value> data) {
5297 char buf[100];
5298 int len;
5299
5300 CHECK(name->IsString());
5301
5302 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005303 len = name.As<String>()->WriteAscii(buf);
Steve Blocka7e24c12009-10-30 11:49:00 +00005304 CHECK_EQ(4, len);
5305
5306 uint16_t buf2[100];
5307
5308 memset(buf, 0x1, sizeof(buf));
Steve Block6ded16b2010-05-10 14:33:55 +01005309 len = name.As<String>()->Write(buf2);
Steve Blocka7e24c12009-10-30 11:49:00 +00005310 CHECK_EQ(4, len);
5311
5312 return true;
5313}
5314
5315
5316static bool IndexedAccessFlatten(Local<v8::Object> global,
5317 uint32_t key,
5318 v8::AccessType type,
5319 Local<Value> data) {
5320 return true;
5321}
5322
5323
5324// Regression test. In access checks, operations that may cause
5325// garbage collection are not allowed. It used to be the case that
5326// using the Write operation on a string could cause a garbage
5327// collection due to flattening of the string. This is no longer the
5328// case.
5329THREADED_TEST(AccessControlFlatten) {
5330 named_access_count = 0;
5331 indexed_access_count = 0;
5332
5333 v8::HandleScope handle_scope;
5334
5335 // Create an environment.
5336 v8::Persistent<Context> context0 = Context::New();
5337 context0->Enter();
5338
5339 // Create an object that requires access-check functions to be
5340 // called for cross-domain access.
5341 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5342 object_template->SetAccessCheckCallbacks(NamedAccessFlatten,
5343 IndexedAccessFlatten);
5344 Local<v8::Object> object = object_template->NewInstance();
5345
5346 v8::HandleScope scope1;
5347
5348 // Create another environment.
5349 v8::Persistent<Context> context1 = Context::New();
5350 context1->Enter();
5351
5352 // Make easy access to the object from the other environment.
5353 v8::Handle<v8::Object> global1 = context1->Global();
5354 global1->Set(v8_str("obj"), object);
5355
5356 v8::Handle<Value> value;
5357
5358 value = v8_compile("var p = 'as' + 'df';")->Run();
5359 value = v8_compile("obj[p];")->Run();
5360
5361 context1->Exit();
5362 context0->Exit();
5363 context1.Dispose();
5364 context0.Dispose();
5365}
5366
5367
5368static v8::Handle<Value> AccessControlNamedGetter(
5369 Local<String>, const AccessorInfo&) {
5370 return v8::Integer::New(42);
5371}
5372
5373
5374static v8::Handle<Value> AccessControlNamedSetter(
5375 Local<String>, Local<Value> value, const AccessorInfo&) {
5376 return value;
5377}
5378
5379
5380static v8::Handle<Value> AccessControlIndexedGetter(
5381 uint32_t index,
5382 const AccessorInfo& info) {
5383 return v8_num(42);
5384}
5385
5386
5387static v8::Handle<Value> AccessControlIndexedSetter(
5388 uint32_t, Local<Value> value, const AccessorInfo&) {
5389 return value;
5390}
5391
5392
5393THREADED_TEST(AccessControlInterceptorIC) {
5394 named_access_count = 0;
5395 indexed_access_count = 0;
5396
5397 v8::HandleScope handle_scope;
5398
5399 // Create an environment.
5400 v8::Persistent<Context> context0 = Context::New();
5401 context0->Enter();
5402
5403 // Create an object that requires access-check functions to be
5404 // called for cross-domain access. The object also has interceptors
5405 // interceptor.
5406 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
5407 object_template->SetAccessCheckCallbacks(NamedAccessCounter,
5408 IndexedAccessCounter);
5409 object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
5410 AccessControlNamedSetter);
5411 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
5412 AccessControlIndexedSetter);
5413 Local<v8::Object> object = object_template->NewInstance();
5414
5415 v8::HandleScope scope1;
5416
5417 // Create another environment.
5418 v8::Persistent<Context> context1 = Context::New();
5419 context1->Enter();
5420
5421 // Make easy access to the object from the other environment.
5422 v8::Handle<v8::Object> global1 = context1->Global();
5423 global1->Set(v8_str("obj"), object);
5424
5425 v8::Handle<Value> value;
5426
5427 // Check that the named access-control function is called every time
5428 // eventhough there is an interceptor on the object.
5429 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run();
5430 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;"
5431 "obj.x")->Run();
5432 CHECK(value->IsNumber());
5433 CHECK_EQ(42, value->Int32Value());
5434 CHECK_EQ(21, named_access_count);
5435
5436 value = v8_compile("var p = 'x';")->Run();
5437 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
5438 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
5439 "obj[p]")->Run();
5440 CHECK(value->IsNumber());
5441 CHECK_EQ(42, value->Int32Value());
5442 CHECK_EQ(42, named_access_count);
5443
5444 // Check that the indexed access-control function is called every
5445 // time eventhough there is an interceptor on the object.
5446 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
5447 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
5448 "obj[0]")->Run();
5449 CHECK(value->IsNumber());
5450 CHECK_EQ(42, value->Int32Value());
5451 CHECK_EQ(21, indexed_access_count);
5452
5453 context1->Exit();
5454 context0->Exit();
5455 context1.Dispose();
5456 context0.Dispose();
5457}
5458
5459
5460THREADED_TEST(Version) {
5461 v8::V8::GetVersion();
5462}
5463
5464
5465static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) {
5466 ApiTestFuzzer::Fuzz();
5467 return v8_num(12);
5468}
5469
5470
5471THREADED_TEST(InstanceProperties) {
5472 v8::HandleScope handle_scope;
5473 LocalContext context;
5474
5475 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5476 Local<ObjectTemplate> instance = t->InstanceTemplate();
5477
5478 instance->Set(v8_str("x"), v8_num(42));
5479 instance->Set(v8_str("f"),
5480 v8::FunctionTemplate::New(InstanceFunctionCallback));
5481
5482 Local<Value> o = t->GetFunction()->NewInstance();
5483
5484 context->Global()->Set(v8_str("i"), o);
5485 Local<Value> value = Script::Compile(v8_str("i.x"))->Run();
5486 CHECK_EQ(42, value->Int32Value());
5487
5488 value = Script::Compile(v8_str("i.f()"))->Run();
5489 CHECK_EQ(12, value->Int32Value());
5490}
5491
5492
5493static v8::Handle<Value>
5494GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) {
5495 ApiTestFuzzer::Fuzz();
5496 return v8::Handle<Value>();
5497}
5498
5499
5500THREADED_TEST(GlobalObjectInstanceProperties) {
5501 v8::HandleScope handle_scope;
5502
5503 Local<Value> global_object;
5504
5505 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5506 t->InstanceTemplate()->SetNamedPropertyHandler(
5507 GlobalObjectInstancePropertiesGet);
5508 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5509 instance_template->Set(v8_str("x"), v8_num(42));
5510 instance_template->Set(v8_str("f"),
5511 v8::FunctionTemplate::New(InstanceFunctionCallback));
5512
5513 {
5514 LocalContext env(NULL, instance_template);
5515 // Hold on to the global object so it can be used again in another
5516 // environment initialization.
5517 global_object = env->Global();
5518
5519 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5520 CHECK_EQ(42, value->Int32Value());
5521 value = Script::Compile(v8_str("f()"))->Run();
5522 CHECK_EQ(12, value->Int32Value());
5523 }
5524
5525 {
5526 // Create new environment reusing the global object.
5527 LocalContext env(NULL, instance_template, global_object);
5528 Local<Value> value = Script::Compile(v8_str("x"))->Run();
5529 CHECK_EQ(42, value->Int32Value());
5530 value = Script::Compile(v8_str("f()"))->Run();
5531 CHECK_EQ(12, value->Int32Value());
5532 }
5533}
5534
5535
5536static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) {
5537 ApiTestFuzzer::Fuzz();
5538 return v8_num(42);
5539}
5540
5541
5542static int shadow_y;
5543static int shadow_y_setter_call_count;
5544static int shadow_y_getter_call_count;
5545
5546
5547static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) {
5548 shadow_y_setter_call_count++;
5549 shadow_y = 42;
5550}
5551
5552
5553static v8::Handle<Value> ShadowYGetter(Local<String> name,
5554 const AccessorInfo& info) {
5555 ApiTestFuzzer::Fuzz();
5556 shadow_y_getter_call_count++;
5557 return v8_num(shadow_y);
5558}
5559
5560
5561static v8::Handle<Value> ShadowIndexedGet(uint32_t index,
5562 const AccessorInfo& info) {
5563 return v8::Handle<Value>();
5564}
5565
5566
5567static v8::Handle<Value> ShadowNamedGet(Local<String> key,
5568 const AccessorInfo&) {
5569 return v8::Handle<Value>();
5570}
5571
5572
5573THREADED_TEST(ShadowObject) {
5574 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
5575 v8::HandleScope handle_scope;
5576
5577 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New();
5578 LocalContext context(NULL, global_template);
5579
5580 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5581 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
5582 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
5583 Local<ObjectTemplate> proto = t->PrototypeTemplate();
5584 Local<ObjectTemplate> instance = t->InstanceTemplate();
5585
5586 // Only allow calls of f on instances of t.
5587 Local<v8::Signature> signature = v8::Signature::New(t);
5588 proto->Set(v8_str("f"),
5589 v8::FunctionTemplate::New(ShadowFunctionCallback,
5590 Local<Value>(),
5591 signature));
5592 proto->Set(v8_str("x"), v8_num(12));
5593
5594 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter);
5595
5596 Local<Value> o = t->GetFunction()->NewInstance();
5597 context->Global()->Set(v8_str("__proto__"), o);
5598
5599 Local<Value> value =
5600 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run();
5601 CHECK(value->IsBoolean());
5602 CHECK(!value->BooleanValue());
5603
5604 value = Script::Compile(v8_str("x"))->Run();
5605 CHECK_EQ(12, value->Int32Value());
5606
5607 value = Script::Compile(v8_str("f()"))->Run();
5608 CHECK_EQ(42, value->Int32Value());
5609
5610 Script::Compile(v8_str("y = 42"))->Run();
5611 CHECK_EQ(1, shadow_y_setter_call_count);
5612 value = Script::Compile(v8_str("y"))->Run();
5613 CHECK_EQ(1, shadow_y_getter_call_count);
5614 CHECK_EQ(42, value->Int32Value());
5615}
5616
5617
5618THREADED_TEST(HiddenPrototype) {
5619 v8::HandleScope handle_scope;
5620 LocalContext context;
5621
5622 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5623 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5624 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5625 t1->SetHiddenPrototype(true);
5626 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5627 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5628 t2->SetHiddenPrototype(true);
5629 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5630 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5631 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5632
5633 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5634 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5635 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5636 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5637
5638 // Setting the prototype on an object skips hidden prototypes.
5639 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5640 o0->Set(v8_str("__proto__"), o1);
5641 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5642 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5643 o0->Set(v8_str("__proto__"), o2);
5644 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5645 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5646 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5647 o0->Set(v8_str("__proto__"), o3);
5648 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5649 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5650 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5651 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5652
5653 // Getting the prototype of o0 should get the first visible one
5654 // which is o3. Therefore, z should not be defined on the prototype
5655 // object.
5656 Local<Value> proto = o0->Get(v8_str("__proto__"));
5657 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005658 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined());
Steve Blocka7e24c12009-10-30 11:49:00 +00005659}
5660
5661
Andrei Popescu402d9372010-02-26 13:31:12 +00005662THREADED_TEST(SetPrototype) {
5663 v8::HandleScope handle_scope;
5664 LocalContext context;
5665
5666 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
5667 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
5668 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
5669 t1->SetHiddenPrototype(true);
5670 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
5671 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
5672 t2->SetHiddenPrototype(true);
5673 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
5674 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
5675 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
5676
5677 Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
5678 Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
5679 Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
5680 Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
5681
5682 // Setting the prototype on an object does not skip hidden prototypes.
5683 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5684 CHECK(o0->SetPrototype(o1));
5685 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5686 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5687 CHECK(o1->SetPrototype(o2));
5688 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5689 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5690 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5691 CHECK(o2->SetPrototype(o3));
5692 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
5693 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
5694 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
5695 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
5696
5697 // Getting the prototype of o0 should get the first visible one
5698 // which is o3. Therefore, z should not be defined on the prototype
5699 // object.
5700 Local<Value> proto = o0->Get(v8_str("__proto__"));
5701 CHECK(proto->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005702 CHECK_EQ(proto.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005703
5704 // However, Object::GetPrototype ignores hidden prototype.
5705 Local<Value> proto0 = o0->GetPrototype();
5706 CHECK(proto0->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005707 CHECK_EQ(proto0.As<v8::Object>(), o1);
Andrei Popescu402d9372010-02-26 13:31:12 +00005708
5709 Local<Value> proto1 = o1->GetPrototype();
5710 CHECK(proto1->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005711 CHECK_EQ(proto1.As<v8::Object>(), o2);
Andrei Popescu402d9372010-02-26 13:31:12 +00005712
5713 Local<Value> proto2 = o2->GetPrototype();
5714 CHECK(proto2->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01005715 CHECK_EQ(proto2.As<v8::Object>(), o3);
Andrei Popescu402d9372010-02-26 13:31:12 +00005716}
5717
5718
5719THREADED_TEST(SetPrototypeThrows) {
5720 v8::HandleScope handle_scope;
5721 LocalContext context;
5722
5723 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5724
5725 Local<v8::Object> o0 = t->GetFunction()->NewInstance();
5726 Local<v8::Object> o1 = t->GetFunction()->NewInstance();
5727
5728 CHECK(o0->SetPrototype(o1));
5729 // If setting the prototype leads to the cycle, SetPrototype should
5730 // return false and keep VM in sane state.
5731 v8::TryCatch try_catch;
5732 CHECK(!o1->SetPrototype(o0));
5733 CHECK(!try_catch.HasCaught());
5734 ASSERT(!i::Top::has_pending_exception());
5735
5736 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
5737}
5738
5739
Steve Blocka7e24c12009-10-30 11:49:00 +00005740THREADED_TEST(GetterSetterExceptions) {
5741 v8::HandleScope handle_scope;
5742 LocalContext context;
5743 CompileRun(
5744 "function Foo() { };"
5745 "function Throw() { throw 5; };"
5746 "var x = { };"
5747 "x.__defineSetter__('set', Throw);"
5748 "x.__defineGetter__('get', Throw);");
5749 Local<v8::Object> x =
5750 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x")));
5751 v8::TryCatch try_catch;
5752 x->Set(v8_str("set"), v8::Integer::New(8));
5753 x->Get(v8_str("get"));
5754 x->Set(v8_str("set"), v8::Integer::New(8));
5755 x->Get(v8_str("get"));
5756 x->Set(v8_str("set"), v8::Integer::New(8));
5757 x->Get(v8_str("get"));
5758 x->Set(v8_str("set"), v8::Integer::New(8));
5759 x->Get(v8_str("get"));
5760}
5761
5762
5763THREADED_TEST(Constructor) {
5764 v8::HandleScope handle_scope;
5765 LocalContext context;
5766 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5767 templ->SetClassName(v8_str("Fun"));
5768 Local<Function> cons = templ->GetFunction();
5769 context->Global()->Set(v8_str("Fun"), cons);
5770 Local<v8::Object> inst = cons->NewInstance();
5771 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst);
5772 Local<Value> value = CompileRun("(new Fun()).constructor === Fun");
5773 CHECK(value->BooleanValue());
5774}
5775
5776THREADED_TEST(FunctionDescriptorException) {
5777 v8::HandleScope handle_scope;
5778 LocalContext context;
5779 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
5780 templ->SetClassName(v8_str("Fun"));
5781 Local<Function> cons = templ->GetFunction();
5782 context->Global()->Set(v8_str("Fun"), cons);
5783 Local<Value> value = CompileRun(
5784 "function test() {"
5785 " try {"
5786 " (new Fun()).blah()"
5787 " } catch (e) {"
5788 " var str = String(e);"
5789 " if (str.indexOf('TypeError') == -1) return 1;"
5790 " if (str.indexOf('[object Fun]') != -1) return 2;"
5791 " if (str.indexOf('#<a Fun>') == -1) return 3;"
5792 " return 0;"
5793 " }"
5794 " return 4;"
5795 "}"
5796 "test();");
5797 CHECK_EQ(0, value->Int32Value());
5798}
5799
5800
5801THREADED_TEST(EvalAliasedDynamic) {
5802 v8::HandleScope scope;
5803 LocalContext current;
5804
5805 // Tests where aliased eval can only be resolved dynamically.
5806 Local<Script> script =
5807 Script::Compile(v8_str("function f(x) { "
5808 " var foo = 2;"
5809 " with (x) { return eval('foo'); }"
5810 "}"
5811 "foo = 0;"
5812 "result1 = f(new Object());"
5813 "result2 = f(this);"
5814 "var x = new Object();"
5815 "x.eval = function(x) { return 1; };"
5816 "result3 = f(x);"));
5817 script->Run();
5818 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value());
5819 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value());
5820 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value());
5821
5822 v8::TryCatch try_catch;
5823 script =
5824 Script::Compile(v8_str("function f(x) { "
5825 " var bar = 2;"
5826 " with (x) { return eval('bar'); }"
5827 "}"
5828 "f(this)"));
5829 script->Run();
5830 CHECK(try_catch.HasCaught());
5831 try_catch.Reset();
5832}
5833
5834
5835THREADED_TEST(CrossEval) {
5836 v8::HandleScope scope;
5837 LocalContext other;
5838 LocalContext current;
5839
5840 Local<String> token = v8_str("<security token>");
5841 other->SetSecurityToken(token);
5842 current->SetSecurityToken(token);
5843
5844 // Setup reference from current to other.
5845 current->Global()->Set(v8_str("other"), other->Global());
5846
5847 // Check that new variables are introduced in other context.
5848 Local<Script> script =
5849 Script::Compile(v8_str("other.eval('var foo = 1234')"));
5850 script->Run();
5851 Local<Value> foo = other->Global()->Get(v8_str("foo"));
5852 CHECK_EQ(1234, foo->Int32Value());
5853 CHECK(!current->Global()->Has(v8_str("foo")));
5854
5855 // Check that writing to non-existing properties introduces them in
5856 // the other context.
5857 script =
5858 Script::Compile(v8_str("other.eval('na = 1234')"));
5859 script->Run();
5860 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value());
5861 CHECK(!current->Global()->Has(v8_str("na")));
5862
5863 // Check that global variables in current context are not visible in other
5864 // context.
5865 v8::TryCatch try_catch;
5866 script =
5867 Script::Compile(v8_str("var bar = 42; other.eval('bar');"));
5868 Local<Value> result = script->Run();
5869 CHECK(try_catch.HasCaught());
5870 try_catch.Reset();
5871
5872 // Check that local variables in current context are not visible in other
5873 // context.
5874 script =
5875 Script::Compile(v8_str("(function() { "
5876 " var baz = 87;"
5877 " return other.eval('baz');"
5878 "})();"));
5879 result = script->Run();
5880 CHECK(try_catch.HasCaught());
5881 try_catch.Reset();
5882
5883 // Check that global variables in the other environment are visible
5884 // when evaluting code.
5885 other->Global()->Set(v8_str("bis"), v8_num(1234));
5886 script = Script::Compile(v8_str("other.eval('bis')"));
5887 CHECK_EQ(1234, script->Run()->Int32Value());
5888 CHECK(!try_catch.HasCaught());
5889
5890 // Check that the 'this' pointer points to the global object evaluating
5891 // code.
5892 other->Global()->Set(v8_str("t"), other->Global());
5893 script = Script::Compile(v8_str("other.eval('this == t')"));
5894 result = script->Run();
5895 CHECK(result->IsTrue());
5896 CHECK(!try_catch.HasCaught());
5897
5898 // Check that variables introduced in with-statement are not visible in
5899 // other context.
5900 script =
5901 Script::Compile(v8_str("with({x:2}){other.eval('x')}"));
5902 result = script->Run();
5903 CHECK(try_catch.HasCaught());
5904 try_catch.Reset();
5905
5906 // Check that you cannot use 'eval.call' with another object than the
5907 // current global object.
5908 script =
5909 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')"));
5910 result = script->Run();
5911 CHECK(try_catch.HasCaught());
5912}
5913
5914
5915// Test that calling eval in a context which has been detached from
5916// its global throws an exception. This behavior is consistent with
5917// other JavaScript implementations.
5918THREADED_TEST(EvalInDetachedGlobal) {
5919 v8::HandleScope scope;
5920
5921 v8::Persistent<Context> context0 = Context::New();
5922 v8::Persistent<Context> context1 = Context::New();
5923
5924 // Setup function in context0 that uses eval from context0.
5925 context0->Enter();
5926 v8::Handle<v8::Value> fun =
5927 CompileRun("var x = 42;"
5928 "(function() {"
5929 " var e = eval;"
5930 " return function(s) { return e(s); }"
5931 "})()");
5932 context0->Exit();
5933
5934 // Put the function into context1 and call it before and after
5935 // detaching the global. Before detaching, the call succeeds and
5936 // after detaching and exception is thrown.
5937 context1->Enter();
5938 context1->Global()->Set(v8_str("fun"), fun);
5939 v8::Handle<v8::Value> x_value = CompileRun("fun('x')");
5940 CHECK_EQ(42, x_value->Int32Value());
5941 context0->DetachGlobal();
5942 v8::TryCatch catcher;
5943 x_value = CompileRun("fun('x')");
5944 CHECK(x_value.IsEmpty());
5945 CHECK(catcher.HasCaught());
5946 context1->Exit();
5947
5948 context1.Dispose();
5949 context0.Dispose();
5950}
5951
5952
5953THREADED_TEST(CrossLazyLoad) {
5954 v8::HandleScope scope;
5955 LocalContext other;
5956 LocalContext current;
5957
5958 Local<String> token = v8_str("<security token>");
5959 other->SetSecurityToken(token);
5960 current->SetSecurityToken(token);
5961
5962 // Setup reference from current to other.
5963 current->Global()->Set(v8_str("other"), other->Global());
5964
5965 // Trigger lazy loading in other context.
5966 Local<Script> script =
5967 Script::Compile(v8_str("other.eval('new Date(42)')"));
5968 Local<Value> value = script->Run();
5969 CHECK_EQ(42.0, value->NumberValue());
5970}
5971
5972
5973static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
Andrei Popescu402d9372010-02-26 13:31:12 +00005974 ApiTestFuzzer::Fuzz();
Steve Blocka7e24c12009-10-30 11:49:00 +00005975 if (args.IsConstructCall()) {
5976 if (args[0]->IsInt32()) {
5977 return v8_num(-args[0]->Int32Value());
5978 }
5979 }
5980
5981 return args[0];
5982}
5983
5984
5985// Test that a call handler can be set for objects which will allow
5986// non-function objects created through the API to be called as
5987// functions.
5988THREADED_TEST(CallAsFunction) {
5989 v8::HandleScope scope;
5990 LocalContext context;
5991
5992 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
5993 Local<ObjectTemplate> instance_template = t->InstanceTemplate();
5994 instance_template->SetCallAsFunctionHandler(call_as_function);
5995 Local<v8::Object> instance = t->GetFunction()->NewInstance();
5996 context->Global()->Set(v8_str("obj"), instance);
5997 v8::TryCatch try_catch;
5998 Local<Value> value;
5999 CHECK(!try_catch.HasCaught());
6000
6001 value = CompileRun("obj(42)");
6002 CHECK(!try_catch.HasCaught());
6003 CHECK_EQ(42, value->Int32Value());
6004
6005 value = CompileRun("(function(o){return o(49)})(obj)");
6006 CHECK(!try_catch.HasCaught());
6007 CHECK_EQ(49, value->Int32Value());
6008
6009 // test special case of call as function
6010 value = CompileRun("[obj]['0'](45)");
6011 CHECK(!try_catch.HasCaught());
6012 CHECK_EQ(45, value->Int32Value());
6013
6014 value = CompileRun("obj.call = Function.prototype.call;"
6015 "obj.call(null, 87)");
6016 CHECK(!try_catch.HasCaught());
6017 CHECK_EQ(87, value->Int32Value());
6018
6019 // Regression tests for bug #1116356: Calling call through call/apply
6020 // must work for non-function receivers.
6021 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])";
6022 value = CompileRun(apply_99);
6023 CHECK(!try_catch.HasCaught());
6024 CHECK_EQ(99, value->Int32Value());
6025
6026 const char* call_17 = "Function.prototype.call.call(obj, this, 17)";
6027 value = CompileRun(call_17);
6028 CHECK(!try_catch.HasCaught());
6029 CHECK_EQ(17, value->Int32Value());
6030
6031 // Check that the call-as-function handler can be called through
Leon Clarkee46be812010-01-19 14:06:41 +00006032 // new.
Steve Blocka7e24c12009-10-30 11:49:00 +00006033 value = CompileRun("new obj(43)");
6034 CHECK(!try_catch.HasCaught());
6035 CHECK_EQ(-43, value->Int32Value());
6036}
6037
6038
6039static int CountHandles() {
6040 return v8::HandleScope::NumberOfHandles();
6041}
6042
6043
6044static int Recurse(int depth, int iterations) {
6045 v8::HandleScope scope;
6046 if (depth == 0) return CountHandles();
6047 for (int i = 0; i < iterations; i++) {
6048 Local<v8::Number> n = v8::Integer::New(42);
6049 }
6050 return Recurse(depth - 1, iterations);
6051}
6052
6053
6054THREADED_TEST(HandleIteration) {
6055 static const int kIterations = 500;
6056 static const int kNesting = 200;
6057 CHECK_EQ(0, CountHandles());
6058 {
6059 v8::HandleScope scope1;
6060 CHECK_EQ(0, CountHandles());
6061 for (int i = 0; i < kIterations; i++) {
6062 Local<v8::Number> n = v8::Integer::New(42);
6063 CHECK_EQ(i + 1, CountHandles());
6064 }
6065
6066 CHECK_EQ(kIterations, CountHandles());
6067 {
6068 v8::HandleScope scope2;
6069 for (int j = 0; j < kIterations; j++) {
6070 Local<v8::Number> n = v8::Integer::New(42);
6071 CHECK_EQ(j + 1 + kIterations, CountHandles());
6072 }
6073 }
6074 CHECK_EQ(kIterations, CountHandles());
6075 }
6076 CHECK_EQ(0, CountHandles());
6077 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations));
6078}
6079
6080
6081static v8::Handle<Value> InterceptorHasOwnPropertyGetter(
6082 Local<String> name,
6083 const AccessorInfo& info) {
6084 ApiTestFuzzer::Fuzz();
6085 return v8::Handle<Value>();
6086}
6087
6088
6089THREADED_TEST(InterceptorHasOwnProperty) {
6090 v8::HandleScope scope;
6091 LocalContext context;
6092 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6093 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6094 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
6095 Local<Function> function = fun_templ->GetFunction();
6096 context->Global()->Set(v8_str("constructor"), function);
6097 v8::Handle<Value> value = CompileRun(
6098 "var o = new constructor();"
6099 "o.hasOwnProperty('ostehaps');");
6100 CHECK_EQ(false, value->BooleanValue());
6101 value = CompileRun(
6102 "o.ostehaps = 42;"
6103 "o.hasOwnProperty('ostehaps');");
6104 CHECK_EQ(true, value->BooleanValue());
6105 value = CompileRun(
6106 "var p = new constructor();"
6107 "p.hasOwnProperty('ostehaps');");
6108 CHECK_EQ(false, value->BooleanValue());
6109}
6110
6111
6112static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC(
6113 Local<String> name,
6114 const AccessorInfo& info) {
6115 ApiTestFuzzer::Fuzz();
6116 i::Heap::CollectAllGarbage(false);
6117 return v8::Handle<Value>();
6118}
6119
6120
6121THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
6122 v8::HandleScope scope;
6123 LocalContext context;
6124 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6125 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
6126 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
6127 Local<Function> function = fun_templ->GetFunction();
6128 context->Global()->Set(v8_str("constructor"), function);
6129 // Let's first make some stuff so we can be sure to get a good GC.
6130 CompileRun(
6131 "function makestr(size) {"
6132 " switch (size) {"
6133 " case 1: return 'f';"
6134 " case 2: return 'fo';"
6135 " case 3: return 'foo';"
6136 " }"
6137 " return makestr(size >> 1) + makestr((size + 1) >> 1);"
6138 "}"
6139 "var x = makestr(12345);"
6140 "x = makestr(31415);"
6141 "x = makestr(23456);");
6142 v8::Handle<Value> value = CompileRun(
6143 "var o = new constructor();"
6144 "o.__proto__ = new String(x);"
6145 "o.hasOwnProperty('ostehaps');");
6146 CHECK_EQ(false, value->BooleanValue());
6147}
6148
6149
6150typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property,
6151 const AccessorInfo& info);
6152
6153
6154static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
6155 const char* source,
6156 int expected) {
6157 v8::HandleScope scope;
6158 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6159 templ->SetNamedPropertyHandler(getter);
6160 LocalContext context;
6161 context->Global()->Set(v8_str("o"), templ->NewInstance());
6162 v8::Handle<Value> value = CompileRun(source);
6163 CHECK_EQ(expected, value->Int32Value());
6164}
6165
6166
6167static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name,
6168 const AccessorInfo& info) {
6169 ApiTestFuzzer::Fuzz();
6170 CHECK(v8_str("x")->Equals(name));
6171 return v8::Integer::New(42);
6172}
6173
6174
6175// This test should hit the load IC for the interceptor case.
6176THREADED_TEST(InterceptorLoadIC) {
6177 CheckInterceptorLoadIC(InterceptorLoadICGetter,
6178 "var result = 0;"
6179 "for (var i = 0; i < 1000; i++) {"
6180 " result = o.x;"
6181 "}",
6182 42);
6183}
6184
6185
6186// Below go several tests which verify that JITing for various
6187// configurations of interceptor and explicit fields works fine
6188// (those cases are special cased to get better performance).
6189
6190static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name,
6191 const AccessorInfo& info) {
6192 ApiTestFuzzer::Fuzz();
6193 return v8_str("x")->Equals(name)
6194 ? v8::Integer::New(42) : v8::Handle<v8::Value>();
6195}
6196
6197
6198THREADED_TEST(InterceptorLoadICWithFieldOnHolder) {
6199 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6200 "var result = 0;"
6201 "o.y = 239;"
6202 "for (var i = 0; i < 1000; i++) {"
6203 " result = o.y;"
6204 "}",
6205 239);
6206}
6207
6208
6209THREADED_TEST(InterceptorLoadICWithSubstitutedProto) {
6210 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6211 "var result = 0;"
6212 "o.__proto__ = { 'y': 239 };"
6213 "for (var i = 0; i < 1000; i++) {"
6214 " result = o.y + o.x;"
6215 "}",
6216 239 + 42);
6217}
6218
6219
6220THREADED_TEST(InterceptorLoadICWithPropertyOnProto) {
6221 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6222 "var result = 0;"
6223 "o.__proto__.y = 239;"
6224 "for (var i = 0; i < 1000; i++) {"
6225 " result = o.y + o.x;"
6226 "}",
6227 239 + 42);
6228}
6229
6230
6231THREADED_TEST(InterceptorLoadICUndefined) {
6232 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6233 "var result = 0;"
6234 "for (var i = 0; i < 1000; i++) {"
6235 " result = (o.y == undefined) ? 239 : 42;"
6236 "}",
6237 239);
6238}
6239
6240
6241THREADED_TEST(InterceptorLoadICWithOverride) {
6242 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6243 "fst = new Object(); fst.__proto__ = o;"
6244 "snd = new Object(); snd.__proto__ = fst;"
6245 "var result1 = 0;"
6246 "for (var i = 0; i < 1000; i++) {"
6247 " result1 = snd.x;"
6248 "}"
6249 "fst.x = 239;"
6250 "var result = 0;"
6251 "for (var i = 0; i < 1000; i++) {"
6252 " result = snd.x;"
6253 "}"
6254 "result + result1",
6255 239 + 42);
6256}
6257
6258
6259// Test the case when we stored field into
6260// a stub, but interceptor produced value on its own.
6261THREADED_TEST(InterceptorLoadICFieldNotNeeded) {
6262 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6263 "proto = new Object();"
6264 "o.__proto__ = proto;"
6265 "proto.x = 239;"
6266 "for (var i = 0; i < 1000; i++) {"
6267 " o.x;"
6268 // Now it should be ICed and keep a reference to x defined on proto
6269 "}"
6270 "var result = 0;"
6271 "for (var i = 0; i < 1000; i++) {"
6272 " result += o.x;"
6273 "}"
6274 "result;",
6275 42 * 1000);
6276}
6277
6278
6279// Test the case when we stored field into
6280// a stub, but it got invalidated later on.
6281THREADED_TEST(InterceptorLoadICInvalidatedField) {
6282 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6283 "proto1 = new Object();"
6284 "proto2 = new Object();"
6285 "o.__proto__ = proto1;"
6286 "proto1.__proto__ = proto2;"
6287 "proto2.y = 239;"
6288 "for (var i = 0; i < 1000; i++) {"
6289 " o.y;"
6290 // Now it should be ICed and keep a reference to y defined on proto2
6291 "}"
6292 "proto1.y = 42;"
6293 "var result = 0;"
6294 "for (var i = 0; i < 1000; i++) {"
6295 " result += o.y;"
6296 "}"
6297 "result;",
6298 42 * 1000);
6299}
6300
6301
Steve Block6ded16b2010-05-10 14:33:55 +01006302static int interceptor_load_not_handled_calls = 0;
6303static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name,
6304 const AccessorInfo& info) {
6305 ++interceptor_load_not_handled_calls;
6306 return v8::Handle<v8::Value>();
6307}
6308
6309
6310// Test how post-interceptor lookups are done in the non-cacheable
6311// case: the interceptor should not be invoked during this lookup.
6312THREADED_TEST(InterceptorLoadICPostInterceptor) {
6313 interceptor_load_not_handled_calls = 0;
6314 CheckInterceptorLoadIC(InterceptorLoadNotHandled,
6315 "receiver = new Object();"
6316 "receiver.__proto__ = o;"
6317 "proto = new Object();"
6318 "/* Make proto a slow-case object. */"
6319 "for (var i = 0; i < 1000; i++) {"
6320 " proto[\"xxxxxxxx\" + i] = [];"
6321 "}"
6322 "proto.x = 17;"
6323 "o.__proto__ = proto;"
6324 "var result = 0;"
6325 "for (var i = 0; i < 1000; i++) {"
6326 " result += receiver.x;"
6327 "}"
6328 "result;",
6329 17 * 1000);
6330 CHECK_EQ(1000, interceptor_load_not_handled_calls);
6331}
6332
6333
Steve Blocka7e24c12009-10-30 11:49:00 +00006334// Test the case when we stored field into
6335// a stub, but it got invalidated later on due to override on
6336// global object which is between interceptor and fields' holders.
6337THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) {
6338 CheckInterceptorLoadIC(InterceptorLoadXICGetter,
6339 "o.__proto__ = this;" // set a global to be a proto of o.
6340 "this.__proto__.y = 239;"
6341 "for (var i = 0; i < 10; i++) {"
6342 " if (o.y != 239) throw 'oops: ' + o.y;"
6343 // Now it should be ICed and keep a reference to y defined on field_holder.
6344 "}"
6345 "this.y = 42;" // Assign on a global.
6346 "var result = 0;"
6347 "for (var i = 0; i < 10; i++) {"
6348 " result += o.y;"
6349 "}"
6350 "result;",
6351 42 * 10);
6352}
6353
6354
6355static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) {
6356 ApiTestFuzzer::Fuzz();
6357 return v8_num(239);
6358}
6359
6360
6361static void SetOnThis(Local<String> name,
6362 Local<Value> value,
6363 const AccessorInfo& info) {
6364 info.This()->ForceSet(name, value);
6365}
6366
6367
6368THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
6369 v8::HandleScope scope;
6370 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6371 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6372 templ->SetAccessor(v8_str("y"), Return239);
6373 LocalContext context;
6374 context->Global()->Set(v8_str("o"), templ->NewInstance());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006375
6376 // Check the case when receiver and interceptor's holder
6377 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006378 v8::Handle<Value> value = CompileRun(
6379 "var result = 0;"
6380 "for (var i = 0; i < 7; i++) {"
6381 " result = o.y;"
6382 "}");
6383 CHECK_EQ(239, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006384
6385 // Check the case when interceptor's holder is in proto chain
6386 // of receiver.
6387 value = CompileRun(
6388 "r = { __proto__: o };"
6389 "var result = 0;"
6390 "for (var i = 0; i < 7; i++) {"
6391 " result = r.y;"
6392 "}");
6393 CHECK_EQ(239, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006394}
6395
6396
6397THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
6398 v8::HandleScope scope;
6399 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6400 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6401 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6402 templ_p->SetAccessor(v8_str("y"), Return239);
6403
6404 LocalContext context;
6405 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6406 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6407
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006408 // Check the case when receiver and interceptor's holder
6409 // are the same objects.
Steve Blocka7e24c12009-10-30 11:49:00 +00006410 v8::Handle<Value> value = CompileRun(
6411 "o.__proto__ = p;"
6412 "var result = 0;"
6413 "for (var i = 0; i < 7; i++) {"
6414 " result = o.x + o.y;"
6415 "}");
6416 CHECK_EQ(239 + 42, value->Int32Value());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01006417
6418 // Check the case when interceptor's holder is in proto chain
6419 // of receiver.
6420 value = CompileRun(
6421 "r = { __proto__: o };"
6422 "var result = 0;"
6423 "for (var i = 0; i < 7; i++) {"
6424 " result = r.x + r.y;"
6425 "}");
6426 CHECK_EQ(239 + 42, value->Int32Value());
Steve Blocka7e24c12009-10-30 11:49:00 +00006427}
6428
6429
6430THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
6431 v8::HandleScope scope;
6432 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6433 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6434 templ->SetAccessor(v8_str("y"), Return239);
6435
6436 LocalContext context;
6437 context->Global()->Set(v8_str("o"), templ->NewInstance());
6438
6439 v8::Handle<Value> value = CompileRun(
6440 "fst = new Object(); fst.__proto__ = o;"
6441 "snd = new Object(); snd.__proto__ = fst;"
6442 "var result1 = 0;"
6443 "for (var i = 0; i < 7; i++) {"
6444 " result1 = snd.x;"
6445 "}"
6446 "fst.x = 239;"
6447 "var result = 0;"
6448 "for (var i = 0; i < 7; i++) {"
6449 " result = snd.x;"
6450 "}"
6451 "result + result1");
6452 CHECK_EQ(239 + 42, value->Int32Value());
6453}
6454
6455
6456// Test the case when we stored callback into
6457// a stub, but interceptor produced value on its own.
6458THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
6459 v8::HandleScope scope;
6460 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6461 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6462 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6463 templ_p->SetAccessor(v8_str("y"), Return239);
6464
6465 LocalContext context;
6466 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6467 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6468
6469 v8::Handle<Value> value = CompileRun(
6470 "o.__proto__ = p;"
6471 "for (var i = 0; i < 7; i++) {"
6472 " o.x;"
6473 // Now it should be ICed and keep a reference to x defined on p
6474 "}"
6475 "var result = 0;"
6476 "for (var i = 0; i < 7; i++) {"
6477 " result += o.x;"
6478 "}"
6479 "result");
6480 CHECK_EQ(42 * 7, value->Int32Value());
6481}
6482
6483
6484// Test the case when we stored callback into
6485// a stub, but it got invalidated later on.
6486THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
6487 v8::HandleScope scope;
6488 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6489 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6490 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6491 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6492
6493 LocalContext context;
6494 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6495 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6496
6497 v8::Handle<Value> value = CompileRun(
6498 "inbetween = new Object();"
6499 "o.__proto__ = inbetween;"
6500 "inbetween.__proto__ = p;"
6501 "for (var i = 0; i < 10; i++) {"
6502 " o.y;"
6503 // Now it should be ICed and keep a reference to y defined on p
6504 "}"
6505 "inbetween.y = 42;"
6506 "var result = 0;"
6507 "for (var i = 0; i < 10; i++) {"
6508 " result += o.y;"
6509 "}"
6510 "result");
6511 CHECK_EQ(42 * 10, value->Int32Value());
6512}
6513
6514
6515// Test the case when we stored callback into
6516// a stub, but it got invalidated later on due to override on
6517// global object which is between interceptor and callbacks' holders.
6518THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
6519 v8::HandleScope scope;
6520 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6521 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6522 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New();
6523 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis);
6524
6525 LocalContext context;
6526 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6527 context->Global()->Set(v8_str("p"), templ_p->NewInstance());
6528
6529 v8::Handle<Value> value = CompileRun(
6530 "o.__proto__ = this;"
6531 "this.__proto__ = p;"
6532 "for (var i = 0; i < 10; i++) {"
6533 " if (o.y != 239) throw 'oops: ' + o.y;"
6534 // Now it should be ICed and keep a reference to y defined on p
6535 "}"
6536 "this.y = 42;"
6537 "var result = 0;"
6538 "for (var i = 0; i < 10; i++) {"
6539 " result += o.y;"
6540 "}"
6541 "result");
6542 CHECK_EQ(42 * 10, value->Int32Value());
6543}
6544
6545
6546static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name,
6547 const AccessorInfo& info) {
6548 ApiTestFuzzer::Fuzz();
6549 CHECK(v8_str("x")->Equals(name));
6550 return v8::Integer::New(0);
6551}
6552
6553
6554THREADED_TEST(InterceptorReturningZero) {
6555 CheckInterceptorLoadIC(InterceptorLoadICGetter0,
6556 "o.x == undefined ? 1 : 0",
6557 0);
6558}
6559
6560
6561static v8::Handle<Value> InterceptorStoreICSetter(
6562 Local<String> key, Local<Value> value, const AccessorInfo&) {
6563 CHECK(v8_str("x")->Equals(key));
6564 CHECK_EQ(42, value->Int32Value());
6565 return value;
6566}
6567
6568
6569// This test should hit the store IC for the interceptor case.
6570THREADED_TEST(InterceptorStoreIC) {
6571 v8::HandleScope scope;
6572 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6573 templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
6574 InterceptorStoreICSetter);
6575 LocalContext context;
6576 context->Global()->Set(v8_str("o"), templ->NewInstance());
6577 v8::Handle<Value> value = CompileRun(
6578 "for (var i = 0; i < 1000; i++) {"
6579 " o.x = 42;"
6580 "}");
6581}
6582
6583
6584THREADED_TEST(InterceptorStoreICWithNoSetter) {
6585 v8::HandleScope scope;
6586 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6587 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
6588 LocalContext context;
6589 context->Global()->Set(v8_str("o"), templ->NewInstance());
6590 v8::Handle<Value> value = CompileRun(
6591 "for (var i = 0; i < 1000; i++) {"
6592 " o.y = 239;"
6593 "}"
6594 "42 + o.y");
6595 CHECK_EQ(239 + 42, value->Int32Value());
6596}
6597
6598
6599
6600
6601v8::Handle<Value> call_ic_function;
6602v8::Handle<Value> call_ic_function2;
6603v8::Handle<Value> call_ic_function3;
6604
6605static v8::Handle<Value> InterceptorCallICGetter(Local<String> name,
6606 const AccessorInfo& info) {
6607 ApiTestFuzzer::Fuzz();
6608 CHECK(v8_str("x")->Equals(name));
6609 return call_ic_function;
6610}
6611
6612
6613// This test should hit the call IC for the interceptor case.
6614THREADED_TEST(InterceptorCallIC) {
6615 v8::HandleScope scope;
6616 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6617 templ->SetNamedPropertyHandler(InterceptorCallICGetter);
6618 LocalContext context;
6619 context->Global()->Set(v8_str("o"), templ->NewInstance());
6620 call_ic_function =
6621 v8_compile("function f(x) { return x + 1; }; f")->Run();
6622 v8::Handle<Value> value = CompileRun(
6623 "var result = 0;"
6624 "for (var i = 0; i < 1000; i++) {"
6625 " result = o.x(41);"
6626 "}");
6627 CHECK_EQ(42, value->Int32Value());
6628}
6629
6630
6631// This test checks that if interceptor doesn't provide
6632// a value, we can fetch regular value.
6633THREADED_TEST(InterceptorCallICSeesOthers) {
6634 v8::HandleScope scope;
6635 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6636 templ->SetNamedPropertyHandler(NoBlockGetterX);
6637 LocalContext context;
6638 context->Global()->Set(v8_str("o"), templ->NewInstance());
6639 v8::Handle<Value> value = CompileRun(
6640 "o.x = function f(x) { return x + 1; };"
6641 "var result = 0;"
6642 "for (var i = 0; i < 7; i++) {"
6643 " result = o.x(41);"
6644 "}");
6645 CHECK_EQ(42, value->Int32Value());
6646}
6647
6648
6649static v8::Handle<Value> call_ic_function4;
6650static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name,
6651 const AccessorInfo& info) {
6652 ApiTestFuzzer::Fuzz();
6653 CHECK(v8_str("x")->Equals(name));
6654 return call_ic_function4;
6655}
6656
6657
6658// This test checks that if interceptor provides a function,
6659// even if we cached shadowed variant, interceptor's function
6660// is invoked
6661THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
6662 v8::HandleScope scope;
6663 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6664 templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
6665 LocalContext context;
6666 context->Global()->Set(v8_str("o"), templ->NewInstance());
6667 call_ic_function4 =
6668 v8_compile("function f(x) { return x - 1; }; f")->Run();
6669 v8::Handle<Value> value = CompileRun(
6670 "o.__proto__.x = function(x) { return x + 1; };"
6671 "var result = 0;"
6672 "for (var i = 0; i < 1000; i++) {"
6673 " result = o.x(42);"
6674 "}");
6675 CHECK_EQ(41, value->Int32Value());
6676}
6677
6678
6679// Test the case when we stored cacheable lookup into
6680// a stub, but it got invalidated later on
6681THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
6682 v8::HandleScope scope;
6683 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6684 templ->SetNamedPropertyHandler(NoBlockGetterX);
6685 LocalContext context;
6686 context->Global()->Set(v8_str("o"), templ->NewInstance());
6687 v8::Handle<Value> value = CompileRun(
6688 "proto1 = new Object();"
6689 "proto2 = new Object();"
6690 "o.__proto__ = proto1;"
6691 "proto1.__proto__ = proto2;"
6692 "proto2.y = function(x) { return x + 1; };"
6693 // Invoke it many times to compile a stub
6694 "for (var i = 0; i < 7; i++) {"
6695 " o.y(42);"
6696 "}"
6697 "proto1.y = function(x) { return x - 1; };"
6698 "var result = 0;"
6699 "for (var i = 0; i < 7; i++) {"
6700 " result += o.y(42);"
6701 "}");
6702 CHECK_EQ(41 * 7, value->Int32Value());
6703}
6704
6705
6706static v8::Handle<Value> call_ic_function5;
6707static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name,
6708 const AccessorInfo& info) {
6709 ApiTestFuzzer::Fuzz();
6710 if (v8_str("x")->Equals(name))
6711 return call_ic_function5;
6712 else
6713 return Local<Value>();
6714}
6715
6716
6717// This test checks that if interceptor doesn't provide a function,
6718// cached constant function is used
6719THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
6720 v8::HandleScope scope;
6721 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6722 templ->SetNamedPropertyHandler(NoBlockGetterX);
6723 LocalContext context;
6724 context->Global()->Set(v8_str("o"), templ->NewInstance());
6725 v8::Handle<Value> value = CompileRun(
6726 "function inc(x) { return x + 1; };"
6727 "inc(1);"
6728 "o.x = inc;"
6729 "var result = 0;"
6730 "for (var i = 0; i < 1000; i++) {"
6731 " result = o.x(42);"
6732 "}");
6733 CHECK_EQ(43, value->Int32Value());
6734}
6735
6736
6737// This test checks that if interceptor provides a function,
6738// even if we cached constant function, interceptor's function
6739// is invoked
6740THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
6741 v8::HandleScope scope;
6742 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6743 templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
6744 LocalContext context;
6745 context->Global()->Set(v8_str("o"), templ->NewInstance());
6746 call_ic_function5 =
6747 v8_compile("function f(x) { return x - 1; }; f")->Run();
6748 v8::Handle<Value> value = CompileRun(
6749 "function inc(x) { return x + 1; };"
6750 "inc(1);"
6751 "o.x = inc;"
6752 "var result = 0;"
6753 "for (var i = 0; i < 1000; i++) {"
6754 " result = o.x(42);"
6755 "}");
6756 CHECK_EQ(41, value->Int32Value());
6757}
6758
6759
6760// Test the case when we stored constant function into
6761// a stub, but it got invalidated later on
6762THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
6763 v8::HandleScope scope;
6764 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6765 templ->SetNamedPropertyHandler(NoBlockGetterX);
6766 LocalContext context;
6767 context->Global()->Set(v8_str("o"), templ->NewInstance());
6768 v8::Handle<Value> value = CompileRun(
6769 "function inc(x) { return x + 1; };"
6770 "inc(1);"
6771 "proto1 = new Object();"
6772 "proto2 = new Object();"
6773 "o.__proto__ = proto1;"
6774 "proto1.__proto__ = proto2;"
6775 "proto2.y = inc;"
6776 // Invoke it many times to compile a stub
6777 "for (var i = 0; i < 7; i++) {"
6778 " o.y(42);"
6779 "}"
6780 "proto1.y = function(x) { return x - 1; };"
6781 "var result = 0;"
6782 "for (var i = 0; i < 7; i++) {"
6783 " result += o.y(42);"
6784 "}");
6785 CHECK_EQ(41 * 7, value->Int32Value());
6786}
6787
6788
6789// Test the case when we stored constant function into
6790// a stub, but it got invalidated later on due to override on
6791// global object which is between interceptor and constant function' holders.
6792THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
6793 v8::HandleScope scope;
6794 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
6795 templ->SetNamedPropertyHandler(NoBlockGetterX);
6796 LocalContext context;
6797 context->Global()->Set(v8_str("o"), templ->NewInstance());
6798 v8::Handle<Value> value = CompileRun(
6799 "function inc(x) { return x + 1; };"
6800 "inc(1);"
6801 "o.__proto__ = this;"
6802 "this.__proto__.y = inc;"
6803 // Invoke it many times to compile a stub
6804 "for (var i = 0; i < 7; i++) {"
6805 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);"
6806 "}"
6807 "this.y = function(x) { return x - 1; };"
6808 "var result = 0;"
6809 "for (var i = 0; i < 7; i++) {"
6810 " result += o.y(42);"
6811 "}");
6812 CHECK_EQ(41 * 7, value->Int32Value());
6813}
6814
6815
Leon Clarke4515c472010-02-03 11:58:03 +00006816// Test the case when actual function to call sits on global object.
6817THREADED_TEST(InterceptorCallICCachedFromGlobal) {
6818 v8::HandleScope scope;
6819 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
6820 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
6821
6822 LocalContext context;
6823 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
6824
6825 v8::Handle<Value> value = CompileRun(
6826 "try {"
6827 " o.__proto__ = this;"
6828 " for (var i = 0; i < 10; i++) {"
6829 " var v = o.parseFloat('239');"
6830 " if (v != 239) throw v;"
6831 // Now it should be ICed and keep a reference to parseFloat.
6832 " }"
6833 " var result = 0;"
6834 " for (var i = 0; i < 10; i++) {"
6835 " result += o.parseFloat('239');"
6836 " }"
6837 " result"
6838 "} catch(e) {"
6839 " e"
6840 "};");
6841 CHECK_EQ(239 * 10, value->Int32Value());
6842}
6843
Andrei Popescu402d9372010-02-26 13:31:12 +00006844static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
6845 const AccessorInfo& info) {
6846 ApiTestFuzzer::Fuzz();
6847 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
6848 ++(*call_count);
6849 if ((*call_count) % 20 == 0) {
Steve Block8defd9f2010-07-08 12:39:36 +01006850 i::Heap::CollectAllGarbage(true);
Andrei Popescu402d9372010-02-26 13:31:12 +00006851 }
6852 return v8::Handle<Value>();
6853}
6854
6855static v8::Handle<Value> FastApiCallback_TrivialSignature(
6856 const v8::Arguments& args) {
6857 ApiTestFuzzer::Fuzz();
6858 CHECK_EQ(args.This(), args.Holder());
6859 CHECK(args.Data()->Equals(v8_str("method_data")));
6860 return v8::Integer::New(args[0]->Int32Value() + 1);
6861}
6862
6863static v8::Handle<Value> FastApiCallback_SimpleSignature(
6864 const v8::Arguments& args) {
6865 ApiTestFuzzer::Fuzz();
6866 CHECK_EQ(args.This()->GetPrototype(), args.Holder());
6867 CHECK(args.Data()->Equals(v8_str("method_data")));
6868 // Note, we're using HasRealNamedProperty instead of Has to avoid
6869 // invoking the interceptor again.
6870 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
6871 return v8::Integer::New(args[0]->Int32Value() + 1);
6872}
6873
6874// Helper to maximize the odds of object moving.
6875static void GenerateSomeGarbage() {
6876 CompileRun(
6877 "var garbage;"
6878 "for (var i = 0; i < 1000; i++) {"
6879 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
6880 "}"
6881 "garbage = undefined;");
6882}
6883
6884THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
6885 int interceptor_call_count = 0;
6886 v8::HandleScope scope;
6887 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6888 v8::Handle<v8::FunctionTemplate> method_templ =
6889 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
6890 v8_str("method_data"),
6891 v8::Handle<v8::Signature>());
6892 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6893 proto_templ->Set(v8_str("method"), method_templ);
6894 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6895 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6896 NULL, NULL, NULL, NULL,
6897 v8::External::Wrap(&interceptor_call_count));
6898 LocalContext context;
6899 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6900 GenerateSomeGarbage();
6901 context->Global()->Set(v8_str("o"), fun->NewInstance());
6902 v8::Handle<Value> value = CompileRun(
6903 "var result = 0;"
6904 "for (var i = 0; i < 100; i++) {"
6905 " result = o.method(41);"
6906 "}");
6907 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6908 CHECK_EQ(100, interceptor_call_count);
6909}
6910
6911THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
6912 int interceptor_call_count = 0;
6913 v8::HandleScope scope;
6914 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6915 v8::Handle<v8::FunctionTemplate> method_templ =
6916 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6917 v8_str("method_data"),
6918 v8::Signature::New(fun_templ));
6919 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6920 proto_templ->Set(v8_str("method"), method_templ);
6921 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6922 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6923 NULL, NULL, NULL, NULL,
6924 v8::External::Wrap(&interceptor_call_count));
6925 LocalContext context;
6926 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6927 GenerateSomeGarbage();
6928 context->Global()->Set(v8_str("o"), fun->NewInstance());
6929 v8::Handle<Value> value = CompileRun(
6930 "o.foo = 17;"
6931 "var receiver = {};"
6932 "receiver.__proto__ = o;"
6933 "var result = 0;"
6934 "for (var i = 0; i < 100; i++) {"
6935 " result = receiver.method(41);"
6936 "}");
6937 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
6938 CHECK_EQ(100, interceptor_call_count);
6939}
6940
6941THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
6942 int interceptor_call_count = 0;
6943 v8::HandleScope scope;
6944 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6945 v8::Handle<v8::FunctionTemplate> method_templ =
6946 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6947 v8_str("method_data"),
6948 v8::Signature::New(fun_templ));
6949 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6950 proto_templ->Set(v8_str("method"), method_templ);
6951 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6952 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6953 NULL, NULL, NULL, NULL,
6954 v8::External::Wrap(&interceptor_call_count));
6955 LocalContext context;
6956 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6957 GenerateSomeGarbage();
6958 context->Global()->Set(v8_str("o"), fun->NewInstance());
6959 v8::Handle<Value> value = CompileRun(
6960 "o.foo = 17;"
6961 "var receiver = {};"
6962 "receiver.__proto__ = o;"
6963 "var result = 0;"
6964 "var saved_result = 0;"
6965 "for (var i = 0; i < 100; i++) {"
6966 " result = receiver.method(41);"
6967 " if (i == 50) {"
6968 " saved_result = result;"
6969 " receiver = {method: function(x) { return x - 1 }};"
6970 " }"
6971 "}");
6972 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
6973 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
6974 CHECK_GE(interceptor_call_count, 50);
6975}
6976
6977THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
6978 int interceptor_call_count = 0;
6979 v8::HandleScope scope;
6980 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
6981 v8::Handle<v8::FunctionTemplate> method_templ =
6982 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
6983 v8_str("method_data"),
6984 v8::Signature::New(fun_templ));
6985 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
6986 proto_templ->Set(v8_str("method"), method_templ);
6987 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
6988 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
6989 NULL, NULL, NULL, NULL,
6990 v8::External::Wrap(&interceptor_call_count));
6991 LocalContext context;
6992 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
6993 GenerateSomeGarbage();
6994 context->Global()->Set(v8_str("o"), fun->NewInstance());
6995 v8::Handle<Value> value = CompileRun(
6996 "o.foo = 17;"
6997 "var receiver = {};"
6998 "receiver.__proto__ = o;"
6999 "var result = 0;"
7000 "var saved_result = 0;"
7001 "for (var i = 0; i < 100; i++) {"
7002 " result = receiver.method(41);"
7003 " if (i == 50) {"
7004 " saved_result = result;"
7005 " o.method = function(x) { return x - 1 };"
7006 " }"
7007 "}");
7008 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7009 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7010 CHECK_GE(interceptor_call_count, 50);
7011}
7012
Steve Block6ded16b2010-05-10 14:33:55 +01007013THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
7014 int interceptor_call_count = 0;
7015 v8::HandleScope scope;
7016 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7017 v8::Handle<v8::FunctionTemplate> method_templ =
7018 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7019 v8_str("method_data"),
7020 v8::Signature::New(fun_templ));
7021 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7022 proto_templ->Set(v8_str("method"), method_templ);
7023 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7024 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7025 NULL, NULL, NULL, NULL,
7026 v8::External::Wrap(&interceptor_call_count));
7027 LocalContext context;
7028 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7029 GenerateSomeGarbage();
7030 context->Global()->Set(v8_str("o"), fun->NewInstance());
7031 v8::TryCatch try_catch;
7032 v8::Handle<Value> value = CompileRun(
7033 "o.foo = 17;"
7034 "var receiver = {};"
7035 "receiver.__proto__ = o;"
7036 "var result = 0;"
7037 "var saved_result = 0;"
7038 "for (var i = 0; i < 100; i++) {"
7039 " result = receiver.method(41);"
7040 " if (i == 50) {"
7041 " saved_result = result;"
7042 " receiver = 333;"
7043 " }"
7044 "}");
7045 CHECK(try_catch.HasCaught());
7046 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7047 try_catch.Exception()->ToString());
7048 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7049 CHECK_GE(interceptor_call_count, 50);
7050}
7051
Andrei Popescu402d9372010-02-26 13:31:12 +00007052THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
7053 int interceptor_call_count = 0;
7054 v8::HandleScope scope;
7055 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7056 v8::Handle<v8::FunctionTemplate> method_templ =
7057 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7058 v8_str("method_data"),
7059 v8::Signature::New(fun_templ));
7060 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7061 proto_templ->Set(v8_str("method"), method_templ);
7062 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7063 templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
7064 NULL, NULL, NULL, NULL,
7065 v8::External::Wrap(&interceptor_call_count));
7066 LocalContext context;
7067 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7068 GenerateSomeGarbage();
7069 context->Global()->Set(v8_str("o"), fun->NewInstance());
7070 v8::TryCatch try_catch;
7071 v8::Handle<Value> value = CompileRun(
7072 "o.foo = 17;"
7073 "var receiver = {};"
7074 "receiver.__proto__ = o;"
7075 "var result = 0;"
7076 "var saved_result = 0;"
7077 "for (var i = 0; i < 100; i++) {"
7078 " result = receiver.method(41);"
7079 " if (i == 50) {"
7080 " saved_result = result;"
7081 " receiver = {method: receiver.method};"
7082 " }"
7083 "}");
7084 CHECK(try_catch.HasCaught());
7085 CHECK_EQ(v8_str("TypeError: Illegal invocation"),
7086 try_catch.Exception()->ToString());
7087 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7088 CHECK_GE(interceptor_call_count, 50);
7089}
7090
7091THREADED_TEST(CallICFastApi_TrivialSignature) {
7092 v8::HandleScope scope;
7093 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7094 v8::Handle<v8::FunctionTemplate> method_templ =
7095 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
7096 v8_str("method_data"),
7097 v8::Handle<v8::Signature>());
7098 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7099 proto_templ->Set(v8_str("method"), method_templ);
7100 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7101 LocalContext context;
7102 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7103 GenerateSomeGarbage();
7104 context->Global()->Set(v8_str("o"), fun->NewInstance());
7105 v8::Handle<Value> value = CompileRun(
7106 "var result = 0;"
7107 "for (var i = 0; i < 100; i++) {"
7108 " result = o.method(41);"
7109 "}");
7110
7111 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7112}
7113
7114THREADED_TEST(CallICFastApi_SimpleSignature) {
7115 v8::HandleScope scope;
7116 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7117 v8::Handle<v8::FunctionTemplate> method_templ =
7118 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7119 v8_str("method_data"),
7120 v8::Signature::New(fun_templ));
7121 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7122 proto_templ->Set(v8_str("method"), method_templ);
7123 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7124 LocalContext context;
7125 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7126 GenerateSomeGarbage();
7127 context->Global()->Set(v8_str("o"), fun->NewInstance());
7128 v8::Handle<Value> value = CompileRun(
7129 "o.foo = 17;"
7130 "var receiver = {};"
7131 "receiver.__proto__ = o;"
7132 "var result = 0;"
7133 "for (var i = 0; i < 100; i++) {"
7134 " result = receiver.method(41);"
7135 "}");
7136
7137 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
7138}
7139
Steve Block6ded16b2010-05-10 14:33:55 +01007140THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) {
Andrei Popescu402d9372010-02-26 13:31:12 +00007141 v8::HandleScope scope;
7142 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7143 v8::Handle<v8::FunctionTemplate> method_templ =
7144 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7145 v8_str("method_data"),
7146 v8::Signature::New(fun_templ));
7147 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7148 proto_templ->Set(v8_str("method"), method_templ);
7149 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7150 LocalContext context;
7151 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7152 GenerateSomeGarbage();
7153 context->Global()->Set(v8_str("o"), fun->NewInstance());
7154 v8::Handle<Value> value = CompileRun(
7155 "o.foo = 17;"
7156 "var receiver = {};"
7157 "receiver.__proto__ = o;"
7158 "var result = 0;"
7159 "var saved_result = 0;"
7160 "for (var i = 0; i < 100; i++) {"
7161 " result = receiver.method(41);"
7162 " if (i == 50) {"
7163 " saved_result = result;"
7164 " receiver = {method: function(x) { return x - 1 }};"
7165 " }"
7166 "}");
7167 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
7168 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7169}
7170
Steve Block6ded16b2010-05-10 14:33:55 +01007171THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
7172 v8::HandleScope scope;
7173 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
7174 v8::Handle<v8::FunctionTemplate> method_templ =
7175 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
7176 v8_str("method_data"),
7177 v8::Signature::New(fun_templ));
7178 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
7179 proto_templ->Set(v8_str("method"), method_templ);
7180 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
7181 LocalContext context;
7182 v8::Handle<v8::Function> fun = fun_templ->GetFunction();
7183 GenerateSomeGarbage();
7184 context->Global()->Set(v8_str("o"), fun->NewInstance());
7185 v8::TryCatch try_catch;
7186 v8::Handle<Value> value = CompileRun(
7187 "o.foo = 17;"
7188 "var receiver = {};"
7189 "receiver.__proto__ = o;"
7190 "var result = 0;"
7191 "var saved_result = 0;"
7192 "for (var i = 0; i < 100; i++) {"
7193 " result = receiver.method(41);"
7194 " if (i == 50) {"
7195 " saved_result = result;"
7196 " receiver = 333;"
7197 " }"
7198 "}");
7199 CHECK(try_catch.HasCaught());
7200 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"),
7201 try_catch.Exception()->ToString());
7202 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7203}
7204
Leon Clarke4515c472010-02-03 11:58:03 +00007205
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007206v8::Handle<Value> keyed_call_ic_function;
7207
7208static v8::Handle<Value> InterceptorKeyedCallICGetter(
7209 Local<String> name, const AccessorInfo& info) {
7210 ApiTestFuzzer::Fuzz();
7211 if (v8_str("x")->Equals(name)) {
7212 return keyed_call_ic_function;
7213 }
7214 return v8::Handle<Value>();
7215}
7216
7217
7218// Test the case when we stored cacheable lookup into
7219// a stub, but the function name changed (to another cacheable function).
7220THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
7221 v8::HandleScope scope;
7222 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7223 templ->SetNamedPropertyHandler(NoBlockGetterX);
7224 LocalContext context;
7225 context->Global()->Set(v8_str("o"), templ->NewInstance());
7226 v8::Handle<Value> value = CompileRun(
7227 "proto = new Object();"
7228 "proto.y = function(x) { return x + 1; };"
7229 "proto.z = function(x) { return x - 1; };"
7230 "o.__proto__ = proto;"
7231 "var result = 0;"
7232 "var method = 'y';"
7233 "for (var i = 0; i < 10; i++) {"
7234 " if (i == 5) { method = 'z'; };"
7235 " result += o[method](41);"
7236 "}");
7237 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7238}
7239
7240
7241// Test the case when we stored cacheable lookup into
7242// a stub, but the function name changed (and the new function is present
7243// both before and after the interceptor in the prototype chain).
7244THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
7245 v8::HandleScope scope;
7246 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7247 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
7248 LocalContext context;
7249 context->Global()->Set(v8_str("proto1"), templ->NewInstance());
7250 keyed_call_ic_function =
7251 v8_compile("function f(x) { return x - 1; }; f")->Run();
7252 v8::Handle<Value> value = CompileRun(
7253 "o = new Object();"
7254 "proto2 = new Object();"
7255 "o.y = function(x) { return x + 1; };"
7256 "proto2.y = function(x) { return x + 2; };"
7257 "o.__proto__ = proto1;"
7258 "proto1.__proto__ = proto2;"
7259 "var result = 0;"
7260 "var method = 'x';"
7261 "for (var i = 0; i < 10; i++) {"
7262 " if (i == 5) { method = 'y'; };"
7263 " result += o[method](41);"
7264 "}");
7265 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7266}
7267
7268
7269// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
7270// on the global object.
7271THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
7272 v8::HandleScope scope;
7273 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7274 templ->SetNamedPropertyHandler(NoBlockGetterX);
7275 LocalContext context;
7276 context->Global()->Set(v8_str("o"), templ->NewInstance());
7277 v8::Handle<Value> value = CompileRun(
7278 "function inc(x) { return x + 1; };"
7279 "inc(1);"
7280 "function dec(x) { return x - 1; };"
7281 "dec(1);"
7282 "o.__proto__ = this;"
7283 "this.__proto__.x = inc;"
7284 "this.__proto__.y = dec;"
7285 "var result = 0;"
7286 "var method = 'x';"
7287 "for (var i = 0; i < 10; i++) {"
7288 " if (i == 5) { method = 'y'; };"
7289 " result += o[method](41);"
7290 "}");
7291 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7292}
7293
7294
7295// Test the case when actual function to call sits on global object.
7296THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
7297 v8::HandleScope scope;
7298 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7299 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7300 LocalContext context;
7301 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7302
7303 v8::Handle<Value> value = CompileRun(
7304 "function len(x) { return x.length; };"
7305 "o.__proto__ = this;"
7306 "var m = 'parseFloat';"
7307 "var result = 0;"
7308 "for (var i = 0; i < 10; i++) {"
7309 " if (i == 5) {"
7310 " m = 'len';"
7311 " saved_result = result;"
7312 " };"
7313 " result = o[m]('239');"
7314 "}");
7315 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
7316 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
7317}
7318
7319// Test the map transition before the interceptor.
7320THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
7321 v8::HandleScope scope;
7322 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7323 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7324 LocalContext context;
7325 context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
7326
7327 v8::Handle<Value> value = CompileRun(
7328 "var o = new Object();"
7329 "o.__proto__ = proto;"
7330 "o.method = function(x) { return x + 1; };"
7331 "var m = 'method';"
7332 "var result = 0;"
7333 "for (var i = 0; i < 10; i++) {"
7334 " if (i == 5) { o.method = function(x) { return x - 1; }; };"
7335 " result += o[m](41);"
7336 "}");
7337 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7338}
7339
7340
7341// Test the map transition after the interceptor.
7342THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
7343 v8::HandleScope scope;
7344 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
7345 templ_o->SetNamedPropertyHandler(NoBlockGetterX);
7346 LocalContext context;
7347 context->Global()->Set(v8_str("o"), templ_o->NewInstance());
7348
7349 v8::Handle<Value> value = CompileRun(
7350 "var proto = new Object();"
7351 "o.__proto__ = proto;"
7352 "proto.method = function(x) { return x + 1; };"
7353 "var m = 'method';"
7354 "var result = 0;"
7355 "for (var i = 0; i < 10; i++) {"
7356 " if (i == 5) { proto.method = function(x) { return x - 1; }; };"
7357 " result += o[m](41);"
7358 "}");
7359 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
7360}
7361
7362
Steve Blocka7e24c12009-10-30 11:49:00 +00007363static int interceptor_call_count = 0;
7364
7365static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
7366 const AccessorInfo& info) {
7367 ApiTestFuzzer::Fuzz();
7368 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
7369 return call_ic_function2;
7370 }
7371 return v8::Handle<Value>();
7372}
7373
7374
7375// This test should hit load and call ICs for the interceptor case.
7376// Once in a while, the interceptor will reply that a property was not
7377// found in which case we should get a reference error.
7378THREADED_TEST(InterceptorICReferenceErrors) {
7379 v8::HandleScope scope;
7380 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7381 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
7382 LocalContext context(0, templ, v8::Handle<Value>());
7383 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
7384 v8::Handle<Value> value = CompileRun(
7385 "function f() {"
7386 " for (var i = 0; i < 1000; i++) {"
7387 " try { x; } catch(e) { return true; }"
7388 " }"
7389 " return false;"
7390 "};"
7391 "f();");
7392 CHECK_EQ(true, value->BooleanValue());
7393 interceptor_call_count = 0;
7394 value = CompileRun(
7395 "function g() {"
7396 " for (var i = 0; i < 1000; i++) {"
7397 " try { x(42); } catch(e) { return true; }"
7398 " }"
7399 " return false;"
7400 "};"
7401 "g();");
7402 CHECK_EQ(true, value->BooleanValue());
7403}
7404
7405
7406static int interceptor_ic_exception_get_count = 0;
7407
7408static v8::Handle<Value> InterceptorICExceptionGetter(
7409 Local<String> name,
7410 const AccessorInfo& info) {
7411 ApiTestFuzzer::Fuzz();
7412 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
7413 return call_ic_function3;
7414 }
7415 if (interceptor_ic_exception_get_count == 20) {
7416 return v8::ThrowException(v8_num(42));
7417 }
7418 // Do not handle get for properties other than x.
7419 return v8::Handle<Value>();
7420}
7421
7422// Test interceptor load/call IC where the interceptor throws an
7423// exception once in a while.
7424THREADED_TEST(InterceptorICGetterExceptions) {
7425 interceptor_ic_exception_get_count = 0;
7426 v8::HandleScope scope;
7427 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7428 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
7429 LocalContext context(0, templ, v8::Handle<Value>());
7430 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
7431 v8::Handle<Value> value = CompileRun(
7432 "function f() {"
7433 " for (var i = 0; i < 100; i++) {"
7434 " try { x; } catch(e) { return true; }"
7435 " }"
7436 " return false;"
7437 "};"
7438 "f();");
7439 CHECK_EQ(true, value->BooleanValue());
7440 interceptor_ic_exception_get_count = 0;
7441 value = CompileRun(
7442 "function f() {"
7443 " for (var i = 0; i < 100; i++) {"
7444 " try { x(42); } catch(e) { return true; }"
7445 " }"
7446 " return false;"
7447 "};"
7448 "f();");
7449 CHECK_EQ(true, value->BooleanValue());
7450}
7451
7452
7453static int interceptor_ic_exception_set_count = 0;
7454
7455static v8::Handle<Value> InterceptorICExceptionSetter(
7456 Local<String> key, Local<Value> value, const AccessorInfo&) {
7457 ApiTestFuzzer::Fuzz();
7458 if (++interceptor_ic_exception_set_count > 20) {
7459 return v8::ThrowException(v8_num(42));
7460 }
7461 // Do not actually handle setting.
7462 return v8::Handle<Value>();
7463}
7464
7465// Test interceptor store IC where the interceptor throws an exception
7466// once in a while.
7467THREADED_TEST(InterceptorICSetterExceptions) {
7468 interceptor_ic_exception_set_count = 0;
7469 v8::HandleScope scope;
7470 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7471 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
7472 LocalContext context(0, templ, v8::Handle<Value>());
7473 v8::Handle<Value> value = CompileRun(
7474 "function f() {"
7475 " for (var i = 0; i < 100; i++) {"
7476 " try { x = 42; } catch(e) { return true; }"
7477 " }"
7478 " return false;"
7479 "};"
7480 "f();");
7481 CHECK_EQ(true, value->BooleanValue());
7482}
7483
7484
7485// Test that we ignore null interceptors.
7486THREADED_TEST(NullNamedInterceptor) {
7487 v8::HandleScope scope;
7488 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7489 templ->SetNamedPropertyHandler(0);
7490 LocalContext context;
7491 templ->Set("x", v8_num(42));
7492 v8::Handle<v8::Object> obj = templ->NewInstance();
7493 context->Global()->Set(v8_str("obj"), obj);
7494 v8::Handle<Value> value = CompileRun("obj.x");
7495 CHECK(value->IsInt32());
7496 CHECK_EQ(42, value->Int32Value());
7497}
7498
7499
7500// Test that we ignore null interceptors.
7501THREADED_TEST(NullIndexedInterceptor) {
7502 v8::HandleScope scope;
7503 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
7504 templ->SetIndexedPropertyHandler(0);
7505 LocalContext context;
7506 templ->Set("42", v8_num(42));
7507 v8::Handle<v8::Object> obj = templ->NewInstance();
7508 context->Global()->Set(v8_str("obj"), obj);
7509 v8::Handle<Value> value = CompileRun("obj[42]");
7510 CHECK(value->IsInt32());
7511 CHECK_EQ(42, value->Int32Value());
7512}
7513
7514
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01007515THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
7516 v8::HandleScope scope;
7517 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7518 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
7519 LocalContext env;
7520 env->Global()->Set(v8_str("obj"),
7521 templ->GetFunction()->NewInstance());
7522 ExpectTrue("obj.x === 42");
7523 ExpectTrue("!obj.propertyIsEnumerable('x')");
7524}
7525
7526
Steve Blocka7e24c12009-10-30 11:49:00 +00007527static v8::Handle<Value> ParentGetter(Local<String> name,
7528 const AccessorInfo& info) {
7529 ApiTestFuzzer::Fuzz();
7530 return v8_num(1);
7531}
7532
7533
7534static v8::Handle<Value> ChildGetter(Local<String> name,
7535 const AccessorInfo& info) {
7536 ApiTestFuzzer::Fuzz();
7537 return v8_num(42);
7538}
7539
7540
7541THREADED_TEST(Overriding) {
7542 v8::HandleScope scope;
7543 LocalContext context;
7544
7545 // Parent template.
7546 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New();
7547 Local<ObjectTemplate> parent_instance_templ =
7548 parent_templ->InstanceTemplate();
7549 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter);
7550
7551 // Template that inherits from the parent template.
7552 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New();
7553 Local<ObjectTemplate> child_instance_templ =
7554 child_templ->InstanceTemplate();
7555 child_templ->Inherit(parent_templ);
7556 // Override 'f'. The child version of 'f' should get called for child
7557 // instances.
7558 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter);
7559 // Add 'g' twice. The 'g' added last should get called for instances.
7560 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter);
7561 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter);
7562
7563 // Add 'h' as an accessor to the proto template with ReadOnly attributes
7564 // so 'h' can be shadowed on the instance object.
7565 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate();
7566 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0,
7567 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7568
7569 // Add 'i' as an accessor to the instance template with ReadOnly attributes
7570 // but the attribute does not have effect because it is duplicated with
7571 // NULL setter.
7572 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0,
7573 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly);
7574
7575
7576
7577 // Instantiate the child template.
7578 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance();
7579
7580 // Check that the child function overrides the parent one.
7581 context->Global()->Set(v8_str("o"), instance);
7582 Local<Value> value = v8_compile("o.f")->Run();
7583 // Check that the 'g' that was added last is hit.
7584 CHECK_EQ(42, value->Int32Value());
7585 value = v8_compile("o.g")->Run();
7586 CHECK_EQ(42, value->Int32Value());
7587
7588 // Check 'h' can be shadowed.
7589 value = v8_compile("o.h = 3; o.h")->Run();
7590 CHECK_EQ(3, value->Int32Value());
7591
7592 // Check 'i' is cannot be shadowed or changed.
7593 value = v8_compile("o.i = 3; o.i")->Run();
7594 CHECK_EQ(42, value->Int32Value());
7595}
7596
7597
7598static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) {
7599 ApiTestFuzzer::Fuzz();
7600 if (args.IsConstructCall()) {
7601 return v8::Boolean::New(true);
7602 }
7603 return v8::Boolean::New(false);
7604}
7605
7606
7607THREADED_TEST(IsConstructCall) {
7608 v8::HandleScope scope;
7609
7610 // Function template with call handler.
7611 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7612 templ->SetCallHandler(IsConstructHandler);
7613
7614 LocalContext context;
7615
7616 context->Global()->Set(v8_str("f"), templ->GetFunction());
7617 Local<Value> value = v8_compile("f()")->Run();
7618 CHECK(!value->BooleanValue());
7619 value = v8_compile("new f()")->Run();
7620 CHECK(value->BooleanValue());
7621}
7622
7623
7624THREADED_TEST(ObjectProtoToString) {
7625 v8::HandleScope scope;
7626 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
7627 templ->SetClassName(v8_str("MyClass"));
7628
7629 LocalContext context;
7630
7631 Local<String> customized_tostring = v8_str("customized toString");
7632
7633 // Replace Object.prototype.toString
7634 v8_compile("Object.prototype.toString = function() {"
7635 " return 'customized toString';"
7636 "}")->Run();
7637
7638 // Normal ToString call should call replaced Object.prototype.toString
7639 Local<v8::Object> instance = templ->GetFunction()->NewInstance();
7640 Local<String> value = instance->ToString();
7641 CHECK(value->IsString() && value->Equals(customized_tostring));
7642
7643 // ObjectProtoToString should not call replace toString function.
7644 value = instance->ObjectProtoToString();
7645 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
7646
7647 // Check global
7648 value = context->Global()->ObjectProtoToString();
7649 CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
7650
7651 // Check ordinary object
7652 Local<Value> object = v8_compile("new Object()")->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01007653 value = object.As<v8::Object>()->ObjectProtoToString();
Steve Blocka7e24c12009-10-30 11:49:00 +00007654 CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
7655}
7656
7657
7658bool ApiTestFuzzer::fuzzing_ = false;
Steve Block8defd9f2010-07-08 12:39:36 +01007659i::Semaphore* ApiTestFuzzer::all_tests_done_=
7660 i::OS::CreateSemaphore(0);
Steve Blocka7e24c12009-10-30 11:49:00 +00007661int ApiTestFuzzer::active_tests_;
7662int ApiTestFuzzer::tests_being_run_;
7663int ApiTestFuzzer::current_;
7664
7665
7666// We are in a callback and want to switch to another thread (if we
7667// are currently running the thread fuzzing test).
7668void ApiTestFuzzer::Fuzz() {
7669 if (!fuzzing_) return;
7670 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_;
7671 test->ContextSwitch();
7672}
7673
7674
7675// Let the next thread go. Since it is also waiting on the V8 lock it may
7676// not start immediately.
7677bool ApiTestFuzzer::NextThread() {
7678 int test_position = GetNextTestNumber();
Steve Blockd0582a62009-12-15 09:54:21 +00007679 const char* test_name = RegisterThreadedTest::nth(current_)->name();
Steve Blocka7e24c12009-10-30 11:49:00 +00007680 if (test_position == current_) {
Steve Blockd0582a62009-12-15 09:54:21 +00007681 if (kLogThreading)
7682 printf("Stay with %s\n", test_name);
Steve Blocka7e24c12009-10-30 11:49:00 +00007683 return false;
7684 }
Steve Blockd0582a62009-12-15 09:54:21 +00007685 if (kLogThreading) {
7686 printf("Switch from %s to %s\n",
7687 test_name,
7688 RegisterThreadedTest::nth(test_position)->name());
7689 }
Steve Blocka7e24c12009-10-30 11:49:00 +00007690 current_ = test_position;
7691 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal();
7692 return true;
7693}
7694
7695
7696void ApiTestFuzzer::Run() {
7697 // When it is our turn...
7698 gate_->Wait();
7699 {
7700 // ... get the V8 lock and start running the test.
7701 v8::Locker locker;
7702 CallTest();
7703 }
7704 // This test finished.
7705 active_ = false;
7706 active_tests_--;
7707 // If it was the last then signal that fact.
7708 if (active_tests_ == 0) {
7709 all_tests_done_->Signal();
7710 } else {
7711 // Otherwise select a new test and start that.
7712 NextThread();
7713 }
7714}
7715
7716
7717static unsigned linear_congruential_generator;
7718
7719
7720void ApiTestFuzzer::Setup(PartOfTest part) {
7721 linear_congruential_generator = i::FLAG_testing_prng_seed;
7722 fuzzing_ = true;
7723 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1);
7724 int end = (part == FIRST_PART)
7725 ? (RegisterThreadedTest::count() >> 1)
7726 : RegisterThreadedTest::count();
7727 active_tests_ = tests_being_run_ = end - start;
7728 for (int i = 0; i < tests_being_run_; i++) {
7729 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start);
7730 }
7731 for (int i = 0; i < active_tests_; i++) {
7732 RegisterThreadedTest::nth(i)->fuzzer_->Start();
7733 }
7734}
7735
7736
7737static void CallTestNumber(int test_number) {
7738 (RegisterThreadedTest::nth(test_number)->callback())();
7739}
7740
7741
7742void ApiTestFuzzer::RunAllTests() {
7743 // Set off the first test.
7744 current_ = -1;
7745 NextThread();
7746 // Wait till they are all done.
7747 all_tests_done_->Wait();
7748}
7749
7750
7751int ApiTestFuzzer::GetNextTestNumber() {
7752 int next_test;
7753 do {
7754 next_test = (linear_congruential_generator >> 16) % tests_being_run_;
7755 linear_congruential_generator *= 1664525u;
7756 linear_congruential_generator += 1013904223u;
7757 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_);
7758 return next_test;
7759}
7760
7761
7762void ApiTestFuzzer::ContextSwitch() {
7763 // If the new thread is the same as the current thread there is nothing to do.
7764 if (NextThread()) {
7765 // Now it can start.
7766 v8::Unlocker unlocker;
7767 // Wait till someone starts us again.
7768 gate_->Wait();
7769 // And we're off.
7770 }
7771}
7772
7773
7774void ApiTestFuzzer::TearDown() {
7775 fuzzing_ = false;
7776 for (int i = 0; i < RegisterThreadedTest::count(); i++) {
7777 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_;
7778 if (fuzzer != NULL) fuzzer->Join();
7779 }
7780}
7781
7782
7783// Lets not be needlessly self-referential.
7784TEST(Threading) {
7785 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART);
7786 ApiTestFuzzer::RunAllTests();
7787 ApiTestFuzzer::TearDown();
7788}
7789
7790TEST(Threading2) {
7791 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART);
7792 ApiTestFuzzer::RunAllTests();
7793 ApiTestFuzzer::TearDown();
7794}
7795
7796
7797void ApiTestFuzzer::CallTest() {
Steve Blockd0582a62009-12-15 09:54:21 +00007798 if (kLogThreading)
7799 printf("Start test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007800 CallTestNumber(test_number_);
Steve Blockd0582a62009-12-15 09:54:21 +00007801 if (kLogThreading)
7802 printf("End test %d\n", test_number_);
Steve Blocka7e24c12009-10-30 11:49:00 +00007803}
7804
7805
7806static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) {
7807 CHECK(v8::Locker::IsLocked());
7808 ApiTestFuzzer::Fuzz();
7809 v8::Unlocker unlocker;
7810 const char* code = "throw 7;";
7811 {
7812 v8::Locker nested_locker;
7813 v8::HandleScope scope;
7814 v8::Handle<Value> exception;
7815 { v8::TryCatch try_catch;
7816 v8::Handle<Value> value = CompileRun(code);
7817 CHECK(value.IsEmpty());
7818 CHECK(try_catch.HasCaught());
7819 // Make sure to wrap the exception in a new handle because
7820 // the handle returned from the TryCatch is destroyed
7821 // when the TryCatch is destroyed.
7822 exception = Local<Value>::New(try_catch.Exception());
7823 }
7824 return v8::ThrowException(exception);
7825 }
7826}
7827
7828
7829static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) {
7830 CHECK(v8::Locker::IsLocked());
7831 ApiTestFuzzer::Fuzz();
7832 v8::Unlocker unlocker;
7833 const char* code = "throw 7;";
7834 {
7835 v8::Locker nested_locker;
7836 v8::HandleScope scope;
7837 v8::Handle<Value> value = CompileRun(code);
7838 CHECK(value.IsEmpty());
7839 return v8_str("foo");
7840 }
7841}
7842
7843
7844// These are locking tests that don't need to be run again
7845// as part of the locking aggregation tests.
7846TEST(NestedLockers) {
7847 v8::Locker locker;
7848 CHECK(v8::Locker::IsLocked());
7849 v8::HandleScope scope;
7850 LocalContext env;
7851 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS);
7852 Local<Function> fun = fun_templ->GetFunction();
7853 env->Global()->Set(v8_str("throw_in_js"), fun);
7854 Local<Script> script = v8_compile("(function () {"
7855 " try {"
7856 " throw_in_js();"
7857 " return 42;"
7858 " } catch (e) {"
7859 " return e * 13;"
7860 " }"
7861 "})();");
7862 CHECK_EQ(91, script->Run()->Int32Value());
7863}
7864
7865
7866// These are locking tests that don't need to be run again
7867// as part of the locking aggregation tests.
7868TEST(NestedLockersNoTryCatch) {
7869 v8::Locker locker;
7870 v8::HandleScope scope;
7871 LocalContext env;
7872 Local<v8::FunctionTemplate> fun_templ =
7873 v8::FunctionTemplate::New(ThrowInJSNoCatch);
7874 Local<Function> fun = fun_templ->GetFunction();
7875 env->Global()->Set(v8_str("throw_in_js"), fun);
7876 Local<Script> script = v8_compile("(function () {"
7877 " try {"
7878 " throw_in_js();"
7879 " return 42;"
7880 " } catch (e) {"
7881 " return e * 13;"
7882 " }"
7883 "})();");
7884 CHECK_EQ(91, script->Run()->Int32Value());
7885}
7886
7887
7888THREADED_TEST(RecursiveLocking) {
7889 v8::Locker locker;
7890 {
7891 v8::Locker locker2;
7892 CHECK(v8::Locker::IsLocked());
7893 }
7894}
7895
7896
7897static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) {
7898 ApiTestFuzzer::Fuzz();
7899 v8::Unlocker unlocker;
7900 return v8::Undefined();
7901}
7902
7903
7904THREADED_TEST(LockUnlockLock) {
7905 {
7906 v8::Locker locker;
7907 v8::HandleScope scope;
7908 LocalContext env;
7909 Local<v8::FunctionTemplate> fun_templ =
7910 v8::FunctionTemplate::New(UnlockForAMoment);
7911 Local<Function> fun = fun_templ->GetFunction();
7912 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7913 Local<Script> script = v8_compile("(function () {"
7914 " unlock_for_a_moment();"
7915 " return 42;"
7916 "})();");
7917 CHECK_EQ(42, script->Run()->Int32Value());
7918 }
7919 {
7920 v8::Locker locker;
7921 v8::HandleScope scope;
7922 LocalContext env;
7923 Local<v8::FunctionTemplate> fun_templ =
7924 v8::FunctionTemplate::New(UnlockForAMoment);
7925 Local<Function> fun = fun_templ->GetFunction();
7926 env->Global()->Set(v8_str("unlock_for_a_moment"), fun);
7927 Local<Script> script = v8_compile("(function () {"
7928 " unlock_for_a_moment();"
7929 " return 42;"
7930 "})();");
7931 CHECK_EQ(42, script->Run()->Int32Value());
7932 }
7933}
7934
7935
Leon Clarked91b9f72010-01-27 17:25:45 +00007936static int GetGlobalObjectsCount() {
Leon Clarkeeab96aa2010-01-27 16:31:12 +00007937 int count = 0;
Steve Block8defd9f2010-07-08 12:39:36 +01007938 i::HeapIterator it;
Leon Clarked91b9f72010-01-27 17:25:45 +00007939 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
7940 if (object->IsJSGlobalObject()) count++;
7941 return count;
7942}
7943
7944
7945static int GetSurvivingGlobalObjectsCount() {
Steve Blocka7e24c12009-10-30 11:49:00 +00007946 // We need to collect all garbage twice to be sure that everything
7947 // has been collected. This is because inline caches are cleared in
7948 // the first garbage collection but some of the maps have already
7949 // been marked at that point. Therefore some of the maps are not
7950 // collected until the second garbage collection.
Steve Block8defd9f2010-07-08 12:39:36 +01007951 i::Heap::CollectAllGarbage(false);
7952 i::Heap::CollectAllGarbage(false);
Leon Clarked91b9f72010-01-27 17:25:45 +00007953 int count = GetGlobalObjectsCount();
Steve Blocka7e24c12009-10-30 11:49:00 +00007954#ifdef DEBUG
Steve Block8defd9f2010-07-08 12:39:36 +01007955 if (count > 0) i::Heap::TracePathToGlobal();
Steve Blocka7e24c12009-10-30 11:49:00 +00007956#endif
7957 return count;
7958}
7959
7960
7961TEST(DontLeakGlobalObjects) {
7962 // Regression test for issues 1139850 and 1174891.
7963
7964 v8::V8::Initialize();
7965
7966 int count = GetSurvivingGlobalObjectsCount();
7967
7968 for (int i = 0; i < 5; i++) {
7969 { v8::HandleScope scope;
7970 LocalContext context;
7971 }
7972 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7973
7974 { v8::HandleScope scope;
7975 LocalContext context;
7976 v8_compile("Date")->Run();
7977 }
7978 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7979
7980 { v8::HandleScope scope;
7981 LocalContext context;
7982 v8_compile("/aaa/")->Run();
7983 }
7984 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7985
7986 { v8::HandleScope scope;
7987 const char* extension_list[] = { "v8/gc" };
7988 v8::ExtensionConfiguration extensions(1, extension_list);
7989 LocalContext context(&extensions);
7990 v8_compile("gc();")->Run();
7991 }
7992 CHECK_EQ(count, GetSurvivingGlobalObjectsCount());
7993 }
7994}
7995
7996
7997v8::Persistent<v8::Object> some_object;
7998v8::Persistent<v8::Object> bad_handle;
7999
8000void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) {
8001 v8::HandleScope scope;
8002 bad_handle = v8::Persistent<v8::Object>::New(some_object);
8003}
8004
8005
8006THREADED_TEST(NewPersistentHandleFromWeakCallback) {
8007 LocalContext context;
8008
8009 v8::Persistent<v8::Object> handle1, handle2;
8010 {
8011 v8::HandleScope scope;
8012 some_object = v8::Persistent<v8::Object>::New(v8::Object::New());
8013 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8014 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8015 }
8016 // Note: order is implementation dependent alas: currently
8017 // global handle nodes are processed by PostGarbageCollectionProcessing
8018 // in reverse allocation order, so if second allocated handle is deleted,
8019 // weak callback of the first handle would be able to 'reallocate' it.
8020 handle1.MakeWeak(NULL, NewPersistentHandleCallback);
8021 handle2.Dispose();
8022 i::Heap::CollectAllGarbage(false);
8023}
8024
8025
8026v8::Persistent<v8::Object> to_be_disposed;
8027
8028void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) {
8029 to_be_disposed.Dispose();
8030 i::Heap::CollectAllGarbage(false);
8031}
8032
8033
8034THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) {
8035 LocalContext context;
8036
8037 v8::Persistent<v8::Object> handle1, handle2;
8038 {
8039 v8::HandleScope scope;
8040 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8041 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8042 }
8043 handle1.MakeWeak(NULL, DisposeAndForceGcCallback);
8044 to_be_disposed = handle2;
8045 i::Heap::CollectAllGarbage(false);
8046}
8047
Steve Blockd0582a62009-12-15 09:54:21 +00008048void DisposingCallback(v8::Persistent<v8::Value> handle, void*) {
8049 handle.Dispose();
8050}
8051
8052void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) {
8053 v8::HandleScope scope;
8054 v8::Persistent<v8::Object>::New(v8::Object::New());
8055}
8056
8057
8058THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
8059 LocalContext context;
8060
8061 v8::Persistent<v8::Object> handle1, handle2, handle3;
8062 {
8063 v8::HandleScope scope;
8064 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New());
8065 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New());
8066 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New());
8067 }
8068 handle2.MakeWeak(NULL, DisposingCallback);
8069 handle3.MakeWeak(NULL, HandleCreatingCallback);
8070 i::Heap::CollectAllGarbage(false);
8071}
8072
Steve Blocka7e24c12009-10-30 11:49:00 +00008073
8074THREADED_TEST(CheckForCrossContextObjectLiterals) {
8075 v8::V8::Initialize();
8076
8077 const int nof = 2;
8078 const char* sources[nof] = {
8079 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }",
8080 "Object()"
8081 };
8082
8083 for (int i = 0; i < nof; i++) {
8084 const char* source = sources[i];
8085 { v8::HandleScope scope;
8086 LocalContext context;
8087 CompileRun(source);
8088 }
8089 { v8::HandleScope scope;
8090 LocalContext context;
8091 CompileRun(source);
8092 }
8093 }
8094}
8095
8096
8097static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) {
8098 v8::HandleScope inner;
8099 env->Enter();
8100 v8::Handle<Value> three = v8_num(3);
8101 v8::Handle<Value> value = inner.Close(three);
8102 env->Exit();
8103 return value;
8104}
8105
8106
8107THREADED_TEST(NestedHandleScopeAndContexts) {
8108 v8::HandleScope outer;
8109 v8::Persistent<Context> env = Context::New();
8110 env->Enter();
8111 v8::Handle<Value> value = NestedScope(env);
8112 v8::Handle<String> str = value->ToString();
8113 env->Exit();
8114 env.Dispose();
8115}
8116
8117
8118THREADED_TEST(ExternalAllocatedMemory) {
8119 v8::HandleScope outer;
8120 v8::Persistent<Context> env = Context::New();
8121 const int kSize = 1024*1024;
8122 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize);
8123 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0);
8124}
8125
8126
8127THREADED_TEST(DisposeEnteredContext) {
8128 v8::HandleScope scope;
8129 LocalContext outer;
8130 { v8::Persistent<v8::Context> inner = v8::Context::New();
8131 inner->Enter();
8132 inner.Dispose();
8133 inner.Clear();
8134 inner->Exit();
8135 }
8136}
8137
8138
8139// Regression test for issue 54, object templates with internal fields
8140// but no accessors or interceptors did not get their internal field
8141// count set on instances.
8142THREADED_TEST(Regress54) {
8143 v8::HandleScope outer;
8144 LocalContext context;
8145 static v8::Persistent<v8::ObjectTemplate> templ;
8146 if (templ.IsEmpty()) {
8147 v8::HandleScope inner;
8148 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New();
8149 local->SetInternalFieldCount(1);
8150 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local));
8151 }
8152 v8::Handle<v8::Object> result = templ->NewInstance();
8153 CHECK_EQ(1, result->InternalFieldCount());
8154}
8155
8156
8157// If part of the threaded tests, this test makes ThreadingTest fail
8158// on mac.
8159TEST(CatchStackOverflow) {
8160 v8::HandleScope scope;
8161 LocalContext context;
8162 v8::TryCatch try_catch;
8163 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(
8164 "function f() {"
8165 " return f();"
8166 "}"
8167 ""
8168 "f();"));
8169 v8::Handle<v8::Value> result = script->Run();
8170 CHECK(result.IsEmpty());
8171}
8172
8173
8174static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script,
8175 const char* resource_name,
8176 int line_offset) {
8177 v8::HandleScope scope;
8178 v8::TryCatch try_catch;
8179 v8::Handle<v8::Value> result = script->Run();
8180 CHECK(result.IsEmpty());
8181 CHECK(try_catch.HasCaught());
8182 v8::Handle<v8::Message> message = try_catch.Message();
8183 CHECK(!message.IsEmpty());
8184 CHECK_EQ(10 + line_offset, message->GetLineNumber());
8185 CHECK_EQ(91, message->GetStartPosition());
8186 CHECK_EQ(92, message->GetEndPosition());
8187 CHECK_EQ(2, message->GetStartColumn());
8188 CHECK_EQ(3, message->GetEndColumn());
8189 v8::String::AsciiValue line(message->GetSourceLine());
8190 CHECK_EQ(" throw 'nirk';", *line);
8191 v8::String::AsciiValue name(message->GetScriptResourceName());
8192 CHECK_EQ(resource_name, *name);
8193}
8194
8195
8196THREADED_TEST(TryCatchSourceInfo) {
8197 v8::HandleScope scope;
8198 LocalContext context;
8199 v8::Handle<v8::String> source = v8::String::New(
8200 "function Foo() {\n"
8201 " return Bar();\n"
8202 "}\n"
8203 "\n"
8204 "function Bar() {\n"
8205 " return Baz();\n"
8206 "}\n"
8207 "\n"
8208 "function Baz() {\n"
8209 " throw 'nirk';\n"
8210 "}\n"
8211 "\n"
8212 "Foo();\n");
8213
8214 const char* resource_name;
8215 v8::Handle<v8::Script> script;
8216 resource_name = "test.js";
8217 script = v8::Script::Compile(source, v8::String::New(resource_name));
8218 CheckTryCatchSourceInfo(script, resource_name, 0);
8219
8220 resource_name = "test1.js";
8221 v8::ScriptOrigin origin1(v8::String::New(resource_name));
8222 script = v8::Script::Compile(source, &origin1);
8223 CheckTryCatchSourceInfo(script, resource_name, 0);
8224
8225 resource_name = "test2.js";
8226 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7));
8227 script = v8::Script::Compile(source, &origin2);
8228 CheckTryCatchSourceInfo(script, resource_name, 7);
8229}
8230
8231
8232THREADED_TEST(CompilationCache) {
8233 v8::HandleScope scope;
8234 LocalContext context;
8235 v8::Handle<v8::String> source0 = v8::String::New("1234");
8236 v8::Handle<v8::String> source1 = v8::String::New("1234");
8237 v8::Handle<v8::Script> script0 =
8238 v8::Script::Compile(source0, v8::String::New("test.js"));
8239 v8::Handle<v8::Script> script1 =
8240 v8::Script::Compile(source1, v8::String::New("test.js"));
8241 v8::Handle<v8::Script> script2 =
8242 v8::Script::Compile(source0); // different origin
8243 CHECK_EQ(1234, script0->Run()->Int32Value());
8244 CHECK_EQ(1234, script1->Run()->Int32Value());
8245 CHECK_EQ(1234, script2->Run()->Int32Value());
8246}
8247
8248
8249static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) {
8250 ApiTestFuzzer::Fuzz();
8251 return v8_num(42);
8252}
8253
8254
8255THREADED_TEST(CallbackFunctionName) {
8256 v8::HandleScope scope;
8257 LocalContext context;
8258 Local<ObjectTemplate> t = ObjectTemplate::New();
8259 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback));
8260 context->Global()->Set(v8_str("obj"), t->NewInstance());
8261 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name");
8262 CHECK(value->IsString());
8263 v8::String::AsciiValue name(value);
8264 CHECK_EQ("asdf", *name);
8265}
8266
8267
8268THREADED_TEST(DateAccess) {
8269 v8::HandleScope scope;
8270 LocalContext context;
8271 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0);
8272 CHECK(date->IsDate());
Steve Block6ded16b2010-05-10 14:33:55 +01008273 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue());
Steve Blocka7e24c12009-10-30 11:49:00 +00008274}
8275
8276
8277void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) {
Steve Block6ded16b2010-05-10 14:33:55 +01008278 v8::Handle<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008279 v8::Handle<v8::Array> props = obj->GetPropertyNames();
8280 CHECK_EQ(elmc, props->Length());
8281 for (int i = 0; i < elmc; i++) {
8282 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i)));
8283 CHECK_EQ(elmv[i], *elm);
8284 }
8285}
8286
8287
8288THREADED_TEST(PropertyEnumeration) {
8289 v8::HandleScope scope;
8290 LocalContext context;
8291 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New(
8292 "var result = [];"
8293 "result[0] = {};"
8294 "result[1] = {a: 1, b: 2};"
8295 "result[2] = [1, 2, 3];"
8296 "var proto = {x: 1, y: 2, z: 3};"
8297 "var x = { __proto__: proto, w: 0, z: 1 };"
8298 "result[3] = x;"
8299 "result;"))->Run();
Steve Block6ded16b2010-05-10 14:33:55 +01008300 v8::Handle<v8::Array> elms = obj.As<v8::Array>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008301 CHECK_EQ(4, elms->Length());
8302 int elmc0 = 0;
8303 const char** elmv0 = NULL;
8304 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0);
8305 int elmc1 = 2;
8306 const char* elmv1[] = {"a", "b"};
8307 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1);
8308 int elmc2 = 3;
8309 const char* elmv2[] = {"0", "1", "2"};
8310 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2);
8311 int elmc3 = 4;
8312 const char* elmv3[] = {"w", "z", "x", "y"};
8313 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3);
8314}
8315
8316
Steve Blocka7e24c12009-10-30 11:49:00 +00008317static bool NamedSetAccessBlocker(Local<v8::Object> obj,
8318 Local<Value> name,
8319 v8::AccessType type,
8320 Local<Value> data) {
8321 return type != v8::ACCESS_SET;
8322}
8323
8324
8325static bool IndexedSetAccessBlocker(Local<v8::Object> obj,
8326 uint32_t key,
8327 v8::AccessType type,
8328 Local<Value> data) {
8329 return type != v8::ACCESS_SET;
8330}
8331
8332
8333THREADED_TEST(DisableAccessChecksWhileConfiguring) {
8334 v8::HandleScope scope;
8335 LocalContext context;
8336 Local<ObjectTemplate> templ = ObjectTemplate::New();
8337 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8338 IndexedSetAccessBlocker);
8339 templ->Set(v8_str("x"), v8::True());
8340 Local<v8::Object> instance = templ->NewInstance();
8341 context->Global()->Set(v8_str("obj"), instance);
8342 Local<Value> value = CompileRun("obj.x");
8343 CHECK(value->BooleanValue());
8344}
8345
8346
8347static bool NamedGetAccessBlocker(Local<v8::Object> obj,
8348 Local<Value> name,
8349 v8::AccessType type,
8350 Local<Value> data) {
8351 return false;
8352}
8353
8354
8355static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
8356 uint32_t key,
8357 v8::AccessType type,
8358 Local<Value> data) {
8359 return false;
8360}
8361
8362
8363
8364THREADED_TEST(AccessChecksReenabledCorrectly) {
8365 v8::HandleScope scope;
8366 LocalContext context;
8367 Local<ObjectTemplate> templ = ObjectTemplate::New();
8368 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8369 IndexedGetAccessBlocker);
8370 templ->Set(v8_str("a"), v8_str("a"));
8371 // Add more than 8 (see kMaxFastProperties) properties
8372 // so that the constructor will force copying map.
8373 // Cannot sprintf, gcc complains unsafety.
8374 char buf[4];
8375 for (char i = '0'; i <= '9' ; i++) {
8376 buf[0] = i;
8377 for (char j = '0'; j <= '9'; j++) {
8378 buf[1] = j;
8379 for (char k = '0'; k <= '9'; k++) {
8380 buf[2] = k;
8381 buf[3] = 0;
8382 templ->Set(v8_str(buf), v8::Number::New(k));
8383 }
8384 }
8385 }
8386
8387 Local<v8::Object> instance_1 = templ->NewInstance();
8388 context->Global()->Set(v8_str("obj_1"), instance_1);
8389
8390 Local<Value> value_1 = CompileRun("obj_1.a");
8391 CHECK(value_1->IsUndefined());
8392
8393 Local<v8::Object> instance_2 = templ->NewInstance();
8394 context->Global()->Set(v8_str("obj_2"), instance_2);
8395
8396 Local<Value> value_2 = CompileRun("obj_2.a");
8397 CHECK(value_2->IsUndefined());
8398}
8399
8400
8401// This tests that access check information remains on the global
8402// object template when creating contexts.
8403THREADED_TEST(AccessControlRepeatedContextCreation) {
8404 v8::HandleScope handle_scope;
8405 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8406 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker,
8407 IndexedSetAccessBlocker);
8408 i::Handle<i::ObjectTemplateInfo> internal_template =
8409 v8::Utils::OpenHandle(*global_template);
8410 CHECK(!internal_template->constructor()->IsUndefined());
8411 i::Handle<i::FunctionTemplateInfo> constructor(
8412 i::FunctionTemplateInfo::cast(internal_template->constructor()));
8413 CHECK(!constructor->access_check_info()->IsUndefined());
8414 v8::Persistent<Context> context0 = Context::New(NULL, global_template);
8415 CHECK(!constructor->access_check_info()->IsUndefined());
8416}
8417
8418
8419THREADED_TEST(TurnOnAccessCheck) {
8420 v8::HandleScope handle_scope;
8421
8422 // Create an environment with access check to the global object disabled by
8423 // default.
8424 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
8425 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker,
8426 IndexedGetAccessBlocker,
8427 v8::Handle<v8::Value>(),
8428 false);
8429 v8::Persistent<Context> context = Context::New(NULL, global_template);
8430 Context::Scope context_scope(context);
8431
8432 // Set up a property and a number of functions.
8433 context->Global()->Set(v8_str("a"), v8_num(1));
8434 CompileRun("function f1() {return a;}"
8435 "function f2() {return a;}"
8436 "function g1() {return h();}"
8437 "function g2() {return h();}"
8438 "function h() {return 1;}");
8439 Local<Function> f1 =
8440 Local<Function>::Cast(context->Global()->Get(v8_str("f1")));
8441 Local<Function> f2 =
8442 Local<Function>::Cast(context->Global()->Get(v8_str("f2")));
8443 Local<Function> g1 =
8444 Local<Function>::Cast(context->Global()->Get(v8_str("g1")));
8445 Local<Function> g2 =
8446 Local<Function>::Cast(context->Global()->Get(v8_str("g2")));
8447 Local<Function> h =
8448 Local<Function>::Cast(context->Global()->Get(v8_str("h")));
8449
8450 // Get the global object.
8451 v8::Handle<v8::Object> global = context->Global();
8452
8453 // Call f1 one time and f2 a number of times. This will ensure that f1 still
8454 // uses the runtime system to retreive property a whereas f2 uses global load
8455 // inline cache.
8456 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1)));
8457 for (int i = 0; i < 4; i++) {
8458 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1)));
8459 }
8460
8461 // Same for g1 and g2.
8462 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1)));
8463 for (int i = 0; i < 4; i++) {
8464 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1)));
8465 }
8466
8467 // Detach the global and turn on access check.
8468 context->DetachGlobal();
8469 context->Global()->TurnOnAccessCheck();
8470
8471 // Failing access check to property get results in undefined.
8472 CHECK(f1->Call(global, 0, NULL)->IsUndefined());
8473 CHECK(f2->Call(global, 0, NULL)->IsUndefined());
8474
8475 // Failing access check to function call results in exception.
8476 CHECK(g1->Call(global, 0, NULL).IsEmpty());
8477 CHECK(g2->Call(global, 0, NULL).IsEmpty());
8478
8479 // No failing access check when just returning a constant.
8480 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1)));
8481}
8482
8483
8484// This test verifies that pre-compilation (aka preparsing) can be called
8485// without initializing the whole VM. Thus we cannot run this test in a
8486// multi-threaded setup.
8487TEST(PreCompile) {
8488 // TODO(155): This test would break without the initialization of V8. This is
8489 // a workaround for now to make this test not fail.
8490 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008491 const char* script = "function foo(a) { return a+1; }";
8492 v8::ScriptData* sd =
Steve Blockd0582a62009-12-15 09:54:21 +00008493 v8::ScriptData::PreCompile(script, i::StrLength(script));
Steve Blocka7e24c12009-10-30 11:49:00 +00008494 CHECK_NE(sd->Length(), 0);
8495 CHECK_NE(sd->Data(), NULL);
Leon Clarkee46be812010-01-19 14:06:41 +00008496 CHECK(!sd->HasError());
8497 delete sd;
8498}
8499
8500
8501TEST(PreCompileWithError) {
8502 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008503 const char* script = "function foo(a) { return 1 * * 2; }";
8504 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008505 v8::ScriptData::PreCompile(script, i::StrLength(script));
8506 CHECK(sd->HasError());
8507 delete sd;
8508}
8509
8510
8511TEST(Regress31661) {
8512 v8::V8::Initialize();
Leon Clarkef7060e22010-06-03 12:02:55 +01008513 const char* script = " The Definintive Guide";
8514 v8::ScriptData* sd =
Leon Clarkee46be812010-01-19 14:06:41 +00008515 v8::ScriptData::PreCompile(script, i::StrLength(script));
8516 CHECK(sd->HasError());
Steve Blocka7e24c12009-10-30 11:49:00 +00008517 delete sd;
8518}
8519
8520
Leon Clarkef7060e22010-06-03 12:02:55 +01008521// Tests that ScriptData can be serialized and deserialized.
8522TEST(PreCompileSerialization) {
8523 v8::V8::Initialize();
8524 const char* script = "function foo(a) { return a+1; }";
8525 v8::ScriptData* sd =
8526 v8::ScriptData::PreCompile(script, i::StrLength(script));
8527
8528 // Serialize.
8529 int serialized_data_length = sd->Length();
8530 char* serialized_data = i::NewArray<char>(serialized_data_length);
8531 memcpy(serialized_data, sd->Data(), serialized_data_length);
8532
8533 // Deserialize.
8534 v8::ScriptData* deserialized_sd =
8535 v8::ScriptData::New(serialized_data, serialized_data_length);
8536
8537 // Verify that the original is the same as the deserialized.
8538 CHECK_EQ(sd->Length(), deserialized_sd->Length());
8539 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length()));
8540 CHECK_EQ(sd->HasError(), deserialized_sd->HasError());
8541
8542 delete sd;
8543 delete deserialized_sd;
8544}
8545
8546
8547// Attempts to deserialize bad data.
8548TEST(PreCompileDeserializationError) {
8549 v8::V8::Initialize();
8550 const char* data = "DONT CARE";
8551 int invalid_size = 3;
8552 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size);
8553
8554 CHECK_EQ(0, sd->Length());
8555
8556 delete sd;
8557}
8558
8559
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01008560// Verifies that the Handle<String> and const char* versions of the API produce
8561// the same results (at least for one trivial case).
8562TEST(PreCompileAPIVariationsAreSame) {
8563 v8::V8::Initialize();
8564 v8::HandleScope scope;
8565
8566 const char* cstring = "function foo(a) { return a+1; }";
8567 v8::ScriptData* sd_from_cstring =
8568 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
8569
8570 TestAsciiResource* resource = new TestAsciiResource(cstring);
8571 v8::ScriptData* sd_from_istring = v8::ScriptData::PreCompile(
8572 v8::String::NewExternal(resource));
8573
8574 CHECK_EQ(sd_from_cstring->Length(), sd_from_istring->Length());
8575 CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
8576 sd_from_istring->Data(),
8577 sd_from_cstring->Length()));
8578
8579 delete sd_from_cstring;
8580 delete sd_from_istring;
8581}
8582
8583
Steve Blocka7e24c12009-10-30 11:49:00 +00008584// This tests that we do not allow dictionary load/call inline caches
8585// to use functions that have not yet been compiled. The potential
8586// problem of loading a function that has not yet been compiled can
8587// arise because we share code between contexts via the compilation
8588// cache.
8589THREADED_TEST(DictionaryICLoadedFunction) {
8590 v8::HandleScope scope;
8591 // Test LoadIC.
8592 for (int i = 0; i < 2; i++) {
8593 LocalContext context;
8594 context->Global()->Set(v8_str("tmp"), v8::True());
8595 context->Global()->Delete(v8_str("tmp"));
8596 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');");
8597 }
8598 // Test CallIC.
8599 for (int i = 0; i < 2; i++) {
8600 LocalContext context;
8601 context->Global()->Set(v8_str("tmp"), v8::True());
8602 context->Global()->Delete(v8_str("tmp"));
8603 CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
8604 }
8605}
8606
8607
8608// Test that cross-context new calls use the context of the callee to
8609// create the new JavaScript object.
8610THREADED_TEST(CrossContextNew) {
8611 v8::HandleScope scope;
8612 v8::Persistent<Context> context0 = Context::New();
8613 v8::Persistent<Context> context1 = Context::New();
8614
8615 // Allow cross-domain access.
8616 Local<String> token = v8_str("<security token>");
8617 context0->SetSecurityToken(token);
8618 context1->SetSecurityToken(token);
8619
8620 // Set an 'x' property on the Object prototype and define a
8621 // constructor function in context0.
8622 context0->Enter();
8623 CompileRun("Object.prototype.x = 42; function C() {};");
8624 context0->Exit();
8625
8626 // Call the constructor function from context0 and check that the
8627 // result has the 'x' property.
8628 context1->Enter();
8629 context1->Global()->Set(v8_str("other"), context0->Global());
8630 Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
8631 CHECK(value->IsInt32());
8632 CHECK_EQ(42, value->Int32Value());
8633 context1->Exit();
8634
8635 // Dispose the contexts to allow them to be garbage collected.
8636 context0.Dispose();
8637 context1.Dispose();
8638}
8639
8640
8641class RegExpInterruptTest {
8642 public:
8643 RegExpInterruptTest() : block_(NULL) {}
8644 ~RegExpInterruptTest() { delete block_; }
8645 void RunTest() {
8646 block_ = i::OS::CreateSemaphore(0);
8647 gc_count_ = 0;
8648 gc_during_regexp_ = 0;
8649 regexp_success_ = false;
8650 gc_success_ = false;
8651 GCThread gc_thread(this);
8652 gc_thread.Start();
8653 v8::Locker::StartPreemption(1);
8654
8655 LongRunningRegExp();
8656 {
8657 v8::Unlocker unlock;
8658 gc_thread.Join();
8659 }
8660 v8::Locker::StopPreemption();
8661 CHECK(regexp_success_);
8662 CHECK(gc_success_);
8663 }
8664 private:
8665 // Number of garbage collections required.
8666 static const int kRequiredGCs = 5;
8667
8668 class GCThread : public i::Thread {
8669 public:
8670 explicit GCThread(RegExpInterruptTest* test)
8671 : test_(test) {}
8672 virtual void Run() {
8673 test_->CollectGarbage();
8674 }
8675 private:
8676 RegExpInterruptTest* test_;
8677 };
8678
8679 void CollectGarbage() {
8680 block_->Wait();
8681 while (gc_during_regexp_ < kRequiredGCs) {
8682 {
8683 v8::Locker lock;
8684 // TODO(lrn): Perhaps create some garbage before collecting.
8685 i::Heap::CollectAllGarbage(false);
8686 gc_count_++;
8687 }
8688 i::OS::Sleep(1);
8689 }
8690 gc_success_ = true;
8691 }
8692
8693 void LongRunningRegExp() {
8694 block_->Signal(); // Enable garbage collection thread on next preemption.
8695 int rounds = 0;
8696 while (gc_during_regexp_ < kRequiredGCs) {
8697 int gc_before = gc_count_;
8698 {
8699 // Match 15-30 "a"'s against 14 and a "b".
8700 const char* c_source =
8701 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8702 ".exec('aaaaaaaaaaaaaaab') === null";
8703 Local<String> source = String::New(c_source);
8704 Local<Script> script = Script::Compile(source);
8705 Local<Value> result = script->Run();
8706 if (!result->BooleanValue()) {
8707 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit.
8708 return;
8709 }
8710 }
8711 {
8712 // Match 15-30 "a"'s against 15 and a "b".
8713 const char* c_source =
8714 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
8715 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'";
8716 Local<String> source = String::New(c_source);
8717 Local<Script> script = Script::Compile(source);
8718 Local<Value> result = script->Run();
8719 if (!result->BooleanValue()) {
8720 gc_during_regexp_ = kRequiredGCs;
8721 return;
8722 }
8723 }
8724 int gc_after = gc_count_;
8725 gc_during_regexp_ += gc_after - gc_before;
8726 rounds++;
8727 i::OS::Sleep(1);
8728 }
8729 regexp_success_ = true;
8730 }
8731
8732 i::Semaphore* block_;
8733 int gc_count_;
8734 int gc_during_regexp_;
8735 bool regexp_success_;
8736 bool gc_success_;
8737};
8738
8739
8740// Test that a regular expression execution can be interrupted and
8741// survive a garbage collection.
8742TEST(RegExpInterruption) {
8743 v8::Locker lock;
8744 v8::V8::Initialize();
8745 v8::HandleScope scope;
8746 Local<Context> local_env;
8747 {
8748 LocalContext env;
8749 local_env = env.local();
8750 }
8751
8752 // Local context should still be live.
8753 CHECK(!local_env.IsEmpty());
8754 local_env->Enter();
8755
8756 // Should complete without problems.
8757 RegExpInterruptTest().RunTest();
8758
8759 local_env->Exit();
8760}
8761
8762
8763class ApplyInterruptTest {
8764 public:
8765 ApplyInterruptTest() : block_(NULL) {}
8766 ~ApplyInterruptTest() { delete block_; }
8767 void RunTest() {
8768 block_ = i::OS::CreateSemaphore(0);
8769 gc_count_ = 0;
8770 gc_during_apply_ = 0;
8771 apply_success_ = false;
8772 gc_success_ = false;
8773 GCThread gc_thread(this);
8774 gc_thread.Start();
8775 v8::Locker::StartPreemption(1);
8776
8777 LongRunningApply();
8778 {
8779 v8::Unlocker unlock;
8780 gc_thread.Join();
8781 }
8782 v8::Locker::StopPreemption();
8783 CHECK(apply_success_);
8784 CHECK(gc_success_);
8785 }
8786 private:
8787 // Number of garbage collections required.
8788 static const int kRequiredGCs = 2;
8789
8790 class GCThread : public i::Thread {
8791 public:
8792 explicit GCThread(ApplyInterruptTest* test)
8793 : test_(test) {}
8794 virtual void Run() {
8795 test_->CollectGarbage();
8796 }
8797 private:
8798 ApplyInterruptTest* test_;
8799 };
8800
8801 void CollectGarbage() {
8802 block_->Wait();
8803 while (gc_during_apply_ < kRequiredGCs) {
8804 {
8805 v8::Locker lock;
8806 i::Heap::CollectAllGarbage(false);
8807 gc_count_++;
8808 }
8809 i::OS::Sleep(1);
8810 }
8811 gc_success_ = true;
8812 }
8813
8814 void LongRunningApply() {
8815 block_->Signal();
8816 int rounds = 0;
8817 while (gc_during_apply_ < kRequiredGCs) {
8818 int gc_before = gc_count_;
8819 {
8820 const char* c_source =
8821 "function do_very_little(bar) {"
8822 " this.foo = bar;"
8823 "}"
8824 "for (var i = 0; i < 100000; i++) {"
8825 " do_very_little.apply(this, ['bar']);"
8826 "}";
8827 Local<String> source = String::New(c_source);
8828 Local<Script> script = Script::Compile(source);
8829 Local<Value> result = script->Run();
8830 // Check that no exception was thrown.
8831 CHECK(!result.IsEmpty());
8832 }
8833 int gc_after = gc_count_;
8834 gc_during_apply_ += gc_after - gc_before;
8835 rounds++;
8836 }
8837 apply_success_ = true;
8838 }
8839
8840 i::Semaphore* block_;
8841 int gc_count_;
8842 int gc_during_apply_;
8843 bool apply_success_;
8844 bool gc_success_;
8845};
8846
8847
8848// Test that nothing bad happens if we get a preemption just when we were
8849// about to do an apply().
8850TEST(ApplyInterruption) {
8851 v8::Locker lock;
8852 v8::V8::Initialize();
8853 v8::HandleScope scope;
8854 Local<Context> local_env;
8855 {
8856 LocalContext env;
8857 local_env = env.local();
8858 }
8859
8860 // Local context should still be live.
8861 CHECK(!local_env.IsEmpty());
8862 local_env->Enter();
8863
8864 // Should complete without problems.
8865 ApplyInterruptTest().RunTest();
8866
8867 local_env->Exit();
8868}
8869
8870
8871// Verify that we can clone an object
8872TEST(ObjectClone) {
8873 v8::HandleScope scope;
8874 LocalContext env;
8875
8876 const char* sample =
8877 "var rv = {};" \
8878 "rv.alpha = 'hello';" \
8879 "rv.beta = 123;" \
8880 "rv;";
8881
8882 // Create an object, verify basics.
8883 Local<Value> val = CompileRun(sample);
8884 CHECK(val->IsObject());
Steve Block6ded16b2010-05-10 14:33:55 +01008885 Local<v8::Object> obj = val.As<v8::Object>();
Steve Blocka7e24c12009-10-30 11:49:00 +00008886 obj->Set(v8_str("gamma"), v8_str("cloneme"));
8887
8888 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha")));
8889 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8890 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma")));
8891
8892 // Clone it.
8893 Local<v8::Object> clone = obj->Clone();
8894 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha")));
8895 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta")));
8896 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma")));
8897
8898 // Set a property on the clone, verify each object.
8899 clone->Set(v8_str("beta"), v8::Integer::New(456));
8900 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta")));
8901 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta")));
8902}
8903
8904
8905class AsciiVectorResource : public v8::String::ExternalAsciiStringResource {
8906 public:
8907 explicit AsciiVectorResource(i::Vector<const char> vector)
8908 : data_(vector) {}
8909 virtual ~AsciiVectorResource() {}
8910 virtual size_t length() const { return data_.length(); }
8911 virtual const char* data() const { return data_.start(); }
8912 private:
8913 i::Vector<const char> data_;
8914};
8915
8916
8917class UC16VectorResource : public v8::String::ExternalStringResource {
8918 public:
8919 explicit UC16VectorResource(i::Vector<const i::uc16> vector)
8920 : data_(vector) {}
8921 virtual ~UC16VectorResource() {}
8922 virtual size_t length() const { return data_.length(); }
8923 virtual const i::uc16* data() const { return data_.start(); }
8924 private:
8925 i::Vector<const i::uc16> data_;
8926};
8927
8928
8929static void MorphAString(i::String* string,
8930 AsciiVectorResource* ascii_resource,
8931 UC16VectorResource* uc16_resource) {
8932 CHECK(i::StringShape(string).IsExternal());
8933 if (string->IsAsciiRepresentation()) {
8934 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008935 CHECK(string->map() == i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008936 // Morph external string to be TwoByte string.
Steve Blockd0582a62009-12-15 09:54:21 +00008937 string->set_map(i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008938 i::ExternalTwoByteString* morphed =
8939 i::ExternalTwoByteString::cast(string);
8940 morphed->set_resource(uc16_resource);
8941 } else {
8942 // Check old map is not symbol or long.
Steve Blockd0582a62009-12-15 09:54:21 +00008943 CHECK(string->map() == i::Heap::external_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008944 // Morph external string to be ASCII string.
Steve Blockd0582a62009-12-15 09:54:21 +00008945 string->set_map(i::Heap::external_ascii_string_map());
Steve Blocka7e24c12009-10-30 11:49:00 +00008946 i::ExternalAsciiString* morphed =
8947 i::ExternalAsciiString::cast(string);
8948 morphed->set_resource(ascii_resource);
8949 }
8950}
8951
8952
8953// Test that we can still flatten a string if the components it is built up
8954// from have been turned into 16 bit strings in the mean time.
8955THREADED_TEST(MorphCompositeStringTest) {
8956 const char* c_string = "Now is the time for all good men"
8957 " to come to the aid of the party";
8958 uint16_t* two_byte_string = AsciiToTwoByteString(c_string);
8959 {
8960 v8::HandleScope scope;
8961 LocalContext env;
8962 AsciiVectorResource ascii_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008963 i::Vector<const char>(c_string, i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008964 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00008965 i::Vector<const uint16_t>(two_byte_string,
8966 i::StrLength(c_string)));
Steve Blocka7e24c12009-10-30 11:49:00 +00008967
8968 Local<String> lhs(v8::Utils::ToLocal(
8969 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8970 Local<String> rhs(v8::Utils::ToLocal(
8971 i::Factory::NewExternalStringFromAscii(&ascii_resource)));
8972
8973 env->Global()->Set(v8_str("lhs"), lhs);
8974 env->Global()->Set(v8_str("rhs"), rhs);
8975
8976 CompileRun(
8977 "var cons = lhs + rhs;"
8978 "var slice = lhs.substring(1, lhs.length - 1);"
8979 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);");
8980
8981 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource);
8982 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource);
8983
8984 // Now do some stuff to make sure the strings are flattened, etc.
8985 CompileRun(
8986 "/[^a-z]/.test(cons);"
8987 "/[^a-z]/.test(slice);"
8988 "/[^a-z]/.test(slice_on_cons);");
8989 const char* expected_cons =
8990 "Now is the time for all good men to come to the aid of the party"
8991 "Now is the time for all good men to come to the aid of the party";
8992 const char* expected_slice =
8993 "ow is the time for all good men to come to the aid of the part";
8994 const char* expected_slice_on_cons =
8995 "ow is the time for all good men to come to the aid of the party"
8996 "Now is the time for all good men to come to the aid of the part";
8997 CHECK_EQ(String::New(expected_cons),
8998 env->Global()->Get(v8_str("cons")));
8999 CHECK_EQ(String::New(expected_slice),
9000 env->Global()->Get(v8_str("slice")));
9001 CHECK_EQ(String::New(expected_slice_on_cons),
9002 env->Global()->Get(v8_str("slice_on_cons")));
9003 }
9004}
9005
9006
9007TEST(CompileExternalTwoByteSource) {
9008 v8::HandleScope scope;
9009 LocalContext context;
9010
9011 // This is a very short list of sources, which currently is to check for a
9012 // regression caused by r2703.
9013 const char* ascii_sources[] = {
9014 "0.5",
9015 "-0.5", // This mainly testes PushBack in the Scanner.
9016 "--0.5", // This mainly testes PushBack in the Scanner.
9017 NULL
9018 };
9019
9020 // Compile the sources as external two byte strings.
9021 for (int i = 0; ascii_sources[i] != NULL; i++) {
9022 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]);
9023 UC16VectorResource uc16_resource(
Steve Blockd0582a62009-12-15 09:54:21 +00009024 i::Vector<const uint16_t>(two_byte_string,
9025 i::StrLength(ascii_sources[i])));
Steve Blocka7e24c12009-10-30 11:49:00 +00009026 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource);
9027 v8::Script::Compile(source);
9028 }
9029}
9030
9031
9032class RegExpStringModificationTest {
9033 public:
9034 RegExpStringModificationTest()
9035 : block_(i::OS::CreateSemaphore(0)),
9036 morphs_(0),
9037 morphs_during_regexp_(0),
9038 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)),
9039 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {}
9040 ~RegExpStringModificationTest() { delete block_; }
9041 void RunTest() {
9042 regexp_success_ = false;
9043 morph_success_ = false;
9044
9045 // Initialize the contents of two_byte_content_ to be a uc16 representation
9046 // of "aaaaaaaaaaaaaab".
9047 for (int i = 0; i < 14; i++) {
9048 two_byte_content_[i] = 'a';
9049 }
9050 two_byte_content_[14] = 'b';
9051
9052 // Create the input string for the regexp - the one we are going to change
9053 // properties of.
9054 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_);
9055
9056 // Inject the input as a global variable.
9057 i::Handle<i::String> input_name =
9058 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5));
9059 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE);
9060
9061
9062 MorphThread morph_thread(this);
9063 morph_thread.Start();
9064 v8::Locker::StartPreemption(1);
9065 LongRunningRegExp();
9066 {
9067 v8::Unlocker unlock;
9068 morph_thread.Join();
9069 }
9070 v8::Locker::StopPreemption();
9071 CHECK(regexp_success_);
9072 CHECK(morph_success_);
9073 }
9074 private:
9075
9076 // Number of string modifications required.
9077 static const int kRequiredModifications = 5;
9078 static const int kMaxModifications = 100;
9079
9080 class MorphThread : public i::Thread {
9081 public:
9082 explicit MorphThread(RegExpStringModificationTest* test)
9083 : test_(test) {}
9084 virtual void Run() {
9085 test_->MorphString();
9086 }
9087 private:
9088 RegExpStringModificationTest* test_;
9089 };
9090
9091 void MorphString() {
9092 block_->Wait();
9093 while (morphs_during_regexp_ < kRequiredModifications &&
9094 morphs_ < kMaxModifications) {
9095 {
9096 v8::Locker lock;
9097 // Swap string between ascii and two-byte representation.
9098 i::String* string = *input_;
9099 MorphAString(string, &ascii_resource_, &uc16_resource_);
9100 morphs_++;
9101 }
9102 i::OS::Sleep(1);
9103 }
9104 morph_success_ = true;
9105 }
9106
9107 void LongRunningRegExp() {
9108 block_->Signal(); // Enable morphing thread on next preemption.
9109 while (morphs_during_regexp_ < kRequiredModifications &&
9110 morphs_ < kMaxModifications) {
9111 int morphs_before = morphs_;
9112 {
9113 // Match 15-30 "a"'s against 14 and a "b".
9114 const char* c_source =
9115 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/"
9116 ".exec(input) === null";
9117 Local<String> source = String::New(c_source);
9118 Local<Script> script = Script::Compile(source);
9119 Local<Value> result = script->Run();
9120 CHECK(result->IsTrue());
9121 }
9122 int morphs_after = morphs_;
9123 morphs_during_regexp_ += morphs_after - morphs_before;
9124 }
9125 regexp_success_ = true;
9126 }
9127
9128 i::uc16 two_byte_content_[15];
9129 i::Semaphore* block_;
9130 int morphs_;
9131 int morphs_during_regexp_;
9132 bool regexp_success_;
9133 bool morph_success_;
9134 i::Handle<i::String> input_;
9135 AsciiVectorResource ascii_resource_;
9136 UC16VectorResource uc16_resource_;
9137};
9138
9139
9140// Test that a regular expression execution can be interrupted and
9141// the string changed without failing.
9142TEST(RegExpStringModification) {
9143 v8::Locker lock;
9144 v8::V8::Initialize();
9145 v8::HandleScope scope;
9146 Local<Context> local_env;
9147 {
9148 LocalContext env;
9149 local_env = env.local();
9150 }
9151
9152 // Local context should still be live.
9153 CHECK(!local_env.IsEmpty());
9154 local_env->Enter();
9155
9156 // Should complete without problems.
9157 RegExpStringModificationTest().RunTest();
9158
9159 local_env->Exit();
9160}
9161
9162
9163// Test that we can set a property on the global object even if there
9164// is a read-only property in the prototype chain.
9165TEST(ReadOnlyPropertyInGlobalProto) {
9166 v8::HandleScope scope;
9167 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9168 LocalContext context(0, templ);
9169 v8::Handle<v8::Object> global = context->Global();
9170 v8::Handle<v8::Object> global_proto =
9171 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__")));
9172 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly);
9173 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly);
9174 // Check without 'eval' or 'with'.
9175 v8::Handle<v8::Value> res =
9176 CompileRun("function f() { x = 42; return x; }; f()");
9177 // Check with 'eval'.
9178 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()");
9179 CHECK_EQ(v8::Integer::New(42), res);
9180 // Check with 'with'.
9181 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
9182 CHECK_EQ(v8::Integer::New(42), res);
9183}
9184
9185static int force_set_set_count = 0;
9186static int force_set_get_count = 0;
9187bool pass_on_get = false;
9188
9189static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
9190 const v8::AccessorInfo& info) {
9191 force_set_get_count++;
9192 if (pass_on_get) {
9193 return v8::Handle<v8::Value>();
9194 } else {
9195 return v8::Int32::New(3);
9196 }
9197}
9198
9199static void ForceSetSetter(v8::Local<v8::String> name,
9200 v8::Local<v8::Value> value,
9201 const v8::AccessorInfo& info) {
9202 force_set_set_count++;
9203}
9204
9205static v8::Handle<v8::Value> ForceSetInterceptSetter(
9206 v8::Local<v8::String> name,
9207 v8::Local<v8::Value> value,
9208 const v8::AccessorInfo& info) {
9209 force_set_set_count++;
9210 return v8::Undefined();
9211}
9212
9213TEST(ForceSet) {
9214 force_set_get_count = 0;
9215 force_set_set_count = 0;
9216 pass_on_get = false;
9217
9218 v8::HandleScope scope;
9219 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9220 v8::Handle<v8::String> access_property = v8::String::New("a");
9221 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
9222 LocalContext context(NULL, templ);
9223 v8::Handle<v8::Object> global = context->Global();
9224
9225 // Ordinary properties
9226 v8::Handle<v8::String> simple_property = v8::String::New("p");
9227 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
9228 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9229 // This should fail because the property is read-only
9230 global->Set(simple_property, v8::Int32::New(5));
9231 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9232 // This should succeed even though the property is read-only
9233 global->ForceSet(simple_property, v8::Int32::New(6));
9234 CHECK_EQ(6, global->Get(simple_property)->Int32Value());
9235
9236 // Accessors
9237 CHECK_EQ(0, force_set_set_count);
9238 CHECK_EQ(0, force_set_get_count);
9239 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9240 // CHECK_EQ the property shouldn't override it, just call the setter
9241 // which in this case does nothing.
9242 global->Set(access_property, v8::Int32::New(7));
9243 CHECK_EQ(3, global->Get(access_property)->Int32Value());
9244 CHECK_EQ(1, force_set_set_count);
9245 CHECK_EQ(2, force_set_get_count);
9246 // Forcing the property to be set should override the accessor without
9247 // calling it
9248 global->ForceSet(access_property, v8::Int32::New(8));
9249 CHECK_EQ(8, global->Get(access_property)->Int32Value());
9250 CHECK_EQ(1, force_set_set_count);
9251 CHECK_EQ(2, force_set_get_count);
9252}
9253
9254TEST(ForceSetWithInterceptor) {
9255 force_set_get_count = 0;
9256 force_set_set_count = 0;
9257 pass_on_get = false;
9258
9259 v8::HandleScope scope;
9260 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9261 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
9262 LocalContext context(NULL, templ);
9263 v8::Handle<v8::Object> global = context->Global();
9264
9265 v8::Handle<v8::String> some_property = v8::String::New("a");
9266 CHECK_EQ(0, force_set_set_count);
9267 CHECK_EQ(0, force_set_get_count);
9268 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9269 // Setting the property shouldn't override it, just call the setter
9270 // which in this case does nothing.
9271 global->Set(some_property, v8::Int32::New(7));
9272 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9273 CHECK_EQ(1, force_set_set_count);
9274 CHECK_EQ(2, force_set_get_count);
9275 // Getting the property when the interceptor returns an empty handle
9276 // should yield undefined, since the property isn't present on the
9277 // object itself yet.
9278 pass_on_get = true;
9279 CHECK(global->Get(some_property)->IsUndefined());
9280 CHECK_EQ(1, force_set_set_count);
9281 CHECK_EQ(3, force_set_get_count);
9282 // Forcing the property to be set should cause the value to be
9283 // set locally without calling the interceptor.
9284 global->ForceSet(some_property, v8::Int32::New(8));
9285 CHECK_EQ(8, global->Get(some_property)->Int32Value());
9286 CHECK_EQ(1, force_set_set_count);
9287 CHECK_EQ(4, force_set_get_count);
9288 // Reenabling the interceptor should cause it to take precedence over
9289 // the property
9290 pass_on_get = false;
9291 CHECK_EQ(3, global->Get(some_property)->Int32Value());
9292 CHECK_EQ(1, force_set_set_count);
9293 CHECK_EQ(5, force_set_get_count);
9294 // The interceptor should also work for other properties
9295 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
9296 CHECK_EQ(1, force_set_set_count);
9297 CHECK_EQ(6, force_set_get_count);
9298}
9299
9300
9301THREADED_TEST(ForceDelete) {
9302 v8::HandleScope scope;
9303 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
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::DontDelete);
9310 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9311 // This should fail because the property is dont-delete.
9312 CHECK(!global->Delete(simple_property));
9313 CHECK_EQ(4, global->Get(simple_property)->Int32Value());
9314 // This should succeed even though the property is dont-delete.
9315 CHECK(global->ForceDelete(simple_property));
9316 CHECK(global->Get(simple_property)->IsUndefined());
9317}
9318
9319
9320static int force_delete_interceptor_count = 0;
9321static bool pass_on_delete = false;
9322
9323
9324static v8::Handle<v8::Boolean> ForceDeleteDeleter(
9325 v8::Local<v8::String> name,
9326 const v8::AccessorInfo& info) {
9327 force_delete_interceptor_count++;
9328 if (pass_on_delete) {
9329 return v8::Handle<v8::Boolean>();
9330 } else {
9331 return v8::True();
9332 }
9333}
9334
9335
9336THREADED_TEST(ForceDeleteWithInterceptor) {
9337 force_delete_interceptor_count = 0;
9338 pass_on_delete = false;
9339
9340 v8::HandleScope scope;
9341 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
9342 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
9343 LocalContext context(NULL, templ);
9344 v8::Handle<v8::Object> global = context->Global();
9345
9346 v8::Handle<v8::String> some_property = v8::String::New("a");
9347 global->Set(some_property, v8::Integer::New(42), v8::DontDelete);
9348
9349 // Deleting a property should get intercepted and nothing should
9350 // happen.
9351 CHECK_EQ(0, force_delete_interceptor_count);
9352 CHECK(global->Delete(some_property));
9353 CHECK_EQ(1, force_delete_interceptor_count);
9354 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9355 // Deleting the property when the interceptor returns an empty
9356 // handle should not delete the property since it is DontDelete.
9357 pass_on_delete = true;
9358 CHECK(!global->Delete(some_property));
9359 CHECK_EQ(2, force_delete_interceptor_count);
9360 CHECK_EQ(42, global->Get(some_property)->Int32Value());
9361 // Forcing the property to be deleted should delete the value
9362 // without calling the interceptor.
9363 CHECK(global->ForceDelete(some_property));
9364 CHECK(global->Get(some_property)->IsUndefined());
9365 CHECK_EQ(2, force_delete_interceptor_count);
9366}
9367
9368
9369// Make sure that forcing a delete invalidates any IC stubs, so we
9370// don't read the hole value.
9371THREADED_TEST(ForceDeleteIC) {
9372 v8::HandleScope scope;
9373 LocalContext context;
9374 // Create a DontDelete variable on the global object.
9375 CompileRun("this.__proto__ = { foo: 'horse' };"
9376 "var foo = 'fish';"
9377 "function f() { return foo.length; }");
9378 // Initialize the IC for foo in f.
9379 CompileRun("for (var i = 0; i < 4; i++) f();");
9380 // Make sure the value of foo is correct before the deletion.
9381 CHECK_EQ(4, CompileRun("f()")->Int32Value());
9382 // Force the deletion of foo.
9383 CHECK(context->Global()->ForceDelete(v8_str("foo")));
9384 // Make sure the value for foo is read from the prototype, and that
9385 // we don't get in trouble with reading the deleted cell value
9386 // sentinel.
9387 CHECK_EQ(5, CompileRun("f()")->Int32Value());
9388}
9389
9390
9391v8::Persistent<Context> calling_context0;
9392v8::Persistent<Context> calling_context1;
9393v8::Persistent<Context> calling_context2;
9394
9395
9396// Check that the call to the callback is initiated in
9397// calling_context2, the directly calling context is calling_context1
9398// and the callback itself is in calling_context0.
9399static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) {
9400 ApiTestFuzzer::Fuzz();
9401 CHECK(Context::GetCurrent() == calling_context0);
9402 CHECK(Context::GetCalling() == calling_context1);
9403 CHECK(Context::GetEntered() == calling_context2);
9404 return v8::Integer::New(42);
9405}
9406
9407
9408THREADED_TEST(GetCallingContext) {
9409 v8::HandleScope scope;
9410
9411 calling_context0 = Context::New();
9412 calling_context1 = Context::New();
9413 calling_context2 = Context::New();
9414
9415 // Allow cross-domain access.
9416 Local<String> token = v8_str("<security token>");
9417 calling_context0->SetSecurityToken(token);
9418 calling_context1->SetSecurityToken(token);
9419 calling_context2->SetSecurityToken(token);
9420
9421 // Create an object with a C++ callback in context0.
9422 calling_context0->Enter();
9423 Local<v8::FunctionTemplate> callback_templ =
9424 v8::FunctionTemplate::New(GetCallingContextCallback);
9425 calling_context0->Global()->Set(v8_str("callback"),
9426 callback_templ->GetFunction());
9427 calling_context0->Exit();
9428
9429 // Expose context0 in context1 and setup a function that calls the
9430 // callback function.
9431 calling_context1->Enter();
9432 calling_context1->Global()->Set(v8_str("context0"),
9433 calling_context0->Global());
9434 CompileRun("function f() { context0.callback() }");
9435 calling_context1->Exit();
9436
9437 // Expose context1 in context2 and call the callback function in
9438 // context0 indirectly through f in context1.
9439 calling_context2->Enter();
9440 calling_context2->Global()->Set(v8_str("context1"),
9441 calling_context1->Global());
9442 CompileRun("context1.f()");
9443 calling_context2->Exit();
9444
9445 // Dispose the contexts to allow them to be garbage collected.
9446 calling_context0.Dispose();
9447 calling_context1.Dispose();
9448 calling_context2.Dispose();
9449 calling_context0.Clear();
9450 calling_context1.Clear();
9451 calling_context2.Clear();
9452}
9453
9454
9455// Check that a variable declaration with no explicit initialization
9456// value does not shadow an existing property in the prototype chain.
9457//
9458// This is consistent with Firefox and Safari.
9459//
9460// See http://crbug.com/12548.
9461THREADED_TEST(InitGlobalVarInProtoChain) {
9462 v8::HandleScope scope;
9463 LocalContext context;
9464 // Introduce a variable in the prototype chain.
9465 CompileRun("__proto__.x = 42");
9466 v8::Handle<v8::Value> result = CompileRun("var x; x");
9467 CHECK(!result->IsUndefined());
9468 CHECK_EQ(42, result->Int32Value());
9469}
9470
9471
9472// Regression test for issue 398.
9473// If a function is added to an object, creating a constant function
9474// field, and the result is cloned, replacing the constant function on the
9475// original should not affect the clone.
9476// See http://code.google.com/p/v8/issues/detail?id=398
9477THREADED_TEST(ReplaceConstantFunction) {
9478 v8::HandleScope scope;
9479 LocalContext context;
9480 v8::Handle<v8::Object> obj = v8::Object::New();
9481 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New();
9482 v8::Handle<v8::String> foo_string = v8::String::New("foo");
9483 obj->Set(foo_string, func_templ->GetFunction());
9484 v8::Handle<v8::Object> obj_clone = obj->Clone();
9485 obj_clone->Set(foo_string, v8::String::New("Hello"));
9486 CHECK(!obj->Get(foo_string)->IsUndefined());
9487}
9488
9489
9490// Regression test for http://crbug.com/16276.
9491THREADED_TEST(Regress16276) {
9492 v8::HandleScope scope;
9493 LocalContext context;
9494 // Force the IC in f to be a dictionary load IC.
9495 CompileRun("function f(obj) { return obj.x; }\n"
9496 "var obj = { x: { foo: 42 }, y: 87 };\n"
9497 "var x = obj.x;\n"
9498 "delete obj.y;\n"
9499 "for (var i = 0; i < 5; i++) f(obj);");
9500 // Detach the global object to make 'this' refer directly to the
9501 // global object (not the proxy), and make sure that the dictionary
9502 // load IC doesn't mess up loading directly from the global object.
9503 context->DetachGlobal();
9504 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value());
9505}
9506
9507
9508THREADED_TEST(PixelArray) {
9509 v8::HandleScope scope;
9510 LocalContext context;
Steve Blockd0582a62009-12-15 09:54:21 +00009511 const int kElementCount = 260;
Steve Blocka7e24c12009-10-30 11:49:00 +00009512 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
9513 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount,
9514 pixel_data);
9515 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9516 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009517 pixels->set(i, i % 256);
Steve Blocka7e24c12009-10-30 11:49:00 +00009518 }
9519 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9520 for (int i = 0; i < kElementCount; i++) {
Steve Blockd0582a62009-12-15 09:54:21 +00009521 CHECK_EQ(i % 256, pixels->get(i));
9522 CHECK_EQ(i % 256, pixel_data[i]);
Steve Blocka7e24c12009-10-30 11:49:00 +00009523 }
9524
9525 v8::Handle<v8::Object> obj = v8::Object::New();
9526 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9527 // Set the elements to be the pixels.
9528 // jsobj->set_elements(*pixels);
9529 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
9530 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9531 obj->Set(v8_str("field"), v8::Int32::New(1503));
9532 context->Global()->Set(v8_str("pixels"), obj);
9533 v8::Handle<v8::Value> result = CompileRun("pixels.field");
9534 CHECK_EQ(1503, result->Int32Value());
9535 result = CompileRun("pixels[1]");
9536 CHECK_EQ(1, result->Int32Value());
9537
9538 result = CompileRun("var sum = 0;"
9539 "for (var i = 0; i < 8; i++) {"
9540 " sum += pixels[i] = pixels[i] = -i;"
9541 "}"
9542 "sum;");
9543 CHECK_EQ(-28, result->Int32Value());
9544
9545 result = CompileRun("var sum = 0;"
9546 "for (var i = 0; i < 8; i++) {"
9547 " sum += pixels[i] = pixels[i] = 0;"
9548 "}"
9549 "sum;");
9550 CHECK_EQ(0, result->Int32Value());
9551
9552 result = CompileRun("var sum = 0;"
9553 "for (var i = 0; i < 8; i++) {"
9554 " sum += pixels[i] = pixels[i] = 255;"
9555 "}"
9556 "sum;");
9557 CHECK_EQ(8 * 255, result->Int32Value());
9558
9559 result = CompileRun("var sum = 0;"
9560 "for (var i = 0; i < 8; i++) {"
9561 " sum += pixels[i] = pixels[i] = 256 + i;"
9562 "}"
9563 "sum;");
9564 CHECK_EQ(2076, result->Int32Value());
9565
9566 result = CompileRun("var sum = 0;"
9567 "for (var i = 0; i < 8; i++) {"
9568 " sum += pixels[i] = pixels[i] = i;"
9569 "}"
9570 "sum;");
9571 CHECK_EQ(28, result->Int32Value());
9572
9573 result = CompileRun("var sum = 0;"
9574 "for (var i = 0; i < 8; i++) {"
9575 " sum += pixels[i];"
9576 "}"
9577 "sum;");
9578 CHECK_EQ(28, result->Int32Value());
9579
9580 i::Handle<i::Smi> value(i::Smi::FromInt(2));
9581 i::SetElement(jsobj, 1, value);
9582 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value());
9583 *value.location() = i::Smi::FromInt(256);
9584 i::SetElement(jsobj, 1, value);
9585 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value());
9586 *value.location() = i::Smi::FromInt(-1);
9587 i::SetElement(jsobj, 1, value);
9588 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9589
9590 result = CompileRun("for (var i = 0; i < 8; i++) {"
9591 " pixels[i] = (i * 65) - 109;"
9592 "}"
9593 "pixels[1] + pixels[6];");
9594 CHECK_EQ(255, result->Int32Value());
9595 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9596 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value());
9597 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value());
9598 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value());
9599 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value());
9600 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value());
9601 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value());
9602 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value());
9603 result = CompileRun("var sum = 0;"
9604 "for (var i = 0; i < 8; i++) {"
9605 " sum += pixels[i];"
9606 "}"
9607 "sum;");
9608 CHECK_EQ(984, result->Int32Value());
9609
9610 result = CompileRun("for (var i = 0; i < 8; i++) {"
9611 " pixels[i] = (i * 1.1);"
9612 "}"
9613 "pixels[1] + pixels[6];");
9614 CHECK_EQ(8, result->Int32Value());
9615 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value());
9616 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value());
9617 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value());
9618 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value());
9619 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value());
9620 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value());
9621 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value());
9622 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value());
9623
9624 result = CompileRun("for (var i = 0; i < 8; i++) {"
9625 " pixels[7] = undefined;"
9626 "}"
9627 "pixels[7];");
9628 CHECK_EQ(0, result->Int32Value());
9629 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value());
9630
9631 result = CompileRun("for (var i = 0; i < 8; i++) {"
9632 " pixels[6] = '2.3';"
9633 "}"
9634 "pixels[6];");
9635 CHECK_EQ(2, result->Int32Value());
9636 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value());
9637
9638 result = CompileRun("for (var i = 0; i < 8; i++) {"
9639 " pixels[5] = NaN;"
9640 "}"
9641 "pixels[5];");
9642 CHECK_EQ(0, result->Int32Value());
9643 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9644
9645 result = CompileRun("for (var i = 0; i < 8; i++) {"
9646 " pixels[8] = Infinity;"
9647 "}"
9648 "pixels[8];");
9649 CHECK_EQ(255, result->Int32Value());
9650 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value());
9651
9652 result = CompileRun("for (var i = 0; i < 8; i++) {"
9653 " pixels[9] = -Infinity;"
9654 "}"
9655 "pixels[9];");
9656 CHECK_EQ(0, result->Int32Value());
9657 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value());
9658
9659 result = CompileRun("pixels[3] = 33;"
9660 "delete pixels[3];"
9661 "pixels[3];");
9662 CHECK_EQ(33, result->Int32Value());
9663
9664 result = CompileRun("pixels[0] = 10; pixels[1] = 11;"
9665 "pixels[2] = 12; pixels[3] = 13;"
9666 "pixels.__defineGetter__('2',"
9667 "function() { return 120; });"
9668 "pixels[2];");
9669 CHECK_EQ(12, result->Int32Value());
9670
9671 result = CompileRun("var js_array = new Array(40);"
9672 "js_array[0] = 77;"
9673 "js_array;");
9674 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9675
9676 result = CompileRun("pixels[1] = 23;"
9677 "pixels.__proto__ = [];"
9678 "js_array.__proto__ = pixels;"
9679 "js_array.concat(pixels);");
9680 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9681 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9682
9683 result = CompileRun("pixels[1] = 23;");
9684 CHECK_EQ(23, result->Int32Value());
9685
Steve Blockd0582a62009-12-15 09:54:21 +00009686 // Test for index greater than 255. Regression test for:
9687 // http://code.google.com/p/chromium/issues/detail?id=26337.
9688 result = CompileRun("pixels[256] = 255;");
9689 CHECK_EQ(255, result->Int32Value());
9690 result = CompileRun("var i = 0;"
9691 "for (var j = 0; j < 8; j++) { i = pixels[256]; }"
9692 "i");
9693 CHECK_EQ(255, result->Int32Value());
9694
Steve Blocka7e24c12009-10-30 11:49:00 +00009695 free(pixel_data);
9696}
9697
9698
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009699THREADED_TEST(PixelArrayInfo) {
9700 v8::HandleScope scope;
9701 LocalContext context;
9702 for (int size = 0; size < 100; size += 10) {
9703 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
9704 v8::Handle<v8::Object> obj = v8::Object::New();
9705 obj->SetIndexedPropertiesToPixelData(pixel_data, size);
9706 CHECK(obj->HasIndexedPropertiesInPixelData());
9707 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
9708 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
9709 free(pixel_data);
9710 }
9711}
9712
9713
9714static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
9715 switch (array_type) {
9716 case v8::kExternalByteArray:
9717 case v8::kExternalUnsignedByteArray:
9718 return 1;
9719 break;
9720 case v8::kExternalShortArray:
9721 case v8::kExternalUnsignedShortArray:
9722 return 2;
9723 break;
9724 case v8::kExternalIntArray:
9725 case v8::kExternalUnsignedIntArray:
9726 case v8::kExternalFloatArray:
9727 return 4;
9728 break;
9729 default:
9730 UNREACHABLE();
9731 return -1;
9732 }
9733 UNREACHABLE();
9734 return -1;
9735}
9736
9737
Steve Block3ce2e202009-11-05 08:53:23 +00009738template <class ExternalArrayClass, class ElementType>
9739static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
9740 int64_t low,
9741 int64_t high) {
9742 v8::HandleScope scope;
9743 LocalContext context;
9744 const int kElementCount = 40;
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01009745 int element_size = ExternalArrayElementSize(array_type);
Steve Block3ce2e202009-11-05 08:53:23 +00009746 ElementType* array_data =
9747 static_cast<ElementType*>(malloc(kElementCount * element_size));
9748 i::Handle<ExternalArrayClass> array =
9749 i::Handle<ExternalArrayClass>::cast(
9750 i::Factory::NewExternalArray(kElementCount, array_type, array_data));
9751 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9752 for (int i = 0; i < kElementCount; i++) {
9753 array->set(i, static_cast<ElementType>(i));
9754 }
9755 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9756 for (int i = 0; i < kElementCount; i++) {
9757 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
9758 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
9759 }
9760
9761 v8::Handle<v8::Object> obj = v8::Object::New();
9762 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
9763 // Set the elements to be the external array.
9764 obj->SetIndexedPropertiesToExternalArrayData(array_data,
9765 array_type,
9766 kElementCount);
9767 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
9768 obj->Set(v8_str("field"), v8::Int32::New(1503));
9769 context->Global()->Set(v8_str("ext_array"), obj);
9770 v8::Handle<v8::Value> result = CompileRun("ext_array.field");
9771 CHECK_EQ(1503, result->Int32Value());
9772 result = CompileRun("ext_array[1]");
9773 CHECK_EQ(1, result->Int32Value());
9774
9775 // Check pass through of assigned smis
9776 result = CompileRun("var sum = 0;"
9777 "for (var i = 0; i < 8; i++) {"
9778 " sum += ext_array[i] = ext_array[i] = -i;"
9779 "}"
9780 "sum;");
9781 CHECK_EQ(-28, result->Int32Value());
9782
9783 // Check assigned smis
9784 result = CompileRun("for (var i = 0; i < 8; i++) {"
9785 " ext_array[i] = i;"
9786 "}"
9787 "var sum = 0;"
9788 "for (var i = 0; i < 8; i++) {"
9789 " sum += ext_array[i];"
9790 "}"
9791 "sum;");
9792 CHECK_EQ(28, result->Int32Value());
9793
9794 // Check assigned smis in reverse order
9795 result = CompileRun("for (var i = 8; --i >= 0; ) {"
9796 " ext_array[i] = i;"
9797 "}"
9798 "var sum = 0;"
9799 "for (var i = 0; i < 8; i++) {"
9800 " sum += ext_array[i];"
9801 "}"
9802 "sum;");
9803 CHECK_EQ(28, result->Int32Value());
9804
9805 // Check pass through of assigned HeapNumbers
9806 result = CompileRun("var sum = 0;"
9807 "for (var i = 0; i < 16; i+=2) {"
9808 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
9809 "}"
9810 "sum;");
9811 CHECK_EQ(-28, result->Int32Value());
9812
9813 // Check assigned HeapNumbers
9814 result = CompileRun("for (var i = 0; i < 16; i+=2) {"
9815 " ext_array[i] = (i * 0.5);"
9816 "}"
9817 "var sum = 0;"
9818 "for (var i = 0; i < 16; i+=2) {"
9819 " sum += ext_array[i];"
9820 "}"
9821 "sum;");
9822 CHECK_EQ(28, result->Int32Value());
9823
9824 // Check assigned HeapNumbers in reverse order
9825 result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
9826 " ext_array[i] = (i * 0.5);"
9827 "}"
9828 "var sum = 0;"
9829 "for (var i = 0; i < 16; i+=2) {"
9830 " sum += ext_array[i];"
9831 "}"
9832 "sum;");
9833 CHECK_EQ(28, result->Int32Value());
9834
9835 i::ScopedVector<char> test_buf(1024);
9836
9837 // Check legal boundary conditions.
9838 // The repeated loads and stores ensure the ICs are exercised.
9839 const char* boundary_program =
9840 "var res = 0;"
9841 "for (var i = 0; i < 16; i++) {"
9842 " ext_array[i] = %lld;"
9843 " if (i > 8) {"
9844 " res = ext_array[i];"
9845 " }"
9846 "}"
9847 "res;";
9848 i::OS::SNPrintF(test_buf,
9849 boundary_program,
9850 low);
9851 result = CompileRun(test_buf.start());
9852 CHECK_EQ(low, result->IntegerValue());
9853
9854 i::OS::SNPrintF(test_buf,
9855 boundary_program,
9856 high);
9857 result = CompileRun(test_buf.start());
9858 CHECK_EQ(high, result->IntegerValue());
9859
9860 // Check misprediction of type in IC.
9861 result = CompileRun("var tmp_array = ext_array;"
9862 "var sum = 0;"
9863 "for (var i = 0; i < 8; i++) {"
9864 " tmp_array[i] = i;"
9865 " sum += tmp_array[i];"
9866 " if (i == 4) {"
9867 " tmp_array = {};"
9868 " }"
9869 "}"
9870 "sum;");
9871 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
9872 CHECK_EQ(28, result->Int32Value());
9873
9874 // Make sure out-of-range loads do not throw.
9875 i::OS::SNPrintF(test_buf,
9876 "var caught_exception = false;"
9877 "try {"
9878 " ext_array[%d];"
9879 "} catch (e) {"
9880 " caught_exception = true;"
9881 "}"
9882 "caught_exception;",
9883 kElementCount);
9884 result = CompileRun(test_buf.start());
9885 CHECK_EQ(false, result->BooleanValue());
9886
9887 // Make sure out-of-range stores do not throw.
9888 i::OS::SNPrintF(test_buf,
9889 "var caught_exception = false;"
9890 "try {"
9891 " ext_array[%d] = 1;"
9892 "} catch (e) {"
9893 " caught_exception = true;"
9894 "}"
9895 "caught_exception;",
9896 kElementCount);
9897 result = CompileRun(test_buf.start());
9898 CHECK_EQ(false, result->BooleanValue());
9899
9900 // Check other boundary conditions, values and operations.
9901 result = CompileRun("for (var i = 0; i < 8; i++) {"
9902 " ext_array[7] = undefined;"
9903 "}"
9904 "ext_array[7];");
9905 CHECK_EQ(0, result->Int32Value());
9906 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
9907
9908 result = CompileRun("for (var i = 0; i < 8; i++) {"
9909 " ext_array[6] = '2.3';"
9910 "}"
9911 "ext_array[6];");
9912 CHECK_EQ(2, result->Int32Value());
9913 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
9914
9915 if (array_type != v8::kExternalFloatArray) {
9916 // Though the specification doesn't state it, be explicit about
9917 // converting NaNs and +/-Infinity to zero.
9918 result = CompileRun("for (var i = 0; i < 8; i++) {"
9919 " ext_array[i] = 5;"
9920 "}"
9921 "for (var i = 0; i < 8; i++) {"
9922 " ext_array[i] = NaN;"
9923 "}"
9924 "ext_array[5];");
9925 CHECK_EQ(0, result->Int32Value());
9926 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9927
9928 result = CompileRun("for (var i = 0; i < 8; i++) {"
9929 " ext_array[i] = 5;"
9930 "}"
9931 "for (var i = 0; i < 8; i++) {"
9932 " ext_array[i] = Infinity;"
9933 "}"
9934 "ext_array[5];");
9935 CHECK_EQ(0, result->Int32Value());
9936 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9937
9938 result = CompileRun("for (var i = 0; i < 8; i++) {"
9939 " ext_array[i] = 5;"
9940 "}"
9941 "for (var i = 0; i < 8; i++) {"
9942 " ext_array[i] = -Infinity;"
9943 "}"
9944 "ext_array[5];");
9945 CHECK_EQ(0, result->Int32Value());
9946 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
9947 }
9948
9949 result = CompileRun("ext_array[3] = 33;"
9950 "delete ext_array[3];"
9951 "ext_array[3];");
9952 CHECK_EQ(33, result->Int32Value());
9953
9954 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
9955 "ext_array[2] = 12; ext_array[3] = 13;"
9956 "ext_array.__defineGetter__('2',"
9957 "function() { return 120; });"
9958 "ext_array[2];");
9959 CHECK_EQ(12, result->Int32Value());
9960
9961 result = CompileRun("var js_array = new Array(40);"
9962 "js_array[0] = 77;"
9963 "js_array;");
9964 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9965
9966 result = CompileRun("ext_array[1] = 23;"
9967 "ext_array.__proto__ = [];"
9968 "js_array.__proto__ = ext_array;"
9969 "js_array.concat(ext_array);");
9970 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
9971 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
9972
9973 result = CompileRun("ext_array[1] = 23;");
9974 CHECK_EQ(23, result->Int32Value());
9975
Steve Blockd0582a62009-12-15 09:54:21 +00009976 // Test more complex manipulations which cause eax to contain values
9977 // that won't be completely overwritten by loads from the arrays.
9978 // This catches bugs in the instructions used for the KeyedLoadIC
9979 // for byte and word types.
9980 {
9981 const int kXSize = 300;
9982 const int kYSize = 300;
9983 const int kLargeElementCount = kXSize * kYSize * 4;
9984 ElementType* large_array_data =
9985 static_cast<ElementType*>(malloc(kLargeElementCount * element_size));
9986 i::Handle<ExternalArrayClass> large_array =
9987 i::Handle<ExternalArrayClass>::cast(
9988 i::Factory::NewExternalArray(kLargeElementCount,
9989 array_type,
9990 array_data));
9991 v8::Handle<v8::Object> large_obj = v8::Object::New();
9992 // Set the elements to be the external array.
9993 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data,
9994 array_type,
9995 kLargeElementCount);
9996 context->Global()->Set(v8_str("large_array"), large_obj);
9997 // Initialize contents of a few rows.
9998 for (int x = 0; x < 300; x++) {
9999 int row = 0;
10000 int offset = row * 300 * 4;
10001 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10002 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10003 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10004 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10005 row = 150;
10006 offset = row * 300 * 4;
10007 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10008 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10009 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10010 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10011 row = 298;
10012 offset = row * 300 * 4;
10013 large_array_data[offset + 4 * x + 0] = (ElementType) 127;
10014 large_array_data[offset + 4 * x + 1] = (ElementType) 0;
10015 large_array_data[offset + 4 * x + 2] = (ElementType) 0;
10016 large_array_data[offset + 4 * x + 3] = (ElementType) 127;
10017 }
10018 // The goal of the code below is to make "offset" large enough
10019 // that the computation of the index (which goes into eax) has
10020 // high bits set which will not be overwritten by a byte or short
10021 // load.
10022 result = CompileRun("var failed = false;"
10023 "var offset = 0;"
10024 "for (var i = 0; i < 300; i++) {"
10025 " if (large_array[4 * i] != 127 ||"
10026 " large_array[4 * i + 1] != 0 ||"
10027 " large_array[4 * i + 2] != 0 ||"
10028 " large_array[4 * i + 3] != 127) {"
10029 " failed = true;"
10030 " }"
10031 "}"
10032 "offset = 150 * 300 * 4;"
10033 "for (var i = 0; i < 300; i++) {"
10034 " if (large_array[offset + 4 * i] != 127 ||"
10035 " large_array[offset + 4 * i + 1] != 0 ||"
10036 " large_array[offset + 4 * i + 2] != 0 ||"
10037 " large_array[offset + 4 * i + 3] != 127) {"
10038 " failed = true;"
10039 " }"
10040 "}"
10041 "offset = 298 * 300 * 4;"
10042 "for (var i = 0; i < 300; i++) {"
10043 " if (large_array[offset + 4 * i] != 127 ||"
10044 " large_array[offset + 4 * i + 1] != 0 ||"
10045 " large_array[offset + 4 * i + 2] != 0 ||"
10046 " large_array[offset + 4 * i + 3] != 127) {"
10047 " failed = true;"
10048 " }"
10049 "}"
10050 "!failed;");
10051 CHECK_EQ(true, result->BooleanValue());
10052 free(large_array_data);
10053 }
10054
Steve Block3ce2e202009-11-05 08:53:23 +000010055 free(array_data);
10056}
10057
10058
10059THREADED_TEST(ExternalByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010060 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010061 v8::kExternalByteArray,
10062 -128,
10063 127);
10064}
10065
10066
10067THREADED_TEST(ExternalUnsignedByteArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010068 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010069 v8::kExternalUnsignedByteArray,
10070 0,
10071 255);
10072}
10073
10074
10075THREADED_TEST(ExternalShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010076 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010077 v8::kExternalShortArray,
10078 -32768,
10079 32767);
10080}
10081
10082
10083THREADED_TEST(ExternalUnsignedShortArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010084 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010085 v8::kExternalUnsignedShortArray,
10086 0,
10087 65535);
10088}
10089
10090
10091THREADED_TEST(ExternalIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010092 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010093 v8::kExternalIntArray,
10094 INT_MIN, // -2147483648
10095 INT_MAX); // 2147483647
10096}
10097
10098
10099THREADED_TEST(ExternalUnsignedIntArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010100 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>(
Steve Block3ce2e202009-11-05 08:53:23 +000010101 v8::kExternalUnsignedIntArray,
10102 0,
10103 UINT_MAX); // 4294967295
10104}
10105
10106
10107THREADED_TEST(ExternalFloatArray) {
Steve Block8defd9f2010-07-08 12:39:36 +010010108 ExternalArrayTestHelper<i::ExternalFloatArray, float>(
Steve Block3ce2e202009-11-05 08:53:23 +000010109 v8::kExternalFloatArray,
10110 -500,
10111 500);
10112}
10113
10114
10115THREADED_TEST(ExternalArrays) {
10116 TestExternalByteArray();
10117 TestExternalUnsignedByteArray();
10118 TestExternalShortArray();
10119 TestExternalUnsignedShortArray();
10120 TestExternalIntArray();
10121 TestExternalUnsignedIntArray();
10122 TestExternalFloatArray();
10123}
10124
10125
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010126void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
10127 v8::HandleScope scope;
10128 LocalContext context;
10129 for (int size = 0; size < 100; size += 10) {
10130 int element_size = ExternalArrayElementSize(array_type);
10131 void* external_data = malloc(size * element_size);
10132 v8::Handle<v8::Object> obj = v8::Object::New();
10133 obj->SetIndexedPropertiesToExternalArrayData(
10134 external_data, array_type, size);
10135 CHECK(obj->HasIndexedPropertiesInExternalArrayData());
10136 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
10137 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
10138 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
10139 free(external_data);
10140 }
10141}
10142
10143
10144THREADED_TEST(ExternalArrayInfo) {
10145 ExternalArrayInfoTestHelper(v8::kExternalByteArray);
10146 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
10147 ExternalArrayInfoTestHelper(v8::kExternalShortArray);
10148 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
10149 ExternalArrayInfoTestHelper(v8::kExternalIntArray);
10150 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
10151 ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
10152}
10153
10154
Steve Blocka7e24c12009-10-30 11:49:00 +000010155THREADED_TEST(ScriptContextDependence) {
10156 v8::HandleScope scope;
10157 LocalContext c1;
10158 const char *source = "foo";
10159 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source));
10160 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source));
10161 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100));
10162 CHECK_EQ(dep->Run()->Int32Value(), 100);
10163 CHECK_EQ(indep->Run()->Int32Value(), 100);
10164 LocalContext c2;
10165 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101));
10166 CHECK_EQ(dep->Run()->Int32Value(), 100);
10167 CHECK_EQ(indep->Run()->Int32Value(), 101);
10168}
10169
10170
10171THREADED_TEST(StackTrace) {
10172 v8::HandleScope scope;
10173 LocalContext context;
10174 v8::TryCatch try_catch;
10175 const char *source = "function foo() { FAIL.FAIL; }; foo();";
10176 v8::Handle<v8::String> src = v8::String::New(source);
10177 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test");
10178 v8::Script::New(src, origin)->Run();
10179 CHECK(try_catch.HasCaught());
10180 v8::String::Utf8Value stack(try_catch.StackTrace());
10181 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL);
10182}
10183
10184
Kristian Monsen25f61362010-05-21 11:50:48 +010010185// Checks that a StackFrame has certain expected values.
10186void checkStackFrame(const char* expected_script_name,
10187 const char* expected_func_name, int expected_line_number,
10188 int expected_column, bool is_eval, bool is_constructor,
10189 v8::Handle<v8::StackFrame> frame) {
10190 v8::HandleScope scope;
10191 v8::String::Utf8Value func_name(frame->GetFunctionName());
10192 v8::String::Utf8Value script_name(frame->GetScriptName());
10193 if (*script_name == NULL) {
10194 // The situation where there is no associated script, like for evals.
10195 CHECK(expected_script_name == NULL);
10196 } else {
10197 CHECK(strstr(*script_name, expected_script_name) != NULL);
10198 }
10199 CHECK(strstr(*func_name, expected_func_name) != NULL);
10200 CHECK_EQ(expected_line_number, frame->GetLineNumber());
10201 CHECK_EQ(expected_column, frame->GetColumn());
10202 CHECK_EQ(is_eval, frame->IsEval());
10203 CHECK_EQ(is_constructor, frame->IsConstructor());
10204}
10205
10206
10207v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) {
10208 v8::HandleScope scope;
10209 const char* origin = "capture-stack-trace-test";
10210 const int kOverviewTest = 1;
10211 const int kDetailedTest = 2;
10212
10213 ASSERT(args.Length() == 1);
10214
10215 int testGroup = args[0]->Int32Value();
10216 if (testGroup == kOverviewTest) {
10217 v8::Handle<v8::StackTrace> stackTrace =
10218 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview);
10219 CHECK_EQ(4, stackTrace->GetFrameCount());
10220 checkStackFrame(origin, "bar", 2, 10, false, false,
10221 stackTrace->GetFrame(0));
10222 checkStackFrame(origin, "foo", 6, 3, false, false,
10223 stackTrace->GetFrame(1));
10224 checkStackFrame(NULL, "", 1, 1, false, false,
10225 stackTrace->GetFrame(2));
10226 // The last frame is an anonymous function that has the initial call.
10227 checkStackFrame(origin, "", 8, 7, false, false,
10228 stackTrace->GetFrame(3));
10229
10230 CHECK(stackTrace->AsArray()->IsArray());
10231 } else if (testGroup == kDetailedTest) {
10232 v8::Handle<v8::StackTrace> stackTrace =
10233 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed);
10234 CHECK_EQ(4, stackTrace->GetFrameCount());
10235 checkStackFrame(origin, "bat", 4, 22, false, false,
10236 stackTrace->GetFrame(0));
10237 checkStackFrame(origin, "baz", 8, 3, false, true,
10238 stackTrace->GetFrame(1));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010010239#ifdef ENABLE_DEBUGGER_SUPPORT
10240 bool is_eval = true;
10241#else // ENABLE_DEBUGGER_SUPPORT
10242 bool is_eval = false;
10243#endif // ENABLE_DEBUGGER_SUPPORT
10244
10245 checkStackFrame(NULL, "", 1, 1, is_eval, false,
Kristian Monsen25f61362010-05-21 11:50:48 +010010246 stackTrace->GetFrame(2));
10247 // The last frame is an anonymous function that has the initial call to foo.
10248 checkStackFrame(origin, "", 10, 1, false, false,
10249 stackTrace->GetFrame(3));
10250
10251 CHECK(stackTrace->AsArray()->IsArray());
10252 }
10253 return v8::Undefined();
10254}
10255
10256
10257// Tests the C++ StackTrace API.
10258THREADED_TEST(CaptureStackTrace) {
10259 v8::HandleScope scope;
10260 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test");
10261 Local<ObjectTemplate> templ = ObjectTemplate::New();
10262 templ->Set(v8_str("AnalyzeStackInNativeCode"),
10263 v8::FunctionTemplate::New(AnalyzeStackInNativeCode));
10264 LocalContext context(0, templ);
10265
10266 // Test getting OVERVIEW information. Should ignore information that is not
10267 // script name, function name, line number, and column offset.
10268 const char *overview_source =
10269 "function bar() {\n"
10270 " var y; AnalyzeStackInNativeCode(1);\n"
10271 "}\n"
10272 "function foo() {\n"
10273 "\n"
10274 " bar();\n"
10275 "}\n"
10276 "var x;eval('new foo();');";
10277 v8::Handle<v8::String> overview_src = v8::String::New(overview_source);
10278 v8::Handle<Value> overview_result =
10279 v8::Script::New(overview_src, origin)->Run();
10280 ASSERT(!overview_result.IsEmpty());
10281 ASSERT(overview_result->IsObject());
10282
10283 // Test getting DETAILED information.
10284 const char *detailed_source =
10285 "function bat() {AnalyzeStackInNativeCode(2);\n"
10286 "}\n"
10287 "\n"
10288 "function baz() {\n"
10289 " bat();\n"
10290 "}\n"
10291 "eval('new baz();');";
10292 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source);
10293 // Make the script using a non-zero line and column offset.
10294 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3);
10295 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5);
10296 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset);
10297 v8::Handle<v8::Script> detailed_script(
10298 v8::Script::New(detailed_src, &detailed_origin));
10299 v8::Handle<Value> detailed_result = detailed_script->Run();
10300 ASSERT(!detailed_result.IsEmpty());
10301 ASSERT(detailed_result->IsObject());
10302}
10303
10304
Steve Block3ce2e202009-11-05 08:53:23 +000010305// Test that idle notification can be handled and eventually returns true.
Steve Blocka7e24c12009-10-30 11:49:00 +000010306THREADED_TEST(IdleNotification) {
Steve Block3ce2e202009-11-05 08:53:23 +000010307 bool rv = false;
10308 for (int i = 0; i < 100; i++) {
10309 rv = v8::V8::IdleNotification();
10310 if (rv)
10311 break;
10312 }
10313 CHECK(rv == true);
Steve Blocka7e24c12009-10-30 11:49:00 +000010314}
10315
10316
10317static uint32_t* stack_limit;
10318
10319static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
10320 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
10321 return v8::Undefined();
10322}
10323
10324
10325// Uses the address of a local variable to determine the stack top now.
10326// Given a size, returns an address that is that far from the current
10327// top of stack.
10328static uint32_t* ComputeStackLimit(uint32_t size) {
10329 uint32_t* answer = &size - (size / sizeof(size));
10330 // If the size is very large and the stack is very near the bottom of
10331 // memory then the calculation above may wrap around and give an address
10332 // that is above the (downwards-growing) stack. In that case we return
10333 // a very low address.
10334 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
10335 return answer;
10336}
10337
10338
10339TEST(SetResourceConstraints) {
10340 static const int K = 1024;
10341 uint32_t* set_limit = ComputeStackLimit(128 * K);
10342
10343 // Set stack limit.
10344 v8::ResourceConstraints constraints;
10345 constraints.set_stack_limit(set_limit);
10346 CHECK(v8::SetResourceConstraints(&constraints));
10347
10348 // Execute a script.
10349 v8::HandleScope scope;
10350 LocalContext env;
10351 Local<v8::FunctionTemplate> fun_templ =
10352 v8::FunctionTemplate::New(GetStackLimitCallback);
10353 Local<Function> fun = fun_templ->GetFunction();
10354 env->Global()->Set(v8_str("get_stack_limit"), fun);
10355 CompileRun("get_stack_limit();");
10356
10357 CHECK(stack_limit == set_limit);
10358}
10359
10360
10361TEST(SetResourceConstraintsInThread) {
10362 uint32_t* set_limit;
10363 {
10364 v8::Locker locker;
10365 static const int K = 1024;
10366 set_limit = ComputeStackLimit(128 * K);
10367
10368 // Set stack limit.
10369 v8::ResourceConstraints constraints;
10370 constraints.set_stack_limit(set_limit);
10371 CHECK(v8::SetResourceConstraints(&constraints));
10372
10373 // Execute a script.
10374 v8::HandleScope scope;
10375 LocalContext env;
10376 Local<v8::FunctionTemplate> fun_templ =
10377 v8::FunctionTemplate::New(GetStackLimitCallback);
10378 Local<Function> fun = fun_templ->GetFunction();
10379 env->Global()->Set(v8_str("get_stack_limit"), fun);
10380 CompileRun("get_stack_limit();");
10381
10382 CHECK(stack_limit == set_limit);
10383 }
10384 {
10385 v8::Locker locker;
10386 CHECK(stack_limit == set_limit);
10387 }
10388}
Steve Block3ce2e202009-11-05 08:53:23 +000010389
10390
10391THREADED_TEST(GetHeapStatistics) {
10392 v8::HandleScope scope;
10393 LocalContext c1;
10394 v8::HeapStatistics heap_statistics;
Steve Blockd0582a62009-12-15 09:54:21 +000010395 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0);
10396 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0);
Steve Block3ce2e202009-11-05 08:53:23 +000010397 v8::V8::GetHeapStatistics(&heap_statistics);
Steve Blockd0582a62009-12-15 09:54:21 +000010398 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0);
10399 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0);
10400}
10401
10402
10403static double DoubleFromBits(uint64_t value) {
10404 double target;
10405#ifdef BIG_ENDIAN_FLOATING_POINT
10406 const int kIntSize = 4;
10407 // Somebody swapped the lower and higher half of doubles.
10408 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10409 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10410#else
10411 memcpy(&target, &value, sizeof(target));
10412#endif
10413 return target;
10414}
10415
10416
10417static uint64_t DoubleToBits(double value) {
10418 uint64_t target;
10419#ifdef BIG_ENDIAN_FLOATING_POINT
10420 const int kIntSize = 4;
10421 // Somebody swapped the lower and higher half of doubles.
10422 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize);
10423 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize);
10424#else
10425 memcpy(&target, &value, sizeof(target));
10426#endif
10427 return target;
10428}
10429
10430
10431static double DoubleToDateTime(double input) {
10432 double date_limit = 864e13;
10433 if (IsNaN(input) || input < -date_limit || input > date_limit) {
10434 return i::OS::nan_value();
10435 }
10436 return (input < 0) ? -(floor(-input)) : floor(input);
10437}
10438
10439// We don't have a consistent way to write 64-bit constants syntactically, so we
10440// split them into two 32-bit constants and combine them programmatically.
10441static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) {
10442 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits);
10443}
10444
10445
10446THREADED_TEST(QuietSignalingNaNs) {
10447 v8::HandleScope scope;
10448 LocalContext context;
10449 v8::TryCatch try_catch;
10450
10451 // Special double values.
10452 double snan = DoubleFromBits(0x7ff00000, 0x00000001);
10453 double qnan = DoubleFromBits(0x7ff80000, 0x00000000);
10454 double infinity = DoubleFromBits(0x7ff00000, 0x00000000);
10455 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu);
10456 double min_normal = DoubleFromBits(0x00100000, 0x00000000);
10457 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu);
10458 double min_denormal = DoubleFromBits(0x00000000, 0x00000001);
10459
10460 // Date values are capped at +/-100000000 days (times 864e5 ms per day)
10461 // on either side of the epoch.
10462 double date_limit = 864e13;
10463
10464 double test_values[] = {
10465 snan,
10466 qnan,
10467 infinity,
10468 max_normal,
10469 date_limit + 1,
10470 date_limit,
10471 min_normal,
10472 max_denormal,
10473 min_denormal,
10474 0,
10475 -0,
10476 -min_denormal,
10477 -max_denormal,
10478 -min_normal,
10479 -date_limit,
10480 -date_limit - 1,
10481 -max_normal,
10482 -infinity,
10483 -qnan,
10484 -snan
10485 };
10486 int num_test_values = 20;
10487
10488 for (int i = 0; i < num_test_values; i++) {
10489 double test_value = test_values[i];
10490
10491 // Check that Number::New preserves non-NaNs and quiets SNaNs.
10492 v8::Handle<v8::Value> number = v8::Number::New(test_value);
10493 double stored_number = number->NumberValue();
10494 if (!IsNaN(test_value)) {
10495 CHECK_EQ(test_value, stored_number);
10496 } else {
10497 uint64_t stored_bits = DoubleToBits(stored_number);
10498 // Check if quiet nan (bits 51..62 all set).
10499 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10500 }
10501
10502 // Check that Date::New preserves non-NaNs in the date range and
10503 // quiets SNaNs.
10504 v8::Handle<v8::Value> date = v8::Date::New(test_value);
10505 double expected_stored_date = DoubleToDateTime(test_value);
10506 double stored_date = date->NumberValue();
10507 if (!IsNaN(expected_stored_date)) {
10508 CHECK_EQ(expected_stored_date, stored_date);
10509 } else {
10510 uint64_t stored_bits = DoubleToBits(stored_date);
10511 // Check if quiet nan (bits 51..62 all set).
10512 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff));
10513 }
10514 }
10515}
10516
10517
10518static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) {
10519 v8::HandleScope scope;
10520 v8::TryCatch tc;
10521 v8::Handle<v8::String> str = args[0]->ToString();
10522 if (tc.HasCaught())
10523 return tc.ReThrow();
10524 return v8::Undefined();
10525}
10526
10527
10528// Test that an exception can be propagated down through a spaghetti
10529// stack using ReThrow.
10530THREADED_TEST(SpaghettiStackReThrow) {
10531 v8::HandleScope scope;
10532 LocalContext context;
10533 context->Global()->Set(
10534 v8::String::New("s"),
10535 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction());
10536 v8::TryCatch try_catch;
10537 CompileRun(
10538 "var i = 0;"
10539 "var o = {"
10540 " toString: function () {"
10541 " if (i == 10) {"
10542 " throw 'Hey!';"
10543 " } else {"
10544 " i++;"
10545 " return s(o);"
10546 " }"
10547 " }"
10548 "};"
10549 "s(o);");
10550 CHECK(try_catch.HasCaught());
10551 v8::String::Utf8Value value(try_catch.Exception());
10552 CHECK_EQ(0, strcmp(*value, "Hey!"));
10553}
10554
10555
Steve Blockd0582a62009-12-15 09:54:21 +000010556TEST(Regress528) {
10557 v8::V8::Initialize();
10558
10559 v8::HandleScope scope;
10560 v8::Persistent<Context> context;
10561 v8::Persistent<Context> other_context;
10562 int gc_count;
10563
10564 // Create a context used to keep the code from aging in the compilation
10565 // cache.
10566 other_context = Context::New();
10567
10568 // Context-dependent context data creates reference from the compilation
10569 // cache to the global object.
10570 const char* source_simple = "1";
10571 context = Context::New();
10572 {
10573 v8::HandleScope scope;
10574
10575 context->Enter();
10576 Local<v8::String> obj = v8::String::New("");
10577 context->SetData(obj);
10578 CompileRun(source_simple);
10579 context->Exit();
10580 }
10581 context.Dispose();
10582 for (gc_count = 1; gc_count < 10; gc_count++) {
10583 other_context->Enter();
10584 CompileRun(source_simple);
10585 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010586 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010587 if (GetGlobalObjectsCount() == 1) break;
10588 }
10589 CHECK_GE(2, gc_count);
10590 CHECK_EQ(1, GetGlobalObjectsCount());
10591
10592 // Eval in a function creates reference from the compilation cache to the
10593 // global object.
10594 const char* source_eval = "function f(){eval('1')}; f()";
10595 context = Context::New();
10596 {
10597 v8::HandleScope scope;
10598
10599 context->Enter();
10600 CompileRun(source_eval);
10601 context->Exit();
10602 }
10603 context.Dispose();
10604 for (gc_count = 1; gc_count < 10; gc_count++) {
10605 other_context->Enter();
10606 CompileRun(source_eval);
10607 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010608 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010609 if (GetGlobalObjectsCount() == 1) break;
10610 }
10611 CHECK_GE(2, gc_count);
10612 CHECK_EQ(1, GetGlobalObjectsCount());
10613
10614 // Looking up the line number for an exception creates reference from the
10615 // compilation cache to the global object.
10616 const char* source_exception = "function f(){throw 1;} f()";
10617 context = Context::New();
10618 {
10619 v8::HandleScope scope;
10620
10621 context->Enter();
10622 v8::TryCatch try_catch;
10623 CompileRun(source_exception);
10624 CHECK(try_catch.HasCaught());
10625 v8::Handle<v8::Message> message = try_catch.Message();
10626 CHECK(!message.IsEmpty());
10627 CHECK_EQ(1, message->GetLineNumber());
10628 context->Exit();
10629 }
10630 context.Dispose();
10631 for (gc_count = 1; gc_count < 10; gc_count++) {
10632 other_context->Enter();
10633 CompileRun(source_exception);
10634 other_context->Exit();
Steve Block8defd9f2010-07-08 12:39:36 +010010635 i::Heap::CollectAllGarbage(false);
Steve Blockd0582a62009-12-15 09:54:21 +000010636 if (GetGlobalObjectsCount() == 1) break;
10637 }
10638 CHECK_GE(2, gc_count);
10639 CHECK_EQ(1, GetGlobalObjectsCount());
10640
10641 other_context.Dispose();
Steve Block3ce2e202009-11-05 08:53:23 +000010642}
Andrei Popescu402d9372010-02-26 13:31:12 +000010643
10644
10645THREADED_TEST(ScriptOrigin) {
10646 v8::HandleScope scope;
10647 LocalContext env;
10648 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10649 v8::Handle<v8::String> script = v8::String::New(
10650 "function f() {}\n\nfunction g() {}");
10651 v8::Script::Compile(script, &origin)->Run();
10652 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10653 env->Global()->Get(v8::String::New("f")));
10654 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10655 env->Global()->Get(v8::String::New("g")));
10656
10657 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
10658 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
10659 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
10660
10661 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
10662 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
10663 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
10664}
10665
10666
10667THREADED_TEST(ScriptLineNumber) {
10668 v8::HandleScope scope;
10669 LocalContext env;
10670 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
10671 v8::Handle<v8::String> script = v8::String::New(
10672 "function f() {}\n\nfunction g() {}");
10673 v8::Script::Compile(script, &origin)->Run();
10674 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
10675 env->Global()->Get(v8::String::New("f")));
10676 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
10677 env->Global()->Get(v8::String::New("g")));
10678 CHECK_EQ(0, f->GetScriptLineNumber());
10679 CHECK_EQ(2, g->GetScriptLineNumber());
10680}
10681
10682
10683static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
10684 const AccessorInfo& info) {
10685 return v8_num(42);
10686}
10687
10688
10689static void SetterWhichSetsYOnThisTo23(Local<String> name,
10690 Local<Value> value,
10691 const AccessorInfo& info) {
10692 info.This()->Set(v8_str("y"), v8_num(23));
10693}
10694
10695
Steve Block6ded16b2010-05-10 14:33:55 +010010696TEST(SetterOnConstructorPrototype) {
Andrei Popescu402d9372010-02-26 13:31:12 +000010697 v8::HandleScope scope;
10698 Local<ObjectTemplate> templ = ObjectTemplate::New();
10699 templ->SetAccessor(v8_str("x"),
10700 GetterWhichReturns42,
10701 SetterWhichSetsYOnThisTo23);
10702 LocalContext context;
10703 context->Global()->Set(v8_str("P"), templ->NewInstance());
10704 CompileRun("function C1() {"
10705 " this.x = 23;"
10706 "};"
10707 "C1.prototype = P;"
10708 "function C2() {"
10709 " this.x = 23"
10710 "};"
10711 "C2.prototype = { };"
10712 "C2.prototype.__proto__ = P;");
10713
10714 v8::Local<v8::Script> script;
10715 script = v8::Script::Compile(v8_str("new C1();"));
10716 for (int i = 0; i < 10; i++) {
10717 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10718 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10719 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10720 }
10721
10722 script = v8::Script::Compile(v8_str("new C2();"));
10723 for (int i = 0; i < 10; i++) {
10724 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10725 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
10726 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
10727 }
10728}
10729
10730
10731static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
10732 Local<String> name, const AccessorInfo& info) {
10733 return v8_num(42);
10734}
10735
10736
10737static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
10738 Local<String> name, Local<Value> value, const AccessorInfo& info) {
10739 if (name->Equals(v8_str("x"))) {
10740 info.This()->Set(v8_str("y"), v8_num(23));
10741 }
10742 return v8::Handle<Value>();
10743}
10744
10745
10746THREADED_TEST(InterceptorOnConstructorPrototype) {
10747 v8::HandleScope scope;
10748 Local<ObjectTemplate> templ = ObjectTemplate::New();
10749 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
10750 NamedPropertySetterWhichSetsYOnThisTo23);
10751 LocalContext context;
10752 context->Global()->Set(v8_str("P"), templ->NewInstance());
10753 CompileRun("function C1() {"
10754 " this.x = 23;"
10755 "};"
10756 "C1.prototype = P;"
10757 "function C2() {"
10758 " this.x = 23"
10759 "};"
10760 "C2.prototype = { };"
10761 "C2.prototype.__proto__ = P;");
10762
10763 v8::Local<v8::Script> script;
10764 script = v8::Script::Compile(v8_str("new C1();"));
10765 for (int i = 0; i < 10; i++) {
10766 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10767 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10768 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10769 }
10770
10771 script = v8::Script::Compile(v8_str("new C2();"));
10772 for (int i = 0; i < 10; i++) {
10773 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
10774 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
10775 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
10776 }
10777}
Steve Block6ded16b2010-05-10 14:33:55 +010010778
10779
10780TEST(Bug618) {
10781 const char* source = "function C1() {"
10782 " this.x = 23;"
10783 "};"
10784 "C1.prototype = P;";
10785
10786 v8::HandleScope scope;
10787 LocalContext context;
10788 v8::Local<v8::Script> script;
10789
10790 // Use a simple object as prototype.
10791 v8::Local<v8::Object> prototype = v8::Object::New();
10792 prototype->Set(v8_str("y"), v8_num(42));
10793 context->Global()->Set(v8_str("P"), prototype);
10794
10795 // This compile will add the code to the compilation cache.
10796 CompileRun(source);
10797
10798 script = v8::Script::Compile(v8_str("new C1();"));
10799 for (int i = 0; i < 10; i++) {
10800 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10801 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
10802 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
10803 }
10804
10805 // Use an API object with accessors as prototype.
10806 Local<ObjectTemplate> templ = ObjectTemplate::New();
10807 templ->SetAccessor(v8_str("x"),
10808 GetterWhichReturns42,
10809 SetterWhichSetsYOnThisTo23);
10810 context->Global()->Set(v8_str("P"), templ->NewInstance());
10811
10812 // This compile will get the code from the compilation cache.
10813 CompileRun(source);
10814
10815 script = v8::Script::Compile(v8_str("new C1();"));
10816 for (int i = 0; i < 10; i++) {
10817 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
10818 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
10819 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
10820 }
10821}
10822
10823int prologue_call_count = 0;
10824int epilogue_call_count = 0;
10825int prologue_call_count_second = 0;
10826int epilogue_call_count_second = 0;
10827
10828void PrologueCallback(v8::GCType, v8::GCCallbackFlags) {
10829 ++prologue_call_count;
10830}
10831
10832void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) {
10833 ++epilogue_call_count;
10834}
10835
10836void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10837 ++prologue_call_count_second;
10838}
10839
10840void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) {
10841 ++epilogue_call_count_second;
10842}
10843
10844TEST(GCCallbacks) {
10845 LocalContext context;
10846
10847 v8::V8::AddGCPrologueCallback(PrologueCallback);
10848 v8::V8::AddGCEpilogueCallback(EpilogueCallback);
10849 CHECK_EQ(0, prologue_call_count);
10850 CHECK_EQ(0, epilogue_call_count);
10851 i::Heap::CollectAllGarbage(false);
10852 CHECK_EQ(1, prologue_call_count);
10853 CHECK_EQ(1, epilogue_call_count);
10854 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond);
10855 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond);
10856 i::Heap::CollectAllGarbage(false);
10857 CHECK_EQ(2, prologue_call_count);
10858 CHECK_EQ(2, epilogue_call_count);
10859 CHECK_EQ(1, prologue_call_count_second);
10860 CHECK_EQ(1, epilogue_call_count_second);
10861 v8::V8::RemoveGCPrologueCallback(PrologueCallback);
10862 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback);
10863 i::Heap::CollectAllGarbage(false);
10864 CHECK_EQ(2, prologue_call_count);
10865 CHECK_EQ(2, epilogue_call_count);
10866 CHECK_EQ(2, prologue_call_count_second);
10867 CHECK_EQ(2, epilogue_call_count_second);
10868 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond);
10869 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond);
10870 i::Heap::CollectAllGarbage(false);
10871 CHECK_EQ(2, prologue_call_count);
10872 CHECK_EQ(2, epilogue_call_count);
10873 CHECK_EQ(2, prologue_call_count_second);
10874 CHECK_EQ(2, epilogue_call_count_second);
10875}
Kristian Monsen25f61362010-05-21 11:50:48 +010010876
10877
10878THREADED_TEST(AddToJSFunctionResultCache) {
10879 i::FLAG_allow_natives_syntax = true;
10880 v8::HandleScope scope;
10881
10882 LocalContext context;
10883
10884 const char* code =
10885 "(function() {"
10886 " var key0 = 'a';"
10887 " var key1 = 'b';"
10888 " var r0 = %_GetFromCache(0, key0);"
10889 " var r1 = %_GetFromCache(0, key1);"
10890 " var r0_ = %_GetFromCache(0, key0);"
10891 " if (r0 !== r0_)"
10892 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
10893 " var r1_ = %_GetFromCache(0, key1);"
10894 " if (r1 !== r1_)"
10895 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
10896 " return 'PASSED';"
10897 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010010898 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010010899 ExpectString(code, "PASSED");
10900}
10901
10902
10903static const int k0CacheSize = 16;
10904
10905THREADED_TEST(FillJSFunctionResultCache) {
10906 i::FLAG_allow_natives_syntax = true;
10907 v8::HandleScope scope;
10908
10909 LocalContext context;
10910
10911 const char* code =
10912 "(function() {"
10913 " var k = 'a';"
10914 " var r = %_GetFromCache(0, k);"
10915 " for (var i = 0; i < 16; i++) {"
10916 " %_GetFromCache(0, 'a' + i);"
10917 " };"
10918 " if (r === %_GetFromCache(0, k))"
10919 " return 'FAILED: k0CacheSize is too small';"
10920 " return 'PASSED';"
10921 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010010922 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010010923 ExpectString(code, "PASSED");
10924}
10925
10926
10927THREADED_TEST(RoundRobinGetFromCache) {
10928 i::FLAG_allow_natives_syntax = true;
10929 v8::HandleScope scope;
10930
10931 LocalContext context;
10932
10933 const char* code =
10934 "(function() {"
10935 " var keys = [];"
10936 " for (var i = 0; i < 16; i++) keys.push(i);"
10937 " var values = [];"
10938 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
10939 " for (var i = 0; i < 16; i++) {"
10940 " var v = %_GetFromCache(0, keys[i]);"
10941 " if (v !== values[i])"
10942 " return 'Wrong value for ' + "
10943 " keys[i] + ': ' + v + ' vs. ' + values[i];"
10944 " };"
10945 " return 'PASSED';"
10946 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010010947 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010010948 ExpectString(code, "PASSED");
10949}
10950
10951
10952THREADED_TEST(ReverseGetFromCache) {
10953 i::FLAG_allow_natives_syntax = true;
10954 v8::HandleScope scope;
10955
10956 LocalContext context;
10957
10958 const char* code =
10959 "(function() {"
10960 " var keys = [];"
10961 " for (var i = 0; i < 16; i++) keys.push(i);"
10962 " var values = [];"
10963 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
10964 " for (var i = 15; i >= 16; i--) {"
10965 " var v = %_GetFromCache(0, keys[i]);"
10966 " if (v !== values[i])"
10967 " return 'Wrong value for ' + "
10968 " keys[i] + ': ' + v + ' vs. ' + values[i];"
10969 " };"
10970 " return 'PASSED';"
10971 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010010972 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010010973 ExpectString(code, "PASSED");
10974}
10975
10976
10977THREADED_TEST(TestEviction) {
10978 i::FLAG_allow_natives_syntax = true;
10979 v8::HandleScope scope;
10980
10981 LocalContext context;
10982
10983 const char* code =
10984 "(function() {"
10985 " for (var i = 0; i < 2*16; i++) {"
10986 " %_GetFromCache(0, 'a' + i);"
10987 " };"
10988 " return 'PASSED';"
10989 "})()";
Steve Block8defd9f2010-07-08 12:39:36 +010010990 i::Heap::ClearJSFunctionResultCaches();
Kristian Monsen25f61362010-05-21 11:50:48 +010010991 ExpectString(code, "PASSED");
10992}
Steve Block8defd9f2010-07-08 12:39:36 +010010993
10994
10995THREADED_TEST(TwoByteStringInAsciiCons) {
10996 // See Chromium issue 47824.
10997 v8::HandleScope scope;
10998
10999 LocalContext context;
11000 const char* init_code =
11001 "var str1 = 'abelspendabel';"
11002 "var str2 = str1 + str1 + str1;"
11003 "str2;";
11004 Local<Value> result = CompileRun(init_code);
11005
11006 CHECK(result->IsString());
11007 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result));
11008 int length = string->length();
11009 CHECK(string->IsAsciiRepresentation());
11010
11011 FlattenString(string);
11012 i::Handle<i::String> flat_string = FlattenGetString(string);
11013
11014 CHECK(string->IsAsciiRepresentation());
11015 CHECK(flat_string->IsAsciiRepresentation());
11016
11017 // Create external resource.
11018 uint16_t* uc16_buffer = new uint16_t[length + 1];
11019
11020 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length);
11021 uc16_buffer[length] = 0;
11022
11023 TestResource resource(uc16_buffer);
11024
11025 flat_string->MakeExternal(&resource);
11026
11027 CHECK(flat_string->IsTwoByteRepresentation());
11028
11029 // At this point, we should have a Cons string which is flat and ASCII,
11030 // with a first half that is a two-byte string (although it only contains
11031 // ASCII characters). This is a valid sequence of steps, and it can happen
11032 // in real pages.
11033
11034 CHECK(string->IsAsciiRepresentation());
11035 i::ConsString* cons = i::ConsString::cast(*string);
11036 CHECK_EQ(0, cons->second()->length());
11037 CHECK(cons->first()->IsTwoByteRepresentation());
11038
11039 // Check that some string operations work.
11040
11041 // Atom RegExp.
11042 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;");
11043 CHECK_EQ(6, reresult->Int32Value());
11044
11045 // Nonatom RegExp.
11046 reresult = CompileRun("str2.match(/abe./g).length;");
11047 CHECK_EQ(6, reresult->Int32Value());
11048
11049 reresult = CompileRun("str2.search(/bel/g);");
11050 CHECK_EQ(1, reresult->Int32Value());
11051
11052 reresult = CompileRun("str2.search(/be./g);");
11053 CHECK_EQ(1, reresult->Int32Value());
11054
11055 ExpectTrue("/bel/g.test(str2);");
11056
11057 ExpectTrue("/be./g.test(str2);");
11058
11059 reresult = CompileRun("/bel/g.exec(str2);");
11060 CHECK(!reresult->IsNull());
11061
11062 reresult = CompileRun("/be./g.exec(str2);");
11063 CHECK(!reresult->IsNull());
11064
11065 ExpectString("str2.substring(2, 10);", "elspenda");
11066
11067 ExpectString("str2.substring(2, 20);", "elspendabelabelspe");
11068
11069 ExpectString("str2.charAt(2);", "e");
11070
11071 reresult = CompileRun("str2.charCodeAt(2);");
11072 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value());
11073}